Cheat sheet — AWS IAM & policy simulation¶
Companion to Module 02 — Cloud Identity & IAM · CC BY 4.0 — print it, pin it, share it.
Last reviewed: 2026-07
Who am I, what can I reach¶
aws sts get-caller-identity # who is this key? account, ARN, userId
aws iam list-users # every user in the account
aws iam list-roles # every role (assumable identities)
aws iam get-account-authorization-details # ONE dump: users, roles, groups, all policies
Enumerate a principal's grants¶
aws iam list-attached-user-policies --user-name dev-alice # managed policies on the user
aws iam list-user-policies --user-name dev-alice # inline policy names
aws iam get-user-policy --user-name dev-alice --policy-name p # inline policy body
# managed policy → its live default version document
aws iam get-policy --policy-arn arn:aws:iam::111122223333:policy/dev
aws iam get-policy-version --policy-arn <arn> --version-id v3
# a role's trust policy — WHO is allowed to assume it (the federation wall)
aws iam get-role --role-name AdminRole --query 'Role.AssumeRolePolicyDocument'
Prove the reach — simulate-principal-policy¶
This runs AWS's real evaluation logic and returns allowed / explicitDeny / implicitDeny.
# does dev-alice's over-broad policy actually permit the dangerous action?
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::111122223333:user/dev-alice \
--action-names iam:PassRole s3:DeleteObject ec2:RunInstances \
--resource-arns "*"
# scope the sim to a specific resource (PassRole a specific admin role)
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::111122223333:user/dev-alice \
--action-names iam:PassRole \
--resource-arns arn:aws:iam::111122223333:role/AdminRole
# read only the verdict, not the whole envelope
aws iam simulate-principal-policy --policy-source-arn <arn> \
--action-names s3:DeleteObject --resource-arns "*" \
--query 'EvaluationResults[].{action:EvalActionName,decision:EvalDecision}'
simulate-custom-policytests a policy document you paste before it's attached — useful for validating the least-privilege rewrite before you apply it.
The escalation pair to always check¶
# iam:PassRole + a launch action = admin. Simulate BOTH; either denied breaks the chain.
aws iam simulate-principal-policy --policy-source-arn <user-arn> \
--action-names iam:PassRole ec2:RunInstances --resource-arns "*"
# self-grant primitives worth a second look
# iam:CreateAccessKey iam:CreatePolicyVersion iam:AttachUserPolicy iam:PutUserPolicy
Read the verdict — the evaluation rulebook¶
- No matching
Allow→implicitDeny(default-deny). - A matching
Allow→allowed. - Any matching
Denyanywhere (identity / resource / SCP / boundary / session) →explicitDeny, and it wins over everyAllow.
Author the least-privilege cut, then re-simulate¶
# a scoped policy: keep the legit action, kill the dangerous reach
cat > fix.json <<'JSON'
{ "Version": "2012-10-17",
"Statement": [
{ "Effect": "Allow", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::dev-bucket/*" },
{ "Effect": "Deny", "Action": "iam:PassRole", "Resource": "*" }
] }
JSON
aws iam put-user-policy --user-name dev-alice --policy-name least-priv --policy-document file://fix.json
# then re-run simulate-principal-policy: dangerous action → explicitDeny, legit action → allowed
Gotchas worth remembering¶
- A policy's name is not its boundary. "dev" scoped
s3:*on*reads and deletes every bucket. ReadAction×Resource; the moment either is*, the label means nothing. - The escalation hides in composition, not in one line. No single statement says "admin," but
iam:PassRoleon*plusec2:RunInstancesdoes. Simulate the pair, not each in isolation. - A finding is a hypothesis until
simulate-principal-policyconfirms it. An SCP, permission boundary, orDenyyou didn't read may already cap the grant — the simulator sees the full chain. - The trust policy is a separate wall.
"Principal": {"AWS": "...:root"}trusts the whole account, not one identity; an OIDC trust with nosubcondition trusts every workflow. Check who can assume a role, not just what the role can do. - "Fixed" means the sim flips. The dangerous action must return
explicitDeny/implicitDenyand the legitimate action must still returnallowed— cut the reach, don't break the job.
Only enumerate and simulate accounts you own or have explicit written permission to assess.
Comments
Sign in with GitHub to comment. Choose the type: Feedback (errors or suggestions on this page) · Hints (help for fellow learners — no spoilers) · General (anything else).