Where this fits
In AWS Landing Zone & Control Tower, parts 1–3 established the org structure and operating spine: the AWS Organizations account tree, the OU design (Security, Infrastructure, Workloads, Sandbox, Suspended), the three foundational accounts (management, Log Archive, Audit), and centralized CloudTrail/Config logging. None of that enforces anything on its own. Part 4 — Guardrails (SCPs & Controls) is where the landing zone grows teeth: it is the layer that takes governance intent and turns it into rules that block, detect, or prevent non-compliant behaviour automatically across every account, including ones that don’t exist yet. AWS Control Tower packages these as controls (the term that replaced “guardrails” in the console, though everyone still says guardrails), and a control is delivered through one of three behaviours — preventive via Service Control Policies, detective via AWS Config rules, proactive via CloudFormation Hooks — and assigned through one of three categories — mandatory, strongly recommended, elective. This article goes deep on all four of those sub-components, because getting the behaviour-to-mechanism mapping right is what separates a landing zone that scales to 800 accounts from one that becomes an exemption-ticket factory.

Preventive guardrails: Service Control Policies (SCPs)
What it is
A Service Control Policy is an AWS Organizations policy that sets the maximum available permissions for the IAM principals (users and roles) in the accounts it is attached to. It is a guardrail, not a grant: an SCP never gives anyone permission — it only defines the ceiling that an account’s own IAM policies operate beneath. The effective permission of any principal is the intersection of what the SCP allows and what the principal’s IAM (identity and resource) policies allow. If either side says no, the answer is no. Crucially, SCPs apply to everyone in the member account including the account root user — but they explicitly do not apply to the management account, to service-linked roles, or to the org’s service-managed resources. That management-account blind spot is the single most important SCP fact to internalise: never run workloads there, because you cannot fence them in.
SCPs attach to three target types in the org tree — the root, an Organizational Unit, or an individual account — and they inherit downward. A policy on the root applies to every account; a policy on the Workloads OU applies to every account in and below it. Inheritance is additive in restriction: a child OU can make things stricter but can never loosen what a parent SCP forbade. There are two authoring styles:
- Deny lists (blocklist strategy): start from the AWS-managed
FullAWSAccesspolicy (allow*on*) attached at the root, then attachDenystatements that subtract specific capabilities. This is the default and by far the most common pattern. - Allow lists (allowlist strategy): detach
FullAWSAccessand attach explicitAllowstatements enumerating every permitted action. Powerful but brutally high-maintenance — every new service AWS launches is blocked until you add it. Most enterprises use deny lists and reserve allowlisting for tightly-scoped, high-compliance OUs.
A closely related newer control is the Resource Control Policy (RCP). Where an SCP bounds what principals in your accounts can do, an RCP bounds what any principal (including external/cross-account ones) can do to resources in your accounts — the resource-perimeter complement to the identity perimeter. SCPs and RCPs are both Organizations policies, both inherit down the tree, and both intersect with the relevant grant; a mature landing zone uses both.
Why it matters
SCPs are the only AWS mechanism that can make a permission impossible to grant — not merely ungranted, but unobtainable even by an account’s own administrator or root user. That property is what lets a platform team hand a workload account to a product team with full autonomy inside the box while guaranteeing certain things can never happen: the central CloudTrail can’t be stopped, the org can’t be left, the encryption key for the log bucket can’t be deleted, resources can’t be created outside approved regions. Without SCPs you are relying on every account’s IAM being authored correctly forever, which is a bet you will lose at the 50th account. With SCPs, a non-negotiable written once at the root is enforced on account 800 exactly as on account 1, and cannot be weakened by a child scope.
They are also the cleanest way to encode compliance and data-residency law as code: a single region-deny SCP on the EU OU does more for GDPR data-residency assurance than a hundred pages of policy documentation, because it is mechanically unbreakable.
How to do it well
- Keep
FullAWSAccessat the root and use deny lists. Don’t fight the allow/deny default. Express your guardrails as targetedDenystatements layered onto OUs, which keeps new AWS services working by default and keeps each policy readable. - Exempt the org-control and global-service roles, always. Every region-lock or service-deny must carve out the paths the platform itself depends on. Global services authenticate through
us-east-1(IAM, Organizations, Route 53, CloudFront, STS, Support, WAF for CloudFront, Artifact), so a naive region lock will lock you out of IAM. Useaws:RequestedRegionwith aNotActionexemption list, and exempt the Control Tower execution role and your CI/CD roles withaws:PrincipalArnconditions where automation must cross the guardrail. - Mind the hard limits. Organizations enforces caps: a maximum of 5 SCPs per target (root/OU/account) and a per-policy document size of 5,120 characters. This is why the discipline is few, dense, well-commented policies assigned at the right altitude, not dozens of single-purpose policies. Consolidate related denies, strip whitespace, and use
NotAction/NotResourceto keep statements compact. - Assign high, exempt low. Put truly universal denies (leave-org, region lock minus exemptions, root-user hardening) at the root; put archetype-specific ones (e.g. “deny anything but approved services” for a
SandboxOU, “deny disabling GuardDuty” for prod) on the OU. Reserve account-level SCPs for genuine one-offs. - Test with the IAM Policy Simulator and roll out in stages. SCP mistakes cause outages. Validate intent with the simulator, deploy to a non-production OU first, watch CloudTrail
AccessDeniedevents, then promote. Treat SCPs as code in Git with peer review and a pipeline — never hand-edit in the console at scale. - Use
aws:PrincipalOrgIDandaws:PrincipalOrgPathsconditions to scope denies to your own org, and lean on RCPs for the resource-side perimeter (e.g. “no S3 bucket in this org may be accessed by a principal outsideo-xxxxunless it’s an approved AWS service”).
The canonical SCP patterns
| Guardrail intent | Mechanism | Where to attach |
|---|---|---|
| Prevent member accounts leaving the org | Deny organizations:LeaveOrganization |
Root |
| Region allowlist (data residency) | Deny NotAction:[global svcs] when aws:RequestedRegion ∉ allowlist |
Root or geo OU |
| Protect CloudTrail / Config / log bucket | Deny cloudtrail:Stop*/Delete*, config:Delete*, S3 deletes on log bucket |
Root |
| Block root-user activity except break-glass | Deny * with aws:PrincipalArn = root and aws:PrincipalType = Root |
Root |
| Require IMDSv2 / deny IMDSv1 launches | Deny ec2:RunInstances unless ec2:MetadataHttpTokens = required |
Workloads OU |
| Sandbox service allowlist | Deny NotAction:[approved services] |
Sandbox OU |
| Deny disabling security services | Deny guardduty:Delete*, securityhub:Disable*, macie2:Disable* |
Security + Workloads OUs |
| Resource perimeter (block external access) | RCP with aws:PrincipalOrgID StringNotEquals |
Root |
Concrete artifacts, decisions, and tools
- Artifacts: the SCP/RCP-as-code repository (JSON policy documents + Terraform/CloudFormation that attaches them to OUs), an SCP-to-OU attachment matrix, an exemption register (which roles/ARNs are carved out of which deny and why), and a break-glass runbook for the root-user path.
- Decisions: deny-list vs allow-list per OU; which denies live at root vs OU; the exact global-service
NotActionexemption list; whether to adopt RCPs now or later; how break-glass bypasses the root-user deny. - Tools/services: AWS Organizations (SCP/RCP authoring and attachment), AWS Control Tower (which manages its own SCPs prefixed
aws-guardrails-*— never edit those by hand), IAM Policy Simulator, IAM Access Analyzer (custom-policy checks, unused-access findings), CloudTrail (to observeAccessDenied), and Terraform’saws_organizations_policy/aws_organizations_policy_attachmentresources.
Callout: Control Tower writes and owns a set of SCPs to implement its preventive controls; they carry the
aws-guardrails-name prefix and anaws-control-towertag. Do not edit, reorder, or delete these manually — doing so puts the OU into a non-compliant/drift state that Control Tower will flag and may try to remediate. Layer your custom SCPs alongside them, not on top of them.
Detective guardrails: AWS Config rules
What it is
A detective control does not block anything — it continuously evaluates the live state of your resources against a desired configuration and reports compliant / non-compliant. In the landing zone this is delivered by AWS Config: Config records the configuration of supported resources as configuration items, stores their history, and runs Config rules against them. A rule is either AWS-managed (one of hundreds of prebuilt checks like s3-bucket-public-read-prohibited, encrypted-volumes, rds-storage-encrypted, iam-password-policy) or custom (backed by an AWS Lambda function or a Guard policy in Config Custom Policy rules). Rules evaluate on configuration change, on a periodic schedule, or both.
Across an organisation, detective controls are deployed at scale through AWS Config conformance packs — a pack is a named collection of Config rules and remediation actions deployed as a single CloudFormation-based unit, and an organization conformance pack rolls that bundle out to every member account from the management or a delegated administrator account in one operation. Control Tower’s detective controls are exactly this under the hood: managed Config rules deployed org-wide, with findings surfaced in the Control Tower console and (typically) aggregated into AWS Security Hub.
Why it matters
Preventive controls can only stop the things you thought to forbid in advance, and only at the API boundary. Detective controls cover the long tail: configuration drift introduced by a permitted-but-misused API, by an action a too-broad SCP exemption allowed, by a console change, or simply by a resource that was compliant when created and drifted later (a security group opened to 0.0.0.0/0 last Tuesday). They give you the continuous-compliance evidence auditors actually want — a timestamped, per-resource history of compliant/non-compliant state mapped to a framework — and they are the input to auto-remediation. A detective control is the difference between “we have a policy that says volumes must be encrypted” and “here is the live count of unencrypted volumes by account, trending down, with the remediation that runs on each new violation.”
They also catch what preventive controls structurally cannot: things created in the management account (no SCP), changes by service-linked roles, and slow drift. A landing zone with only preventive controls is blind to its own decay.
How to do it well
- Aggregate, don’t island. Stand up an AWS Config aggregator in the Audit account (or a delegated Config admin account) so you see org-wide compliance in one pane instead of per-account. Control Tower wires this for you; if you’re self-managing, make the aggregator account a delegated administrator.
- Deploy rules as conformance packs, not click-by-click. Use organization conformance packs so a rule set lands identically on every account and every future account, and version the pack templates in Git. Start from AWS’s sample packs mapped to CIS AWS Foundations Benchmark, PCI DSS, NIST 800-53, or AWS Foundational Security Best Practices, then trim/extend.
- Attach remediation where it’s safe. Config rules can trigger AWS Systems Manager Automation remediation documents (SSM runbooks) — automatic or manual-approval. Make remediation automatic for low-risk, idempotent fixes (enable bucket encryption, tag a resource, disable public access block) and manual-approval for anything that could disrupt a workload (modifying a security group on a running app).
- Control cost deliberately. Config bills per configuration item recorded and per rule evaluation, and a chatty estate is expensive. Tune the recorder to record only the resource types you actually govern, exclude high-churn noise where appropriate, and prefer change-triggered over frequent periodic rules. This is a real line item at scale — budget it.
- Route findings to Security Hub and act on them. Send Config findings into Security Hub for a normalised, framework-scored view and into EventBridge for ticketing/alerting. A finding nobody routes is a finding nobody fixes.
- Mind the detective gap. Detective controls evaluate after the fact — there is a window between a non-compliant change and its detection/remediation. For anything where that window is unacceptable, you need a preventive or proactive control instead; detection is a complement, not a substitute.
Detective control building blocks
| Building block | Role in the landing zone |
|---|---|
| Configuration recorder | Captures configuration items per account; the data source for everything |
| AWS-managed Config rule | Prebuilt compliance check (e.g. s3-bucket-server-side-encryption-enabled) |
| Custom Config rule (Lambda / Guard) | Org-specific checks not covered by managed rules |
| Conformance pack | A versioned bundle of rules + remediations deployed as one unit |
| Organization conformance pack | The same bundle rolled out org-wide, current and future accounts |
| Config aggregator | Single-pane org-wide compliance view (in Audit account) |
| SSM Automation remediation | The corrective action a rule fires (auto or on approval) |
| Security Hub | Normalises + scores findings against frameworks |
Concrete artifacts, decisions, and tools
- Artifacts: the conformance-pack templates (CloudFormation/YAML), the rule-to-framework mapping (which Config rule satisfies which CIS/PCI/NIST control), the remediation catalogue (rule → SSM runbook → auto vs manual), the recorder scope decision (which resource types are recorded), and the aggregator definition.
- Decisions: managed vs custom per check; auto vs manual remediation per rule; recorder scope and cost ceiling; which framework(s) the pack maps to; change-triggered vs periodic per rule.
- Tools/services: AWS Config (recorder, rules, aggregator, conformance packs), AWS Systems Manager Automation (remediation), AWS Security Hub (CIS/FSBP/PCI standards + finding aggregation), Amazon EventBridge (routing), AWS Lambda and CloudFormation Guard (custom rules), and AWS Audit Manager for evidence collection.
Proactive controls: CloudFormation Hooks
What it is
A proactive control is the third behaviour, and it closes the gap between preventive and detective. It checks resource configuration at provisioning time, before the resource is created or updated, and blocks the deployment if it would be non-compliant — but unlike an SCP, it inspects the desired resource properties, not just the API verb. The delivery mechanism is AWS CloudFormation Hooks: a hook runs during a CloudFormation stack operation, after the template is submitted but before resources are provisioned, and can return PASS or FAIL (with FAIL aborting the stack op, or WARN to advise without blocking). Control Tower’s proactive controls are managed CloudFormation Hooks AWS authors and operates on your behalf.
The conceptual difference is sharp:
- An SCP can deny
s3:CreateBucketentirely, or deny it unless a request context key is present — but it reasons about the API call and its conditions, and many resource properties simply aren’t exposed as condition keys. - A proactive hook can allow
s3:CreateBucketbut inspect the full proposed bucket configuration and fail the stack if encryption is off or public-access-block is disabled — reasoning about the resource shape before it exists. - A detective Config rule would let the bad bucket be created and flag it afterward.
Proactive controls therefore give you “encryption-required” semantics with no non-compliant window at all, for resources deployed via CloudFormation, without the bluntness of denying the whole API.
Why it matters
Proactive controls move enforcement left, into the pipeline, with zero remediation lag. For infrastructure-as-code shops — which any serious landing zone is — this is the highest-leverage control type for property-level requirements: encryption, versioning, public-access blocks, deletion protection, mandatory tags, instance-type allowlists, log-retention minimums. They catch the violation at cloudformation:CreateStack time, returning an error to the developer in their deploy with a clear reason, rather than blocking an opaque API call (SCP) or filing a finding hours later (Config). The result is a tighter feedback loop, fewer exemptions, and fewer resources that ever enter a non-compliant state in the first place.
The trade-off is scope: a CloudFormation Hook only fires for resources deployed through CloudFormation (which includes Service Catalog, CDK, and Control Tower’s own Account Factory). Resources created by the console, raw API/SDK calls, or Terraform are not seen by the hook — which is precisely why proactive, preventive, and detective controls are layered together rather than chosen between.
How to do it well
- Use Control Tower’s managed proactive controls first. They ship as ready-to-enable hooks tied to common requirements (e.g. “require encryption on a resource type,” “disallow public access”). Enable them on the OUs where IaC is the deployment path.
- Standardise the deployment path on CloudFormation/Service Catalog so the hook actually fires. If half your estate deploys via Terraform, proactive coverage is patchy — close that gap with Config (detective) and SCPs (preventive), or run equivalent checks in the Terraform pipeline (e.g.
cfn-guard/OPA in CI) as the moral equivalent. - Author custom hooks with CloudFormation Guard. For org-specific rules, write a Guard ruleset (a concise DSL for asserting on resource properties) and register it as a hook — far easier than a Lambda-backed hook and version-controllable.
- Use
WARNto socialise,FAILto enforce. Land a new proactive control in advisoryWARNmode, measure how many deploys it would have blocked, communicate, then flip toFAIL. This is the proactive analogue of landing an SCP in a non-prod OU first. - Keep the message actionable. A hook failure should tell the developer exactly which property failed and what the requirement is, so they fix it in seconds rather than filing a support ticket.
Behaviour comparison: the layering matrix
| Property | Preventive (SCP) | Proactive (CFN Hook) | Detective (Config rule) |
|---|---|---|---|
| When it acts | At the API call | At stack provisioning, pre-create | After the resource exists |
| What it reasons about | API action + condition keys | Full proposed resource properties | Recorded resource state |
| Effect | Blocks the call | Blocks the stack op | Flags; can auto-remediate |
| Non-compliant window | None | None | Yes (detect/remediate lag) |
| Coverage | Every principal, every path | CloudFormation-deployed only | Every supported resource |
| Best for | “Never allowed at all” | “Property-level, in-pipeline” | “Drift + audit evidence” |
| Underlying service | AWS Organizations | AWS CloudFormation Hooks | AWS Config |
Concrete artifacts, decisions, and tools
- Artifacts: the enabled proactive-control list per OU, any custom Guard hook rulesets (in Git), the WARN→FAIL promotion plan, and the IaC-path standard that declares CloudFormation/Service Catalog the governed deployment mechanism.
- Decisions: which property requirements are enforced proactively vs left to detective; how non-CloudFormation deploys are covered; WARN vs FAIL per control; managed hooks vs custom Guard hooks.
- Tools/services: AWS CloudFormation Hooks, AWS Control Tower (managed proactive controls), CloudFormation Guard (
cfn-guard), AWS Service Catalog (the governed provisioning portal that ensures hooks fire), and AWS CDK (which synthesises to CloudFormation and is thus covered).
Mandatory, strongly-recommended and elective controls
What it is
This is the category axis of the Control Tower control library, orthogonal to behaviour. Every control AWS ships is classified by how strongly AWS recommends it and whether you can turn it off:
- Mandatory — always-on, applied automatically when you set up the landing zone or enroll an OU, and cannot be disabled. They are the bedrock that makes the landing zone trustworthy: they protect the central logging and the control machinery itself. Examples include disallowing changes to the CloudTrail configuration, disallowing deletion of the Log Archive, disallowing changes to the IAM roles Control Tower uses for logging/monitoring, and disallowing public read/write access to the log-archive buckets.
- Strongly recommended — controls AWS recommends for all customers, aligned to the Well-Architected Framework and AWS security best practice. They are optional but you should enable them broadly. Examples: detect public read/write on S3 buckets, detect unrestricted SSH/RDP, require encrypted EBS volumes, require MFA for the root user.
- Elective — common but situational locks that many organisations want but few need everywhere. You enable them surgically on the OUs that warrant them. Examples: disallow deletion of S3 buckets, disallow versioning changes, disallow certain cross-account actions.
A separate axis, guidance, also marks some controls ELECTIVE/STRONGLY_RECOMMENDED/MANDATORY in the API alongside their behaviour, and AWS additionally groups controls into control objectives (e.g. “Establish logging and monitoring,” “Encrypt data at rest,” “Limit network access”) and frameworks so you can enable a coherent set rather than cherry-picking. The library is large (hundreds of controls) and spans the behaviours above — so a single elective control might be implemented as an SCP, a Config rule, or a hook.
Why it matters
The category model is how you right-size enforcement per OU without re-deriving it from first principles. Mandatory controls remove the question of whether the landing zone’s own integrity is protected — it always is, you can’t switch it off, and an auditor can rely on that. Strongly-recommended controls give you an AWS-curated, Well-Architected baseline you can apply estate-wide with confidence. Elective controls let you tighten specific OUs (a regulated-data OU, a PCI OU) without imposing those locks on teams that would only file exemptions. The practical payoff is that you express your control posture as “which categories of control on which OU” — a small, reviewable matrix — instead of evaluating hundreds of individual controls account by account.
It also keeps you aligned with AWS’s roadmap: AWS adds and updates controls over time, and because mandatory/strongly-recommended sets are curated by AWS, enabling them broadly means you inherit improvements rather than maintaining a bespoke catalogue forever.
How to do it well
- Never fight the mandatory set. Don’t try to disable or work around it; design the landing zone so you never need to (e.g. never run workloads in the management account, never put deletable data in the Log Archive’s path).
- Turn on strongly-recommended broadly, at the OU level. Enable the recommended set on the Workloads OU (and Prod sub-OU) so it covers every current and future account beneath. Enabling at the OU — not the account — is what makes new accounts compliant on day zero.
- Apply elective controls by OU intent, not blanket. Map elective controls to the OUs whose purpose demands them (e.g. “disallow S3 deletion” on a records-retention OU). Blanket-applying electives generates exemption traffic and erodes trust.
- Drive it through the API/IaC, not the console. Enable controls with
controltower:EnableControlagainst the target OU ARN, and manage the set as code so it’s reviewable and reproducible. - Track behaviour while you choose category. Remember each control is a preventive/detective/proactive mechanism — so an “elective” choice still inherits the coverage and timing characteristics of its behaviour. Don’t pick an elective detective control for something that genuinely needs preventive timing.
Enabling a control on an OU
# Discover available controls and their behaviour/category
aws controltower list-controls
# Enable a strongly-recommended control on the Workloads-Prod OU
aws controltower enable-control \
--control-identifier "arn:aws:controltower:us-east-1::control/AWS-GR_RESTRICTED_SSH" \
--target-identifier "arn:aws:organizations::123456789012:ou/o-exampleorgid/ou-prod-xxxxxxxx"
# Inspect what's enabled on a target
aws controltower list-enabled-controls \
--target-identifier "arn:aws:organizations::123456789012:ou/o-exampleorgid/ou-prod-xxxxxxxx"
Category vs behaviour: how they combine
| Preventive (SCP) | Detective (Config) | Proactive (Hook) | |
|---|---|---|---|
| Mandatory | Protect CloudTrail/Config config; protect log roles | Detect missing CloudTrail; log-bucket public access | (Fewer; integrity-focused) |
| Strongly recommended | Region/IMDSv2 hardening (as enabled) | Public S3, open SSH/RDP, unencrypted EBS | Require encryption / no-public-access on create |
| Elective | Disallow specific cross-account actions | Detect optional posture items | Block creation of specific non-compliant types |
Concrete artifacts, decisions, and tools
- Artifacts: the control-enablement matrix (category → OU, plus the explicit elective list per OU), the as-code enablement definitions (
aws_controltower_control/ API calls in the pipeline), and a control-objective coverage map (which control objectives are satisfied where). - Decisions: which strongly-recommended controls are enabled estate-wide vs deferred; the elective list per OU; how custom SCPs/hooks complement Control Tower’s catalogue; review cadence for newly-released controls.
- Tools/services: AWS Control Tower (the control library,
EnableControl/ListControls/ListEnabledControls, drift detection), AWS Organizations (the OU targets), and Terraform’saws_controltower_controlresource for managing enablement as code.
Real-world enterprise scenario
Meridian Logistics, a freight and supply-chain company, runs a Control Tower landing zone with 140 accounts across a familiar OU hierarchy: Security (Log Archive, Audit), Infrastructure (shared networking, CI/CD), Workloads → Prod / NonProd, PCI (their payments and customer-billing systems), EU (data that must stay in-region under GDPR), and Sandbox. A SOC 2 audit and a new EU data-residency obligation force them to make the guardrail layer real and provable. They work it sub-component by sub-component.
Preventive (SCPs). They keep FullAWSAccess at the root and author six dense deny-list SCPs as code in a org-scps repo, deployed by Terraform. At the root: an SCP denying organizations:LeaveOrganization, an SCP protecting CloudTrail/Config and the Log Archive buckets, and a root-user hardening SCP that denies all root activity except a documented break-glass role. On the EU OU: a region-lock SCP using aws:RequestedRegion restricted to eu-west-1/eu-central-1, with a NotAction exemption for IAM, Organizations, Route 53, CloudFront, STS, Support, and an aws:PrincipalArn carve-out for the Control Tower execution role. On Workloads: an IMDSv2-required SCP (ec2:RunInstances denied unless ec2:MetadataHttpTokens = required) and a security-services-protection SCP (Deny guardduty:Delete*, securityhub:Disable*). They hit the 5-SCP-per-target ceiling on Workloads and consolidate two drafts into one. They also adopt a single RCP at the root denying any S3/KMS access from principals outside aws:PrincipalOrgID (with approved-service exemptions) to close the resource perimeter. Every policy ships through CI with an IAM Policy Simulator check and a non-prod-OU canary before promotion.
Detective (Config rules). They enable Control Tower’s strongly-recommended detective controls estate-wide and add a custom organization conformance pack mapped to CIS AWS Foundations Benchmark v3.0 plus AWS Foundational Security Best Practices, deployed from the Audit account (delegated Config administrator) so it lands on all 140 accounts and every future one. They stand up a Config aggregator in Audit for a single org-wide compliance view, route findings to Security Hub, and tune the configuration recorder to skip a few high-churn resource types to keep the Config bill predictable. Low-risk rules (S3 default-encryption, public-access-block, missing tags) get automatic SSM Automation remediation; security-group changes get manual-approval remediation so they never disrupt a running app.
Proactive (CloudFormation Hooks). Because all Prod and PCI infrastructure deploys through Service Catalog (CDK-synthesised CloudFormation), Meridian enables Control Tower’s managed proactive controls there — “require encryption” and “disallow public access” on S3, RDS, and EBS — so a non-compliant resource is blocked at stack-create time with zero non-compliant window. They author one custom cfn-guard hook asserting a mandatory DataClassification tag and a log-retention minimum, land it in WARN mode for two sprints to socialise it (it would have blocked 38 deploys), then flip it to FAIL. NonProd and Sandbox, which still allow some Terraform, rely on the detective pack plus a cfn-guard/OPA check in their Terraform CI as the moral equivalent.
Categories. Mandatory controls stay untouched. Strongly-recommended controls are enabled on the Workloads OU root (covering Prod and NonProd and every future child). Elective controls are applied surgically: “disallow S3 bucket deletion” and “disallow versioning changes” on the PCI OU (records retention), and a Sandbox service-allowlist SCP keeps experimentation cheap and safe. The whole posture is captured in a one-page control-enablement matrix (category → OU) plus the SCP-attachment matrix, both in Git.
Outcome. Within one quarter: org-wide Config compliance moved from 71% to 96% with the remaining 4% tracked as accepted exemptions; the EU region-lock made GDPR data-residency mechanically provable (auditors were shown the SCP, not a spreadsheet); zero unencrypted S3/EBS resources entered Prod after the proactive hooks went to FAIL; the SOC 2 audit’s “logging integrity” and “encryption at rest” controls were evidenced directly from mandatory controls and the conformance pack with no manual screenshotting; and exemption tickets dropped because most violations were now caught in-pipeline by developers rather than in production by the platform team.
Deliverables & checklist
Common pitfalls
- Region-locking yourself out of IAM. A blanket
aws:RequestedRegiondeny without exempting global services (which authenticate throughus-east-1) bricks IAM, Organizations, and Route 53 administration. Always pair the region lock with aNotActionexemption list and test in a non-prod OU first. - Running workloads — or anything — in the management account. SCPs do not apply to the management account, so nothing you put there can be fenced in, and it has org-admin blast radius. Keep it empty of workloads; the landing zone’s whole trust model assumes this.
- Editing Control Tower’s managed SCPs by hand. The
aws-guardrails-*policies are owned by Control Tower; manual edits cause drift, non-compliant OU status, and may be auto-reverted. Add your own SCPs alongside them instead. - Treating detective controls as if they prevented anything. Config flags drift after the fact, with a detect/remediate lag. If a requirement cannot tolerate a non-compliant window, it needs a preventive (SCP) or proactive (hook) control — not a Config rule alone.
- Assuming proactive hooks cover everything. CloudFormation Hooks only fire for CloudFormation/Service Catalog/CDK deployments — never for console, raw API, or Terraform. Layer detective + preventive controls to cover the un-hooked paths, or replicate the Guard checks in your Terraform CI.
- Hitting SCP limits late. Discovering the 5-per-target / 5,120-character ceilings after you’ve drafted ten single-purpose policies forces a painful rewrite. Design dense, consolidated, well-commented SCPs from the start, and use
NotAction/NotResourceto stay compact.
What’s next
With guardrails enforced through preventive SCPs, detective Config rules, proactive Hooks, and the right mix of mandatory/strongly-recommended/elective controls, Part 5 of AWS Landing Zone & Control Tower turns to account vending and customization at scale — automating compliant account provisioning with Account Factory and Account Factory for Terraform (AFT) so every new account is born inside these guardrails.