hashicorp / terraform-provider-aws

The AWS Provider enables Terraform to manage AWS resources.
https://registry.terraform.io/providers/hashicorp/aws
Mozilla Public License 2.0
9.81k stars 9.16k forks source link

associate_public_ip_address true when subnet map_public_ip_on_launch is true #411

Open hashibot opened 7 years ago

hashibot commented 7 years ago

This issue was originally opened by @mengesb as hashicorp/terraform#10568. It was migrated here as part of the provider split. The original body of the issue is below.


Some time ago I opened an issue whereby I explicitly set associate_public_ip_address = false on an aws_instance; however because the subnet's default for map_public_ip_on_launch = true a public IP was associated anyway.

I understand that if there is no provided value, we should adopt the subnet's defaults however when you explicitly set something, that should be obeyed. As a result, every 'apply' results in my aws instances being destroyed and re-created.

Terraform Version

0.7.13

Affected Resource(s)

Please list the resources as a list, for example:

If this issue appears to affect multiple resources, it may be an issue with Terraform's core, so please mention this.

Terraform Configuration Files

resource "aws_instance" "anastance" {
  count            = "${var.host_count}"
  ami              = "${var.instance["id"]}"
  instance_type    = "${var.instance["type"]}"
  subnet_id        = "${var.instance["subnet"]}"
  associate_public_ip_address = "${var.instance["public"]}"
  vpc_security_group_ids = ["${var.vpc_sgids}"]
  key_name         = "${var.instance_defs["key_name"]}"
  tags             = {
    Name           = "${element(random_id.guid.*.keepers.hostname, count.index)}.${var.host_domain}"
    Description    = "${var.host_desc}"
    Terraform      = "true"
    chef_environment = "${var.chef["env"]}"
  }
  root_block_device = {
    delete_on_termination = "${var.instance_defs["root_delete"]}"
  }
}

When I specify var.instance["public"] = "false", the instance always attempts to recreate itself.

Debug Output

N/A (sensitive material; draft a simple plan to see debug)

Panic Output

N/A

Expected Behavior

If an aws_instance declares associate_public_ip_address, override the subnet's default for map_public_ip_on_launch and accept user input.

Actual Behavior

Recreate resource because of conflict with associate_public_ip_address

-/+ module.host.aws_instance.myinstance
    ami:                                       "ami-ffffffff" => "ami-ffffffff"
    associate_public_ip_address:               "true" => "false" (forces new resource)
    availability_zone:                         "us-east-1b" => "<computed>"
    ebs_block_device.#:                        "0" => "<computed>"
    ephemeral_block_device.#:                  "0" => "<computed>"
    instance_state:                            "running" => "<computed>"
    instance_type:                             "t2.large" => "t2.large"
    key_name:                                  "key.pem" => "key.pem"
    network_interface_id:                      "eni-99403b67" => "<computed>"
    placement_group:                           "" => "<computed>"
    private_dns:                               "ip-10-0-0-35.ec2.internal" => "<computed>"
    private_ip:                                "10.0.0.35" => "<computed>"
    public_dns:                                "ec2-54-1-1-83.compute-1.amazonaws.com" => "<computed>"
    public_ip:                                 "54.1.1.83" => "<computed>"
    root_block_device.#:                       "1" => "1"
    root_block_device.0.delete_on_termination: "true" => "true"
    root_block_device.0.iops:                  "0" => "<computed>"
    root_block_device.0.volume_size:           "50" => "<computed>"
    root_block_device.0.volume_type:           "standard" => "<computed>"
    security_groups.#:                         "0" => "<computed>"
    source_dest_check:                         "true" => "true"
    subnet_id:                                 "subnet-ffffffff" => "subnet-ffffffff"
    tags.%:                                    "4" => "4"
    tags.Description:                          "Made by Terraform (sip)" => "Made by Terraform (sip)"
    tags.Name:                                 "ec2-host1-1-sip.domain.tld" => "ec2-host1-1-sip.domain.tld"
    tags.Terraform:                            "true" => "true"
    tags.chef_environment:                     "sip" => "sip"
    tenancy:                                   "default" => "<computed>"
    vpc_security_group_ids.#:                  "2" => "2"
    vpc_security_group_ids.1348469803:         "sg-ffffffff" => "sg-ffffffff"
    vpc_security_group_ids.390452254:          "sg-dddddddd" => "sg-dddddddd"

Steps to Reproduce

Please list the steps required to reproduce the issue, for example:

  1. Create plan with an aws_instance.associate_public_ip_address set to false on a subnet where map_public_ip_on_launch is set to true
  2. terraform apply

Important Factoids

If the subnet's default is to not map a public IP, you can map one by setting associate_public_ip_address to true. The reverse should work as well.

References

Unable to search and find some, but I know I've encountered this before.

selivanovm commented 5 years ago

Is there any workaround?

sc250024 commented 5 years ago

This should have been resolved a while ago. Still seeing issues from 2016 about this!

rick-masters commented 3 years ago

I have confirmed this bug has been fixed and can be closed. The following configuration will not work with terraform 0.8.7 but works with 0.14.5:

main.tf:

