Skip to content

EC2 launch templates should use Instance Metadata Service Version 2 (IMDSv2)

The EC2 metadata service at 169.254.169.254 hands out temporary IAM credentials, instance identity documents, and user data scripts to anyone who asks. IMDSv1 asks for nothing in return: any HTTP GET works. A vulnerable web app, a misconfigured proxy, or a basic SSRF payload is enough to retrieve credentials in seconds.

IMDSv2 requires a session token obtained via a PUT request with a TTL header. Most SSRF vectors can't issue PUT requests or forward arbitrary headers, which cuts the attack chain at the source. Setting this at the launch template level means every instance launched from it inherits the protection automatically, without relying on individual teams to remember the flag at launch time.

Retrofit consideration

Existing workloads using IMDSv1 SDK calls (AWS SDKs before mid-2019, custom scripts issuing plain GET requests to 169.254.169.254) will break when HttpTokens is switched to required. Check the CloudWatch metric MetadataNoToken on affected instances before enforcing.

Implementation

Choose the approach that matches how you manage Terraform.

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"
  network_interfaces = [
    {
      associate_public_ip_address = false
      security_groups             = ["sg-12345678"]
      subnet_id                   = "subnet-12345678"
    }
  ]
  vpc_zone_identifier = ["subnet-12345678"]

  metadata_options = {
    http_tokens = "required"
  }
}

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"
  }

  metadata_options {
    http_tokens = "required"
  }
}

What this control checks

The aws_launch_template resource must include a metadata_options block with http_tokens set to "required". When http_tokens is omitted or set to "optional", the control fails because instances launched from the template accept IMDSv1 requests.

Set http_endpoint to "enabled" (the default) to keep the metadata service available. Setting http_put_response_hop_limit to 1 prevents containers and multi-hop network paths from reaching the endpoint, though that is outside the scope of this control.

Evaluation targets the default version of the launch template, identified by default_version or update_default_version = true. New versions created without a metadata_options block do not inherit the setting from previous versions, so the default version must explicitly carry http_tokens = "required".

Common pitfalls

  • Omitting metadata_options defaults to IMDSv1 allowed

    metadata_options { http_tokens = "required" } must be explicit. Omit the block entirely and AWS defaults HttpTokens to optional, IMDSv1 is permitted, and the control fails. There is no implicit inheritance.

  • New launch template versions may not inherit metadata_options

    Terraform does not carry metadata_options forward automatically when you restructure a resource block. Add update_default_version = true and reorganize the block, and the new default version can silently drop the http_tokens setting. Always include the block explicitly in every version definition.

  • Auto Scaling Groups pinned to a specific version

    An aws_autoscaling_group that pins a specific launch template version number ignores changes to the default version entirely. Updating the default to require IMDSv2 has no effect on that ASG or the instances it launches. Reference $Default instead, or update the pinned version directly.

  • SDK and application compatibility with IMDSv2

    AWS SDK versions from before mid-2019 use IMDSv1 GET calls for credential retrieval. Flipping http_tokens to required on instances running those SDKs causes credential failures immediately. Check MetadataNoToken in CloudWatch before enforcing this on existing workloads.

Audit evidence

The primary artifact is aws ec2 describe-launch-template-versions --launch-template-id <id> --versions $Default showing MetadataOptions.HttpTokens as required for every in-scope launch template. Config rule results for ec2-launch-template-imdsv2-check support this, and Security Hub or a CSPM tool provides continuous pass/fail status across the account.

For change history, CloudTrail events for CreateLaunchTemplate and CreateLaunchTemplateVersion should show the MetadataOptions parameter in each request. A non-default version with HttpTokens set to optional does not fail this control unless it is promoted to the default version.

Framework-specific interpretation

Tool mappings

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

  • Compliance.tf Control: ec2_launch_template_default_version_uses_imdsv2

  • AWS Config Managed Rule: EC2_LAUNCH_TEMPLATE_IMDSV2_CHECK

  • Checkov Check: CKV_AWS_79

  • Powerpipe Control: aws_compliance.control.ec2_launch_template_default_version_uses_imdsv2

  • Prowler Check: ec2_launch_template_imdsv2_required

  • AWS Security Hub Controls: EC2.170, EC2.8

  • KICS Query: c306ac53-ee5b-41d3-86a9-0fd2722b4e67

Last reviewed: 2026-03-09