AWS Control Tower is easiest to understand as an orchestrator. It does not enforce anything by itself. When it blocks an action, an AWS Organizations policy is doing the blocking. When it reports a non-compliant bucket, an AWS Config rule found it. When it stops a CloudFormation stack from creating an unencrypted volume, a CloudFormation hook said no. Control Tower decides which of those mechanisms to deploy, where to deploy them, and keeps them from drifting. It also gives you an account vending process and a dashboard over the result.
Changes are asynchronous because they fan out across many accounts, and drift exists because the underlying resources can still be edited by hand. This guide works from that model: the landing zone, the three kinds of control, baselines and enrollment, the API you would automate with, the changes in landing zone 4.0, and a worked rollout for a mid-sized organization. For the policy-evaluation mechanics underneath, read AWS Organizations and SCPs alongside this page.
The model: landing zone, OUs and hub accounts
A landing zone is the multi-account environment Control Tower manages. It has a version (for example 3.3 or 4.0), a home Region where Control Tower's own resources live, a list of governed Regions, and a manifest that describes which integrations are on. Since late 2023 the landing zone can be created and updated through the CreateLandingZone and UpdateLandingZone APIs, so it can live in code rather than in console clicks.
Inside the organization, Control Tower works at the level of organizational units. You register an OU, and every account in it becomes governed. Controls are enabled on OUs, never on single accounts, and they apply to every account below that OU. This is the most important design constraint: if two accounts need different guardrails, they belong in different OUs.
The classic layout uses three shared accounts. The management account holds the organization and Control Tower itself. The Log Archive account receives the organization CloudTrail trail and AWS Config history in locked-down S3 buckets. The Audit account (often called the security tooling account) gets cross-account roles for security teams and receives notifications. Up to landing zone 3.x these two hub accounts had to live in a Security OU. Landing zone 4.0 drops that requirement; the only rule left is that the hub accounts share an OU.
The model: landing zone, OUs and hub accounts
A landing zone is the multi-account environment Control Tower manages. It has a version (for example 3.3 or 4.0), a home Region where Control Tower's own resources live, a list of governed Regions, and a manifest that describes which integrations are on. Since late 2023 the landing zone can be created and updated through the CreateLandingZone and UpdateLandingZone APIs, so it can live in code rather than in console clicks.
Inside the organization, Control Tower works at the level of organizational units. You register an OU, and every account in it becomes governed. Controls are enabled on OUs, never on single accounts, and they apply to every account below that OU. This is the most important design constraint: if two accounts need different guardrails, they belong in different OUs.
The classic layout uses three shared accounts. The management account holds the organization and Control Tower itself. The Log Archive account receives the organization CloudTrail trail and AWS Config history in locked-down S3 buckets. The Audit account (often called the security tooling account) gets cross-account roles for security teams and receives notifications. Up to landing zone 3.x these two hub accounts had to live in a Security OU. Landing zone 4.0 drops that requirement; the only rule left is that the hub accounts share an OU.
Controls: behaviour, implementation and guidance
A control is a named, versioned governance rule with a behaviour, an implementation and a guidance level. Behaviour says when it acts:
| Behaviour | Acts when | Implemented by | Typical example |
|---|---|---|---|
| Preventive | At API call time, before the action happens | SCPs; resource control policies (since Nov 2024); declarative policies (since Dec 2024) | Deny actions outside approved Regions |
| Detective | After the fact, on configuration change or schedule | AWS Config rules, deployed as service-linked rules since June 2025 | Flag S3 buckets without versioning |
| Proactive | When CloudFormation provisions a resource | CloudFormation hooks managed by Control Tower | Reject an EC2 instance type outside the allowed list |
Guidance says how optional it is. Mandatory controls protect Control Tower's own resources (its roles, the log buckets, the trail) and cannot be turned off. Strongly recommended controls encode common best practice. Elective controls are there if you need them. The EnableControl API only accepts strongly recommended and elective controls, plus the Region deny control.
Two practical consequences follow from the implementation column. First, preventive controls share the organization's SCP budget: AWS Organizations limits how many SCPs attach to a target and how large each one can be, and Control Tower packs its controls into the policies it manages. If you attach many of your own SCPs to the same OUs, you can run out of room. Second, proactive controls only see CloudFormation. A resource created through the console, the CLI or Terraform's AWS provider calling APIs directly never passes through a hook, so a proactive control is a fast-feedback tool for pipelines, not a perimeter. Pair it with a detective control on the same property if you need to catch every path.
All controls are listed in the Control Catalog (renamed from the Controls Library in June 2025), which has its own API: ListControls and GetControl return metadata including behaviour, severity, implementation, governed resource types and accepted parameters, and ListControlMappings maps controls to frameworks such as PCI DSS.
Baselines, enrollment and account vending
Registering an OU is, under the hood, enabling a baseline on it. The AWSControlTowerBaseline baseline deploys the per-account plumbing (roles, the Config recorder where that integration is on, and so on) and enrolls every account in the OU. The baseline APIs (EnableBaseline, ListEnabledBaselines, and related calls) arrived in 2024, and since May 2025 ListEnabledBaselines with includeChildren reports drift and enrollment status per account, which is what your monitoring should poll.
New accounts arrive through one of three routes. Account Factory is the built-in route: a Service Catalog product that creates an account in a chosen OU and enrolls it, optionally applying a blueprint. Account Factory for Terraform (AFT) is an AWS-maintained Terraform module that runs a pipeline: you commit an account request to a Git repository, and it vends the account and applies global and per-account Terraform customizations. Customizations for Control Tower (CfCT) does the same with CloudFormation and StackSets, driven by a manifest. Pick the one that matches your existing infrastructure-as-code tool.
Moving existing accounts between OUs used to create drift. Since October 2025, on landing zone 3.1 or later, you can opt in to auto-enrollment by setting the landing zone's RemediationType to Inheritance Drift. With it, an account moved into a registered OU (even with the Organizations MoveAccount API) picks up that OU's baseline and controls, and loses the previous OU's.
What landing zone 4.0 changed
Landing zone 4.0, released on 17 November 2025, is the biggest change to the service's shape since it launched. Earlier versions were all-or-nothing: you got CloudTrail, Config, the Security OU and the shared accounts whether or not you already ran your own equivalents. Version 4.0 makes the pieces optional.
- Optional service integrations. AWS Config, AWS CloudTrail, Security Roles and AWS Backup can each be enabled or disabled. Disabling one cleans up the resources Control Tower deployed for it. There is a dependency: to disable the Config integration you must also disable Security Roles, IAM Identity Center and Backup.
- Dedicated resources. Config and CloudTrail get separate S3 buckets and each service gets its own SNS topic, instead of shared ones. Bucket policies and lifecycle rules can now differ per service.
- Flexible structure. No mandatory Security OU; hub accounts just need to sit in the same OU.
- A controls-only setup. The manifest is optional, so you can create a minimal landing zone that integrates with Organizations and lets you enable controls without enabling
AWSControlTowerBaselineon your OUs. This suits organizations that already have mature logging and only want the catalog of managed policies and rules. - Config changes. A separate Config spoke baseline for detective controls, and a service-linked Config aggregator in the Config hub account replacing the older organization and account aggregators.
The catch is that a controls-only setup leaves you responsible for whatever you turned off. If you disable the CloudTrail integration, nothing in Control Tower will tell you that your own organization trail has stopped delivering. Read AWS's landing zone 4.0 migration guide before updating, and rehearse the update in a test organization first.
Controls as code
Everything above can be driven through two API clients: controlcatalog to find controls and controltower to enable them. Control operations are asynchronous: EnableControl returns an operationIdentifier (kept for 90 days) that you poll with GetControlOperation. The script below looks controls up by name rather than hard-coding ARNs, because ARN formats have changed over the service's history and the catalog is the source of truth.
import time
import boto3
HOME = "eu-west-1" # your landing zone home Region
ct = boto3.client("controltower", region_name=HOME)
cat = boto3.client("controlcatalog", region_name=HOME)
def find_controls(fragment):
"""Return (arn, name, behavior) for catalog controls whose name contains fragment."""
found, token = [], None
while True:
page = cat.list_controls(**({"NextToken": token} if token else {}))
for ctl in page["Controls"]:
if fragment.lower() in ctl["Name"].lower():
found.append((ctl["Arn"], ctl["Name"], ctl.get("Behavior")))
token = page.get("NextToken")
if not token:
return found
def enable(control_arn, ou_arn, parameters=None, poll=15):
"""Enable one control on one OU and block until the operation finishes."""
req = {"controlIdentifier": control_arn, "targetIdentifier": ou_arn}
if parameters: # e.g. [{"key": "...", "value": [...]}]
req["parameters"] = parameters # read accepted keys from GetControl first
op_id = ct.enable_control(**req)["operationIdentifier"]
while True:
op = ct.get_control_operation(operationIdentifier=op_id)["controlOperation"]
if op["status"] != "IN_PROGRESS":
return op # SUCCEEDED or FAILED, with statusMessage
time.sleep(poll)
for arn, name, behavior in find_controls("versioning"):
print(behavior, name, arn)For declarative management, CloudFormation has an AWS::ControlTower::EnabledControl resource with ControlIdentifier, TargetIdentifier, optional Parameters and Tags, and Terraform's AWS provider has an equivalent resource. Keep one stack per OU so a failed control enablement rolls back a small blast radius. Respect the concurrency quota (100 concurrent control operations since May 2024) and handle ConflictException: Control Tower rejects a new operation on a target while another one is still changing it, so a pipeline that fires twenty enables in parallel against one OU should serialize them.
Drift and repair
Drift is the gap between what Control Tower deployed and what exists now. Common causes: someone edits or detaches a Control Tower-managed SCP, deletes one of its IAM roles, moves an account between OUs outside Control Tower without auto-enrollment, deletes a registered OU, or turns off trusted access for Control Tower in Organizations. Control Tower scans its managed SCPs daily and surfaces drift in the console and through the baseline APIs. Since August 2025, two former drift types (a control SCP attached to another OU, or attached directly to a member account) are handled by Control Tower itself and no longer count as drift.
Repair is targeted. ResetEnabledControl (November 2024) redeploys one control. Re-registering an OU repairs its baseline and controls. Resetting the landing zone repairs the shared resources and mandatory controls. Unrepaired drift can block operations such as landing zone updates, so alert on it and fix it the same day.
Worked example: governing a 40-account organization
Take a company with about 40 accounts in a hand-built organization: one shared production account that grew too big, a dozen team accounts, data-platform accounts and personal sandboxes. Security wants a single audit trail and a Region restriction; engineering wants account vending that takes minutes, not tickets.
- Design OUs around guardrail sets, not org charts. Security (hub accounts), Infrastructure (networking, shared services), Workloads/Prod, Workloads/NonProd, Sandbox, and Suspended. Teams get an account in each environment OU rather than an OU of their own.
- Set up the landing zone in the existing organization with eu-west-1 as home Region and two governed Regions. Bring the existing security and logging accounts rather than creating new ones, which Control Tower has supported since 2022.
- Register Sandbox first. It has the least to lose, and it shows what breaks.
- Enable controls in stages: detective controls on every OU first (they report, never block), then preventive controls on NonProd for two weeks, then Prod. Enable the Region deny control last, with exemptions for global services you use (IAM, Organizations, CloudFront, Route 53 and similar) checked against its parameters.
- Move vending to AFT, with account requests reviewed as pull requests.
- Enroll the remaining accounts by OU, using auto-enrollment so moving an account is the enrollment.
The result: governance attaches to the OU tree and the audit trail lands in one account nobody else can write to.
Failure modes
| Symptom | Likely cause | What to do |
|---|---|---|
| Deploys fail with AccessDenied after enabling a control | A preventive control's SCP or RCP is denying the call, often Region deny or a deny on root-user actions | Read the CloudTrail event's error; check which policy denied it; adjust the control's parameters or the OU placement, not the SCP by hand |
| Landing zone update refuses to start | Unrepaired drift, or an earlier operation still running | Check ListLandingZoneOperations; repair drift; retry |
| Pipeline gets ConflictException | Parallel control operations on the same OU | Serialize per target; retry with backoff |
| Account enrollment fails | Pre-existing Config recorder or delivery channel in the account, or missing execution role | Remove the conflicting recorder, or adopt it per the docs, then re-enroll |
| Config remediations disappeared | Control rules moved to service-linked rules, which do not accept remediation configurations | Run remediation from EventBridge and automation instead of attaching it to the rule |
| Proactive control did not stop a resource | The resource was created outside CloudFormation | Add the matching detective control |
Trade-offs
Control Tower gives you a supported, upgradeable baseline and a large managed control catalog for little effort. In exchange it constrains you to OU-level control assignment, asynchronous operations, and a set of shared resources you must not touch. Building the same thing yourself with Organizations, StackSets and Terraform gives full control and no drift concept, but you own every policy, every upgrade and every new Region. Many teams combine Control Tower for the landing zone and catalog with AFT or CfCT and their own SCPs. Whatever you choose, keep identity design separate; the IAM fundamentals and IAM condition keys decide what people can do inside the guardrails, and AWS Config is where detective findings ultimately live.
What to do next
- List your accounts and group them by the guardrails they need; turn the groups into an OU design before touching Control Tower.
- Check your landing zone version (or decide on 4.0 for a new one) and read the 4.0 migration guide if you are on 3.x.
- Run the catalog script above and shortlist detective controls for every OU; enable them first.
- Pilot preventive controls on a sandbox OU, then non-production, then production, with a rollback plan per stage.
- Put enabled controls in CloudFormation or Terraform, one stack per OU, serialized in the pipeline.
- Poll
ListEnabledBaselineswithincludeChildrenand alert on drift; repair the same day. - Choose one vending path (Account Factory, AFT or CfCT) and retire the others.