amazon-web-servicesnetwork-programmingterraformterraform-provider-aws

EC2 can't get a public ip address after stop-start


I am creating EC2, with 3 additional private interfaces using terraform. After plan/apply, everything is created (and public ip). But when I do ec2 stop and then start again, aws does not provide public ip. And accordingly, when I make a terraform plan/apply again, all resources are change and destroy. Auto-assign public IPv4 address - Yes enter image description here tf version - v1.6.6


   aws = {
      source  = "hashicorp/aws"
      version = "~> 4.0"
    }



resource "aws_instance" "monitoring" {
  ami                    = var.ami_id   
  instance_type          = var.instance_type
  subnet_id              = var.subnet_id
  associate_public_ip_address = true
  key_name               = var.key
  # iam_instance_profile   = var.aws_iam_instance_profile
  # security_groups = [aws_security_group.common.id]

  tags = merge(
    local.common_tags,
        {
      "Name" = "${var.monitoring}"
    }
  )

  lifecycle {
    # ignore_changes = [network_interface, associate_public_ip_address, vpc_security_group_ids, security_groups]
    prevent_destroy = false
  }
}

resource "aws_network_interface" "private1" {
  subnet_id       = var.subnet_id
  # security_groups = [aws_security_group.common.id]
  attachment {
    instance     = aws_instance.monitoring.id
    device_index = 1
  }
 

  tags = merge(
    local.common_tags,
        {
      "Name" = "${var.monitoring}"
    }
  )
}

.....

output


Terraform will perform the following actions:

  # aws_instance.monitoring must be replaced
-/+ resource "aws_instance" "monitoring" {
      ~ arn                                  = "arn:aws:ec2:eu-west-1:335094912020:instance/i-04dbad35419e5403d" -> (known after apply)
      ~ associate_public_ip_address          = false -> true # forces replacement
      ~ availability_zone                    = "eu-west-1a" -> (known after apply)
      ~ cpu_core_count                       = 2 -> (known after apply)
      ~ cpu_threads_per_core                 = 2 -> (known after apply)
      ~ disable_api_stop                     = false -> (known after apply)
      ~ disable_api_termination              = 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)
      + iam_instance_profile                 = (known after apply)
      ~ id                                   = "i-04dbad35419e5403d" -> (known after apply)
      ~ instance_initiated_shutdown_behavior = "stop" -> (known after apply)
      ~ instance_state                       = "running" -> (known after apply)
      ~ ipv6_address_count                   = 0 -> (known after apply)
      ~ ipv6_addresses                       = [] -> (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-067ddc45b42b3bc58" -> (known after apply)
      ~ private_dns                          = "ip-10-1-4-200.eu-west-1.compute.internal" -> (known after apply)
      ~ private_ip                           = "10.1.4.200" -> (known after apply)
      + public_dns                           = (known after apply)
      + public_ip                            = (known after apply)
      ~ secondary_private_ips                = [] -> (known after apply)
      ~ security_groups                      = [] -> (known after apply)
        tags                                 = {
            "Name"      = "advertisement-monitoring"
            "Owner"     = "devops-team"
            "Project"   = "advertising"
            "Role"      = "monitoring"
            "Terraform" = "true"
        }
      ~ tenancy                              = "default" -> (known after apply)
      + user_data                            = (known after apply)
      + user_data_base64                     = (known after apply)
      ~ vpc_security_group_ids               = [
          - "sg-058c8236c532c5a43",
        ] -> (known after apply)
        # (8 unchanged attributes hidden)

      - capacity_reservation_specification {
          - capacity_reservation_preference = "open" -> null
        }

      - cpu_options {
          - core_count       = 2 -> null
          - threads_per_core = 2 -> 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_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 {
          - delete_on_termination = true -> null
          - device_name           = "/dev/xvda" -> null
          - encrypted             = false -> null
          - iops                  = 3000 -> null
          - tags                  = {} -> null
          - throughput            = 125 -> null
          - volume_id             = "vol-05544aeda21e9408c" -> null
          - volume_size           = 8 -> null
          - volume_type           = "gp3" -> null
        }
    }

  # aws_network_interface.private1 will be updated in-place
  ~ resource "aws_network_interface" "private1" {
        id                        = "eni-0f66a33626a3a3f66"
        tags                      = {
            "Name"      = "advertisement-monitoring"
            "Owner"     = "devops-team"
            "Project"   = "advertising"
            "Role"      = "monitoring"
            "Terraform" = "true"
        }
        # (22 unchanged attributes hidden)

      - attachment {
          - attachment_id = "eni-attach-0740bf5f5dfab0d38" -> null
          - device_index  = 1 -> null
          - instance      = "i-04dbad35419e5403d" -> null
        }
      + attachment {
          + attachment_id = (known after apply)
          + device_index  = 1
          + instance      = (known after apply)
        }
    }



Solution

  • This the expected beahvior as if you have more than two eni in your EC2 then you will not receive a new public IP, from the docs :

    In certain cases, we release the public IP address from your instance, or assign it a new one:

    We release your instance's public IP address when it is stopped, hibernated, or terminated. Your stopped or hibernated instance receives a new public IP address when it is started.

    We release your instance's public IP address when you associate an Elastic IP address with it. When you disassociate the Elastic IP address from your instance, it receives a new public IP address.

    If the public IP address of your instance in a VPC has been released, it will not receive a new one if there is more than one network interface attached to your instance.

    If your instance's public IP address is released while it has a secondary private IP address that is associated with an Elastic IP address, the instance does not receive a new public IP address.

    From Terraform perspective the instance must have a public IP, So after losing it it will try to recreate it to restablish the state this what's shown in the plan :

    ~ associate_public_ip_address          = false -> true # forces replacement
    

    try to use EIPs instead on relying on auto assignement of public IPs bear in mind that is incuring charges from February 1, 2024 despite in use.