Terraform count vs for_each

count creates indexed copies (aws_instance.web[0], [1], [2]). for_each creates named resources by key (aws_instance.web["dev"], ["prod"]). Production rule: use count for identical copies, for_each for named/environment resources because changing list ORDER with count destroys and recreates resources — for_each uses stable keys, safer for production.

Overview

Two ways to make many instances

Both count and for_each create multiple instances of a resource or module. They differ in how they identify each instance, and that difference has major production safety implications.

count: indexed resources

This creates aws_instance.web[0], aws_instance.web[1] and aws_instance.web[2].

The critical count danger

Terraform identifies count instances by index. With count = length(var.environments) and var.environments = ["dev", "test", "prod"], reordering the list to ["prod", "dev", "test"] makes index [0] "prod" instead of "dev". Terraform plans to destroy web[0] and recreate it.

> [!WARNING] In production this destroys a real instance just because someone changed the order of a list.

for_each: named resources

This creates aws_instance.web["dev"], aws_instance.web["test"] and aws_instance.web["prod"]. The map key is the stable resource identifier.

Production rule

  • for_each — Any resource that represents a named environment, team, region or tenant
  • count — Purely identical, anonymous resources, for example count = 3 worker nodes with no individual identity

> [!IMPORTANT] Converting count to for_each requires moving state entries (terraform state mv), which is not trivial. Get it right from the start.

Key concepts

count.index
The zero-based index of the current resource instance when using count. Used to create unique names: Name = "web-${count.index}". Danger: index depends on position in the list, not the value.
each.key / each.value
Available inside a for_each resource. each.key is the map key or set element. each.value is the map value (for maps) or same as each.key (for sets). Used to configure each instance differently.
toset()
Converts a list to a set for use with for_each. toset(["dev", "prod"]) creates resources named by the string values. Removes duplicates. Use when you have a list of unique strings and want stable for_each keys.
Resource address
How Terraform identifies a specific resource instance. count: aws_instance.web[0]. for_each: aws_instance.web["dev"]. Matters for: terraform state mv, terraform import, and understanding plan output.
Index stability
The core difference. count uses positional index — fragile with list reordering. for_each uses named key — stable regardless of what other keys are added or removed. Key stability = safer production changes.

When to use it

  • for_each: creating resources per environment (dev/staging/prod), per region, per team, per service
  • count: creating N identical, anonymous resources with no individual identity (batch worker nodes, identical EC2 in an ASG supplement)
  • for_each with toset(): iterating over a set of strings where you need one resource per value
  • for_each with a map: when each resource needs different configuration (instance_type, size, name)

Interview questions

  • Why is for_each safer than count for production resources?

Sign up free to read the full topic

More DevOps topics

Start free on Cariara · Coding problems · System design studio