Reputation: 4955
What is the least painful way to migrate state of resources from one project (i.e., move a module invocation) to another, particularly when using remote state storage? While refactoring is relatively straightforward within the same state file (i.e., take this resource and move it to a submodule or vice-versa), I don't see an alternative to JSON surgery for refactoring into different state files, particularly if we use remote (S3) state (i.e., take this submodule and move it to another project).
Upvotes: 66
Views: 51071
Reputation: 1346
If you have the state already in a local file (here tfstate.json) you can initialize the new backend and push the state.
terraform init -backend-config=backend.config
terraform state push tfstate.json
Upvotes: 0
Reputation: 627
With terraform version 1.7+ you could solve this declaratively using removed
and import
blocks, for resources that are importable.
# In source state:
removed {
from = random_uuid.example
lifecycle {
destroy = false
}
}
# In destination state:
import {
to = random_uuid.example
id = "aabbccdd-eeff-0011-2233-445566778899"
}
resource "random_uuid" "example" {}
Upvotes: 1
Reputation: 4454
I use this script (not work from v0.12) to migrate the state while refactoring. Feel free to adopt it to your need.
src=<source dir>
dst=<target dir>
resources=(
aws_s3_bucket.bucket1
aws_iam_role.role2
aws_iam_user.user1
aws_s3_bucket.bucket2
aws_iam_policy.policy2
)
cd $src
terraform state pull >/tmp/source.tfstate
cd $dst
terraform state pull >/tmp/target.tfstate
for resource in "${resources[@]}"; do
terraform state mv -state=/tmp/source.tfstate -state-out=/tmp/target.tfstate "${resource}" "${resource}"
done
terraform state push /tmp/target.tfstate
cd $src
terraform state push /tmp/source.tfstate
Note that terraform pull
is deprecated from v0.12 (but not removed and still works), and terraform push
does not work anymore from v0.12.
Important: The terraform push command is deprecated, and only works with the legacy version of Terraform Enterprise. In the current version of Terraform Cloud, you can upload configurations using the API. See the docs about API-driven runs for more details.
==================
Below are unrelated to the OP:
If you are renaming your resources in the same project.
terraform state mv ...
.moved
statement described: here or here.There are several other useful commands that I listed in my blog
Upvotes: 8
Reputation: 12788
The least painful way I’ve found is to pull both remote states local, move the modules/resources between the two, then push back up. Also remember, if you’re moving a module, don’t move the individual resources; move the whole module.
For example:
cd dirA
terraform state pull > ../dirA.tfstate
cd ../dirB
terraform state pull > ../dirB.tfstate
terraform state mv -state=../dirA.tfstate -state-out=../dirB.tfstate module.foo module.foo
terraform state push ../dirB.tfstate
# verify state was moved
terraform state list | grep foo
cd ../dirA
terraform state push ../dirA.tfstate
Unfortunately, the terraform state mv command
doesn’t support specifying two remote backends, so this is the easiest way I’ve found to move state between multiple remotes.
Upvotes: 143
Reputation: 5868
As mentioned in a related Terraform Q -> Best practices when using Terraform
- It is easier and faster to work with smaller number of resources:
- Cmds
terraform plan
andterraform
apply both make cloud API calls to verify the status of resources.- If you have your entire infrastructure in a single composition this can take many minutes (even if you have several files in the same folder).
So if you'll end up with a mono-dir with every resource, never is late to start segregating them by service, team, client, etc.
Suppose we have a folder named common
with all our .tf
files for a certain project and we decided to divide (move) our .tf
Terraform resources to a new project folder named security
. so we now need to move some resources from common
project folder to security
.
If the security
folder still does not exists (which is the best scenario).
common
execute make init
to be sure your .terraform
local folder it's synced with your remote state.security
folder still does not exists (which should be true) clone (copy) the common
folder with the destination name security
and update the config.tf
file inside this new cloned folder to point to the new S3 backend path (consider updating 1 account at a time starting with the less critical one and evaluate the results with terraform state list
).eg:
# Backend Config (partial)
terraform {
required_version = ">= 0.11.14"
backend "s3" {
key = "account-name/security/terraform.tfstate"
}
}
security
folder, run terraform-init
(without removing the copied .terraform
local folder, which was already generated and synced in step 2) which, as a result, will generate a new copy of the resources state (interactively asking) in the new S3 path. This is a safe operation since we haven't removed the resources from the old .tfstate
path file yet.$ make init
terraform init -backend-config=../config/backend.config
Initializing modules...
- module.cloudtrail
- module.cloudtrail.cloudtrail_label
Initializing the backend...
Backend configuration changed!
Terraform has detected that the configuration specified for the backend
has changed. Terraform will now check for existing state in the backends.
Acquiring state lock. This may take a few moments...
Acquiring state lock. This may take a few moments...
Do you want to copy existing state to the new backend?
Pre-existing state was found while migrating the previous "s3" backend to the
newly configured "s3" backend. No existing state was found in the newly
configured "s3" backend. Do you want to copy this state to the new "s3"
backend? Enter "yes" to copy and "no" to start with an empty state.
Enter a value: yes
...
Successfully configured the backend "s3"! Terraform will automatically
use this backend unless the backend configuration changes.
Initializing provider plugins...
...
Terraform has been successfully initialized!
...
terraform state rm module.foo
) in order to keep the desired ones in /common
and /security
paths. Moreover, It's a must to carry out in parallel the necessary updates (add/remove) of the modules/resources from your .tf files in each folder to keep both your local code base declaration and your remote .tfstate
in sync. This is a sensible operation, please start by testing the procedure in the less critical possible single resource. As reference we can consider the following doc and tools:
If the security
folder already exists and has it's associated remote .tfstate in its AWS S3 path you'll need to use a different sequence of steps and commands, possible the ones referenced in the links below:
1. https://www.terraform.io/docs/commands/state/list.html
2. https://www.terraform.io/docs/commands/state/pull.html
3. https://www.terraform.io/docs/commands/state/mv.html
4. https://www.terraform.io/docs/commands/state/push.html
Ref links:
Upvotes: 6
Reputation: 56997
Probably the simplest option is to use terraform import
on the resource in the new state file location and then terraform state rm
in the old location.
Terraform does handle some automatic state migration when copying/moving the .terraform folder around but I've only used that when shifting the whole state file rather than part of it.
Upvotes: 15