Renaming a Terraform module is not a cosmetic change. Terraform identifies managed objects by their full address. If you change a module name without telling Terraform how to move state, Terraform can interpret the rename as “delete everything in the old module” and “create everything in the new module.”
This guide shows the safest ways to rename a module without triggering resource recreation.
Step 1: Identify the current module name and usage
Start by locating the module you want to rename and understanding where and how it’s used.
Check:
- The module block label (for example, >module “network”)
- Any references like
module.network.*across the codebase - Whether the module uses
countorfor_each(this changes the address format) - What Terraform currently tracks in state under that module
To inspect state:
terraform state list | grep '^module\.<old_module_name>'If the module is instantiated with count or for_each, you’ll see addresses like module.network[0]... or module.network["prod"].... That detail matters for the move step.
Step 2: Update all module references
Once you understand the current usage, rename the module in your Terraform configuration.
For example, you might change:
module "network" {
source = "./modules/network"
}to:
module "vpc" {
source = "./modules/network"
}After renaming the module block, search your codebase for any references to the old module name and update them accordingly. This includes outputs, input variables, and any expressions that reference module.<old_name>.
At this point, avoid running terraform apply until the state has been updated.
Step 3: Choose your migration method
You have two safe options:
Option A (preferred): Use moved blocks (Terraform 1.1+)
If you’re on Terraform 1.1 or later, use moved blocks to declare the rename in configuration. This is cleaner than manual state surgery and makes the rename repeatable.
Add this to a .tf file in the same root module:
moved {
from = module.network
to = module.vpc
}If the module call uses count or for_each, a single moved block from module.network to module.vpc will apply to all instances (as long as the keys/indices don’t change). Only write per-instance moves when you’re also changing instance keys (e.g., refactoring between single → for_each, or changing keys).
moved {
from = module.network[0]
to = module.vpc[0]
}or:
moved {
from = module.network["prod"]
to = module.vpc["prod"]
}Run:
terraform planTerraform should show the address move and no create/destroy.
If you’re renaming a module call inside a child module, put the moved block in that same calling module (not somewhere else).
Keep the moved block around until you’re confident every environment/workspace has applied the change.
Option B: Use terraform state mv
If you can’t use moved blocks (older Terraform, or you need a one-time move), use terraform state mv.
Move the module address:
terraform state mv module.network module.vpcIf the module uses count/for_each, move each instance explicitly:
terraform state mv 'module.network[0]' 'module.vpc[0]'
terraform state mv 'module.network["prod"]' 'module.vpc["prod"]'After the move, verify:
terraform state list | grep '^module\.vpc'Step 4: Run terraform plan and validate changes
After updating both configuration and state, run a plan to verify Terraform’s understanding of the changes:
terraform planA correct rename should result in no resources being created or destroyed. If Terraform proposes infrastructure changes, stop and investigate before proceeding, as this usually indicates a missing reference or an incomplete state move.
Step 5: Apply the changes safely
Once the plan confirms that no infrastructure changes will occur, apply the configuration:
terraform applySince this change is purely logical, the apply step should complete quickly. Afterward, it’s a good practice to run another plan to ensure everything is fully aligned.
Is it possible to rename a module with one CLI command?
Terraform treats module names as part of a resource’s address, not as a mutable attribute. Because of this, renaming a module is considered a change in address, and Terraform expects you to explicitly tell it how state should move.
The closest Terraform provides is the terraform state mv command. This command lets you tell Terraform that resources tracked under one module address should now live under another. This CLI command moves all resources in state from the old module address to the new one, but it does not update your .tf files. You must still rename the module block in configuration manually.
Best practices for renaming Terraform modules
Whether it’s just a local refactor or a production-safe change, here are the best practices to follow when renaming Terraform modules:
- Treat the rename as an address change, not a cosmetic update – Renaming a module changes resource addresses. Without explicit state handling, Terraform will assume resources were removed and plan to recreate them.
- Prefer moved blocks or explicit state moves – Use
movedblocks (Terraform 1.1+) to declare the rename in code. If that’s not an option, useterraform state mvto preserve state during the rename. - Keep the change focused and isolated – Avoid combining a module rename with provider upgrades, internal refactors, or functional changes. A narrow scope makes the change easier to reason about and safer to apply.
- Validate the change with
terraform plan– Always run a plan after renaming and updating state. A correct rename should result in no planned resource creation or destruction. - Handle count and for_each modules carefully – Modules using
countorfor_eachrely on stable indices or keys. Make sure these don’t change during the rename, and move state at the correct module instance address (for example,module.old[0]→module.new[0], ormodule.old["key"]→module.new["key"]). - Apply safely and verify after the change – Use state locking, apply through your normal deployment workflow, and run a final plan after apply to confirm there’s no drift.
Key points
Renaming a Terraform module is an address migration. Update references in configuration, then use moved blocks (preferred) or terraform state mv to preserve state. A safe rename results in a plan with no resource creation or destruction.
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)
- Multi-IaC workflows
- Self-service infrastructure
- Integrations with any third-party tools
If you want to learn more about Spacelift, create a free account today or book a demo with one of our engineers.
Note: New versions of Terraform are released under the BUSL license, but everything created before version 1.5.x remains 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 with Spacelift
Build more complex workflows based on Terraform using policy as code, programmatic configuration, context sharing, drift detection, resource visualization and many more.
