The "Moved block with ambiguous destination" error occurs when multiple moved blocks in your Terraform configuration target the same destination resource. Terraform cannot determine which source should be moved to that destination, creating ambiguity that prevents the refactoring from proceeding.
Terraform's moved block is used to refactor infrastructure code by telling Terraform that a resource has been renamed or relocated without destroying and recreating it. However, each resource can only have one source—it can only be moved from one previous location. When you have multiple moved blocks that conflict by pointing to the same destination, Terraform cannot determine which source is the correct origin. This creates an ambiguous situation where the refactoring cannot be safely applied. The error prevents the configuration from being evaluated because Terraform needs absolute clarity about which resource in your state file maps to which resource in your code.
First, find which moved blocks are conflicting. Search your configuration files for all moved blocks that have the same to address:
grep -r "to.*=" . --include="*.tf" | grep moved -A 2Or manually review your moved blocks. The error message should tell you which destination is ambiguous. For example:
# These two blocks conflict
moved {
from = aws_instance.old_name_1
to = aws_instance.new_name # First source
}
moved {
from = aws_instance.old_name_2
to = aws_instance.new_name # Second source - CONFLICT!
}List all moved blocks that point to the same destination.
Examine your Terraform state file to see which resource actually exists:
terraform state list | grep -E "old_name|new_name"Or view more detail with:
terraform state show aws_instance.old_name_1
terraform state show aws_instance.old_name_2This will show you which resource currently exists in your state. You need to identify:
- Which from address corresponds to an actual resource in your state
- Whether both exist (in which case you need to consolidate them first)
- Which refactoring move is the correct one
Only one of the conflicting moved blocks should be valid.
Delete the moved blocks that are not correct. Keep only the moved block that accurately reflects how your state needs to be remapped:
# KEEP THIS - if old_name_1 is the actual resource
moved {
from = aws_instance.old_name_1
to = aws_instance.new_name
}
# DELETE THIS - remove the conflicting block
# moved {
# from = aws_instance.old_name_2
# to = aws_instance.new_name
# }After removing the conflicting moved block, run:
terraform planThe error should resolve.
If both source resources actually exist in your state file and you genuinely need to consolidate them, you cannot use a single moved block. Instead:
1. Manually consolidate in state first:
# Remove the state of one resource (keeping the other)
terraform state rm aws_instance.old_name_22. Then apply your moved block:
moved {
from = aws_instance.old_name_1
to = aws_instance.new_name
}3. Plan and apply:
terraform plan
terraform applyBe careful with this approach—removing state should only be done when you're certain that resource instance is no longer needed.
If you're using modules, check for conflicts across module boundaries:
# Parent module
moved {
from = module.networking.aws_vpc.main
to = aws_vpc.shared
}
# Child module (networking/main.tf)
moved {
from = aws_vpc.main
to = aws_vpc.consolidated # Different destination
}If you have moved blocks in both parent and child modules targeting overlapping resources, you need to:
- Keep moved blocks only at the appropriate scope
- Remove duplication across module boundaries
- Ensure only one moved block per resource address
Simplify to have moved blocks at just one level (either parent or child, not both).
Why does this restriction exist?
Terraform's state file is the source of truth for your infrastructure. When you apply a moved block, Terraform remaps the state so the same physical resource (identified by its unique ID in the cloud) gets a new logical address in your code.
The ambiguity rule prevents data loss: if multiple moved blocks targeted the same destination, Terraform would not know which source's state data to move there, risking overwriting one resource's state with another's.
Implicit moved blocks
In some cases, Terraform may automatically detect renames and generate implicit moved blocks. These can sometimes cause ambiguity if the detection is unclear. To debug:
terraform plan -json | jq '.diagnostic_events | select(.summary == "Moved'Preventing this error:
- Plan your refactoring carefully before writing moved blocks
- Use a naming convention that makes the before/after clear
- Apply moved blocks incrementally: refactor one module at a time
- Test in a non-production environment first
- Keep moved blocks close to the resources they affect (avoid cross-module moves unless necessary)
- Document why each moved block exists with comments
Related limitation: Terraform does not support dynamic moved blocks (e.g., using for_each or count to generate moved blocks). Each move must be explicitly declared.
Error: Error rendering template: template not found
How to fix "template not found" error in Terraform
Error: Error generating private key
How to fix 'Error generating private key' in Terraform
Error creating Kubernetes Service: field is immutable
How to fix "field is immutable" errors in Terraform
Error: Error creating local file: open: permission denied
How to fix "Error creating local file: permission denied" in Terraform
Error: line endings have changed from CRLF to LF
Line endings have changed from CRLF to LF in Terraform