# 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

```text
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

1. Look at the type terraform reports (`list of string`, `tuple`, `list of object`). That is what you must convert away from.
2. For a list of unique strings, convert to a set:

   ```hcl
   for_each = toset(var.bucket_names)
   ```

   Instances are then addressed as `aws_s3_bucket.by_each["logs"]`. Expected: validate passes.
3. For a list of objects, build a map keyed by a unique attribute:

   ```hcl
   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"]`.
4. 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

- `validate` or `plan` fails with `Invalid for_each argument` / `value is unsuitable` and names a list or tuple type.
- You changed a variable from a set to a list.

## When it does NOT apply

- `Invalid for_each argument` where 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_each` typing 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 need `count`, not `for_each`.
- Sets have no order. If instance order matters to you, it does not; that is a sign you want `count`.
- `for_each` at the resource level never accepts a list, but `dynamic` blocks do iterate lists. Do not confuse the two.