> ## Documentation Index
> Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# count vs for each

> Explains Terraform's count versus for_each, how they track resource identity, risks of index shifts, and best practices for stable resources.

This article compares Terraform's `count` and `for_each` meta-arguments. Both create multiple instances of a resource from a collection, but they record resource identity differently in the Terraform state. Choosing the wrong one can cause unexpected resource deletion and recreation in production—so understanding the differences is important.

Quick overview

* `count` creates instances indexed by number: `resource.example[0]`, `resource.example[1]`.
* `for_each` creates instances keyed by an element from the collection: `resource.example["key"]`.

Minimal usage examples

```hcl theme={null}
# count
count = length(var.items)

# for_each
for_each = toset(var.items)
```

How Terraform tracks identity

* count: Terraform tracks instances by numeric index. If the order of the input list changes, instances shift indices and Terraform may plan to destroy and recreate resources to match new indices.
* for\_each: Terraform tracks instances by key derived from the collection (element value for sets or keys for maps). As long as keys stay the same, resources remain stable even if the collection is reordered.

Concrete resource examples

Using `count` (index-based):

```hcl theme={null}
variable "items" {
  type    = list(string)
  default = ["rg-dev", "rg-prod"]
}

resource "aws_resource" "example_count" {
  count = length(var.items)

  name = var.items[count.index]
  # other configuration...
}
```

Using `for_each` (key-based):

```hcl theme={null}
variable "items" {
  type    = list(string)
  default = ["rg-dev", "rg-prod"]
}

resource "aws_resource" "example_each" {
  for_each = toset(var.items)

  name = each.key
  # other configuration...
}
```

Referencing created resources

* With `count`:
  * Inside the resource: `count.index`
  * Outside the resource: `aws_resource.example_count[0].id`, `aws_resource.example_count[1].id`

* With `for_each`:
  * Inside the resource: `each.key` (or `each.value` when iterating maps)
  * Outside the resource: `aws_resource.example_each["rg-dev"].id`, `aws_resource.example_each["rg-prod"].id`

Comparison table

|                 Feature |                      `count`                     |                   `for_each`                   |
| ----------------------: | :----------------------------------------------: | :--------------------------------------------: |
|       Identity tracking |           Numeric index (`0`, `1`, `2`)          |      Stable key (element value or map key)     |
|                Best for | Simple numeric repetition or ephemeral resources | Long-lived resources that need stable identity |
|     Behavior on reorder |  Risk of destroy/recreate due to index shifting  |     No recreation if keys remain unchanged     |
| Example state addresses |             `aws_resource.example[0]`            |        `aws_resource.example["rg-dev"]`        |
|          Recommendation |         Use when identity doesn't matter         |     Use as default for production resources    |

Behavior when inputs change

* count: Because instances are index-driven, inserting, removing, or reordering items in a list can change indices. Terraform will view shifted indices as different resources and may destroy/create instances to reconcile state.
* for\_each: Because instances are key-driven, reordering a collection does not affect existing resources. Adding or removing keys only adds or removes the specific instances affected.

Example scenario

Initial variable:

```hcl theme={null}
variable "items" {
  default = ["a", "b", "c"]
}
```

* With `count`, Terraform creates: `resource[0]` -> "a", `resource[1]` -> "b", `resource[2]` -> "c". If the list becomes `["b","a","c"]`, indices change and Terraform may recreate resources.
* With `for_each` (e.g., `for_each = toset(var.items)`), Terraform creates keys `"a"`, `"b"`, `"c"`. Reordering the list does not change keys or result in recreation.

Stable keys and metadata

When you need stable identifiers plus per-item attributes, prefer a map for `for_each`. Example:

```hcl theme={null}
variable "items_map" {
  type = map(object({
    cidr = string
  }))

  default = {
    "rg-dev"  = { cidr = "10.0.0.0/24" }
    "rg-prod" = { cidr = "10.0.1.0/24" }
  }
}

resource "aws_resource" "example_map" {
  for_each = var.items_map

  name = each.key
  cidr = each.value.cidr
}
```

Important nuances and gotchas

* `toset()` deduplicates and loses order. If duplicates in your list matter, do not convert to a set. Use a map keyed by a stable identifier or create explicit keys with `zipmap`.
* If you must use `count` with a list, keep the list ordering stable (for example, sort it explicitly) to avoid index shifts.
* `for_each` requires keys to be unique. For lists use `toset()` only when duplicates aren’t significant; otherwise, map elements to unique keys.
* When converting a list to a map for stable keys, you can use `zipmap()` to build explicit keys.

Best practices

* Default to `for_each` for long-lived, production resources where stable identity matters.
* Use `count` for simple numeric repetition (like creating N identical test resources) or when you intentionally want index semantics.
* Use maps with `for_each` for predictable keys and to attach metadata per item.
* Be deliberate when changing the input collection for an existing resource block; plan and review `terraform plan` to avoid accidental destruction.

Reference links

* [Terraform docs: count](https://www.terraform.io/language/meta-arguments/count)
* [Terraform docs: for\_each](https://www.terraform.io/language/meta-arguments/for_each)
* [Terraform docs: zipmap](https://www.terraform.io/language/functions/zipmap)

<Callout icon="lightbulb" color="#1CB2FE">
  Prefer `for_each` for stable, long-lived resources and `count` for simple numeric repetition. Remember that `toset()` removes duplicates and discards order—use maps or `zipmap()` to create deterministic keys when you need stable identities and per-item metadata.
</Callout>

With this, the comparison between `count` and `for_each` is complete.

<CardGroup>
  <Card title="Watch Video" icon="video" cta="Learn more" href="https://learn.kodekloud.com/user/courses/terraform-on-azure/module/fb5019bb-df21-4583-818e-6dae40fde2ec/lesson/f4c88663-77b9-419e-8725-5389e3e7e2fa" />
</CardGroup>
