The modern AWS multi-account environment has evolved far beyond the simple creation of separate logins. Today, enterprises rely on complex landing zones where security, compliance, and operational efficiency are non-negotiable requirements. AWS Control Tower serves as the central engine for this governance, automating the setup and management of a secure multi-account environment by building upon AWS Organizations, Service Catalog, and AWS Config. However, managing these governance structures through manual console clicks or static scripts introduces fragility, lack of version control, and limited auditability. This is where Terraform emerges as a critical component. By integrating Terraform with AWS Control Tower, teams can move their infrastructure from a reactive posture to a proactive, code-driven state. This integration allows for the automated provisioning of Organizational Units (OUs), the consistent application of guardrails, and the streamlined creation of new accounts through the Account Factory for Terraform (AFT). This article provides a comprehensive technical deep dive into the architecture, workflows, and best practices for managing AWS Control Tower using Terraform, focusing on the AFT pipeline, control management, and secure state handling.
Core Architecture of Control Tower and Terraform
To understand the integration, one must first dissect the components that AWS Control Tower orchestrates. Control Tower is not a single service but an umbrella that coordinates several AWS services to create a pre-configured landing zone. The primary structural element is the Landing Zone, which represents the overall multi-account environment. Within this structure, Organizational Units (OUs) group accounts that share common policies and governance requirements. For instance, an organization might maintain a Security OU, a Workloads OU for production applications, and a Sandbox OU for developer experimentation.
Two specialized accounts are automatically provisioned by Control Tower to maintain governance integrity: the Log Archive Account and the Audit Account. The Log Archive Account centralizes logging from all other accounts, ensuring that data cannot be deleted or tampered with by application-level admins. The Audit Account is isolated from the rest of the organization and is used for security and compliance auditing, preventing the auditors from being influenced by the workload configurations they are monitoring.
Terraform interacts with this structure by treating these components as manageable resources. Instead of creating an OU through the console and manually attaching SCPs (Service Control Policies), a Terraform configuration defines the OU and the associated aws_controltower_control resources. This approach ensures that the governance layer is as versioned and reproducible as the application infrastructure. The following table outlines the primary components of the Control Tower ecosystem and their corresponding Terraform management strategies.
| Control Tower Component | Function | Terraform Management Approach |
|---|---|---|
| Landing Zone | Overall multi-account environment structure | Managed via AFT pipeline or manual aws_controltower_enrolled resources |
| Organizational Units (OUs) | Groups of accounts with shared policies | Created and modified using aws_organizations_organizational_unit resources |
| Guardrails | Preventive and detective controls | Implemented via aws_controltower_control resources referencing global control IDs |
| Account Factory | Automated account provisioning | Automated via AFT pipeline triggered by Git commits |
| Log Archive Account | Centralized logging | Automatically managed by Control Tower; access via cross-account IAM roles |
| Audit Account | Security and compliance auditing | Automatically managed by Control Tower; isolated from management account |
The integration relies on a specific trust model. Terraform should never run directly from the Management account for day-to-day resource delivery. Instead, a dedicated Shared Services or CI/CD account hosts the deployment runners. To enable cross-account deployments, the execution runner in the CI/CD account assumes an IAM role within the target workload account. Control Tower automatically provisions an administrative role named AWSControlTowerExecution in every managed account. Terraform configurations leverage this pre-existing trust relationship to assume the execution role, allowing centralized pipelines to provision resources across the entire organizational landscape without exposing permanent root or administrator credentials. This pattern eliminates the need for distributing static credentials across the organization, significantly reducing the attack surface.
Account Factory for Terraform (AFT) Workflow
While managing controls and OUs is foundational, the primary value proposition of integrating Terraform with Control Tower lies in the Account Factory for Terraform (AFT). AFT is a Terraform module, maintained by AWS, that makes it easy to create and customize new accounts that comply with your organization's security guidelines. AFT defines a pipeline for the automated and consistent creation of AWS Control Tower accounts, combining the benefits of Terraform's workflow with Control Tower's governance features.
The AFT workflow is triggered not by direct Terraform plan/apply commands for account creation, but by a simple Git push. Users create an account request Terraform file, which serves as the input to trigger the AFT workflow. This file specifies the details of the new account, such as the name, email, and target OU. When this file is pushed to the repository, the AFT pipeline detects the change and initiates the provisioning process.
The AFT pipeline supports HCP Terraform, Terraform Enterprise, and Terraform Community Edition. The workflow consists of distinct stages. First, the account provisioning stage occurs, where Control Tower creates the new account within the specified OU. This stage ensures that all standard governance guardrails are applied immediately upon creation. After the account provisioning stage is complete, AFT automatically runs a series of steps before the account customizations stage begins. This customization stage is where organization-specific Terraform code is applied to the new account, allowing for the deployment of network infrastructure, IAM users, and other baseline resources.
A critical feature of AFT is account customization request tracing. Every time a customization request is submitted, AFT generates a unique tracing token. This token passes through an AFT customizations AWS Step Functions state machine, which logs the token as part of its execution. This traceability is vital for debugging and auditing. Engineers can use Amazon CloudWatch Logs insights queries to search specific timestamp ranges and retrieve the request token, allowing them to correlate a specific account customization issue with the exact pipeline execution and logs. This level of observability is far superior to traditional manual provisioning, where failures are often opaque and difficult to diagnose.
The following table summarizes the prerequisites and supported environments for the AFT module.
| Prerequisite / Requirement | Specification / Detail |
|---|---|
| Terraform Version | v0.15+ installed locally |
| AWS Credentials | Non-root user with AdministratorAccess policy attached |
| Credential Duration | Long enough duration (some steps take up to 30 minutes) |
| Supported Terraform Engines | HCP Terraform, Terraform Enterprise, Terraform Community Edition |
| Tracing Mechanism | Unique token logged via AWS Step Functions |
| Log Retrieval | Amazon CloudWatch Logs Insights queries |
Managing Control Tower Controls with Terraform
Beyond account creation, Terraform is essential for managing the governance controls (guardrails) themselves. AWS Control Tower controls are high-level rules that provide ongoing governance for the environment. They can be preventive, detective, or proactive. Preventive controls are designed to stop actions from occurring, typically implemented with Service Control Policies (SCPs) or Resource Control Policies (RCPs) in AWS Organizations. Detective controls monitor for non-compliance and send notifications or take corrective actions.
When managing these controls via Terraform, it is crucial to use the correct identifier format. Previous versions of the Terraform AWS Provider and Control Tower integration relied on regional identifiers. However, these are now legacy. The current best practice, and requirement for new deployments, is to use global identifiers. Global identifiers have the format:
arn:<PARTITION>:controlcatalog:::control/<CONTROL_CATALOG_OPAQUE_ID>
In most cases, the value for <PARTITION> is aws. The shift to global identifiers allows for easier management and expands the number of controls that can be used. Using legacy regional identifiers may result in errors or deprecated behavior.
The aws_controltower_control resource in Terraform allows you to enable specific controls on a specific target, usually an OU. The control_identifier specifies the control, and the target_identifier specifies the OU ARN where the control is enforced. This applies the control to the entire OU, meaning every AWS account within that OU is subject to the rule. For example, you might enforce MFA for root accounts across all production workloads, or block public access to S3 buckets organization-wide.
The following code examples demonstrate how to enable various controls using Terraform. Note the use of global control identifiers.
```hcl
Enable strongly recommended and elective controls on the Workloads OU
resource "awscontroltowercontrol" "disallowpublics3" {
controlidentifier = "arn:aws:controlcatalog:::control/AWS-GRS3ACCOUNTLEVELPUBLICACCESSBLOCKSPERIODIC"
targetidentifier = awsorganizationsorganizationalunit.workloads.arn
}
resource "awscontroltowercontrol" "rootaccountmfa" {
controlidentifier = "arn:aws:controlcatalog:::control/AWS-GRROOTACCOUNTMFAENABLED"
targetidentifier = awsorganizationsorganizational_unit.workloads.arn
}
Enable specific controls for the Production OU
resource "awscontroltowercontrol" "disallowinternetaccess" {
controlidentifier = "arn:aws:controlcatalog:::control/AWS-GRDISALLOWVPCINTERNETACCESS"
targetidentifier = awsorganizationsorganizational_unit.production.arn
}
resource "awscontroltowercontrol" "requireencryption" {
controlidentifier = "arn:aws:controlcatalog:::control/AWS-GRENCRYPTEDVOLUMES"
targetidentifier = awsorganizationsorganizationalunit.workloads.arn
}
resource "awscontroltowercontrol" "ebsoptimizedinstance" {
controlidentifier = "arn:aws:controlcatalog:::control/AWS-GREBSOPTIMIZEDINSTANCE"
targetidentifier = awsorganizationsorganizationalunit.workloads.arn
}
```
The following table categorizes the types of controls and their typical implementations.
| Control Behavior | Implementation Mechanism | Status/Outcome | Example Control |
|---|---|---|---|
| Preventive | SCPs or RCPs in AWS Organizations | Enforced or Not Enabled | Disallow VPC Internet Access |
| Detective | Config Rules and Monitoring | Compliant or Non-Compliant | Root Account MFA Enabled |
| Proactive | Automated Remediation or Notifications | Action Taken | Block Public S3 Access |
Secure State Management in Multi-Account Environments
State management represents the most critical component of a production-grade Terraform deployment. In a multi-account Control Tower environment, storing state locally or within individual application accounts introduces massive security risks and concurrency conflicts. If state is stored in a workload account, a compromised workload could potentially alter the Terraform state, leading to unauthorized infrastructure changes. Furthermore, if multiple engineers run Terraform against different accounts without a centralized locking mechanism, race conditions can corrupt the state file.
The industry standard mandates isolating the state architecture inside a centralized, highly secure account, typically the Audit account or a dedicated Shared Services tooling account. This state storage architecture utilizes an Amazon S3 bucket protected by strict bucket policies, mandatory multi-factor authentication (MFA) delete, and server-side encryption via AWS Key Management Service (KMS).
The S3 bucket should be configured to reject writes from any principal other than the specific IAM roles used by the Terraform runners. Additionally, DynamoDB is often used to provide state locking, preventing concurrent execution of Terraform against the same state file. This centralized approach ensures that all infrastructure changes are auditable, locked, and secured against unauthorized access. The separation of state storage from the workload accounts also allows for centralized monitoring of infrastructure changes, aligning with the security posture enforced by Control Tower.
Conclusion
The integration of Terraform with AWS Control Tower represents a paradigm shift in cloud governance, moving from manual, error-prone console operations to a fully automated, code-driven infrastructure lifecycle. By leveraging the Account Factory for Terraform (AFT), organizations can standardize account creation, ensuring that every new account inherits the necessary security guardrails and is customized according to organizational standards through a traceable, pipeline-driven process. The use of global identifiers for control management ensures that the governance layer is scalable and aligned with current AWS best practices. Furthermore, the enforcement of a secure, centralized state management architecture mitigates the significant security and operational risks associated with multi-account Terraform deployments.
The technical depth required to implement this stack is substantial, necessitating a robust understanding of both Terraform workflows and AWS organizational structures. However, the rewards are significant: a landing zone that is not only secure by design but also resilient, observable, and capable of rapid, consistent expansion. As enterprises continue to expand their AWS footprint, the ability to manage governance and infrastructure through a single, integrated codebase becomes not just a best practice, but a fundamental requirement for operational excellence.
Sources
- HashiCorp Developer: AWS Control Tower Account Factory for Terraform
- OneUptime: Create Control Tower with Terraform
- AWS Documentation: Overview of AWS Control Tower Account Factory for Terraform (AFT)
- DevOps Roles: Deploy Terraform on AWS with Control Tower
- AWS Prescriptive Guidance: Deploy and manage AWS Control Tower controls by using Terraform