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:

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