IAM Roles for Applications & Services
An IAM role is an identity you attach to compute, not a set of stored keys.
Search across all documentation pages
An IAM role is an identity you attach to compute, not a set of stored keys.
Code running on that compute gets temporary AWS credentials automatically, which is why roles are the default way to authenticate SDK calls in production.
A role carries permissions but has no long-lived secret of its own.
Something assumes the role and receives temporary credentials from STS that expire and rotate on their own.
For code on AWS, the assuming is invisible - an instance profile on EC2, a task role on ECS, or an execution role on Lambda hands credentials to the SDK through a metadata endpoint.
Your code just creates a client with no keys, and the default provider chain does the rest.
For anything off that compute, or across accounts, you call sts:AssumeRole explicitly.
Quick-reference recipe card - copy-paste ready.
# --- Python (boto3) ---
import boto3
# On EC2/ECS/Lambda: no keys. The SDK reads the attached role's
# temporary credentials from the metadata endpoint automatically.
s3 = boto3.client("s3")
print([b["Name"] for b in s3.list_buckets()["Buckets"]])// --- TypeScript (AWS SDK v3) ---
import { S3Client, ListBucketsCommand } from "@aws-sdk/client-s3";
// Same: empty config, the provider chain finds the role credentials.
const s3 = new S3Client({});
const out = await s3.send(new ListBucketsCommand({}));
console.log(out.Buckets?.map((b) => b.Name));When to reach for this:
Create a Lambda execution role with a trust policy, attach a permission policy, and confirm the identity - all through the SDK.
# --- Python (boto3) ---
import json, boto3
iam = boto3.client("iam")
# 1. Trust policy: only the Lambda service may assume this role.
trust = {"Version": "2012-10-17", "Statement": [
{"Effect": "Allow",
"Principal": {"Service": "lambda.amazonaws.com"},
"Action": "sts:AssumeRole"}]}
role = iam.create_role(
RoleName="report-generator-role",
AssumeRolePolicyDocument=json.dumps(trust),
)["Role"]
# 2. Grant the role only the actions the function needs.
iam.attach_role_policy(
RoleName="report-generator-role",
PolicyArn="arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole",
)
print("Role ready:", role["Arn"])// --- TypeScript (AWS SDK v3) ---
import {
IAMClient, CreateRoleCommand, AttachRolePolicyCommand,
} from "@aws-sdk/client-iam";
const iam = new IAMClient({});
// 1. Trust policy: only the Lambda service may assume this role.
const trust = { Version: "2012-10-17", Statement: [
{ Effect: "Allow",
Principal: { Service: "lambda.amazonaws.com" },
Action: "sts:AssumeRole" }] };
const { Role } = await iam.send(new CreateRoleCommand({
RoleName: "report-generator-role",
AssumeRolePolicyDocument: JSON.stringify(trust),
}));
// 2. Grant the role only the actions the function needs.
await iam.send(new AttachRolePolicyCommand({
RoleName: "report-generator-role",
PolicyArn: "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole",
}));
console.log("Role ready:", Role?.Arn);What this demonstrates:
Principal.Service value binds the role to a specific AWS service.AWSLambdaBasicExecutionRole is an AWS-managed policy granting CloudWatch Logs access.| Compute | Role mechanism | How the SDK gets credentials |
|---|---|---|
| EC2 | Instance profile wrapping a role | Instance Metadata Service (IMDSv2) |
| ECS / Fargate | Task role | Container credentials endpoint |
| Lambda | Execution role | Injected environment credentials |
| EKS pod | IRSA (service-account role) | Projected web-identity token + STS |
For code that is not on the target compute, or that needs another account's resources, assume the role explicitly.
# --- Python (boto3) ---
import boto3
sts = boto3.client("sts")
c = sts.assume_role(
RoleArn="arn:aws:iam::444455556666:role/cross-account-read",
RoleSessionName="reporting",
)["Credentials"]
s3 = boto3.client("s3",
aws_access_key_id=c["AccessKeyId"],
aws_secret_access_key=c["SecretAccessKey"],
aws_session_token=c["SessionToken"])// --- TypeScript (AWS SDK v3) ---
import { STSClient, AssumeRoleCommand } from "@aws-sdk/client-sts";
import { S3Client } from "@aws-sdk/client-s3";
const sts = new STSClient({});
const { Credentials: c } = await sts.send(new AssumeRoleCommand({
RoleArn: "arn:aws:iam::444455556666:role/cross-account-read",
RoleSessionName: "reporting",
}));
const s3 = new S3Client({ credentials: {
accessKeyId: c!.AccessKeyId!, secretAccessKey: c!.SecretAccessKey!,
sessionToken: c!.SessionToken! } });The target role's trust policy must name your calling principal or account, or the assume fails with AccessDenied.
AssumeRolePolicyDocument to the exact principal (service, account, or role ARN) that will assume it.| Alternative | Use When | Don't Use When |
|---|---|---|
| Attached compute role (profile/task/exec) | Code runs on AWS EC2/ECS/Lambda/EKS | Running entirely outside AWS |
sts:AssumeRole | Cross-account or off-compute access | The correct role is already attached |
| IAM Identity Center (SSO) roles | Human/CLI federated access | Machine workloads on AWS compute |
| IAM user access keys | No role path exists at all | Any role-based option is available |
A role has permissions but no permanent credentials. It is assumed to produce temporary credentials, whereas a user has long-lived access keys attached directly.
The attached instance profile exposes the role's temporary credentials at the Instance Metadata Service. The SDK's default provider chain reads them automatically and refreshes before expiry.
AssumeRolePolicyDocument) that says who may assume the role.A container that lets an EC2 instance carry an IAM role. EC2 cannot attach a role directly, so it attaches the instance profile, which wraps the role. They usually share a name.
ECS uses a task role, and the SDK reads credentials from the container credentials endpoint rather than the instance metadata service. The programming model is otherwise identical - no keys in code.
The role Lambda assumes to run your function. Its credentials are injected into the function environment, and the SDK picks them up automatically. Grant it only the actions the function calls.
IAM Roles for Service Accounts. A Kubernetes service account is associated with an IAM role, and pods exchange a projected web-identity token with STS to receive scoped, temporary credentials.
When the code is not running on the compute that carries the target role, or when you cross into another account. Otherwise the attached role covers you and no explicit assume is needed.
The target role's trust policy must list your calling principal or account. If it does not, STS refuses the assume regardless of your permissions.
Yes - that is the point. They are temporary and rotate automatically. On AWS compute the SDK refreshes them for you before they expire.
It can, but it is discouraged. Sharing a broad role widens the blast radius of any single compromise. Prefer one least-privilege role per workload.
For code running on AWS, effectively yes: there is no stored secret and credentials rotate on their own. Access keys are a fallback only when no role path exists.
Stack versions: This page was written for boto3 1.43.x (Python 3.10+) and the AWS SDK for JavaScript v3 (Node.js 18+).
Reviewed by Chris St. John·Last updated Jul 23, 2026