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