How to fix Error: Invalid each attribute - The each object does not have an attribute named id in Terraform
This Terraform error occurs when you reference an attribute on the 'each' object that doesn't exist. The 'each' object only provides 'key' and 'value' attributes. Fix it by using the correct attribute or checking your for_each input structure.
Quick fix
Fixes Error: Invalid each attribute - The each object does not have an attribute named id
resource "aws_instance" "example" {
for_each = var.instances
ami = each.id # ERROR: 'each' has no 'id' attribute
instance_type = each.name # ERROR: 'each' has no 'name' attribute
}- Understand the each object structure
- Verify your for_each data structure
- Use the correct each reference based on data type
- Fix common for_each mistakes
resource "aws_instance" "example" {for_each = var.instancesami = each.id # ERROR: 'each' has no 'id' attributeinstance_type = each.name # ERROR: 'each' has no 'name' attribute}Error: Invalid each attribute - The each object does not have an attribute named id
What this error means
The "Invalid each attribute" error in Terraform indicates that you're trying to access an attribute on the "each" object that doesn't exist. When using "for_each" to iterate over resources or modules, Terraform provides exactly two attributes on the "each" object: "key" (the current map key) and "value" (the current map value). This error commonly occurs when developers try to access attributes that don't exist on the "each" object itself, such as "id", "name", or other properties. The error can also happen when the data structure passed to "for_each" doesn't match what the code expects, or when trying to access nested properties using dot notation without understanding the data structure. Unlike "count.index" which provides a numeric index, or "count.primary" which provides a boolean, the "each" object has a fixed, simple interface with only "key" and "value" available for all iterations.
Typical symptoms
- Plan or apply fails with 'The each object does not have an attribute named [attribute]'
- Error references a specific attribute like 'id', 'name', or other property
- Error occurs during 'terraform plan' or 'terraform apply'
- Code that was working previously suddenly fails after changing data structures
- Mixing for_each with count-style attribute references (like each.id)
- Trying to access nested properties directly on the each object
Common causes
- Attempting to access an attribute that doesn't exist on the each object (only 'key' and 'value' are available)
- Using each.id instead of each.key or each.value when iterating
- Forgetting that each.value is the entire object when using complex data structures
- Misunderstanding the structure of the data passed to for_each (expecting a list when it's a map, or vice versa)
- Trying to access nested attributes without proper object property access syntax
- Copy-pasting code from count-based iteration and not updating each references correctly
How to fix "Error: Invalid each attribute - The each object does not have an attribute named id"
The "each" object in Terraform only provides two attributes, regardless of the data being iterated:
Available attributes:
- each.key: The key from the map (string)
- each.value: The value from the map (can be a string, number, object, list, etc.)
Incorrect - trying to access non-existent attributes:
resource "aws_instance" "example" {
for_each = var.instances
ami = each.id # ERROR: 'each' has no 'id' attribute
instance_type = each.name # ERROR: 'each' has no 'name' attribute
}Correct - using only 'key' and 'value':
resource "aws_instance" "example" {
for_each = var.instances
ami = each.value.ami
instance_type = each.value.instance_type
tags = {
Name = each.key # Use the key for naming
}
}Remember: no matter what data structure you pass to "for_each", you always access it through "each.key" and "each.value".
Make sure you understand what data structure you're iterating over:
When iterating a map of objects:
variable "instances" {
type = map(object({
ami = string
instance_type = string
}))
default = {
web = {
ami = "ami-12345"
instance_type = "t2.micro"
}
db = {
ami = "ami-67890"
instance_type = "t2.small"
}
}
}
resource "aws_instance" "example" {
for_each = var.instances
ami = each.value.ami # Access nested properties through 'value'
instance_type = each.value.instance_type
tags = {
Name = each.key # 'key' is "web" or "db"
}
}When iterating a set of strings:
variable "availability_zones" {
type = set(string)
default = ["us-east-1a", "us-east-1b", "us-east-1c"]
}
resource "aws_subnet" "example" {
for_each = var.availability_zones
availability_zone = each.value # 'value' is the zone string directly
vpc_id = aws_vpc.main.id
tags = {
Name = "subnet-${each.key}" # 'key' is auto-generated index for sets
}
}Print your variables during development to verify structure:
output "debug_instances" {
value = var.instances
}Different data types passed to "for_each" require different access patterns:
For map(object): Use each.key for the map key and each.value to access object properties:
for_each = {
"web-1" = { size = "t2.micro", az = "us-east-1a" }
"web-2" = { size = "t2.micro", az = "us-east-1b" }
}
instance_type = each.value.size # Not each.instance_type or each.size
tags = { Name = each.key } # 'web-1' or 'web-2'For map(string): Use each.value as the string directly:
for_each = {
"web" = "ami-12345"
"db" = "ami-67890"
}
ami = each.value # Not each.ami or each.keyFor list converted to map: Convert list to map first using tomap():
# Instead of for_each = var.my_list (which fails for non-sets/maps)
for_each = { for idx, item in var.my_list : idx => item }
# Access the item through each.value
resource_name = each.value.name # Not each.name or each.idNever use count-style attributes:
# WRONG - count attributes don't work with for_each
each.id # ❌ Doesn't exist
each.index # ❌ Doesn't exist (use count.index with count, not for_each)
each.count # ❌ Doesn't exist
# CORRECT - only for_each attributes
each.key # ✅ The map key
each.value # ✅ The map valueHere are fixes for the most common patterns that trigger this error:
Mistake 1: Using each.id when you mean each.key:
# ❌ Wrong
for_each = toset(["web", "db", "cache"])
resource "aws_security_group" "example" {
name = each.id
}
# ✅ Correct
for_each = toset(["web", "db", "cache"])
resource "aws_security_group" "example" {
name = each.value
}Mistake 2: Accessing nested properties incorrectly:
# ❌ Wrong - trying to access nested property directly
for_each = var.instances
instance_type = each.instance_type
# ✅ Correct - use each.value then the property
for_each = var.instances
instance_type = each.value.instance_typeMistake 3: Mixing up key and value usage:
# ❌ Wrong
for_each = { a = "value1", b = "value2" }
output_name = each.value # When you meant the key
# ✅ Correct
for_each = { a = "value1", b = "value2" }
output_name = each.key # If you want the key (a or b)
output_data = each.value # If you want the value (value1 or value2)Mistake 4: Using list instead of map/set:
# ❌ Wrong - lists are not supported directly by for_each
for_each = ["item1", "item2", "item3"]
# ✅ Correct - convert to set or map
for_each = toset(["item1", "item2", "item3"])
# OR
for_each = { for idx, item in ["item1", "item2", "item3"] : idx => item }When you're unsure about your data structure, add debug outputs to understand what you're iterating over:
variable "my_resources" {
type = any # Temporary - use 'any' to accept any structure
}
output "debug_for_each" {
value = var.my_resources
}
output "debug_for_each_type" {
value = "Type check: ${jsonencode(var.my_resources)}"
}
resource "aws_instance" "example" {
for_each = var.my_resources
# Debug output for each iteration
tags = {
DebugKey = each.key
DebugValue = jsonencode(each.value)
}
}Then run:
terraform planCheck the debug outputs to see exactly what structure you're working with. This helps you understand whether "each.value" is a string, number, object, or something else.
Use Terraform console to test expressions:
terraform console
> var.my_resources
> keys(var.my_resources)
> values(var.my_resources)The same principles apply when using "for_each" with modules and data sources:
With modules:
variable "subnet_config" {
type = map(object({
cidr_block = string
az = string
}))
}
module "subnets" {
for_each = var.subnet_config
source = "./modules/subnet"
name = each.key # Map key
cidr_block = each.value.cidr_block # Nested property
az = each.value.az
}
# Access module outputs
output "subnet_ids" {
value = { for k, v in module.subnets : k => v.subnet_id }
}With data sources:
data "aws_ami" "example" {
for_each = toset(["ubuntu", "amazon-linux", "centos"])
filter {
name = "name"
values = ["${each.value}-ami-*"] # Use each.value directly
}
}
output "ami_ids" {
value = { for k, v in data.aws_ami.example : k => v.id }
}Remember: even with modules and data sources, the "each" object still only has "key" and "value" attributes.
Advanced notes
Understanding for_each vs count:
- "for_each" creates resources with map-based keys and provides "each.key"/"each.value"
- "count" creates resources with numeric indices and provides "count.index"
- Do not mix attributes: use "each.*" with for_each and "count.*" with count
for_each vs splat expressions:
When you use "for_each" on a resource, you cannot use splat expressions like "aws_instance.example[*].id". Instead, convert to values:
# Wrong with for_each
output "ids" {
value = aws_instance.example[*].id
}
# Correct with for_each
output "ids" {
value = { for k, v in aws_instance.example : k => v.id }
}Dynamic blocks and for_each:
When using "for_each" within a resource and also using dynamic blocks inside:
resource "aws_lb_listener_rule" "example" {
for_each = var.rules
dynamic "condition" {
for_each = each.value.conditions
content {
path_pattern {
values = [condition.value.path]
}
}
}
}Sensitive data in for_each:
Be aware that "for_each" values appear in state and plan outputs. For sensitive data:
resource "aws_instance" "example" {
for_each = var.instances
ami = each.value.ami
# Mark sensitive data
tags = {
Password = sensitive(each.value.password) # Won't show in output
}
}Changing for_each keys:
Adding or removing keys in "for_each" will destroy and recreate resources. Use stable identifiers in your map keys when the resource needs to persist.
Frequently asked questions
- What does "Error: Invalid each attribute - The each object does not have an attri" mean?
- The "Invalid each attribute" error in Terraform indicates that you're trying to access an attribute on the "each" object that doesn't exist. When using "for_each" to iterate over resources or modules, Terraform provides exactly two attributes on the "each" object: "key" (the current map key) and "value" (the current map value). This error commonly occurs when developers try to access attributes that don't exist on the "each" object itself, such as "id", "name", or other properties.
- How do I fix "Error: Invalid each attribute - The each object does not have an attri"?
- This Terraform error occurs when you reference an attribute on the 'each' object that doesn't exist. The 'each' object only provides 'key' and 'value' attributes. Fix it by using the correct attribute or checking your for_each input structure.
- What causes "Error: Invalid each attribute - The each object does not have an attri"?
- Attempting to access an attribute that doesn't exist on the each object (only 'key' and 'value' are available) Using each.id instead of each.key or each.value when iterating Forgetting that each.value is the entire object when using complex data structures
- Is the "Error: Invalid each attribute - The each object does not have an attri" error serious?
- It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.
Official resources & further reading
- 📚Terraform Official Documentation
- 📖Terraform Docs: for_each Meta-Argument(Official Docs)
- 📖Terraform Docs: Troubleshooting Workflow(Official Docs)
- 🐙