DevOpsInterviewPrep logo
Infrastructure as Code & Configuration / 01
hard★ EssentialNewHashiCorpGoogleAmazon

count versus for_each in Terraform: when does list ordering cause a destroy and recreate?

The Terraform question that actually filters. Everyone knows both meta-arguments exist; the answer is about how state addresses resources, and why an index is a fragile key.

Updated Sep 2026 · Grounded in researched DevOps, SRE and platform engineering interview loops, written to a senior-engineer editorial bar, and never padded to hit a word count.

TL;DR: count addresses resources by positional index, so removing an item from the middle of the list shifts every later item's address and Terraform reassociates later addresses with different configuration. Whether each update is in place or a replacement depends on the attributes and provider. for_each addresses by a stable string key, so removing one item touches only that one.

How to approach it

Frame it as a state addressing problem, not a syntax preference. Then walk a concrete plan. This question rewards showing the actual resource addresses.

A strong answer

Terraform records each instance in state under an address. With count that address is aws_instance.web[0], [1], [2]. The index is the identity. With for_each it is aws_instance.web["api"], ["worker"], and the map key is the identity.

rendering diagram…

Now delete the middle element of a three-item list under count. The list becomes two items, so [1] is now what used to be [2], and [2] no longer exists. Terraform compares desired to state and concludes that instance 1 must change to a different configuration and instance 2 must be destroyed. A changed Name tag on an EC2 instance is normally an in-place update, as in the fragments below. Changing an immutable attribute can instead force replacement. The unwanted identity reassignment is the issue in either case.

These are alternative addressing fragments, not one runnable module; each EC2 resource also needs a suitable AMI and instance type. Do not declare both with the same address.

# fragile: identity is the position in the list
variable "names" { default = ["api", "worker", "cron"] }
resource "aws_instance" "web" {
  count = length(var.names)
  tags  = { Name = var.names[count.index] }
}
# removing "worker" -> plans changes to 1 and destroys 2

# stable: identity is the key
resource "aws_instance" "web" {
  for_each = toset(var.names)
  tags     = { Name = each.key }
}
# removing "worker" -> destroys exactly aws_instance.web["worker"]

The rule I use: count is for a homogeneous fleet where the members have no identity, and for the conditional-creation idiom count = var.enabled ? 1 : 0. Everything else uses for_each. If the resources have names, roles, or attached state, they have identity, and identity belongs in the key.

The migration matters too. Moving an existing resource from count to for_each is not a no-op, because every state address changes. You either use terraform state mv 'aws_instance.web[0]' 'aws_instance.web["api"]' for each instance, or in newer versions a moved block in the configuration, which is reviewable and survives being run by someone else.

One further trap: for_each keys must be known at plan time. Keying on an attribute of a not-yet-created resource produces the "Invalid for_each argument" error, and the fix is to key on something static such as an input variable rather than a computed value.

What interviewers probe next

"How would you fix this without downtime on a running fleet?" moved blocks or state mv, verified with a plan that shows zero changes. A plan that shows zero changes is the proof; unexpected changes require investigation: they may reflect drift or input changes as well as an incorrect mapping.

"When is count still the right answer?" Conditional creation, and identical members with no individual identity. Say the second part explicitly, because it is the honest limit of the rule.

"What happens if two keys collide in a toset?" The set deduplicates, so you silently get fewer resources than list entries. Worth a validation if the input is user-supplied.

Common mistakes

Answering "use for_each, it is better" with no mechanism. The interviewer is testing whether you know why, because the why is what lets you predict the plan.

Forgetting the migration cost and implying you can swap the meta-argument in place. That answer has caused real outages.

Describing the difference as a style choice. It is a state identity choice, and state identity is what Terraform is.

Sources: Terraform count.

That one was free, and so are 10 answers per topic without an account. Signing in doubles that to 20, keeps your bookmarks, and tracks which topics you keep getting wrong.one Google click · no card · nothing to cancel
HOW DID IT GO?
0
UP NEXT ON YOUR JOURNEY
DISCUSSION · 0

Nothing here yet. Say how you would answer it.