Chris Collins

Tools · cloud

AWS IAM policy evaluator

Paste IAM policies — identity-based, resource-based, permissions boundaries, session policies, SCPs and RCPs — and a request, and see Allow, explicit deny or implicit deny with a trace of every statement and every step of AWS’s evaluation logic; plus a policy linter and an ARN parser.

Added

How to use it

“Why is this AccessDenied?” is usually a question about how the policies combine: a bucket policy in another account, a permissions boundary nobody remembers, an SCP two OUs up, a condition key that isn’t in the request. This page runs AWS’s documented policy evaluation logic over the policies you paste and a request you describe, and shows where the decision was made and why every statement did or didn’t match.

How to use it

Evaluate. Start from an example — explicit deny, permissions boundary, same-account role ARN vs role-session ARN, cross-account, a KMS key policy, a GitHub Actions OIDC trust policy, role trust, SCPs at three levels, an RCP data perimeter, ${aws:username} home folders, session policies, the NotPrincipal pitfall, conditions (IP ranges, MFA, a change window) and the ForAllValues empty-set pitfall — or clear it and paste your own.

  • Policies. Each card is one policy with its kind: identity-based, resource-based (bucket, queue, key or trust policy), permissions boundary, session policy, SCP, RCP or VPC endpoint policy. SCPs and RCPs have a level (Root, an OU, the account): every level must allow, and policies at the same level are OR-ed, as in AWS Organizations. Paste plain JSON or what the AWS CLI prints — get-bucket-policy, get-key-policy, get-role-policy, get-policy-version, get-role, sqs get-queue-attributes, organizations describe-policy, or a URL-encoded document from the IAM API — and Format unwraps it. Each card is linted as you type.

  • Request. The principal is an ARN: an IAM user, a role (give a session name) or an assumed-role session ARN, a federated user, an account’s root, a service principal such as sns.amazonaws.com, an OIDC or SAML provider ARN, or anonymous. Paste get-caller-identity… takes the output of aws sts get-caller-identity. Add the action, the resource ARN, the resource owner when the ARN doesn’t say (S3), principal tags, and any context keys as JSON (aws:SourceIp, aws:PrincipalOrgID, s3:prefix, aws:TagKeys as a list…). Keys AWS derives from the principal and resource — aws:PrincipalArn, aws:PrincipalAccount, aws:PrincipalType, aws:username, aws:PrincipalTag/…, aws:RequestedRegion, aws:CurrentTime — are filled in unless you give them.

  • Decision shows Allowed, Explicit deny or Implicit deny, then the steps in AWS’s order — explicit deny, RCPs, SCPs, resource-based policies, identity-based policies, permissions boundary, session policy — and under that every statement of every policy with a ✓ or ✕ for its Action, Resource, Principal and each condition, with the values compared (aws:SourceIp: "203.0.113.5" matches none of "192.0.2.0/24").

  • The example buttons under the request (Try: payroll/ → Explicit deny) switch to variants of the request; Several actions × resources evaluates a grid, like the policy simulator’s action list.

Lint a policy checks one policy on its own: structure and element names, then common mistakes — Allow with NotAction or NotResource, object actions on a bucket ARN and bucket actions on bucket/, actions that only take "Resource": "", Principal: "" without a condition, trust policies open to any account, GitHub OIDC trust without a sub condition, partial wildcards in Principal, NotPrincipal without the session and account ARNs, ForAllValues without a Null check, Bool instead of BoolIfExists for MFA, iam:PassRole on , misspelt operators and global condition keys, empty regions, policy variables with the old Version, and size limits.

ARN parser splits an ARN into partition, service, region, account and resource, and explains it: S3 bucket vs object, IAM paths (and that session ARNs leave them out), the role behind an assumed-role session, Lambda qualifiers, KMS keys vs aliases, DynamoDB indexes. One click sends it to the evaluator as the principal or resource.

How AWS decides summarises the rules the evaluator follows.

For general policy-as-code — Rego and CEL, Kubernetes admission, JWT claims — see the policy playground. To design the architecture these policies protect, and get least-privilege CDK grants for it, use the AWS architecture builder. For DynamoDB access patterns, the DynamoDB single-table modeller.

How faithful it is

The evaluator follows the IAM User Guide’s “Policy evaluation logic”, “Policy evaluation for requests within a single account”, “Cross-account policy evaluation logic”, the condition operator and policy variable reference, and the KMS key policy rules. The tests run the guide’s own examples — the permissions boundary that stops iam:CreateUser, the home-folder policy with ${aws:username}, IP-range and MFA guardrails, the GitHub OIDC trust policy, the data-perimeter RCP — and the edge cases the documentation spells out: a resource policy naming a role session isn’t limited by a boundary but one naming the role is; an identity-based policy can’t grant use of a KMS key that doesn’t trust the account; ForAllValues is true for an empty request; a negated operator matches a missing key; a * at the end of an ARN segment runs past colons.

It is a model, not AWS. It doesn’t know every service’s actions, resource types and condition keys, so a statement naming an ARN for an action that only supports matches here when AWS would evaluate against (the linter and a hint warn about the common ones). It doesn’t model S3 ACLs, Block Public Access or Object Ownership, KMS grants, or the extra context AWS adds to real requests. Check anything that matters with the IAM policy simulator, Access Analyzer’s policy checks, or a real request.

Browser support

Any current browser.

Privacy

Everything runs in this page; nothing you paste is sent anywhere and the page makes no network requests. Your policies and request are kept in this browser’s local storage. Policies often contain account IDs, role names and bucket names — think before sharing a link to your own policies, because a share link carries them.

Runs entirely in your browser; nothing you type leaves this page.