The terraform state mv
command ensures smooth transitions when renaming resources, reorganizing configurations, or splitting state files. In this article, we explore this command in-depth, with step-by-step examples for handling single resources, modules, for_each scenarios, and cross-file migrations.
terraform state mv
is a Terraform command used to move items within the Terraform state. It allows you to rename resources or modules within the Terraform state file without recreating or destroying them, which is particularly useful when refactoring configurations or reorganizing Terraform resources.
terraform state mv [options] SOURCE DESTINATION
SOURCE
: The current address of the resource, module, or instance in the state file (e.g.,module.old.module.resource_name
).DESTINATION
: The new address in the state file where you want the resource or module moved (e.g.,module.new.resource_name
).[options]
: Optional flags, such as:
-
-state
: Path to the state file to use.-state-out
: Path to write the updated state file if different from the input state.-lock
: Enables or disables state file locking during the operation.-lock-timeout=TIMEOUT
: Specifies the duration to wait for a lock on the state file before failing.
The terraform state mv
command only affects the Terraform state file because it does not change the actual infrastructure. However, improper use of the command can lead to mismatches between the state and the infrastructure. Always validate the changes after running this command.
What does terraform state mv do?
The terraform state mv
command moves resources or modules from one location in Terraform’s state file to another. Here are some scenarios when it can be useful:
- Renaming resources: When you change the name of a resource in your configuration file, you can use
terraform state mv
to update the state file to reflect the new name, avoiding resource destruction and recreation. - Reorganizing resources: If you move resources into modules or between modules,
terraform state mv
allows you to keep the state in sync with your new configuration structure. - Fixing state issues:
terraform state mv
helps correct discrepancies between your Terraform configuration and the state.
What is the difference between terraform state mv and terraform import?
The difference between terraform state mv
and terraform import
lies in their purposes and how they interact with Terraform state. terraform state mv
is used to move an existing resource within your Terraform state, and the terraform import
command is used to import an existing resource that was created outside of Terraform into your Terraform state.
In short, terraform state mv
rearranges existing resources within the state, while terraform import
brings external resources under Terraform’s management.
Let’s see some examples.
Example 1: Moving a single resource
Let’s suppose we initially created an AWS S3 bucket called aws_s3_bucket.my_bucket
in your Terraform configuration. Later, we decide to rename the resource to aws_s3_bucket.main_bucket
.
After updating the configuration, the Terraform state still retains the old name (aws_s3_bucket.my_bucket
), and if the state is not updated Terraform will try to recreate the bucket with the new name.
To avoid this, we would run:
terraform state mv aws_s3_bucket.my_bucket aws_s3_bucket.main_bucket
Terraform will no longer attempt to recreate the resource, as the state reflects the new configuration.
Example 2: Moving multiple resources
In this example, we have multiple AWS EC2 instances in our root module:
resource "aws_instance" "web1" {
ami = "ami-12345678"
instance_type = "t2.micro"
}
resource "aws_instance" "web2" {
ami = "ami-12345678"
instance_type = "t2.micro"
}
resource "aws_instance" "web3" {
ami = "ami-12345678"
instance_type = "t2.micro"
}
We want to move from the root module to a nested module (module.instances
). If the resources are updated in the configuration to be within the module, the Terraform state needs to reflect this change.
# Updated Configuration
module "instances" {
source = "./modules/instances"
instances = [
{
ami = "ami-12345678"
instance_type = "t2.micro"
},
{
ami = "ami-12345678"
instance_type = "t2.micro"
},
{
ami = "ami-12345678"
instance_type = "t2.micro"
},
]
}
Instead of moving each resource individually, we can use the terraform state mv
command for each one:
terraform state mv aws_instance.web1 module.instances.aws_instance.web1
terraform state mv aws_instance.web2 module.instances.aws_instance.web2
terraform state mv aws_instance.web3 module.instances.aws_instance.web3
Note: There is no single bulk operation for moving multiple resources at once.
Example 3: Moving resources between state files
Let’s consider a scenario where we want to split your Terraform configuration into separate state files, such as when moving resources from one state file (state1.tfstate
) to another (state2.tfstate
).
Suppose you have an AWS RDS instance defined as follows in state1.tfstate
:
resource "aws_db_instance" "database" {
allocated_storage = 20
engine = "mysql"
instance_class = "db.t2.micro"
name = "mydatabase"
username = "admin"
password = "password123"
}
We want to move this resource to another state file (state2.tfstate
) for better state management. We will run the following command:
terraform state mv -state-out=state2.tfstate aws_db_instance.database
The -state-out
flag specifies the target state file (state2.tfstate
) to which the resource will be moved. Terraform updates state2.tfstate
to include the resource and removes it from state1.tfstate
.
Always validate the changes by inspecting both state files using terraform show
to ensure the resource was transferred correctly.
Example 4: Moving a resource into a module
In this example, we defined an AWS IAM role in the root module as aws_iam_role.my_role
:
resource "aws_iam_role" "my_role" {
name = "my-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "ec2.amazonaws.com"
}
}]
})
}
Now we want to organize your configuration by moving the role into a new module called roles
.
module "roles" {
source = "./modules/roles"
iam_roles = [
{
name = "my-role"
assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [{
Action = "sts:AssumeRole"
Effect = "Allow"
Principal = {
Service = "ec2.amazonaws.com"
}
}]
})
}
]
}
After updating our configuration to place the resource within the module, the Terraform state must be updated to reflect the new structure.
terraform state mv aws_iam_role.my_role module.roles.aws_iam_role.my_role
Terraform now recognizes the IAM role within the roles module and won’t attempt to recreate it.
Example 5: Renaming a resource across modules
Let’s suppose you have a resource module.network.aws_vpc.main
in a module named network
. We want to rename this VPC to module.network.aws_vpc.primary
within the same module.
module "network" {
source = "./modules/network"
vpc_name = "main-vpc"
}
module "network" {
source = "./modules/network"
vpc_name = "primary-vpc"
}
The terraform state mv
command updates the state to recognize the VPC under its new name, avoiding recreation of the resource.
terraform state mv module.network.aws_vpc.main module.network.aws_vpc.primary
variable "bucket_names" {
default = ["bucket1", "bucket2", "bucket3"]
}
resource "aws_s3_bucket" "buckets" {
for_each = toset(var.bucket_names)
bucket = each.key
}
We now want to refactor our configuration and move them into a new module called buckets
.
module "buckets" {
source = "./modules/buckets"
bucket_names = ["bucket1", "bucket2", "bucket3"]
}
modules/buckets/main.tf
variable "bucket_names" {
type = list(string)
}
resource "aws_s3_bucket" "buckets" {
for_each = toset(var.bucket_names)
bucket = each.key
}
For each bucket, you need to move its state individually:
terraform state mv aws_s3_bucket.buckets["bucket1"] module.buckets.aws_s3_bucket.buckets["bucket1"]
terraform state mv aws_s3_bucket.buckets["bucket2"] module.buckets.aws_s3_bucket.buckets["bucket2"]
terraform state mv aws_s3_bucket.buckets["bucket3"] module.buckets.aws_s3_bucket.buckets
Ensure that each source and destination address is complete and correct to avoid state mismatch or duplication.
Terraform is really powerful, but to achieve an end-to-end secure Gitops approach, you need to use a product that can run your Terraform workflows. Spacelift takes managing Terraform to the next level by giving you access to a powerful CI/CD workflow and unlocking features such as:
- Policies (based on Open Policy Agent) – You can control how many approvals you need for runs, what kind of resources you can create, and what kind of parameters these resources can have, and you can also control the behavior when a pull request is open or merged.
- Multi-IaC workflows – Combine Terraform with Kubernetes, Ansible, and other infrastructure-as-code (IaC) tools such as OpenTofu, Pulumi, and CloudFormation, create dependencies among them, and share outputs
- Build self-service infrastructure – You can use Blueprints to build self-service infrastructure; simply complete a form to provision infrastructure based on Terraform and other supported tools.
- Integrations with any third-party tools – You can integrate with your favorite third-party tools and even build policies for them. For example, see how to Integrate security tools in your workflows using Custom Inputs.
Spacelift enables you to create private workers inside your infrastructure, which helps you execute Spacelift-related workflows on your end. Read the documentation for more information on configuring private workers.
You can check it for free by creating a trial account or booking a demo with one of our engineers.
The terraform state mv
command enables precise state management by relocating resources within or across modules and state files. It prevents resource recreation during refactoring or renaming, maintaining alignment between the state and configuration.
Remember that improper use of terraform state mv
can result in state drift or resource duplication, so always back up your state files, validate the changes, and test the configuration after running the command.
Note: New versions of Terraform are placed under the BUSL license, but everything created before version 1.5.x stays open-source. OpenTofu is an open-source version of Terraform that expands on Terraform’s existing concepts and offerings. It is a viable alternative to HashiCorp’s Terraform, being forked from Terraform version 1.5.6.
Manage Terraform better and faster
If you are struggling with Terraform automation and management, check out Spacelift. It helps you manage Terraform state, build more complex workflows, and adds several must-have capabilities for end-to-end infrastructure management.