The source address in a moved block cannot reference a resource that uses count or for_each. You must specify individual resource instances.
Terraform's moved block is designed to update state when resources are renamed or relocated. However, it does not support using wildcard-like references from resources that use count or for_each at the source. The source must refer to specific resource instances or a resource without meta-arguments. This limitation exists because moved blocks must unambiguously identify which state objects to relocate, and count/for_each patterns generate multiple instances that cannot be referenced as a group.
First, modify the resource definition to remove the count or for_each meta-argument. If you have multiple instances created by count or for_each, you'll need to handle each instance individually in the next steps.
# Before:
resource "aws_instance" "web" {
count = 3
# ...
}
# After:
resource "aws_instance" "web" {
# remove count = 3
# ...
}If you had multiple instances from count or for_each, you must create separate moved blocks for each one. Map the old indexed references to new addresses.
moved {
from = aws_instance.web[0]
to = aws_instance.web_primary
}
moved {
from = aws_instance.web[1]
to = aws_instance.web_secondary
}For large-scale migrations with many count or for_each instances, use the terraform state mv command to handle the refactoring before applying configuration changes. This bypasses the moved block limitation.
terraform state mv 'aws_instance.web[0]' 'aws_instance.web_primary'
terraform state mv 'aws_instance.web[1]' 'aws_instance.web_secondary'Alternatively, generate the moved blocks using an external script to avoid manual errors.
After removing count/for_each and creating moved blocks, run terraform plan to verify that the changes will update the state correctly.
terraform planThe plan should show that resources will be moved in the state without being destroyed and recreated. You should see entries indicating the moved resources with no creation or deletion.
Once the plan looks correct and shows moved entries with no destroys, apply the configuration to update the Terraform state.
terraform applyFor large-scale refactoring (e.g., migrating 50+ resources from count to for_each), manually creating moved blocks is error-prone. Consider generating moved blocks programmatically using a script or tool that parses your state file and configuration. Some teams use custom bash or Python scripts to generate the necessary moved block statements. If you are refactoring a module that is consumed by multiple users, remember that moved blocks should be retained indefinitely in shared modules to provide a clear migration path for module users upgrading versions. Terraform 1.1+ is required to use moved blocks; if you're stuck on an older version, use terraform state mv instead.
Error: Error installing helm release: cannot re-use a name that is still in use
How to fix "release name in use" error in Terraform with Helm
Error: Error creating GKE Cluster: BadRequest
BadRequest error creating GKE cluster in Terraform
Error: External program failed to produce valid JSON
External program failed to produce valid JSON
Error: Unsupported argument in child module call
How to fix "Unsupported argument in child module call" in Terraform
Error: network is unreachable
How to fix "network is unreachable" in Terraform