Terraformで作成したEC2を停止したら差分が!instance_stateをignore_changesしたるでぇ!←無駄無駄ァ!
ことの始まり
ある日、ちょっとした一時的な作業用にパブリックIPアドレスを付与したEC2インスタンスを立ち上げる必要があり、以下のようなTerraformコードを用意しました。
resource "aws_instance" "this" {
ami = "ami-01ff47a07ed5e00bf"
instance_type = "t4g.micro"
subnet_id = "subnet-xxxxxxxxxxxxxxxxx"
associate_public_ip_address = true
root_block_device {
volume_size = 8
volume_type = "gp3"
iops = 3000
delete_on_termination = true
encrypted = true
throughput = 125
}
iam_instance_profile = "work-ec2-role"
disable_api_termination = false
tags = {
Name = "work-ec2"
}
}
terraform applyを実行してEC2インスタンスを作成した後、念のため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.
ところが、AWSマネジメントコンソールでEC2インスタンスを停止した後、再度terraform planを実行してみると、EC2インスタンスを再作成しようとする差分が表示されました。どうして・・・。
Terraform used the selected providers to generate the following execution plan. Resource actions are indicated with the following symbols:
-/+ destroy and then create replacement
Terraform will perform the following actions:
# aws_instance.this must be replaced
-/+ resource "aws_instance" "this" {
~ arn = "arn:aws:ec2:ap-northeast-1:xxxxxxxxxxxx:instance/i-xxxxxxxxxxxxxxxxx" -> (known after apply)
~ associate_public_ip_address = false -> true # forces replacement
~ availability_zone = "ap-northeast-1a" -> (known after apply)
~ cpu_core_count = 2 -> (known after apply)
~ cpu_threads_per_core = 1 -> (known after apply)
~ disable_api_stop = false -> (known after apply)
~ ebs_optimized = false -> (known after apply)
- hibernation = false -> null
+ host_id = (known after apply)
+ host_resource_group_arn = (known after apply)
~ id = "i-xxxxxxxxxxxxxxxxx" -> (known after apply)
~ instance_initiated_shutdown_behavior = "stop" -> (known after apply)
+ instance_lifecycle = (known after apply)
~ instance_state = "stopped" -> (known after apply)
~ ipv6_address_count = 0 -> (known after apply)
~ ipv6_addresses = [] -> (known after apply)
+ key_name = (known after apply)
~ monitoring = false -> (known after apply)
+ outpost_arn = (known after apply)
+ password_data = (known after apply)
+ placement_group = (known after apply)
~ placement_partition_number = 0 -> (known after apply)
~ primary_network_interface_id = "eni-xxxxxxxxxxxxxxxxx" -> (known after apply)
~ private_dns = "ip-xx-xx-xx-xx.ap-northeast-1.compute.internal" -> (known after apply)
~ private_ip = "xx.xx.xx.xx" -> (known after apply)
+ public_dns = (known after apply)
+ public_ip = (known after apply)
~ secondary_private_ips = [] -> (known after apply)
~ security_groups = [] -> (known after apply)
+ spot_instance_request_id = (known after apply)
tags = {
"Name" = "work-ec2"
}
~ tenancy = "default" -> (known after apply)
+ user_data
= (known after apply)
+ user_data_base64 = (known after apply)
# (10 unchanged attributes hidden)
- capacity_reservation_specification {
- capacity_reservation_preference = "open" -> null
}
- cpu_options {
- core_count = 2 -> null
- threads_per_core = 1 -> null
}
- credit_specification {
- cpu_credits = "unlimited" -> null
}
- enclave_options {
- enabled = false -> null
}
- maintenance_options {
- auto_recovery = "default" -> null
}
- metadata_options {
- http_endpoint = "enabled" -> null
- http_protocol_ipv6 = "disabled" -> null
- http_put_response_hop_limit = 2 -> null
- http_tokens = "required" -> null
- instance_metadata_tags = "disabled" -> null
}
- private_dns_name_options {
- enable_resource_name_dns_a_record = false -> null
- enable_resource_name_dns_aaaa_record = false -> null
- hostname_type = "ip-name" -> null
}
~ root_block_device {
~ device_name = "/dev/xvda" -> (known after apply)
~ kms_key_id = "arn:aws:kms:ap-northeast-1:xxxxxxxxxxxx:key/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" -> (known after apply)
- tags = {} -> null
~ volume_id = "vol-xxxxxxxxxxxxxxxxx" -> (known after apply)
# (6 unchanged attributes hidden)
}
}
Plan: 1 to add, 0 to change, 1 to destroy.
原因の調査
AWSマネジメントコンソールでEC2インスタンスを停止した後に差分が生じたんだから、停止状態であることが原因なのでは?と考え、まずは以下の部分に焦点を当てました。
~ instance_state = "stopped" -> (known after apply)
とりあえずignore_changesに入れてしまえば検知しなくなって万事解決!!ということで、以下の通りTerraformコードを修正。
resource "aws_instance" "this" {
# (省略)
# lifecycle ブロックを追加
lifecycle {
ignore_changes = [
instance_state
]
}
# (省略)
}
ところが、この状態で terraform plan を実行すると、差分は依然として生じたままで、さらには以下のWarningまで表示されてしまいました。
Adding an attribute name to ignore_changes tells Terraform to ignore future changes to the argument in configuration after the object has been created, retaining the value originally configured.
The attribute instance_state is decided by the provider alone and therefore there can be no configured value to compare with. Including this attribute in ignore_changes has no effect. Remove the attribute from ignore_changes to quiet this warning.
「instance_state はプロバイダによってのみ決定される属性だから、Terraformの構成と比較する設定された値は存在しないよ〜。ignore_changes に入れても無意味だよ〜」みたいなことが書いてあるようです。
えー・・・それじゃどうしようもないじゃん・・・と思いながらなんとなく差分を見ていた私。しばらくしてようやく本当の原因に気づきました。
本当の原因
・・・まてよ?こっちか!?
~ associate_public_ip_address = false -> true # forces replacement
なぜすぐに気づかなかったのでしょう。冒頭の terraform plan 実行結果を見れば一目瞭然ですが、EC2インスタンスに自動割り当てされたパブリックIPアドレスは停止時に解放されるため、ここが差分の原因となっているようでした。
よって、以下の通りコードを修正しました。
resource "aws_instance" "this" {
# (省略)
lifecycle {
ignore_changes = [
# instance_state から associate_public_ip_address に変更
associate_public_ip_address
]
}
# (省略)
}
やったー!!!!
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.
結論
terraform plan実行時の差分の内容は、心を落ち着かせてじっくり読みましょう。
Discussion