{"id":13505868,"url":"https://github.com/busser/tfautomv","last_synced_at":"2025-04-14T15:45:54.220Z","repository":{"id":58591665,"uuid":"482225540","full_name":"busser/tfautomv","owner":"busser","description":"Generate Terraform moved blocks automatically for painless refactoring","archived":false,"fork":false,"pushed_at":"2025-02-21T21:40:18.000Z","size":13298,"stargazers_count":729,"open_issues_count":17,"forks_count":23,"subscribers_count":9,"default_branch":"main","last_synced_at":"2025-04-07T04:14:53.712Z","etag":null,"topics":["block","golang","hacktoberfest","move","refactor","refactoring","terraform"],"latest_commit_sha":null,"homepage":"https://github.com/busser/tfautomv","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/busser.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-04-16T10:38:07.000Z","updated_at":"2025-03-23T14:26:21.000Z","dependencies_parsed_at":"2023-02-10T19:31:32.946Z","dependency_job_id":"07a11e64-4009-4479-87de-13566f258f0e","html_url":"https://github.com/busser/tfautomv","commit_stats":{"total_commits":108,"total_committers":7,"mean_commits":"15.428571428571429","dds":0.6944444444444444,"last_synced_commit":"da21db0409921aa5d08d16911465f31717965e1a"},"previous_names":["busser/tfautomv","padok-team/tfautomv"],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/busser%2Ftfautomv","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/busser%2Ftfautomv/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/busser%2Ftfautomv/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/busser%2Ftfautomv/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/busser","download_url":"https://codeload.github.com/busser/tfautomv/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248908511,"owners_count":21181542,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["block","golang","hacktoberfest","move","refactor","refactoring","terraform"],"created_at":"2024-08-01T00:01:15.818Z","updated_at":"2025-04-14T15:45:54.194Z","avatar_url":"https://github.com/busser.png","language":"Go","funding_links":[],"categories":["Go","Tools","Repositories"],"sub_categories":["Community providers"],"readme":"# tfautomv \u003c!-- omit in toc --\u003e\n\n[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\n[![GitHub release](https://img.shields.io/github/release/busser/tfautomv.svg)](https://github.com/busser/tfautomv/releases/latest)\n[![Go Report Card](https://goreportcard.com/badge/github.com/busser/tfautomv)](https://goreportcard.com/report/github.com/busser/tfautomv)\n\nGenerate Terraform `moved` blocks automatically.\n\n- [Why?](#why)\n- [Requirements](#requirements)\n- [Installation](#installation)\n  - [Homebrew](#homebrew)\n  - [Yay](#yay)\n  - [asdf](#asdf)\n  - [Shell script](#shell-script)\n  - [Download](#download)\n  - [From source](#from-source)\n- [Usage](#usage)\n  - [Generating `moved` blocks](#generating-moved-blocks)\n  - [Generating `terraform state mv` commands](#generating-terraform-state-mv-commands)\n  - [Finding moves across multiple directories](#finding-moves-across-multiple-directories)\n  - [Skipping the `init` and `refresh` steps](#skipping-the-init-and-refresh-steps)\n  - [Understanding why a resource was not moved](#understanding-why-a-resource-was-not-moved)\n  - [Ignoring certain differences](#ignoring-certain-differences)\n    - [The `everything` kind](#the-everything-kind)\n    - [The `whitespace` kind](#the-whitespace-kind)\n    - [The `prefix` kind](#the-prefix-kind)\n    - [Referencing nested attributes](#referencing-nested-attributes)\n  - [Passing additional arguments to Terraform](#passing-additional-arguments-to-terraform)\n  - [Using Terragrunt instead of Terraform](#using-terragrunt-instead-of-terraform)\n  - [Disabling colors in output](#disabling-colors-in-output)\n- [Thanks](#thanks)\n- [License](#license)\n\n## Why?\n\n`tfautomv` (a.k.a Terraform auto-move) is a refactoring helper. With it, making\nstructural changes to your Terraform codebase becomes much easier.\n\nWhen you move a resource in your code, Terraform loses track of the resource's\nstate. The next time you run Terraform, it will plan to delete the resource it\nhas memory of and create the \"new\" resource it found in your refactored code.\n\n`tfautomv` inspects the output of `terraform plan`, detects such\ncreation/deletion pairs and writes a [`moved` block](https://developer.hashicorp.com/terraform/language/modules/develop/refactoring#moved-block-syntax)\nso that Terraform now knows no deletion or creation is required.\n\nWe explain why we built tfautomv in more detail [in this blog article](https://www.padok.fr/en/blog/terraform-refactoring-tfautomv).\n\nHere's a quick view of what `tfautomv` does:\n\n![demo](./docs/content/getting-started/demo.gif)\n\n## Requirements\n\n`tfautomv` uses the Terraform CLI command under the hood. This allows it to work\nwith any Terraform version reliably.\n\nCertain features require specific versions of Terraform:\n\n- `moved` blocks require Terraform v1.1 or above\n- cross-module `terraform state mv` commands require Terraform v0.14 or above\n- single-module `terraform state mv` commands require Terraform v0.13 or above\n\n## Installation\n\n_Contributions to support other installation methods are welcome!_\n\n### Homebrew\n\nOn MacOS or Linux:\n\n```bash\nbrew install busser/tap/tfautomv\n```\n\n### Yay\n\nOn Arch Linux:\n\n```bash\nyay tfautomv-bin\n```\n\n### asdf\n\nWith asdf version manager:\n\n```bash\nasdf plugin add tfautomv https://github.com/busser/asdf-tfautomv.git\n```\n\n### Shell script\n\nOn MacOS or Linux:\n\n```bash\ncurl -sSfL https://raw.githubusercontent.com/busser/tfautomv/main/install.sh | sh\n```\n\n_This script can probably support Windows with a small amount of work.\nContributions welcome!_\n\n### Download\n\nOn the Github repository's [Releases page](https://github.com/busser/tfautomv/releases),\ndownload the binary that matches your workstation's OS and CPU architecture.\n\nPut the binary in a directory present in your system's `PATH` environment\nvariable.\n\n### From source\n\nYou must have Go 1.18+ installed to compile tfautomv.\n\nClone the repository and build the binary:\n\n```bash\ngit clone https://github.com/busser/tfautomv\ncd tfautomv\nmake build\n```\n\nThen, move `bin/tfautomv` to a directory resent in your system's `PATH`\nenvironment variable.\n\n## Usage\n\n### Generating `moved` blocks\n\nIn any directory where you would run `terraform plan`, you can run:\n\n```bash\ntfautomv\n```\n\nThis will run `terraform init`, `terraform refresh`, and `terraform plan`, and\nthen write `moved` blocks to a `moves.tf` file.\n\nThat's all there is to it!\n\nYou can also target a specific working directory:\n\n```bash\ntfautomv ./production\n```\n\n### Generating `terraform state mv` commands\n\nBy default, tfautomv writes moves to as `moved` blocks when possible and falls\nback to `terraform state mv` commands when not.\n\nYou can force `tfautomv` to write only `moved` blocks with the `--output=moves`\nflag:\n\n```bash\ntfautomv --output=moves\n```\n\nYou can force `tfautomv` to write only `terraform state mv` commands with the\n`--output=commands` flag:\n\n```bash\ntfautomv --output=commands\n```\n\nThis will print commands to standard output. You can copy and paste them to a\nterminal to run them manually.\n\nAlternatively, you can write the commands to a file:\n\n```bash\ntfautomv --output=commands \u003e moves.sh\n```\n\nOr pipe them into a shell to run them immediately:\n\n```bash\ntfautomv --output=commands | sh\n```\n\nThe `-o` flag is shorthand for `--output`:\n\n```bash\ntfautomv -o commands\n```\n\n### Finding moves across multiple directories\n\nIf you have multiple Terraform modules in different directories, you can pass\nthose directories to `tfautomv`:\n\n```bash\ntfautomv ./production/main ./production/backup -o commands\n```\n\nThis will run `terraform init`, `terraform refresh`, and `terraform plan` in\neach directory, and then write `terraform state mv` commands to standard output.\nThese commands will move resources within and across directories as needed.\n\nTerraform does not natively support moving resources across directories. To\nachieve this, `tfautomv` will output commands that pull copies of each\ndirectory's state, perform the moves, and then push the new state back to the\ndirectory's state backend.\n\nYou can pass as many directories as you want to `tfautomv`.\n\nThis is only compatible with the `commands` output format. Terraform's `moved`\nblock syntax does not support moving resources across directories.\n\n### Skipping the `init` and `refresh` steps\n\nBy default, `tfautomv` runs Terraform's `init`, `refresh`, and `plan` steps.\nTo save time, you can skip the `init` or `refresh` steps with the `--skip-init`\nand `--skip-refresh` flags:\n\n```bash\ntfautomv --skip-init --skip-refresh\n```\n\nThe `-s` flag is shorthand for `--skip-init` and `-S` for `--skip-refresh:\n\n```bash\ntfautomv -sS\n```\n\n### Understanding why a resource was not moved\n\nIf you are not seeing a `moved` block for a resource you expected to be moved,\nyou can increase `tfautomv`'s verbosity with the `-v` flag to get more\ninformation:\n\n```bash\ntfautomv -v\n```\n\nThe default verbosity level is 0. You can increase the verbosity up to 3 by\nrepeating the `-v` flag:\n\n```bash\ntfautomv -vvv\n```\n\nAlternatively, you can specify a specific verbosity level with the `--verbosity`\nflag:\n\n```bash\ntfautomv --verbosity=2\n```\n\nBased on why the resource was not moved, you can choose to edit your code,\nwrite a `moved` block manually, or use the `-ignore` flag to ignore certain\ndifferences.\n\n|                     level 0 (default)                     |                      level 1 (`-v`)                       |                      level 2 (`-vv`)                      |                     level 3 (`-vvv`)                      |\n| :-------------------------------------------------------: | :-------------------------------------------------------: | :-------------------------------------------------------: | :-------------------------------------------------------: |\n| ![verbosity level 0](./docs/images/verbosity/level-0.png) | ![verbosity level 1](./docs/images/verbosity/level-1.png) | ![verbosity level 2](./docs/images/verbosity/level-2.png) | ![verbosity level 3](./docs/images/verbosity/level-3.png) |\n\n### Ignoring certain differences\n\n`tfautomv` works by comparing resources Terraform plans to create (those in your\ncode) to those Terraform plans to delete (those in your state). Sometimes,\n`tfautomv` may not be able to match two resources together because of a\ndifference in a specific attribute, even though the resources are in fact the\nsame. This usually happens when the Terraform provider that manages the resource\nhas transformed the attribute's value in some way.\n\nIn those cases, you can use the `-ignore` flag to ignore specific differences.\n`tfautomv` will ignore differences based on a set of rules that you can\nprovide.\n\nEach rule includes:\n\n- A _kind_ that identifies the nature of the difference to ignore\n- A _resource type_ the rule applies to\n- An _attribute_ inside the resource the rule applies to\n- Optionally, additional arguments specific to the class\n\nA rule is written as a colon-separated string:\n\n```plaintext\n\u003cKIND\u003e:\u003cRESOURCE TYPE\u003e:\u003cATTRIBUTE NAME\u003e[:\u003cKIND ARGUMENTS\u003e]\n```\n\nYou can use the `--ignore` flag multiple times to provide multiple rules:\n\n```bash\ntfautomv \\\n  --ignore=\"whitespace:azurerm_api_management_policy:xml_content\" \\\n  --ignore=\"prefix:google_storage_bucket_iam_member:bucket:b/\"\n```\n\n_If you have a use case that is not covered by existing kinds, please open an\nissue so we can track demand for it._\n\n#### The `everything` kind\n\nUse the `everything` kind to ignore any difference between two values of an\nattribute:\n\n```bash\ntfautomv --ignore=\"everything:\u003cRESOURCE TYPE\u003e:\u003cATTRIBUTE\u003e\"\n```\n\nFor example:\n\n```bash\ntfautomv --ignore=\"everything:random_pet:length\"\n```\n\n#### The `whitespace` kind\n\nUse the `whitespace` kind to ignore differences in whitespace between two\nvalues of an attribute:\n\n```bash\ntfautomv --ignore=\"whitespace:\u003cRESOURCE TYPE\u003e:\u003cATTRIBUTE NAME\u003e\"\n```\n\nFor example, this rule:\n\n```bash\ntfautomv --ignore=\"whitespace:azurerm_api_management_policy:xml_content\"\n```\n\nwill allow these two resources to match:\n\n```terraform\n# This resource has its XML nicely formatted.\nresource \"azurerm_api_management_policy\" \"foo\" {\n  api_management_id = \"...\"\n\n  xml_content = \u003c\u003c-EOT\n  \u003cpolicies\u003e\n    \u003cinbound\u003e\n      \u003ccross-domain /\u003e\n      \u003cbase /\u003e\n      \u003cfind-and-replace from=\"xyz\" to=\"abc\" /\u003e\n    \u003c/inbound\u003e\n  \u003c/policies\u003e\n  EOT\n}\n\n# This resource has its XML on one line.\nresource \"azurerm_api_management_policy\" \"bar\" {\n  api_management_id = \"...\"\n\n  xml_content = \"\u003cpolicies\u003e\u003cinbound\u003e\u003ccross-domain /\u003e\u003cbase /\u003e\u003cfind-and-replace from=\\\"xyz\\\" to=\\\"abc\\\" /\u003e\u003c/inbound\u003e\u003c/policies\u003e\"\n}\n```\n\n#### The `prefix` kind\n\nUse the `prefix` kind to ignore a specific prefix between in one of two values\nof an attribute:\n\n```bash\ntfautomv --ignore=\"prefix:\u003cRESOURCE TYPE\u003e:\u003cATTRIBUTE NAME\u003e:\u003cPREFIX\u003e\"\n```\n\nFor example:\n\n```bash\ntfautomv --ignore=\"prefix:google_storage_bucket_iam_member:bucket:b/\"\n```\n\nwill strip the `b/` prefix from the `bucket` attribute of any\n`google_storage_bucket_iam_member` resources before comparing the attirbute's\nvalues.\n\n#### Referencing nested attributes\n\nJoin parent attributes with child attributes with a `.`:\n\n```plaintext\n\u003cKIND\u003e:\u003cRESOURCE TYPE\u003e:parent_obj.child_field\n\u003cKIND\u003e:\u003cRESOURCE TYPE\u003e:parent_list.0\n```\n\nTo get an attribute's full path, increase the verbosity level with the `-v`\nflag:\n\n```bash\ntfautomv -vvv\n```\n\n### Passing additional arguments to Terraform\n\nYou can pass additional arguments to Terraform by using Terraform's built-in\n[`TF_CLI_ARGS` and `TF_CLI_ARGS_name` environment variables.](https://www.terraform.io/cli/config/environment-variables#tf_cli_args-and-tf_cli_args_name).\n\nFor example, in order to use a file of variables during Terraform's plan:\n\n```bash\nTF_CLI_ARGS_plan=\"-var-file=production.tfvars\" tfautomv\n```\n\n### Using Terragrunt instead of Terraform\n\nYou can tell `tfautomv` to use the Terragrunt CLI instead of the Terraform CLI\nwith the `--terraform-bin` flag:\n\n```bash\ntfautomv --terraform-bin=terragrunt\n```\n\nThis also works with any other executable that has an `init` and `plan` command.\n\n### Disabling colors in output\n\nAdd the `--no-color` flag to your `tfautomv` command to disable output\nformatting like colors, bold text, etc.\n\nFor example:\n\n```bash\ntfautomv --no-color\n```\n\nAlternatively, you can achieve the same result by setting the `NO_COLOR`\nenvironment variable to any value:\n\n```bash\nNO_COLOR=true tfautomv\n```\n\n## Thanks\n\nThanks to [Padok](https://www.padok.fr), where this project was born 💜\n\n## License\n\nThe code is licensed under the permissive Apache v2.0 license. [Read this](\u003chttps://tldrlegal.com/license/apache-license-2.0-(apache-2.0)\u003e) for a summary.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbusser%2Ftfautomv","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbusser%2Ftfautomv","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbusser%2Ftfautomv/lists"}