I Tried Out "PolicyExclusionList," a New Feature of IAM Policy Simulator
This page has been translated by machine translation. View original
Introduction
On July 30, 2026, there was an update to the IAM Policy Simulator, along with its integration into the console.
Of the added features, PolicyExclusionList is a parameter that lets you run a simulation with specified policies excluded from evaluation. Is it safe to delete a certain Deny policy? Even if you delete it, would another blocker still stop the request? Previously, you couldn't know until you actually deleted it. With this parameter, you can estimate the impact before deleting. In this article, I gradually increased the number of excluded targets and tracked how the evaluation results changed.
Verification
Verification environment setup
For the IAM user used in the verification, I prepared one source of permission and two blockers for s3:GetObject.
| Element | Content | Role for s3:GetObject |
|---|---|---|
Managed policy ReadOnlyAccess (AWS managed) |
Allows s3:GetObject |
Source of permission |
Inline policy DenyS3GetObject |
Explicitly denies s3:GetObject |
Blocker 1 |
Permissions Boundary cm-simulator-exclusion-boundary (customer managed) |
Allows only s3:ListBucket / s3:ListAllMyBuckets |
Blocker 2 (does not allow s3:GetObject = implicit Deny) |
Running a simulation requires the iam:SimulatePrincipalPolicy permission.
Basic simulation (no exclusions)
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::123456789012:user/cm-simulator-exclusion-test-user \
--action-names s3:GetObject \
--resource-arns "arn:aws:s3:::cm-simulator-test-bucket/*"
EvalDecision:explicitDenyMatchedStatements:user_cm-simulator-exclusion-test-user_DenyS3GetObjectPermissionsBoundaryDecisionDetail:{"AllowedByPermissionsBoundary": false}
EvalDecision indicates whether the action is allowed, and MatchedStatements indicates the policy that affected that decision. Here, the explicit Deny in the inline policy matched, and this is Blocker 1.
Note that the bucket specified in --resource-arns does not need to actually exist.
Excluding the blockers one at a time
Each element of PolicyExclusionList is a union structure (PolicyIdentifier). You specify exactly one of PolicyType / PolicyArn / InlinePolicyIdentifier. InlinePolicyIdentifier, used to specify an inline policy, requires all three fields: PolicyName, AttachmentType, and AttachmentName.
Let's exclude only the inline policy DenyS3GetObject. If the explicit Deny disappears, it seems the Allow in ReadOnlyAccess would go through, but the result was different.
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::123456789012:user/cm-simulator-exclusion-test-user \
--action-names s3:GetObject \
--resource-arns "arn:aws:s3:::cm-simulator-test-bucket/*" \
--policy-exclusion-list '[{"InlinePolicyIdentifier":{"PolicyName":"DenyS3GetObject","AttachmentType":"user","AttachmentName":"cm-simulator-exclusion-test-user"}}]'
EvalDecision:implicitDenyMatchedStatements: emptyPermissionsBoundaryDecisionDetail:{"AllowedByPermissionsBoundary": false}
EvalDecision changed from explicitDeny to implicitDeny, so the exclusion of the explicit Deny worked. However, because there are multiple blockers, excluding just one does not result in allowed. At this point MatchedStatements is empty, and ReadOnlyAccess, which was not excluded, does not appear either, so the cause cannot be read from it. The cause is that PermissionsBoundaryDecisionDetail remains false. The Permissions Boundary of Blocker 2 does not allow s3:GetObject.
Next, let's exclude the Permissions Boundary as well.
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::123456789012:user/cm-simulator-exclusion-test-user \
--action-names s3:GetObject \
--resource-arns "arn:aws:s3:::cm-simulator-test-bucket/*" \
--policy-exclusion-list '[{"InlinePolicyIdentifier":{"PolicyName":"DenyS3GetObject","AttachmentType":"user","AttachmentName":"cm-simulator-exclusion-test-user"}},{"PolicyType":"permission-boundary"}]'
EvalDecision:allowedMatchedStatements:ReadOnlyAccess(IAM Policy)
Specifying permission-boundary for PolicyType excludes the entire attached Permissions Boundary. Both blockers are now removed, and the result is allowed.
When resource-based policies are involved
Simulations that include resource-based policies are supported only for IAM users (SimulatePrincipalPolicy).
Exclude all three identity-side items (ReadOnlyAccess, DenyS3GetObject, and the Permissions Boundary). Instead, grant permission through a resource-based policy (bucket policy) using --resource-policy.
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::123456789012:user/cm-simulator-exclusion-test-user \
--action-names s3:GetObject \
--resource-arns "arn:aws:s3:::cm-simulator-test-bucket/*" \
--policy-exclusion-list '[{"InlinePolicyIdentifier":{"PolicyName":"DenyS3GetObject","AttachmentType":"user","AttachmentName":"cm-simulator-exclusion-test-user"}},{"PolicyType":"permission-boundary"},{"PolicyArn":"arn:aws:iam::aws:policy/ReadOnlyAccess"}]' \
--resource-policy '{"Version":"2012-10-17","Statement":[{"Sid":"AllowUserGetObject","Effect":"Allow","Principal":{"AWS":"arn:aws:iam::123456789012:user/cm-simulator-exclusion-test-user"},"Action":"s3:GetObject","Resource":"arn:aws:s3:::cm-simulator-test-bucket/*"}]}'
EvalDecision:allowedMatchedStatements:ResourcePolicy(SourcePolicyType: Resource Policy)
This allowed is due solely to the Allow in the resource-based policy. When I removed only --resource-policy while leaving the exclusions unchanged, the result was implicitDeny and MatchedStatements was also empty. Since no identity-side permissions remain, there is no reason it passed other than the resource-based policy.
Summary
The policies that can be excluded with PolicyExclusionList are of five types: inline policies, AWS managed policies, customer managed policies, Permissions Boundaries, and SCPs. What I tested this time were the exclusion of an inline policy, an AWS managed policy, and a Permissions Boundary, as well as the evaluation of a resource-based policy provided via --resource-policy. I did not test configurations that include SCPs.
In a configuration with multiple blockers, excluding just one explicit Deny left the result at implicitDeny, and it became allowed only after the Permissions Boundary was also removed. With PolicyExclusionList, you can isolate this question of "how much do I need to remove for it to pass?" step by step, before touching any policies.
