Build1 publisher2 min readPublished
The nested OU format AFT documents fails on the three accounts Control Tower owns
An engineer adopting Account Factory for Terraform onto nine hand-built AWS accounts hit an OU error on Log Archive, Audit and the management account, and the cause is a plain string comparison in shared_account.py.
The Engineer · Build desk

What happened
- Nine AWS accounts built by hand in the console over three years, already enrolled under a live Control Tower landing zone, were the estate the Account Factory for Terraform adoption had to work on.
- Six were ordinary workload accounts in ordinary OUs, and each picked up a pipeline within minutes once the request matched the account email and name exactly and named the OU.
- Control Tower's Log Archive, Audit and management accounts run down a separate code path, where get_shared_ids in shared_account.py reads three SSM parameters and returns exactly those three account IDs.
- The Log Archive request, written in the nested OU format the documentation uses, failed with "Unsupported action: Cannot change OU for a Shared CT account or CT management account".
Compiled by The EngineerSomething wrong?How this is made
Why it matters
- contradiction The post's title reports AWS Support saying the Control Tower management account cannot be imported into AFT, while the framework's own shared account list names it; an operator who takes the support answer as final plans around a limit the code does not have.
- capability An adopted account with a pipeline takes Terraform pushes like any other and can have its customizations re-invoked on demand, so hand-built accounts join the same workflow as vended ones.
- decision Anyone adopting AFT onto an older estate has to reconcile the Service Catalog provisioned product email against Organizations first, and re-register the account in Control Tower where the two have drifted.
- constraint A corrected request that touches only tags or change management fields never reaches Service Catalog, so the operator debugging an adoption sees a Step Function run and no visible change.
There is no terraform import in this procedure [3]. You write an account request file for an account that already exists, push it, and AFT compares the requested email against the accounts already in your organization [3][4]. On a match it skips provisioning and does everything after it: creates the AWSAFTExecution role, applies your tags, writes metadata to DynamoDB, and builds the account a customization pipeline [4].
That path has been in the tool for ordinary enrolled accounts since AFT 1.3.3, in February 2022 [6]. Shared account support followed about three months later [1]. Writing on dev.to, the engineer who did the work said the six ordinary workload accounts were "genuinely boring, which is the highest compliment you can pay infrastructure tooling" [8].
The failure on the Control Tower accounts comes down to a naming convention. AFT normally wants a nested OU written as Name (ou-id), so a second level OU is Sandbox (ou-abcd-1a2b3c4d), because a bare name is ambiguous once you have OUs at different depths [15]. Shared accounts ignore that convention entirely [21]. ou_contains_account does a plain string comparison against the OU Name that AWS Organizations returns, not the ID and not the path [16]. AFT ships a parser for the bracketed form, in helpers named ou_name_is_nested_format and get_name_and_id_from_nested_ou, and the source comment for it uses Sandbox (ou-1234-zxcv) as its example [17]. The shared path does not call it [21]. The request carrying the documented format therefore compares unequal, and the error names an OU change the operator never made [14].
Whether the result transfers depends on two things you can check before writing a request. Your AFT deployment has to be at 1.4.0 or later, because that is the release where the shared account code path landed [13]. And the OU string in the request has to be the bare name Organizations returns for that OU [16].
The estate here was nine accounts created by hand in the console over three years, on a landing zone that was already live, and deleting and re-vending production to get the clean start the tutorials assume was not on offer [1][19]. Three of the nine went down the shared path [18]. The post describes the remaining work as "two strings that nobody wrote down" [20], and the text breaks off before it gives the second one.
What to watch
- Whether AWS documents the OU string shared accounts expect, or leaves shared_account.py as the only record of it.
- Whether a later AFT release routes the shared account path through get_name_and_id_from_nested_ou so both OU formats parse.
- Whether AWS Support revises its answer on adopting the Control Tower management account into AFT.