Terraform Advanced — Advanced HCL Exercises

1 minute read

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 from count resources; use values() for for_each
  • try/can/one prevent runtime errors on optional attributes
  • Locals precompute complex expressions; map lookups replace nested conditionals

← Locals and Conditionals Chapter 2: Remote State →

Updated: