Skip to main content
AWSSecurityadvanced

AWS Organizations & SCPs

Complete guide to AWS Organizations covering OU design, Service Control Policies, consolidated billing, delegated administration, and automated account provisioning.

CloudToolStack Team26 min readPublished Mar 14, 2026

Prerequisites

Introduction to AWS Organizations

AWS Organizations is a free service that lets you consolidate multiple AWS accounts into an organization that you centrally manage. Instead of managing dozens or hundreds of standalone accounts independently, Organizations gives you a hierarchical structure of Organizational Units (OUs), centralized billing, and most importantly, Service Control Policies (SCPs) that set permission guardrails across all accounts.

The multi-account strategy is an AWS best practice because it provides hard boundaries between workloads. A misconfigured security group in a development account cannot affect production resources in a different account. Resource limits (like the maximum number of VPCs or Lambda functions) apply per account, so one team cannot exhaust limits that another team needs. And blast radius is contained: a compromised account is isolated from others.

This guide covers the complete lifecycle of AWS Organizations: designing your OU hierarchy, implementing SCPs as preventive guardrails, setting up consolidated billing, delegating administration to member accounts, automating account creation, and integrating with other AWS services like IAM Identity Center, CloudTrail, and AWS Config.

Organizations Is Free

AWS Organizations itself has no cost. You pay only for the AWS resources used in each member account. Consolidated billing can actually reduce costs through volume discounts: usage across all accounts is aggregated for services like S3 and EC2 that offer tiered pricing. Reserved Instance and Savings Plan benefits can be shared across the organization.

Designing Your OU Hierarchy

The Organizational Unit (OU) hierarchy is the backbone of your multi-account strategy. OUs group accounts that share similar security requirements, compliance needs, or operational characteristics. SCPs are attached to OUs and apply to all accounts within them, making OUs the primary mechanism for applying governance at scale.

AWS recommends a layered OU structure based on function rather than team or application. The foundational OUs handle infrastructure and security concerns, while workload OUs organize by environment and team. Here is a recommended structure that scales from small startups to large enterprises.

Recommended OU Structure

OU NamePurposeExample Accounts
RootOrganization root (apply baseline SCPs here)Management account only
SecuritySecurity tooling and auditLog Archive, Security Tooling, Audit
InfrastructureShared infrastructure servicesNetworking, Shared Services, DNS
Workloads / ProductionProduction application accountsApp-A-Prod, App-B-Prod, Data-Prod
Workloads / Non-ProductionDevelopment, staging, testingApp-A-Dev, App-A-Staging, Sandbox
SandboxExperimentation with limited guardrailsDeveloper sandboxes
SuspendedQuarantined or decommissioned accountsCompromised accounts, retired apps
bash
# Create the organization (run from the management account)
aws organizations create-organization --feature-set ALL

# Create the OU hierarchy
ROOT_ID=$(aws organizations list-roots \
  --query 'Roots[0].Id' --output text)

# Top-level OUs
SECURITY_OU=$(aws organizations create-organizational-unit \
  --parent-id $ROOT_ID --name "Security" \
  --query 'OrganizationalUnit.Id' --output text)

INFRA_OU=$(aws organizations create-organizational-unit \
  --parent-id $ROOT_ID --name "Infrastructure" \
  --query 'OrganizationalUnit.Id' --output text)

WORKLOADS_OU=$(aws organizations create-organizational-unit \
  --parent-id $ROOT_ID --name "Workloads" \
  --query 'OrganizationalUnit.Id' --output text)

SANDBOX_OU=$(aws organizations create-organizational-unit \
  --parent-id $ROOT_ID --name "Sandbox" \
  --query 'OrganizationalUnit.Id' --output text)

SUSPENDED_OU=$(aws organizations create-organizational-unit \
  --parent-id $ROOT_ID --name "Suspended" \
  --query 'OrganizationalUnit.Id' --output text)

# Nested OUs under Workloads
PROD_OU=$(aws organizations create-organizational-unit \
  --parent-id $WORKLOADS_OU --name "Production" \
  --query 'OrganizationalUnit.Id' --output text)

