Terraform-provider-aws: Adding aws_eip to existing instance does not trigger update to other resources.

Created on 13 Jun 2017  路  14Comments  路  Source: hashicorp/terraform-provider-aws

_This issue was originally opened by @rbowlby as hashicorp/terraform#3216. It was migrated here as part of the provider split. The original body of the issue is below._


Adding an EIP to an existing resource does not cause other aws terraform resources to appropriately update to reflect this change.

If I add an aws_eip to an existing ec2 instance and that instance has a corresponding route53 cname that uses the ec2 public_dns it will NOT update those route53 entries till a subsequent apply is performed.

bug servicec2

Most helpful comment

Just to add that I was having this same problem outlined by https://github.com/hashicorp/terraform/issues/3216#issuecomment-250233806. This is particularly annoying when referencing the DNS of a instance that has an EIP attached through Route53 or Cloudfront (which can't use an ip address, requiring a public DNS).

In the first apply, Route53/Cloudfront would get the instance public DNS _before_ the EIP is attached, and a second apply is required to fix the value.

I managed to get this working by creating a local value that infers the public DNS from the EIP. Leaving my solution here in case someone has the same problem and stumble upon this issue:

locals {
  public_dns = "${format("ec2-%s.compute-1.amazonaws.com", replace(aws_eip.myip.public_ip, ".", "-"))}"
}

resource "aws_eip" "myip" {
  instance = "${aws_instance.myinstance.id}"
  vpc      = true
}

resource "aws_route53_record" "myrecord" {
  zone_id = "${var.route53_hosted_zone_id}"
  name    = "${var.route53_name)}"

  type    = "CNAME"
  ttl     = "300"

  records = ["${local.public_dns}"]
}

All 14 comments

_This comment was originally opened by @rbowlby as https://github.com/hashicorp/terraform/issues/3216#issuecomment-139431936. It was migrated here as part of the provider split. The original comment is below._


Particularly nasty bug since unless you have the sense to run "terraform plan" again after doing a successful apply you wouldn't be aware of the fact that your r53 entries are now borked. :(

_This comment was originally opened by @catsby as https://github.com/hashicorp/terraform/issues/3216#issuecomment-141203864. It was migrated here as part of the provider split. The original comment is below._


Thanks for the report @rbowlby 鈥撀燿o you possibly have an example config that demonstrates this? I'd like to try it out and work on a patch.

Thanks!

_This comment was originally opened by @lawrencepit as https://github.com/hashicorp/terraform/issues/3216#issuecomment-147272693. It was migrated here as part of the provider split. The original comment is below._


Encountered same thing. Example config taking from the Getting Started pages, I added this from http://www.terraform.io/intro/getting-started/dependencies.html:

resource "aws_eip" "ip" {
    instance = "${aws_instance.example.id}"
}

then went on to http://www.terraform.io/intro/getting-started/provision.html and added this:

resource "aws_instance" "example" {
    ami = "ami-aa7ab6c2"
    instance_type = "t1.micro"

    provisioner "local-exec" {
        command = "echo ${aws_instance.example.public_ip} > file.txt"
    }
}

after running terraform destroy and then terraform apply the output in file.txt does not contain the correct public ip

_This comment was originally opened by @bwestover as https://github.com/hashicorp/terraform/issues/3216#issuecomment-168111816. It was migrated here as part of the provider split. The original comment is below._


the output in file.txt does not contain the correct public ip

I also encountered this when I first walked through the provisioning page of the getting started guide.

bwestover$ terraform show
aws_eip.ip:
...
  public_ip = 52.71.80.96   <---*** The elastic IP that was assigned
aws_instance.example:
...
  public_ip = 52.71.80.96  <---*** Also shows up as the public_ip of the instance
...

bwestover$ cat file.txt
54.88.60.48   <--The file created by my provisioner has this OTHER ip in it

My config is right out of the getting started guide.

example.tf:

provider "aws" {
    access_key = "${var.access_key}"
    secret_key = "${var.secret_key}"
    region = "${var.region}"
}

resource "aws_instance" "example" {
    ami = "${lookup(var.amis, var.region)}"
    instance_type = "t2.micro"

    provisioner "local-exec" {
        command = "echo ${aws_instance.example.public_ip} > file.txt"
    }
}

resource "aws_eip" "ip" {
    instance = "${aws_instance.example.id}"
}

variables.tf:

