Service Control Policies (SCPs) & Organizations
Key concepts
SCPs set permission boundaries
SCPs don't grant permissions
Organizational units (OUs)
Policy inheritance
Deny lists vs allow lists
The Morning Theo Almost Bought a Supercomputer
Six weeks after the photo bucket incident, Theo has been rehabilitated. Trailbase now runs three AWS accounts: production, development, and a sandbox where Theo holds AdministratorAccess and is encouraged to break things. One Friday he decides his trail-photo classifier needs more muscle, opens the EC2 launch wizard, and picks a p4d.24xlarge, a GPU monster that costs more per day than Theo earns in a month. He reaches the review screen before Mara glances over and asks what, exactly, he is about to click.
Here is the uncomfortable part: nothing inside the sandbox account could have stopped him. Theo is an administrator there, and IAM policies live inside the account, within reach of that administrator. Any fence Dana builds with IAM, admin-Theo can edit or delete. A guardrail that binds an administrator has to be anchored somewhere the administrator cannot reach, which means above the account itself. That is the job of AWS Organizations, the free service that gathers multiple AWS accounts under one management umbrella, and of Service Control Policies (SCPs), the policies attached to that umbrella which define the maximum permissions available inside each account. Back in lesson 1 you learned the evaluation ladder: explicit deny beats allow, and SCPs cap everything from above. This lesson is about that cap.
Key Principle
SCPs set the maximum available permissions. They grant nothing on their own. Effective permissions are the intersection of IAM policies and SCPs: if either side denies an action, the action is denied. Theo can hold AdministratorAccess inside the sandbox forever; the SCP above him decides what that title is actually worth.
Two exemptions carry a disproportionate number of SAA-C03 points: SCPs never affect the management account, and they never affect service-linked roles. When a scenario says an admin was somehow able to do a denied thing, check whether they were in the management account.
One Account Becomes Many
Why did Trailbase split into three accounts at all? Because the account is the strongest isolation boundary AWS offers. A runaway experiment in the sandbox cannot touch production data, blow through production quotas, or appear in production bills. Once you have more than one account, though, someone has to manage the pile, and that is what AWS Organizations does.
Dana creates the organization from a fourth, deliberately empty account. That account becomes the management account: it owns the organization, pays the consolidated bill, and holds the power to create accounts and attach policies. Every other account joins as a member account. Dana keeps the management account as empty as her root credentials vault, and for the same reason: it is exempt from the very guardrails it hands out, so anything running inside it runs unguarded.

AWS Organizations
AWS Organizations is a free service for centrally managing multiple AWS accounts. It provides consolidated billing (a single payment method for all accounts), centralized account creation and management, policy-based governance through SCPs and other policy types, and programmatic account automation.
Its hierarchy has four pieces. The organization is the container for everything. The Root sits at the top of the hierarchy; AWS overloads the word here, since the Root of an organization is a container while the root user is a sign-in identity inside a single account. Organizational units (OUs) are logical groupings of accounts, and the accounts themselves are ordinary AWS accounts: one management account plus members.
Trailbase groups its accounts into organizational units, or OUs: folders in the hierarchy that exist so you can attach policy once and have it apply to every account inside. Production sits in a Workloads OU, development in an SDLC OU, and Theo's playground in a Sandbox OU. The point of the grouping becomes clear the moment Dana writes her first guardrail.
SCPs: The Fence Admins Cannot Move
Monday morning, Dana attaches a policy to the Sandbox OU that denies ec2:RunInstances whenever the requested instance type is large. Theo still has AdministratorAccess. He can still create users, delete buckets, and rearrange the sandbox however he likes. But when he retries the p4d.24xlarge launch, AWS refuses, and no amount of IAM editing inside the account will change that answer, because the refusal comes from a layer he cannot see, let alone modify.
That policy is a Service Control Policy. An SCP works purely by subtraction: it defines the outer boundary of what identities in an account can ever do, and the actual grants still come from IAM policies inside that boundary. Attach an SCP allowing s3:* to an account containing a user with zero IAM policies, and that user can still do nothing. The SCP opened the gate; nobody inside handed the user a key. The JSON is the same policy language you learned in lesson 1, and since 2025 it is the full language.
Service Control Policies
SCPs are organization policies that define the maximum permissions for member accounts. They are boundary policies that set limits without granting anything, they are inherited down through the OU hierarchy, their effect is cumulative (all applicable SCPs must allow an action), and they use the same JSON syntax as IAM policies.
SCPs can control API actions (Allow/Deny), resources (with full ARN support as of 2025), and conditions such as source IP, tags, and regions.
New SCP Capabilities (2025)
AWS now supports full IAM policy language in SCPs:
- Conditions: Use any IAM condition keys
- Resource ARNs: Specify individual resources
- NotAction with Allow: More flexible allow statements
- NotResource: Exclude specific resources
- Wildcards: Use
*anywhere in action strings
This enables much more granular control than before. Older exam prep material that says SCPs cannot target specific resource ARNs is out of date.
An IAM user in a member account has the AdministratorAccess managed policy attached, yet every attempt to launch an EC2 instance is denied. What is the MOST likely cause?
Inheritance: Guardrails Flow Downhill
Where you attach an SCP decides who it binds, and the rule is simple: policies flow down the tree. Attach at the Root and every OU and account inherits it. Attach to the Workloads OU and only the accounts under Workloads feel it. For an action to succeed, every SCP on the path from the Root down to the account must allow it. Evaluation is an intersection, so a child OU can never re-allow something a parent denied.
SCP Inheritance Model
SCPs attached to the root apply to every OU and account. SCPs attached to an OU apply to all child OUs and accounts beneath it, and SCPs attached directly to an account apply to that account alone. For an action to be permitted, all applicable SCPs must allow it.
The resulting formula: Effective Permissions = Root SCPs ∩ OU SCPs ∩ Account SCPs ∩ IAM Policies. If any SCP in the path denies an action, the action is denied regardless of IAM policies.
Watch it work at Trailbase. After the operations team once terminated a production instance during a deploy scare, Dana added a termination deny at the Production OU, with an MFA-protected exception role exempted by ARN condition for the rare legitimate case. The production account now inherits two layers of policy without a single policy attached to the account itself:
SCP Inheritance Example
| Level | SCP | Effect |
|---|---|---|
| Root | Allow: ec2:*, s3:*, rds:* | Maximum permissions for org |
| Production OU | Deny: ec2:TerminateInstances | Cannot terminate instances |
| Account A (in Production) | No additional SCPs | Inherits: Allow EC2/S3/RDS, Cannot terminate |
Multiple SCPs apply to an account: one at the root, one on its OU, and one attached directly to the account. How does AWS combine them?
The Exemptions Everyone Forgets
A month in, Dana notices something odd in CloudTrail: an action her root-level SCP denies was performed anyway, from the management account. This is by design, and it is the single most tested SCP fact on the exam.
SCP Limitations
SCPs do NOT apply to:
- Management account: Always exempt from SCPs
- Service-linked roles: Used by AWS services internally
- Resource-based policies: SCPs only affect principals in the organization, so a bucket policy granting access to an outside principal is evaluated on its own terms
- Actions performed by AWS services on your behalf
These exemptions are frequently tested on the exam.
The management account exemption exists so an organization can never lock itself out of its own steering wheel, and it is exactly why Dana keeps that account empty of workloads. The service-linked role exemption matters for the same reason in miniature: Auto Scaling, for example, acts through a service-linked role, and an SCP that appeared to block EC2 launches would silently fail to stop scaling events. Priya, reviewing Trailbase's setup during her audit, called the empty management account the best decision in the whole architecture.
Which of the following is NOT affected by Service Control Policies?
Two Ways to Run the Guardrails
Every new organization ships with a managed SCP called FullAWSAccess attached everywhere, which allows everything. From that starting point you have two strategies, and choosing between them is a classic exam decision.

