Terraform Advanced — Dynamic Blocks
Dynamic blocks let you generate repeated nested blocks from a variable — eliminating copy-paste for security group rules, tags, and more.
The Problem
Without dynamic blocks, adding 10 ingress rules means 10 copy-pasted blocks:
# ❌ Anti-pattern: copy-paste hell
resource "aws_security_group" "web" {
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
# ... 8 more blocks
}
Dynamic Block Syntax
# ✅ Best practice: dynamic block
variable "ingress_rules" {
type = list(object({
port = number
protocol = string
cidr_blocks = list(string)
}))
default = [
{ port = 80, protocol = "tcp", cidr_blocks = ["0.0.0.0/0"] },
{ port = 443, protocol = "tcp", cidr_blocks = ["0.0.0.0/0"] },
{ port = 22, protocol = "tcp", cidr_blocks = ["10.0.0.0/8"] },
]
}
resource "aws_security_group" "web" {
name = "web-sg"
description = "Web security group"
vpc_id = var.vpc_id
dynamic "ingress" {
for_each = var.ingress_rules
content {
from_port = ingress.value.port
to_port = ingress.value.port
protocol = ingress.value.protocol
cidr_blocks = ingress.value.cidr_blocks
}
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
How It Works
dynamic "<block_label>" {
for_each = <collection> # list or map to iterate over
content {
# <block_label>.value → current item
# <block_label>.key → index (list) or key (map)
}
}
Nested Dynamic Blocks
resource "aws_lb_listener" "https" {
load_balancer_arn = aws_lb.main.arn
port = 443
protocol = "HTTPS"
ssl_policy = "ELBSecurityPolicy-TLS13-1-2-2021-06"
certificate_arn = var.cert_arn
dynamic "default_action" {
for_each = var.default_actions
content {
type = default_action.value.type
target_group_arn = default_action.value.target_group_arn
dynamic "fixed_response" {
for_each = default_action.value.type == "fixed-response" ? [1] : []
content {
content_type = "text/plain"
message_body = "OK"
status_code = "200"
}
}
}
}
}
💡 Production Tip: Don’t nest more than 2 levels of dynamic blocks. If you need deeper nesting, refactor into a module.
What’s Next?
Master advanced for_each patterns.