What are dynamic blocks, and when are they useful?
Quick Answer
A `dynamic` block programmatically generates a variable number of nested configuration blocks (like multiple `ingress` blocks in a security group) by iterating over a list/map, since ordinary nested blocks can't be created with `count`/`for_each` themselves. It's useful when the number of repeated nested blocks depends on input data — e.g., a variable list of firewall rules — but should be used sparingly, since it trades readability for flexibility; when the set of nested blocks is fixed, writing them out explicitly is clearer.
Detailed Answer
count and for_each (from the previous question) let you repeat an entire resource block. dynamic blocks solve a related but different problem: repeating a nested block inside a resource, based on the size of some input data.
The problem it solves
Consider a security group with a variable number of ingress rules:
resource "aws_security_group" "web" {
name = "web-sg"
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
# ...how many more `ingress` blocks do we need? Depends on input data.
}
You can't apply count/for_each directly to the ingress { ... } nested block itself — those meta-arguments only work on top-level resource/module blocks.
The dynamic block
variable "ingress_rules" {
type = list(object({
port = number
cidr_blocks = list(string)
}))
}
resource "aws_security_group" "web" {
name = "web-sg"
dynamic "ingress" {
for_each = var.ingress_rules
content {
from_port = ingress.value.port
to_port = ingress.value.port
protocol = "tcp"
cidr_blocks = ingress.value.cidr_blocks
}
}
}
For each element in var.ingress_rules, Terraform generates one ingress { ... } block, with ingress.value referencing the current element (the iterator variable name matches the block label, "ingress", unless overridden with iterator).
When to use it (and when not to)
- Use it when the number of nested blocks genuinely depends on input data — a variable list of firewall rules, a variable number of load balancer listener rules, etc.
- Avoid it when the set of nested blocks is fixed and known — writing them out explicitly is more readable and easier to diff in a PR. Dynamic blocks trade some readability for flexibility, so reach for them only when the alternative is truly impractical (e.g., hardcoding a variable-length list).
Interview-ready summary
dynamic blocks are to nested configuration blocks what for_each is to whole resources — a way to generate a variable number of them from a collection, used sparingly because they add a layer of indirection to otherwise-static configuration.