If you’ve tried to access an AWS resource and hit an “Access Denied” error, you’re not alone. I’ve spent countless hours troubleshooting these errors across dozens of accounts, and the root cause is rarely obvious at first glance. AWS evaluates permissions across multiple policy layers, and an issue in any one of them will deny your access. In this post, I’ll walk through exactly what causes this and how to fix it.
The Problem
When you attempt to access an AWS resource, you see an error like this:
| Error Type | Error Message |
|---|---|
| AccessDenied | User: arn:aws:iam::123456789012:user/alice is not authorized to perform: s3:GetObject on resource: arn:aws:s3:::my-bucket/file.txt |
This single error message hides five different policy evaluation layers. Your action is denied if any single layer blocks it, which is why debugging feels like finding a needle in a haystack.
Why Does This Happen?
- Missing identity-based policy: The IAM user, role, or group lacks an explicit Allow for the action on the resource
- SCP blocking at the Organizations level: An AWS Service Control Policy attached to your OU or account denies the action, overriding your IAM policy even if it says Allow
- Resource-based policy denial: The S3 bucket policy, KMS key policy, or other resource has an explicit Deny for your principal
- IAM Permission Boundary restriction: A permission boundary attached to your role acts as a ceiling — both the identity policy AND the boundary must allow the action
- VPC endpoint or session policy blocking: If you’re making requests through a VPC endpoint, the endpoint policy must also allow the action; similarly, temporary credentials from
sts:AssumeRolewith a session policy are restricted by that policy
The Fix
I recommend a systematic approach that tests each policy layer in order. Start with the AWS Policy Simulator, which evaluates your identity-based policies and permission boundaries:
# Test if your identity policy allows the action
aws iam simulate-principal-policy \
--policy-source-arn arn:aws:iam::123456789012:user/alice \
--action-names s3:GetObject \
--resource-arns arn:aws:s3:::my-bucket/file.txt \
--output text
If this returns allowed, your identity policy is fine. Move to the next layer. If it returns implicitDeny, add the permission to your IAM policy.
Layer 1: Check SCPs in AWS Organizations
If Policy Simulator says allowed but real API calls fail, an SCP is likely the culprit. SCPs act as a maximum permission ceiling for member accounts (the management account is exempt).
# List all SCPs attached to your account/OU
aws organizations list-policies-for-target \
--target-id 123456789012 \
--filter SERVICE_CONTROL_POLICY \
--output text
# Get the actual policy content
aws organizations describe-policy \
--policy-id p-xxxxxxxxxx \
--query 'Policy.Content' \
--output text
Look for any Deny statements or missing Allow statements that cover your action. SCPs are evaluated before IAM policies, so even a perfect IAM policy is worthless if an SCP blocks the action.
Layer 2: Check Resource-Based Policies
For S3, check the bucket policy:
aws s3api get-bucket-policy \
--bucket my-bucket \
--query 'Policy' \
--output text
Look for an explicit "Effect": "Deny" that matches your principal ARN. Also check if the bucket has Block Public Access enabled, which silently denies public access regardless of policies.
Layer 3: Check Permission Boundaries
aws iam get-role \
--role-name MyRole \
--query 'Role.PermissionsBoundary' \
--output text
If a permission boundary is attached, get its policy document and verify it includes your action. A permission boundary acts as an AND gate — both the identity policy AND the boundary must allow the action.
Layer 4: Check CloudTrail for the Actual Event
CloudTrail logs the exact reason for denial:
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=EventName,AttributeValue=GetObject \
--max-results 10 \
--output text
The CloudTrail event includes the error code and message that explains why the call was denied.
Is This Safe?
Yes. Using the Policy Simulator and reviewing your policies is completely safe — these are read-only operations. Modifying policies should follow your change management process, but troubleshooting is risk-free.
Key Takeaway
AWS IAM denials happen at five policy layers: identity-based policy, SCP, resource-based policy, permission boundary, and session policy. Test each layer systematically using the Policy Simulator, IAM Access Analyzer, and CloudTrail. In my experience, SCPs are the most commonly missed culprit — always check your Organizations policies when a denial doesn’t make sense.
Have questions or ran into a different IAM issue? Connect with me on LinkedIn or X.