The Error
You run terraform plan or terraform apply and hit this immediately:
โ Error: Invalid combination of "count" and "for_each"
โ
โ on main.tf line 3, in resource "aws_instance" "web":
โ 3: resource "aws_instance" "web" {
โ
โ The "count" and "for_each" meta-arguments are mutually exclusive.
A resource block (or module block) has both count and for_each defined at once. Terraform can't decide which iteration strategy to use, so it stops right there.
Why This Happens
Both count and for_each tell Terraform to spin up multiple instances of a resource โ but they work in fundamentally different ways:
countโ creates N copies, referenced by index (resource[0],resource[1], โฆ)for_eachโ creates one copy per map key or set element, referenced by key (resource["web"],resource["db"], โฆ)
Having both in the same block is contradictory. It's like telling a chef to cook a steak medium-rare and well-done at the same time โ one has to go.
Three situations where this tends to bite people:
- Migrating old
count-based code tofor_each, but forgetting to delete the originalcountline - Copy-pasting a resource block from another project that used
count, then slappingfor_eachon top without cleanup - A module call that receives both as arguments from different sources
Step-by-Step Fix
Step 1: Find the offending block
Terraform's error includes the exact file and line number. Open that file and look for a block with both arguments side by side:
resource "aws_instance" "web" {
count = 3 # โ remove one of these
for_each = toset(["a", "b", "c"]) # โ keep only one
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.micro"
}
Step 2: Choose the right meta-argument
Pick the one that actually fits your situation.
Use count when you need N identical (or near-identical) copies and keys don't matter:
resource "aws_instance" "web" {
count = 3
ami = "ami-0c55b159cbfafe1f0"
instance_type = "t3.micro"
tags = {
Name = "web-${count.index}"
}
}
Use for_each when each instance has a distinct role or identity โ web, api, db โ or you need to reference them by name later:
locals {
instances = {
web = "t3.micro"
api = "t3.small"
db = "t3.medium"
}
}
resource "aws_instance" "servers" {
for_each = local.instances
ami = "ami-0c55b159cbfafe1f0"
instance_type = each.value
tags = {
Name = each.key
}
}
Referencing a specific instance later is clean and readable:
output "web_ip" {
value = aws_instance.servers["web"].public_ip
}
Step 3: Remove the unused meta-argument
Delete whichever one you're not keeping. Scan the block carefully โ a stray count = ... or for_each = ... hiding at the bottom is enough to trigger the error. Each resource should have exactly one (or neither, if you only need a single instance).
Module blocks follow the same rules
Module calls are just as vulnerable:
# Broken
module "vpc" {
source = "./modules/vpc"
count = 2
for_each = toset(["us-east-1", "eu-west-1"])
}
# Fixed โ pick one:
module "vpc" {
source = "./modules/vpc"
for_each = toset(["us-east-1", "eu-west-1"])
region = each.value
}
Verify the Fix
Run terraform validate first โ it's faster than a full plan and catches syntax issues immediately:
terraform validate
You should see:
Success! The configuration is valid.
Then confirm the plan looks right:
terraform plan
One thing to watch: if you're switching an existing resource from count to for_each, Terraform will plan to destroy and recreate every instance. The state key format changes from resource[0] to resource["key"], and Terraform treats them as different objects. Use terraform state mv to migrate the state manually if you need to avoid recreation.
Quick Tips
- Lean toward
for_eachfor most real infrastructure. Remove one item from acount-based list mid-range and Terraform will shift all subsequent indexes โ potentially destroying and recreating resources you never touched. Withfor_each, only the deleted key disappears. - Empty map or set is fine โ
for_each = {}creates zero resources without any error. - Can't pass a plain list to
for_eachโ wrap it withtoset():for_each = toset(var.names). Lists allow duplicates;for_eachdoesn't. - Dynamic blocks are a different story โ using
for_eachinside adynamicblock within a resource that already usescountis perfectly valid. That's not the conflict Terraform is complaining about.

