how/resources
Two ways to create N identical resources. One indexes by number, the other by key. Deleting the middle of the array produces completely different plans.
count and for_each are two ways to create several instances
of one resource. From the outside they look similar:
# count
resource "aws_iam_user" "users" {count = length(var.users)
name = var.users[count.index]
}
# for_each
resource "aws_iam_user" "users" {for_each = toset(var.users)
name = each.key
}
The difference is hidden in state. count writes resources under indexes
[0], [1], [2]. for_each writes them under keys ["alice"], ["bob"],
["carol"]. That changes everything when you delete an element from the middle.
Hit ▶ and watch how the same var.users with a deleted
middle breaks the count version and leaves for_each untouched.
On the left, a standard count with an index into the array. On the right,
for_each over toset(...), because for_each wants
a collection with unique keys.
For the goal of "create three IAM users" both versions are equivalent. The difference shows up later, when we try to change something.
recap
What to remember:
count indexes by position in the array. Delete an element
from the middle and every element after it shifts one position
left. To Terraform that is a destroy + create for
each shifted index.for_each indexes by key. Delete the key "bob" and
Terraform sees exactly one change: destroy
users["bob"]. The rest are untouched.count recreates half of your
IAM users, and those users may have attached access keys,
policies, and MFA tokens. Recreation means losing those attachments.for_each requires the values to be unique (which is why
you usually use toset(...)) and known at plan time. If a key is
a uuid() or the result of aws_instance.x.id for a resource that
does not exist yet, Terraform fails with The "for_each" map includes keys derived from resource attributes.count to for_each without destroy/create is
exactly the case where you need a moved block:
moved { from = aws_iam_user.users[1] to = aws_iam_user.users["bob"] }.
See tf-moved-block.for_each always, except when you
genuinely need numeric indexes (an array without
natural unique keys).Next: tf-resource-lifecycle on controlling resource behavior at the HCL level.