Terraform has some fabulous built-in abilities to change where its configuration is stored, but sometimes there's no getting around the need to do state surgery.
The Scenario
In this case, I have a repo with some Terraform configuration. Later, that repo became a module rather than an independent repo.
The straightforward path is to terraform destroy
the old resources and terraform apply
the new module.
However, there are resources that I want to preserve (S3 buckets, KMS keys...)
Lifecycle Rules
Before we proceed, make absolutely sure that the resources you want to move can't be deleted by accident.
Add the following to any resource to prevent Terraform from deleting it.
lifecycle {
prevent_destroy = true
}
If you issue a terraform destroy
while those resources are tracked, Terraform will not allow you to proceed.
The Move
The basic plan of attack is to make the source Terraform stop tracking the resource with terraform state rm
and get the destination Terraform to start with terraform import
.
Open a terminal in both the source and destination Terraform repos.
In this example, we're moving a KMS key.
resource "aws_kms_key" "foo" {
lifecycle {
prevent_destroy = true
}
}
This is known in the source Terraform as aws_kms_key.foo
and the destination as module.example-module.aws_kms_key.foo
.
Note that since the repo is a module in the destination that its name is prefixed with module.${MODULE_NAME}.
Get its state from the source
In the source repo
source> terraform state show aws_kms_key.foo
id = aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee
arn = arn:aws:kms:us-west-2:REDACTED:key/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee
enable_key_rotation = false
is_enabled = true
key_id = aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee
key_usage = ENCRYPT_DECRYPT
policy = ...
Specifically what we are looking for is the first line, the id
.
Or for simplicity, terraform state show aws_kms_key.foo | head -n1
.
We could tell Terraform to stop tracking it at this point, but let's do so after the import to be safe.
Import it into the destination
In your destination repo
destination> terraform import module.example-module.aws_kms_key.foo aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee
module.example-module.aws_kms_key.foo: Importing from ID "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"...
module.example-module.aws_kms_key.foo: Import complete!
Imported aws_kms_key (ID: aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee)
module.example-module.aws_kms_key.foo: Refreshing state... (ID: aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee)
Import successful!
If this step gives you any trouble, the Terraform docs always give an example of how to import something.
Remove state from the source
Back in your source repo, rm
the resource from the state. This will preserve it from any future destroy
operations.
source> terraform state rm aws_kms_key.foo
1 items removed.
Item removal successful.
Wrap-up
That's it. Repeat for all your critical resources.
At the end you get to terraform destroy
your old repo and terraform apply
your new one.
Top comments (2)
++
Nice article Sam. Not a topic many people touch on but an important one to know about for sure. Well done examples as well.