Wildcard actions such as "Action": "*", "s3:*" or an Allow with NotAction grant every matching API call a service offers, and most AWS accounts hold more of them than anyone has reviewed. They usually start as a quick fix during a build and stay because nothing breaks. This runbook shows how to find every IAM policy with wildcard actions in an account or an AWS Organization using read-only sources AWS provides: the account authorisation details export, AWS Config managed rules, Security Hub controls IAM.1 and IAM.21, and IAM Access Analyzer policy checks. It then covers how to rank what you find, replace wildcards with generated least privilege policies, and roll back through policy versions if something breaks. It is for cloud security and platform engineers who own IAM hygiene. For the policy model itself, see the AWS IAM Policies and Roles Guide.
This is an independent guide from NHI Mgmt Group, based on AWS's public documentation as of November 2026. It is not sponsored or reviewed by AWS. Product features change, so confirm settings against AWS's current documentation.
Key takeaways
- Look for four shapes, not one. Full wildcard
*, service wildcardservice:*, partial wildcards such asiam:*AccessKey*, andAllowwithNotAction, which allows everything not listed. - The managed rules miss inline policies. AWS Config rules
iam-policy-no-statements-with-admin-accessandiam-policy-no-statements-with-full-access(Security Hub IAM.1 and IAM.21) evaluate customer managed policies only, so inline policies need your own scan. - Only the default version counts. A managed policy can hold several versions; only the one with
IsDefaultVersiontrue is in effect, but old versions are one API call from being restored. - Rank by who holds it and what the service is.
iam:*,sts:*,kms:*,iam:PassRolewithResource: "*", and anything on a role an EC2 instance, Lambda function or CI pipeline can assume come first. - Replace with evidence, roll back with versions. Access Analyzer policy generation drafts a policy from up to 90 days of CloudTrail activity; ship it as a new default version so the previous version is the rollback.
When you need this
- Audit or compliance finding: Security Hub IAM.1 (High) or IAM.21 (Low) failing, or an assessor asking for evidence of least privilege under NIST SP 800-53 AC-6.
- After a credential exposure: when an access key or role session leaks, the blast radius is whatever its policies allow. A wildcard turns a contained leak into account takeover. The Capital One breach showed how much data a single over-permissioned role on one instance can reach.
- Before tightening guardrails: introducing service control policies, permission boundaries or a new account structure goes better when you know which principals rely on broad grants.
- Machine identity reviews: roles for workloads, pipelines and AI agents are the principals most likely to carry wildcards and least likely to have a human owner watching them.
What counts as a wildcard action
AWS documents that the * and ? wildcards can match all actions of a service ("s3:*") or part of an action name: "iam:*AccessKey*" matches CreateAccessKey, DeleteAccessKey, ListAccessKeys and UpdateAccessKey. Action names and prefixes are case insensitive, so IAM:listaccesskeys equals iam:ListAccessKeys and your scanner must normalise case. Because a wildcard matches whatever actions a service offers when the request is evaluated, actions AWS adds later are covered too, a point that follows from how matching works rather than one AWS states on the page.
| Pattern | Example | What it grants | Flagged by | Default priority |
|---|---|---|---|---|
| Full wildcard | "Action": "*" with "Resource": "*" | Administrator access to the account | Config iam-policy-no-statements-with-admin-access; Security Hub IAM.1 (High) | Critical |
| Service wildcard | "ec2:*", "iam:*" | Every action in that service | Config iam-policy-no-statements-with-full-access; Security Hub IAM.21 (Low) | Critical for iam, sts, kms, organizations, lambda; high otherwise |
| Allow with NotAction | "Effect": "Allow", "NotAction": "iam:*" | Every action in every service except those listed | Security Hub IAM.21 (for NotAction: service:*); your own scan | Critical |
| Wildcard in service name | "s*:Get*" | Matching actions across several services | Access Analyzer suggestion "Wildcard in service name" | High |
| Partial action wildcard | "s3:Get*", "iam:*AccessKey*" | A family of actions, sometimes including sensitive ones | Not flagged by IAM.21 (prefix with suffixed wildcard passes) | Medium; high when it includes write or credential actions |
| PassRole on any role | "iam:PassRole" with "Resource": "*" | Ability to hand any role to a service | Access Analyzer security warning "Pass role with star in resource" | Critical |
AWS describes NotAction with Allow as matching all services and actions not explicitly listed, and warns it can grant more than intended. Treat it as a wildcard. Deny with NotAction is different: it grants nothing and is a common guardrail pattern, so exclude it from findings.
Before you start
| Source | Covers | Gaps | Permission or cost |
|---|---|---|---|
aws iam get-account-authorization-details | Users, groups, roles with inline policies, attached managed policies, and every managed policy version with its document | One account per call; documents are URL-encoded in the API response | iam:GetAccountAuthorizationDetails; free |
| AWS Config managed rules | Default version of customer managed policies, re-evaluated on change | No inline or AWS managed policies; not available in every Region | AWS Config recording of AWS::IAM::Policy; Config rule evaluation charges |
| Security Hub IAM.1, IAM.21 | Same rules surfaced as findings across accounts | Same gaps; both run with excludePermissionBoundaryPolicy set to true | Security Hub with the standard enabled |
aws accessanalyzer validate-policy | Any policy document you pass, including inline policies and drafts | One document per call | See AWS pricing for Access Analyzer |
aws accessanalyzer check-access-not-granted | Proves a document does not grant up to 100 listed sensitive actions | Answers yes or no per document | Charged per check; see AWS pricing |
Run discovery from a read-only role, ideally one assumed into every member account from a security tooling account. Keep remediation behind a separate role that needs approval. Agree with the account owners which services are on your critical list before you rank anything.
Step-by-step
Steps 1 to 5 are read-only. Step 6 changes permissions but is reversible through policy versions.
- Export every policy in the account.
The output hasaws iam get-account-authorization-details --output json > authz-<account-id>.jsonUserDetailList,GroupDetailList,RoleDetailList(withRolePolicyListinline policies,AttachedManagedPolicies,InstanceProfileListandRoleLastUsed) andPolicies, where each managed policy has aPolicyVersionList. Use--filter LocalManagedPolicyto limit it to customer managed policies, or--filter Rolefor roles. The CLI paginates automatically; the API returnsIsTruncatedandMarker, and AWS notes it can return fewer items than requested even when more exist. Policy documents are URL-encoded under RFC 3986 in the API response, so decode them if your tool has not. - Scan statements for the six patterns. For each policy document (inline, and the managed version where
IsDefaultVersionis true), normaliseStatementandActionto lists, lower-case the action strings, and flag anyAllowstatement where an action contains*or?, or whereNotActionappears. Record the policy name or ARN, the statementSidor index, the matched pattern, theResourcevalue and anyCondition. Scan non-default versions separately and report them as a lower priority list: they are not in effect, but an identity withiam:SetDefaultPolicyVersioncan restore one. - Turn on the managed rules for continuous coverage. Deploy
IAM_POLICY_NO_STATEMENTS_WITH_ADMIN_ACCESSandIAM_POLICY_NO_STATEMENTS_WITH_FULL_ACCESSas AWS Config managed rules (or enable Security Hub, which runs them as IAM.1 and IAM.21). Both evaluate on configuration change againstAWS::IAM::Policy. The full access rule passesec2:Describe*and failsec2:*, so it will not catch partial wildcards. Read results with:aws configservice describe-compliance-by-config-rule --config-rule-names <rule-name> aws configservice get-compliance-details-by-config-rule --config-rule-name <rule-name> --compliance-types NON_COMPLIANT - Validate each flagged document with Access Analyzer. Pass the document to policy validation to get AWS's own security warnings with line and column locations:
Findings come back asaws accessanalyzer validate-policy --policy-document file://policy.json --policy-type IDENTITY_POLICYERROR,SECURITY_WARNING,WARNINGorSUGGESTION, each with anissueCodeand alearnMoreLink. Security warnings such as "Pass role with star in resource" go straight to the top of the queue. - Prove which sensitive actions are reachable and rank. For each document, test a fixed list of high-risk actions:
aws accessanalyzer check-access-not-granted --policy-document file://policy.json \ --access actions="iam:PassRole","iam:CreateAccessKey","iam:AttachRolePolicy","sts:AssumeRole","kms:Decrypt" \ --policy-type IDENTITY_POLICYFAILmeans the policy might allow at least one of them, andreasonsnames the statement. Join the result with who holds the policy:aws iam list-entities-for-policy --policy-arn <arn>for managed policies, the owning principal for inline ones. A wildcard on a role in an instance profile, a Lambda execution role or a CI federation role outranks the same wildcard on an unused test user. - Replace wildcards with generated or hand-written least privilege. For roles with activity, start Access Analyzer policy generation, which reads up to 90 days of CloudTrail events from an existing trail and drafts a policy with action-level detail for supported services and service-level detail for others. Note that
iam:PassRoleis not tracked and must be added by hand, and data events get no action-level detail. Review the draft, scope resources where the actions support resource-level permissions, then publish it as a new default version:
For inline policies, export the current document first, then useaws iam create-policy-version --policy-arn <arn> --policy-document file://scoped.json --set-as-defaultaws iam put-role-policy(orput-user-policy,put-group-policy) with the scoped version. Where a broad service grant is genuinely needed, keep the wildcard but addResourceARNs andConditionkeys, and record the exception.
Verify it worked
- Rule results: the Config rules and Security Hub IAM.1 and IAM.21 findings move to compliant or passed after the next change-triggered evaluation.
- Re-scan: a new authorisation details export shows no unexcepted wildcard statements in default versions or inline policies.
- Checks pass:
check-access-not-grantedreturnsPASSfor the sensitive action list on every remediated document. - No breakage: CloudTrail shows no rise in
AccessDeniederrors from the affected principals over a full business cycle, including month-end and batch jobs.
Rollback and common errors
| Situation | Fix |
|---|---|
| A workload fails after a new managed policy version | aws iam set-default-policy-version --policy-arn <arn> --version-id <previous>; the old version is still stored |
LimitExceeded on create-policy-version | A managed policy keeps at most five versions; delete the oldest non-default version with delete-policy-version after exporting it |
| An inline policy change breaks a job | Re-apply the exported original with put-role-policy; inline policies have no version history, so the export is your only rollback |
| Generated policy is missing actions | The CloudTrail window missed rare paths, iam:PassRole or data events; add them from the failing AccessDenied event |
| Policy generation will not start | Trails created by AWS Control Tower are not supported because the logs sit in the log archive account; use a trail Access Analyzer can read |
| Wildcard is in an AWS managed policy | You cannot edit it; detach it and attach a scoped customer managed policy instead |
Choosing the right approach
A single account with a few hundred policies can be covered by the export and a short script on a weekly schedule at no cost. An AWS Organization should rely on Security Hub IAM.1 and IAM.21 for customer managed policies across every account and Region, add the authorisation details scan for inline policies, and run Access Analyzer validation in the pipeline so new wildcards are caught in pull requests rather than in production. Where teams deploy through infrastructure as code, fixing the template is the real remediation: a console fix will be overwritten on the next deploy.
Decide on exceptions explicitly. Break-glass administrator roles and the roles your landing zone uses legitimately hold "*"; tag them, protect them with MFA, session policies or SCP conditions, and exclude them from the KPI rather than leaving them as permanent red findings. For the wider set of reduction techniques, see the Least Privilege Techniques Guide, and for preventive guardrails the AWS Organizations and SCPs Guide.
Where identity fits
Wildcards matter most on non-human identities. A role assumed by an EC2 instance, a Lambda function, a container task or a GitHub Actions workflow runs unattended, its credentials can be taken from the workload, and nobody notices extra permissions until they are used. Prioritise roles whose trust policy names a service principal or an OIDC provider, and check whether the same role also holds iam:PassRole or iam:*, which makes a wildcard an escalation path. See How to Find IAM Privilege Escalation Paths in AWS and How to Find ec2 instance profiles with admin rights.
Common failure patterns
- Trusting a green IAM.21: the control passes
s3:Get*and ignores inline policies, so a clean dashboard can hide broad grants. - Scanning only the default version: a stored older version with
"*"is restored by anyone withiam:SetDefaultPolicyVersion, a known escalation path. - Missing NotAction: a scanner that only looks for
*inActionmissesAllowplusNotAction, which is often broader. - Fixing the console, not the code: the next pipeline run reapplies the wildcard from the template.
- Removing a wildcard from a dormant role first: effort goes to principals that pose little risk while active workload roles keep
"*". Rank by use and exposure. - Leaked keys on a broad policy: in the LLMjacking campaigns, stolen cloud credentials were used to call AI services their owners never meant to expose; how far such a credential reaches depends on the policies behind it.
Metrics
| Metric | Target |
|---|---|
| Security Hub IAM.1 failed findings | Zero outside documented break-glass exceptions |
| Policies with service wildcards on critical services (iam, sts, kms, organizations) | Zero |
| Inline policies with wildcard actions | Falling each month |
Workload roles with iam:PassRole on "*" | Zero |
| Pull requests blocked by Access Analyzer security warnings | Tracked; rising at first, then falling |
| Rollbacks after remediation | Low and explained |
Framework mapping
| Framework | Reference | Relevance |
|---|---|---|
| NIST SP 800-53 Rev 5 | AC-6 Least Privilege; AC-6(5) Privileged Accounts; AC-6(7) Review of User Privileges | Remove broad grants and review them on a schedule |
| NIST CSF 2.0 | PR.AA-05 | Access permissions defined, reviewed and following least privilege |
| MITRE ATT&CK | T1078.004 Cloud Accounts; T1098.003 Additional Cloud Roles | Wildcards widen what valid cloud credentials can do and let attackers grant themselves roles |
| AWS Security Hub | IAM.1, IAM.21 | Native detective controls for customer managed policies |
Practitioner checklist
- Export authorisation details from every account and scan inline and default managed versions.
- Flag full, service, partial and service-name wildcards, Allow with NotAction, and PassRole on "*".
- Enable Config rules or Security Hub IAM.1 and IAM.21 for continuous coverage.
- Validate flagged documents with Access Analyzer and test a fixed sensitive action list.
- Rank by principal type, use and service; workload roles first.
- Replace with generated policies as new default versions; export inline policies before editing.
- Fix the infrastructure as code template, not just the live policy.
- Tag and document exceptions; add validation to the deployment pipeline.
Standards and references
- AWS: IAM JSON policy elements: Action
- AWS: IAM JSON policy elements: NotAction
- AWS CLI Reference: get-account-authorization-details
- AWS: Security Hub controls for IAM
- AWS Config: iam-policy-no-statements-with-full-access
- AWS: IAM Access Analyzer policy check reference
- AWS CLI Reference: check-access-not-granted
- AWS: IAM Access Analyzer policy generation
Related NHI Mgmt Group resources: How to find unused AWS IAM roles and access keys · IAM Access Analyzer Guide · Top 10 AWS IAM Mistakes · Least Privilege Techniques Guide