The "Invalid for_each argument" error occurs when Terraform cannot determine the keys in a for_each loop before applying the plan. This typically happens when the for_each value depends on computed resources or unknown attributes that haven't been created yet.
Terraform's for_each meta-argument requires all keys to be known at plan time (before apply), not at apply time. When you use for_each with a value that depends on a resource that hasn't been created yet—such as outputs from data sources, computed attributes, or other resources—Terraform cannot predict how many instances will be created. The error message indicates that the value passed to for_each contains key references that depend on resource attributes that cannot be determined until the apply phase. This creates a "chicken-and-egg" problem: Terraform needs to know the keys to create the resources, but the keys depend on resources that don't exist yet. This is a core limitation of Terraform's planning model, not a bug.
The most common workaround is to apply the dependency first using the -target argument, then apply the rest of your configuration.
First, apply only the resources that for_each depends on:
terraform apply -target='aws_eks_cluster.main'Then apply the full configuration:
terraform applyThis two-step process ensures that the dependency is created before Terraform tries to evaluate the for_each expression.
Instead of making both keys and values depend on computed attributes, keep the keys static and only compute the values:
# DON'T DO THIS - keys depend on computed data
for_each = data.aws_availability_zones.available.names
# DO THIS - keys are static, values are computed
for_each = {
az1 = "us-east-1a"
az2 = "us-east-1b"
az3 = "us-east-1c"
}
# Or use a static list with toset()
for_each = toset([
"us-east-1a",
"us-east-1b",
"us-east-1c"
])This approach requires knowing the keys upfront, but keeps your configuration deterministic.
When passing for_each values between modules, ensure the map keys are not computed:
# In parent module
module "worker_nodes" {
for_each = tomap({
"group-1" : var.group_1_config,
"group-2" : var.group_2_config,
"group-3" : var.group_3_config,
})
source = "./modules/worker"
# ... pass each.value to module
}
# The keys ("group-1", "group-2", etc.) are static strings
# Only the values come from dynamic configBy using static string keys with tomap(), you ensure Terraform knows the for_each structure at plan time.
If you need to derive keys from a data source, compute them in a local and reference that instead:
locals {
# Query data source once, outside for_each
available_azs = data.aws_availability_zones.available.names
# Build a map with known keys
az_map = {
for idx, az in local.available_azs :
"az-${idx + 1}" => az
}
}
# Now use the pre-computed map
resource "aws_subnet" "main" {
for_each = local.az_map
availability_zone = each.value
# ... rest of config
}This approach pre-computes the for_each structure so Terraform knows it at plan time.
Terraform also rejects sensitive values (secrets, passwords) in for_each because the keys are exposed in the UI output.
If you're passing sensitive variables to for_each:
# This will fail if var.secret_config is marked sensitive
for_each = var.secret_config # ❌ Error if sensitive
# Workaround: Extract only the non-sensitive parts
locals {
# Use only the non-sensitive subset
safe_config = {
for k, v in var.config : k => v.username
}
}
for_each = local.safe_config # ✓ WorksAlways keep for_each keys and values non-sensitive.
Why does this happen?
Terraform's execution model has two phases: plan and apply. During the plan phase, Terraform must determine all the resources that will be created so it can show you what changes will occur. The for_each expression must be fully evaluated during planning.
However, some values (like data source results, computed resource attributes, or outputs from other for_each loops) are only determined during the apply phase. This creates a planning problem: Terraform cannot generate a complete plan if the for_each structure is unknown.
Why not use count instead?
While count can sometimes work with dynamic values, it has its own limitations and is generally less flexible than for_each. The recommended solution is to restructure your configuration so that for_each dependencies are satisfied at plan time.
Preventing this in future configurations:
- Always ensure for_each keys are known at plan time
- Avoid passing raw data source results to for_each; pre-compute them in locals
- Use static string keys when possible
- When using computed values, limit them to the values (not keys) of your for_each map
- Consider splitting complex configurations into separate Terraform applies when necessary
Related Terraform limitations:
- Dynamic blocks cannot solve this; they still require the content to be deterministic at plan time
- Splat syntax (e.g., aws_instance.*.id) cannot be used as for_each keys
- Terraform Cloud/Enterprise may apply additional constraints on plan/apply workflows
Error: Error installing helm release: cannot re-use a name that is still in use
How to fix "release name in use" error in Terraform with Helm
Error: Error creating GKE Cluster: BadRequest
BadRequest error creating GKE cluster in Terraform
Error: External program failed to produce valid JSON
External program failed to produce valid JSON
Error: Unsupported argument in child module call
How to fix "Unsupported argument in child module call" in Terraform
Error: network is unreachable
How to fix "network is unreachable" in Terraform