AWS IAM via SDK: Users, Roles & Policies
IAM is the identity and permission layer that authorizes every other AWS call.
Busca en todas las páginas de la documentación
IAM is the identity and permission layer that authorizes every other AWS call.
This page builds the core pieces from code: a scoped, least-privilege policy, and a role that trusts a service and carries that policy. It then verifies the wiring and cleans up.
Roles are the identity applications should use, so this quickstart focuses on a role rather than a user with static keys.
Quick-reference recipe card - copy-paste ready.
# --- Python (boto3) ---
import json, boto3
iam = boto3.client("iam")
# A role that the Lambda service can assume.
trust = {"Version": "2012-10-17", "Statement": [{
"Effect": "Allow",
"Principal": {"Service": "lambda.amazonaws.com"},
"Action": "sts:AssumeRole"}]}
role = iam.create_role(
RoleName="report-reader",
AssumeRolePolicyDocument=json.dumps(trust),
)["Role"]
print("role arn:", role["Arn"])// --- TypeScript (AWS SDK v3) ---
import { IAMClient, CreateRoleCommand } from "@aws-sdk/client-iam";
const iam = new IAMClient({});
// A role that the Lambda service can assume.
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-reader",
AssumeRolePolicyDocument: JSON.stringify(trust),
}));
console.log("role arn:", Role?.Arn);When to reach for this:
Create a scoped customer-managed policy, create a role that a service can assume, attach the policy, confirm the attachment, then clean up in the correct order.
# --- Python (boto3) ---
import json, boto3
iam = boto3.client("iam")
# 1. A least-privilege policy: read one bucket, nothing else.
perm = {"Version": "2012-10-17", "Statement": [{
"Effect": "Allow",
"Action": ["s3:GetObject"],
"Resource": "arn:aws:s3:::reports-bucket/*"}]}
policy = iam.create_policy(
PolicyName="read-reports-bucket",
PolicyDocument=json.dumps(perm),
)["Policy"]
# 2. A role the Lambda service may assume.
trust = {"Version": "2012-10-17", "Statement": [{
"Effect": "Allow",
"Principal": {"Service": "lambda.amazonaws.com"},
"Action": "sts:AssumeRole"}]}
iam.create_role(RoleName="report-reader",
AssumeRolePolicyDocument=json.dumps(trust))
# 3. Attach the policy and confirm.
iam.attach_role_policy(RoleName="report-reader",
PolicyArn=policy["Arn"])
attached = iam.list_attached_role_policies(RoleName="report-reader")
print([p["PolicyName"] for p in attached["AttachedPolicies"]])
# 4. Clean up in order: detach, then delete policy and role.
iam.detach_role_policy(RoleName="report-reader", PolicyArn=policy["Arn"])
iam.delete_policy(PolicyArn=policy["Arn"])
iam.delete_role(RoleName="report-reader")// --- TypeScript (AWS SDK v3) ---
import {
IAMClient, CreatePolicyCommand, CreateRoleCommand, AttachRolePolicyCommand,
ListAttachedRolePoliciesCommand, DetachRolePolicyCommand, DeletePolicyCommand, DeleteRoleCommand,
} from "@aws-sdk/client-iam";
const iam = new IAMClient({});
// 1. A least-privilege policy: read one bucket, nothing else.
const perm = { Version: "2012-10-17", Statement: [{
Effect: "Allow",
Action: ["s3:GetObject"],
Resource: "arn:aws:s3:::reports-bucket/*" }] };
const { Policy } = await iam.send(new CreatePolicyCommand({
PolicyName: "read-reports-bucket",
PolicyDocument: JSON.stringify(perm),
}));
// 2. A role the Lambda service may assume.
const trust = { Version: "2012-10-17", Statement: [{
Effect: "Allow",
Principal: { Service: "lambda.amazonaws.com" },
Action: "sts:AssumeRole" }] };
await iam.send(new CreateRoleCommand({
RoleName: "report-reader",
AssumeRolePolicyDocument: JSON.stringify(trust),
}));
// 3. Attach the policy and confirm.
await iam.send(new AttachRolePolicyCommand({ RoleName: "report-reader", PolicyArn: Policy!.Arn }));
const attached = await iam.send(new ListAttachedRolePoliciesCommand({ RoleName: "report-reader" }));
console.log((attached.AttachedPolicies ?? []).map((p) => p.PolicyName));
// 4. Clean up in order: detach, then delete policy and role.
await iam.send(new DetachRolePolicyCommand({ RoleName: "report-reader", PolicyArn: Policy!.Arn }));
await iam.send(new DeletePolicyCommand({ PolicyArn: Policy!.Arn }));
await iam.send(new DeleteRoleCommand({ RoleName: "report-reader" }));What this demonstrates:
Effect, Action, and Resource - here scoped to one bucket.AttachRolePolicy binds a reusable customer-managed policy to the role by ARN.Every role answers two separate questions, and they are debugged separately.
AssumeRolePolicyDocument) answers who may assume the role - a service, an account, or a specific principal.A broad permission policy is useless if the trust policy blocks the caller, and vice versa.
Rather than guessing, simulate a policy decision before you rely on it.
# --- Python (boto3) ---
res = iam.simulate_principal_policy(
PolicySourceArn="arn:aws:iam::111122223333:role/report-reader",
ActionNames=["s3:GetObject"],
ResourceArns=["arn:aws:s3:::reports-bucket/report.pdf"],
)
print(res["EvaluationResults"][0]["EvalDecision"]) # allowed / implicitDeny// --- TypeScript (AWS SDK v3) ---
import { SimulatePrincipalPolicyCommand } from "@aws-sdk/client-iam";
const res = await iam.send(new SimulatePrincipalPolicyCommand({
PolicySourceArn: "arn:aws:iam::111122223333:role/report-reader",
ActionNames: ["s3:GetObject"],
ResourceArns: ["arn:aws:s3:::reports-bucket/report.pdf"],
}));
console.log(res.EvaluationResults?.[0].EvalDecision); // allowed / implicitDenyThe simulator answers "would this be allowed?" without performing the action, which is ideal for testing least-privilege policies.
"Action": "*" on "Resource": "*" turns any leak into a full compromise. Fix: scope actions and resource ARNs to exactly what the workload needs.| Alternative | Use When | Don't Use When |
|---|---|---|
| Role + managed policy (this page) | Applications and services needing scoped access | A one-off permission for a single identity |
| Inline policy on a role/user | A policy that should live and die with one identity | You want to reuse the policy across identities |
| AWS-managed policy | A common, AWS-maintained permission set fits | You need tightly scoped, custom permissions |
| IAM user + access keys | No role path exists at all | Any role-based option is available |
| IAM Identity Center (SSO) | Human and CLI federated access | Machine workloads on AWS compute |
A policy is a permission document. A role is an assumable identity with no permanent credentials that carries policies. A user is an identity for a person or legacy app, optionally with long-lived keys.
A trust policy that says who may assume the role, and one or more permission policies that say what the role can do once assumed. They are configured and debugged separately.
Use a customer-managed policy when you want to reuse and version a permission set across identities. Use an inline policy when the permission should live and die with a single identity.
A role cannot be deleted while policies are attached (and a policy cannot be deleted while attached to any identity). Detach first, then delete the policy and the role.
List only the specific actions the workload calls, and restrict Resource to the exact ARNs involved. Avoid "*" for actions or resources unless truly required.
Use SimulatePrincipalPolicy to check whether a principal would be allowed a given action on a given resource, without performing the action itself.
A role. Applications on EC2, ECS, Lambda, or EKS assume a role and receive rotating temporary credentials, so there is no static key to leak. Reserve users for humans or legacy cases.
It names who is allowed to assume the role - a service like lambda.amazonaws.com, an AWS account, or a specific role or user ARN. STS refuses the assume if the caller is not listed.
IAM is eventually consistent. A newly created role or attachment can take a moment to propagate, so a dependent call may need a short retry.
Yes. That is the point of a customer-managed policy: create it once and attach it by ARN to any number of roles or users, updating it in one place.
Stack versions: This page was written for boto3 1.43.x (Python 3.10+) and the AWS SDK for JavaScript v3 (Node.js 18+).
Revisado por Chris St. John·Última actualización: 24 jul 2026