Account factory (AFT)
Account Factory for Terraform turns account creation from a manual checklist into a reviewed pull request. The account arrives with identity, logging, guardrails and the organisation's customisations already applied.
Why it exists
Creating accounts by hand produces accounts that differ. One has CloudTrail, one does not; one has the standard IAM roles, one has a variant; one has tags, one was created at 2 a.m. AFT makes the baseline a property of the account's creation, not of someone's memory.
How it works
account request (pull request)
│
▼
AFT pipeline ──► Control Tower Account Factory
│ │
│ ▼
│ new account in target OU
│ │
▼ ▼
global customisations ─► account customisations
(all accounts) (by OU / account)
Four repositories define the behaviour:
| Repository | Purpose |
|---|---|
aft-account-request | The requested accounts — one entry per account, reviewed like code |
aft-global-customizations | Applied to every account: baseline roles, Config, tags, logging |
aft-account-customizations | Applied by OU or by account: network, service quotas, specific roles |
aft-account-provisioning-customizations | Runs during provisioning, before the account is handed over |
Account request
Each account is a Terraform resource in the request repository:
module "prod_payments" {
source = "./modules/aft-account-request"
control_tower_parameters = {
AccountEmail = "aws-prod-payments@example.com"
AccountName = "prod-payments"
ManagedOrganizationalUnit = "Workloads-Prod"
SSOUserEmail = "platform-owner@example.com"
SSOUserFirstName = "Platform"
SSOUserLastName = "Owner"
}
account_tags = {
Environment = "prod"
Project = "payments"
Owner = "payments-team"
CostCenter = "cc-1042"
DataClassification = "confidential"
}
change_management_parameters = {
change_requested_by = "platform-team"
change_reason = "New payment service workload"
}
account_customizations_name = "payments"
}
What global customisations apply
- Standard IAM roles for pipeline access, with OIDC trust to the CI provider.
- The Terraform state lock table in
us-east-1. - Config recorder and delivery to the log archive account.
- CloudTrail organisation trail enrolment.
- Default EBS encryption.
- Baseline tags.
- GuardDuty membership in the delegated administrator account.
- Service quota increases required by the workload profile.
What provisioning customisations apply
- VPC and subnet layout for the environment.
- Transit Gateway attachment and route table entries.
- DNS delegation.
- Initial service-linked roles.
Controls on the process
Review
An account request is a pull request. Two reviewers, at least one from platform, approve before the pipeline runs.
Unique root email
Each account uses a distinct root email address. Reuse causes provisioning failure and is a security problem.
OU placement
The OU determines the guardrail profile. A production account is never created in a non-production OU.
Verification
Provisioning ends with a compliance check. An account that fails the check is not released to the workload team.
Audit
Every account has a recorded requester, reason and approval trail in version control.
Common problems
| Problem | Cause | Response |
|---|---|---|
| Account provisioning fails | Root email already used, or OU not enrolled | Use an unused alias; confirm the OU exists and is enrolled in Control Tower |
| Customisation did not apply | Account request applied before the customisation repository was updated | Re-run the customisation pipeline for the account |
| Account cannot assume baseline roles | Trust policy or permission set mismatch | Correct through the customisation repository; never patch the role in the console |
| Drift after handover | Manual changes in the account | Detect with Config, revert via pipeline, investigate access |