Learn to use powerful `for` and `for_each` constructs in Terraform to efficiently iterate over nested data structures and manage complex resource configurations with ease.
Terraform provides powerful looping constructs for working with nested data structures. This flexibility allows you to manage complex infrastructure setups efficiently. There are two primary methods for iterating over nested data in Terraform: nested for_each
loops and a combination of for
and for_each
.
To iterate over nested data structures in Terraform, you can use nested for_each
loops or a combination of for
and for_each
.
Nested for_each
loops:
resource "example_resource" "example" {
for_each = var.teams
team_name = each.key
dynamic "workspace" {
for_each = each.value.workspaces
content {
workspace_name = workspace.value
}
}
}
In this example, the outer for_each
iterates over the teams
map, while the inner for_each
iterates over the workspaces
list within each team.
Using for
within for_each
:
resource "example_resource" "example" {
for_each = var.teams
dynamic "workspace" {
for i in range(length(each.value.workspaces)) {
content {
workspace_name = each.value.workspaces[i]
}
}
}
}
Here, the for
loop iterates over the indices of the workspaces
list, and you can access individual elements using the index i
.
Accessing nested values:
You can access nested values within the loop using the each
object and the appropriate keys. For example, each.value.workspaces[i]
accesses the i
th workspace name within the current team.
Dynamic blocks:
When working with nested structures, you can use dynamic blocks to create resources based on the nested data. The dynamic
block allows you to iterate over a collection and define resources within it.
Important considerations:
each
object.flatten
function to simplify complex nested structures before iterating.The code provides examples of how to iterate over nested data structures in Terraform using techniques like nested for_each loops, combining for loops within for_each, and flattening nested structures. It showcases creating AWS resources like S3 buckets, security groups, and IAM users with policies based on the nested data. The examples demonstrate how to access specific elements within nested structures and use them to dynamically generate resources and configurations.
1. Nested for_each
loops:
This example creates AWS S3 buckets for different teams and their respective workspaces.
variable "teams" {
type = map(object({
workspaces = list(string)
}))
default = {
"team_a" = {
workspaces = ["dev", "staging", "prod"]
},
"team_b" = {
workspaces = ["development", "production"]
}
}
}
resource "aws_s3_bucket" "team_buckets" {
for_each = var.teams
bucket = "${each.key}-bucket"
dynamic "versioning" {
for_each = each.value.workspaces
content {
enabled = true
}
}
tags = {
Team = each.key
}
}
2. Using for
within for_each
:
This example creates security groups for different environments and adds ingress rules based on port numbers.
variable "environments" {
type = map(object({
ports = list(number)
}))
default = {
"development" = {
ports = [22, 80, 443]
},
"production" = {
ports = [443, 8443]
}
}
}
resource "aws_security_group" "example" {
for_each = var.environments
name = "sg-${each.key}"
description = "Security group for ${each.key} environment"
dynamic "ingress" {
for i in range(length(each.value.ports)) {
content {
from_port = each.value.ports[i]
to_port = each.value.ports[i]
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
}
}
}
3. Accessing nested values and using flatten
:
This example demonstrates flattening a nested data structure and creating IAM users with specific policies.
variable "users_data" {
type = list(object({
username = string
teams = list(string)
policies = list(string)
}))
default = [
{
username = "john.doe",
teams = ["developers", "sre"],
policies = ["read-only", "admin"]
},
{
username = "jane.doe",
teams = ["developers"],
policies = ["read-only"]
}
]
}
locals {
users = flatten([
for user in var.users_data : [
for team in user.teams : {
username = user.username
team = team
policies = user.policies
}
]
])
}
resource "aws_iam_user" "example" {
for_each = { for user in local.users : "${user.username}-${user.team}" => user }
name = each.value.username
tags = {
Team = each.value.team
}
}
resource "aws_iam_user_policy_attachment" "example" {
for_each = { for user in local.users : "${user.username}-${user.team}" => user }
user = aws_iam_user.example[each.key].name
policy_arn = "arn:aws:iam::aws:policy/${each.value.policies[0]}"
}
These examples demonstrate different ways to iterate over nested data structures in Terraform. Remember to choose the approach that best suits your specific needs and data structure.
Choosing the right approach:
for_each
: Best for situations where you need to iterate over all elements in both the outer and inner collections. Offers clean syntax for direct access to nested values.for
within for_each
: Useful when you need more control over the iteration process, like skipping elements or accessing indices. Can be less readable than nested for_each
for simple cases.flatten
function: Consider this when dealing with deeply nested structures or when you need to transform the data into a simpler format before iterating. Can significantly improve readability and maintainability.Best Practices:
Common Pitfalls:
for_each
, ensure unique keys to avoid conflicts, especially when creating resources.each
object within nested loops. The inner loop's each
refers to the current element of the inner collection.By understanding these concepts and following best practices, you can effectively leverage Terraform's looping constructs to manage complex infrastructure deployments with ease.
This table summarizes the methods and key points for iterating over nested data structures in Terraform:
Method | Description | Example | Key Points |
---|---|---|---|
Nested for_each loops |
Uses nested for_each blocks to iterate over nested maps and lists. |
terraform resource "example_resource" "example" { for_each = var.teams team_name = each.key dynamic "workspace" { for_each = each.value.workspaces content { workspace_name = workspace.value } } } |
- Outer loop iterates over the main data structure. - Inner loop iterates over nested structures within each element of the outer loop. |
for loop within for_each |
Uses a for loop within a for_each block to iterate over lists within the main data structure. |
terraform resource "example_resource" "example" { for_each = var.teams dynamic "workspace" { for i in range(length(each.value.workspaces)) { content { workspace_name = each.value.workspaces[i] } } } } |
- for loop iterates over indices of the nested list. - Access elements using the index i . |
Accessing Nested Values | Use the each object and appropriate keys to access nested values within the loop. |
each.value.workspaces[i] |
- each refers to the current element of the outer loop. |
Dynamic Blocks | Use dynamic blocks to create resources based on nested data. |
terraform dynamic "workspace" { ... } |
- Allows defining resources iteratively based on the nested data. |
General Considerations:
each
object.flatten
function to simplify complex structures.Terraform provides powerful looping constructs for working with nested data structures. This flexibility allows you to manage complex infrastructure setups efficiently. There are two primary methods for iterating over nested data in Terraform: nested for_each
loops and a combination of for
and for_each
. Understanding these methods and their respective use cases empowers you to write cleaner, more maintainable Terraform code. By leveraging these looping constructs effectively, you can automate the provisioning of intricate infrastructure with ease and precision. Remember to prioritize code readability and clarity, especially when dealing with nested structures, to ensure maintainability and facilitate collaboration within your team.