NONPROD_OU=$(aws organizations create-organizational-unit \
  --parent-id $WORKLOADS_OU --name "Non-Production" \
  --query 'OrganizationalUnit.Id' --output text)

echo "OU hierarchy created successfully"

# Create a new member account
aws organizations create-account \
  --email "app-a-prod@company.com" \
  --account-name "App-A-Production" \
  --iam-user-access-to-billing ALLOW

# Move the account to the Production OU
aws organizations move-account \
  --account-id 222222222222 \
  --source-parent-id $ROOT_ID \
  --destination-parent-id $PROD_OU

Management Account Best Practices

The management account (formerly called the master account) has special privileges that cannot be restricted by SCPs. Never deploy workloads in the management account. Use it only for organization management, billing, and account provisioning. SCPs do not apply to the management account, so a compromised management account has unrestricted access to the entire organization.

Service Control Policies (SCPs) Explained

Service Control Policies are the most powerful governance mechanism in AWS Organizations. SCPs define the maximum permissions available to accounts in an OU. They do not grant permissions (that is still done by IAM policies in each account), but they set a ceiling. Even if an IAM policy in an account allows an action, the SCP must also allow it for the action to succeed. Think of SCPs as guardrails rather than permissions.

SCPs use the same JSON syntax as IAM policies but with important differences: they support only Allow and Deny statements (no conditions on some elements), they apply to all IAM principals in the account (including the root user), and they do not affect service-linked roles. SCPs have a maximum size of 5,120 characters, which limits complexity per policy.

SCP Evaluation Logic

SCPs are evaluated in a chain from the root to the account. For an action to be allowed, every SCP in the chain (root OU, parent OU, child OU) must allow it. A Deny at any level overrides an Allow at any other level. This means you can apply broad restrictions at the root and more specific ones at lower levels.

json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyRegionsOutsideApproved",
      "Effect": "Deny",
      "Action": "*",
      "Resource": "*",
      "Condition": {
        "StringNotEquals": {
          "aws:RequestedRegion": [
            "us-east-1",
            "us-west-2",
            "eu-west-1"
          ]
        },
        "ArnNotLike": {
          "aws:PrincipalARN": [
            "arn:aws:iam::*:role/OrganizationAccountAccessRole"
          ]
        }
      }
    }
  ]
}

Deny List vs. Allow List Strategy

There are two SCP strategies. The deny list approach starts with the default FullAWSAccess SCP and adds explicit Deny statements for actions you want to prevent. The allow list approach removes the FullAWSAccess SCP and creates explicit Allow statements for only the services you want to permit. The deny list strategy is easier to manage and is recommended for most organizations. The allow list strategy is more secure but requires significant effort to maintain as new services are added.

Essential SCPs for Every Organization

These SCPs represent baseline security guardrails that most organizations should apply at the root OU level. They prevent common security mistakes without interfering with normal development work.

Prevent Disabling CloudTrail

json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "ProtectCloudTrail",
      "Effect": "Deny",
      "Action": [
        "cloudtrail:DeleteTrail",
        "cloudtrail:StopLogging",
        "cloudtrail:UpdateTrail",
        "cloudtrail:PutEventSelectors"
      ],
      "Resource": "arn:aws:cloudtrail:*:*:trail/organization-trail",
      "Condition": {
        "ArnNotLike": {
          "aws:PrincipalARN": [
            "arn:aws:iam::*:role/SecurityAdminRole"
          ]
        }
      }
    }
  ]
}

Prevent Leaving the Organization

json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyLeaveOrg",
      "Effect": "Deny",
      "Action": "organizations:LeaveOrganization",
      "Resource": "*"
    }
  ]
}

Require IMDSv2 for EC2

json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "RequireIMDSv2",
      "Effect": "Deny",
      "Action": "ec2:RunInstances",
      "Resource": "arn:aws:ec2:*:*:instance/*",
      "Condition": {
        "StringNotEquals": {
          "ec2:MetadataHttpTokens": "required"
        }
      }
    },
    {
      "Sid": "DenyIMDSv1Modification",
      "Effect": "Deny",
      "Action": "ec2:ModifyInstanceMetadataOptions",
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "ec2:MetadataHttpTokens": "optional"
        }
      }
    }
  ]
}

