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
- Storage Errors — FailedMount, Pending PVCs, Multi-Attach and Full Volumes
- Strangler Fig
- Team Topologies
- Tekton
- Terraform — Production Errors, Mistakes and Recovery
- Terraform & OpenTofu
- Terraform Essentials — Plan and Apply, Variables, Outputs, Data Sources, and Sharing State at Scale
- Terraform in CI/CD Pipelines
- Terraform Remote State — S3 + DynamoDB
- Test Pyramid
- The C/C++ Compilation Model
- The flux CLI
Start free on Cariara · Coding problems · System design studio