Retrieving EC2 Instance ID via Instance Metadata Service (IMDSv2)
Retrieving your EC2 instance ID from within a running script is a foundational task — but using the wrong metadata approach exposes your workload to SSRF-based credential theft.
TL;DR: IMDSv1 vs IMDSv2 for Retrieving Instance ID
| Approach | Mechanism | Best For | Complexity |
|---|---|---|---|
| IMDSv1 (legacy) | Single unauthenticated GET | Isolated dev environments only | Low |
| IMDSv2 (recommended) | Session-oriented PUT + GET with token | All production workloads | Low-Medium |
Why IMDSv2 Is Safer for EC2 Instance Metadata Retrieval
IMDSv2 requires a session token obtained via an HTTP PUT request before any metadata can be read. This session-oriented design defeats the most common SSRF attack vector: a forged GET request from a compromised application that silently reads IAM credentials or the instance ID from 169.254.169.254. IMDSv1 has no such gate — any process or proxied request reaching that IP gets a response. IMDSv2 enforces a two-step handshake that SSRF payloads cannot complete without controlling the PUT verb and the TTL header.
Think of IMDSv1 as a door with no lock — anyone who reaches it walks in. IMDSv2 adds a deadbolt that requires a specific knock sequence only the instance itself can perform reliably.
How the IMDSv2 Token Flow Works
The sequence below shows the two-leg exchange your script must complete before reading any metadata path, including instance-id.
- PUT /latest/api/token — The instance sends a PUT with a TTL header. The IMDS endpoint returns a session token in the response body.
- GET /latest/meta-data/instance-id — The instance attaches the token as a request header. IMDS validates the token and returns the instance ID string.
- If the PUT is blocked (e.g.,
HttpEndpointdisabled or hop-limit exceeded), the token request fails and no metadata is accessible.
Step 1: Verify IMDSv2 Is Enforced on Your Instance
Before writing any script, confirm the instance is configured to require IMDSv2. An instance left in optional mode still accepts IMDSv1 requests, which negates the security benefit.
— Why this step: if HttpTokens is still set to optional, your hardened script works correctly but an attacker's IMDSv1 request also works — the enforcement gap is invisible at the script level.
aws ec2 describe-instances \
--instance-ids i-0abcdef1234567890 \
--query 'Reservations[*].Instances[*].MetadataOptions' \
--output table \
--region us-east-1
Look for HttpTokens: required and HttpEndpoint: enabled in the output. If HttpTokens shows optional, enforce IMDSv2 with:
aws ec2 modify-instance-metadata-options \
--instance-id i-0abcdef1234567890 \
--http-tokens required \
--http-endpoint enabled \
--region us-east-1
Enforcing required at the instance level is a hard gate — it cannot be overridden by the script or the application running on it.
Step 2: Retrieve the Instance ID Using IMDSv2 (Shell)
The two-step pattern below is the AWS-documented approach. The token is returned in the response body of the PUT, so reading stdout directly is both correct and sufficient.
— Why this step: the token lives in the response body, not in a header — scripts that try to parse headers for the token will silently receive an empty value and fail on the subsequent GET.
Show: Production-hardened shell snippet
#!/bin/bash
set -euo pipefail
# Step 1: Obtain IMDSv2 session token (TTL = 6 hours)
TOKEN=$(curl -sf -X PUT \
"http://169.254.169.254/latest/api/token" \
-H "X-aws-ec2-metadata-token-ttl-seconds: 21600") \
|| { echo "ERROR: Failed to obtain IMDSv2 token. Verify HttpEndpoint is enabled."; exit 1; }
# Step 2: Use token to retrieve instance ID
INSTANCE_ID=$(curl -sf \
"http://169.254.169.254/latest/meta-data/instance-id" \
-H "X-aws-ec2-metadata-token: ${TOKEN}") \
|| { echo "ERROR: Failed to retrieve instance-id."; exit 1; }
echo "Instance ID: ${INSTANCE_ID}"
Key flags:
-s— silent mode, suppresses progress output-f— fail on HTTP error codes (4xx/5xx), enabling the|| { ... }error trap-X PUT— required for the token endpoint; a GET to that path returns a 405X-aws-ec2-metadata-token-ttl-seconds— mandatory header; omitting it returns a 400
Step 3: Retrieve the Instance ID Using IMDSv2 (Python)
For application code, the requests library maps cleanly to the two-step flow. The pattern below is safe for Lambda-style initialization blocks and long-running daemon processes alike.
— Why this step: Python scripts that use boto3 to call describe-instances for self-identification make an outbound API call requiring IAM permissions and network egress — the metadata endpoint requires neither.
Show: Python IMDSv2 snippet
import requests
import sys
IMDS_BASE = "http://169.254.169.254/latest"
TOKEN_TTL = "21600"
def get_imdsv2_token() -> str:
resp = requests.put(
f"{IMDS_BASE}/api/token",
headers={"X-aws-ec2-metadata-token-ttl-seconds": TOKEN_TTL},
timeout=2,
)
resp.raise_for_status()
return resp.text
def get_instance_id(token: str) -> str:
resp = requests.get(
f"{IMDS_BASE}/meta-data/instance-id",
headers={"X-aws-ec2-metadata-token": token},
timeout=2,
)
resp.raise_for_status()
return resp.text
if __name__ == "__main__":
try:
token = get_imdsv2_token()
instance_id = get_instance_id(token)
print(f"Instance ID: {instance_id}")
except requests.exceptions.RequestException as exc:
print(f"ERROR: {exc}", file=sys.stderr)
sys.exit(1)
Set timeout=2 on both calls. The IMDS endpoint is link-local and responds in milliseconds on a healthy instance — a hung request almost always means the endpoint is disabled or the hop-limit is misconfigured, not a transient network blip.
Step 4: Confirm the Hop-Limit Is Not Blocking Container Workloads
Container workloads add a network hop between the container and the host. If the http-put-response-hop-limit is set to 1 (the default for instances launched before the IMDSv2 enforcement rollout), the PUT response never reaches the container and the token request times out silently.
— Why this step: the symptom — a curl timeout on the PUT — looks identical to a disabled endpoint, so engineers fix the wrong thing first and lose time.
aws ec2 describe-instances \
--instance-ids i-0abcdef1234567890 \
--query 'Reservations[*].Instances[*].MetadataOptions.HttpPutResponseHopLimit' \
--output text \
--region us-east-1
If the output is 1 and your workload runs inside a container, increase it to 2:
aws ec2 modify-instance-metadata-options \
--instance-id i-0abcdef1234567890 \
--http-put-response-hop-limit 2 \
--http-tokens required \
--http-endpoint enabled \
--region us-east-1
A hop-limit of 2 covers the container-to-host boundary without opening the endpoint to requests proxied further up the stack.
Required IAM Permissions for Metadata Option Management
Reading and modifying instance metadata options requires EC2 API permissions, not IMDS permissions. The IMDS endpoint itself requires no IAM — it is accessible to any process on the instance. The IAM policy below covers the diagnostic and enforcement steps in this post.
Show: IAM policy for metadata option management
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DescribeInstanceMetadataOptions",
"Effect": "Allow",
"Action": "ec2:DescribeInstances",
"Resource": "*"
},
{
"Sid": "EnforceIMDSv2",
"Effect": "Allow",
"Action": "ec2:ModifyInstanceMetadataOptions",
"Resource": "arn:aws:ec2:us-east-1:123456789012:instance/i-0abcdef1234567890"
}
]
}
ec2:DescribeInstances does not support resource-level restrictions and requires "Resource": "*". ec2:ModifyInstanceMetadataOptions supports instance-level ARN restrictions and should be scoped accordingly.
Troubleshooting IMDSv2 Token Failures
- PUT returns 400 — The
X-aws-ec2-metadata-token-ttl-secondsheader is missing or malformed. Verify the header name and that the value is a positive integer. - PUT times out — Either
HttpEndpointis disabled, or the hop-limit is too low for the network path (common in containers). Run Step 1 and Step 4 diagnostics above. - GET returns 401 — The token has expired or was not passed correctly. Re-run the PUT to obtain a fresh token and verify the header name on the GET is
X-aws-ec2-metadata-token. - GET returns 404 — The metadata path is incorrect. The canonical path for instance ID is
/latest/meta-data/instance-id— verify there is no trailing slash or typo.
Glossary
- IMDS (Instance Metadata Service)
- A link-local HTTP endpoint at
169.254.169.254that provides instance-specific data to processes running on an EC2 instance without requiring IAM credentials or network egress. - IMDSv2
- Session-oriented version of IMDS introduced in 2019. Requires a PUT-based token exchange before metadata can be read, mitigating SSRF-based credential theft.
- HttpTokens
- Instance metadata option controlling whether IMDSv2 is
requiredoroptional. Setting this torequireddisables IMDSv1 access entirely. - HttpPutResponseHopLimit
- The maximum number of network hops the IMDSv2 PUT response packet can traverse. Default is 1; container workloads typically require 2.
- SSRF (Server-Side Request Forgery)
- An attack where a compromised application is tricked into making HTTP requests to internal endpoints — such as the IMDS — on behalf of an attacker.
- Link-local address
- The
169.254.0.0/16address range, non-routable beyond the local network segment. The IMDS endpoint uses169.254.169.254within this range.
Retrieving EC2 Instance ID Safely: Final Checklist
Using IMDSv2 to retrieve your EC2 instance ID is a two-line change that closes a well-documented attack surface. Enforce HttpTokens: required at the instance level, set the hop-limit correctly for your runtime environment, and scope the IAM policy for metadata option management to specific instance ARNs. For deeper context on securing EC2 workloads, see the AWS documentation on Configuring the Instance Metadata Service and the EC2 Security Best Practices guide.
Comments
Post a Comment