How do I replace an entire block of a terraform script with a variable? For example,
resource "azurerm_data_factory_trigger_schedule" "sfa-data-project-agg-pipeline-trigger" {
name = "aggregations_pipeline_trigger"
data_factory_id = var.data_factory_resource_id
pipeline_name = "my-pipeline"
frequency = var.frequency
schedule {
hours = [var.adf_pipeline_schedule_hour]
minutes = [var.adf_pipeline_schedule_minute]
}
}
In the above sample, how to make the entire schedule block configurable using a terraform variable?
I tried this but it isn't working.
schedule = var.schedule
Nope, that is not possible as schedule is a block with arguments and it is not an argument itself. Maps are aggregate types and and they are made of primitive types (e.g., numbers in this case). A more detailed explanation on primitive and aggregate types with examples can be found in [1] (h/t: Matt Schuchard). In such cases, I prefer to do something like this:
variable "schedule" {
type = object({
hours = number
minutes = number
})
description = "Variable to define the values for hours and minutes."
default = {
hours = 0
minutes = 0
}
}
Then, in the resource:
resource "azurerm_data_factory_trigger_schedule" "sfa-data-project-agg-pipeline-trigger" {
name = "aggregations_pipeline_trigger"
data_factory_id = var.data_factory_resource_id
pipeline_name = "my-pipeline"
frequency = var.frequency
schedule {
hours = [var.schedule.hours]
minutes = [var.schedule.minutes]
}
}
[1] https://www.terraform.io/plugin/sdkv2/schemas/schema-types#typemap
Related
I need to validate if a provided variable number is even or odd in terraform, but I was unable to find a simple solution for it.
variable "my_number" {
type = number
validation {
condition = ???
error_message = "Only even numbers are accepted."
}
}
You can use the modulo operator [1]:
variable "my_number" {
type = number
validation {
condition = var.my_number % 2 == 0
error_message = "Only even numbers are accepted."
}
}
[1] https://developer.hashicorp.com/terraform/language/expressions/operators#a-b-4
I have previously created A type cloudflare record and would like to keep it while adding another one. So I defined a flag to just skip it.
resource "cloudflare_record" "cloudflare-a-record" {
count = var.flag != false ? 1 : 0
zone_id = var.zone_id
name = var.sub_domain
type = "A"
value = aws_eip.my_eip.public_ip
ttl = 1
proxied = false
}
resource "cloudflare_record" "vault-cloudflare-cname-record" {
count = var.flag == false ? 1 : 0
zone_id = var.zone_id
name = cloudflare_record.cloudflare-a-record.hostname
type = "CNAME"
value = aws_eip.my_eip.public_dns
ttl = 1
proxied = false
}
But Terraform deleted this resource with the following message:
cloudflare_record.vault-cloudflare-a-record[0] will be destroyed
(because index [0] is out of range for count)
Is there another way to ignore this resource? Or is the code wrong?
In this case, you cannot use the same flag for two different resources as the variable value will remain the same for both A and CNAME resources. The way I see it there are two possible options with the current code since you are using different conditionals (in A you use == and in CNAME you use !=):
var.flag == false ? 1 : 0 # A record
var.flag != false ? 1 : 0 # CNAME record
This means if the flag = false the A record will be created (as the count will be 1) and the CNAME record will not be created (as the count will be 0). Now, if the flag's value changes to true, then the A record will be deleted (as true == false will return false) and the CNAME record will be created (as true != false will be true). This means that the same flag should not be used for two different resources. You could use the same conditional for both resources, which means that both would be created/deleted together (not sure if that is what you want). A better way would be to define two variables, one for A and one for CNAME record:
variable "create_a_record" {
type = bool
}
variable "create_cname_record" {
type = bool
}
Then, in your code you would change the lines that use the count meta-argument to:
resource "cloudflare_record" "cloudflare-a-record" {
count = var.create_a_record ? 1 : 0
zone_id = var.zone_id
name = var.sub_domain
type = "A"
value = aws_eip.my_eip.public_ip
ttl = 1
proxied = false
}
resource "cloudflare_record" "vault-cloudflare-cname-record" {
count = var.create_cname_record ? 1 : 0
zone_id = var.zone_id
name = cloudflare_record.cloudflare-a-record.hostname
type = "CNAME"
value = aws_eip.my_eip.public_dns
ttl = 1
proxied = false
}
This way you can control if you want to have both created or only one. Also note the following:
count = var.create_a_record ? 1 : 0
count = var.create_cname_record ? 1 : 0
When variables are of type bool (true or false), when using them in conditionals, you do not have to check their equality against another boolean, as the left-most value will anyway be either true or false. So for example, if you set create_a_record = true, that would make the above expression:
count = true ? 1 : 0
and that would evaluate to count = 1. You could also set default values for variables, e.g., if you want to make sure the A record is always there, you can just do this:
variable "create_a_record" {
type = bool
default = true
}
[1] https://www.terraform.io/language/expressions/conditionals
Regarding to the Docs (https://www.terraform.io/language/meta-arguments/count), counting with Terraform starts at 0.
variable "vm_count" {
type = number
}
resource "azurerm_windows_virtual_machine" "vmdeployment" {
count = var.vm_count
name = "cmw-${var.service_name}-${format("%04d", count.index)}"
How can I define a different starting point when using [count.index] to deploy multiple VM-Instances?
count.index will always start with 0.
If you want your VM names to start with another number, 1 for example, just use some math:
name = "cmw-${var.service_name}-${format("%04d", count.index + 1)}"
Alternatively, you could use the Terraform range function, to create a list of a specific range of numbers, and then use for_each instead of count in your resource definition.
I have solved it with for_each.
First, I create a Variable
terraform.tfvars
vm_list = [1,2,3]
service_name = "foobar"
variables.tf
variable "vm_list" {
type = list(any)
}
variable "service_name" {
type = string
}
And finally my main.tf
for_each = { for i in var.vm_list : "key_${i}" => i }
name = "vm-${var.service_name}-${format("%04d", each.value)}"
I have a network module with variable:
variable "subnetsCount" {
type = number
description = "Define amount of subnets between 2 min and 4 max"
validation {
condition = var.subnetsCount < 2 || var.subnetsCount > 4
error_message = "Variable subnetsCount should be between 2 and 4."
}
default = 2
}
I want to only allow a number value between 2 and 4. When I pass any value greater or smaller say 1 or 10 it doesn't throw any errors why?
1.For example I pass this to subnet:
module "network" {
source = "./network"
subnetsCount = 4
}
then type in terminal terraform apply, yet no errors thrown.
Your condition should be:
condition = var.subnetsCount >= 2 && var.subnetsCount <= 4
I've started to use Terraform lately, and i need some help. I hope it's not too basic. I've the following Terraform data structure.
abc_template = {
a = var.a
b = var.b
c = var.c
d = var.d
....
....
....
k = var.k
}
And then i run:
resource "local_file" "aaa" {
count = 1
content = templatefile("${path.module}/templates/abc.tmpl", local.abc_template)
....
....
}
I need to create a new template (xyz_template), Which should be very similar to abc_template, While only a few variables will changed from the original template. What can i do instead of duplicating so many code lines? Is there a way to inherit abc_template, and just to override the relevant variables, Instead of creating xyz_template which might be very similar to abc_template?
Please advise.
You could use a map:
variable "global" {
type = "map"
default = {
name = "TEST"
addr = "Test123"
}
}
output "example" {
value = templatefile("${path.module}/web.tpl", {
global = var.global
})
}
template:
My name is ${global.name}.
And you can override values in a map using the merge() function.
You can use the merge function to produce a new map using a blend of elements from multiple maps.
For example:
locals {
abc_template = {
a = var.a
b = var.b
c = var.c
d = var.d
}
xyz_template = merge(
local.abc_template,
{
d = var.other_d
x = var.x
y = var.y
z = var.z
},
)
}
In the above example, local.xyz_template would have all of the same elements as local.abc_template except that d is overridden, and it would additionally have elements x, y, and z.