Error: Invalid for_each argument
Fixes terraform's "Error: Invalid for_each argument" where the for_each value is unsuitable. Use when validate or plan rejects a for_each over a list or tuple instead of a map or set of strings. Shows toset() conversion and for-expression map building. Not for for_each over unknown-until-apply values or null handling, which are separate errors.
Fix terraform "Error: Invalid for_each argument" (value is unsuitable)
TL;DR: for_each only accepts a map or a set of strings, and you handed it a list or tuple. Wrap the value in toset(...) if the values are unique strings, or convert a list of objects into a map with a for expression keyed by a unique attribute. Re-run validate and it passes.
The error
Error: Invalid for_each argument
on main.tf line 55, in resource "aws_s3_bucket" "by_each":
55: for_each = var.bucket_names
+----------------
| var.bucket_names is a list of string
The given "for_each" argument value is unsuitable: the "for_each" argument
must be a map, or set of strings, and you have provided a value of type list
of string.Steps
- Look at the type terraform reports (
list of string,tuple,list of object). That is what you must convert away from. - For a list of unique strings, convert to a set:
for_each = toset(var.bucket_names) Instances are then addressed as aws_s3_bucket.by_each["logs"]. Expected: validate passes.
- For a list of objects, build a map keyed by a unique attribute:
for_each = { for s in var.storage_accounts : s.name => s } Expected: each instance keyed by its name, e.g. azurerm_storage_account.sa["acct1"].
- If the reported type is
null, the variable defaulted to null. Guard it:for_each = try(var.settings, {}). Expected: zero instances instead of an error.
When this applies
validateorplanfails withInvalid for_each argument/value is unsuitableand names a list or tuple type.- You changed a variable from a set to a list.
When it does NOT apply
Invalid for_each argumentwhere the value depends on resource attributes unknown until apply ("cannot be determined until apply"). That needs a plan-time-known value, not a type conversion.- Keys derived from resource attributes ("map includes keys derived from resource attributes"). Same family, different fix: make keys static strings with
tostring().
Tool and version compatibility
- Terraform CLI 0.12+ through 1.x.
for_eachtyping rules unchanged.
Why it happens
for_each keys become part of resource addresses in state, so they must be stable and unique. Lists have order and allow duplicates, neither of which survives as an identity. Terraform refuses the value instead of inventing keys for you.
Edge cases and pitfalls
toset()on a list with duplicates silently drops dupes. If duplicates are legitimate data, you needcount, notfor_each.- Sets have no order. If instance order matters to you, it does not; that is a sign you want
count. for_eachat the resource level never accepts a list, butdynamicblocks do iterate lists. Do not confuse the two.