count and for_each

Terraform · Practical · 12 min

Repetition is everywhere, and choosing count where for_each belongs causes destructive plans later.

Two meta-arguments create multiple instances from one block:

  • count: count = 3 makes aws_instance.web[0], [1], [2], with count.index inside. Also the standard way to make a resource conditional: count = var.enabled ? 1 : 0.
  • for_each: takes a map or a set of strings and makes one instance per key: aws_iam_user.u["ana"], with each.key and each.value inside.

Why it matters: instances are tracked by index (count) or key (for_each). With count over a list ["a", "b", "c"], removing "a" shifts "b" to index 0 and "c" to 1, so Terraform plans to change or replace b and c and destroy the last one. With for_each, removing "a" destroys only "a". Rule of thumb: use count for N identical things or on/off switches; use for_each whenever items have identities.

for_each keys must be known at plan time: you can't key on an attribute generated during apply, such as an ID. Use names from your input. Convert a list with toset(var.names), or a list of objects with { for o in var.items : o.name => o }.

Refer to all instances with splat (aws_instance.web[*].id, count only) or a for expression ([for u in aws_iam_user.u : u.arn], works for both). Both meta-arguments also work on module blocks.

Example

variable "users" {
  type    = set(string)
  default = ["ana", "bo", "cy"]
}

variable "create_bastion" {
  type    = bool
  default = false
}

resource "terraform_data" "user" {
  for_each = var.users                # address: terraform_data.user["bo"]
  input    = "user-${each.key}"
}

resource "terraform_data" "bastion" {
  count = var.create_bastion ? 1 : 0  # conditional resource
  input = "bastion"
}

output "user_inputs" { value = [for u in terraform_data.user : u.output] }
# ["user-ana", "user-bo", "user-cy"]
# Removing "ana" later destroys only terraform_data.user["ana"].

Remember

  • count: N copies or on/off; for_each: one instance per key
  • Removing a list item under count shifts indexes and churns resources
  • for_each keys must be known at plan time

Watch out for

  • Using count over a list of names, then deleting the first name and watching every later instance be replaced.

Check yourself

  1. A count-based list of 3 users loses its first element. What does the plan typically show?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: developer.hashicorp.com/terraform/language/meta-arguments/for_each