The Index Problem
Creating multiple similar resources is one of the most common Terraform tasks. Terraform provides two mechanisms for this: count and for_each. They look similar but have fundamentally different behavior, and choosing the wrong one causes destructive, unintended infrastructure changes.
Option A: count — Simple but Fragile
count creates N copies of a resource, addressed by numeric index:
variable "users" {
default = ["alice", "bob", "charlie"]
}
resource "aws_iam_user" "u" {
count = length(var.users)
name = var.users[count.index]
}
Terraform tracks these as aws_iam_user.u[0], aws_iam_user.u[1], aws_iam_user.u[2].
The trap: Remove "alice" from the beginning of the list. The list becomes ["bob", "charlie"]. Now:
- Index 0 =
"bob"(was"alice") → Terraform destroysalice, createsbob - Index 1 =
"charlie"(was"bob") → Terraform destroysbob, createscharlie - Index 2 no longer exists → Terraform destroys
charlie
Three destructive operations to remove one user. If these were database users with attached permissions, you just caused an outage.
Option B: for_each — Stable and Correct
for_each creates resources keyed by a string identifier, not a numeric index:
resource "aws_iam_user" "u" {
for_each = toset(var.users)
name = each.key
}
Terraform tracks these as aws_iam_user.u["alice"], aws_iam_user.u["bob"], aws_iam_user.u["charlie"].
Remove "alice" from the list. Terraform sees:
aws_iam_user.u["alice"]no longer in config → destroy only Aliceaws_iam_user.u["bob"]unchanged → no actionaws_iam_user.u["charlie"]unchanged → no action
One targeted destroy. Bob and Charlie are untouched.
for_each with maps is even more powerful — the key doesn't have to be a bare identifier from a list, it can index into a map of structured per-resource configuration, so each instance can carry its own settings (region, feature flags, sizing) while still being addressed by a stable name rather than a position.
Rule of thumb: Use count only when the number of resources is truly numeric and order-independent (e.g., count = var.replica_count for identical read replicas). Use for_each whenever resources have distinct identities.