5

Here is a scenario - you develop terraform modules for use in your R&D organisation. They are in use by one or two micro services already, which translates into a dozen pods. You identify a refactoring opportunity, like pulling out some functionality into its own terraform module. Great, but now you have to update a dozen terraform states, because this is the price of terraform code refactoring.

After one such refactoring I found myself spending 8 hours to update all the terraform states. I do it in an ad hoc fashion - with powershell scripts wrapping the various terraform state commands. One can quickly loose one's sanity doing it too often.

Of course, we could say - do not refactor. But this is not possible, because terraform code is ... code. So, is there a better way to do it? Some kind of a tool that can help, somehow?

mark
  • 59,016
  • 79
  • 296
  • 580
  • Wrapper script is basically the way you have to go, but you can leverage more powerful interpretive languages that may already have TF bindings like Python and Ruby. – Matthew Schuchard Jun 12 '20 at 18:13
  • 1
    What do you mean by TF bindings? Do you mean that when I rename a terraform resource name (address in TF parlance) they automatically reach out to all the terraform remote states containing the resource and execute **terraform state mv** ? – mark Jun 12 '20 at 21:25
  • I also feel that is one of the great flaws of terraform, refactoring is so hard. It would be nice to couple the state move and the upgrade to the declarative state in an atomic way. I've often thought it would be possible to write such a tool, but not easy. You would want to back the state, validate some preconditions, move the state, then apply the update to modules and tf code. Then validate the plan doesn't show anything studpid. But to make it work you need to not change anything else, and that can be hard to avoid. – David Roussel Jun 15 '20 at 20:33
  • Have you seen this Terraform documentation on refactoring? https://developer.hashicorp.com/terraform/language/modules/develop/refactoring – Brent Bradburn May 10 '23 at 04:20
  • @BrentBradburn - yes, definitely. This question was created before the `moved` structure was introduced. I use `moved` often, it is a life saver. This used to be a nightmare, now it is manageable. I forgot about this question, but your comment is timely - please, arrange it as an answer and I will credit you. – mark May 10 '23 at 15:54

2 Answers2

1

I recommend using terraform-state-mover.

It offers an interactive prompt for the terraform state mv command. There is a short demo on github that shows how to use it.

Pascal Hofmann
  • 974
  • 9
  • 7
0

If you maintain a script that imports the complete state, then, at any time, you can discard your local state and do a fresh import.

When using this approach, you won't need to mv state around to support a Terraform code refactor.


This kind of bulk import is well suited to a simple scripting language.

Here, I'm using workspaces to store local state...

#! /usr/bin/env bash

set -e ; set -o pipefail

stack=$(terraform workspace list |grep '*' |cut -d' ' -f 2)

# workspace-specific values
if [[ "${stack}" == "dev" ]] ; then
    cdnid=ABCD12345
    origin_access_identity=ABCD23456
elif [[ "${stack}" == "stg" ]] ; then
    cdnid=ABCD34567
    origin_access_identity=ABCD45678
fi

echo "stack: ${stack}"
echo "cdnid: ${cdnid}"
echo "origin_access_identity: ${origin_access_identity}"

## discard local state (with backup)
( cd terraform.tfstate.d/${stack}/ && mv terraform.tfstate terraform.tfstate.$(date +%s).backup ) ||true

terraform import module.bucket.aws_s3_bucket_policy.bucket_policy stuff-${stack}
terraform import module.bucket.aws_s3_bucket_versioning.bucket_versioning stuff-${stack}
terraform import module.bucket.aws_s3_bucket_acl.bucket_acl stuff-${stack}
terraform import module.bucket.aws_s3_bucket_request_payment_configuration.bucket_request_payment_configuration stuff-${stack}
terraform import module.bucket.aws_s3_bucket.bucket stuff-${stack}
terraform import aws_cloudfront_origin_access_identity.origin_access ${origin_access_identity}
terraform import aws_cloudfront_distribution.cdn ${cdnid}

You could run it like this...

$ terraform workspace select dev && bash -x ./import.sh

Then you should get something like this...

$ terraform workspace select dev && terraform plan

No changes. Your infrastructure matches the configuration.

Terraform has compared your real infrastructure against your configuration and found no differences, so no changes are needed.

I imagine that this would be a built-in feature of Terraform if it weren't for the complexity of identifying and associating resource IDs.

