When you're managing multiple AWS accounts, you need a way to control and restrict usage to for example unused AWS Services or AWS Regions.
A feature from AWS Organizations called AWS Service Control Policies (SCPs) allows you to create a set of rules to restrict or allow access to AWS resources on multiple accounts at once.
In this article, I'll be sharing comprehensive AWS SCP examples that you can immediately apply to your own AWS Organization to improve the security of your AWS accounts. These examples are organized by Organizational Unit (OU) type to show you exactly where and why to apply each policy in your multi-account strategy.
By default, all actions are allowed within an AWS Organization. Therefore the AWS SCPs that we're sharing here use the deny list strategy.
Note: make sure to review and test the AWS SCP examples before you proceed to activate them in a production account or Organizational Unit (OU).
Foundation SCPs: Apply These to Every OU
These fundamental SCPs prevent catastrophic mistakes regardless of environment. Apply them organization-wide at the root level or to every OU in your structure.
1. Deny access to AWS resources for the AWS account root user
When to use this: Apply this foundation SCP to every OU in your multi-account organization (Production OU, Development OU, Security OU, Infrastructure OU, and Sandbox OU) except the Root/Management account itself (where emergency root access might be needed for billing or account recovery). This is a universal guardrail that should protect all member accounts regardless of their purpose.
Why this matters: The root user bypasses all IAM policies and has unrestricted access to all resources, billing information, and account closure. This prevents accidental or malicious actions using root credentials and enforces the use of IAM users/roles with proper audit trails and scoped permissions.
It's generally a best practice to not use the root user to do your tasks in your AWS account. Instead, you should create an IAM admin user and use that to do administrative tasks.
Since the root user has full access to all your resources and billing information you should further protect it with the following steps:
- Enable AWS multi-factor authentication (MFA)
- Delete the access keys from the Security credentials page
- Setting up a strong password
As an additional layer of protection, you can set up a guardrail in the form of a Service Control Policy to deny access to AWS resources from the root user.
{ "Version": "2012-10-17", "Statement": [ { "Sid": "DenyRootUser", "Effect": "Deny", "Action": "*", "Resource": "*", "Condition": { "StringLike": { "aws:PrincipalArn": "arn:aws:iam::*:root" } } } ] } What this prevents: Any AWS API call using root credentials fails immediately. Developers and administrators must use IAM users or roles, which means proper audit trails, scoped permissions, and accountability.
2. Deny access to AWS services in unsupported AWS regions
When to use this: Apply this foundation SCP to all OUs (Production, Development, Security, Sandbox) except your Infrastructure OU (specifically the Network accounts, which might need global services like CloudFront, Route53, or IAM). Restrict resource creation to your approved operational regions only. For example, if you operate exclusively in eu-west-1, deny all other regions to enforce data residency requirements and limit the blast radius during security incidents.
Why this matters: Restricting unused regions prevents attackers from expanding into regions you don't monitor during a security breach, limits cryptocurrency mining in unexpected regions, enforces data residency compliance requirements, and concentrates your security monitoring and cost visibility in approved regions only.
This SCP restricts the use of AWS services in unsupported AWS Regions. This is very useful if you only deploy to a single AWS region.
By revoking access to other AWS regions you'll effectively limit the blast radius in the event of a security breach.
As you can see in the example below, if the AWS API call doesn't match with the eu-west-1 regions then deny all actions on all resources except for the AWS services in the NotAction element.
If you look closer to the NotAction element, the services that are listed there are global services and are hosted in the us-east-1 region by default.
Be aware, that blocking the services that are whitelisted in this action might cause issues in your active region.
{ "Version": "2012-10-17", "Statement": [ { "Sid": "DenyUnsupportedRegions", "Effect": "Deny", "NotAction": [ "a4b:*", "acm:*", "aws-marketplace-management:*", "aws-marketplace:*", "aws-portal:*", "budgets:*", "ce:*", "chime:*", "cloudfront:*", "config:*", "cur:*", "directconnect:*", "ec2:DescribeRegions", "ec2:DescribeTransitGateways", "ec2:DescribeVpnGateways", "fms:*", "globalaccelerator:*", "health:*", "iam:*", "importexport:*", "kms:*", "mobileanalytics:*", "networkmanager:*", "organizations:*", "pricing:*", "route53:*", "route53domains:*", "s3:GetAccountPublic*", "s3:ListAllMyBuckets", "s3:PutAccountPublic*", "shield:*", "sts:*", "support:*", "trustedadvisor:*", "waf-regional:*", "waf:*", "wafv2:*", "wellarchitected:*" ], "Resource": "*", "Condition": { "StringNotEquals": { "aws:RequestedRegion": ["eu-west-1"] } } } ] } What this prevents: Attackers can't expand their footprint into regions you don't monitor. Developers can't accidentally launch resources in the wrong region, causing data residency compliance violations. You maintain concentrated visibility and control.
3. Enforce S3 Bucket owner enforced setting
When to use this: Apply this foundation SCP to all workload OUs (Production OU, Development OU, Staging OU, and Sandbox OU) anywhere your teams create S3 buckets for applications, data storage, or file sharing. This ensures that all new S3 buckets disable ACLs and enforce bucket owner control, preventing a common security misconfiguration that can lead to data loss or unauthorized access.
Why this matters: Without this policy, external parties can upload objects with ACLs that transfer ownership to their accounts, causing you to lose control of your own data in your own buckets. Enforcing bucket owner control prevents ownership transfer via ACLs and maintains centralized access management through bucket policies and IAM.
Deny the s3:CreateBucket permission for IAM users or roles unless you set the bucket owner enforced setting for Object Ownership and disable ACLs.
{ "Version": "2012-10-17", "Statement": [ { "Sid": "RequireBucketOwnerFullControl", "Effect": "Deny", "Action": ["s3:CreateBucket"], "Resource": "*", "Condition": { "StringNotEquals": { "s3:x-amz-object-ownership": "BucketOwnerEnforced" } } } ] } What this prevents: All S3 buckets automatically disable ACLs and enforce bucket owner control. You cannot create buckets where external entities could gain ownership of objects, eliminating a common security misconfiguration.
Security OU SCPs: Protecting Your Security Foundation
Your Security OU contains the crown jewels: audit logs, security tooling, and compliance validation infrastructure. These accounts need absolute protection from tampering.
4. Prevent deletion or modification of security logging
When to use this: In a Security OU containing your Log Archive Account, Audit Account, and Security Tooling Account, apply this SCP to make your security foundation immutable. As detailed in our multi-account strategy guide, these accounts house your organization's crown jewels (audit logs, compliance data, and security monitoring tools) and require absolute protection from tampering.
Why this matters: Prevents insider threats from deleting evidence of malicious activity, stops accidental disabling of CloudTrail or GuardDuty during troubleshooting, maintains immutable audit trails required for compliance investigations, and protects log retention policies from modification. Even account administrators cannot delete or disable security logging with this SCP in place.
{ "Version": "2012-10-17", "Statement": [ { "Sid": "ProtectCloudTrailLogs", "Effect": "Deny", "Action": [ "cloudtrail:StopLogging", "cloudtrail:DeleteTrail", "cloudtrail:UpdateTrail", "cloudtrail:PutEventSelectors" ], "Resource": "*" }, { "Sid": "ProtectLogArchiveBuckets", "Effect": "Deny", "Action": [ "s3:DeleteBucket", "s3:DeleteBucketPolicy", "s3:PutBucketPolicy", "s3:PutLifecycleConfiguration" ], "Resource": "arn:aws:s3:::*-cloudtrail-logs-*" }, { "Sid": "ProtectGuardDuty", "Effect": "Deny", "Action": [ "guardduty:DeleteDetector", "guardduty:DisassociateFromMasterAccount", "guardduty:DisassociateMembers", "guardduty:StopMonitoringMembers", "guardduty:UpdateDetector" ], "Resource": "*" } ] } What this prevents: Even account administrators cannot disable CloudTrail, delete audit logs, modify retention policies, or turn off GuardDuty. Your security foundation remains immutable. Attackers cannot erase evidence. Developers cannot accidentally disable security monitoring to troubleshoot an issue.
5. Restrict security accounts to security operations only
When to use this: In a Security OU containing your Log Archive, Audit, Security Tooling, and potentially Forensics accounts (as shown in the mid-size and enterprise patterns), apply this SCP to prevent scope creep where these accounts accidentally become application hosts. Security accounts should only run security-related services (GuardDuty, Security Hub, CloudTrail) and audit tools, never workloads like EC2 instances, RDS databases, or Lambda functions processing business data.
Why this matters: Prevents security accounts from becoming application hosts, maintains separation of duties for compliance audits, protects audit integrity by ensuring security accounts remain isolated from business workloads, and stops convenience deployments that violate your multi-account architecture principles.
{ "Version": "2012-10-17", "Statement": [ { "Sid": "DenyWorkloadDeployment", "Effect": "Deny", "Action": [ "ec2:RunInstances", "rds:CreateDBInstance", "lambda:CreateFunction", "ecs:CreateCluster", "eks:CreateCluster" ], "Resource": "*" } ] } What this prevents: Security accounts cannot run workloads. They remain dedicated to security functions, maintaining the integrity of your audit trail and security tooling. This separation ensures security accounts never become attack targets for production data access.
Production OU SCPs: Strict Change Control and Protection
Production accounts require the most restrictive policies. These SCPs prevent accidental destruction while enforcing encryption and compliance requirements.
6. Enforce encryption on all production data
When to use this: In a Production OU containing your production application accounts and production data accounts, apply this SCP to enforce encryption at rest for all data storage services. Whether you're following the startup pattern with a single Production Account or the enterprise pattern with region-specific production accounts, encryption enforcement is non-negotiable for production data.
Why this matters: Prevents creation of unencrypted S3 buckets, EBS volumes, and RDS instances that violate compliance requirements (SOC2, HIPAA, PCI-DSS), makes encryption failures impossible rather than requiring detection and remediation, eliminates the risk of accidentally storing sensitive data unencrypted, and enforces security best practices at the infrastructure level.
{ "Version": "2012-10-17", "Statement": [ { "Sid": "DenyUnencryptedS3Buckets", "Effect": "Deny", "Action": "s3:CreateBucket", "Resource": "*", "Condition": { "StringNotEquals": { "s3:x-amz-server-side-encryption": "AES256" } } }, { "Sid": "DenyUnencryptedEBSVolumes", "Effect": "Deny", "Action": "ec2:RunInstances", "Resource": "arn:aws:ec2:*:*:volume/*", "Condition": { "Bool": { "ec2:Encrypted": "false" } } }, { "Sid": "DenyUnencryptedRDSInstances", "Effect": "Deny", "Action": "rds:CreateDBInstance", "Resource": "*", "Condition": { "Bool": { "rds:StorageEncrypted": "false" } } } ] } What this prevents: All S3 buckets, EBS volumes, and RDS instances must use encryption. No exceptions. Developers cannot create unencrypted resources even by accident. Compliance violations become technically impossible rather than policy violations requiring detection and remediation.
7. Prevent unauthorized resource termination
When to use this: In a Production OU containing your production application accounts, apply this SCP to require explicit role assumption before any destructive operations. This adds a critical human checkpoint before deletion of databases, EC2 instances, or DynamoDB tables. Production accounts (whether you have a single production account in the startup pattern or region-specific production accounts in the enterprise pattern) need protection from accidental destruction.
Why this matters: Prevents accidental termination of production resources during fatigued late-night deployments, blocks Terraform destroy commands run in the wrong account, creates an audit trail for all destructive operations through required role assumption, and forces deliberate action before deleting critical infrastructure. This SCP has prevented countless production outages caused by simple human error.
{ "Version": "2012-10-17", "Statement": [ { "Sid": "RequireApprovalForTermination", "Effect": "Deny", "Action": [ "ec2:TerminateInstances", "rds:DeleteDBInstance", "dynamodb:DeleteTable" ], "Resource": "*", "Condition": { "StringNotLike": { "aws:PrincipalArn": [ "arn:aws:iam::*:role/ApprovedDestructionRole" ] } } } ] } What this prevents: Critical resources cannot be terminated except by specifically authorized roles (typically tied to change management workflows). Even account administrators cannot terminate production databases or instances without assuming the approved destruction role, creating an audit trail and forcing deliberate action.
Development OU SCPs: Cost Controls and Safe Experimentation
Development accounts need flexibility but not unlimited spending. These SCPs enable innovation while preventing expensive mistakes.
8. Prevent expensive instance types and services
When to use this: In a Development OU containing team-specific development accounts or a Staging OU with pre-production testing accounts, apply this SCP to enable innovation while preventing cost explosions. As outlined in the mid-size architecture pattern, development accounts need flexibility for experimentation, but developers don't need access to expensive GPU instances, high-memory machines, or the ability to purchase Reserved Instances.
Why this matters: Prevents launching expensive GPU or high-memory instances that rack up costs in non-production environments, blocks forgotten development resources from running up large bills, ensures Reserved Instance and Savings Plans purchases are centrally managed by FinOps teams, and keeps development costs predictable while maintaining sufficient resources for testing.
{ "Version": "2012-10-17", "Statement": [ { "Sid": "DenyExpensiveEC2Instances", "Effect": "Deny", "Action": "ec2:RunInstances", "Resource": "arn:aws:ec2:*:*:instance/*", "Condition": { "StringNotLike": { "ec2:InstanceType": [ "t3.*", "t3a.*", "t2.*", "m5.large", "m5.xlarge" ] } } }, { "Sid": "DenyExpensiveRDSInstances", "Effect": "Deny", "Action": "rds:CreateDBInstance", "Resource": "*", "Condition": { "StringNotLike": { "rds:DatabaseClass": [ "db.t3.*", "db.t2.*" ] } } }, { "Sid": "DenyReservedPurchases", "Effect": "Deny", "Action": [ "ec2:PurchaseReservedInstancesOffering", "rds:PurchaseReservedDBInstancesOffering", "dynamodb:PurchaseReservedCapacityOfferings" ], "Resource": "*" } ] } What this prevents: Developers can only launch cost-effective instance types (t3 family and small m5 instances). They cannot launch GPU instances, high-memory instances, or other expensive compute resources. They cannot purchase Reserved Instances (which should be centrally managed). Development costs stay predictable and reasonable.
Sandbox OU SCPs: Aggressive Spending Limits
Sandbox accounts are for experimentation and learning. They need the most aggressive cost controls because they serve temporary, exploratory purposes.
9. Restrict sandbox accounts to basic services with spending caps
When to use this: In a Sandbox OU containing individual developer sandbox accounts (typically one per engineer), apply this SCP to create safe, isolated experimentation environments with aggressive cost controls. Following the enterprise sandbox pattern, sandbox accounts serve temporary, exploratory purposes and need the strictest spending limits. Each sandbox should be limited to $100-500 per month and auto-terminate after 30-90 days of inactivity.
Why this matters: Limits sandbox spending to basic, low-cost services only, prevents access to expensive services like SageMaker, EMR, or Redshift that aren't needed for learning, maintains complete isolation by blocking network connections to production or shared services, and keeps aggregate sandbox costs manageable across large engineering organizations. Combined with AWS Budgets, this prevents sandbox sprawl from becoming a significant cost center.
{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowOnlyBasicServices", "Effect": "Deny", "NotAction": [ "ec2:*", "s3:*", "lambda:*", "dynamodb:*", "rds:*", "cloudwatch:*", "logs:*", "iam:*", "sts:*" ], "Resource": "*" }, { "Sid": "DenyExpensiveInstancesInSandbox", "Effect": "Deny", "Action": "ec2:RunInstances", "Resource": "arn:aws:ec2:*:*:instance/*", "Condition": { "StringNotLike": { "ec2:InstanceType": [ "t2.micro", "t2.small", "t3.micro", "t3.small" ] } } }, { "Sid": "DenyNetworkConnectivity", "Effect": "Deny", "Action": [ "ec2:CreateVpcPeeringConnection", "ec2:AcceptVpcPeeringConnection", "directconnect:*", "globalaccelerator:*" ], "Resource": "*" } ] } What this prevents: Sandbox accounts can only use basic services (EC2, S3, Lambda, RDS) and only tiny instance types. They cannot access expensive services like SageMaker, EMR, or Redshift. They cannot create network connections to production or shared services, maintaining complete isolation. Combined with AWS Budgets set to $100-500 per month, this creates true sandboxes: safe, isolated, cheap experimentation environments.
Infrastructure OU SCPs: Service-Specific Restrictions
Infrastructure accounts (Network, Shared Services, CI/CD) should only perform their designated functions. These SCPs prevent scope creep.
10. Restrict Network accounts to networking operations only
When to use this: In an Infrastructure OU containing specialized infrastructure accounts like your Network Hub Account, Network Production Account, and Network Development Account (as defined in the enterprise infrastructure pattern), apply this SCP to prevent these accounts from becoming application hosts. Network accounts should only manage VPCs, Transit Gateways, Direct Connect, Route53, and network firewall rules, never compute, storage, or application resources.
Why this matters: Enforces strict separation of duties by preventing compute or application deployments in network accounts, maintains clear account boundaries for compliance audits, stops convenience deployments that violate architectural principles, and ensures network accounts remain dedicated to networking functions only. This prevents infrastructure accounts from accidentally becoming mixed-purpose accounts that complicate security and compliance.
{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowOnlyNetworkingServices", "Effect": "Deny", "NotAction": [ "ec2:*Vpc*", "ec2:*Subnet*", "ec2:*Gateway*", "ec2:*Route*", "ec2:*NetworkAcl*", "ec2:*SecurityGroup*", "ec2:*TransitGateway*", "ec2:Describe*", "directconnect:*", "route53:*", "route53resolver:*", "networkfirewall:*", "cloudwatch:*", "logs:*", "iam:*", "sts:*" ], "Resource": "*" }, { "Sid": "DenyComputeAndStorage", "Effect": "Deny", "Action": [ "ec2:RunInstances", "rds:*", "s3:CreateBucket", "lambda:*", "ecs:*", "eks:*" ], "Resource": "*" } ] } What this prevents: Network accounts can only manage networking resources (VPCs, Transit Gateways, Route53, Direct Connect, Network Firewall). They cannot launch compute instances, create databases, or deploy applications. This enforces strict separation: networking infrastructure remains purely infrastructure, never becoming an application hosting platform.
Implementation Strategy: Rolling Out SCPs Safely
Don't apply these SCPs all at once across your entire organization. That's a recipe for breaking production systems and creating chaos.
Phase 1: Test in Development (Week 1-2)
- Apply SCPs to a single development account
- Monitor CloudTrail for denied actions
- Identify legitimate use cases that need exceptions
- Refine policies based on findings
Phase 2: Expand to Non-Production (Week 3-4)
- Apply SCPs to Development OU and Sandbox OU
- Communicate changes to development teams
- Provide 1-week notice before enforcement
- Document exceptions and approval processes
Phase 3: Protect Production (Week 5-6)
- Apply SCPs to Production OU during maintenance window
- Start with least restrictive policies (root user denial, region restrictions)
- Gradually add encryption enforcement and resource protection
- Monitor for legitimate denials and adjust
Phase 4: Lock Down Security and Infrastructure (Week 7-8)
- Apply strictest SCPs to Security OU and Infrastructure OU
- These should rarely deny legitimate actions (they're specialized accounts)
- Monitor for 2 weeks to ensure no operational impact
Conclusion
The AWS SCP examples provided in this article will help to better secure the multiple environments that you're managing within your AWS Organization from a central point.
These policies represent real-world lessons learned from security incidents, cost overruns, and compliance failures across dozens of organizations. Each SCP prevents a specific category of disaster: data breaches, accidental deletions, cost explosions, compliance violations, or operational chaos.
The key insight: SCPs are preventive controls, not detective controls. They stop mistakes before they happen rather than alerting you after damage is done. A developer cannot accidentally delete production databases if an SCP prevents it. An attacker cannot expand into unsupported regions if an SCP blocks it. A data scientist cannot launch $30,000 GPU instances in development if an SCP denies it.
Using the deny list strategy to block access to specific AWS Services or regions can have a huge impact on the teams and applications that are running on your AWS accounts.
Therefore it's important to test a new AWS SCP on the development or test account before proceeding to activate the permission policy on production.
Start with foundation SCPs (root user denial, region restrictions, encryption enforcement). Expand to environment-specific policies (production protection, development cost controls, sandbox restrictions). Finish with infrastructure lockdown (security account immutability, network account restrictions).
The structure presented here aligns with the multi-account strategy patterns we've detailed previously. As your organization grows from startup to mid-size to enterprise, your SCP strategy should evolve with it, adding guardrails that match your increasing complexity and risk exposure.
title="Get Production-Ready, SOC 2 Compliant AWS Accounts from Day One"
subtitle="We deploy our AWS Landing Zone (using infrastructure as code) with pre-configured multi-account architecture, built-in security controls and guardrails including monitoring to stay in control of what happens so you can safely start deploying workloads immediately."
href="/services/aws-landing-zone"
buttonText="Learn More About Our Landing Zone"
/>
Written by Danny, Founder @ towardsthecloud → Helping startups cut costs and ship faster on AWS.
FYI; I'm also building cloudburn.io → Help developers catch expensive infra in PR's before they deploy on AWS Cloud.
Top comments (0)