Explain `count` vs `for_each` — when should you use each?

5 minintermediateterraformcountfor_eachmeta-arguments

Quick Answer

`count` creates N copies of a resource indexed by number (`resource[0]`, `resource[1]`); it's simplest for a fixed quantity of identical resources, but inserting/removing an item in the middle of the list shifts every subsequent index, causing Terraform to destroy/recreate resources unnecessarily. `for_each` iterates over a map or set of strings, keying each instance by a stable key (`resource["web"]`) instead of a position, so adding/removing one entry only affects that entry. Prefer `for_each` whenever instances are distinguishable (different names, configs) and stable identity matters; use `count` mainly for truly identical, position-independent replicas or simple conditional (0/1) resource creation.

Detailed Answer

Both count and for_each let a single resource block create multiple instances, but they key those instances differently, and that difference has real consequences during updates.

count — indexed by position

resource "aws_instance" "web" {
  count         = 3
  ami           = var.ami_id
  instance_type = "t3.micro"
  tags = { Name = "web-${count.index}" }
}

This creates aws_instance.web[0], aws_instance.web[1], aws_instance.web[2]. The problem: if you remove the first item from a list-driven count (say, count = length(var.names) and you delete names[0]), every subsequent index shifts down by one — Terraform sees instance [1]'s new contents differ from what used to be at [1], and may plan to destroy/recreate resources that didn't conceptually change at all, just moved position.

for_each — keyed by a stable identifier

resource "aws_instance" "web" {
  for_each      = toset(["blue", "green"])
  ami           = var.ami_id
  instance_type = "t3.micro"
  tags = { Name = "web-${each.key}" }
}

This creates aws_instance.web["blue"] and aws_instance.web["green"]. Removing "blue" from the set only affects aws_instance.web["blue"]"green" is untouched, because its identity is its key, not its position.

When to use which

  • Use for_each whenever instances are distinguishable — different names, different configuration per instance, or anything from a map/set that could grow or shrink over time. It's the safer default for anything beyond the simplest cases.
  • Use count for:
    • A simple conditional resource: count = var.create_bucket ? 1 : 0 (0 or 1 instances — no ordering issue since there's at most one).
    • Truly identical, position-independent replicas where you genuinely don't care about individual identity (rare in practice).

Interview-ready summary

count = "how many, indexed by number"; for_each = "which ones, indexed by stable key." Prefer for_each for anything with real identity, to avoid unintended destroy/recreate churn when the underlying collection changes.

Related Resources