Terraform Advanced — Advanced for_each Patterns

1 minute read

Go beyond simple for_each with maps of objects, toset(), and complex iteration patterns.


count vs for_each — When to Use Each

Feature count for_each
State key resource[0], resource[1] resource["name"]
Reorder safe? ❌ No — indices shift ✅ Yes — keys are stable
Best for Simple on/off toggle Named resources
Delete one? Shifts all indices Removes only that key

⚠️ Warning: If you use count with a list and remove an item from the middle, Terraform will destroy and recreate all subsequent resources because their indices shift.


for_each with a Map of Objects

# ✅ Best practice: map of objects
variable "s3_buckets" {
  type = map(object({
    versioning = bool
    acl        = string
  }))
  default = {
    "logs"    = { versioning = true,  acl = "private" }
    "assets"  = { versioning = false, acl = "private" }
    "backups" = { versioning = true,  acl = "private" }
  }
}

resource "aws_s3_bucket" "this" {
  for_each = var.s3_buckets
  bucket   = "${var.project}-${each.key}"
}

resource "aws_s3_bucket_versioning" "this" {
  for_each = { for k, v in var.s3_buckets : k => v if v.versioning }
  bucket   = aws_s3_bucket.this[each.key].id

  versioning_configuration {
    status = "Enabled"
  }
}

State keys: aws_s3_bucket.this["logs"], aws_s3_bucket.this["assets"], etc.


for_each with toset()

When you have a list but need for_each:

variable "iam_users" {
  type    = list(string)
  default = ["alice", "bob", "carol"]
}

resource "aws_iam_user" "this" {
  for_each = toset(var.iam_users)
  name     = each.value
}

for Expressions (Transform Data)

# Transform a map: uppercase all bucket names
locals {
  upper_buckets = { for k, v in var.s3_buckets : upper(k) => v }
}

# Filter: only versioned buckets
locals {
  versioned_only = { for k, v in var.s3_buckets : k => v if v.versioning }
}

# Flatten: create a flat list from nested structure
locals {
  all_subnets = flatten([
    for env, config in var.environments : [
      for subnet in config.subnets : {
        env    = env
        cidr   = subnet.cidr
        az     = subnet.az
      }
    ]
  ])
}

What’s Next?

Learn splat expressions.

← Dynamic Blocks Splat Expressions →

Updated: