If you’ve ever had an IAM policy that clearly allows an action, run it through the AWS Policy Simulator and gotten an “allowed” result, but then the real API call still fails, you’re not alone. I’ve debugged this issue in dozens of organizations, and nine times out of ten, the culprit is an AWS Service Control Policy (SCP) silently overriding your permissions. In this post, I’ll walk through exactly what SCPs do and how to identify when one is the real culprit.
The Problem
You have an IAM policy that looks correct:
{
"Effect": "Allow",
"Action": "ec2:TerminateInstances",
"Resource": "*"
}
You run the Policy Simulator and it says “allowed”. But when you try to terminate an instance, you get:
| Error Type | Error Message |
|---|---|
| UnauthorizedOperation | You are not authorized to perform this operation. |
Your IAM policy allows it. The Policy Simulator allows it. Yet the API call fails. This is the classic signature of an SCP in your way.
Why Does This Happen?
Service Control Policies operate differently than traditional IAM policies. Here’s what makes them dangerous:
- SCPs are evaluated first: AWS checks SCPs before evaluating your identity-based policy. If an SCP denies an action, it’s denied, period — no IAM Allow can override it
- SCPs act as a maximum ceiling: An SCP doesn’t grant permissions; it defines the maximum set of permissions allowed. If you’re in an OU with an SCP that denies
ec2:*, even if your IAM policy allowsec2:TerminateInstances, you’re blocked - Management account is exempt: SCPs apply only to member accounts. If you’re in the AWS Organizations management account, SCPs don’t affect you at all
- Effective permission = IAM policy AND SCP: For a member account, the actual permission = (what IAM allows) AND (what SCP allows). Both must say yes
- They’re often invisible: Many organizations deploy SCPs during landing zone setup and never communicate them to development teams, leading to hours of debugging
The Fix
First, identify if an SCP is actually blocking you. List all SCPs attached to your account or OU:
# Get your account ID
aws sts get-caller-identity \
--query 'Account' \
--output text
# List SCPs for your specific account
aws organizations list-policies-for-target \
--target-id 123456789012 \
--filter SERVICE_CONTROL_POLICY \
--output text
# List SCPs for your OU (if you know it)
aws organizations list-policies-for-target \
--target-id ou-abc1-xxxxxxxx \
--filter SERVICE_CONTROL_POLICY \
--output text
Now get the actual policy content to see what’s being denied:
# Retrieve the full policy document
aws organizations describe-policy \
--policy-id p-xxxxxxxxxx \
--query 'Policy.Content' \
--output text | jq .
Look for "Effect": "Deny" statements. The key question: does the SCP deny your action?
Understanding the Evaluation Order
AWS evaluates policies in this order:
- SCP evaluation (for member accounts): If any SCP says Deny, the request is blocked immediately
- Permission boundary evaluation: If you have a permission boundary, it must allow the action
- Identity-based policy evaluation: Your IAM role/user policy must allow it
- Resource-based policy evaluation: The resource (S3 bucket, KMS key, etc.) must allow it
If an SCP denies ec2:TerminateInstances, you cannot perform it in that account — full stop — no matter what your IAM policy says.
How to Work Around It
If an SCP is blocking a legitimate action, you have two options:
Option 1: Modify the SCP (if you have permissions in the Organizations management account)
# Get the current policy
aws organizations describe-policy \
--policy-id p-xxxxxxxxxx \
--query 'Policy.Content' \
--output text > scp-policy.json
# Edit the policy to add an Allow or remove the Deny
# Then update it
aws organizations update-policy \
--policy-id p-xxxxxxxxxx \
--content file://scp-policy.json
Option 2: Request an exception from your organization’s cloud governance team to adjust the SCP to exclude your use case (recommended in most enterprises).
Key Concept: The Permission Matrix
Think of permissions like this:
Effective Permission = (IAM Policy) AND (SCP) AND (Permission Boundary) AND (Resource Policy) AND (Session Policy)
If any single layer is “Deny” or missing “Allow”, the entire request is denied. This is why SCPs are so powerful — and so dangerous if misconfigured.
Is This Safe?
Checking which SCPs are attached to your account is completely safe — these are read-only operations. Modifying SCPs should follow your organization’s change management process, as they affect entire OUs and accounts.
Key Takeaway
Service Control Policies are the silent killer of seemingly valid IAM permissions. If your IAM policy allows an action, the Policy Simulator says it should work, but the real API call fails, check your SCPs first. In my experience, 80% of these “mysterious” denials are caused by an SCP that was deployed as part of landing zone setup and forgotten. Always check list-policies-for-target when you hit an unexplained AccessDenied error.
Have questions or ran into a different IAM issue? Connect with me on LinkedIn or X.