resource "aws_instance" "anastance" {
  count            = "${var.host_count}"
  ami              = "${var.instance["id"]}"
  instance_type    = "${var.instance["type"]}"
  subnet_id        = "${var.instance["subnet"]}"
  associate_public_ip_address = "${var.instance["public"]}"
  vpc_security_group_ids = ["${var.vpc_sgids}"]
  key_name         = "${var.instance_defs["key_name"]}"
  tags             = {
    Name           = "Test - Delme"
    Description    = "desc"
    Terraform      = "true"
    chef_environment = "chef"
  }
  root_block_device = {
    delete_on_termination = "${var.instance_defs["root_delete"]}"
  }
}

variables.tf:

variable instance {
   type = "map"
   default = {
    "id" = "ami-0928f4202481dfdf6"
    "type" = "m5.large"
    "public" = false
    "subnet"  = "subnet-012346redacted"
   }
}
variable vpc_sgids { default = "sg-012345redacted" }
variable instance_defs {
    type = "map"
    default = {
    "key_name" =  "redactedkey"
    "root_delete" =  true
   }
}
variable "host_count" { default = 1 }

(Note: You have to remove the equals sign after the root_block_device keyword for this config to work with terraform 0.14.5. See below)

The problem was that older versions of terraform left the variable in the string form of "false" instead of a boolean false.

Old terraform:

$ ~/go/bin/terraform-0.8.7 apply                                                                     
aws_instance.anastance: Creating...                                                                                                     
  ami:                                       "" => "ami-0928f4202481dfdf6"                                                              
  associate_public_ip_address:               "" => "false"
...

Recent terraform:

$ sed -i 's/root_block_device = {/root_block_device {/' main.tf 
$ terraform apply      

An execution plan has been generated and is shown below.            
Resource actions are indicated with the following symbols:          
  + create                                                          

Terraform will perform the following actions:          

  # aws_instance.anastance[0] will be created          
  + resource "aws_instance" "anastance" {                           
      + ami                          = "ami-0928f4202481dfdf6"      
      + arn                          = (known after apply)                                                                              
      + associate_public_ip_address  = false           

I do not know when this got fixed but it works properly now.

nigelgbanks commented 3 years ago

This doesn't appear to work with Terraform v0.13.2 if the subnet the instance is being deployed into has map_public_ip_on_launch set to true as is typical with the default VPC. I also tried with Terraform v0.15.0 and it does not work with that version either. In both cases terraform outputs:

      ~ associate_public_ip_address  = true -> false # forces replacement

And regardless the instance has a public IP regardless of the redeployment. As a workaround:

# Deploy into default vpc but make sure the default subnet does not automatically assign a public IP.
resource "aws_default_subnet" "default" {
  availability_zone       = var.availability_zone
  map_public_ip_on_launch = false
}

resource "aws_instance" "this" {
 #...
 depends_on = [
    aws_default_subnet.default
 ]
}
rick-masters commented 3 years ago

@nigelbanks - I spend quite a bit of time going through this issue and your comment and testing various configurations and versions and I as far as I can tell, the original reported problem was fixed in terraform 0.10.0 using terraform-provider-aws version 1.0.0, around September of 2017.

In my previous comment I said that it looked like older versions could not handle associate_public_ip_addression when set with quotation marks. Now I think I was wrong about that and must have just been confused by the output from the older version. Actually, in versions prior to terraform 0.10.0 it appears that associate_public_ip_address did not honor false at all when the subnet has map_public_ip_on_launch set to true.

The following configuration reproduces that problem with terraform prior to 0.10.0. In those version, the public IP is assigned but it shouldn't be.

resource "aws_vpc" "test_vpc" {
  cidr_block = "172.16.0.0/16"
  tags = {
    Name = "Test 411 VPC"
  }
}

resource "aws_subnet" "test_subnet" {
  vpc_id = "${aws_vpc.test_vpc.id}"

  cidr_block = "172.16.1.0/24"
  # *** This may need to be altered
  availability_zone = "us-west-2a"
  tags = {
    Name = "Test 411 subnet"
  }
  map_public_ip_on_launch = true
}

resource "aws_instance" "testmachine" {
  ami              = "ami-0928f4202481dfdf6"
  instance_type    = "m5.large"
  subnet_id        = "${aws_subnet.test_subnet.id}"
  root_block_device {
    delete_on_termination = true
  }
  tags = {
     Name = "Test 411 instance"
  }
  associate_public_ip_address = false
}

This configuration does not produce a public IP address for me in versions 0.10.0 with provider 1.0.0, 0.13.2 with provider 3.5.0 and 0.15.0 with provider 3.37.0.

So, I'm wondering if you are experiencing a different issue. Any additional information you could provide would be helpful. What is it exactly that does not appear to be working for you? If you could provide a minimal configuration that reproduces the problem along with the exact steps you are performing that would be ideal.

nigelgbanks commented 3 years ago

@rick-masters I'm a bit pressed for time atm, I've changed my system around to not rely on the default VPC so I no longer am deploying into a subnet that auto assigns a public IP. I'll likely have a bit of time in like a week or so to try to reproduce it again.