Deny Public S3 Buckets

json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyS3PublicAccess",
      "Effect": "Deny",
      "Action": [
        "s3:PutBucketPublicAccessBlock",
        "s3:PutAccountPublicAccessBlock"
      ],
      "Resource": "*",
      "Condition": {
        "ArnNotLike": {
          "aws:PrincipalARN": [
            "arn:aws:iam::*:role/SecurityAdminRole"
          ]
        }
      }
    },
    {
      "Sid": "DenyPublicBucketPolicies",
      "Effect": "Deny",
      "Action": "s3:PutBucketPolicy",
      "Resource": "*",
      "Condition": {
        "StringEquals": {
          "s3:PolicyPermission": "Public"
        }
      }
    }
  ]
}

Applying and Managing SCPs

SCPs are created at the organization level and attached to OUs or individual accounts. You can attach up to 5 SCPs per OU or account. Since each SCP is limited to 5,120 characters, you typically need multiple SCPs to cover all your guardrails.

bash
# Create an SCP
SCP_ID=$(aws organizations create-policy \
  --name "deny-unapproved-regions" \
  --description "Restrict resource creation to approved regions" \
  --type SERVICE_CONTROL_POLICY \
  --content file://deny-unapproved-regions.json \
  --query 'Policy.PolicySummary.Id' \
  --output text)

echo "SCP created: $SCP_ID"

# Attach the SCP to the Workloads OU
aws organizations attach-policy \
  --policy-id $SCP_ID \
  --target-id $WORKLOADS_OU

# Attach to multiple OUs
for OU in $WORKLOADS_OU $SANDBOX_OU $INFRA_OU; do
  aws organizations attach-policy \
    --policy-id $SCP_ID \
    --target-id $OU
  echo "Attached to: $OU"
done

# List SCPs attached to an OU
aws organizations list-policies-for-target \
  --target-id $PROD_OU \
  --filter SERVICE_CONTROL_POLICY \
  --query 'Policies[].{Id:Id, Name:Name}' \
  --output table

# List all SCPs in the organization
aws organizations list-policies \
  --filter SERVICE_CONTROL_POLICY \
  --query 'Policies[].{Id:Id, Name:Name, Attached:AwsManaged}' \
  --output table

# Test an SCP before applying (simulate the effect)
# Use IAM Policy Simulator with the SCP context
aws iam simulate-custom-policy \
  --policy-input-list file://deny-unapproved-regions.json \
  --action-names "ec2:RunInstances" "s3:CreateBucket" \
  --resource-arns "arn:aws:ec2:ap-southeast-1:*:instance/*" \
  --output table

SCP Testing Is Critical

A misconfigured SCP can lock you out of accounts or break running applications. Always test SCPs in a sandbox OU first. Start with a single test account, verify that intended actions are denied and legitimate actions are allowed, then gradually roll out to broader OUs. Keep an emergency break-glass procedure that can detach SCPs if they cause unexpected outages.

Consolidated Billing and Cost Management

AWS Organizations provides consolidated billing, which aggregates usage across all member accounts into a single bill paid by the management account (or a designated billing account). This simplifies financial management and unlocks volume discounts.

Benefits of consolidated billing include: volume pricing tiers are calculated across all accounts (more S3 storage across accounts means lower per-GB pricing), Reserved Instances and Savings Plans can be shared across accounts (an unused RI in one account is automatically applied to matching usage in another), and you get a single invoice instead of managing dozens of separate bills.

bash
# View the consolidated bill for the organization
aws ce get-cost-and-usage \
  --time-period Start=2026-02-01,End=2026-03-01 \
  --granularity MONTHLY \
  --metrics "BlendedCost" "UnblendedCost" \
  --group-by Type=DIMENSION,Key=LINKED_ACCOUNT \
  --output table

# Break down costs by service per account
aws ce get-cost-and-usage \
  --time-period Start=2026-02-01,End=2026-03-01 \
  --granularity MONTHLY \
  --metrics "BlendedCost" \
  --group-by Type=DIMENSION,Key=SERVICE \
  --filter '{"Dimensions": {"Key": "LINKED_ACCOUNT", "Values": ["222222222222"]}}' \
  --output table

