Work-around: Terraform 0.11 wants to rebuild EC2 instances when they haven't changed
Terraform claims that my EC2 instance needs to be rebuilt due to changes in the ebs_block_device even though we haven't made any changes to the block device definition. Note the ebs_block_device
lines that claim 'forces new resource':
aws_instance.infosec-gatekeeper (new resource required)
id: "i-01234567890123456" => <computed> (forces new resource)
ami: "ami-0123456789abcdef0" => "ami-0123456789abcdef0"
arn: "arn:aws:ec2:us-east-1:098765432109:instance/i-01234567890123456" => <computed>
associate_public_ip_address: "false" => <computed>
availability_zone: "us-east-1e => <computed>
cpu_core_count: "1" => <computed>
cpu_threads_per_core: "2" => <computed>
ebs_block_device.#: "0" => "1"
ebs_block_device.1357911171.delete_on_termination: "" => "true" (forces new resource)
ebs_block_device.1357911171.device_name: "" => "/dev/xvda" (forces new resource)
ebs_block_device.1357911171.encrypted: "" => <computed> (forces new resource)
ebs_block_device.1357911171.iops: "" => ""
ebs_block_device.1357911171.kms_key_id: "" => <computed> (forces new resource)
ebs_block_device.1357911171.snapshot_id: "" => <computed> (forces new resource)
ebs_block_device.1357911171.volume_id: "" => <computed>
ebs_block_device.1357911171.volume_size: "" => "16" (forces new resource)
ebs_block_device.1357911171.volume_type: "" => "gp2" (forces new resource)
This was in an environment with:
- terraform 0.11.14.7
- aws provider 2.56.0
References:
- ebs_block_device shows changes on every plan/apply after 2.7.0 update [github.com/terraform-providers]
- lifecycle ignore specific attributes [github.com/hashicorp]
- Terraform Resources [terraform.io/docs]
- Terraform remote state [stackoverflow.com]
Work-around
We don't want to rebuild our EC2 instances when there are no material changes to them. Fortunately there is a work-around that addressed this issue: use a lifecycle
tag on the aws_instance
resource to ignore_changes on 'ebs_block_device'. Here's an example:
resource "aws_instance" "appservice-instance"{
instance_type = "t2.micro"
key_name = "ssh-keypair-name"
ami = "${data.terraform_remote_state.global.ami-appservice}"
subnet_id = "{$data.terraform_remote_state.global.subnet_ids_list[1]}"
vpc_security_group_ids = ["${aws_security_group.appservice.id}"]
iam_instance_profile = "${aws_iam_instance_profile.appservice-profile.name}"
tags {
Name = "appservice"
CostCenter = "r&d"
}
ebs_block_device {
device_name = "/dev/xvda"
volume_type = "gp2"
volume_size = 16
}
#
# This is the secret sauce for the work around (the 'ignore_changes' piece
lifecycle {
ignore_changes = ["tags", "ebs_block_device"]
create_before_destroy = true
}
user_data = <<EOF
#!/bin/bash
echo "your init script here!"
EOF
}
Zeroing in, this is the key block:
lifecycle {
ignore_changes = ["tags", "ebs_block_device"]
}
Now we can change non-ec2 related terraform without worrying about triggering an EC2 instance rebuild