Manage AWS accounts using Control Tower Account Factory for Terraform
AWS Control Tower Account Factory for Terraform (AFT) is a Terraform module that makes it easy to create and customize new accounts that comply with your organization's security guidelines. AFT defines a pipeline for automated and consistent creation of AWS Control Tower accounts, giving you the benefits of Terraform's workflow and Control Tower's governance features. AWS maintains this module.
This tutorial guides you through the one-time steps required to deploy AFT to create the pipeline for account creation. Then, you will to use AFT to create and customize your Control Tower accounts. In this tutorial, you will deploy the AFT module, review the support account customization options, and learn about the components of AFT and its workflow.
Prerequisites
This tutorial assumes that you are familiar with the standard Terraform workflow. If you are new to Terraform, complete the Get Started tutorials first.
Note
This is an advanced tutorial that provisions many resources and requires administrator privileges in your AWS account. You must run these steps as an AWS user with the AdministratorAccess
policy.
For this tutorial, you will need:
- Terraform v0.15+ installed locally configured with credentials for the non-root user with
AdministratorAccess
- an AWS account, with credentials for a non-root user with the
AdministratorAccess
policy attached. Some steps can take up to 30 minutes, so make sure your credentials have a long enough duration. - AWS Control Tower enabled, with the default Log and Audit accounts created
- the AWS CLI
- a Github account
Create AWS AFT organizational unit and account
This tutorial will primarily reference two AWS accounts:
- the Control Tower management account, in which you launched your Control Tower landing zone, and
- the AFT management account, which you will provision in this section. The AFT module creates most of its resources in this account.
In the AWS dashboard, navigate to Control Tower, then select Organizational Units (OU) in the left sidebar. Click Add an OU to create a new organizational unit for AFT.
Note
You must be authenticated as a non-root user to complete these steps.
Name the OU Learn AFT
and select your Root OU as the parent OU, then click Add.
Once your new OU is ready, navigate to Account Factory in the left sidebar, then click Enroll Account.
Enter an email address not associated with existing AWS accounts in the Account email field. This will be the account's root email address. Enter learn-aft
for the display name; enter an email address you have access to for the SSO email, and enter Learn
and AFT
for the AWS SSO user’s first and
last name. Then, set Learn AFT as the Parent OU. Finally, click
Enroll Account.
It can take 20 to 30 minutes to provision a new account in Control Tower. Continue on to the next section while you wait for AWS to complete provisioning your AFT management account.
Clone and fork example configuration
This tutorial requires working across 5 repositories: one with the AFT module deployment and 4 that the module requires you to define your account specifications in. You will only run Terraform commands within the first repository, and AFT will execute the configuration in the account specification repositories.
AFT supports multiple VCS providers (AWS CodeCommit, Github, Bitbucket, and GitHub Enterprise Server) for the required repositories. By default, it uses AWS CodeCommit and will provision CodeCommit repositories for you to use.
This tutorial uses Github as the VCS provider for AFT.
First, clone the example repository containing the AFT module configuration.
Next, fork the four account configuration repositories into your personal Github account.
- The
learn-terraform-aft-account-request
repository, which contains example configuration to kick off new account provisioning using AFT. - The
learn-terraform-aft-global-customizations
repository, which contains boilerplate configuration for customizations to apply to all accounts created by AFT. - The
learn-terraform-aft-account-customizations
repository, which contains boilerplate configuration for account-specific customizations. - The
learn-terraform-aft-account-provisioning-customizations
repository, which contains boilerplate configuration for provisioning-time customizations to apply to accounts.
Then, clone your copies of the repositories.
Clone your fork of the
learn-terraform-aft-account-request
repository, replacingUSERNAME
with your own Github username.Clone your fork of the
learn-terraform-aft-global-customizations
repository, replacingUSERNAME
with your own Github username.Clone your fork of the
learn-terraform-aft-account-customizations
repository, replacingUSERNAME
with your own Github username.Clone your fork of the
learn-terraform-aft-account-provisioning-customizations
repository, replacingUSERNAME
with your own Github username.
Deploy AFT module
The AWS team maintains the control_tower_account_factory
Terraform
module
. This module defines a pipeline of AWS services that allow you to provision
and customize accounts in Control Tower. In this section, you will deploy the
module and review its services and resources.
In your terminal, navigate to the learn-terraform-aws-control-tower-aft
repository you cloned earlier.
Update AFT module configuration
Open the main.tf
file in your code editor to review and modify the module
configuration. This file is the minimal configuration for deploying AFT.
This module provisions resources across the Log, Audit, Control Tower Management, and AFT management accounts in your Landing Zone. Navigate to the Control Tower dashboard to access the respective account IDs and update the configuration.
For example, to get the AFT management account ID, go to your Control Tower account details
page
and select the learn-aft
account, the AFT management account you created
earlier. AWS displays the account ID on this page.
Update terraform.tfvars
with this value, and repeat this step for the other
accounts.
Input variable | Account name (assuming defaults used) |
---|---|
ct_management_account_id | Your root account name |
log_archive_account_id | Log Archive |
audit_account_id | Audit |
aft_management_account_id | learn-aft |
For the ct_home_region
input variable, use the same region as the one Control
Tower is enabled in. Verify the region in the top right hand corner of the AWS
console. You must deploy the module in the same region as your Control
Tower enablement.
AFT creates a backend to store Terraform state. The primary backend region is
the same as the region you enabled Control Tower in. It also replicates the
data to a secondary region. Set the tf_backend_secondary_region
variable to
the replication region of your choice.
While AFT supports multiple VCS providers, this tutorial uses Github and
enables it using the vcs_provider
input variable in the module. Set the
github_username
input variable in terraform.tfvars
to your own to point the
pipeline to your forked repositories for account creation and customization.
You can review additional AFT configuration options in more detail in the AWS documentation. In addition to the input variables defined, you can also define specific branches in your VCS repositories for AFT to track and configure which version and edition of Terraform the pipeline uses to deploy your resources. You can also enable feature flags, such as deleting the default VPC in accounts or enabling organization-level CloudTrail logging.
Apply configuration
Once AWS finishes provisioning the AFT management account you created in the Create AWS AFT organization unit and account section, deploy the AFT module.
In your terminal, confirm that your AWS credentials are configured for a user
with AdministratorAccess
privileges in your Control Tower management account.
Use the AWS CLI to verify which user you are authenticated as. The value for Account
should
match your ct_management_account_id
input variable.
Initialize the configuration to install the AWS provider and download the AFT module.
Now, apply your configuration to provision all of AFT’s services. Respond yes
to the prompt to confirm the operation.
Note
This step can take up to 30 minutes. Make sure the credentials you use will not expire in that timeframe.
While Terraform provisions the AFT module, continue on to the next section to review the components of the pipeline.
Review AFT components and workflow
AFT uses a collection of AWS services to help you manage AWS Control Tower accounts through Terraform configuration. One advantage of AFT over manual account provisioning is the ability to queue multiple account requests with your configuration. AWS Control Tower currently allows you to create only one account at a time, but AFT uses DynamoDB and SQS to queue your account requests, making batched account creation more efficient. As you define your account specifications across the 4 repositories, the CodeBuild job watches for changes to those repositories.
When you use AFT to create a new account, you must first define a new instance
of the account request module in your account request repository (for example,
learn-terraform-aft-account-request
). You must also add any customizations you wish to apply to the account to the account customization repositories.
Once you push your changes to version control, AFT kicks of a workflow to provision and customize your account:
- CodePipeline kicks off CodeBuild projects to create a DynamoDB table item with your new account details. The new item triggers a Lambda that records your account requests in SQS, allowing you to define multiple new accounts at once.
- The new SQS messages trigger Lambda functions to process your account request and kick off the account vending process in Control Tower. AFT also creates an account-specific pipeline to manage your new account’s customization and an execution role in your new account that it can use to customize it.
- Once AFT creates your new account, it triggers additional Lambda functions that then activate your account-specific pipeline to apply global and account-specific customizations. If your customizations include Terraform configuration to create resources in your account, it stores the state for those resources in S3. AFT uses the execution role it created to apply account customizations within your new account.
Along the way, the pipeline forwards its logs to S3 and logs pipeline events to SNS topics that you can subscribe to. Review the documentation to learn more about all of the component services in the pipeline.
There are 3 categories of customizations, each managed in a distinct repository that you passed as an input variable to the module. The pipeline executes them in the following order:
- The provisioning customizations run during account provisioning and use the state machine you define in your AFT management account.
- The global customizations apply to all accounts.
- The account customizations only apply to a subset of accounts. If you declare an input variable named
account_customizations_name
in your account request file, the pipeline will use the customizations defined in a subdirectory of the same name in the account customizations repository.
You will review the types of account customizations in more detail later in this tutorial once you provision a new account.
Review provider configuration
Navigate to the
.terraform/modules/aft
directory to
review the module configuration.
In your code editor, open providers.tf
.
This configuration defines multiple instances of the AWS provider, one for
each account and region supplied in the root module configuration. The provider
definitions reference the account IDs you supplied as input variables in the
assume_role
blocks, enabling the pipeline to provision resources across
multiple accounts without additional credentials or configuration. The AFT
module created the AWSControlTowerExecution
IAM role in each of the other
accounts to enable its functions.
Enable CodeStar Connection
The AFT module sets up a CodeStar connection to watch for changes to your Github account request and customization repositories. In this section, you will approve the CodeStar connection to enable the service.
Once Terraform completes deploying the AFT module, log in to your AFT management account using the email you specified in its configuration earlier.
Tip
Use a different browser or incognito mode to avoid logging in and out of multiple accounts.
Then, navigate to your CodeSuite connections in the AWS Console.
Click on the ct-aft-github-connection
connection, which is still pending.
On the details page, click Update pending connection.
Follow the workflow to Install a new app, and connect it to your personal Github account. After configuring it, click Connect to enable the AWS Connector for Github.
Grant AFT access to Service Catalog portfolio
Though AFT runs in your AFT management account, it needs access to your
Control Tower management account in order to fulfill your account
provisioning requests. To enable access, you must add the AWSAFTExecution
IAM
role created by the pipeline to Control Tower’s Service Catalog portfolio.
Log into the Control Tower management account in the AWS console. This is
the account in which you enabled Control Tower and manually created the
learn-aft
account at the beginning of this tutorial.
Then, navigate to Portfolios in the Service Catalog page.
Click on the AWS Control Tower Account Factory Portfolio
.
Select the Groups, roles, and users tab, then click Add groups, roles, users.
Select the Roles tab, then search for AWSAFTExecution
. Check the box next
to it, and click Add access.
Rerun account provisioning pipeline
When you first deploy AFT, it attempts to run the account provisioning customizations pipeline but fails since you had not yet enabled the CodeStar connection. AWS automatically runs the pipeline on creation. You must now rerun the pipeline to create its downstream resources for when you are ready to provision an account using AFT.
In your AFT management account, navigate to your CodePipeline
page.
Select the ct-aft-account-provisioning-customizations
pipeline, then click
Release change. Then, click Release to rerun the pipeline.
This concludes the one-time configuration steps that are necessary to set up AFT.
Deploy an account with AFT
Once you have deployed AFT and granted Github and Service Catalog access, you can use the pipeline to provision a new account in Control Tower.
Navigate to your cloned learn-terraform-aft-account-request
repository.
You can skip to the next section to provision the account without reviewing its configuration.
Review repository
AFT expects your account request repository to have the following structure:
Your configuration must live in a terraform
directory, with any account
requests defined in Terraform configuration at its top level. It must also
contain the aft-providers.jinja
and backend.jinja
files and the
modules/aft-account-request
directory, which defines a module that creates a
DynamoDB item. AFT and its CodeBuild jobs require this structure and
that you include the module definition in your account request repository.
Open the terraform/modules/aft-account-request/ddb.tf
file in your code editor.
The control_tower_parameters
attribute contains the required arguments for
creating an account in Control Tower. The other attributes are specific to AFT
and allow you to customize your account.
Now open the terraform/main.tf
file, which contains an instance of the
aft-account-request
module. You must define a new, uniquely named instance of
the account request module for each account you create.
In addition to control_tower_parameters
, which configure your new sandbox-aft
account, the module has the following inputs:
- the
account_tags
attribute lets you apply tags to your account according to your business criteria - the
change_management_parameters
attribute lets you document who issues the account request and its purposes - the
custom_fields
attribute lets you define additional metadata for your account, which you can use in your account customizations or provisioning configuration - the
account_customizations_name
attribute lets you specify the subdirectory in the account customizations repository the pipeline should use to modify this account, if any
Modify account request configuration
In the configuration, replace <ACCOUNT_EMAIL>
with the root email address you
wish to use for this sandbox account, which must not be associated with any
existing AWS accounts. Replace <SSO_EMAIL>
with the email address you wish to
use for SSO login. For ease, you can use the same email as you did for your AFT
management account, and make sure this is an email address you have access to.
Note
The root account email address you provide must be unique and not associated with any existing accounts. Your account name (sandbox-aft
in this tutorial) must be unique with your organization. If you are using nested OUs, include the OU ID in parentheses, such as Sandbox (ou-44...)
.
In your terminal, add your configuration changes.
Then, commit your changes.
Finally, push your changes.
The AWS CodePipeline AFT created listens for changes to your account requests and customizations repositories. It will apply launch a CodeBuild job to apply this Terraform configuration and create the DynamoDB item with your account specifications. That new item will trigger the rest of the pipeline to create and customize your new account.
As before, creating a new account can take between 20 and 30 minutes. Continue on to the next section to review account customizations in the meanwhile.
Review account customization options
AFT can apply customizations to your accounts after it creates them. The customizations can be Bash or Python scripts, interact with the AWS CLI, or apply Terraform configuration to create predefined resources in your new account. AFT creates a CodePipeline in the AFT management account to customize your AFT-provisioned accounts.
Like the account requests, the customizations live in VCS repositories and must conform to a directory structure required by AWS for the CodeBuild jobs to succeed.
In these sections, you will only review the account customization options defined in your forked Github repositories. You will not modify any of these files.
Account provisioning customizations framework
AFT applies account provisioning customizations as it creates your account.
This configuration is more advanced and requres knowledge of AWS Step
Functions. AFT uses the repository supplied for the
account_provisioning_customizations_repo_name
input variable in your AFT
module definition for these customizations.
AFT expects the configuration in this repository to define AWS Step Functions. These are the first customizations that AWS will apply to your account, which will run during provisioning. Step Functions can integrate with other AWS services, such as launching a Lambda or Step Function, or writing to SNS topics or SQS queues.
The learn-terraform-aft-account-provisioning-customizations
repository you
cloned contains the framework for this state machine with a no-op Step
Function. To add this customization to your account, you would define the state
machine in Terraform configuration in this repository and commit your changes
to version control. To learn more about Step Functions and creating a state
machine, review the
AFT
and Step Functions documentation
pages.
Global customizations
Global customizations apply to all accounts deployed by AFT. This allows you to automatically enforce security standards or provision standardized resources and infrastructure in every new account, making compliance with your organization’s standards easier.
You must define your global customizations in the repository you pass to the
AFT module as the global_customizations_repo_name
input variable.
Navigate to your fork of the global customizations repository.
This repository contains boilerplate configuration for global customizations to your accounts.
This configuration does not define any global customizations for your account.
Tip
If your custom configurations are Python scripts, you must place
them in the api_helpers/python
directory of the repository. If they are Bash
scripts, place them in the api_helpers
directory. Terraform configuration
should be added to the terraform
directory.
Account customizations
AFT applies Account customizations to a specific account or set of accounts.
It uses the customizations defined in the repository you pass to the
account_customizations_repo_name
input variable.
The value of that input variable must match a subdirectory of your account customizations repository. You can use account customizations to make specific modifications to groups of accounts, such as applying stricter permissions guardrails to accounts that run production resources.
Navigate to your fork of the account customizations repository.
The structure of this repository and its usage is similar to that of the global
customizations repository, but the customizations are organized within
subdirectories. These subdirectory names are the ones you can pass as the
account_customizations_name
input variable to your account request module.
In this case, the account customizations repository defines a group of
sandbox
customizations. The terraform
directory contains configuration for
an S3 bucket, which AFT will apply to the new account. This customization can
apply to any account that you include the account_customizations = "sandbox"
input variable.
This S3 bucket is just one example of an account-specific customization you can
create. To create other customization groups, copy this directory structure and
rename the top level directory (sandbox
).
Inspect new account
Verify that AFT created your new sandbox account by finding the sandbox-aft
account in the list of accounts in your Control Tower
Accounts dashboard.
Once AFT finishes provisioning the account, you should receive an email to
configure SSO login for the user. Follow the steps to create a password and
login as your sandbox-aft
user.
In the AWS dashboard, navigate to S3 to find your sandbox bucket, which AFT created as part of your account customizations.
You have now used AFT to provision and customize a new account.
Clean up provisioned resources
Now that you have used AFT for your account management, you can destroy the pipeline and its related resources.
Some of the resources created by the pipeline are managed dynamically outside of the Terraform workflow and require manual deletion. You also need to empty some of the S3 buckets created across the various Control Tower accounts.
Delete pipeline in AFT management account
First, log in to your AFT management account in the AWS console, and navigate to the CodePipeline dashboard.
Select the pipeline created for your sandbox account, which contains an account ID in the name. Select the pipeline, then click Delete pipeline and confirm the deletion.
Delete S3 buckets in accounts
Next, navigate to your learn-terraform-aws-control-tower-aft
repository,
which contains a remove_buckets.sh
script to empty and delete buckets created
in your log archive and AFT management accounts.
Note
If you are using a non-default AWS profile for this tutorial, update the source_profile
name in the remove_buckets.sh
script.
Run the script.
Delete sandbox account
Next, decommission your sandbox-aft
account from your Control Tower landing zone. This disassociates your account, but does not close it.
Log in to the AWS Console as your Control Tower management user, then
navigate to your Provisioned Products in
ServiceCatalog.
Select the Account
Access Filter, then select your sandbox-aft
account.
Under Actions, click Terminate. Then click Terminate provisioned
product to confirm the termination. This will remove the account from Control
Tower, and move it under the Root organization for your account.
Delete AFT resources
Then, run terraform destroy
to delete the AFT resources from your account.
Respond yes to the prompt to confirm the operation.
After you destroy these resources, you will need to close the sandbox-aft
and
AFT management accounts you created, which will require logging into each using
the root user email address you provided. Follow the AWS
documentation
for instructions on how to close an AWS account.
Next steps
You have now deployed AFT and used the pipeline to provision and customize a sandbox account. Using AFT allows you to leverage the benefits of both infrastructure as code's safety and scalability and Control Tower's governance features for multi-account architectures.
To learn more about Terraform, AWS, and the concepts covered in this tutorial check out the following resources:
- Learn how to use the AWS Cloud Control provider, which supports even more AWS resources.
- Learn how to configure AssumeRole access in the Terraform AWS provider to provision resources across accounts.
- Review the AFT documentation to learn more about AFT and Control Tower.