AWS CDK Landing Zone

Service Control Policies

Shipped guardrail SCPs and how to author custom Service Control Policies for your AWS Organization.

src/config/service-control-policies/ contains the Service Control Policies that govern what actions are allowed across your AWS Organization. The landing zone ships six ready-made SCPs covering the most critical guardrails: five attached by default and one optional one you can attach when it fits your setup. You can use them as-is, modify their content, or add your own.

SCPs are attached in organization-structure.ts at the root, OU, or account level. AWS Organizations enforces a maximum of 10 SCPs per attachment point.

Shipped SCPs (attached by default)

SCPDefault attachmentWhat it enforces
criticalSecurityGuardrailsSCPRootDenies leaving the organization, creating IAM users and access keys, disabling EBS encryption by default, and modifying the account password policy
denyAllOutsidePrimaryAndSecondaryRegionsSCPRootRestricts all AWS API calls to your configured primary and secondary regions; exempts global services (IAM, Route 53, CloudFront, etc.) and landing-zone roles
protectTaggedCloudFormationStacksSCPRootDenies deleting CloudFormation stacks tagged with your organization name (organization: <organizationName>), protecting landing-zone managed stacks from accidental deletion
protectSecurityHubConfigurationSCPSecurity accountPrevents modification of Security Hub configuration in the delegated administrator account: disabling standards, deleting members, changing org configuration
lockdownSuspendedAccountsSCPSuspended OUDenies all AWS actions in accounts that have been moved to the Suspended OU

Shipped SCPs (optional, not attached by default)

SCPWhat it enforces
protectGuardDutyConfigCloudTrailAndSecurityHubSCPDenies disabling or reconfiguring GuardDuty, AWS Config, CloudTrail, and Security Hub in member accounts; exempts SSO administrators and landing-zone roles

To enable the optional SCP, import it in organization-structure.ts and add it to a serviceControlPolicies array (see Authoring a custom SCP for the attachment pattern). It exempts the roles returned by the landing zone's principal allow list (SSO administrator roles and any external admin roles), so platform operations keep working.

Region guardrail exemptions

The denyAllOutsidePrimaryAndSecondaryRegionsSCP automatically exempts:

  • Global services: IAM, Route 53, CloudFront, AWS Organizations, billing, KMS, S3, STS, support, and others that have no regional API boundary
  • Landing-zone roles: StackSet execution roles (StackSet*, stacksets-exec-*) and the optional external admin role
  • AWS SSO administrator roles: AWSReservedSSO_AdministratorAccess_*

When you add secondaryRegions to landing-zone-settings.ts and redeploy, the SCP automatically includes those regions in the allow list.

Authoring a custom SCP

Create a new file (or add to an existing one) in src/config/service-control-policies/:

export const denyS3PublicAccessSCP: ServiceControlPolicy = {
  content: {
    Version: '2012-10-17',
    Statement: [
      {
        Sid: 'DenyS3PublicAccess',
        Effect: 'Deny',
        Action: [
          's3:PutBucketPublicAccessBlock',
          's3:DeletePublicAccessBlock',
        ],
        Resource: '*',
        Condition: {
          StringEquals: {
            's3:publicAccessBlockConfiguration/BlockPublicAcls': 'false',
          },
        },
      },
    ],
  },
  description: 'Prevents disabling S3 Block Public Access at the bucket level',
  policyName: 'DenyS3PublicAccess',
  policyType: PolicyType.SERVICE_CONTROL_POLICY,
};

Export it from src/config/service-control-policies/index.ts:

export * from './your-new-guardrails';

Then attach it in organization-structure.ts at the appropriate level:

import { denyS3PublicAccessSCP } from './service-control-policies';

// In the organizationStructure object:
ProductionOU: {
  name: `${orgName}-workload-prod-ou`,
  serviceControlPolicies: [denyS3PublicAccessSCP],
  accounts: { ... },
},

Things to know

  • Maximum 10 SCPs per attachment point, counting inherited ones. AWS raised this limit from 5 to 10 in May 2026. An account in an OU that already has 4 SCPs can only receive 6 more at the account level.
  • SCPs are deny-only: they can only restrict what IAM already allows; they cannot grant permissions.
  • The FullAWSAccess managed SCP is always attached by AWS Organizations at every level. Your custom SCPs layer on top of it as additional restrictions.
  • Test in a non-production OU first: attach a new SCP to a development account before applying it at the root or to production accounts.