Deny List Strategy (Recommended)
Start with Allow, add explicit Denies. Keep the FullAWSAccess policy, which allows everything, then attach explicit Deny statements for the actions you want blocked. New AWS services are automatically available the day they launch, the policy set stays simple to maintain, and anyone can read exactly what is blocked. This is the right choice for most organizations.
Allow List Strategy
Only allow explicitly permitted actions. Remove the FullAWSAccess policy and allow specific services and actions; everything else is implicitly denied. The cost is real: every new AWS service must be added by hand, legitimate workloads risk being blocked, and maintenance is heavier. Reserve this for highly regulated industries such as finance and healthcare that require strict control.
Trailbase picks the deny list, and so should almost everyone: the team wants each new AWS service usable on day one, with a short, readable list of forbidden actions. The allow list earns its cost only where a regulator demands that nothing unapproved can ever run.
The Trailbase Guardrail Set
Over the following quarter, Dana and Mara build up five deny-list policies. Each one exists because something happened, or nearly happened, and together they cover the patterns the exam draws from.
The first came from cost review. Development instances kept appearing in far-flung regions where nobody looked at the console, and European hiker data has GDPR residency requirements anyway. So Trailbase pins the whole organization to three approved regions, with an exemption for the admin role so a global operation can never strand them. One caveat for real deployments: global services such as IAM, Route 53, and CloudFront issue their calls through specific regions, so region-pinning SCPs need carve-outs for them.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyUnapprovedRegions",
"Effect": "Deny",
"Action": "*",
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:RequestedRegion": [
"us-east-1",
"us-west-2",
"eu-west-1"
]
},
"ArnNotLike": {
"aws:PrincipalARN": "arn:aws:iam::*:role/OrganizationAdminRole"
}
}
}
]
}The second policy came straight from Priya's audit findings. CloudTrail is the log that caught nothing during the root-credentials era because there was nothing to attribute; now that it attributes everything, the log itself must be untouchable, even by account admins. The same goes for AWS Config, the service recording each resource's configuration history. Attached at the Root, the policy holds for every account Trailbase will ever create, which is the exam answer whenever a scenario says no user in any account may disable CloudTrail:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ProtectCloudTrail",
"Effect": "Deny",
"Action": [
"cloudtrail:DeleteTrail",
"cloudtrail:StopLogging",
"cloudtrail:UpdateTrail"
],
"Resource": "*"
},
{
"Sid": "ProtectConfig",
"Effect": "Deny",
"Action": [
"config:DeleteConfigRule",
"config:DeleteConfigurationRecorder",
"config:DeleteDeliveryChannel",
"config:StopConfigurationRecorder"
],
"Resource": "*"
}
]
}The third enforces encryption. Mara's data pipelines write hiker analytics to S3, and rather than trusting every script to remember the encryption header, the organization denies any unencrypted upload. The Null condition reads as "if the encryption header is absent":
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyUnencryptedS3Objects",
"Effect": "Deny",
"Action": "s3:PutObject",
"Resource": "*",
"Condition": {
"Null": {
"s3:x-amz-server-side-encryption": "true"
}
}
}
]
}The fourth protects the protections. S3 Block Public Access is the account-level switch that keeps buckets private, and this policy stops anyone below the security admin role from flipping it:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyPublicBucketACL",
"Effect": "Deny",
"Action": [
"s3:PutBucketPublicAccessBlock",
"s3:DeletePublicAccessBlock"
],
"Resource": "*",
"Condition": {
"ArnNotLike": {
"aws:PrincipalARN": "arn:aws:iam::*:role/SecurityAdminRole"
}
}
}
]
}The fifth closes the loop on lesson 1. Every member account still has root credentials, and each one is a standing risk. Recall that inside an account, root sits above every IAM policy; inside an organization, an SCP sits above member-account root, so it can do what IAM never could and neutralize that root user entirely.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyRootUser",
"Effect": "Deny",
"Action": "*",
"Resource": "*",
"Condition": {
"StringLike": {
"aws:PrincipalArn": "arn:aws:iam::*:root"
}
}
}
]
}Designing the OU Tree
With the policies written, the remaining question is where they hang, which means designing the OU tree. AWS publishes a recommended structure, and its guiding rule is to organize by function and compliance need rather than by the company reporting chart:
Recommended OU Structure
| OU | Purpose | SCP Focus |
|---|---|---|
| Security | Log Archive, Audit, Security Tools | Protect security resources, deny deletions |
| Infrastructure | Networking, Shared Services | Protect shared resources, limit modifications |
| Workloads/Production | Production applications | Deny dangerous actions, require encryption |
| Workloads/SDLC | Dev, Test, Staging | More permissive, allow experimentation |
| Sandbox | Developer experimentation | Budget limits, region restrictions, minimal guardrails |
| PolicyStaging | Test new SCPs before rollout | Test policies safely |
| Transitional | Migrating/acquired accounts | Temporary permissive policies |
Two entries deserve a story each. PolicyStaging exists because an SCP typo can take down production in every account at once; Dana tests each new policy against throwaway accounts in that OU before promoting it. Transitional exists for the day Trailbase acquires a smaller trail-mapping startup whose account arrives with unknown workloads: it joins the organization under temporarily permissive policies instead of being strangled by production guardrails on day one.
SCP and OU Best Practices
- Use the deny list strategy: Start permissive, add explicit denies
- Test before applying: Use a PolicyStaging OU first
- Attach at the OU level: Easier to manage at scale than per-account attachments
- Create exception roles: Never paint yourself into a corner
- Protect security services: CloudTrail, Config, GuardDuty
- Monitor with CloudTrail: Audit SCP-denied actions
- Organize OUs by function and compliance need, keep the hierarchy shallow (AWS supports 5 levels; 2 or 3 is usually enough), separate production from non-production, and document every policy
A company wants to verify that a new SCP will not break production workloads before rolling it out broadly. What is the recommended approach?
Trailbase, Governed
The p4d incident ends as a slide in the onboarding deck. Theo keeps his sandbox admin rights and his salary. The organization now has guardrails that no admin can loosen, an audit trail no admin can silence, and a management account that stays empty precisely because it answers to nothing. Priya signs off on the audit.
SAA-C03 SCP questions follow a template: some identity has generous IAM permissions yet an action fails, or a requirement says an action must be impossible for everyone, including admins, across all accounts. Both point to SCPs. Then check the twist: management account and service-linked roles are exempt, and all SCPs on the path must allow the action.
Exam Scenarios
| Scenario | Solution | Why |
|---|---|---|
| Prevent all users from disabling CloudTrail | SCP denying cloudtrail:StopLogging at root | SCPs are preventive guardrails that cannot be overridden |
| Allow EC2 in us-east-1 only | SCP with region condition | Conditions can restrict to specific regions |
| User has IAM admin access but cannot launch EC2 | SCP is denying EC2 actions | SCPs limit maximum permissions, even for admins |
| Management account can still perform denied action | Expected behavior | SCPs do not affect the management account |
| Need to test new SCP before production | Apply to PolicyStaging OU first | Test impact on non-production accounts |
| Lambda function denied by SCP but it uses service-linked role | Service-linked roles are exempt | SCPs do not affect service-linked roles |
| Acquired company needs temporary permissive access | Place in Transitional OU | Minimal restrictions during migration |
| Consolidate billing across 10 accounts | AWS Organizations consolidated billing | Single payment method for all member accounts |
Common Pitfalls
Locking Yourself Out
An SCP that denies iam:* or organizations:* too broadly can leave nobody able to undo it, and recovery may require AWS Support. Always keep a break-glass exception role exempted by condition, and stage every policy in the PolicyStaging OU before it touches real accounts.
Trusting SCPs to Guard the Management Account
The management account is never affected by SCPs, so any workload running there runs without guardrails. Keep that account empty, restrict who can access it, and rely on tightly scoped IAM policies for the few humans who sign in.
Expecting a Child Allow to Beat a Parent Deny
Inheritance is cumulative AND logic flowing downward. Every SCP on the path from root to account must allow an action, so an allow attached lower in the tree can never restore what a parent denied. Plan the hierarchy before attaching anything: broad allows at the root, targeted denies below.
Allow Lists in Fast-Moving Environments
With an allow list strategy, every new AWS service is blocked until someone updates the SCP, which turns the security team into a bottleneck. Reserve allow lists for regulated environments that genuinely require them, and automate the updates if you must run one.
Quick Reference
SCP Service Limits
| Limit | Value |
|---|---|
| Maximum SCPs per organization | 1,000 |
| Maximum SCPs per OU or account | 5 |
| Maximum SCP document size | 5,120 characters |
| Maximum OU nesting depth | 5 levels |
SCP vs IAM Policies
| Aspect | SCP | IAM Policy |
|---|---|---|
| Purpose | Set maximum permissions | Grant permissions |
| Scope | Organization/OU/Account | User/Group/Role |
| Grants Access | No - only restricts | Yes |
| Affects Management Account | No | Yes |
| Affects Service-Linked Roles | No | Limited |
| Inheritance | Yes - through OU hierarchy | No automatic inheritance |
# List all SCPs in organization
aws organizations list-policies --filter SERVICE_CONTROL_POLICY
# Get SCP content
aws organizations describe-policy --policy-id p-abc123def
# Create new SCP
aws organizations create-policy \
--name "DenyUnapprovedRegions" \
--description "Prevents use of unapproved regions" \
--type SERVICE_CONTROL_POLICY \
--content file://scp-deny-regions.json
# Attach SCP to OU
aws organizations attach-policy \
--policy-id p-abc123def \
--target-id ou-root-example
# Detach SCP from OU
aws organizations detach-policy \
--policy-id p-abc123def \
--target-id ou-root-example
# List OUs in organization
aws organizations list-organizational-units-for-parent \
--parent-id r-abc1
# Move account to different OU
aws organizations move-account \
--account-id 123456789012 \
--source-parent-id ou-source \
--destination-parent-id ou-destinationaws:RequestedRegion - Restrict by AWS region
aws:PrincipalArn - Match principal ARN pattern
aws:PrincipalOrgID - Ensure principal is in org
aws:PrincipalTag/* - Match principal tags
aws:ResourceTag/* - Match resource tags
aws:SourceIp - Restrict by source IP
ec2:InstanceType - Restrict instance types
s3:x-amz-server-side-encryption - Require encryption