Learn how to iterate over a list of objects in Terraform 0.12 using the powerful for_each loop for efficient and dynamic infrastructure management.
In Terraform, using the for_each construct with a list of objects requires a map input where keys uniquely identify each object. This ensures proper resource creation during iteration. Let's break down how to achieve this: First, define your list of objects with their respective attributes. Next, transform this list into a map, leveraging a unique attribute from your objects as the key. This step is crucial for for_each to function correctly. Finally, within your resource block, access the attributes of each object using the each.value syntax. Keep in mind some important considerations: always ensure your map keys are unique to prevent errors. If your list lacks inherently unique attributes, consider using toset() for conversion, although this will disregard the original order. Lastly, depending on your scenario, using count with element() might be a more suitable alternative to for_each, particularly when handling lists of simple values. Remember to tailor this approach to your specific use case and resource types.
To use for_each with a list of objects in Terraform, you need to provide a map where the keys are unique strings. These keys will be used to identify each instance of the resource created by the for_each loop. Here's how:
Define your list of objects:
variable "my_objects" {
default = [
{
name = "object1"
value = "value1"
},
{
name = "object2"
value = "value2"
}
]
}Convert the list to a map using a suitable key:
You can use the name attribute from your objects as the key:
resource "example_resource" "example" {
for_each = { for obj in var.my_objects : obj.name => obj }
# Access object attributes within the resource block
name = each.value.name
value = each.value.value
}In this example:
for_each iterates over var.my_objects.obj.name => obj creates a map entry for each object, using its name as the key and the entire object as the value.each.value refers to the current object from the map.Access object attributes within the resource:
Use each.value followed by the attribute name to access individual attributes of each object within the resource block.
Important Considerations:
toset() to convert it into a set of strings, but this will discard the original order of the list.for_each: In some cases, using count with element() might be more suitable than for_each, especially when dealing with lists of simple values.Remember to adapt this approach to your specific use case and resource types.
This Terraform code defines a variable "servers" containing a list of server objects, each with hostname, IP address, and disk size. It then uses a for_each loop to create a Google Compute Instance resource for each server in the list, setting the instance name, disk size, and IP address based on the values from the corresponding server object.
# Define a variable with a list of objects
variable "servers" {
default = [
{
hostname = "server-01"
ip_address = "192.168.1.10"
disk_size = 50
},
{
hostname = "server-02"
ip_address = "192.168.1.11"
disk_size = 100
}
]
}
# Create a cloud resource for each server object
resource "google_compute_instance" "default" {
for_each = { for server in var.servers : server.hostname => server }
name = each.value.hostname
machine_type = "n1-standard-1"
zone = "us-central1-a"
boot_disk {
initialize_params {
image = "debian-cloud/debian-9"
size = each.value.disk_size # Accessing object attribute
}
}
network_interface {
network = "default"
access_ip = each.value.ip_address # Accessing object attribute
}
}Explanation:
Variable servers: This variable defines a list of objects, each representing a server with attributes like hostname, ip_address, and disk_size.
for_each in google_compute_instance:
for_each to create one google_compute_instance resource for each object in the var.servers list.server.hostname => server creates a map where the key is the hostname of each server, and the value is the entire server object.Accessing Attributes:
each.value refers to the current server object being processed.each.value.disk_size and each.value.ip_address to access the respective attributes of each server object and use them to configure the google_compute_instance resource.This example demonstrates how to use for_each with a list of objects to create multiple instances of a resource, each configured with values from the corresponding object. Remember to choose a unique key from your objects for the map to avoid errors.
Choosing the Right Key:
server.hostname as the key clearly indicates that each resource represents a specific server.for_each = { for obj in var.my_objects : "${obj.attr1}-${obj.attr2}" => obj }Handling Changes:
for_each. Inspecting the state file can be helpful for understanding how resources map to your objects.Best Practices:
for_each loop. This improves code organization and reusability.for_each loop and the logic behind your key selection to enhance code maintainability.Beyond the Basics:
for_each: You can nest for_each loops to iterate over complex data structures, but ensure your keys remain unique across all levels.for expressions with if clauses within your for_each loop to conditionally create resources based on object attributes.By understanding these nuances and best practices, you can leverage the power of for_each effectively to manage collections of objects and create dynamic infrastructure with Terraform.
| Feature | Description |
|---|---|
| Defining Objects | Define a list of objects, each containing the necessary attributes. |
| Creating a Map | Convert the list into a map using a unique attribute from each object as the key. |
Iterating with for_each |
Use the for_each meta-argument with the created map to iterate over the objects. |
| Accessing Attributes | Access individual object attributes within the resource block using each.value.[attribute_name]. |
| Unique Keys | Ensure the keys used in the map are unique to avoid errors. |
| Alternatives | Consider using toset() for lists without unique attributes or count with element() for simple lists. |
Mastering the use of for_each with lists of objects is crucial for writing dynamic and efficient Terraform code. By understanding how to define objects, convert lists to maps with unique keys, and access object attributes within resource blocks, you can leverage this powerful construct to manage collections of resources effectively. Remember to consider the important factors of unique keys, alternative iteration methods, and best practices for code clarity and maintainability. As you become more comfortable with for_each, explore advanced techniques like nested loops and conditional resource creation to unlock the full potential of Terraform for your infrastructure automation needs.
The for_each Meta-Argument - Configuration Language | Terraform ... | Because a resource using for_each appears as a map of objects when used in ... Conversion from list to set discards the ordering of the items in the list and ...
Merging remote state file objects in Terraform 0.12.x - Terraform ... | Using Terraform v0.12.9 I’m working on upgrading a project from version 0.11.x to 0.12.9. In this project, each terraform stack references a large number of remote state files. Rather than defining the remote state files by hand, I kept the code neat and short by defining the remote states using a list, like this: variable "remote_states" { default = ["network", "remote_component1","remote_component2","etc"] } data "terraform_remote_state" "remote_states" { count = "${length(var.remote...
Use and declare block as variable in Terraform 0.12 | Sep 12, 2019 ... enabled = bool. expiration = list(object({ ... What if you flip the logic and instead loop over a list of bucket names using count/length?
Looping through a tuple list, for unique subnet_ids - Terraform ... | After generating a data structure of the following, I was trying to use the following to generate a list of subnet_ids per vpc. I ended up calling data aws_subnets_ids (after creating my subnets, however I was wondering if there was a more eloquent way to work with the data that I created): { subnet_id = "12345" subnet_name = "happydays-0" vpc_id = "1" }, { subnet_id = "6789" subnet_name = "happydays-1" vpc_id ...