Lesson 14 / 25

count Versus for_each

Index-based versus key-based instances.

Stable keys avoid surprises

count = N creates N instances addressed by index (by_index[0], [1], ...). for_each over a map or set creates instances addressed by key (by_key["web"]). Removing an item from the middle of a count list shifts every later index, so Terraform replaces or destroys resources you did not mean to touch; with for_each, removing a key affects only that key. Use count for "N identical things" or a 0/1 toggle, and for_each when items have identities.

Repeat without copy-paste

count and for_each create many resources; modules package reusable infrastructure.

Three ideas: count versus for_each, modules, module sources and versions.
Figure 5.1 — count, for_each and modules.

The count and for_each demo

Three services created both ways.

variable "services" {
  type    = map(number)
  default = { web = 8080, api = 3000, worker = 9000 }
}
variable "names" {
  type    = list(string)
  default = ["web", "api", "worker"]
}

resource "local_file" "by_key" {
  for_each = var.services
  filename = "${path.module}/out/foreach-${each.key}.txt"
  content  = "port=${each.value}\n"
}

resource "local_file" "by_index" {
  count    = length(var.names)
  filename = "${path.module}/out/count-${count.index}.txt"
  content  = "service=${var.names[count.index]}\n"
}

Removing "api" from both lists, run

I ran this with Terraform 1.16.4 and the hashicorp/local 2.9.1 and hashicorp/random 3.9.1 providers, which manage local files and random values, so no cloud account was needed; each example starts from a fresh directory. With for_each, removing the api key destroys only by_key["api"]. With count, removing "api" from the middle shifts "worker" from index 2 to 1, so index 1 is replaced and index 2 destroyed: 3 destroys instead of 1.

terraform plan -no-color -var 'services={web=8080,worker=9000}' -var 'names=["web","worker"]' | grep -E "will be|must be|Plan:"

Output:

  # local_file.by_index[1] must be replaced
  # local_file.by_index[2] will be destroyed
  # local_file.by_key["api"] will be destroyed
Plan: 1 to add, 0 to change, 3 to destroy.

Quick check: Why is for_each usually safer than count for named items?

  • for_each never destroys anything
  • for_each is faster to type
  • count cannot create resources
  • Removing one item affects only that key instead of shifting indexes
Answer

Removing one item affects only that key instead of shifting indexes — Stable addresses prevent accidental churn.