TFLAB16-Collection Functions: Maps, Lists and Transformations

Collection Functions: Maps, Lists and Transformations

🔧 Terraform Core ⭐ Intermediate merge() flatten() lookup() keys() / values() coalesce() contains()

Scenario

You're managing tags across multiple resources, flattening nested lists of subnets, looking up values from configuration maps, and handling defaults for optional values. These are the bread-and-butter collection operations you'll use in every Terraform config.

Your Objectives
  • Use merge() to combine multiple maps (tags).
  • Use flatten() to collapse a list of lists.
  • Use lookup() to get a map value with a default fallback.
  • Use keys() and values() to extract map keys and values.
  • Use coalesce() to pick the first non-null/non-empty value.
  • Use contains() to check list membership.

Additional Context

Collection functions operate on the three collection types: list, map, and set. They're pure functions — they return a new value without modifying the input.

main.tf

terraform {
  required_version = ">= 1.5.0"
}

variable "instance_sizes" {
  type = map(string)
  default = {
    dev     = "t3.micro"
    staging = "t3.small"
    prod    = "t3.large"
  }
}

variable "override_region" {
  type    = string
  default = ""
}

locals {
  # ─── merge(): combine multiple maps ───
  # Later maps override earlier ones for duplicate keys
  default_tags = { ManagedBy = "terraform", Team = "platform" }
  env_tags     = { Environment = "dev", CostCenter = "eng-123" }
  all_tags     = merge(local.default_tags, local.env_tags)
  # Result: { ManagedBy = "terraform", Team = "platform", Environment = "dev", CostCenter = "eng-123" }

  # ─── flatten(): collapse nested lists ───
  subnet_groups = [["10.0.1.0/24", "10.0.2.0/24"], ["10.0.3.0/24"]]
  all_subnets   = flatten(local.subnet_groups)
  # Result: ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]

  # ─── lookup(): get value from map with default ───
  dev_size     = lookup(var.instance_sizes, "dev", "t3.micro")       # "t3.micro"
  unknown_size = lookup(var.instance_sizes, "dr", "t3.nano")         # "t3.nano" (default)

  # ─── keys() and values(): extract from map ───
  env_names = keys(var.instance_sizes)      # ["dev", "prod", "staging"]
  sizes     = values(var.instance_sizes)    # ["t3.micro", "t3.large", "t3.small"]

  # ─── coalesce(): first non-empty, non-null value ───
  region = coalesce(var.override_region, "us-east-1")
  # If override_region = "" → returns "us-east-1"

  # ─── contains(): check if list includes a value ───
  has_prod = contains(keys(var.instance_sizes), "prod")   # true
  has_dr   = contains(keys(var.instance_sizes), "dr")     # false

  # ─── element(): get list item by index (wraps around) ───
  first_subnet = element(local.all_subnets, 0)   # "10.0.1.0/24"

  # ─── zipmap(): create map from two lists ───
  labels = ["name", "env", "region"]
  vals   = ["myapp", "dev", "us-east-1"]
  zipped = zipmap(local.labels, local.vals)
  # Result: { name = "myapp", env = "dev", region = "us-east-1" }

  # ─── length(): count elements ───
  tag_count   = length(local.all_tags)
  subnet_count = length(local.all_subnets)
}

outputs.tf

output "merged_tags"   { value = local.all_tags }
output "flattened"     { value = local.all_subnets }
output "lookup_dev"    { value = local.dev_size }
output "lookup_unknown"{ value = local.unknown_size }
output "env_names"     { value = local.env_names }
output "region"        { value = local.region }
output "has_prod"      { value = local.has_prod }
output "has_dr"        { value = local.has_dr }
output "zipped"        { value = local.zipped }
output "tag_count"     { value = local.tag_count }
output "subnet_count"  { value = local.subnet_count }

Workflow Commands

terraform init && terraform apply

# Override the region
terraform apply -var="override_region=eu-west-1"

terraform output -json
✓ Expected Outputs
env_names      = tolist(["dev", "prod", "staging"])
flattened      = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
has_dr         = false
has_prod       = true
lookup_dev     = "t3.micro"
lookup_unknown = "t3.nano"
merged_tags    = {
  "CostCenter"  = "eng-123"
  "Environment" = "dev"
  "ManagedBy"   = "terraform"
  "Team"        = "platform"
}
region         = "us-east-1"
subnet_count   = 3
tag_count      = 4
zipped         = { "env" = "dev", "name" = "myapp", "region" = "us-east-1" }