Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Terraform By James Joyner IV · · 9 min read

Terraform Error Guide: 'Invalid for_each argument' (values cannot be determined) on plan

Fix Terraform's 'Invalid for_each argument' error: handle computed keys, unknown values until apply, null/sensitive maps, and use -target or static keys to break it.

  • #terraform
  • #troubleshooting
  • #errors
  • #for_each

Overview

Invalid for_each argument fires when Terraform cannot determine the keys of a for_each map/set at plan time. Terraform must know every instance key before it plans, so it can show you exactly what will be created. If the keys depend on a value that is only known after another resource is applied (an ARN, a generated ID, a list whose length is computed), Terraform cannot expand the for_each and stops.

You will see this on terraform plan:

Error: Invalid for_each argument

  on iam.tf line 12, in resource "aws_iam_user_policy_attachment" "attach":
  12:   for_each = toset([for u in aws_iam_user.team : u.name])

The "for_each" set includes values derived from resource attributes that cannot
be determined until apply, and so Terraform cannot determine the full set of
keys that will identify the instances of this resource.

The message is precise: the keys (not the values) are unknown until apply. The fix is to make the keys static/known at plan time, or to apply the resource that produces them first.

Symptoms

  • plan fails with Invalid for_each argument.
  • The message says keys are “derived from resource attributes that cannot be determined until apply”.
  • A for_each iterates over outputs of a not-yet-created resource (IDs, ARNs, names).
  • It appears after switching count to for_each, or after adding a new resource into a for_each expression.
terraform plan
Error: Invalid for_each argument

The "for_each" map includes keys derived from resource attributes that cannot
be determined until apply.

Common Root Causes

1. Keys derived from a not-yet-created resource

The for_each set/map keys come from an attribute (ID/ARN/name) of a resource Terraform has not applied yet.

grep -rn 'for_each' iam.tf
resource "aws_iam_user" "team" { for_each = toset(var.usernames) }

resource "aws_iam_user_policy_attachment" "attach" {
  for_each = toset([for u in aws_iam_user.team : u.name])   # unknown keys
}

u.name is computed during apply, so the keys are unknown at plan.

2. A computed list/length feeding for_each

The collection length depends on a resource attribute, so Terraform cannot know how many keys exist.

grep -rn 'for_each\|length(' main.tf
for_each = { for az in data.aws_availability_zones.live.names : az => az }

If data.aws_availability_zones.live is itself gated by a computed value, the names are unknown.

3. Keys built from another resource’s id/arn

Using IDs as map keys guarantees unknown keys, because IDs are assigned by the provider during apply.

grep -rn 'for_each' subnets.tf
for_each = { for s in aws_subnet.app : s.id => s.cidr_block }   # id is computed

Key off a static input (the subnet name/index) instead of the computed id.

4. A whole map value is unknown (not just values)

Even if you intend only the values to be computed, putting a computed expression where Terraform must hash keys makes the keys unknown.

grep -rn 'for_each' secrets.tf
for_each = aws_secretsmanager_secret.app   # the map itself isn't known if app is computed-keyed

5. Null or sensitive value in the for_each

A null in the set, or a sensitive value used as a key, prevents Terraform from determining the key set.

terraform console <<<'toset([for u in var.users : u.email])'
Error: Invalid function argument
toset: a null value cannot be used as a set element

6. switched from count to for_each over computed data

Refactoring count (index-based) to for_each (key-based) over the same computed list surfaces the key problem that count masked.

git diff main.tf | grep -E 'count|for_each'
- count    = length(aws_instance.node)
+ for_each = { for i in aws_instance.node : i.id => i }

Diagnostic Workflow

Step 1: Identify which expression feeds for_each

terraform plan 2>&1 | grep -A4 "Invalid for_each"

The file/line points at the exact for_each whose keys are unknown.

Step 2: Check whether the keys are computed

grep -rn 'for_each' <FILE>

Trace the keys back: do they come from var/local (known) or from a resource/data id/arn/name (computed)?

Step 3: Test the key set in the console

terraform console
> keys({ for u in aws_iam_user.team : u.name => u })
(known after apply)

known after apply for the keys confirms the cause.

Step 4: Re-key on a known input

grep -rn 'for_each' <FILE>

Switch the key to a static input you already have at plan time:

# instead of keying on the computed u.name, key on the input map
resource "aws_iam_user" "team" {
  for_each = toset(var.usernames)
}
resource "aws_iam_user_policy_attachment" "attach" {
  for_each = aws_iam_user.team          # known keys = var.usernames
  user     = each.value.name            # computed value is fine here
}

Step 5: If keys genuinely must be computed, apply in stages

terraform apply -target=aws_iam_user.team
terraform apply

Apply the resource that produces the keys first; on the second run the keys are known.

Example Root Cause Analysis

A plan fails after wiring policy attachments to a set of users:

Error: Invalid for_each argument

  on iam.tf line 18, in resource "aws_iam_user_policy_attachment" "attach":
  18:   for_each = toset([for u in aws_iam_user.team : u.name])

The "for_each" set includes values derived from resource attributes that cannot
be determined until apply.

The for_each keys are built from aws_iam_user.team[*].name, which is computed during apply. But the user names are also exactly what is in var.usernames at plan time — the code is reaching for the computed attribute unnecessarily.

grep -rn 'for_each' iam.tf
resource "aws_iam_user" "team" {
  for_each = toset(var.usernames)
}

Because aws_iam_user.team is itself keyed by the known var.usernames, the attachment can iterate the resource map directly — those keys are known at plan time.

Fix: iterate the resource map, not a list of computed names.

resource "aws_iam_user_policy_attachment" "attach" {
  for_each   = aws_iam_user.team        # keys = var.usernames (known)
  user       = each.value.name
  policy_arn = var.policy_arn
}

terraform plan now expands the for_each and shows one attachment per user.

Prevention Best Practices

  • Key for_each on values you control at plan time (input variables, locals, static names) — never on a resource’s computed id/arn.
  • When iterating over a resource created with for_each, iterate the resource map itself (for_each = aws_x.y) so the keys carry over as known.
  • Strip nulls and avoid sensitive values in the key set; compact() and explicit defaults help.
  • If keys truly depend on created infrastructure, split the apply (-target the producer first) or restructure so the dependency is one-directional.
  • Prefer for_each over count for stable identity, but expect it to surface key-determinability that count hid.
  • For triage, the free incident assistant can trace the for_each expression to the computed attribute that breaks it. More patterns in the Terraform guides.

Quick Command Reference

# Locate the failing for_each
terraform plan 2>&1 | grep -A4 "Invalid for_each"

# Inspect the expression feeding for_each
grep -rn 'for_each' <FILE>

# Test whether the keys are known
terraform console
> keys(<FOR_EACH_EXPRESSION>)

# Re-key on the resource map (keys carry over as known)
# for_each = aws_iam_user.team

# If keys must be computed, stage the apply
terraform apply -target=<PRODUCER>
terraform apply

Conclusion

Invalid for_each argument means Terraform cannot determine the keys of your for_each at plan time. The usual root causes:

  1. Keys derived from a not-yet-created resource’s attributes.
  2. A computed list/length feeding the collection.
  3. Keying on a computed id/arn instead of a static input.
  4. The whole map being unknown at plan.
  5. A null or sensitive value in the key set.
  6. A countfor_each refactor over computed data.

Re-key on values known at plan time (variables, the resource map itself), strip nulls, and only fall back to a staged -target apply when the keys genuinely must come from created infrastructure.

Free download · 368-page PDF

Download the Free 500-Prompt DevOps AI Toolkit

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.