DEV Community

Sam Myers
Sam Myers

Posted on

How To Move Individual Resources Across Terraform Backends

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)

Collapse
 
ckousik profile image
Chinmay Kousik

++

Collapse
 
david_j_eddy profile image
David J Eddy

Nice article Sam. Not a topic many people touch on but an important one to know about for sure. Well done examples as well.