This guide will help you integrate Terraform with AWS Control Tower using the tfctl wrapper. This involves setting up resources for remote state tracking, necessary IAM roles and a tfctl project.
For state tracking we’re going to create a dedicated shared-services
account
under a mgmt
organization unit. We’ll use S3 for state storage and DynamoDB
for locking. TerraformState
IAM role will be created for cross account
access to state resources from the primary AWS account.
In the primary account we’ll create a TfctlOrgAccess
role. It gives tfctl
read only access to AWS Organizations which is used to discover accounts and
the organization unit structure.
We’ll use CloudFormation stacks and stack-sets to bootstrap these resources.
For executing Terraform in spoke accounts we’ll use the
AWSControlTowerExecution
role which is automatically created by Control Tower
account factory and can be assumed from the primary account.
We’re going to create a live
and test
organization units in Control Tower
and provision a couple of accounts for testing.
Before starting you’ll need:
-
An AWS account that is part of an Organization.
-
Control Tower set up in your primary AWS account.
-
A user with
AdministratorAccess
privileges in your primary AWS account. -
AWS CLI tools installed on your local machine.
-
Terraform 0.12 or higher.
Create the following organization units in Control Tower:
-
mgmt
-
live
-
test
Then provision accounts:
-
In
mgmt
OU create an account calledmgmt-shared-services
-
In
live
createlive-example1
-
In
test
createtest-example1
📎
|
Control Tower accounts need to be provisioned one at a time. It takes approximately 20 mins to provision one. |
It’s assumed you have configured AWS CLI access to the primary account.
We’ll use CloudFormation templates in examples/bootstrap/
.
First export configuration using environment variables, making sure to change to values to suit your setup:
# Change these to match your setup
export PRIMARY_ACCOUNT_ID=123456789012
export SHARED_SERVICES_ACCOUNT_ID=345678901234
export STATE_BUCKET_NAME='example-terraform-state'
export AWS_REGION=eu-west-1
Create the remote state resources stack set:
cd examples/bootstrap/
aws cloudformation create-stack-set \
--stack-set-name TerraformState \
--template-body file://terraform-state.template \
--description "Resources for managing Terraform state" \
--capabilities CAPABILITY_NAMED_IAM CAPABILITY_IAM \
--execution-role-name AWSControlTowerExecution \
--administration-role-arn arn:aws:iam::${PRIMARY_ACCOUNT_ID}:role/service-role/AWSControlTowerStackSetRole \
--parameters ParameterKey=PrimaryAccountId,ParameterValue=${PRIMARY_ACCOUNT_ID} \
ParameterKey=TerraformStateBucket,ParameterValue="${STATE_BUCKET_NAME}"
Create a stack set instance in your shared services account:
aws cloudformation create-stack-instances \
--stack-set-name TerraformState \
--accounts ${SHARED_SERVICES_ACCOUNT_ID} \
--regions ${AWS_REGION}
Check status:
aws cloudformation describe-stack-instance \
--stack-set-name TerraformState \
--stack-instance-account ${SHARED_SERVICES_ACCOUNT_ID} \
--stack-instance-region ${AWS_REGION}
📎
|
Initial status will be OUTDATED , it should change to CURRENT once deployed.
|
Deploy the TfctlOrgAccess
IAM role stack:
aws cloudformation create-stack \
--stack-name TfctlOrgAccess \
--template-body file://tfctl-org-access.template \
--capabilities CAPABILITY_NAMED_IAM CAPABILITY_IAM \
--parameters ParameterKey=PrimaryAccountId,ParameterValue=${PRIMARY_ACCOUNT_ID}
Check status:
aws cloudformation describe-stacks --stack-name TfctlOrgAccess
📎
|
Successful status should read: CREATE_COMPLETE .
|
Copy the example project directory examples/control_tower
somewhere convenient
and edit tfctl.yaml
.
You need to modify the following parameters:
-
tf_state_bucket
- set to$STATE_BUCKET_NAME
-
tf_state_role_arn
- set shared services account ID -
tfctl_role_arn
- set primary account ID -
primary_account
- set the primary account name. You can find it through AWS Organizations in the console.
💡
|
You should keep your project directory under version control. |
The example profile will create an S3 bucket in accounts under test
, live
and mgmt
OUs.
📎
|
Run tfctl commands from the root of you project directory. |
First, dump the configuration to verify everything works:
tfctl -s
This will not make any changes but will print out YAML containing the final, merged configuration data. It should contain a list of discovered accounts and their configuration.
Initialise Terraform for all discovered accounts:
tfctl --all -- init
Tfctl will run Terraform against all accounts in parallel.
plan
the Terraform:
tfctl --all -- plan
and apply
it:
tfctl --all -- apply