Starting from v1.1, Terraform provides a powerful feature known as the moved
block. This feature allows you to reorganize your Terraform configuration without causing Terraform to perceive the refactor as a deletion and creation of resources.
In this article, we will walk through a few examples of Terraform refactoring using the moved
block.
Prerequisites
- Terraform (>=1.1)
Move a Resource Into Module
First, we will create a sample S3 bucket to reference as a standalone resource. In your Terraform configuration directory, create a new ./lab-demo/s3.tf
file.
resource "aws_s3_bucket" "moved_demo" {
bucket = "moved-demo"
tags = {
env = "lab"
}
}
Now, let’s create a module directory ./modules/aws/s3
and move the ./lab-demo/s3.tf
into ./modules/aws/
. The directory structure should look like this:
├── lab-demo
│ ├── main.tf
│ ├── s3.tf # Moved to ./modules/aws/s3/s3.tf
│ ├── modules
│ └── aws
│ └── s3
│ └── s3.tf
Since we have the resource in the module, we need to update the reference in the ./lab-demo/main.tf
file.
module "s3" {
source = "./modules/aws/s3"
}
If we run terraform init && terraform plan
now, Terraform will propose to destroy and recreate the same S3 bucket. This is because Terraform thinks that the object reference does not exist anymore.
# aws_s3_bucket.moved_demo will be destroyed
# (because aws_s3_bucket.moved_demo is not in configuration)
...
...
# module.s3.aws_s3_bucket.moved_demo will be created
To fix this, we need to add a moved
block to the ./lab-demo/main.tf
file.
module "s3" {
source = "./modules/aws/s3"
}
moved {
from = aws_s3_bucket.moved_demo
to = module.s3.aws_s3_bucket.moved_demo
}
If you do the terraform plan
again, Terraform will recognize the move and not propose to destroy and recreate the resource.
# aws_s3_bucket.moved_demo has moved to module.s3.aws_s3_bucket.moved_demo
resource "aws_s3_bucket" "moved_demo" {
id = "moved-demo"
tags = {
"env" = "lab"
}
}
Plan: 0 to add, 0 to change, 0 to destroy.
Once you apply the configuration, you can remove the moved
block from the ./lab-demo/main.tf
file.
Moving Resource Between Modules
The moved
block can also be used to move resources between modules. Let’s create a new module ./modules/aws/another_module
and move the ./modules/aws/s3/s3.tf
into ./modules/aws/another_module
. The directory structure should look like this:
├── lab-demo
│ ├── main.tf
│ ├── modules
│ └── aws
│ ├── s3
│ └── another_module
│ └── s3.tf # Moved from ./modules/aws/s3/s3.tf
Since we have the resource in the module, we need to update the reference in the ./lab-demo/main.tf
file.
module "s3" {
source = "./modules/aws/another_module"
}
In order to move the resource from s3
module another_module
, we can use the same moved
block to the ./lab-demo/main.tf
file.
module "s3" {
source = "./modules/aws/s3"
}
moved {
from = module.s3.aws_s3_bucket.moved_demo
to = module.another_module.aws_s3_bucket.moved_demo
}
And the terraform plan
will show the resource move.
# module.s3.aws_s3_bucket.moved_demo has moved to module.another_module.aws_s3_bucket.moved_demo
resource "aws_s3_bucket" "b" {
id = "moved-demo"
tags = {
"env" = "lab"
}
}
Plan: 0 to add, 0 to change, 0 to destroy.
Renaming a Module
Similarly, we can also rename the module using the moved
block. Let’s rename the another_module
to s3
and move the resource back to the s3
module. Below is an example main.tf
file.
## Before: main.tf
module "s3" {
source = "./modules/aws/s3"
}
## After: main.tf
module "s3_renamed_module" {
source = "./modules/aws/s3"
}
moved {
from = module.s3
to = module.s3_renamed_module
}
In this case, all the resources in the s3
module will be moved to the s3_renamed_module
module.
Resources :
https://developer.hashicorp.com/terraform/language/v1.1.x/modules/develop/refactoring