Architecting Scalable AWS Control Tower Environments with Account Factory for Terraform

AWS Control Tower Account Factory for Terraform, commonly referred to as AFT, represents a paradigm shift in how organizations provision and manage multi-account AWS environments. While native AWS Control Tower provides robust governance, guardrails, and service catalog capabilities, it historically lacked the deep customizability required for complex enterprise infrastructure defined as code. AFT bridges this gap by integrating the declarative nature of Terraform with the compliance engine of Control Tower. It functions as a Terraform module maintained by AWS that allows engineers to create and customize new accounts that strictly adhere to organizational security guidelines. By defining a pipeline for automated and consistent creation, AFT delivers the benefits of Terraform's stateful workflow alongside Control Tower's inherent governance features. This approach enables infrastructure teams to treat account provisioning not as a one-off manual task, but as a repeatable, version-controlled, and automated process integrated into their CI/CD pipelines.

Core Architecture and Module Structure

The fundamental unit of operation in AFT is the aft module, which sources directly from the GitHub repository github.com/aws-ia/terraform-aws-control_tower_account_factory. When deploying AFT, engineers instantiate this module in their root Terraform configuration, typically found in a file named main.tf. The module acts as the orchestrator, managing the creation of underlying infrastructure, IAM roles, Lambda functions, and the S3 buckets required to host the customizations and state data.

A critical architectural component of AFT is its separation of concerns regarding configuration. The system is divided into distinct repositories that handle different levels of customization scope. This segregation ensures that global organizational standards do not conflict with specific account requirements. The primary input variables required for the aft module instantiation include the VCS provider (such as GitHub) and the names of the four essential repositories:

  1. account_request_repo_name: The repository containing the Terraform code that defines the basic attributes of the new account request.
  2. account_provisioning_customizations_repo_name: The repository containing Terraform code for infrastructure resources provisioned in the new account.
  3. global_customizations_repo_name: The repository containing Terraform code for resources provisioned in the AFT management account and potentially other shared accounts.
  4. account_customizations_repo_name: The repository containing Terraform code for resources provisioned in the target account based on specific labels or tags.

This modular design allows for granular control. For instance, the account_request_repo_name variable must match a subdirectory within the designated repository. This structure supports multiple account templates within a single repository, allowing an organization to maintain separate definitions for a "Log Archive" account, a "Shared Services" account, and a "Development" account, all within the same version-controlled ecosystem.

Directory Structure and Helper Scripts

The AFT repository structure is designed to facilitate both the provisioning of the AFT framework itself and the execution of custom scripts. A typical directory layout includes shell scripts and Python utilities that assist in the pre-API phase and backend configuration. The structure often looks like this:

text ├── pre-api-helpers.sh ├── python │ └── requirements.txt └── terraform ├── aft-providers.jinja └── backend.jinja

The pre-api-helpers.sh script and the associated Python environment (requirements.txt) handle tasks that occur before the main API calls are made, such as setting up local dependencies or preparing environment variables. The terraform directory contains Jinja2 templates (.jinja files) like aft-providers.jinja and backend.jinja. These templates are rendered to generate the final Terraform configuration for providers and backends, allowing dynamic injection of values based on the deployment context. It is important to note that the initial AFT configuration does not define any global customizations for specific accounts; rather, it sets up the machinery to apply those customizations.

Configuration and Input Variables

The behavior and networking capabilities of AFT are heavily influenced by a set of boolean and string input variables. These flags allow engineers to toggle specific features on or off, ensuring the deployed infrastructure matches organizational policies. The following table details the critical AFT-specific input variables, their types, default values, and descriptions.

Variable Name Description Type Default Required
aft_enable_vpc Flag turning use of VPC on/off for AFT bool true No
aft_feature_cloudtrail_data_events Feature flag toggling CloudTrail data events on/off bool false No
aft_feature_delete_default_vpcs_enabled Feature flag toggling deletion of default VPCs on/off bool false No
aft_feature_enterprise_support Feature flag toggling Enterprise Support enrollment on/off bool false No
aft_framework_repo_git_ref Git branch from which the AFT framework should be sourced from string null No
aft_framework_repo_url Git repo URL where the AFT framework should be sourced from string https://github.com/aws-ia/terraform-aws-control_tower_account_factory.git No
aft_management_account_id AFT Management Account ID string n/a Yes
aft_metrics_reporting Flag toggling reporting of operational metrics bool true No
aft_vpc_cidr CIDR Block to allocate to the AFT VPC string "192.168.0.0/22" No
aft_vpc_endpoints Flag turning VPC endpoints on/off for AFT VPC bool true No
aft_vpc_private_subnet_01_cidr CIDR Block to allocate to the Private Subnet 01 string "192.168.0.0/24" No
aft_vpc_private_subnet_02_cidr CIDR Block to allocate to the Private Subnet 02 string "192.168.1.0/24" No
aft_vpc_public_subnet_01_cidr CIDR Block to allocate to the Public Subnet 01 string "192.168.2.0/25" No
aft_vpc_public_subnet_02_cidr CIDR Block to allocate to the Public Subnet 02 string null No

