Skip to content

EC2 launch templates should not assign public IPs to network interfaces

A launch template with associate_public_ip_address set to true means every instance launched from it gets a public IP by default, bypassing subnet-level controls. The risk compounds when launch templates are referenced by Auto Scaling Groups: a single misconfiguration can propagate to dozens or hundreds of instances before anyone notices.

Public IPs expose instances directly to the internet. Even with tight security groups, a publicly addressed instance is one misconfigured rule away from full exposure. NAT gateways, VPC endpoints, and load balancers give you outbound and inbound connectivity without putting a public address on every instance.

Retrofit consideration

Existing Auto Scaling Groups using non-compliant launch templates will need new template versions published and version references updated. Instances already running with public IPs are unaffected by template changes but should be audited for direct internet dependencies, particularly missing NAT gateway routes, before those IPs are removed.

Implementation

Choose the approach that matches how you manage Terraform.

Use the compliance.tf module to enforce this control by default. See get started with compliance.tf.

module "autoscaling" {
  source  = "pcidss.compliance.tf/terraform-aws-modules/autoscaling/aws"
  version = ">=9.0.0"

  image_id      = "ami-abc12345"
  instance_type = "t4g.nano"
  max_size      = 1
  min_size      = 0
  name          = "abc123"
  network_interfaces = [
    {
      associate_public_ip_address = false
      security_groups             = ["sg-12345678"]
      subnet_id                   = "subnet-12345678"
    }
  ]
  vpc_zone_identifier = ["subnet-12345678"]
}

If you use terraform-aws-modules/autoscaling/aws, set the right module inputs for this control. You can later migrate to the compliance.tf module with minimal changes because it is compatible by design.

module "autoscaling" {
  source  = "terraform-aws-modules/autoscaling/aws"
  version = ">=9.0.0"

  image_id            = "ami-abc12345"
  instance_type       = "t4g.nano"
  max_size            = 1
  min_size            = 0
  name                = "abc123"
  vpc_zone_identifier = ["subnet-12345678"]

  network_interfaces = {
    associate_public_ip_address = "false"
  }
}

Use AWS provider resources directly. See docs for the resources involved: aws_launch_template.

resource "aws_launch_template" "this" {
  name_prefix = "pofix-abc123-"

  network_interfaces {
    associate_public_ip_address = "false"
    subnet_id                   = "subnet-12345678"
  }
}

What this control checks

In Terraform, aws_launch_template has an optional network_interfaces block with an associate_public_ip_address argument that controls whether instances get a public IPv4 at launch. Set it to false on every network_interfaces block, or omit the block entirely and let the subnet's map_public_ip_on_launch setting govern behavior (which should itself be false in private subnets). It fails if associate_public_ip_address = true on any network interface definition. When no network_interfaces block is present, the launch template defers to the subnet default, which this control treats as passing.

Common pitfalls

  • Subnet default override ignored

    Passing this control doesn't guarantee instances stay private. Omit the network_interfaces block and the subnet takes over, but if the target subnet has map_public_ip_on_launch = true, instances will still get public IPs at launch. The control evaluates the template, not the subnet, so it can pass while instances remain publicly accessible.

  • Multiple template versions

    Creating a compliant new template version doesn't help if downstream services pin to an older one. An Auto Scaling Group with launch_template { version = "3" } keeps launching from that version regardless of what you've fixed since. When you publish a remediated template version, update the version references in every ASG and service that consumes it.

  • Inline network_interfaces vs. top-level arguments

    Every network_interfaces block in the template needs associate_public_ip_address = false. For multi-ENI instances with multiple blocks, one block left at true fails the whole template. There's no partial pass here.

  • EIP attachment workaround

    A correctly configured launch template won't prevent someone from attaching an Elastic IP post-launch via aws_eip_association. This control only evaluates the template itself. EIP attachments happen out-of-band and require separate detective or preventive controls to catch.

Audit evidence

AWS Config evaluation results for launch templates, whether from the managed rule or a custom rule calling ec2:DescribeLaunchTemplateVersions, should show all templates as COMPLIANT. On the EC2 console, the "Network interfaces" section of each active template version should confirm "Auto-assign public IP" is disabled or unconfigured. CloudTrail logs for CreateLaunchTemplate and CreateLaunchTemplateVersion provide a record that no public-IP-enabled template version was created during the audit period. Security Hub finding history gives a time-series compliance view across accounts and regions, useful for demonstrating continuous monitoring coverage.

Framework-specific interpretation

PCI DSS v4.0: Requirements 1.3 and 1.4 both apply here. Requirement 1.3 restricts inbound and outbound traffic to the cardholder data environment; a launch template that automatically assigns public IPs places instances directly on the internet and breaks that boundary. Requirement 1.4 adds controls between trusted and untrusted networks, which any instance carrying a public IP by definition straddles.

Tool mappings

Use these identifiers to cross-reference this control across tools, reports, and evidence.

  • Compliance.tf Control: ec2_launch_template_not_publicly_accessible

  • AWS Config Managed Rule: EC2_LAUNCH_TEMPLATE_PUBLIC_IP_DISABLED

  • Checkov Check: CKV_AWS_88

  • Powerpipe Control: aws_compliance.control.ec2_launch_template_not_publicly_accessible

  • Prowler Check: ec2_launch_template_no_public_ip

  • AWS Security Hub Control: EC2.25

Last reviewed: 2026-03-09