By Fatskills Exam Guides Team — the exam nerds behind 28,500+ quizzes and 2.1M practice questions across 500+ global exams.
For engineers who need to write clean, maintainable IaC—fast.
You’re managing a Terraform module for an AWS ALB (Application Load Balancer). The team needs to support two environments: - Dev: No WAF (Web Application Firewall) rules.- Prod: Strict WAF rules (block SQLi, XSS, and rate-limiting).
Without dynamic blocks and conditional expressions, you’d have to: - Copy-paste the entire aws_lb_listener block twice (once for dev, once for prod).- Manually comment/uncomment WAF rules when switching environments.- Risk drift when someone forgets to update both blocks.
aws_lb_listener
Dynamic blocks let you generate nested configurations dynamically (e.g., WAF rules, security group rules, IAM policies).Conditional expressions let you toggle resources or attributes on/off (e.g., "Only create a NAT Gateway if var.env == "prod"").
var.env == "prod"
Why this matters in production:- DRY (Don’t Repeat Yourself): One block, many configurations. Change once, deploy everywhere.- Safety: No manual edits = fewer human errors.- Scalability: Add 10 WAF rules? Just update a list variable—no code changes.- Auditability: Terraform plan shows exactly what will change (e.g., "Adding 3 WAF rules").
Real-world scenario:You inherit a Terraform repo where every aws_security_group has 20+ hardcoded rules. A new compliance requirement adds 5 more rules—but only for prod. With dynamic blocks, you refactor the SG in 10 minutes. Without them? You’d spend hours copy-pasting and risk breaking dev.
aws_security_group
ingress
egress
rule
hcl dynamic "<BLOCK_TYPE>" { for_each = <COLLECTION> # list, map, or set content { <ATTRIBUTE> = <VALUE> # Use `each.key` or `each.value` to reference the collection } }
condition ? true_val : false_val
hcl count = var.env == "prod" ? 1 : 0 # Creates 1 resource in prod, 0 otherwise
for_each
count
aws_s3_bucket.example["logs"].arn
count.index
each.key
each.value
dynamic
[*]
aws_instance.web[*].private_ip
try()
can()
try(var.port, 80)
true
can(aws_s3_bucket.example)
var.waf_rules
dev
prod
aws_lb
aws_wafv2_web_acl
Deploy an ALB with: - Dev: No WAF rules.- Prod: 3 WAF rules (SQLi, XSS, rate-limiting).
Create variables.tf:
variables.tf
variable "env" { description = "Environment (dev or prod)" type = string default = "dev" } variable "waf_rules" { description = "List of WAF rules (only used in prod)" type = list(object({ name = string priority = number action = string # "ALLOW", "BLOCK", "COUNT" rule = string # "AWS-AWSManagedRulesSQLiRuleSet", etc. })) default = [] }
Create main.tf:
main.tf
resource "aws_lb" "example" { name = "example-alb-${var.env}" internal = false load_balancer_type = "application" subnets = ["subnet-12345678", "subnet-87654321"] # Replace with your subnets } resource "aws_wafv2_web_acl" "example" { count = var.env == "prod" ? 1 : 0 # Only create in prod name = "example-waf-${var.env}" scope = "REGIONAL" default_action { allow {} } dynamic "rule" { for_each = var.waf_rules content { name = rule.value.name priority = rule.value.priority action { dynamic "block" { for_each = rule.value.action == "BLOCK" ? [1] : [] content {} } dynamic "allow" { for_each = rule.value.action == "ALLOW" ? [1] : [] content {} } dynamic "count" { for_each = rule.value.action == "COUNT" ? [1] : [] content {} } } statement { managed_rule_group_statement { name = rule.value.rule vendor_name = "AWS" } } visibility_config { cloudwatch_metrics_enabled = true metric_name = rule.value.name sampled_requests_enabled = true } } } visibility_config { cloudwatch_metrics_enabled = true metric_name = "example-waf-${var.env}" sampled_requests_enabled = true } } resource "aws_wafv2_web_acl_association" "example" { count = var.env == "prod" ? 1 : 0 # Only associate in prod resource_arn = aws_lb.example.arn web_acl_arn = aws_wafv2_web_acl.example[0].arn }
Run:
terraform init terraform plan -var="env=dev"
Expected output: - ALB is created.- No WAF resources are created.
Create prod.tfvars:
prod.tfvars
env = "prod" waf_rules = [ { name = "AWS-AWSManagedRulesSQLiRuleSet" priority = 1 action = "BLOCK" rule = "AWS-AWSManagedRulesSQLiRuleSet" }, { name = "AWS-AWSManagedRulesCommonRuleSet" priority = 2 action = "BLOCK" rule = "AWS-AWSManagedRulesCommonRuleSet" }, { name = "RateLimit100" priority = 3 action = "BLOCK" rule = "RateLimit100" # Custom rule (not shown here) } ]
terraform plan -var-file="prod.tfvars"
Expected output: - ALB is created.- WAF ACL with 3 rules is created and associated with the ALB.
example-waf-prod
hcl dynamic "statement" { for_each = var.iam_policies content { actions = statement.value.actions resources = statement.value.resources } }
aws_secretsmanager_secret_version
vault_generic_secret
hcl resource "aws_nat_gateway" "example" { count = var.env == "prod" ? 1 : 0 }
hcl instance_type = try(var.instance_sizes[var.env], "t3.micro")
hcl name = "sg-${var.env}-${each.key}"
hcl tags = merge(var.tags, { Name = "resource-${each.key}" })
hcl dynamic "alarm" { for_each = var.monitored_resources content { alarm_name = "alarm-${alarm.key}" metric_name = alarm.value.metric } }
hcl enable_access_logs = var.env == "prod" ? true : false
aws_s3_bucket.example
name = "bucket-${each.key}"
var.env
try(var.waf_rules, [])
dynamic "ingress"
for_each = var.rules
count = var.env == "prod" ? 1 : 0
aws_instance.web["app1"].id
Exam trap: If the question mentions "recreating resources when order changes," the answer is for_each.
Dynamic blocks vs for_each:
aws_instance
"You need to deploy an ALB with WAF rules only in prod. The WAF rules are defined in a list variable. How would you implement this?" Answer: 1. Use count = var.env == "prod" ? 1 : 0 for the WAF ACL.2. Use dynamic "rule" with for_each = var.waf_rules to generate rules.
dynamic "rule"
for_each = var.waf_rules
Challenge: Write a Terraform module that creates: - 1 EC2 instance in dev.- 3 EC2 instances in prod (with unique names).- A security group with dynamic ingress rules (ports 22, 80, 443).
Solution:
variable "env" { type = string } variable "ingress_ports" { type = list(number) default = [22, 80, 443] } resource "aws_instance" "example" { for_each = var.env == "prod" ? { for i in range(3) : "instance-${i}" => i } : { "instance-0" = 0 } ami = "ami-0c55b159cbfafe1f0" # Amazon Linux 2 instance_type = "t3.micro" tags = { Name = each.key } } resource "aws_security_group" "example" { name = "sg-${var.env}" dynamic "ingress" { for_each = var.ingress_ports content { from_port = ingress.value to_port = ingress.value protocol = "tcp" cidr_blocks = ["0.0.0.0/0"] } } }
Why it works: - for_each creates 1 instance in dev (instance-0) and 3 in prod (instance-0, instance-1, instance-2).- Dynamic ingress block generates rules for all ports in var.ingress_ports.
instance-0
instance-1
instance-2
var.ingress_ports
dynamic "<BLOCK>" { for_each = <COLLECTION> content { ... } }
dynamic "ingress" { for_each = var.ports content { from_port = each.value } }
for_each = <MAP_OR_SET>
for_each = var.instances
name = "resource-${each.key}"
try(<EXPRESSION>, <DEFAULT>)
<RESOURCE>[*].<ATTRIBUTE>
count = length(var.ports)
for_each = toset(var.ports)
ALLOW
block
default_action { block {} }
Join 4M+ learners. Unlock unlimited quizzes, wrong-answer tracking, flashcards + reminders, study guides, and 1-on-1 challenges.