Overview: Fortifying Your AWS Landscape with Preventive Security Guardrails
In today's dynamic cloud environment, organizations are increasingly adopting multi-account strategies on AWS to achieve isolation, improve security, streamline billing, and manage resource quotas more effectively. While a multi-account structure offers significant benefits, it also introduces complexity in governance and security. How do you ensure that all your development teams, security engineers, and operations personnel adhere to a consistent set of security policies across potentially dozens or even hundreds of AWS accounts? This is where AWS Organizations, specifically with the power of Service Control Policies (SCPs), comes into play, providing a robust framework for preventive security guardrails. AWS Organizations is a service that allows you to centrally manage and govern multiple AWS accounts. It enables you to consolidate billing, manage users, and enforce policies across your entire AWS environment. The core components of AWS Organizations include:- Management Account (Payer Account): The primary account that manages all other accounts in the organization. It handles consolidated billing and organizational policy enforcement.
- Organizational Units (OUs): Logical groupings of AWS accounts. OUs allow you to organize your accounts into a hierarchy that reflects your business structure or security requirements, making it easier to apply policies to groups of accounts.
- Member Accounts: All other AWS accounts within the organization, managed by the management account.
Prerequisites
Before diving into the implementation of AWS Organizations and SCPs, ensure you have the following prerequisites in place:- An AWS Account Designated as the Management Account: This account will be the central point of control for your AWS Organization. It should be a dedicated account, ideally without any production workloads, to minimize its blast radius.
- AWS Organizations Enabled with All Features: Your AWS Organization must be configured to enable all features, not just consolidated billing. This is essential for applying SCPs. If not already enabled, you can do so via the AWS Management Console or AWS CLI.
- Basic Understanding of AWS IAM Policies and JSON Syntax: SCPs are written in JSON, similar to IAM policies. A foundational understanding of IAM policy structure (Effect, Action, Resource, Condition) will be beneficial.
- AWS CLI Configured: The AWS Command Line Interface (CLI) should be installed and configured on your local machine or a secure management instance with appropriate credentials for the management account. Ensure these credentials have permissions to manage AWS Organizations (e.g., `OrganizationsFullAccess` or a custom policy with granular permissions).
aws configure AWS Access Key ID [****************ABCD]: AKIAIOSFODNN7EXAMPLE AWS Secret Access Key [****************ABCD]: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY Default region name [us-east-1]: us-east-1 Default output format [json]: json - Clear Organizational Structure Plan: Before creating OUs and accounts, map out your desired organizational structure. Consider how you want to group accounts based on environment (e.g., Production, Development, Sandbox), business unit (e.g., Finance, Engineering), or shared services (e.g., Networking, Security Tooling).
Step-by-Step Implementation
Let's walk through the process of setting up AWS Organizations and implementing SCPs to establish robust security guardrails.1. Enable AWS Organizations (if not already)
If your AWS accounts are only set up for consolidated billing, you'll need to enable all features to use SCPs. This action must be performed from the management account.aws organizations enable-all-features
This command will initiate the process to enable all features. You might need to confirm the action if prompted.
2. Create Organizational Units (OUs)
A well-designed OU structure is fundamental for effective policy application. Let's create a hierarchical structure. First, list the root ID of your organization:aws organizations list-roots
The output will look something like this:
{
"Roots": [
{
"Id": "r-abcd",
"Arn": "arn:aws:organizations::123456789012:root/o-exampleorgid/r-abcd",
"Name": "Root",
"PolicyTypes": [
{
"Type": "SERVICE_CONTROL_POLICY",
"Status": "ENABLED"
},
{
"Type": "TAG_POLICY",
"Status": "ENABLED"
}
]
}
]
}
Note down the `Id` of the Root (e.g., `r-abcd`). Now, let's create some OUs:
# Create a top-level 'Workloads' OU
aws organizations create-organizational-unit --parent-id r-abcd --name Workloads
# Assuming the 'Workloads' OU ID is ou-abcd-efgh1234 (you'd list OUs to get this)
# aws organizations list-organizational-units-for-parent --parent-id r-abcd
# Create 'Production' and 'Development' OUs under 'Workloads'
aws organizations create-organizational-unit --parent-id ou-abcd-efgh1234 --name Production
aws organizations create-organizational-unit --parent-id ou-abcd-efgh1234 --name Development
# Create a 'Security' OU directly under Root for security tooling accounts
aws organizations create-organizational-unit --parent-id r-abcd --name Security
# Create a 'Sandbox' OU for experimentation
aws organizations create-organizational-unit --parent-id r-abcd --name Sandbox
Remember to replace `r-abcd` and `ou-abcd-efgh1234` with your actual Root and OU IDs, which you can retrieve using `aws organizations list-organizational-units-for-parent --parent-id 3. Create New AWS Accounts and Move Existing Ones
For new accounts, you can create them directly within your Organization:aws organizations create-account \
--email "dev-account-01@example.com" \
--name "Development Account 01" \
--role-name AWSOrganizationAccess \
--tags Key=Environment,Value=Development Key=Project,Value=WebApp
# Once created, move it to the appropriate OU (e.g., 'Development' OU)
# You'll need the account ID from the create-account output or by listing accounts.
# Let's assume the new account ID is 987654321098 and the Development OU ID is ou-ijkl-mnop5678.
aws organizations move-account \
--account-id 987654321098 \
--source-parent-id r-abcd \
--destination-parent-id ou-ijkl-mnop5678
The `role-name AWSOrganizationAccess` creates an IAM role in the new account that allows the management account to assume it for administrative tasks.
To move existing accounts that have been invited and accepted into your organization:
# Assuming account 111122223333 is currently under Root (r-abcd)
# and you want to move it to the 'Production' OU (ou-qrst-uvwx9012)
aws organizations move-account \
--account-id 111122223333 \
--source-parent-id r-abcd \
--destination-parent-id ou-qrst-uvwx9012
4. Design and Create Service Control Policies (SCPs)
SCPs are the core of your preventive guardrails. They are JSON policies that you attach to the Root, OUs, or individual accounts. Here are some common and highly recommended SCP examples:SCP Example 1: Deny Access to Specific AWS Regions
This SCP prevents any resource creation or modification outside of your approved regions (e.g., `us-east-1`, `eu-west-1`). Create a file named `deny-regions-scp.json`:{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyRegions",
"Effect": "Deny",
"Action": [
"ec2:RunInstances",
"s3:CreateBucket",
"lambda:CreateFunction",
"rds:CreateDBInstance",
"iam:CreateUser"
// Add more service actions as needed
],
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:RequestedRegion": [
"us-east-1",
"eu-west-1"
// Add other allowed regions
]
}
}
},
{
"Sid": "DenyAllExceptAllowedRegions",
"Effect": "Deny",
"NotAction": [
"cloudfront:*",
"iam:*",
"route53:*",
"aws-marketplace:*",
"support:*",
"organizations:*",
"s3:GetAccountPublicAccessBlock",
"s3:GetBucketLocation",
"s3:ListAllMyBuckets",
"s3:ListBucket"
// Add other global services or actions that might not respect region conditions
],
"Resource": "*",
"Condition": {
"StringNotEquals": {
"aws:RequestedRegion": [
"us-east-1",
"eu-west-1"
// Add other allowed regions
]
}
}
}
]
}
This SCP has two statements. The first explicitly denies specific resource-creating actions in unapproved regions. The second is a more aggressive deny-all for any action not explicitly allowed (global services) outside the approved regions. This ensures comprehensive regional restriction.
Now, create the policy and attach it. Let's attach it to the `Workloads` OU (ID `ou-abcd-efgh1234`) so it applies to both `Production` and `Development` accounts.
aws organizations create-policy \
--content file://deny-regions-scp.json \
--name "Deny-Restricted-Regions" \
--type SERVICE_CONTROL_POLICY
# Output will include the Policy ID, e.g., p-examplepolicy.
# Attach the policy to the 'Workloads' OU.
aws organizations attach-policy \
--policy-id p-examplepolicy \
--target-id ou-abcd-efgh1234
SCP Example 2: Deny Root User Access for Most Actions
The root user of an AWS account is extremely powerful. While some actions *must* be performed by the root user (e.g., changing support plan, closing account), most operational tasks should be delegated to IAM users/roles. This SCP restricts the root user's capabilities. Create `deny-root-user-scp.json`:{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyRootUserAccess",
"Effect": "Deny",
"Action": "*",
"Resource": "*",
"Condition": {
"StringLike": {
"aws:PrincipalArn": [
"arn:aws:iam::*:root"
]
},
"ArnNotLike": {
"aws:PrincipalArn": [
"arn:aws:iam::123456789012:root"
// Explicitly allow root in management account if needed for org-specific actions
]
},
"NotAction": [
"account:EnableRegion",
"account:DisableRegion",
"account:GetAccountInformation",
"billing:*",
"budgets:*",
"config:DeleteDeliveryChannel",
"config:DeleteRetentionConfiguration",
"config:StopConfigurationRecorder",
"organizations:*",
"s3:DeleteBucket",
"s3:PutBucketPublicAccessBlock",
"s3:PutAccountPublicAccessBlock",
"s3:DeleteObject",
"s3:DeleteObjectVersion",
"support:*",
"waf-regional:DeleteWebACL",
"waf:DeleteWebACL",
"wafv2:DeleteWebACL",
"cloudfront:DeleteDistribution",
"cloudfront:UpdateDistribution",
"route53:DeleteHostedZone",
"route53:AssociateVPCWithHostedZone",
"route53:DisassociateVPCFromHostedZone",
"ec2:TerminateInstances",
"rds:DeleteDBInstance",
"lambda:DeleteFunction",
"iam:DeleteUser",
"iam:DeleteRole",
"iam:DeleteGroup",
"iam:DeletePolicy",
"iam:DeleteAccessKey",
"iam:DeleteLoginProfile",
"iam:DeactivateMFADevice",
"iam:UpdateAccessKey",
"iam:UpdateLoginProfile",
"iam:UpdateUser",
"iam:UpdateRole",
"iam:UpdateGroup",
"iam:UpdatePolicy"
]
}
}
]
}
This SCP denies *most* actions for the root user. The `NotAction` element lists actions that the root user *is* allowed to perform, such as billing-related tasks, support, or specific destructive actions that are sometimes only permitted by root. **Carefully review the `NotAction` list** to ensure it aligns with your organization's emergency procedures. You might also want to exclude the management account's root user from this policy using `ArnNotLike` if absolutely necessary for Organization management.
Attach this policy to the `Root` (ID `r-abcd`) to apply it to all accounts in the organization, including the management account (except where `ArnNotLike` is used).
aws organizations create-policy \
--content file://deny-root-user-scp.json \
--name "Deny-Root-User-Dangerous-Actions" \
--type SERVICE_CONTROL_POLICY
# Attach to the Root
aws organizations attach-policy \
--policy-id p-anotherpolicyid \
--target-id r-abcd
SCP Example 3: Enforce S3 Public Access Block
To prevent accidental public exposure of S3 buckets, you can enforce the S3 Public Access Block settings at the account level. Create `enforce-s3-public-access-block-scp.json`:{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "EnforceS3PublicAccessBlock",
"Effect": "Deny",
"Action": [
"s3:PutAccountPublicAccessBlock",
"s3:PutBucketPublicAccessBlock"
],
"Resource": "*",
"Condition": {
"BoolEquals": {
"s3:blockPublicAcls": "false"
}
}
},
{
"Sid": "PreventRemovingPublicAccessBlock",
"Effect": "Deny",
"Action": [
"s3:DeleteAccountPublicAccessBlock",
"s3:DeleteBucketPublicAccessBlock"
],
"Resource": "*"
}
]
}
This SCP prevents accounts from disabling the public access block settings or from deleting the public access block configuration entirely. This ensures that the public access block features remain enabled for all S3 buckets in affected accounts.
Attach this to OUs where S3 security is paramount, e.g., `Production` and `Development`.
aws organizations create-policy \
--content file://enforce-s3-public-access-block-scp.json \
--name "Enforce-S3-Public-Access-Block" \
--type SERVICE_CONTROL_POLICY
# Attach to Production OU
aws organizations attach-policy \
--policy-id p-s3policyid \
--target-id ou-qrst-uvwx9012 # Production OU ID
# Attach to Development OU
aws organizations attach-policy \
--policy-id p-s3policyid \
--target-id ou-ijkl-mnop5678 # Development OU ID
5. Testing and Validation
After attaching SCPs, it's crucial to test their effectiveness. 1. **Switch to a Member Account:** Use AWS SSO (recommended) or assume a role into an account that is part of an OU where an SCP is applied. For example, switch to "Development Account 01". 2. **Attempt a Denied Action:** Try to perform an action that should be blocked by one of your SCPs. * **For "Deny-Restricted-Regions":** Try to create an S3 bucket in a disallowed region, e.g., `ap-southeast-2` (Sydney), if it's not in your allowed list.aws s3api create-bucket --bucket my-denied-region-bucket-12345 --region ap-southeast-2 --create-bucket-configuration LocationConstraint=ap-southeast-2
You should receive an error similar to:
An error occurred (AccessDenied) when calling the CreateBucket operation: Access Denied by Service Control Policy.
* **For "Deny-Root-User-Dangerous-Actions":** If you were able to log in as root (which should be restricted), try to perform a disallowed action.
* **For "Enforce-S3-Public-Access-Block":** Try to disable the public access block settings for an S3 bucket.
aws s3control put-public-access-block \
--account-id 987654321098 \
--public-access-block-configuration "BlockPublicAcls=false,IgnorePublicAcls=false,BlockPublicPolicy=false,RestrictPublicBuckets=false"
This should also result in an `AccessDenied` error due to the SCP.
3. **Monitor CloudTrail:** Review CloudTrail logs in the affected account and the management account. Denied actions due to SCPs will be recorded, providing audit trails and helping with troubleshooting. Look for `AccessDenied` events with `errorCode` `Client.UnauthorizedOperation` and `errorMessage` indicating denial by an SCP.
Security Considerations
Implementing SCPs is a powerful security measure, but it comes with significant implications that require careful consideration:- SCPs Are Powerful and Can Cause Outages: A poorly configured SCP can inadvertently lock down critical services or prevent legitimate administrative actions. Always test SCPs thoroughly in non-production environments first.
- Impact on the Management Account's Root User: The root user of the management account is *not* restricted by SCPs applied to OUs or individual accounts *within* the organization, nor by SCPs applied to the root itself. However, SCPs attached to the *Organization Root* (the `r-abcd` parent) *do* apply to the management account's root user. Be extremely cautious when applying SCPs directly to the Organization Root.
- IAM vs. SCPs: Remember the hierarchy: SCPs define the *maximum* permissions. IAM policies *grant* permissions. An action must be allowed by *all* applicable SCPs *and* by an IAM policy to be permitted. If an SCP denies an action, no IAM policy can override that denial.
- Least Privilege for Management Account Users: Users and roles within the management account should adhere strictly to the principle of least privilege. They should only have permissions necessary to manage AWS Organizations (e.g., creating accounts, OUs, and SCPs) and core organizational services. Avoid running production workloads or sensitive applications in the management account.
- Break-Glass Procedures: Establish clear "break-glass" or emergency access procedures. In case an SCP locks down essential functionality, you need a documented process to temporarily detach the problematic SCP or use a highly secured, exempt account (if designed) to restore access.
- Monitoring and Alerting: Implement CloudTrail logging for all AWS accounts and monitor for SCP-related events, especially `AttachPolicy`, `DetachPolicy`, `CreatePolicy`, `UpdatePolicy`, and `DeletePolicy`. Set up alerts for changes to critical SCPs.
- SCPs Do Not Affect Service-Linked Roles: Service-linked roles are predefined by AWS and allow AWS services to access resources in your account on your behalf. SCPs do not restrict the permissions of these roles.
Best Practices
To maximize the effectiveness and minimize the risks associated with SCPs, follow these best practices:- Design a Logical OU Structure: Organize your accounts into OUs that reflect your security zones, compliance requirements, and operational responsibilities. Common patterns include OUs for `Security`, `Shared Services`, `Workloads` (subdivided into `Production`, `Development`, `Staging`), and `Sandbox`.
- Start with Deny-Statements: SCPs are most effective when used to *deny* actions that are never permitted, rather than attempting to *allow* everything. A deny-by-default approach for specific actions or resources provides stronger guardrails.
- Apply SCPs at the Highest Possible Level: Attach SCPs to OUs rather than individual accounts where possible. This simplifies management and ensures consistent enforcement across groups of accounts. Only apply to individual accounts for unique, specific requirements.
- Use Granular SCPs: Instead of monolithic SCPs, create smaller, focused SCPs for specific security requirements (e.g., "Deny Region Access," "Enforce MFA for Console," "Prevent IAM Role Deletion"). This makes them easier to understand, test, and maintain.
- Implement "Break-Glass" Procedures: Document and regularly test a "break-glass" process. This typically involves a highly restricted, emergency access IAM user/role in the management account with permissions to detach SCPs from specific OUs or accounts in case of an unforeseen lockout.
- Version Control Your SCPs: Treat your SCPs as code. Store their JSON definitions in a version control system (e.g., Git) and integrate them into your CI/CD pipeline for automated deployment and management. This allows for change tracking, peer review, and easy rollback.
- Test Thoroughly in Isolated Environments: Never deploy a new or modified SCP directly to production OUs. Always test its impact in a dedicated `Sandbox` or `Development` OU with non-critical accounts first.
- Regularly Review and Audit SCPs: Your security posture evolves, and so should your SCPs. Periodically review all active SCPs to ensure they are still relevant, effective, and not causing unintended side effects. Use AWS Config rules to monitor for SCP changes.
- Leverage Tagging: While SCPs don't directly use tags in the same way IAM policies do for resource-level permissions, you can use tags to categorize accounts and OUs, which can inform your SCP attachment strategy.
- Educate Your Teams: Ensure that your development and operations teams understand how SCPs work and their implications. This helps prevent frustration when an action is unexpectedly denied and fosters a culture of security awareness.
FAQ
Q1: Can Service Control Policies (SCPs) grant permissions?
No, SCPs cannot grant permissions. Their sole purpose is to define the maximum available permissions for accounts within an organization, an OU, or an individual account. If an action is allowed by an SCP, an IAM policy within the account still needs to explicitly grant that permission for the user or role to perform the action. If an SCP explicitly denies