Fix Terraform Error - Invalid for_each Argument
Fix the Terraform invalid for_each argument error when keys depend on unknown values. Covers static keys, targeting, locals, and for_each best practices.
Troubleshooting
Fix the Terraform invalid count argument error when count depends on resource attributes not known until apply. Covers for_each, data sources, and targeting.
The count value depends on a resource attribute that Terraform can't determine during plan. Replace the computed value with a variable, data source, or switch to for_each with a known set of keys.
When running terraform plan, you encounter:
Error: Invalid count argument
on main.tf line 15, in resource "aws_instance" "worker":
15: count = length(aws_subnet.private[*].id)
The "count" value depends on resource attributes that cannot be
determined until apply, so Terraform cannot predict how many
instances will be created. To work around this, use the -target
argument to first apply only the resources that the count depends on.You may also see the for_each variant:
Error: Invalid for_each argument
The "for_each" value depends on 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.Terraform needs to know count and for_each values at plan time to build the dependency graph. If the value comes from a resource that doesn't exist yet, Terraform can't determine how many instances to create.
Using output of a resource being created:
# BAD — aws_subnet.private doesn't exist yet
resource "aws_instance" "worker" {
count = length(aws_subnet.private[*].id)
subnet_id = aws_subnet.private[count.index].id
}Computed values from data sources that depend on new resources:
# BAD — data source depends on a resource being created
data "aws_subnets" "private" {
filter {
name = "vpc-id"
values = [aws_vpc.main.id] # VPC doesn't exist yet
}
}
resource "aws_instance" "worker" {
count = length(data.aws_subnets.private.ids)
}Dynamically computed lengths:
# BAD — output of another module not known at plan
resource "aws_route53_record" "records" {
count = length(module.certificates.domain_validation_options)
}Replace the computed count with a known variable:
variable "subnet_count" {
description = "Number of private subnets"
type = number
default = 3
}
resource "aws_subnet" "private" {
count = var.subnet_count
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index)
availability_zone = data.aws_availability_zones.available.names[count.index]
}
resource "aws_instance" "worker" {
count = var.subnet_count # Known at plan time
subnet_id = aws_subnet.private[count.index].id
}Instead of counting dynamically, define the instances explicitly:
variable "workers" {
type = map(object({
instance_type = string
subnet_index = number
}))
default = {
"worker-1" = { instance_type = "t3.micro", subnet_index = 0 }
"worker-2" = { instance_type = "t3.micro", subnet_index = 1 }
"worker-3" = { instance_type = "t3.small", subnet_index = 2 }
}
}
resource "aws_instance" "worker" {
for_each = var.workers
ami = data.aws_ami.ubuntu.id
instance_type = each.value.instance_type
subnet_id = aws_subnet.private[each.value.subnet_index].id
tags = {
Name = each.key
}
}If the resources already exist (created outside this config), use a data source:
# GOOD — data source reads existing resources
data "aws_subnets" "private" {
filter {
name = "vpc-id"
values = [var.vpc_id] # Known variable, not computed
}
tags = {
Tier = "private"
}
}
resource "aws_instance" "worker" {
count = length(data.aws_subnets.private.ids)
subnet_id = data.aws_subnets.private.ids[count.index]
}When restructuring isn't practical, apply the dependency first:
# Step 1: Create the subnets
terraform apply -target=aws_subnet.private
# Step 2: Now count is known, apply everything
terraform applyNote: -target is a workaround, not a best practice. Refactor your config when possible.
variable "availability_zones" {
type = list(string)
default = ["us-east-1a", "us-east-1b", "us-east-1c"]
}
locals {
az_count = length(var.availability_zones)
}
resource "aws_subnet" "private" {
count = local.az_count
vpc_id = aws_vpc.main.id
cidr_block = cidrsubnet(var.vpc_cidr, 8, count.index)
availability_zone = var.availability_zones[count.index]
}
resource "aws_instance" "worker" {
count = local.az_count # Same known value
subnet_id = aws_subnet.private[count.index].id
}For resources that reference other resource outputs:
variable "subnet_cidrs" {
type = set(string)
default = ["10.0.1.0/24", "10.0.2.0/24", "10.0.3.0/24"]
}
resource "aws_subnet" "private" {
for_each = var.subnet_cidrs
vpc_id = aws_vpc.main.id
cidr_block = each.value
}
resource "aws_instance" "worker" {
for_each = var.subnet_cidrs # Same static set
subnet_id = aws_subnet.private[each.key].id
ami = data.aws_ami.ubuntu.id
instance_type = "t3.micro"
}| Pattern | Why It Fails | Fix |
|---|---|---|
count = length(aws_resource.x[*].id) | Resource doesn't exist yet | Use a variable for count |
for_each = toset(aws_resource.x[*].name) | Set not known at plan | Use a static variable set |
count = data.source.x.count where data depends on new resource | Data source can't evaluate | Use pre-existing data or variable |
count = module.x.output_count | Module output computed at apply | Pass the count as input to both modules |
count or for_each value depend on a resource being created in the same apply?for_each with a static map work better than count?count from resource attributes in the same configfor_each over count — for_each with known keys is more stable and doesn't reorder on changesterraform plan after changes to catch this earlyThis error protects you from unpredictable infrastructure changes. Terraform needs to know exactly how many resources to create before it starts. Restructure your config so count and for_each use known values — variables, locals, or data sources that don't depend on resources being created in the same apply.
Fix the Terraform invalid for_each argument error when keys depend on unknown values. Covers static keys, targeting, locals, and for_each best practices.
Fix the Terraform 'Backend configuration changed' error. Migrate state between backends (local to S3, S3 to S3), resolve lock conflicts
Fix Terraform provider version conflicts between modules, lock files, and constraint mismatches. Resolve dependency lock errors, upgrade providers safely
Fix the Terraform 'Reference to undeclared resource' error. Causes include typos, missing resources, wrong module references, and for_each/count confusion.