how to audit IAM policies for overly broad access
How to audit AWS IAM policies for overly broad access: finding wildcard actions and resources, reviewing trust policies, and using Access Analyzer findings to tighten permissions. Uses AWS CLI v2. Use during access reviews or after an incident. Triggers: 'audit IAM policies', 'overly permissive IAM'. Not for: writing new policies from scratch, non-AWS clouds.
how to audit IAM policies for overly broad access
TL;DR
Pull every IAM policy, hunt for wildcard actions and resources, and check trust policies for who can assume each role. AWS Access Analyzer flags the worst offenders for free, so start there, then work through the rest by hand. AWS CLI v2 throughout.
how to audit IAM policies for overly broad accessUse this when
- It is access-review season and IAM is on the list
- A pentest or incident flagged an overpowered role
- You inherited an AWS account and nobody knows what half the policies do
- You want a repeatable audit you can run quarterly
Not for this skill when
- You are writing brand-new policies from scratch (different skill)
- You use GCP or Azure (concepts transfer, commands do not)
- You need a full cloud security posture audit beyond IAM
Steps
1. List all customer-managed policies
Start with the policies your team actually wrote, not the AWS-managed ones.
aws iam list-policies --scope Local --query "Policies[*].[PolicyName,Arn]" --output textExpected: a list of policy names and ARNs. If this is empty, your permissions live in inline policies or AWS-managed ones, which need a different approach.
2. Find wildcard actions and resources
Fetch every policy's default version and count the wildcard lines. Any hit needs a human look.
for arn in $(aws iam list-policies --scope Local --query "Policies[*].Arn" --output text); do v=$(aws iam get-policy --policy-arn "$arn" --query "Policy.DefaultVersionId" --output text); aws iam get-policy-version --policy-arn "$arn" --version-id "$v" --query "PolicyVersion.Document" --output text; done | grep -F '"*"' | sort | uniq -cExpected: a count of lines containing "*" across all policy documents. Some are legitimate (like "Resource": "*" on read-only describe actions), most are not.
3. Review role trust policies
A tight permissions policy means nothing if anyone can assume the role. List roles and inspect who is trusted.
aws iam list-roles --query "Roles[*].[RoleName,Arn]" --output text | head -20
aws iam get-role --role-name [role name] --query "Role.AssumeRolePolicyDocument"Expected: the trust document shows exactly which principals can assume the role. Watch for broad AWS principals or service principals that are wider than they need to be.
4. Check Access Analyzer findings
Access Analyzer finds unused access and external access automatically. Make sure you have an analyzer and read its findings.
aws accessanalyzer list-analyzers --query "analyzers[*].[name,status]" --output text
aws accessanalyzer list-findings --analyzer-arn [analyzer arn] --query "findings[*].[resource,action]" --output text | head -20Expected: findings listing resources shared externally or permissions unused in 90 or more days. Unused-access findings are your least-privilege todo list.
5. Tighten and re-check
Replace wildcard actions with the specific ones CloudTrail shows the role actually uses, scope resources to ARNs, and re-run step 2 to confirm the count dropped.
aws iam get-policy-version --policy-arn [policy arn] --version-id v2 --query "PolicyVersion.Document.Statement[*].[Action,Resource]"Expected: specific actions like s3:GetObject on specific bucket ARNs, no bare "*". Record what you changed for the audit trail.
Why this happens
IAM policies start tight and grow loose: someone adds "*" to unblock a deploy at 2am, a tutorial suggests a wildcard, and nobody ever trims it back. Permissions only ever get broader because nothing breaks when you add access, and everything breaks when you remove it. The audit exists to find the drift.
Edge cases and pitfalls
- Some AWS-managed policies contain wildcards by design; focus on customer-managed ones first.
"Resource": "*"is required for actions that do not support resource-level permissions (likeec2:DescribeInstances); check the docs before flagging.- Service-linked roles look scary and are managed by AWS; leave them alone.
- Inline policies attached directly to users are easy to miss; list those too with
aws iam list-user-policies. - Test tightened policies in staging first; removing a permission a deploy pipeline needs is a classic self-inflicted outage.
Provenance
Resolved from the public thread: https://vectle.com/posts/pst_KFoI75k2CK5XTThVOpwiJg
Maintainer review
No maintainer verification is recorded for this version.
This records the version a maintainer checked. It does not assert that the version is the latest upstream release.