terraform state mv is a command that lets you rename resources in your Terraform state file without actually destroying and recreating them.
Let’s see it in action. Imagine you have an AWS S3 bucket defined like this in your main.tf:
resource "aws_s3_bucket" "old_bucket_name" {
bucket = "my-unique-old-bucket-name"
acl = "private"
tags = {
Name = "My Old Bucket"
Environment = "Dev"
}
}
And your state file reflects this resource named aws_s3_bucket.old_bucket_name. Now, you decide to rename this resource to aws_s3_bucket.new_bucket_name in your Terraform code:
resource "aws_s3_bucket" "new_bucket_name" {
bucket = "my-unique-old-bucket-name" # Bucket name stays the same!
acl = "private"
tags = {
Name = "My New Bucket Name" # You can also rename tags or other properties
Environment = "Dev"
}
}
If you were to just run terraform apply after changing the resource name in your .tf file, Terraform would think old_bucket_name is gone and new_bucket_name is a brand new resource. It would then try to create a new S3 bucket with the same configuration, and since S3 bucket names must be globally unique, this would likely fail. Even if it didn’t fail immediately, it would mark the old resource as "destroyed" and the new one as "created," which isn’t what you want for a simple rename.
This is where terraform state mv comes in. It directly manipulates the Terraform state file to reflect the rename without triggering any infrastructure changes.
To perform the rename, you’d execute the following command in your terminal:
terraform state mv aws_s3_bucket.old_bucket_name aws_s3_bucket.new_bucket_name
After running this command, Terraform updates its state file. The resource aws_s3_bucket.old_bucket_name will no longer be present in the state, and aws_s3_bucket.new_bucket_name will now be there, pointing to the existing S3 bucket.
When you then run terraform plan or terraform apply, Terraform will see that the resource aws_s3_bucket.new_bucket_name already exists in the state and matches the configuration in your .tf file. It will correctly identify that no changes are needed for this resource.
This command is incredibly useful for refactoring your Terraform code. You can rename resources for better organization, to align with naming conventions, or to split a monolithic resource into smaller, more manageable ones (though splitting often requires more than just mv). It also works for moving resources between modules. For example, if you decided to move aws_s3_bucket.new_bucket_name from the root module to a storage module, you would run:
terraform state mv aws_s3_bucket.new_bucket_name module.storage.aws_s3_bucket.new_bucket_name
And update your .tf files accordingly.
The core problem terraform state mv solves is the disconnect between your code and the actual state of your infrastructure. Terraform relies on the state file as the single source of truth for what it manages. When you change resource names or move them, the state file needs to be updated to match your intentions before Terraform tries to reconcile the difference. terraform state mv provides a safe, controlled way to perform these structural changes to your managed infrastructure without incurring the cost or risk of destruction and recreation.
A subtle but critical aspect of terraform state mv is that it does not validate the existence of the resource in the cloud provider. It only modifies the state file. If you try to move a resource that doesn’t actually exist in your AWS account (perhaps it was manually deleted or never created), terraform state mv will still happily update the state file. The problem will then manifest during your next terraform plan or apply, where Terraform will attempt to manage a resource in the state that it can’t find in the cloud, leading to a recreation error. This is why it’s best practice to run terraform plan before terraform state mv to ensure the resource is currently managed and accounted for, and then terraform plan again after the mv command to confirm no infrastructure changes are planned.
After successfully renaming resources with terraform state mv and confirming no changes with terraform plan, the next logical step in refactoring might involve updating resource dependencies or configuring new resources that interact with the renamed one.