variable "access_key" {}
variable "secret_key" {}
variable "region" {
    default = "us-east-1"
}
variable "amis" {
    default = {
        us-east-1 = "ami-8eb061e6"
        us-west-2 = "ami-ef5e24df"
    }
}

I can run terraform destroy and then terraform apply and this will reproduce every time.

It seems to me that since there is a dependency on the instance being created before the eip, the provisioner is running before the elastic IP is being applied. This means I get the public ip that is initially assigned in my file.txt and then it is changed to the EIP in the last step.

Another interesting thing is that this workflow:
1) terraform destroy
2) terraform apply
3) terraform show
... will produce an IP in file.txt that matches the public IP on the instance. That IP will NOT match the elastic IP from terraform show or the actual IP on the instance.

However this workflow:
1) terraform destroy
2) terraform apply
3) terraform plan
4) terraform show
...will produce an IP in file.txt that doesn't match the public IP on the instance OR the elastic IP.

_Note_: The output shown above is from this second workflow.

I think step 3 in the second workflow is getting the ACTUAL final state of the resources. Skipping that step in the first workflow returns an ephemeral state that existed while the instance was building (and also the state where the provisioner ran).

This seems like it could lead some surprises.

_This comment was originally opened by @davidwinter as https://github.com/hashicorp/terraform/issues/3216#issuecomment-173230499. It was migrated here as part of the provider split. The original comment is below._


To get the provisioner to output the correct IP address, I added the provisioner to the aws_eip resource instead of the aws_instance:

resource "aws_eip" "ip" {
    instance = "${aws_instance.sshgateway.id}"
    vpc = true

    provisioner "local-exec" {
      command = "echo ${aws_eip.ip.public_ip} > inventory"
    }
}

_This comment was originally opened by @dtaylor-sfdc as https://github.com/hashicorp/terraform/issues/3216#issuecomment-219600036. It was migrated here as part of the provider split. The original comment is below._


I encountered the same problem as originally posted.

To reproduce:

  • create an aws_instance
  • create an aws_eip
  • create a CNAME to aws_instance.my_instance.public_dns
  • on the first plan & apply the CNAME record is the pre-allocation value of public_dns
  • on the second plan & apply the CNAME record is changed to the post-allocation value of public_dns

_This comment was originally opened by @guildencrantz as https://github.com/hashicorp/terraform/issues/3216#issuecomment-250233806. It was migrated here as part of the provider split. The original comment is below._


Looking to add an EIP to an instance and the problem appears to be that the EIP trigger things like aws_route53_record to realize that public_dns will change, so they precompute values based on their current state, ultimately resulting in an incorrect value. I'm guessing during initial apply the public_dns also gets pulled before waiting for the EIP to fully attach to the instance.

+ aws_eip.eip
allocation_id:     "<computed>"
association_id:    "<computed>"
domain:            "<computed>"
instance:          "i-397a302f"
network_interface: "<computed>"
private_ip:        "<computed>"
public_ip:         "<computed>"
vpc:               "true"

~ aws_route53_record.http-proxy-cname
records.2502117172: "ec2-52-45-24-144.compute-1.amazonaws.com" => ""
records.3398562927: "" => "ec2-54-161-157-54.compute-1.amazonaws.com"

The current public_dns of i-397a302f is ec2-54-161-157-54.compute-1.amazonaws.com, however once the EIP has been added this value will change.

_This comment was originally opened by @jeffbyrnes as https://github.com/hashicorp/terraform/issues/3216#issuecomment-280364188. It was migrated here as part of the provider split. The original comment is below._


I鈥檝e encountered this issue as well, though in the case of creating a bastion host in a module, then attempting to use that host as the bastion for my other instances:

# bastion.tf
resource "aws_eip" "bastion_eip" {
  vpc                       = true
  instance                  = "${aws_instance.bastion.id}"
  associate_with_private_ip = "${aws_instance.bastion.private_ip}"
}

resource "aws_instance" "bastion" {
  ami               = "${var.current_ami}"
  instance_type     = "t2.small"
  key_name          = "${var.key_pair_id}"
  subnet_id         = "${aws_subnet.public.0.id}"
  source_dest_check = false

  associate_public_ip_address = true

  vpc_security_group_ids = [
    "${aws_security_group.default.id}",
    "${aws_security_group.bastion.id}",
  ]

  tags {
    Name = "${var.env}-bastion"
    Env  = "${var.env}"
  }
}