# Set up a budget for the entire organization
aws budgets create-budget \
  --account-id 111111111111 \
  --budget '{
    "BudgetName": "org-monthly-budget",
    "BudgetLimit": {"Amount": "10000", "Unit": "USD"},
    "TimeUnit": "MONTHLY",
    "BudgetType": "COST",
    "CostFilters": {}
  }' \
  --notifications-with-subscribers '[
    {
      "Notification": {
        "NotificationType": "ACTUAL",
        "ComparisonOperator": "GREATER_THAN",
        "Threshold": 80,
        "ThresholdType": "PERCENTAGE"
      },
      "Subscribers": [
        {"SubscriptionType": "EMAIL", "Address": "cloud-finance@company.com"}
      ]
    }
  ]'

# Enable RI sharing across the organization (enabled by default)
# Disable if you want to restrict RI benefits to specific accounts
aws organizations disable-aws-service-access \
  --service-principal member.org.stacksets.cloudformation.amazonaws.com

Delegated Administration

The management account should not run workloads or host security tooling. Instead, delegate administration of AWS services to specialized member accounts. For example, the Security Tooling account can be the delegated administrator for GuardDuty, Security Hub, and AWS Config, while the Networking account manages shared resources.

bash
# Delegate GuardDuty administration to the security account
aws organizations register-delegated-administrator \
  --account-id 333333333333 \
  --service-principal guardduty.amazonaws.com

# Delegate Security Hub
aws organizations register-delegated-administrator \
  --account-id 333333333333 \
  --service-principal securityhub.amazonaws.com

# Delegate AWS Config
aws organizations register-delegated-administrator \
  --account-id 333333333333 \
  --service-principal config.amazonaws.com

# Delegate CloudFormation StackSets
aws organizations register-delegated-administrator \
  --account-id 333333333333 \
  --service-principal member.org.stacksets.cloudformation.amazonaws.com

# Delegate IAM Identity Center (AWS SSO)
aws organizations register-delegated-administrator \
  --account-id 333333333333 \
  --service-principal sso.amazonaws.com

# List all delegated administrators
aws organizations list-delegated-administrators \
  --query 'DelegatedAdministrators[].{Account:Id, Name:Name, Service:ServicePrincipal}' \
  --output table

# List services that support delegation
aws organizations list-aws-service-access-for-organization \
  --query 'EnabledServicePrincipals[].ServicePrincipal' \
  --output table

Security Account Pattern

Create two security accounts: a Log Archive account that receives all CloudTrail logs, Config snapshots, and VPC flow logs (with an SCP preventing anyone from deleting logs), and a Security Tooling account that runs GuardDuty, Security Hub, and detective controls. This separation ensures that even if the security tooling is compromised, the immutable log archive remains intact.

Automating Account Creation

Manual account creation does not scale. For organizations that provision accounts regularly (new teams, new projects, new environments), automate the process using AWS Control Tower Account Factory, AWS Service Catalog, or custom automation with CloudFormation StackSets.

python
import boto3
import time

org = boto3.client("organizations")

def create_account(email, name, ou_id):
    """Create a new AWS account and move it to the specified OU."""

    # Create the account
    response = org.create_account(
        Email=email,
        AccountName=name,
        RoleName="OrganizationAccountAccessRole",
        IamUserAccessToBilling="ALLOW",
    )

    request_id = response["CreateAccountStatus"]["Id"]
    print(f"Account creation initiated: {request_id}")

    # Poll for completion
    while True:
        status = org.describe_create_account_status(
            CreateAccountRequestId=request_id
        )
        state = status["CreateAccountStatus"]["State"]
        if state == "SUCCEEDED":
            account_id = status["CreateAccountStatus"]["AccountId"]
            print(f"Account created: {account_id}")
            break
        elif state == "FAILED":
            reason = status["CreateAccountStatus"]["FailureReason"]
            raise Exception(f"Account creation failed: {reason}")
        time.sleep(5)

    # Move the account to the target OU
    root_id = org.list_roots()["Roots"][0]["Id"]
    org.move_account(
        AccountId=account_id,
        SourceParentId=root_id,
        DestinationParentId=ou_id,
    )
    print(f"Account {account_id} moved to OU {ou_id}")

    return account_id

