IAM Basics for SDK Auth
8 examples to get you started with IAM auth for the AWS SDKs - 5 basic and 3 intermediate.
Search across all documentation pages
8 examples to get you started with IAM auth for the AWS SDKs - 5 basic and 3 intermediate.
pip install boto3 (1.43.x, Python 3.10+) or Node.js npm install @aws-sdk/client-sts @aws-sdk/client-iam @aws-sdk/client-s3 (AWS SDK v3, Node 18+).~/.aws/credentials, or an attached role.Before debugging any permission issue, find out who you are.
# --- Python (boto3) ---
import boto3
sts = boto3.client("sts")
me = sts.get_caller_identity()
print(me["Account"], me["Arn"])// --- TypeScript (AWS SDK v3) ---
import { STSClient, GetCallerIdentityCommand } from "@aws-sdk/client-sts";
const sts = new STSClient({});
const me = await sts.send(new GetCallerIdentityCommand({}));
console.log(me.Account, me.Arn);GetCallerIdentity needs no permissions - it always works for a valid principal.Arn shows whether you are a user, an assumed role, or a federated identity.AccessDenied to confirm the wrong identity is not in play.Related: IAM's Role in Every SDK Call - what happens behind each request.
Never hardcode keys - both SDKs walk the default provider chain for you.
# --- Python (boto3) ---
import boto3
# No keys passed: boto3 checks env vars, ~/.aws files, then metadata.
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";
// Empty config triggers the default credential provider chain.
const s3 = new S3Client({});
const out = await s3.send(new ListBucketsCommand({}));
console.log(out.Buckets?.map((b) => b.Name));Scope a first identity to one service with a policy document.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ReadOneBucket",
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:ListBucket"],
"Resource": [
"arn:aws:s3:::my-app-bucket",
"arn:aws:s3:::my-app-bucket/*"
]
}
]
}Version must be the literal "2012-10-17" - it is a policy-language version, not a date you choose.s3:ListBucket targets the bucket ARN; s3:GetObject targets the object ARN with /*.Related: Writing a Least-Privilege Policy for a New SDK Integration - scope actions to your exact calls.
Turn that JSON into a managed policy programmatically.
# --- Python (boto3) ---
import json, boto3
iam = boto3.client("iam")
doc = {"Version": "2012-10-17", "Statement": [
{"Effect": "Allow", "Action": ["s3:GetObject"],
"Resource": "arn:aws:s3:::my-app-bucket/*"}]}
res = iam.create_policy(PolicyName="AppReadObjects",
PolicyDocument=json.dumps(doc))
print(res["Policy"]["Arn"])// --- TypeScript (AWS SDK v3) ---
import { IAMClient, CreatePolicyCommand } from "@aws-sdk/client-iam";
const iam = new IAMClient({});
const doc = { Version: "2012-10-17", Statement: [
{ Effect: "Allow", Action: ["s3:GetObject"],
Resource: "arn:aws:s3:::my-app-bucket/*" }] };
const res = await iam.send(new CreatePolicyCommand({
PolicyName: "AppReadObjects",
PolicyDocument: JSON.stringify(doc),
}));
console.log(res.Policy?.Arn);PolicyDocument is always a JSON string, so serialize the object first.Grant permissions by attaching a managed policy to a principal.
# --- Python (boto3) ---
import boto3
iam = boto3.client("iam")
iam.attach_role_policy(
RoleName="app-runtime-role",
PolicyArn="arn:aws:iam::111122223333:policy/AppReadObjects",
)// --- TypeScript (AWS SDK v3) ---
import { IAMClient, AttachRolePolicyCommand } from "@aws-sdk/client-iam";
const iam = new IAMClient({});
await iam.send(new AttachRolePolicyCommand({
RoleName: "app-runtime-role",
PolicyArn: "arn:aws:iam::111122223333:policy/AppReadObjects",
}));AttachRolePolicy links an existing managed policy to an existing role.AttachUserPolicy for a user, but prefer roles for application code.DetachRolePolicy - deleting the policy while attached fails.Related: IAM Roles for Applications & Services - where these roles get used.
Swap long-lived keys for short-lived ones via STS.
# --- Python (boto3) ---
import boto3
sts = boto3.client("sts")
creds = sts.assume_role(
RoleArn="arn:aws:iam::111122223333:role/app-runtime-role",
RoleSessionName="basics-demo",
)["Credentials"]
s3 = boto3.client(
"s3",
aws_access_key_id=creds["AccessKeyId"],
aws_secret_access_key=creds["SecretAccessKey"],
aws_session_token=creds["SessionToken"],
)
print([b["Name"] for b in s3.list_buckets()["Buckets"]])// --- TypeScript (AWS SDK v3) ---
import { STSClient, AssumeRoleCommand } from "@aws-sdk/client-sts";
import { S3Client, ListBucketsCommand } from "@aws-sdk/client-s3";
const sts = new STSClient({});
const { Credentials: c } = await sts.send(new AssumeRoleCommand({
RoleArn: "arn:aws:iam::111122223333:role/app-runtime-role",
RoleSessionName: "basics-demo",
}));
const s3 = new S3Client({ credentials: {
accessKeyId: c!.AccessKeyId!,
secretAccessKey: c!.SecretAccessKey!,
sessionToken: c!.SessionToken!,
}});
console.log((await s3.send(new ListBucketsCommand({}))).Buckets?.map((b) => b.Name));assume_role returns a temporary access key, secret, and session token - all three are required.A role only assumable by the principal you name.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "Service": "lambda.amazonaws.com" },
"Action": "sts:AssumeRole"
}
]
}# --- Python (boto3) ---
import json, boto3
trust = {"Version": "2012-10-17", "Statement": [
{"Effect": "Allow",
"Principal": {"Service": "lambda.amazonaws.com"},
"Action": "sts:AssumeRole"}]}
boto3.client("iam").create_role(
RoleName="lambda-exec-role",
AssumeRolePolicyDocument=json.dumps(trust),
)// --- TypeScript (AWS SDK v3) ---
import { IAMClient, CreateRoleCommand } from "@aws-sdk/client-iam";
const trust = { Version: "2012-10-17", Statement: [
{ Effect: "Allow",
Principal: { Service: "lambda.amazonaws.com" },
Action: "sts:AssumeRole" }] };
await new IAMClient({}).send(new CreateRoleCommand({
RoleName: "lambda-exec-role",
AssumeRolePolicyDocument: JSON.stringify(trust),
}));Related: IAM Roles for Applications & Services - service roles in depth.
Distinguish an authorization failure from other errors.
# --- Python (boto3) ---
import boto3
from botocore.exceptions import ClientError
s3 = boto3.client("s3")
try:
s3.get_object(Bucket="my-app-bucket", Key="secret.txt")
except ClientError as e:
if e.response["Error"]["Code"] == "AccessDenied":
print("IAM denied this action - fix the policy, do not retry.")
else:
raise// --- TypeScript (AWS SDK v3) ---
import { S3Client, GetObjectCommand } from "@aws-sdk/client-s3";
const s3 = new S3Client({});
try {
await s3.send(new GetObjectCommand({ Bucket: "my-app-bucket", Key: "secret.txt" }));
} catch (e: any) {
if (e.name === "AccessDenied") {
console.log("IAM denied this action - fix the policy, do not retry.");
} else {
throw e;
}
}AccessDenied is a permissions verdict, not a transient error - retrying will not help.Code in boto3, name in SDK v3), not the message text.Related: IAM Policy Simulator & Access Analyzer for SDK Debugging - diagnose denials before prod.
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