output "bastion_public_ip" {
  value = "${aws_eip.bastion_eip.public_ip}"
}
# api.tf
resource "aws_instance" "api" {
  count = "${var.instance_count}"

  provisioner "chef" {
    node_name       = "${var.env}-api-${count.index}"
    environment     = "${var.env}"
    run_list        = ["ds_base", "ds_api"]
    secret_key      = "${file("~/.chef/ds_encrypted_data_bag_secret")}"
    server_url      = "https://api.chef.io/organizations/darksky"
    user_name       = "${var.user_name}"
    user_key        = "${file(var.user_key)}"
    recreate_client = true

    connection {
      user         = "ubuntu"
      private_key  = "${file("~/.ssh/ds_aws_dev")}"
      bastion_host = "${data.terraform_remote_state.vpc.bastion_public_ip}"
    }
  }

  ami           = "${var.current_ami}"
  instance_type = "${var.instance_type}"
  key_name      = "${var.key_pair_id}"
  subnet_id     = "${data.terraform_remote_state.vpc.private_subnet_ids.0}"
}

Just to add that I was having this same problem outlined by https://github.com/hashicorp/terraform/issues/3216#issuecomment-250233806. This is particularly annoying when referencing the DNS of a instance that has an EIP attached through Route53 or Cloudfront (which can't use an ip address, requiring a public DNS).

In the first apply, Route53/Cloudfront would get the instance public DNS _before_ the EIP is attached, and a second apply is required to fix the value.

I managed to get this working by creating a local value that infers the public DNS from the EIP. Leaving my solution here in case someone has the same problem and stumble upon this issue:

locals {
  public_dns = "${format("ec2-%s.compute-1.amazonaws.com", replace(aws_eip.myip.public_ip, ".", "-"))}"
}

resource "aws_eip" "myip" {
  instance = "${aws_instance.myinstance.id}"
  vpc      = true
}

resource "aws_route53_record" "myrecord" {
  zone_id = "${var.route53_hosted_zone_id}"
  name    = "${var.route53_name)}"

  type    = "CNAME"
  ttl     = "300"

  records = ["${local.public_dns}"]
}

Thanks for this pragmatic workaround, @fbcbarbosa . I've gone even simpler, avoiding the local variable, since all my resources have a count clause to make them conditional (I happen to also be associating the EIP to the instance separately). I've also slightly changed the way the target name is derived, since it depends on the AWS region. This may not work in us-east-1, though (?) which we do not use.

resource "aws_eip" "eip" {
  tags     = {
    Name   = "jump-box-${var.project_name}"
  }
  count = "${var.ssh_jump_box_required ? 1 : 0}"
}

resource "aws_eip_association" "eip_assoc" {
  allocation_id = "${aws_eip.eip.id}"
  instance_id   = "${aws_instance.ec2.id}"
  count = "${var.ssh_jump_box_required ? 1 : 0}"
}

resource "aws_route53_record" "jump_box" {
  zone_id    = "${data.aws_route53_zone.route53_primary_domain.zone_id}"
  name       = "jump-box-${var.project_name}"
  type       = "CNAME"
  records    = [ "${format("ec2-%s.${var.aws_region}.compute.amazonaws.com", replace(aws_eip.eip.public_ip, ".", "-"))}" ]
  ttl        = "300"
  count      = "${var.ssh_jump_box_required ? 1 : 0}"
}

I also encountered this issue while following the provisioning section of the Getting Started guide.

I think at the very least it would be worth adding some kind of note to the page explaining what's going on. When the IP in the file didn't match what was showing up in the EC2 Instances console nor the EIP I wasn't sure if I was doing something wrong if there was a bug in Terraform.

See also https://github.com/terraform-providers/terraform-provider-aws/issues/5682

@fbcbarbosa great to see some one giving solution for the issue one i am facing now.it would be great if you give detailed explanation..I am stuck here with variables..please give little details on variables file you used.

I have this problem when I associate an existing EIP, no route53 involved.

resource "aws_eip_association" "eip_assoc" {
  instance_id   = aws_instance.instance.id
  allocation_id = "eipalloc-xxxxxxxxxxx"  
}

resource "aws_eip" "ip" {
    vpc = true
}

A workaround that seems to work for me is to run terraform apply twice. The second time the correct IP is return with terraform output ip. No changes are made during the 2nd apply:

Apply complete! Resources: 0 added, 0 changed, 0 destroyed.

Outputs:

ip = xxxxxxxxxx

Same problem with this approach - https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/eip_association

On a second apply ip will have changed and replace called for.

Was this page helpful?
0 / 5 - 0 ratings