Terraform Advanced — Advanced HCL Exercises
Practice advanced HCL patterns.
Exercise 1 — Dynamic Security Group
Task: Write a security group resource using a dynamic block that generates ingress rules from this variable:
variable "rules" {
default = {
http = { port = 80, cidrs = ["0.0.0.0/0"] }
https = { port = 443, cidrs = ["0.0.0.0/0"] }
ssh = { port = 22, cidrs = ["10.0.0.0/8"] }
}
}
Answer
resource "aws_security_group" "web" {
name = "web-sg"
vpc_id = var.vpc_id
dynamic "ingress" {
for_each = var.rules
content {
from_port = ingress.value.port
to_port = ingress.value.port
protocol = "tcp"
cidr_blocks = ingress.value.cidrs
description = ingress.key
}
}
}
Exercise 2 — Conditional Module
Task: Call a monitoring module only when var.enable_monitoring is true.
Answer
module "monitoring" {
count = var.enable_monitoring ? 1 : 0
source = "./modules/monitoring"
cluster_id = aws_ecs_cluster.main.id
}
# Access output safely:
output "dashboard_url" {
value = one(module.monitoring[*].dashboard_url)
}
Exercise 3 — Flatten and for_each
Task: Given this variable, create one subnet per item using for_each:
variable "subnets" {
default = {
us-east-1a = ["10.0.1.0/24", "10.0.2.0/24"]
us-east-1b = ["10.0.3.0/24", "10.0.4.0/24"]
}
}
Answer
locals {
flat_subnets = flatten([
for az, cidrs in var.subnets : [
for cidr in cidrs : {
az = az
cidr = cidr
}
]
])
subnet_map = { for s in local.flat_subnets : "${s.az}-${s.cidr}" => s }
}
resource "aws_subnet" "this" {
for_each = local.subnet_map
vpc_id = var.vpc_id
cidr_block = each.value.cidr
availability_zone = each.value.az
}
Chapter Summary
- Dynamic blocks generate repeated nested blocks from collections
- for_each with maps creates stable, name-keyed resources (safer than
count) - Splat
[*]extracts attributes fromcountresources; usevalues()forfor_each - try/can/one prevent runtime errors on optional attributes
- Locals precompute complex expressions; map lookups replace nested conditionals