I need to deploy a list of GCP compute instances. How do I loop for_each through the "vms" in a list of objects like this:
"gcp_zone": "us-central1-a",
"image_name": "centos-cloud/centos-7",
"vms": [
{
"hostname": "test1-srfe",
"cpu": 1,
"ram": 4,
"hdd": 15,
"log_drive": 300,
"template": "Template-New",
"service_types": [
"sql",
"db01",
"db02"
]
},
{
"hostname": "test1-second",
"cpu": 1,
"ram": 4,
"hdd": 15,
"template": "APPs-Template",
"service_types": [
"configs"
]
}
]
}
8
Answers
Seem's like I found what to do. If you pass not the maps of maps but the list of maps you can use such code
It will create actual number of instance and when you remove for example middle one of three(if you create three:)), terraform will remove what we asked.
Using the
for_each
block is pretty new and there’s not too much documentation. Some of the best info comes from their announcement blog post: https://www.hashicorp.com/blog/hashicorp-terraform-0-12-preview-for-and-for-each/Also make sure to check out the Dynamic Blocks section of their documentation: https://www.terraform.io/docs/configuration/expressions.html#dynamic-blocks
From what your example looks like you need to have a set of values for each instance that is created so you’ll have a map of maps:
Below is an example I created using Terraform 0.12.12:
Terraform plan output:
From Terraform 1.3, you can use the for_each and objects with modules like the following:
modules/google_compute_instance/variables.tf
modules/google_compute_instance/main.tf
servers.tf
Of course, you can add as many variables as needed and use them in the module.
You can do the following:
Assuming you had the following:
I work a lot with iterators in Terraform, they always gave me bad headaches. Therefore I identified three of the most common iterator patterns (code examples are given below), which helped me construct a lot of nice modules (source).
Using
for_each
and a list of strings is the easiest to understand, you can always use thetoset()
function. When working with a list of objects you need to convert it to amap
where the key is a unique value. The alternative is to put a map inside your Terraform configuration. Personally, I think it looks cleaner to have a list of objects instead of a map in your configuration. The key usually doesn’t have a purpose other than to identify unique items in a map, which can thus be constructed dynamically. I also use iterators to conditionally deploy a resource or resource block, especially when constructing more complex modules.Using for_each on a list of strings
Using for_each on a list of objects
Using for_each as a conditional
I took reference from the for_each example above and used below. This did not work for me, link below has details.
Terraform for_each on custom registry
Error:module.az is object with 2 attributes
If I replace for_each with actual values, the module is working perfectly.
This is a pretty confusing structure in terraform, but given:
You iterate through resource as so:
You reference ANOTHER resource which uses a list of objects as so:
Notice since aws_lb_target_group is also using an array of maps, you must specify the map property when referencing from another resource, as shown above! That could trip people up.
And if you want to output the list of objects, you do as so:
Yes this is possible, you need to use the for expression in Terraform to achieve this though, the for loop converts the list of objects into a value in which Terraform can loop over using for_each, without the for expression, Terraform cannot loop over the list of objects because there is no key value for Terraform to reference.
Below is a is a simple example:
Use the for expression wrapped in curl brackets to convert the variable value, each maps key will be given the value of each maps name input, for example the first map would be given a key of "DenyHTTPInbound"
ref: https://jimferrari.com/2023/02/13/loop-through-list-of-maps-objects-with-terraform/