In addition to feature flags, the networking configuration is precise. By default, AFT creates a VPC with a CIDR block of 192.168.0.0/22. This VPC is segmented into two private subnets (192.168.0.0/24 and 192.168.1.0/24) and two public subnets. The default configuration for the first public subnet is 192.168.2.0/25. The aft_vpc_endpoints flag, when enabled (default true), ensures that the AFT VPC has the necessary VPC endpoints for communication with AWS services without traversing the public internet, enhancing security and latency performance.

It is crucial to understand the implications of these flags. For example, enabling aft_feature_delete_default_vpcs_enabled will trigger the deletion of default VPCs in the accounts provisioned by AFT. This is a powerful cleanup mechanism but requires careful coordination to avoid breaking existing infrastructure that might be relying on default VPCs. Similarly, aft_feature_cloudtrail_data_events allows for the collection of data events in CloudTrail, which is essential for auditing S3 object-level access but comes with significant cost and storage implications.

Versioning, Dependencies, and State Management

The stability of an AFT deployment relies heavily on strict version pinning and proper state management. AFT requires specific versions of Terraform and the AWS provider to function correctly. Using incompatible versions can lead to plan errors or resource drift that the framework cannot handle.

Required Software Versions

The following table outlines the mandatory version constraints for Terraform and the AWS provider.

Component Name Version Constraint
Core terraform >= 1.6.1, < 2.0.0
Provider aws >= 6.0.0, < 7.0.0

The AWS provider requirement is strict. Any customization modules written by the user must also adhere to the aws provider version range of >= 6.0.0, < 7.0.0. The local provider has no specific version constraint (n/a), but it is often used for file operations in helper scripts.

Internal Module Composition

AFT is not a monolithic script; it is composed of several internal modules that handle specific aspects of the provisioning framework. Understanding these internal modules aids in troubleshooting and advanced customization.

Module Name Source Path
aft_account_provisioning_framework ./modules/aft-account-provisioning-framework
aft_account_request_framework ./modules/aft-account-request-framework
aft_backend ./modules/aft-backend
aft_code_repositories ./modules/aft-code-repositories
aft_customizations ./modules/aft-customizations
aft_feature_options ./modules/aft-feature-options
aft_iam_roles ./modules/aft-iam-roles
aft_lambda_layer ./modules/aft-lambda-layer
aft_ssm_parameters ./modules/aft-ssm-parameters
packaging ./modules/aft-archives

These modules work in concert. aft_iam_roles ensures that the necessary permissions are granted to the Lambda functions that orchestrate the account creation. aft_backend handles the configuration of the S3 bucket and DynamoDB table used to store Terraform state, although AFT itself does not manage the state file. This distinction is vital: AFT creates the infrastructure for the state backend, but the user is responsible for the state file itself.

State File Security and Persistence

The Terraform state file is the single source of truth for the AFT deployment. It describes the state of all resources created by Terraform. Because AFT does not manage the backend state internally, it is the user's responsibility to protect this file. If you plan to update the AFT version, you must preserve the Terraform state file or configure a persistent backend using Amazon S3 and DynamoDB.

Security considerations are paramount. The state file may contain sensitive values, such as private SSH keys or Terraform tokens, depending on how input variables are handled. In many deployment methods, these values can be viewable as plain text in the state file. Engineers must encrypt the S3 bucket storing the state file and apply strict IAM policies to restrict access to the state file. Loss of the state file without a backup can lead to an orphaned infrastructure that is difficult to decommission or update, as Terraform will no longer be able to track the resources it has created.

Provisioning Workflow and Customization Logic

The AFT workflow is triggered by the addition of a new Terraform configuration to the account_request_repo. When a new subdirectory is added to this repository, AFT detects the change and initiates the account creation pipeline. The process involves several stages:

  1. Request Validation: The framework validates the account request parameters against the defined schemas.
  2. Account Creation: A new AWS account is created within the Control Tower landing zone.
  3. Customization Application: AFT applies the customizations defined in the account_customizations_repo and global_customizations_repo.
  4. Provisioning: The account_provisioning_customizations_repo is used to deploy infrastructure resources into the new account.

