本日は、Terraformを使っていて、tfstateと実際のリソースに差分が生じてしまった際の修正方法を紹介します。
稼働中で作り直しができないリソースなので、tfstateに手動で変更した状態を取り込むことにしました。
起きたことと、解決までの流れは以下のようになります。
- terraform plan を実行すると予期せぬ差分(.tf に記述していない)が発生
- Manegement Consoleと .tf ファイルを比較し、terraformで管理するリソースに手動で変更が加わっていることを特定
- Management Console側のリソースを、 terraform import で tfstate に反映させる
- tfstateに合わせて、.tfファイルの記述を修正
- terraform plan を実行し、Management Console と差分がなくなったことを確認
tfstateとは
terraformで管理する対象のリソースの情報をjson形式で記載したファイルです。
terraformが自動生成するもので、ここに記載のあるリソースのみterraformの管理下にあります。
最重要ファイルなので、管理場所や1つのファイルで扱うリソースの粒度が難しいポイントです。
terraform cloudを使用するとtfstateの管理場所を意識しなくてよくなったりしますが、この辺はまたの機会に〜
問題発生
で、ちょうどtfstateの管理方法を修正していた最中の出来事。
.tf は書き換えていなかったのですが、add,changed,destroy...
ただ、planでdry-runが実行されてわかりやすく差分が表示されるからterraformはいいですね〜
$ terraform plan # aws_instance.sample must be replaced -/+ resource "aws_instance" "sample" { + ebs_block_device { # forces replacement + delete_on_termination = true + device_name = "xvdf" + encrypted = (known after apply) + iops = (known after apply) + kms_key_id = (known after apply) + snapshot_id = (known after apply) + volume_id = (known after apply) + volume_size = 20 + volume_type = "gp2" } + ephemeral_block_device { + device_name = (known after apply) + no_device = (known after apply) + virtual_name = (known after apply) } ~ metadata_options { ~ http_endpoint = "enabled" -> (known after apply) ~ http_put_response_hop_limit = 1 -> (known after apply) ~ http_tokens = "optional" -> (known after apply) } + network_interface { + delete_on_termination = (known after apply) + device_index = (known after apply) + network_interface_id = (known after apply) } ~ root_block_device { delete_on_termination = true ~ device_name = "/dev/sda1" -> (known after apply) ~ encrypted = false -> (known after apply) ~ iops = 300 -> (known after apply) + kms_key_id = (known after apply) ~ volume_id = "vol-0f00a78377c440cbc" -> (known after apply) ~ volume_size = 100 -> 30 volume_type = "gp2" } Plan: 1 to add, 1 to change, 1 to destroy.
.tf には以下のように、aws_instance の記述されていました。
ebsの追加でのアタッチと、rootブロックデバイスの容量の変更が行われていたようです。
resource "aws_instance" "sample" { ami = local.ami instance_type = local.instance_type key_name = local.key_name vpc_security_group_ids = local.vpc_security_group_ids subnet_id = local.subnet_id private_ip = local.private_ip tags = { Name = local.tag_name backup = local.tag_backup } root_block_device { volume_type = local.root_volume_type volume_size = local.root_volume_size } ebs_block_device { device_name = local.ebs_device_name volume_type = local.ebs_volume_type volume_size = local.ebs_volume_size } }
変更が加わったリソースをTerraform管理下におく
現在のリソースの状態をimportするために .tf に以下のように空のリソースを記述します。参考(import)
resource "aws_instance" "sample_new" { }
このリソース名を使って、手動で変更したリソースの現在の状態を tfstate にインポートします。参考(aws_instance)
$ terraform import aws_instance.sample_new i-023456789abcdefgh
tfstate をs3で管理しているので、ローカルにコピーして vscode で確認してみます。
$ terraform state pull >> /tmp/terraform.state_import_sample $ code /tmp/terraform.state_import_sample
インポートできていました。
{ "mode": "managed", "type": "aws_instance", "name": "sample_new", "provider": "provider.aws", "instances": [ { "schema_version": 1, "attributes": { "ami": "ami-014192b9d69d36b87", "arn": "arn:aws:ec2:ap-northeast-1:421754326399:instance/i-023456789abcdefgh", "associate_public_ip_address": true, "availability_zone": "ap-northeast-1a", "cpu_core_count": 1, "cpu_threads_per_core": 2, "credit_specification": [], "disable_api_termination": false, "ebs_block_device": [], "ebs_optimized": false, "ephemeral_block_device": [], "get_password_data": false, "hibernation": false, "host_id": null, "iam_instance_profile": "", "instance_state": "running", "instance_type": "c3.large", "ipv6_address_count": 0, "ipv6_addresses": [], "private_ip": "172.17.60.15", "root_block_device": [ { "delete_on_termination": true, "device_name": "/dev/sda1", "encrypted": false, "iops": 300, "kms_key_id": "", "volume_id": "vol-0f00a78377abcdefgh", "volume_size": 100, "volume_type": "gp2" } ],
差分を埋めていく
あとは .tf を tfstate に合わせる形で編集して、planで差分を確認していきます。
resource "aws_instance" "sample_new" { ami = local.ami instance_type = local.instance_type key_name = local.key_name vpc_security_group_ids = local.vpc_security_group_ids subnet_id = local.subnet_id private_ip = local.private_ip tags = { Name = local.tag_name backup = local.tag_backup } root_block_device { volume_type = local.root_volume_type volume_size = local.root_volume_size } }
$ terraform plan Plan: 0 to add, 0 to change, 1 to destroy.
現在のリソースを .tf に記述できたので(sample_new)、以前の記述(sample)をtfstateから削除します。
$ terraform state rm aws_instance.sample
これで手動で変更してしまったリソースの状態を取り込んで、terraformでの管理下に戻すことができました!
$ terraform plan No changes. Infrastructure is up-to-date.
慣れない作業のため慎重に、新たなリソース(sample_new)を定義して作業を行いましたが、
- sampleをtfstateから削除(terraform state rm aws_instance.sample)
- インポートし直す(terraform import aws_instance.sample i-023456789abcdefgh)
- .tf を修正
という手順だとよりスマートかと思います。