IAM Policy Simulator & Access Analyzer for SDK Debugging
AccessDenied is the most common IAM error, and guessing at the cause wastes hours.
Busca en todas las páginas de la documentación
AccessDenied is the most common IAM error, and guessing at the cause wastes hours.
The Policy Simulator, CloudTrail, and Access Analyzer let you see exactly which action, resource, or condition failed - before the code ever runs in production.
The Policy Simulator (SimulatePrincipalPolicy) evaluates whether a principal is allowed to perform an action on a resource, without actually making the call.
It returns a decision - allowed, explicitDeny, or implicitDeny - plus the specific statements that matched.
CloudTrail records every real API call, including the exact action and resource behind an AccessDenied, which is often the missing clue.
IAM Access Analyzer works the other direction, flagging policies that grant broader or external access than intended.
Together they turn permission debugging from trial-and-error into a repeatable, side-effect-free process.
Quick-reference recipe card - copy-paste ready.
# --- Python (boto3) ---
import boto3
iam = boto3.client("iam")
res = iam.simulate_principal_policy(
PolicySourceArn="arn:aws:iam::111122223333:role/uploads-worker-role",
ActionNames=["s3:PutObject"],
ResourceArns=["arn:aws:s3:::acme-uploads/tenant-7/file.csv"])
r = res["EvaluationResults"][0]
print(r["EvalActionName"], "->", r["EvalDecision"])// --- 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/uploads-worker-role",
ActionNames: ["s3:PutObject"],
ResourceArns: ["arn:aws:s3:::acme-uploads/tenant-7/file.csv"],
}));
const r = res.EvaluationResults![0];
console.log(r.EvalActionName, "->", r.EvalDecision);When to reach for this:
AccessDenied you cannot immediately explain.Simulate several actions at once, and for each denial print the action so you know exactly what to add to the policy.
# --- Python (boto3) ---
import boto3
iam = boto3.client("iam")
ROLE = "arn:aws:iam::111122223333:role/uploads-worker-role"
res = iam.simulate_principal_policy(
PolicySourceArn=ROLE,
ActionNames=["s3:GetObject", "s3:PutObject", "s3:DeleteObject"],
ResourceArns=["arn:aws:s3:::acme-uploads/tenant-7/file.csv"])
for r in res["EvaluationResults"]:
decision = r["EvalDecision"]
if decision != "allowed":
print(f"BLOCKED: {r['EvalActionName']} ({decision})")
else:
print(f"OK: {r['EvalActionName']}")// --- TypeScript (AWS SDK v3) ---
import {
IAMClient, SimulatePrincipalPolicyCommand,
} from "@aws-sdk/client-iam";
const iam = new IAMClient({});
const ROLE = "arn:aws:iam::111122223333:role/uploads-worker-role";
const res = await iam.send(new SimulatePrincipalPolicyCommand({
PolicySourceArn: ROLE,
ActionNames: ["s3:GetObject", "s3:PutObject", "s3:DeleteObject"],
ResourceArns: ["arn:aws:s3:::acme-uploads/tenant-7/file.csv"],
}));
for (const r of res.EvaluationResults ?? []) {
if (r.EvalDecision !== "allowed") {
console.log(`BLOCKED: ${r.EvalActionName} (${r.EvalDecision})`);
} else {
console.log(`OK: ${r.EvalActionName}`);
}
}What this demonstrates:
EvalDecision per action tells you precisely which permission is missing.implicitDeny means no statement allowed it; explicitDeny means a statement blocked it.MatchedStatements, pointing at the exact statement that produced the decision.ContextEntries to test condition keys like aws:SourceIp or a resource tag.s3:DeleteObject.errorCode: AccessDenied and the precise eventName.resources, the userIdentity, and often the missing action in the message.# --- Python (boto3) ---
import boto3
ct = boto3.client("cloudtrail")
events = ct.lookup_events(LookupAttributes=[
{"AttributeKey": "EventName", "AttributeValue": "PutObject"}],
MaxResults=5)
for e in events["Events"]:
print(e["EventName"], e.get("Username"), e["EventTime"])// --- TypeScript (AWS SDK v3) ---
import {
CloudTrailClient, LookupEventsCommand,
} from "@aws-sdk/client-cloudtrail";
const ct = new CloudTrailClient({});
const out = await ct.send(new LookupEventsCommand({
LookupAttributes: [{ AttributeKey: "EventName", AttributeValue: "PutObject" }],
MaxResults: 5,
}));
out.Events?.forEach((e) => console.log(e.EventName, e.Username, e.EventTime));| EvalDecision | Meaning | Typical fix |
|---|---|---|
allowed | A statement permits the action | Nothing - it works |
implicitDeny | No statement allows it | Add an Allow for the action/resource |
explicitDeny | A statement explicitly denies it | Remove or narrow the Deny |
tenant-7/* allows one path but not another. Fix: simulate the exact ARN your code targets, not a generic one.ResourcePolicy for S3 buckets, SQS queues, and KMS keys.Condition may deny unless the right context is present. Fix: supply ContextEntries (source IP, tags, MFA) to reproduce real conditions.eventName and resources.implicitDeny means add an allow; explicitDeny means find and remove the blocking statement.| Alternative | Use When | Don't Use When |
|---|---|---|
SimulatePrincipalPolicy | Testing an existing principal's permissions | You need to see a real historical denial |
SimulateCustomPolicy | Testing a draft policy not yet attached | The principal already has the policy |
| CloudTrail lookup | Diagnosing a denial that already happened | The action never ran |
| Access Analyzer | Auditing for external/broad access | Debugging a single specific denial |
It evaluates whether a principal is allowed to perform an action on a resource, using the same logic as a live request, but without making the call. You get a decision and the matched statements.
No. The simulator only evaluates policies. It is completely safe to simulate destructive actions like s3:DeleteObject because nothing executes.
implicitDeny: no statement allowed the action, so it defaults to denied - fix by adding an allow.explicitDeny: a statement explicitly denied it - fix by removing or narrowing that deny.Look it up in CloudTrail. The event carries the precise eventName, the resources, and the identity, which often reveals that the real action differs from what you assumed your SDK called.
Yes. Pass multiple values in ActionNames, and the response returns one evaluation result per action, so you can check an entire workflow at once.
Use SimulateCustomPolicy, which takes the raw policy JSON as input instead of a principal ARN. That lets you validate a draft before attaching it.
The simulator may be missing context the real request has - a resource-based policy, a condition key, or an organization SCP. Add the resource policy and condition entries, and confirm SCPs with your org admin.
Provide ContextEntries with the relevant condition key and value (for example aws:SourceIp). The simulator then evaluates the condition as if the request carried that context.
Access Analyzer scans policies for access that reaches outside your account or is broader than intended. It is a proactive audit tool, whereas the simulator answers a specific allow/deny question.
For services like S3, SQS, and KMS, both the identity policy and the resource policy are evaluated. Pass the resource policy to the simulator so its result matches real behavior.
No need. The simulator and CloudTrail let you diagnose denials without running risky calls against production data, so you can fix the policy before deploying.
Look up the CloudTrail event for the exact action and resource, then simulate that same action and ARN against the principal. The decision and matched statements tell you precisely what to change.
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: 23 jul 2026