Roles, trust policies and STS

Amazon Web Services · Practical · 14 min

Roles are how instances, functions, containers, CI and other accounts get credentials without any secret to leak.

A role has two policy sides:

  • The trust policy (a resource-based policy on the role) says which principals may call sts:AssumeRole: a service like lambda.amazonaws.com, another account, a specific role, or a federated identity provider such as GitHub Actions OIDC.
  • Permission policies say what the role can do once assumed.

STS (Security Token Service) returns temporary credentials: an access key ID, secret key and session token, valid by default for one hour (up to the role's maximum session duration, at most 12 hours; role chaining is capped at one hour). Nothing long-lived exists to steal.

How services use roles:

  • EC2: an instance profile wraps the role; the SDK fetches rotating credentials from the instance metadata service. Require IMDSv2, which uses session tokens and blocks common SSRF tricks.
  • Lambda: the execution role.
  • ECS: the task role is what your code uses; the task execution role is what ECS itself uses to pull images and write logs. Mixing them up is a common bug.

Cross-account access: account B creates a role trusting account A; principals in A need permission to call sts:AssumeRole on it. For third-party SaaS that assumes into your account, require an sts:ExternalId condition to prevent the confused-deputy problem.

The CLI and SDKs assume roles automatically when a profile has role_arn and source_profile, refreshing credentials as they expire. CI systems should use OIDC federation (AssumeRoleWithWebIdentity) instead of stored keys.

Example

cat > trust.json <<'EOF'
{"Version": "2012-10-17",
 "Statement": [{"Effect": "Allow",
                "Principal": {"Service": "lambda.amazonaws.com"},
                "Action": "sts:AssumeRole"}]}
EOF
aws iam create-role --role-name orders-fn \
  --assume-role-policy-document file://trust.json
aws iam attach-role-policy --role-name orders-fn \
  --policy-arn arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole

# Assume a role in another account by hand
aws sts assume-role --role-arn arn:aws:iam::444455556666:role/Deployer \
  --role-session-name alice --query Credentials.Expiration --output text
# 2026-10-17T13:04:11+00:00

# ...or let a profile do it automatically
# [profile prod]
# role_arn = arn:aws:iam::444455556666:role/Deployer
# source_profile = dev

Remember

  • Trust policy = who can assume; permission policy = what it can do
  • STS credentials expire (default 1 hour) and carry a session token
  • ECS task role is for your code; task execution role is for ECS itself
  • Use ExternalId for third parties and OIDC federation for CI

Watch out for

  • Putting the permissions a container needs on the ECS task execution role instead of the task role.

Check yourself

  1. Account A's CI role must deploy into account B. What is required?
  2. Where does an EC2 instance's application get its role credentials?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.aws.amazon.com/IAM/latest/UserGuide/id_roles.html