AWS Organizations with SCPs for Preventive Security Guardrails
In the vast and dynamic landscape of cloud computing, managing security across multiple AWS accounts is not just a best practice; it's an imperative. As organizations scale their cloud footprint, the complexity of governance and compliance grows exponentially. This is where AWS Organizations, specifically its Service Control Policies (SCPs), emerge as a powerful, non-negotiable tool for establishing robust, preventive security guardrails. As Sujay Singh, a senior technology writer at TechNews Venture, I'm here to demystify SCPs and guide you through leveraging them to fortify your AWS environment.
Overview: The Imperative of Preventive Security with AWS Organizations and SCPs
AWS Organizations allows you to centrally manage and govern your environment as you grow and scale your AWS resources. It provides programmatic creation of new AWS accounts, grouping accounts into Organizational Units (OUs), and applying policies to those accounts or OUs. While AWS Identity and Access Management (IAM) policies are crucial for defining permissions within an individual account, they are reactive and apply only to IAM identities (users, roles) or resources within that specific account. This is where Service Control Policies (SCPs) step in as a game-changer.
SCPs are a type of organization policy that you can use to manage permissions in your organization. They offer centralized control over the maximum available permissions for all accounts in your organization, or for specific OUs. Unlike IAM policies, SCPs do not grant permissions; instead, they define permission guardrails by specifying the maximum permissions that an IAM user or role can have. If an action is explicitly denied by an SCP, no IAM policy, regardless of how permissive it is, can override that denial. This "deny by default" enforcement mechanism is what makes SCPs an incredibly powerful tool for preventive security.
The primary benefit of SCPs is their ability to enforce compliance and security standards across an entire organization, preventing actions that could lead to security breaches, operational issues, or compliance violations. For instance, you can use SCPs to restrict the AWS regions where resources can be provisioned, ensuring data residency requirements are met. You can prevent member accounts from deleting CloudTrail logs, ensuring auditability. You can even restrict the use of certain high-cost services in development or sandbox accounts. By setting these guardrails at the organizational level, you drastically reduce the blast radius of potential misconfigurations or malicious activities within individual accounts, ensuring a more secure and compliant cloud posture.
In essence, SCPs are your organization's constitution for AWS. They dictate what is permissible across all member accounts, providing a top-down, proactive security layer that complements and strengthens your existing IAM strategies. Let's delve into how to implement these critical guardrails.
Prerequisites
Before we embark on configuring SCPs, ensure you have the following in place:
- An Existing AWS Organization: You must have an AWS Organization set up and configured. If you're starting fresh, the management account (formerly master account) is where you'll initiate this process.
- Management Account Access: You need credentials for an IAM user or role in the management account with sufficient permissions to manage AWS Organizations (e.g., `organizations:*` actions). While the root user can perform these actions, it's always recommended to use an IAM role with least privilege.
- AWS CLI Configured: The AWS Command Line Interface (CLI) should be installed and configured with credentials pointing to your management account.
- Basic Understanding of IAM Policies: Familiarity with JSON policy syntax, effects (Allow/Deny), actions, and resources is beneficial.
- Organizational Unit (OU) Strategy: A conceptual plan for how you want to group your AWS accounts (e.g., by environment like Dev, Prod, Sandbox; or by department like Finance, Engineering).
Step-by-step Implementation: Building Your Security Guardrails
Step 1: Enable Service Control Policies (SCPs)
By default, SCPs are not enabled in new AWS Organizations. You must explicitly enable them. When you enable SCPs, AWS automatically attaches a default `FullAWSAccess` SCP to the root of your organization, which allows all services and actions. This ensures that existing permissions are not immediately disrupted. We will then create and attach our custom SCPs to override or narrow these permissions.
To enable SCPs via the AWS CLI:
aws organizations enable-policy-type \
--root-id r-xxxx \
--policy-type SERVICE_CONTROL_POLICY
Replace
r-xxxxwith the actual ID of your organization's root. You can find this by runningaws organizations list-roots.
Step 2: Structure Your Organization with Organizational Units (OUs)
A well-designed OU structure is fundamental for effective SCP application. It allows you to apply different sets of guardrails to different groups of accounts, reflecting varying security and compliance requirements.
Let's create a few OUs for a typical enterprise setup:
ou-production: For production workloads.ou-development: For development and testing environments.ou-security: For security tools and logging accounts.ou-sandbox: For experimental and non-critical work.
Create the OUs:
# Get the root ID first
ROOT_ID=$(aws organizations list-roots --query 'Roots[0].Id' --output text)
# Create Production OU
aws organizations create-organizational-unit \
--parent-id $ROOT_ID \
--name "Production"
# Create Development OU
aws organizations create-organizational-unit \
--parent-id $ROOT_ID \
--name "Development"
# Create Security OU
aws organizations create-organizational-unit \
--parent-id $ROOT_ID \
--name "Security"
# Create Sandbox OU
aws organizations create-organizational-unit \
--parent-id $ROOT_ID \
--name "Sandbox"
After creating OUs, you would move your member accounts into the appropriate OUs. For example, moving an account with ID `111122223333` into the `Development` OU:
# Get the ID of the Development OU (assuming you've already created it)
DEV_OU_ID=$(aws organizations list-organizational-units \
--parent-id $ROOT_ID \
--query 'OrganizationalUnits[?Name==`Development`].Id' --output text)
# Move account 111122223333 to the Development OU
aws organizations move-account \
--account-id 111122223333 \
--source-parent-id $ROOT_ID \
--destination-parent-id $DEV_OU_ID
Remember to replace
111122223333with your actual member account ID and verify OU IDs.
Step 3: Crafting and Attaching Service Control Policies (SCPs)
Now, let's create some practical SCPs to enforce security guardrails.
SCP Example 1: Deny Access to Specific AWS Regions
This SCP prevents any action in specified regions, often used for data residency compliance or cost control.
Rationale: Ensure all resources are provisioned in approved regions (e.g., `us-east-1`, `eu-west-1`) and prevent accidental deployment to unapproved regions (e.g., `ap-southeast-2`).
Create a JSON file named `deny-unapproved-regions.json`:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyRegions",
"Effect": "Deny",
"NotAction": [
"a4b:Get*",
"aws-marketplace:*",
"aws-portal:*",
"budgets:*",
"ce:*",
"chime:*",
"cloudtrail:*",
"config:*",
"directconnect:*",
"ec2:DescribeRegions",
"ec2:DescribeAvailabilityZones",
"ec2:DescribeVpcs",
"ec2:DescribeSubnets",
"fms:*",
"globalaccelerator:*",
"health:*",
"iam:*",
"importexport:*",
"kms:*",
"organizations:*",
"pricing:*",
"route53:*",
"s3:GetAccountPublicAccessBlock",
"s3:GetBucketLocation",
"s3:ListAllMyBuckets",
"shield:*",
"sts:*",
"support:*",
"trustedadvisor:*"
],
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:RequestedRegion": [
"us-east-1",
"eu-west-1"
]
}
}
}
]
}
Note: The
NotActionlist includes services that are global or need to be accessible from any region (like IAM, Organizations, CloudTrail, S3 bucket locations, etc.) to prevent accidental lockouts. Customize the allowed regions inStringNotEqualsas per your requirements.
Create the SCP:
aws organizations create-policy \
--content file://deny-unapproved-regions.json \
--name "Deny-Unapproved-Regions" \
--description "Denies access to all AWS regions except us-east-1 and eu-west-1" \
--type SERVICE_CONTROL_POLICY
Attach the SCP to the `Root` or specific OUs (e.g., `ou-production`, `ou-development`). Attaching to the `Root` will apply it to all accounts.
# Get the policy ID
POLICY_ID=$(aws organizations list-policies \
--filter SERVICE_CONTROL_POLICY \
--query 'Policies[?Name==`Deny-Unapproved-Regions`].Id' --output text)
# Attach to the Root
aws organizations attach-policy \
--policy-id $POLICY_ID \
--target-id $ROOT_ID
SCP Example 2: Deny Root User Access for Member Accounts (Except for Specific Actions)
Rationale: The root user possesses ultimate power and should be used sparingly. This SCP restricts root user actions in member accounts, forcing the use of IAM roles with least privilege.
Create a JSON file named `deny-root-user-access.json`:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyRootUserAccess",
"Effect": "Deny",
"Action": "*",
"Resource": "*",
"Condition": {
"ArnEquals": {
"aws:PrincipalArn": "arn:aws:iam::*:root"
},
"ForAllValues:StringNotLike": {
"aws:PrincipalArn": [
"arn:aws:iam::123456789012:root"
]
},
"StringNotLike": {
"aws:CalledVia": [
"organizations.amazonaws.com"
]
},
"ArnNotLike": {
"aws:PrincipalArn": [
"arn:aws:iam::*:user/aws-organization-access"
]
},
"NotAction": [
"account:EnableRegion",
"account:Get*",
"account:List*",
"account:DisableRegion",
"iam:CreateServiceLinkedRole",
"s3:GetAccountPublicAccessBlock",
"s3:PutAccountPublicAccessBlock",
"s3:DeleteAccountPublicAccessBlock",
"sns:Publish",
"support:*"
]
}
}
]
}
This policy denies all actions for the root user in member accounts, except for a few necessary ones like managing account settings, creating service-linked roles, and S3 Public Access Block settings. The `aws:PrincipalArn` condition filters for the root user. The
ForAllValues:StringNotLikecondition allows you to exempt the management account's root user (replace123456789012with your management account ID). Theaws:CalledViacondition handles actions initiated by Organizations itself. TheArnNotLikeforaws-organization-accessis often used in scenarios where AWS Organizations itself needs to perform actions.
Create the SCP:
aws organizations create-policy \
--content file://deny-root-user-access.json \
--name "Deny-Root-User-Access" \
--description "Denies all actions for root user in member accounts except for critical ones" \
--type SERVICE_CONTROL_POLICY
Attach the SCP to the `Root` or specific OUs:
# Get the policy ID
POLICY_ID=$(aws organizations list-policies \
--filter SERVICE_CONTROL_POLICY \
--query 'Policies[?Name==`Deny-Root-User-Access`].Id' --output text)
# Attach to the Root
aws organizations attach-policy \
--policy-id $POLICY_ID \
--target-id $ROOT_ID
SCP Example 3: Prevent Accounts from Leaving the Organization
Rationale: Maintain centralized governance and prevent accounts from unilaterally exiting the organization, which could lead to loss of control and compliance issues.
Create a JSON file named `prevent-leave-organization.json`:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyLeaveOrganization",
"Effect": "Deny",
"Action": [
"organizations:LeaveOrganization"
],
"Resource": "*"
}
]
}
Create the SCP:
aws organizations create-policy \
--content file://prevent-leave-organization.json \
--name "Prevent-Leave-Organization" \
--description "Prevents member accounts from leaving the organization" \
--type SERVICE_CONTROL_POLICY
Attach the SCP to the `Root`:
# Get the policy ID
POLICY_ID=$(aws organizations list-policies \
--filter SERVICE_CONTROL_POLICY \
--query 'Policies[?Name==`Prevent-Leave-Organization`].Id' --output text)
# Attach to the Root
aws organizations attach-policy \
--policy-id $POLICY_ID \
--target-id $ROOT_ID
SCP Example 4: Restrict EC2 Instance Types in Development/Sandbox OUs
Rationale: Control costs by preventing the use of expensive or high-performance instance types (e.g., GPU instances, large memory instances) in non-production environments.
Create a JSON file named `restrict-ec2-instance-types.json`:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "RestrictEC2InstanceTypes",
"Effect": "Deny",
"Action": [
"ec2:RunInstances"
],
"Resource": [
"arn:aws:ec2:*:*:instance/*"
],
"Condition": {
"ForAnyValue:StringLike": {
"ec2:InstanceType": [
"p2.*",
"p3.*",
"g3.*",
"g4dn.*",
"g5.*",
"x1.*",
"x1e.*",
"u-*"
]
}
}
}
]
}
This policy denies the launch of specific instance families commonly associated with GPU acceleration or extremely large memory. Adjust the
ec2:InstanceTypelist as needed.
Create the SCP:
aws organizations create-policy \
--content file://restrict-ec2-instance-types.json \
--name "Restrict-EC2-Instance-Types-Dev" \
--description "Denies launching of expensive EC2 instance types in dev/sandbox" \
--type SERVICE_CONTROL_POLICY
Attach the SCP to the `Development` and `Sandbox` OUs (not to Production):
# Get the policy ID
POLICY_ID=$(aws organizations list-policies \
--filter SERVICE_CONTROL_POLICY \
--query 'Policies[?Name==`Restrict-EC2-Instance-Types-Dev`].Id' --output text)
# Get the ID of the Development OU
DEV_OU_ID=$(aws organizations list-organizational-units \
--parent-id $ROOT_ID \
--query 'OrganizationalUnits[?Name==`Development`].Id' --output text)
# Get the ID of the Sandbox OU
SANDBOX_OU_ID=$(aws organizations list-organizational-units \
--parent-id $ROOT_ID \
--query 'OrganizationalUnits[?Name==`Sandbox`].Id' --output text)
# Attach to Development OU
aws organizations attach-policy \
--policy-id $POLICY_ID \
--target-id $DEV_OU_ID
# Attach to Sandbox OU
aws organizations attach-policy \
--policy-id $POLICY_ID \
--target-id $SANDBOX_OU_ID
Step 4: Testing SCPs
After attaching SCPs, it's crucial to test their effectiveness. Log into a member account affected by the SCP (e.g., a development account for region restriction) and attempt to perform an action that should be denied. For instance:
- Try to launch an EC2 instance in a forbidden region (e.g., `ap-southeast-2`) if the region restriction SCP is active.
- Try to launch a `p2.xlarge` instance in a development account if the instance type restriction SCP is active.
- For the root user restriction, try to perform an IAM action as the root user in a member account.
You should receive an "Access Denied" error message. CloudTrail logs in the affected account will also show the denied API call, indicating that an explicit deny from an SCP prevented the action.
Step 5: Listing and Detaching SCPs
To view all policies attached to a target (root, OU, or account):
aws organizations list-policies-for-target \
--target-id $DEV_OU_ID \
--filter SERVICE_CONTROL_POLICY
To detach a policy:
aws organizations detach-policy \
--policy-id $POLICY_ID \
--target-id $DEV_OU_ID
Security Considerations
- SCPs and the Management Account Root User: SCPs do NOT restrict the root user of the management account. They only restrict the root user of member accounts. This is a critical distinction. The management account's root user retains full control and should be secured with extreme vigilance (MFA, strong password, minimal use).
- Inheritance Model: SCPs are inherited down the OU hierarchy. An explicit deny at a higher level (e.g., Root or a parent OU) overrides any allow at a lower level or within an IAM policy in a member account. This "deny trumps allow" principle is fundamental to SCPs.
- Default FullAWSAccess Policy: When you enable SCPs, AWS attaches a `FullAWSAccess` policy to the Root. This policy allows all services and actions. Your custom SCPs effectively narrow or override this default. If you detach `FullAWSAccess` from any target, all permissions will be implicitly denied for that target unless other SCPs explicitly allow them. Exercise extreme caution.
- Thorough Testing: Always test new SCPs in isolated, non-production OUs and accounts before deploying them broadly. An overly restrictive SCP can lead to widespread service disruptions.
- Least Privilege for SCP Management: Restrict who can create, attach, detach, or delete SCPs in your management account. These actions are highly privileged and can significantly impact your entire AWS environment.
- CloudTrail for Auditing: While SCPs prevent actions, CloudTrail logs will show attempts to perform denied actions, providing valuable audit trails.
Best Practices
- Design Your OU Structure Carefully: Plan your OUs to reflect your organization's structure, security zones (e.g., highly sensitive, public-facing), and environments (e.g., production, development, sandbox). This is the foundation for effective SCP application.
- Use Deny Lists Primarily: SCPs are most effective when used to explicitly deny actions. For granting permissions, rely on IAM policies within individual accounts. This separation of concerns simplifies management.
- Start Small and Iterate: Begin with a few critical SCPs (e.g., region restrictions, root user access restrictions) and gradually add more as you gain confidence and understanding.
- Granular Control at Higher Levels: Apply SCPs at the highest possible level (Root or parent OU) to enforce broad guardrails, then use more specific SCPs at lower OUs for targeted restrictions.
- Document Everything: Maintain clear documentation for each SCP, including its purpose, target OUs/accounts, and potential impact.
- Automate SCP Management: Use Infrastructure as Code (IaC) tools like AWS CloudFormation or Terraform to manage your SCPs. This ensures version control, consistency, and easier rollbacks.
- Regular Review: Periodically review your SCPs to ensure they remain relevant, effective, and don't inadvertently block new legitimate services or features.
- Monitor for Policy Violations: Set up CloudWatch alarms on CloudTrail logs to detect and alert on API calls that are denied by SCPs, indicating attempts to bypass guardrails or misconfigurations.
FAQ
-
Can SCPs restrict the root user of the management account?
No. SCPs do not apply to the root user of the AWS Organizations management account. The management account's root user has full administrative privileges and is not subject to any SCPs. SCPs only restrict the root users of member accounts within the organization.
-
How do SCPs interact with IAM policies?
SCPs act as a hard filter on permissions. They define the maximum available permissions for any IAM user or role within an affected account. An IAM policy can only grant permissions that are *also allowed* by the applicable SCPs. If an SCP explicitly denies an action, no IAM policy in the member account can override that denial, even if the IAM policy explicitly allows the action.
-
What is the best way to test SCPs without impacting production workloads?
The recommended approach is to create a dedicated "Sandbox" or "Testing" Organizational Unit (OU) within your AWS Organization. Move one or more non-production accounts into this OU. Apply your new SCPs to this testing OU first, and then thoroughly test the intended deny actions within those accounts. This allows you to validate the SCPs' behavior before rolling them out to production environments or broader OUs.
Conclusion
AWS Organizations with Service Control Policies are an indispensable component of a mature cloud security posture. They provide the foundational, preventive guardrails necessary to enforce compliance, mitigate risks, and maintain control across a growing multi-account AWS environment. By understanding their power, carefully structuring your OUs, and meticulously crafting your SCPs, you can proactively prevent undesirable actions, reduce the potential for human error, and ensure your organization adheres to its security and governance standards from the top down.
Embracing SCPs is not merely an option; it's a strategic investment in the long-term security, efficiency, and compliance of your cloud operations. Start small, test diligently, and continuously refine your policies, and you'll build an AWS environment that is resilient, secure, and ready for future growth.