# Apply baseline configuration using CloudFormation StackSets
cfn = boto3.client("cloudformation")

def apply_baseline(account_id, regions):
    """Apply baseline CloudFormation stack to a new account."""
    cfn.create_stack_instances(
        StackSetName="account-baseline",
        Accounts=[account_id],
        Regions=regions,
        OperationPreferences={
            "FailureToleranceCount": 0,
            "MaxConcurrentCount": 1,
        },
    )
    print(f"Baseline stack applied to {account_id} in {regions}")

# Example usage
new_account = create_account(
    email="platform-staging@company.com",
    name="Platform-Staging",
    ou_id="ou-abc123-nonprod",
)
apply_baseline(new_account, ["us-east-1", "us-west-2"])

Organization-Wide Services

Several AWS services integrate with Organizations to provide organization-wide coverage from a single delegated administrator account. Enable these services to get centralized visibility across your entire organization.

Key Integrated Services

ServiceOrganization FeatureBenefit
CloudTrailOrganization trailCentralized API audit logging for all accounts
GuardDutyDelegated adminThreat detection across all accounts
Security HubDelegated adminCentralized security findings dashboard
AWS ConfigAggregator + delegated adminOrganization-wide configuration compliance
IAM Identity CenterOrganization integrationCentralized access management and SSO
Service CatalogPortfolio sharingApproved product catalog for all accounts
RAMOrganization sharingShare resources (TGW, subnets) across accounts
Systems ManagerDelegated adminCentralized patching and compliance
bash
# Enable trusted access for key services
aws organizations enable-aws-service-access \
  --service-principal cloudtrail.amazonaws.com

aws organizations enable-aws-service-access \
  --service-principal guardduty.amazonaws.com

aws organizations enable-aws-service-access \
  --service-principal securityhub.amazonaws.com

aws organizations enable-aws-service-access \
  --service-principal config.amazonaws.com

aws organizations enable-aws-service-access \
  --service-principal ram.amazonaws.com

aws organizations enable-aws-service-access \
  --service-principal sso.amazonaws.com

# Create an organization-wide CloudTrail
aws cloudtrail create-trail \
  --name organization-trail \
  --s3-bucket-name org-cloudtrail-logs-111111111111 \
  --is-organization-trail \
  --is-multi-region-trail \
  --enable-log-file-validation \
  --kms-key-id arn:aws:kms:us-east-1:111111111111:key/your-kms-key

aws cloudtrail start-logging --name organization-trail

Tag Policies and Backup Policies

Beyond SCPs, Organizations supports three other policy types: Tag Policies for enforcing consistent resource tagging, Backup Policies for centralized backup configurations, and AI Services Opt-Out Policies for controlling how AWS AI services use your data.

json
{
  "tags": {
    "Environment": {
      "tag_key": {
        "@@assign": "Environment"
      },
      "tag_value": {
        "@@assign": [
          "production",
          "staging",
          "development",
          "sandbox"
        ]
      },
      "enforced_for": {
        "@@assign": [
          "ec2:instance",
          "ec2:volume",
          "rds:db",
          "s3:bucket",
          "lambda:function"
        ]
      }
    },
    "CostCenter": {
      "tag_key": {
        "@@assign": "CostCenter"
      },
      "tag_value": {
        "@@assign": [
          "engineering",
          "data-science",
          "platform",
          "security"
        ]
      },
      "enforced_for": {
        "@@assign": [
          "ec2:instance",
          "rds:db",
          "s3:bucket"
        ]
      }
    }
  }
}
bash
# Enable tag policies for the organization
aws organizations enable-policy-type \
  --root-id $ROOT_ID \
  --policy-type TAG_POLICY

# Create a tag policy
aws organizations create-policy \
  --name "standard-tags" \
  --description "Enforce standard tagging for cost allocation" \
  --type TAG_POLICY \
  --content file://tag-policy.json

# Enable backup policies
aws organizations enable-policy-type \
  --root-id $ROOT_ID \
  --policy-type BACKUP_POLICY

