【Terraform】既存resourceの名前だけを変えたい
社内勉強会で教えてもらった内容。
Terraformで既存のresourceの名前を変えたいとき、普通にtfファイル内でresourceの名前を変更するだけだと、resource全体が作り直し(destory->create)されてしまう。
例えば以前の記事で使った aws_internet_gateway
resourceの名前を変更しようとすると、resource全体に差分が生じてしまう。
$ cat aws_internet_gateway.tf resource "aws_internet_gateway" "container-era-webapp-internet-gateway" { vpc_id = aws_vpc.container-era-webapp-vpc.id } $ cat aws_route.tf resource "aws_route" "container-era-webapp-public-route" { route_table_id = aws_route_table.container-era-webapp-public-route-table.id destination_cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.container-era-webapp-internet-gateway.id } # 名前をcontainer-era-webapp-gatewayに変更 $ cat aws_internet_gateway.tf resource "aws_internet_gateway" "container-era-webapp-gateway" { vpc_id = aws_vpc.container-era-webapp-vpc.id } $ cat aws_route.tf resource "aws_route" "container-era-webapp-public-route" { route_table_id = aws_route_table.container-era-webapp-public-route-table.id destination_cidr_block = "0.0.0.0/0" gateway_id = aws_internet_gateway.container-era-webapp-gateway.id } $ terraform plan Terraform will perform the following actions: # aws_internet_gateway.container-era-webapp-gateway will be created + resource "aws_internet_gateway" "container-era-webapp-gateway" { + arn = (known after apply) + id = (known after apply) + owner_id = (known after apply) + vpc_id = "vpc-01ff3b511d075fb99" } # aws_internet_gateway.container-era-webapp-internet-gateway will be destroyed - resource "aws_internet_gateway" "container-era-webapp-internet-gateway" { - arn = "arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:internet-gateway/igw-xxxxxxxxxxxx" -> null - id = "igw-xxxxxxxxxxxxxxx" -> null - owner_id = "xxxxxxxxxxxx" -> null - tags = {} -> null - vpc_id = "vpc-xxxxxxxxxxxx" -> null }
これが仮に本番環境で使われているリソースである場合、一時的にリソースが消えてしまい本番環境に影響が生じる可能性もある。
このような場合、 terraform state mv
コマンドを使い、tfstateファイルを変更する必要がある。
今回はlocalでtfstateファイルの変化を見たいので、まずbackendをS3からlocalに変える。
$ cat config.tf provider "aws" { region = "ap-northeast-1" } # terraform { # backend "s3" { # bucket = "container-era-terraform-udomomo" # key = "sample-app/vpc/terraform.tfstate" # region = "ap-northeast-1" # } # } $ terraform init ... Do you want to copy this state to the new "local" backend? Enter "yes" to copy and "no" to start with an empty state. Enter a value: yes
S3に置かれていたtfstateファイルがlocalにコピーされた。
terraform state mv
コマンドを試してみる。
$ terraform state mv aws_internet_gateway.container-era-webapp-internet-gateway aws_internet_gateway.container-era-webapp-gateway Move "aws_internet_gateway.container-era-webapp-internet-gateway" to "aws_internet_gateway.container-era-webapp-gateway" Successfully moved 1 object(s).
terraform state mv
コマンドを実行すると、自動でバックアップファイルが作られる。差分を見てみると以下のようになっている。
diff -u terraform.tfstate.1595057556.backup terraform.tfstate --- terraform.tfstate.1595057556.backup 2020-07-18 16:32:36.000000000 +0900 +++ terraform.tfstate 2020-07-18 16:32:36.000000000 +0900 @@ -1,7 +1,7 @@ { "version": 4, "terraform_version": "0.12.28", - "serial": 0, + "serial": 1, "lineage": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "outputs": { "cidr_block": { @@ -25,7 +25,7 @@ { "mode": "managed", "type": "aws_internet_gateway", - "name": "container-era-webapp-internet-gateway", + "name": "container-era-webapp-gateway", "provider": "provider.aws", "instances": [ { @@ -70,12 +70,7 @@ "transit_gateway_id": "", "vpc_peering_connection_id": "" }, - "private": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", - "dependencies": [ - "aws_internet_gateway.container-era-webapp-internet-gateway", - "aws_route_table.container-era-webapp-public-route-table", - "aws_vpc.container-era-webapp-vpc" - ] + "private": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" } ] },
旧 container-era-webapp-internet-gateway
に依存していた aws_route
resourceのdependenciesにもdiffがある。これはaws_internet_gatewayのみに変更を加えたためと思われる。公式レポジトリのissueによれば、tfstateファイルはどのような意図でリファクタリングが行われたかは一切関知しておらず、またdependenciesはresourceの削除時にしか使われないため、リファクタリング後に terraform apply
すれば影響がなくなるとのことだった。
terraform plan
で確認する。
$ terraform plan No changes. Infrastructure is up-to-date. This means that Terraform did not detect any differences between your configuration and real physical resources that exist. As a result, no actions need to be performed.
resourceに影響がないと確認できたので、backendをS3に戻し、mvされた後のtfstateファイルをS3にコピーする。
$ cat config.tf provider "aws" { region = "ap-northeast-1" } terraform { backend "s3" { bucket = "container-era-terraform-udomomo" key = "sample-app/vpc/terraform.tfstate" region = "ap-northeast-1" } } $ terraform init ... Do you want to overwrite the state in the new backend with the previous state? Enter "yes" to copy and "no" to start with the existing state in the newly configured "s3" backend. Enter a value: yes ... Terraform has been successfully initialized!
最後にtfファイルをapplyすればよい。
$ terraform apply ... Apply complete! Resources: 0 added, 0 changed, 0 destroyed.
ちなみに、apply後にS3バケットに生成されていたtfstateファイルを確認したところ、先程diffが出ていた部分のdependenciesも正しく復活していた。
... "private": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "dependencies": [ "aws_internet_gateway.container-era-webapp-gateway", "aws_route_table.container-era-webapp-public-route-table", "aws_vpc.container-era-webapp-vpc" ] ...