A dynamic block generates zero or more repeated nested Terraform blocks from a collection, making reusable resources adaptable to changing configuration needs such as network access rules. section 4.10
The construct names the nested block, iterates with for_each, and places generated arguments inside content. Each iteration exposes a keyword named after that block, such as ingress.value; the syntax also supports nested dynamic blocks. section 4.10
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 security-group example to connect a typed collection of rule objects with generated ingress blocks and their per-item values. section 4.10
Toggle one nested block
Set for_each to var.enable_public_https ? ["placeholder"] : []. The empty list produces no ingress block; the one-item list produces a single HTTPS ingress block, even though the placeholder value is unused. section 4.10
Use dynamic blocks deliberately:
- Use them when the number of nested blocks depends on input values or when a reusable module cannot define every possible subblock in advance. section 4.10
- Test zero, one, and many generated blocks, concentrating on configuration logic rather than provider behavior. section 9.1.1
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 inspect conditional collection selection and the resulting public HTTPS ingress rule. section 4.10
