An application running on an EC2 instance needs to read objects from an S3 bucket. Explain how it obtains AWS credentials without any access key being stored on the instance, and what an instance profile is as distinct from the IAM role it holds.
answer
- no keys on the box
- the role needs a wrapper object
- link-local address serves the credentials
- temporary STS creds, auto-refreshed
- SDK looks there without being told
basics
~20 sAttach an IAM role to the instance through an instance profile — a container object that holds exactly one role. EC2 then publishes temporary credentials for that role on the Instance Metadata Service, and the AWS SDK fetches and refreshes them automatically.
solid answer
~50 sYou never put an access key on the instance. You create an IAM role whose trust policy allows the `ec2.amazonaws.com` service principal to assume it, and attach that role to the instance. EC2 cannot hold a role directly, so the role is wrapped in an **instance profile** — a container object that holds exactly one role and shares its name when the console creates it for you; with the CLI or IaC you create it explicitly. Once attached, the instance's metadata service serves short-lived STS credentials for that role at `/latest/meta-data/iam/security-credentials/<role-name>`, reachable at the link-local address `169.254.169.254`. Every AWS SDK and the CLI look there as part of their default credential lookup, cache the credentials, and refresh them before they expire — application code just calls `s3.getObject` with no credentials configured. Rotation, expiry and delivery are AWS's problem, not yours.
code
bash · 10 lines# IMDSv2: get a session token, then read the role's credentials
TOKEN=$(curl -sX PUT "http://169.254.169.254/latest/api/token" \
-H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
ROLE=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/meta-data/iam/security-credentials/)
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" \
"http://169.254.169.254/latest/meta-data/iam/security-credentials/$ROLE"
# -> {"AccessKeyId":"ASIA...","SecretAccessKey":"...","Token":"...","Expiration":"..."}go deeper
Be ready to say plainly that you attach an IAM role instead of copying an access key onto the server, and that the SDK finds the credentials on its own with nothing configured in code.
Explain the mechanics: the instance profile wraps exactly one role, the trust policy names ec2.amazonaws.com, and the metadata service serves short-lived STS credentials that the SDK caches and refreshes.
Show the diagnosis path when it misbehaves — confirming the signing principal, spotting environment or profile credentials that shadow the role, and knowing that containers on the host need an adequate metadata hop limit.
Own the standard: no static keys anywhere in the fleet, IMDSv2 required, one narrowly scoped role per workload rather than a shared fleet role, and a guardrail that stops long-lived keys from being created at all.
## Why static keys are the wrong answer The naive way to let code on EC2 call AWS is to create an IAM user, generate an access key pair, and drop it in a config file or environment variable. That key is long-lived: it works until someone manually rotates it, it survives in AMIs, backups, logs and `docker inspect` output, and it carries no expiry. Almost every published AWS credential-leak incident traces back to a static key that outlived its purpose. AWS's answer for compute is that the *platform* hands the workload short-lived credentials, and no secret is ever written down. ## Role, instance profile — why there are two objects An **IAM role** is an identity with permission policies and a **trust policy** saying who may assume it. For an EC2 workload the trust policy names the service principal: ```json { "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Principal": { "Service": "ec2.amazonaws.com" }, "Action": "sts:AssumeRole" }] } ``` An **instance profile** is a thin container that holds exactly one role and is the thing actually associated with an instance. The indirection is historical plumbing, but it is a real object with its own ARN: the console hides it (creating a profile with the same name as the role), while the CLI, CloudFormation and Terraform make you notice it. The practical consequences are that a profile can hold only one role at a time, and that the API for changing it is `ec2:AssociateIamInstanceProfile` / `ec2:ReplaceIamInstanceProfileAssociation` — you can attach or swap a profile on a running instance without stopping it. ## How the credentials reach the process EC2 exposes the Instance Metadata Service (IMDS) on the link-local address `169.254.169.254`, unroutable off the host. With IMDSv2 the client first does a `PUT` to `/latest/api/token` supplying `X-aws-ec2-metadata-token-ttl-seconds`, then sends the returned token in `X-aws-ec2-metadata-token` on every subsequent `GET`. Reading `/latest/meta-data/iam/security-credentials/` returns the role name; reading that path plus the role name returns a small JSON document with `AccessKeyId`, `SecretAccessKey`, `Token` and `Expiration` — a standard set of temporary STS credentials. AWS SDKs do this for you. The IMDS lookup is the last link in the SDK's default credential lookup, so code written as `new S3Client()` with nothing configured picks up the instance role. The SDK caches the credentials in memory and re-fetches them in the background before `Expiration`, which is why a long-running process does not need a restart when they roll. ## Expiry, rotation, revocation The credentials are minted by STS and last on the order of hours; EC2 refreshes them well ahead of expiry, so a healthy instance always has a valid set. Because they expire, a credential copied off the box has a short life. Changing what the workload can do is a policy edit on the role and takes effect on the next authorization decision — no redeploy. If you must kill credentials already handed out, you revoke sessions by attaching a deny policy conditioned on `aws:TokenIssueTime`, or you detach the profile. ## Where it bites in practice - **The role is attached but calls still fail with AccessDenied under an unexpected principal.** Something earlier in the SDK's lookup won — a stale `AWS_ACCESS_KEY_ID` in the environment, a baked `~/.aws/credentials`, or `AWS_PROFILE`. `aws sts get-caller-identity` tells you which principal the credentials actually resolve to. - **Containers on the instance cannot reach IMDS.** The metadata request from inside a container takes an extra network hop, so an instance whose metadata options set the hop limit to 1 will serve the host but not bridged containers. - **Nothing you attach makes S3 work if the bucket side says no.** A cross-account bucket needs an allow on both the role and the bucket policy. - **Attaching the role requires `iam:PassRole`.** The human or pipeline launching the instance must be allowed to hand that role to EC2. The same shape repeats across AWS compute: the platform delivers a role's temporary credentials to the workload, and the SDK picks them up without any code change. Only the delivery channel differs per service.
- Can one instance profile hold more than one IAM role, and how do you change the role on a running instance?No — an instance profile holds exactly one role. To change what an instance can do you either edit the policies on the attached role, or replace the association with a different profile using `ec2:ReplaceIamInstanceProfileAssociation`, which works on a running instance without a stop or a reboot. The SDK picks up the new role's credentials on its next refresh.
- The instance role allows s3:GetObject, but the call still returns AccessDenied. Where do you look?First confirm which principal is actually signing: `aws sts get-caller-identity`. If it is not the role, something earlier in the SDK's credential lookup — environment variables, a shared credentials file, `AWS_PROFILE` — is shadowing it. If it is the role, the denial is elsewhere: a bucket policy in another account that never allows your role, an explicit deny, a KMS key policy on an encrypted object, or an organization-level restriction.
- Why is the instance role considered safer than an access key even though anything running on the box can read it?It is not immune — code on the instance, or an SSRF bug reaching the metadata endpoint, can read it. The gains are that the credential expires in hours, rotates without human action, never appears in an AMI, repo or backup, and can be scoped to exactly that workload. IMDSv2's session-token handshake also blocks the naive SSRF fetch that plain GET-based metadata allowed.
saying these in an interview costs you the question
- Storing an IAM user's access key in a file on the instance
- Saying an IAM role attaches to an instance directly, with no profile
- Claiming metadata credentials are permanent and need manual rotation
- Thinking you must read the metadata endpoint yourself in application code
- Assuming the instance role alone grants access to a cross-account bucket