A key aspect of this workflow is the separation of account customizations. AFT applies Account customizations to a specific account or set of accounts based on the customizations defined in the repository passed to the account_customizations_repo_name input variable. This allows for label-based customization. For example, if an account is tagged with environment=prod, specific Terraform modules can be applied to that account only, ensuring production-grade security configurations are in place.

It is important to note that certain customizations, such as specific guardrails or control tower settings, must be maintained outside of the AFT Terraform code. These configurations are often re-applied after AFT updates to ensure consistency, as AFT focuses on the account lifecycle and infrastructure provisioning rather than every possible Control Tower policy setting.

Deployment and Decommissioning

Deploying AFT requires specific prerequisites. Engineers must have Terraform v0.15 or later installed locally (though the module now recommends v1.6.1+), configured with credentials for a non-root user with AdministratorAccess privileges. The AWS account credentials must also have the AdministratorAccess policy attached. Given that some provisioning steps can take up to 30 minutes, credentials should have a long enough duration to complete the process without expiring.

Prerequisites

  • Terraform v0.15+ installed locally, configured with credentials for a non-root user with AdministratorAccess.
  • An AWS account with credentials for a non-root user with the AdministratorAccess policy attached.
  • Long-lived credentials to handle the 30-minute potential duration of provisioning steps.

The Decommissioning Process

Decommissioning AFT is a multi-step process that requires careful attention to prevent data loss or orphaned resources. The process involves the following sequence:

  1. Delete the Pipeline: Navigate to the AWS CodePipeline console. Select the AFT pipeline and click Delete pipeline, confirming the deletion. This stops any further account provisioning.
  2. Empty and Delete Buckets: AFT creates S3 buckets in the Log Archive and AFT Management accounts for state and code storage. These must be emptied and deleted. The AFT repository includes a helper script for this purpose.

    bash $ ./remove_buckets.sh

    Running this script ensures that the buckets do not retain data that could incur costs or cause deletion errors.

  3. Decommission the Sandbox Account: If a sandbox account was used for testing, it must be decommissioned from the Control Tower landing zone. This disassociates the account from Control Tower but does not close the AWS account. Log in to the AWS Console as the Control Tower management user. Navigate to Provisioned Products in ServiceCatalog. Select the Account Access Filter and choose the target account. Under Actions, click Terminate, then confirm by clicking Terminate provisioned product. This removes the account from Control Tower and moves it under the Root organization.

  4. Destroy AFT Resources: Finally, run terraform destroy to delete the AFT framework resources from the management account.

    bash $ terraform destroy

    Terraform will display the plan, which typically shows 0 to add, 0 to change, and approximately 297 to destroy. The output will list changes to outputs, such as:

    ```text
    Changes to Outputs:

    • aftmanagementaccount_id = "365371481900" -> null
    • auditaccountid = "187724155000" -> null
    • ctmanagementaccount_id = "447475458655" -> null
    • logarchiveaccount_id = "866255933133" -> null
    • region = "us-east-1" -> null
      ```

    You will be prompted to confirm the operation: Do you really want to destroy all resources?. Responding yes proceeds with the deletion of all managed infrastructure. It is critical to understand that this action is irreversible. Terraform will destroy all managed infrastructure, and there is no undo mechanism.

Conclusion

AWS Control Tower Account Factory for Terraform is a sophisticated framework that empowers engineering teams to bring infrastructure-as-code discipline to multi-account AWS environments. By leveraging the aft module, organizations can enforce security guidelines, automate account creation, and manage complex customization layers through distinct Git repositories. The framework's reliance on specific Terraform and AWS provider versions ensures stability, while its feature flags provide granular control over networking, support plans, and audit capabilities.

Success with AFT hinges on rigorous adherence to its architectural patterns. Engineers must manage the state file securely, understand the internal module composition, and follow the precise decommissioning steps to avoid orphaned resources. The separation of global and account-specific customizations allows for scalable governance, ensuring that as the number of accounts grows, the consistency and security posture remain intact. For organizations seeking to move beyond the limitations of pure GUI-based Control Tower provisioning, AFT offers a robust, code-driven solution that aligns with modern DevOps practices.

Sources

  1. AWS Control Tower AFT Tutorial
  2. terraform-aws-controltoweraccount_factory
  3. AFT Getting Started

Related Posts