Design a Multi-Account AWS Landing Zone with Control Tower
Stand up a governed AWS landing zone with guardrails, central logging, and compliant-by-default account vending.
What you'll build
A governed multi-account AWS environment: an AWS Control Tower landing zone (version 4.0) on top of AWS Organizations, with centralized CloudTrail and Config logging, preventive and detective guardrails, a registered Workloads OU, and Account Factory vending so every new account starts compliant by default.
Prerequisites
Verified against Control Tower landing zone 4.0 (released November 2025) and AWS CLI 2.36 — the controltower and controlcatalog commands used below need a recent v2 CLI.
- A fresh AWS account to serve as the management account, with root MFA enabled and a payment method attached. Don't reuse an account running workloads — the management account should do nothing but billing, OU management, and account vending.
- Three unique email addresses that aren't attached to any existing AWS account: one for the management account and one each for the two shared accounts Control Tower creates. Plus-addressing (
aws+logs@yourco.com) works fine. - No existing AWS Config recorder, Config delivery channel, or organization CloudTrail trail in the management account — these are the number-one cause of failed launches. Check before you start:
aws configservice describe-configuration-recorders
aws configservice describe-delivery-channels
Both should return empty lists.
- A decision on your home Region (where the wizard runs and shared resources live). It cannot be changed after setup without decommissioning the whole landing zone.
Control Tower itself is free; you pay for what it deploys — CloudTrail, Config, S3, CloudWatch. Expect a few dollars a month for an empty landing zone; Config rule evaluations dominate the bill as accounts multiply.
1. Launch the landing zone
Sign in to the management account as an administrator (not root), switch the console to your home Region, open the Control Tower console, and choose Set up landing zone. The wizard walks you through:
- Regions. Confirm the home Region, add any additional Regions to govern, and enable Region deny — it blocks API activity in ungoverned Regions org-wide, so shadow infrastructure can't appear in Stockholm while you're watching Virginia. Start with the minimum set of Regions you actually use; you can expand later, but every added Region multiplies Config costs and OU re-registration time.
- Organizational units. Accept the foundational OU (default name Security) and the additional OU (default Sandbox). Landing zone 4.0 no longer hard-enforces this structure, but keep the Security OU — it's where the service-integration accounts land.
- Shared accounts. Enter the two email addresses. In landing zone 4.0 the defaults are named CloudTrail Administrator (the log archive, an org-wide S3 repository of CloudTrail and Config data) and Aggregator account (the audit/security tooling account); on pre-4.0 landing zones these are called Log Archive and Audit. Rename them if you like.
- Service integrations. New in 4.0: AWS Config, CloudTrail (organization trail), SecurityRoles, and AWS Backup are individually optional. Leave Config, CloudTrail, and SecurityRoles enabled — they're the guardrail and logging backbone; Backup is genuinely optional. Note the dependency chain: SecurityRoles requires Config, and Identity Center requires SecurityRoles.
- Access. Keep AWS IAM Identity Center enabled unless you already run an external IdP. Keep the default S3 log retention (1 year standard, 10 years access logs) or set your own.
Review, tick the service-permissions acknowledgment, and choose Set up landing zone. Provisioning takes about 30 minutes. The result:
graph TD
R[Org root] --> M[Management account]
R --> S[Security OU]
R --> W[Workloads OU]
S --> L[CloudTrail Administrator<br/>log archive S3]
S --> A[Aggregator account<br/>audit + Config aggregation]
W --> P[project-alpha<br/>vended, guardrails inherited]
2. Create and register a Workloads OU
Don't put real workloads in Sandbox. In the Control Tower console go to Organizational units → Create resources → Create organizational unit, name it Workloads, and pick the org root as parent. Creating an OU through the Control Tower console (rather than the Organizations console) registers it: the AWSControlTowerBaseline is applied, which is what lets Account Factory target it and detective controls evaluate its accounts.
3. Enable guardrails beyond the mandatory set
The mandatory controls (protecting the log archive, CloudTrail integrity, etc.) enabled themselves at launch. Add strongly-recommended ones per OU. In the console: Controls → find control → Enable control on OU. From the CLI, find a control's ARN in the Control Catalog, then enable it — here, blocking public read on S3 buckets in Workloads:
# Find the control ARN
aws controlcatalog list-controls \
--query "Controls[?contains(Name,'public read access')].[Name,Arn]" \
--output table
# Get the OU ARN
aws organizations list-organizational-units-for-parent \
--parent-id "$(aws organizations list-roots --query 'Roots[0].Id' --output text)" \
--query "OrganizationalUnits[?Name=='Workloads'].Arn" --output text
# Enable it (substitute the two ARNs you just retrieved)
aws controltower enable-control \
--control-identifier "arn:aws:controlcatalog:::control/<id-from-list-controls>" \
--target-identifier "arn:aws:organizations::<mgmt-acct-id>:ou/<org-id>/<ou-id>"
Control Tower runs one governance action at a time (optional controls and up to ten Account Factory actions can run concurrently), so script sequential enable-control calls with a wait on the returned operationIdentifier.
4. Vend an account with Account Factory
This is the payoff: accounts born with guardrails, logging, and SSO already wired. In the Control Tower console choose Organizations → Create resources → Create account, then fill in the Account email (unused address), a display name, the IAM Identity Center email and name for the account's admin under Access configuration, and select the Workloads OU. Choose Create account. Provisioning takes ~30 minutes; up to five accounts can be in flight at once. Console users who aren't full admins must be in the AWSAccountFactory Identity Center group.
Verify it works
Check the landing zone status from the management account:
LZ_ARN=$(aws controltower list-landing-zones --query 'landingZones[0].arn' --output text)
aws controltower get-landing-zone --landing-zone-identifier "$LZ_ARN" \
--query 'landingZone.{version:version,status:status,drift:driftStatus.status}'
Expected output:
{
"version": "4.0",
"status": "ACTIVE",
"drift": "IN_SYNC"
}
Confirm the vended account exists and controls are live on the OU:
aws organizations list-accounts --query 'Accounts[].[Name,Status]' --output table
aws controltower list-enabled-controls --target-identifier <workloads-ou-arn> \
--query 'enabledControls[].[controlIdentifier,statusSummary.status]' --output table
Every control should show SUCCEEDED. Final smoke test: log into the vended account via the Identity Center access portal URL, try to create a resource in a denied Region, and watch it fail with an explicit deny — that's Region deny doing its job.
Troubleshooting
AWS Control Tower cannot create an AWS Config configuration recorder because one already exists. To continue, delete the existing delivery channel and try again.
Config was enabled at some point in the management account (or an account being enrolled). Disabling Config in the console isn't enough — delete the recorder and channel via CLI in every governed Region: aws configservice stop-configuration-recorder --configuration-recorder-name default, then delete-delivery-channel and delete-configuration-recorder. Retry.
No launch paths found for resource: prod-xxxxxxxxxxxxx when creating an account.
You're either logged in as root (unsupported for provisioning) or your Identity Center user isn't in the AWSAccountFactory group. Sign in as an eligible user, or add yourself to the group in Identity Center.
Landing zone launch fails with CloudFormation StackSet errors.
Usually AWS STS endpoints are deactivated for one of the governed Regions. In IAM → Account settings, activate STS for every Region Control Tower will govern, delete the failed AWSControlTowerBP-BASELINE-CLOUDTRAIL-MASTER stack, and run setup again.
AWS Control Tower can't enable the selected control on this OU ... child accounts have dependencies that are missing. Update all child accounts under the OU, then try again.
Happens after adding a governed Region. Re-register the OU (Organizational units → select OU → Re-register) to update its accounts, then enable the control.
Next steps
Automate vending with Account Factory for Terraform (AFT) so account requests become pull requests; add per-account baseline resources with Account Factory Customization blueprints; browse the Control Catalog for the 500+ optional controls, including Security Hub ones; and read the landing zone 4.0 key changes before toggling any service integration — disabling CentralizedLogging in 4.0 deletes resources, not just pauses them.
Sources & further reading
- AWS Control Tower quick start guide — docs.aws.amazon.com
- Landing zone 4.0 key changes — docs.aws.amazon.com
- Expectations for landing zone configuration — docs.aws.amazon.com
- Provision accounts in the AWS Control Tower console — docs.aws.amazon.com
- Troubleshooting AWS Control Tower — docs.aws.amazon.com
- AWS CLI reference: controltower enable-control — docs.aws.amazon.com
Emeka has spent over a decade tracking threat actors, vulnerability disclosures, and the evolving landscape of application security, bringing a sharp continent-spanning perspective to his reporting. He's known for translating dense CVE advisories into clear, actionable context that developers and security teams alike actually read.
Discussion 0
No comments yet
Be the first to weigh in.