Skip to content

7 — count and for_each

Folder: none yet — paste this into a scratch root.

Both create multiple instances from one resource block. The difference is how instances are addressed, and that difference decides what happens when the collection changes.

count — positional

resource "local_file" "multi" {
  count    = 3
  filename = "/tmp/pet-${count.index}.txt"
  content  = "This is pet file number ${count.index}"
}
terraform apply
ls /tmp/pet-*.txt
terraform state list

Three separate addressable instances from one block:

local_file.multi[0]
local_file.multi[1]
local_file.multi[2]

The flaw: change count = 3 to count = 1 and re-plan. Terraform proposes destroying multi[1] and multi[2] — fine. But the real problem shows up when you remove an item from the middle of a list driving the count. Every subsequent index shifts, and Terraform reads a shifted index as a different resource: destroy and recreate, not rename.

for_each — keyed

variable "pet_names" {
  type    = set(string)
  default = ["fido", "whiskers", "rex"]
}

resource "local_file" "named_pets" {
  for_each = var.pet_names
  filename = "/tmp/pet-${each.value}.txt"
  content  = "Meet ${each.value}, a very good pet."
}
terraform apply
terraform state list

Addresses are keyed, not indexed:

local_file.named_pets["fido"]
local_file.named_pets["rex"]
local_file.named_pets["whiskers"]

The destructive test

This single experiment is the entire argument for for_each. Remove "whiskers" from the set and re-plan:

terraform plan

Only named_pets["whiskers"] is destroyed. "fido" and "rex" are untouched. Contrast with the count version, where removing a middle element churns everything after it.

Which to use

Prefer for_each whenever items can be added to or removed from the middle of a collection. Reserve count for two cases:

  • N genuinely identical copies where position carries no meaning
  • the conditional 0/1 pattern — see Conditionals

The type matters

count needs an ordered list(string); for_each needs a set(string) or a map. The core GCP root keeps both declarations side by side with the count version commented out directly above the for_each version, specifically so you can switch between them and diff terraform state list. The GCP versions of these two sections are Lab 4 and Lab 5.

for_each also applies to module blocks, not just resources — Lab 13.

Not the same as dynamic

count and for_each repeat whole resources. A dynamic block repeats a nested block inside one resource. Different mechanism, easily confused — see Lab 12. There are only two in this repository, both needing a cloud resource to be worth writing: the lifecycle_rule generator in the core GCP root and dynamic "rule" in the sandbox's backend/infra/rbac.tf.

Key takeaway

count addresses instances by position, for_each by key, and that one difference decides whether editing the middle of a collection is a rename or a rebuild. Default to for_each; reserve count for N identical copies where position means nothing, and for the 0/1 conditional.


Theory: §9 count, for_each & dynamic · Quiz: Variables, expressions & guards · Next: Conditionals