When your organization needs a new AWS account (a new workload environment, a team sandbox, or a specialized service account), you add it to organization-structure.ts and deploy. The landing zone creates the account, moves it into the right OU, and automatically rolls out the account baseline without any manual intervention: secure defaults in every member account and a Resource Explorer index in every member account except the FinOps account (which gets its own), plus CDK bootstrap and service quota increases for accounts in the Development and Production OUs. Because GuardDuty, Security Hub, Inspector, and Macie are configured org-wide from the security account, a new account is enrolled in whichever of those services you have enabled. The organization trail in the observability account also records the new account's activity.
Steps
1. Choose an OU and a unique email address
Pick the OU the new account belongs to. The starter ships with DevelopmentOU and ProductionOU for workload accounts, but any OU in your structure works. The OU decides which baseline StackSets the account receives; see What happens on deploy.
Choose an email address that has never been used for any AWS account globally. The aws+<alias>@<domain> plus-addressing pattern lets you generate unique addresses under a single domain:
aws+payments-prod@example.com
If you already manage multiple accounts under your domain, check your existing accounts first to confirm the address is free.
2. Add the account in organization-structure.ts
Open src/config/organization-structure.ts and add a new entry to the accounts map of the target OU. Keep the account key descriptive and PascalCase. If you publish its ID (see below), the key becomes the stack output name and the TypeScript property name on orgVars:
ProductionOU: {
name: 'workload-prod-ou',
budget: { monthlyLimit: 500 },
accounts: {
WorkloadAlphaAccount: {
name: 'Workload Alpha',
email: `aws+workload-alpha@${mailDomain}`,
},
// Add your new account here:
PaymentsAccount: {
name: 'Payments',
email: 'aws+payments-prod@example.com',
},
},
},
The name field is the display name shown in the AWS Organizations console. The email is the root email address for the new account.
A workload account like this one needs nothing more. The baseline StackSets target its OU, so it receives the baseline automatically, and the GuardDuty organization policy covers it, and Macie discovers it from AWS Organizations at deploy time. Only add publishAccountIdOutput: true if another stack needs to reference the account by ID through orgVars.PaymentsAccountId. Most workload accounts never do, and leaving it off keeps you well under the CloudFormation output limit.
To track the account's spend, add an optional budget to the account entry, for example budget: { monthlyLimit: 200 }. It creates a monthly AWS Budgets cost budget in the management account that emails alerts when actual or forecasted cost for the month exceeds the limit (AWS Budgets notifications). The account is already covered by any budget on its OU, as ProductionOU has in the example above, which tracks the combined cost of every account under it. Alerts go to the budget's own emailAddresses, else the nearest budgetAlertRecipients on an OU above it or on the root, else the account's email. See Organization Structure for the budget fields.
To attach account-specific SCPs at the same time, add a serviceControlPolicies array to the account entry. See Service Control Policies for the available SCPs and the 10-per-target limit.
3. Preview the change
Run a diff to confirm only the expected resources change before deploying:
pnpm run organization:diff
You should see a new AWS::Organizations::Account resource in the diff output for the LandingZoneOrganizationStack. If you set publishAccountIdOutput: true, you'll also see a new PaymentsAccountId stack output, and if you set a budget, a new AWS::Budgets::Budget resource.
4. Deploy
pnpm run organization:deploy
This deploys LandingZoneOrganizationStack to your management account: it creates the account and moves it into the target OU. You do not need to re-run the landing-zone phase for a new workload account. The service-managed baseline StackSets already target its OU, so AWS Organizations auto-deploys their stack instances to the account as soon as it joins.
For CI/CD, push to main and let the generated GitHub Actions workflow handle the deployment. See GitHub Actions Deployment for how that pipeline works.
5. Verify
After the deploy completes, confirm the account exists in AWS Organizations:
aws organizations list-accounts --query 'Accounts[?Name==`Payments`]'
If you opted the account into an ID output with publishAccountIdOutput: true, confirm it was published on the organization stack:
aws cloudformation describe-stacks --stack-name LandingZoneOrganizationStack \
--query 'Stacks[0].Outputs[?OutputKey==`PaymentsAccountId`].OutputValue'
The baseline StackSets run asynchronously after the account is created. Check the StackSet instance status in the CloudFormation console (under StackSets) to confirm the instance for the new account reached CURRENT.
What happens on deploy
LandingZoneOrganizationStack calls the AWS Organizations API to create the account and move it into the target OU. If the account sets publishAccountIdOutput: true, it also gets a PaymentsAccountId stack output that other stacks can consume through orgVars. Otherwise no account ID output is created, and the account is still fully provisioned through its OU.
The SetAlternateContactConstruct and UnsubscribeMarketingMailsConstruct in the same stack detect the new account email and run their automation against the new account: applying your configured billing, security, and operations contacts, and opting the account out of AWS marketing email.
The service-managed StackSets in LandingZoneMemberAccountsStack target OUs rather than explicit account lists, so AWS Organizations auto-deploys their stack instances to any new account that joins a targeted OU, without any changes to the stack code or a landing-zone redeploy:
| StackSet | Targets | Gives the new account |
|---|---|---|
AccountSecurityStackSet | Every member account | Secure account defaults |
ResourceExplorerStackSet | Every account except the FinOps account | A Resource Explorer index in every active Region |
CdkBootstrapStackSet | DevelopmentOU and ProductionOU | The CDK toolkit resources that cdk deploy uses |
ServiceQuotasStackSet | DevelopmentOU, ProductionOU, and SecurityOU | Your service quota requests |
An account in an OU that none of these target, such as one in a new OU, receives the first two. See Add an Organizational Unit to extend the targets. Quota increase requests are asynchronous and remain subject to AWS approval.