skip to content

How would you use the IAM policy simulator (the aws iam simulate-principal-policy API) to diagnose an AccessDenied, and what can it not tell you?

level: seniorimportance: nice to knowfreq 38%

answer

  1. reasoning, not a real request
  2. exact action, exact resource ARN
  3. three decisions, three different fixes
  4. matched statement is the payoff
  5. allowed is a hypothesis, not proof

basics

~20 s

Point simulate-principal-policy at the caller's ARN with the exact action, resource ARN and any request conditions; the result reports allowed, implicitDeny or explicitDeny plus the statements that matched. It reasons over the policies you give it, not over the live request, so treat an allow as a hypothesis.

solid answer

~50 s

Run `aws iam simulate-principal-policy` with `--policy-source-arn` set to the calling role or user, `--action-names` set to the exact action such as `s3:GetObject`, and `--resource-arns` set to the precise ARN — object, not bucket. Supply request context with `--context-entries` for any condition key the policy tests, and pass `--resource-policy` and `--permissions-boundary-policy-input-list` when those documents are part of the picture. Each result carries an `EvalDecision` of `allowed`, `implicitDeny` or `explicitDeny` plus `MatchedStatements`, which is the real payoff: it tells you *which statement* decided, so you know whether to add a grant or hunt a Deny. Its blind spot is that it never makes the call. It evaluates the documents you point it at, with the context you supply — so service-specific authorization, real request-time condition values and any policy you did not pass are invisible. Use it to form a hypothesis, then confirm against the live API and its denial message.

code

bash · 5 lines
bash
aws iam simulate-principal-policy \
  --policy-source-arn arn:aws:iam::111122223333:role/report-reader \
  --action-names s3:GetObject \
  --resource-arns arn:aws:s3:::account-b-reports/2026/q1.csv \
  --context-entries ContextKeyName=aws:MultiFactorAuthPresent,ContextKeyType=boolean,ContextKeyValues=true

go deeper

for a junior

Know that AWS offers a way to test whether a principal would be allowed an action without performing it, and that the exact action name and full resource ARN are what you feed it.

for a middle

Be able to run it: policy source ARN, action names, resource ARNs, plus context entries for condition keys — and interpret allowed, implicitDeny and explicitDeny as three different remedies.

for a senior

Position it correctly in an investigation. Start from the denial message, use the simulator to locate the deciding statement, and state plainly what it cannot see so nobody mistakes a simulated allow for a working call.

for a principal

Push it left: make authorization expectations executable assertions in CI so policy changes are reviewed against intent, and decide who owns those assertions when permissions span teams and accounts.

## What the simulator is for The IAM policy simulator answers "would this principal be allowed to do this?" without performing the action. That matters for two situations: before a change, when you want to know whether tightening a policy will break someone, and after a denial, when you want to know which document said no. It is a reasoning engine over policy JSON, not a proxy for the request. ## Running it ```bash aws iam simulate-principal-policy \ --policy-source-arn arn:aws:iam::111122223333:role/report-reader \ --action-names s3:GetObject \ --resource-arns arn:aws:s3:::account-b-reports/2026/q1.csv ``` The companion API, `simulate-custom-policy`, takes policy documents inline via `--policy-input-list` and is the one to use for a policy you have written but not yet attached — the review-before-merge case. Three arguments carry most of the value: - `--context-entries` supplies request context such as `aws:SourceIp`, `aws:PrincipalTag/team` or `aws:MultiFactorAuthPresent`. If a policy's `Condition` tests a key you do not supply, the simulation is not modelling your real request. - `--resource-policy` supplies the bucket, queue or key policy so the simulator can evaluate the resource side too. Without it, a cross-account scenario is only half-modelled. - `--permissions-boundary-policy-input-list` lets you include a boundary in the calculation. ## Reading the output Each entry has an `EvalDecision`, and the three values map directly onto the remedy: - `allowed` — a grant matched and nothing blocked it. - `implicitDeny` — nothing granted the action. Add or widen a policy. - `explicitDeny` — a Deny statement matched. Adding permissions is pointless; find and change that statement. `MatchedStatements` names the policy and statement that produced the decision, which turns "it does not work" into "this Sid in this policy did it". `MissingContextValues` lists condition keys the policy referenced that you did not supply — a strong hint that your simulation is incomplete, and a common explanation for a simulator result that disagrees with reality. ## What it cannot tell you The simulator never issues the API call, so anything decided outside of the policy documents you supplied is invisible: - **Service-specific authorization.** Several services layer checks on top of IAM — S3 Block Public Access, S3 Object Ownership and ACL behaviour, and KMS's requirement that the key policy itself permit access. A simulated `allowed` can still fail live. - **Anything you did not pass in.** Resource policies, boundaries and request context are inputs. Omit one and you have simulated a different request. - **Real request-time values.** Source IP, VPC endpoint ID, MFA state and resource tags are whatever you typed, not what will actually be true. - **The rest of the chain.** A `PutObject` may succeed while the KMS key denies `kms:GenerateDataKey`, or a Lambda's role may be fine while the function's own resource policy rejects the invoker. So simulated `allowed` is necessary, not sufficient. Simulated `explicitDeny`, by contrast, is very high-signal: something you supplied really does deny it. ## Where it fits in a real workflow Start with the live error. AWS denial messages now usually state the reason and the policy type — that no identity-based policy allows the action, or that an explicit deny in a service control policy blocked it — and that single line often ends the investigation before you open a tool. Use the simulator next, to test the hypothesis and to find the exact statement. Then verify with the real call in a safe context. The higher-leverage use is preventive: run `simulate-custom-policy` in CI against a set of assertions — this role must be able to write to that bucket, and must not be able to delete it — so that a policy change that breaks a service, or quietly widens it, fails review instead of production. That converts the simulator from a debugging tool into a regression test for authorization. ## Interview framing Say what it is (offline reasoning over policy documents), what its output gives you (a decision plus the matched statement), what it cannot see (anything outside the documents and context you supply), and where you would actually put it (hypothesis testing during an incident, and permission assertions in CI).

  • When would you use simulate-custom-policy instead of simulate-principal-policy?
    When the policy is not attached yet. simulate-custom-policy takes documents inline via --policy-input-list, so you can evaluate a proposed change in review, or run a suite of assertions in CI, without attaching anything to a live principal. simulate-principal-policy is the right call when you want the effective permissions a real user or role has today.
  • Why can the simulator say allowed while the real call returns AccessDenied?
    Because it evaluated a different request. Common causes are an unsupplied resource policy, condition keys whose real values differ from the ones you passed, and service-side checks outside IAM such as an S3 Block Public Access setting or a KMS key policy that does not permit the caller. Check MissingContextValues first.
  • How would you use the simulator preventively rather than reactively?
    Encode authorization expectations as assertions and run them in CI on every policy change: this deploy role must be able to write the artifact bucket, and must not be able to delete it or read the audit bucket. A failing assertion blocks the merge, which catches both breakage and silent over-permissioning before either reaches production.

saying these in an interview costs you the question

  • Treats a simulated allow as proof the call will succeed
  • Simulates against the bucket ARN instead of the object ARN
  • Ignores MissingContextValues when conditions are involved
  • Forgets to pass the resource policy for cross-account tests
  • Never reads the actual denial message before opening a tool

context