IAM Advanced Basics
8 examples to get you started with IAM Advanced - 5 basic and 3 intermediate.
Busque em todas as páginas da documentação
8 examples to get you started with IAM Advanced - 5 basic and 3 intermediate.
pip install boto3 (1.43.x, Python 3.10+) or Node.js npm install @aws-sdk/client-iam (AWS SDK v3, Node 18+).iam:PutRolePolicy, iam:AttachRolePolicy, and iam:SimulatePrincipalPolicy.Inline policies live and die with the one role they're attached to.
# --- Python (boto3) ---
import json, boto3
iam = boto3.client("iam")
doc = {"Version": "2012-10-17", "Statement": [
{"Effect": "Allow", "Action": ["dynamodb:GetItem"],
"Resource": "arn:aws:dynamodb:us-east-1:111122223333:table/Orders"}]}
iam.put_role_policy(
RoleName="orders-reader-role",
PolicyName="orders-read-inline",
PolicyDocument=json.dumps(doc),
)// --- TypeScript (AWS SDK v3) ---
import { IAMClient, PutRolePolicyCommand } from "@aws-sdk/client-iam";
const iam = new IAMClient({});
const doc = { Version: "2012-10-17", Statement: [
{ Effect: "Allow", Action: ["dynamodb:GetItem"],
Resource: "arn:aws:dynamodb:us-east-1:111122223333:table/Orders" }] };
await iam.send(new PutRolePolicyCommand({
RoleName: "orders-reader-role",
PolicyName: "orders-read-inline",
PolicyDocument: JSON.stringify(doc),
}));PutRolePolicy creates or overwrites an inline policy in one call - there's no separate update API.Managed policies are standalone objects you can attach to many roles.
# --- Python (boto3) ---
import json, boto3
iam = boto3.client("iam")
doc = {"Version": "2012-10-17", "Statement": [
{"Effect": "Allow", "Action": ["dynamodb:GetItem"],
"Resource": "arn:aws:dynamodb:us-east-1:111122223333:table/Orders"}]}
policy = iam.create_policy(PolicyName="OrdersReadManaged",
PolicyDocument=json.dumps(doc))
iam.attach_role_policy(RoleName="orders-reader-role",
PolicyArn=policy["Policy"]["Arn"])// --- TypeScript (AWS SDK v3) ---
import {
IAMClient, CreatePolicyCommand, AttachRolePolicyCommand,
} from "@aws-sdk/client-iam";
const iam = new IAMClient({});
const doc = { Version: "2012-10-17", Statement: [
{ Effect: "Allow", Action: ["dynamodb:GetItem"],
Resource: "arn:aws:dynamodb:us-east-1:111122223333:table/Orders" }] };
const policy = await iam.send(new CreatePolicyCommand({
PolicyName: "OrdersReadManaged", PolicyDocument: JSON.stringify(doc),
}));
await iam.send(new AttachRolePolicyCommand({
RoleName: "orders-reader-role", PolicyArn: policy.Policy?.Arn,
}));CreatePolicy produces a versioned, standalone policy object with its own ARN.AttachRolePolicy links that existing object to a role - the same policy can attach to many roles or users.Read back which managed policies are on a role before you change anything.
# --- Python (boto3) ---
import boto3
iam = boto3.client("iam")
res = iam.list_attached_role_policies(RoleName="orders-reader-role")
for p in res["AttachedPolicies"]:
print(p["PolicyName"], p["PolicyArn"])// --- TypeScript (AWS SDK v3) ---
import { IAMClient, ListAttachedRolePoliciesCommand } from "@aws-sdk/client-iam";
const iam = new IAMClient({});
const res = await iam.send(new ListAttachedRolePoliciesCommand({
RoleName: "orders-reader-role",
}));
res.AttachedPolicies?.forEach((p) => console.log(p.PolicyName, p.PolicyArn));ListAttachedRolePolicies only returns managed policies - inline policies never show up here.Inline policies need a two-step read: list the names, then fetch each document.
# --- Python (boto3) ---
import boto3
iam = boto3.client("iam")
names = iam.list_role_policies(RoleName="orders-reader-role")["PolicyNames"]
for name in names:
doc = iam.get_role_policy(RoleName="orders-reader-role", PolicyName=name)
print(name, doc["PolicyDocument"])// --- TypeScript (AWS SDK v3) ---
import {
IAMClient, ListRolePoliciesCommand, GetRolePolicyCommand,
} from "@aws-sdk/client-iam";
const iam = new IAMClient({});
const { PolicyNames } = await iam.send(new ListRolePoliciesCommand({
RoleName: "orders-reader-role",
}));
for (const name of PolicyNames ?? []) {
const doc = await iam.send(new GetRolePolicyCommand({
RoleName: "orders-reader-role", PolicyName: name,
}));
console.log(name, doc.PolicyDocument);
}ListRolePolicies returns only names - you need GetRolePolicy per name to see the actual JSON.PolicyDocument is URL-encoded JSON in some SDK responses; decode before parsing if needed.Don't guess - ask IAM directly whether an action would be allowed.
# --- Python (boto3) ---
import boto3
iam = boto3.client("iam")
res = iam.simulate_principal_policy(
PolicySourceArn="arn:aws:iam::111122223333:role/orders-reader-role",
ActionNames=["dynamodb:GetItem"],
ResourceArns=["arn:aws:dynamodb:us-east-1:111122223333:table/Orders"],
)
print(res["EvaluationResults"][0]["EvalDecision"]) # -> allowed// --- TypeScript (AWS SDK v3) ---
import { IAMClient, SimulatePrincipalPolicyCommand } from "@aws-sdk/client-iam";
const iam = new IAMClient({});
const res = await iam.send(new SimulatePrincipalPolicyCommand({
PolicySourceArn: "arn:aws:iam::111122223333:role/orders-reader-role",
ActionNames: ["dynamodb:GetItem"],
ResourceArns: ["arn:aws:dynamodb:us-east-1:111122223333:table/Orders"],
}));
console.log(res.EvaluationResults?.[0]?.EvalDecision); // -> allowedEvalDecision returns allowed, explicitDeny, or implicitDeny - three distinct outcomes, not a boolean.ResourcePolicy explicitly.Related: IAM's Policy Evaluation Logic - what these three decisions actually mean.
Prove the precedence rule with a real simulation instead of trusting memory.
# --- Python (boto3) ---
import json, boto3
iam = boto3.client("iam")
deny_doc = {"Version": "2012-10-17", "Statement": [
{"Effect": "Deny", "Action": "dynamodb:GetItem",
"Resource": "arn:aws:dynamodb:us-east-1:111122223333:table/Orders"}]}
iam.put_role_policy(RoleName="orders-reader-role",
PolicyName="explicit-deny-test",
PolicyDocument=json.dumps(deny_doc))
res = iam.simulate_principal_policy(
PolicySourceArn="arn:aws:iam::111122223333:role/orders-reader-role",
ActionNames=["dynamodb:GetItem"],
ResourceArns=["arn:aws:dynamodb:us-east-1:111122223333:table/Orders"])
print(res["EvaluationResults"][0]["EvalDecision"]) # -> explicitDeny// --- TypeScript (AWS SDK v3) ---
import {
IAMClient, PutRolePolicyCommand, SimulatePrincipalPolicyCommand,
} from "@aws-sdk/client-iam";
const iam = new IAMClient({});
const denyDoc = { Version: "2012-10-17", Statement: [
{ Effect: "Deny", Action: "dynamodb:GetItem",
Resource: "arn:aws:dynamodb:us-east-1:111122223333:table/Orders" }] };
await iam.send(new PutRolePolicyCommand({
RoleName: "orders-reader-role", PolicyName: "explicit-deny-test",
PolicyDocument: JSON.stringify(denyDoc),
}));
const res = await iam.send(new SimulatePrincipalPolicyCommand({
PolicySourceArn: "arn:aws:iam::111122223333:role/orders-reader-role",
ActionNames: ["dynamodb:GetItem"],
ResourceArns: ["arn:aws:dynamodb:us-east-1:111122223333:table/Orders"],
}));
console.log(res.EvaluationResults?.[0]?.EvalDecision); // -> explicitDenyOrdersReadManaged Allow attached from example 2 - the new Deny wins anyway.explicitDeny is a distinct decision from implicitDeny - the simulator tells you which one fired.DeleteRolePolicy) once you're done testing so the role goes back to allowing the read.A role with both an inline and a managed policy still evaluates as one combined set.
# --- Python (boto3) ---
import boto3
iam = boto3.client("iam")
attached = iam.list_attached_role_policies(RoleName="orders-reader-role")
inline = iam.list_role_policies(RoleName="orders-reader-role")
print("managed:", [p["PolicyName"] for p in attached["AttachedPolicies"]])
print("inline:", inline["PolicyNames"])
res = iam.simulate_principal_policy(
PolicySourceArn="arn:aws:iam::111122223333:role/orders-reader-role",
ActionNames=["dynamodb:GetItem", "dynamodb:PutItem"],
ResourceArns=["arn:aws:dynamodb:us-east-1:111122223333:table/Orders"])
for r in res["EvaluationResults"]:
print(r["EvalActionName"], r["EvalDecision"])// --- TypeScript (AWS SDK v3) ---
import {
IAMClient, ListAttachedRolePoliciesCommand, ListRolePoliciesCommand,
SimulatePrincipalPolicyCommand,
} from "@aws-sdk/client-iam";
const iam = new IAMClient({});
const attached = await iam.send(new ListAttachedRolePoliciesCommand({
RoleName: "orders-reader-role" }));
const inline = await iam.send(new ListRolePoliciesCommand({
RoleName: "orders-reader-role" }));
console.log("managed:", attached.AttachedPolicies?.map((p) => p.PolicyName));
console.log("inline:", inline.PolicyNames);
const res = await iam.send(new SimulatePrincipalPolicyCommand({
PolicySourceArn: "arn:aws:iam::111122223333:role/orders-reader-role",
ActionNames: ["dynamodb:GetItem", "dynamodb:PutItem"],
ResourceArns: ["arn:aws:dynamodb:us-east-1:111122223333:table/Orders"],
}));
res.EvaluationResults?.forEach((r) => console.log(r.EvalActionName, r.EvalDecision));GetItem still shows the effect of any Deny left over from example 6; PutItem falls to implicit Deny since nothing grants it.ActionNames in one call is cheaper than simulating one action at a time.PutRolePolicy overwrites the named inline policy - there is no separate "update" call.
# --- Python (boto3) ---
import json, boto3
iam = boto3.client("iam")
widened = {"Version": "2012-10-17", "Statement": [
{"Effect": "Allow", "Action": ["dynamodb:GetItem", "dynamodb:Query"],
"Resource": "arn:aws:dynamodb:us-east-1:111122223333:table/Orders"}]}
# Same RoleName + PolicyName as example 1: this replaces that document entirely.
iam.put_role_policy(RoleName="orders-reader-role",
PolicyName="orders-read-inline",
PolicyDocument=json.dumps(widened))// --- TypeScript (AWS SDK v3) ---
import { IAMClient, PutRolePolicyCommand } from "@aws-sdk/client-iam";
const iam = new IAMClient({});
const widened = { Version: "2012-10-17", Statement: [
{ Effect: "Allow", Action: ["dynamodb:GetItem", "dynamodb:Query"],
Resource: "arn:aws:dynamodb:us-east-1:111122223333:table/Orders" }] };
// Same RoleName + PolicyName as example 1: this replaces that document entirely.
await iam.send(new PutRolePolicyCommand({
RoleName: "orders-reader-role", PolicyName: "orders-read-inline",
PolicyDocument: JSON.stringify(widened),
}));RoleName + PolicyName pair overwrites the whole document - there's no merge or patch semantics.Related: Policy Document Structure & Conditions - shaping the JSON these calls send.
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 atualização: 25 de jul. de 2026