Common Mistakes and Anti-Patterns

Running workloads in the management account. The management account is exempt from SCPs and has special privileges. Any workload here operates outside your governance guardrails. Keep the management account for organization management only.

Overly restrictive SCPs too early. Starting with an allow-list SCP strategy before you understand your workloads leads to constant firefighting. Begin with deny-list SCPs for known bad actions, monitor what services teams actually use, and tighten restrictions gradually.

Not planning CIDR ranges across accounts. When accounts need to communicate via VPC Peering or Transit Gateway, overlapping CIDR blocks cause routing failures. Establish a centralized IP address management (IPAM) plan before creating accounts. AWS VPC IPAM can help automate this.

Forgetting about the SCP character limit. Each SCP is limited to 5,120 characters. Complex conditions with many region names, service names, or ARN patterns quickly hit this limit. Use multiple SCPs, minimize whitespace, and use wildcards strategically.

Not testing SCPs in isolation. Always test new SCPs in a sandbox OU with a non-critical account before applying to production OUs. A Deny SCP that is too broad can break application deployments, CI/CD pipelines, and even CloudFormation rollbacks.

SCP Does Not Affect the Management Account

This bears repeating: SCPs never apply to the management account. If you attach an SCP that denies all access to the root OU, the management account is unaffected. This is why running workloads in the management account is dangerous: those workloads bypass all your carefully crafted guardrails.

Next Steps

With your organization structure, SCPs, and core services in place, consider these next steps to mature your multi-account strategy:

AWS Control Tower: Automates the setup of a multi-account landing zone with pre-configured guardrails, an account factory, and a dashboard for compliance visibility. Control Tower builds on Organizations and adds opinionated defaults.

AWS Config rules: While SCPs prevent actions, AWS Config rules detect non-compliant resources after creation. Use Config rules as detective controls that complement your preventive SCP guardrails.

Service Catalog: Provide teams with pre-approved infrastructure templates (products) that enforce your architecture standards. Teams self-service from the catalog instead of building from scratch.

Account vending automation: Build a pipeline that creates accounts, applies baselines, configures networking, and assigns access through IAM Identity Center automatically when a team requests a new account.

IAM Best Practices: Securing Your AWS AccountAWS Transit Gateway PatternsAWS Security Hub Overview

Key Takeaways

  1. 1AWS Organizations provides a hierarchical structure of OUs with inherited governance policies.
  2. 2SCPs set the maximum permissions ceiling for accounts; they do not grant permissions themselves.
  3. 3The management account is exempt from SCPs and should never host workloads.
  4. 4Deny-list SCP strategy is easier to manage and recommended for most organizations.
  5. 5Consolidated billing aggregates usage for volume discounts and RI/Savings Plan sharing.
  6. 6Delegated administration lets specialized accounts manage security and compliance services.

Frequently Asked Questions

Do SCPs apply to the management account?
No. SCPs never apply to the management account, regardless of which OU it is in. This is why you should never run workloads in the management account: those workloads bypass all governance guardrails.
What is the SCP character limit?
Each SCP is limited to 5,120 characters, and you can attach up to 5 SCPs per OU or account. Complex policies quickly hit this limit. Minimize whitespace, use wildcards, and split across multiple SCPs as needed.
Can SCPs break existing running resources?
SCPs affect API actions, not running resources directly. A Deny SCP prevents future actions but does not terminate running EC2 instances or delete existing S3 buckets. However, it can prevent modifications, scaling, or deployments, which may cause operational issues.
What is the difference between SCPs and IAM policies?
SCPs set permission boundaries at the organization level (what CAN be done in an account). IAM policies grant specific permissions to users and roles (what IS allowed). Both must allow an action for it to succeed. SCPs are guardrails; IAM policies are permissions.

Written by CloudToolStack Team

Cloud engineers and architects with hands-on experience across AWS, Azure, and GCP. We write guides based on real-world production patterns, not just documentation rewrites.

Disclaimer: This guide is for educational purposes. Cloud services change frequently; always refer to official documentation for the latest information. AWS, Azure, and GCP are trademarks of their respective owners.