Skip to content

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

Illustration accompanying The nested OU format AFT documents fails on the three accounts Control Tower owns

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.
Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories