Quick Answer
# Remove the OLD resource block — keep only the NEW name + moved block
moved {
from = aws_instance.web
to = aws_instance.app
}
resource "aws_instance" "app" { # ← Only this block exists
# ...
}
# Do NOT also have: resource "aws_instance" "web" { ... }
The Error
Error: Moved object still exists
The configuration still has a resource "aws_instance" "web" declared at
main.tf:10,1-32, so Terraform cannot move it to "aws_instance" "app".
To complete the move, remove the original resource block.
Or:
Error: Cross-resource move statement
The move statement at main.tf:1,1-3,2 would conflict with the existing
resource "aws_instance" "web" at main.tf:5,1-7,2.
What Causes This
- Old resource block still exists — you added a
movedblock but didn’t delete the old resource block - Both old and new blocks exist — you have
aws_instance.webANDaws_instance.appin config - Applied moved block not cleaned up — the move succeeded but the
movedblock is still there - Conflicting moves — two
movedblocks try to move the same resource
Solution 1: Remove the Old Resource Block
The moved block tells Terraform “the resource formerly at A is now at B.” You must delete A’s resource block:
Before (broken):
# main.tf
moved {
from = aws_instance.web
to = aws_instance.app
}
resource "aws_instance" "web" { # ❌ Still here — causes error
ami = "ami-abc123"
}
resource "aws_instance" "app" {
ami = "ami-abc123"
}
After (correct):
# main.tf
moved {
from = aws_instance.web
to = aws_instance.app
}
resource "aws_instance" "app" { # ✅ Only the new name
ami = "ami-abc123"
}
terraform plan # Shows: aws_instance.web has moved to aws_instance.app
terraform apply # Updates state, no infrastructure changes
Solution 2: Clean Up Applied Moved Blocks
After terraform apply successfully processes the move, you can remove the moved block:
# After apply — this is safe to remove
# moved {
# from = aws_instance.web
# to = aws_instance.app
# }
resource "aws_instance" "app" {
ami = "ami-abc123"
}
Tip: Keep moved blocks for one release cycle so all team members and CI/CD environments pick up the move, then remove them.
Solution 3: Use terraform state mv Instead
For quick renames without moved blocks:
# Rename in state directly
terraform state mv aws_instance.web aws_instance.app
Then rename the resource block in your .tf files. No moved block needed.
moved vs state mv
moved block | terraform state mv | |
|---|---|---|
| Works in CI/CD | ✅ Automatic on apply | ❌ Manual step |
| Team-friendly | ✅ Code-reviewed in PR | ❌ Must coordinate |
| Reversible | ✅ Remove block to undo | ❌ Run reverse mv |
| Module moves | ✅ Supports module renames | ✅ Supports modules |
| Best for | Team workflows | Quick solo renames |
Common Move Patterns
Rename a Resource
moved {
from = aws_instance.web
to = aws_instance.application_server
}
Move Into a Module
moved {
from = aws_instance.web
to = module.compute.aws_instance.this
}
Move Between Modules
moved {
from = module.old_vpc.aws_vpc.main
to = module.networking.aws_vpc.main
}
Rename a Module
moved {
from = module.web
to = module.application
}
Move from count to for_each
moved {
from = aws_instance.web[0]
to = aws_instance.web["primary"]
}
moved {
from = aws_instance.web[1]
to = aws_instance.web["secondary"]
}
Hands-On Courses
- Terraform for Beginners on CopyPasteLearn
- Terraform By Example — practical code examples
Conclusion
The “moved object still exists” error means you have both the old and new resource blocks. Delete the old one — the moved block handles the state migration. Clean up moved blocks after all environments have applied the change. For quick solo renames, terraform state mv is simpler; for team workflows, moved blocks are safer because they’re code-reviewed.