IAM Policy Explainer / Linter
Paste an IAM policy and read it in plain English — statement by statement — with wildcard and risk findings, each one cited.
The plain-English reading appears here — one card per statement, element by element.
What this reading does not evaluate
This tool reads one policy document, statement by statement. Whether a request is actually allowed depends on things it does not see:
- Explicit-deny precedence across statements and across policies — a Deny elsewhere overrides any Allow read here.
- Permissions boundaries, which cap what an identity-based policy grants.
- Service control policies (SCPs) applied by an organization.
- Session policies passed when a role or federated session is created.
- How identity-based and resource-based policies combine for a specific request.
AWS's own policy evaluation is the only authority on a real request — this page reads statements, it never issues account-level verdicts.
The analysis runs in this tab. Nothing is uploaded and nothing here ever calls AWS.
Press ? for shortcuts
Do it offline:$ aws iam simulate-custom-policy --policy-input-list file://policy.json --action-names s3:GetObject
How to use it
- Paste an IAM policy document — an identity policy, a bucket policy, a trust policy — or load one of the example policies below the field.
- Read the Reading view first: one card per statement, each element turned into a sentence, with wildcard actions expanded into a note about what they cover.
- Switch to Findings for the lint pass: ten rules, grouped by severity, each one linking the exact AWS page it reads from so you can check the guidance yourself.
- Copy the report or download it as JSON for the review thread. The severities are this page's editorial reading of the cited guidance — carry the citations, not just the verdicts.
A policy lands in a review and every reader parses the JSON in their head, differently. Paste it here and read it back statement by statement: what each one allows or denies, which wildcards expand, which conditions narrow it — plus ten cited lint rules for the shapes that cause incidents. The analysis runs in this tab; nothing you paste is uploaded, and nothing here talks to AWS.
How it works
The pipeline has three stages, all local. First the document is parsed: JSON syntax, then policy grammar — Version values, Effect casing, action format, ARN shape, condition operators, principal shapes. Grammar violations do not stop the tool; they surface as per-path notes beside the input while the readable statements still get read.
Second, each statement whose Effect is readable becomes a card of sentences built from the parsed structure: the effect verb and action list, a wildcard-expansion note for entries like s3:Get*, the resource scope, the principal reading for resource-based policies, and one line per condition with the operator turned into words — IpAddress reads as "is inside", StringNotLike as "does not match the pattern", an IfExists suffix as "or when the key is absent entirely".
Third, the lint pass runs ten rules, IAML001 through IAML010, over the same structure: the full Action wildcard, service-wide wildcards, Resource "*" under write-class actions, the anonymous principal, NotAction and NotPrincipal combined with Allow, broad iam:PassRole, and wildcards inside principal ARNs. Every rule row carries the URL of the AWS page it reads from and the date it was checked.
What is deliberately NOT evaluated: explicit-deny precedence across statements and policies, permissions boundaries, service control policies, session policies, and how identity-based and resource-based policies combine for a request. Those live in AWS's evaluation logic, which runs inside AWS — this page reads one document, and the same not-evaluated list is printed under the tool itself.
Examples
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ListBucket",
"Effect": "Allow",
"Action": ["s3:ListBucket"],
"Resource": ["arn:aws:s3:::example-bucket"]
},
{
"Sid": "ReadObjects",
"Effect": "Allow",
"Action": ["s3:GetObject"],
"Resource": ["arn:aws:s3:::example-bucket/*"]
}
]
}Statement 1 — Sid ListBucket · Allow
This statement allows 1 action: s3:ListBucket.
It applies to 1 resource: arn:aws:s3:::example-bucket.
Statement 2 — Sid ReadObjects · Allow
This statement allows 1 action: s3:GetObject.
It applies to 1 resource: arn:aws:s3:::example-bucket/*.The classic S3 read-only pair, adapted from the cited AWS example policy: ListBucket on the bucket ARN, GetObject on the objects under it — two resources because the two actions target different ARN levels. This example is also the tool's false-positive gate: it must lint clean, and it does.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "*",
"Resource": "*"
}
]
}IAML001 · Error — Action is the full wildcard (*)
Statement 1 grants every action in every service. The cited guidance is to grant only the specific actions a task needs.
https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.html
IAML003 · Error — Resource (*) under write-class actions
Statement 1 grants write-class actions (*) on every resource. The cited guidance is to scope Resource to specific ARNs.
https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_resource.html
IAML008 · Error — Admin-equivalent statement — Action (*) on Resource (*)
Statement 1 is the administrator shape: every action on every resource. The cited guidance reserves this for administrator roles.
https://docs.aws.amazon.com/IAM/latest/UserGuide/best-practices.htmlThree findings from one statement, because three distinct cited rules read it: the action wildcard, the resource wildcard under write-class actions, and the combination — the administrator shape AWS's best-practices page tells you to reserve for administrator roles. Note what does NOT fire: the same statement under Deny is a guardrail and stays clean.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowOutsideIamOrgs",
"Effect": "Allow",
"NotAction": ["iam:*", "organizations:*"],
"Resource": "*"
}
]
}READING
Statement 1 — Sid AllowOutsideIamOrgs · Allow
This statement allows every action except 2 actions (iam:*, organizations:*) — NotAction inverts the list.
It applies to every resource — Resource is the full wildcard (*).
FINDINGS
IAML005 · Warning — NotAction with Allow — broader than it looks
Statement 1 allows everything outside its NotAction list (iam:*, organizations:*) — including services and actions that do not exist yet.
https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_policies_elements_notaction.htmlEDGE CASE — the construct everyone misreads. NotAction is not a deny list: with Allow it grants everything OUTSIDE the list, which grows every time AWS launches a service. The reading says so in the sentence itself, and IAML005 cites the NotAction reference page that carries the same warning.
Every example on this page is executed against the same engine that powers the tool, on every build.
Gotchas worth knowing
Explicit deny beats allow — and this page does not simulate that
AWS evaluates a request against every policy in scope, and one explicit Deny anywhere overrides every Allow. This tool reads one document at a time, so it renders what each statement says and stops there — the cross-policy arithmetic belongs to the cited evaluation-logic page and to AWS's own evaluator, not to a reader in a browser tab.
NotAction, NotResource and NotPrincipal read inverted
Each Not-element means everything except the list. The reading prints that inversion in words rather than echoing the JSON, because the misread is always in the same direction: people see a list of scary actions under NotAction and assume they are being denied. With Allow, the statement grants the rest of the universe — the cited element pages say exactly this.
Severity is an editorial reading, not an AWS verdict
AWS documentation states guidance; it does not grade policies. The error/warning/note labels here are this page's editorial mapping of that cited guidance onto ten fixed shapes, and each finding links its source so you can weigh the original text yourself. A clean findings list means none of the ten shapes matched — it is not a security review.
Common errors
This isn't valid JSON — Expected double-quoted property name in JSON at position 25 (line 1 column 26)- Policy documents are strict JSON: double quotes, no trailing commas, no comments. The parser's own position lands on the first character it refused, so the fix is usually a truncated paste or a comma before a closing brace.
"Statement" must be a JSON array of statement objects — the policy grammar allows no other shape.- A single statement still rides in an array. Consoles and SDKs sometimes hand you the inner object alone; wrap it in [ ] and the reading proceeds.
Statement[0].Effect is "allow" — it must be exactly Allow or Deny (the value is case-sensitive).- Effect is case-sensitive in the policy grammar, and a lowercase allow is refused by AWS at save time. The statement is excluded from the reading until the casing is fixed — the note names the exact path, so the fix lands on the right statement.
The document is 262,145 characters — the limit for this tool is 262,144 characters. Shorten it, or split it into separate policies.- The reader has one hard bound and prints the real numbers it compared. AWS's own limits are far smaller — a customer-managed policy caps at 6,144 non-whitespace characters, which the structure notes point out long before this refusal.
FAQ
Where do the example IAM policies come from?
The example block ships three example IAM policies: an S3 read-only policy adapted from the cited AWS documentation example, and two hand-built risky shapes — the admin wildcard and NotAction with Allow — whose expected findings are frozen in this page's tests. Each example chip fills the input, so the reading you get is the live tool over those exact bytes, not a screenshot.
Is Resource "*" always wrong?
No. Some actions bind to no specific resource, and read-class access to everything is sometimes the actual intent. The lint therefore reads context: IAML003 fires when the star sits under write-class actions, IAML008 when it is combined with the full action wildcard under Allow. A Deny statement over every resource is a guardrail and raises nothing.
What does the iam:PassRole warning mean?
iam:PassRole lets a caller hand a role to a service — and whatever that role is allowed to do, the service then does. Granting PassRole over a broad Resource means any role in scope may be passed, including ones more privileged than the caller. The cited AWS page recommends scoping PassRole to named role ARNs, which is exactly the shape IAML006 stays silent on.
The reading says my statement allows an action — why does the request still fail?
Because this page reads one document, and AWS evaluates many. An explicit Deny in another statement or policy, a permissions boundary, a service control policy, a session policy, or the interplay between identity-based and resource-based policies decides the real outcome. None of those are visible from the pasted JSON, and the tool's not-evaluated list states this beside the results.
Is my policy uploaded anywhere?
No. The parse, the reading and the lint pass all run in this tab, the share link carries only your view preference — never the document — and nothing on this page calls AWS or any other host. You can watch the network panel while you work; after load, it stays empty.
How is this different from AWS IAM Access Analyzer?
Access Analyzer runs inside AWS with your real resources, policies and organization in view, so its policy checks and external-access findings are verdicts about your account. This page is the step before that: a fast, local reading of a document you are still drafting or reviewing. For an authoritative answer, the cited simulate-custom-policy command under the tool requires the AWS CLI and — unlike this page — talks to AWS: it runs AWS's own evaluator.
Keyboard shortcuts
- /
- Focus the policy field.
- ⌘/Ctrl + S
- Download the report as a .json file.
- ⌘/Ctrl + C
- Copy the report when no text is selected.
- ⌘/Ctrl + Shift + C
- Copy the report even over an active selection.
- t
- Switch to the next theme.
- ? or ⌘/Ctrl + /
- Open the keyboard-shortcut reference.
- Esc
- Close the topmost overlay and return focus.