Brent Bradburn
  • 51,587
  • 17
  • 154
  • 173
  • Suggestion for using workspaces safely: `plan dev` using `function plan { terraform workspace select $1 && terraform plan ; }` – Brent Bradburn Aug 14 '22 at 19:52
  • Even just making an existing resource/module [conditional](https://stackoverflow.com/q/66522967/86967) requires reworking the state. Probably better to just rework the import script (using [Bash](https://stackoverflow.com/q/13542832/86967) [conditionals](https://stackoverflow.com/q/2953646/86967), for example). – Brent Bradburn Aug 15 '22 at 22:12
  • A related (but less powerful) existing feature is [`terraform refresh`](https://stackoverflow.com/q/42628660/86967), which similarly downloads a fresh copy of the state. However, `refresh` doesn't solve the refactoring problem because it won't know how to map the existing resources to their new state names. – Brent Bradburn Aug 22 '22 at 17:18
  • There is an open source CLI tool [Terraformer](https://github.com/GoogleCloudPlatform/terraformer) that might further streamline the process of bulk-import. Some discussion: [How can I import all the existing infrastructure state from aws and compare it with the generated reverse-terraformed resources?](https://stackoverflow.com/q/63229803/86967) – Brent Bradburn Aug 27 '22 at 22:22
  • I was looking into [terraform-backend-git](https://github.com/plumber-cd/terraform-backend-git) and it mentions that [Terraform Cloud](https://cloud.hashicorp.com/products/terraform) "provides state management as a service". I hope this doesn't get in the way of eventually eliminating the vestigial state management altogether. – Brent Bradburn Aug 28 '22 at 06:11
  • Saved state is a handy feature for streamlining a couple of workflows, but it's barely used in the normal case: [2017>"we will want to do more validation at plan time and some of it will inevitably involve reaching out to services for information"](https://github.com/hashicorp/terraform/issues/14254#issuecomment-308323084) – Brent Bradburn Oct 11 '22 at 01:57
  • This quote, from old Terraform docs, describes the (somewhat) necessary aspect of state files ("it maps"). Often, these mappings are deterministic from the .tf code, but there are some cases where .tfstate tracks generated (unpredictable) but stable resource IDs as the remote value of the mapping -- and that's what has to be maintained and shared outside of the .tf source: "This state file is extremely important; it maps various resource metadata to actual resource IDs so that Terraform knows what it is managing. This file must be saved and distributed to anyone who might run Terraform." – Brent Bradburn Oct 11 '22 at 15:05
  • Though-provoking question and answers including some historical documentation about state file storage and locking: [Should I commit .tfstate files to Git?](https://stackoverflow.com/q/38486335/86967) – Brent Bradburn Oct 21 '22 at 14:51
  • Just doing `terraform plan` effectively invalidates and refreshes the stored state. So what's the point, actually? – Brent Bradburn Jan 30 '23 at 07:07
  • BTW: [1.3.7] `terraform state mv` has a bug in which it ignores local `backend.workspace_dir`, or at least modifies it to ignore a leading `.`. – Brent Bradburn Feb 01 '23 at 03:42
  • `terraform plan -refresh=false -out tfplan` can be a real time-saver for configs with slow refresh -- but maybe a little more dangerous. – Brent Bradburn Feb 02 '23 at 05:25
  • Although it's easy to write this in Bash, a nice improvement would be to use a more powerful/expressive language that also facilitates concurrency. – Brent Bradburn Feb 25 '23 at 14:33
  • Tangent topic: I can't understand why Terraform doesn't have a DynamoDB backend. Why would you need to use S3 for storage when [you need to use DynamoDB for locking anyway](https://developer.hashicorp.com/terraform/language/settings/backends/s3). Oh yes, I remember the reason they don't create sane backends -- their revenue comes from [Terraform Cloud](https://developer.hashicorp.com/terraform/cloud-docs/overview#remote-state-management-data-sharing-and-run-triggers). Incidentally, Terraform Cloud is based on using 'workspaces' (as is my demo above). – Brent Bradburn Mar 05 '23 at 00:33
  • The aforementioned bug with `terraform state mv` only seems to affect the creation of a backup -- so a workaround is to create a temporary folder with the bugged name to receive the backup. – Brent Bradburn Mar 21 '23 at 03:16
  • Similar line of thought: [Import all resources defined in tf file](https://stackoverflow.com/q/47613926/86967) – Brent Bradburn May 05 '23 at 20:49
  • Due to provider limitations (I guess), not all resources are importable: [Resource Importability](https://developer.hashicorp.com/terraform/cli/import/importability), [Resources - Import](https://developer.hashicorp.com/terraform/plugin/sdkv2/resources/import), [Implement Import](https://developer.hashicorp.com/terraform/tutorials/providers/provider-import) – Brent Bradburn May 05 '23 at 21:46
  • There's a native way to do this as of Terraform 1.5: [import blocks](https://developer.hashicorp.com/terraform/language/import). TBD [how to use it with workspaces](https://stackoverflow.com/q/76486088/86967). – Brent Bradburn Jun 15 '23 at 23:29
  • "I imagine that this would be a built-in feature of Terraform if it weren't for the complexity of identifying and associating resource IDs." An easy to overlook detail is that _a lot_ of resource names are deterministic based on the resource parameters. It's only the rare exception that actually requires capturing unpredictable resource ids. Handling this without full-blown state backends seems like it could done with only a small change to the existing workflows. – Brent Bradburn Jul 07 '23 at 00:53
  • Interesting thought showing the limited value of state (although 'data' items are included in `state list`): [Terraform import vs data sources](https://www.reddit.com/r/Terraform/comments/h8g8qq/terraform_import_vs_data_sources/) – Brent Bradburn Aug 15 '23 at 17:47
  • The fascinating question about Terraform's upcoming [fork in the road](https://opentf.org/) will be to see which branch is less encumbered by for-profit conflicts of interest. The supreme irony of Gruntwork assuming leadership of OpenTF is that ongoing Terraform improvements should continue to be geared towards making Terragrunt irrelevant. I just hope that they can appreciate that. – Brent Bradburn Aug 29 '23 at 15:28
  • Q: [What's the word for Unnecessary Complexity](https://english.stackexchange.com/q/339746/140227)? A: [Terragrunt](https://terragrunt.gruntwork.io/); see also: [Extraneous cognitive load](https://www.atlassian.com/blog/productivity/cognitive-overload) – Brent Bradburn Aug 30 '23 at 03:08