Terraform uses iteration to process collection values, generate strings, and define a dynamic number of infrastructure resources. Unlike imperative languages, typical do and while loops do not fit Terraform's model. section 4.0 section 4.8
Terraform supports three practical forms of iteration:
- Use for_each or count to make one resource block define multiple resources; a block cannot use both parameters. section 4.8
- Use a for expression to transform groups of data from one type into another. section 4.8
- Use template directives to iterate over map or list values while generating strings. section 4.4.3
Iterating inside a template
A template can emit each key and value from an object, or each value from a list:\n\n%{ for key, value in variable_object }\n${key} ${value}\n%{ endfor }\n\n%{ for value in variable_list }\n${value}\n%{ endfor }\n\nTemplates can also branch with percentage-bracket directives and evaluate expressions inside dollar brackets. section 4.4.3
Listing 4.46 dynamic block
variable "security_group_rules" {
type = list(object({
description = string,
from_port = number,
to_port = number
protocol = string
cidr_blocks = list(string)
}))
}
resource "aws_security_group" "main" {
name = "${var.name}-sg"
description = "The security group for an AWS Instance."
vpc_id = var.vpc_id
dynamic "ingress" { #1
for_each = var.security_group_rules #2
content {
description = ingress.value.description #3
from_port = ingress.value.from_port #3
to_port = ingress.value.to_port #3
protocol = ingress.value.protocol #3
cidr_blocks = ingress.value.cidr_blocks #3
}
}
ingress { #4
description = "HTTPS"
from_port = 443
to_port = 443
protocol = tcp
cidr_blocks = ["0.0.0.0/0"]
}
}
Use the dynamic-block listing to expand a security group's ingress block once for every object in a security-rule variable. The block reads each generated item's fields through ingress.value. section 4.10
Listing 4.47 dynamic block toggle
resource "aws_security_group" "dynamic" {
name = "${var.name}-sg"
description = "The security group for an AWS Instance."
vpc_id = var.vpc_id
dynamic "ingress" { #1
for_each = var.enable_public_https ? ["placeholder"] : [] #2
#B
content {
description = "Enable global access to port 443 (HTTPS)."
from_port = 443 #3
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"] #4
}
}
egress { #5
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
ipv6_cidr_blocks = ["::/0"]
}
}
Use the toggle listing to enable a nested ingress block conditionally: a one-item placeholder list creates the block, while an empty list omits it. section 4.10


























