本日は、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 を修正
という手順だとよりスマートかと思います。
