AWS Control Tower - Reducing Cost 1
This post explains some customizations I did to save AWS Control Tower costs. I followed the next article to set it up: Manage AWS Accounts Using Control Tower Account Factory for Terraform. I recommend you read it before continuing. Basic knowledge about AWS and Terraform is required.
Contextโ
Whenever I want to start a new project, I create an AWS account for each environment, I do it to separate domains and costs. So far, I have been doing this manually, but then I discover the Account Factory for Terraform (AFT) as part of the AWS Control Tower services.
AFT defines a GitOps process to automate the account creation and update. It is based on two main elements:
- Git repositories where the account creation request and customizations are defined.
- AWS resources used in the account creation/update process. All of them are based on the repository code. Some of them are CodePipeline, Dynamo tables, Lambdas, and Step Functions.
I don't want to go into details, but I think it is worth mentioning that this automatization simplifies the whole process, not only because we avoid manual operations (that is always nice, right?), but because it allows us to align different AWS accounts.
A customization example could be to define a Budget after creating the account. We can add a notification when we are close to a maximum amount (in fact, this was my main goal at first, to avoid any unexpected cost ๐). Next are other examples:
- Create default resources like S3 buckets
- Add SSM parameters to describe the account
- Add custom policies
I'm not going to go into the customization details in this article. I just want to point out a few examples. I will explain it in a separate post in the future.
Problem: Costsโ
AFT does not have a cost itself, the cost depends on the resources deployed and used. Next are the main costs per resource after a few days of using it:
- VPC Endpoints: billed hourly.
- NatGateway.
I found it in the Cost Explorer of my root account, grouping by API Operation:

Solutionโ
Delete the VPC Endpoints when not usedโ
The AFT repository (terraform-aws-control_tower_account_factory) offers the flag aft_vpc_endpoints to enable/disable the VPC Endpoints. As those are billed hourly, it doesn't make sense to have them on if you are not using them. So, to save costs, only turn them on before requesting a new account, and once it is finished, turn them off. The idea is simple, let's see that in practice:
The basic TF code that creates the AFT infrastructure is:
module "aft" {
source = "github.com/aws-ia/terraform-aws-control_tower_account_factory"
ct_management_account_id = var.ct_management_account_id
log_archive_account_id = var.log_archive_account_id
audit_account_id = var.audit_account_id
aft_management_account_id = var.aft_management_account_id
ct_home_region = var.ct_home_region
tf_backend_secondary_region = "us-west-2"
vcs_provider = "github"
account_request_repo_name = "${var.github_username}/learn-terraform-aft-account-request"
global_customizations_repo_name = "${var.github_username}/learn-terraform-aft-global-customizations"
account_customizations_repo_name = "${var.github_username}/learn-terraform-aft-account-customizations"
account_provisioning_customizations_repo_name = "${var.github_username}/learn-terraform-aft-account-provisioning-customizations"
}
Once we have created the infrastructure by executing a terraform apply, the VPC endpoints are enabled by default, we process to send the account creation request (explained in the tutorial). Once it is finished, we set the aft_vpc_endpoints flag to false:
module "aft" {
source = "github.com/aws-ia/terraform-aws-control_tower_account_factory"
...
aft_vpc_endpoints = false
}
We reexecute terraform apply and done! VPC endpoints deleted and costs reduced!
Delete the NAT related resources when not usedโ
Maybe, the first approach we think is to manually delete the NAT resources. Then, before queuing any account creation request, we use Terraform to recreate them. That might work, but it involves manual intervention on in the AWS Console for every request, we don't want that.
So, taking into account that the AFT code is in a GitHub repository, I thought it was better to fork it and add a new flag to conditionally create the NAT resources. So, I did the next steps:
- I forked the AWS GH repo aws-ia/terraform-aws-control_tower_account_factory in cangulo-aws-aft/terraform-aws-control_tower_account_factory.
- I created a new flag called
aft_vpc_nat_gatewayfollowing theaft_vpc_endpointsmodel. - I modify the NAT resources to depend on the new flag.
Everything is done in this commit.
Then, I simply update the "aft" module source to my fork and provide the new flag. Next is the result:
module "aft" {
# source = "github.com/cangulo-aws-aft/terraform-aws-control_tower_account_factory" # repo in the cloud
# source = "github.com/cangulo-aws-aft/terraform-aws-control_tower_account_factory"
source = "../terraform-aws-control_tower_account_factory" # repo in the same local path
ct_management_account_id = var.ct_management_account_id
log_archive_account_id = var.log_archive_account_id
audit_account_id = var.audit_account_id
aft_management_account_id = var.aft_management_account_id
ct_home_region = var.ct_home_region
tf_backend_secondary_region = var.tf_backend_secondary_region
vcs_provider = "github"
account_request_repo_name = "${var.github_username}/account-request"
account_provisioning_customizations_repo_name = "${var.github_username}/account-provisioning-customizations"
global_customizations_repo_name = "${var.github_username}/global-customizations"
account_customizations_repo_name = "${var.github_username}/account-customizations"
aft_feature_delete_default_vpcs_enabled = false
aft_vpc_aws_nat_gateway = false
aft_vpc_endpoints = false
}
Reduced Costsโ

I deactivated the VPC Endpoints since the day 11, NAT costs disappeared next days.
References:โ
- Youtube Tutorial for setting up AWS Control Tower: Enable AWS Control Tower for Existing Organizations
- Manage AWS Accounts Using Control Tower Account Factory for Terraform
- Account Factory for Terraform
Git Repositoriesโ
learn-terraform-aft-account-provisioning-customizations
learn-terraform-aft-account-customizations
learn-terraform-aft-global-customizations
learn-terraform-aft-account-request
About meโ
I'm a Senior Platform Engineer. I build and evolve Internal Developer Platforms โ golden paths, self-service tooling, and paved roads spanning AWS governance, CI/CD standardization, and developer enablement โ so engineering teams ship faster, safer, and more autonomously, with less cognitive load. My core stack is AWS, Terraform, and GitHub Actions, and I'm an AWS Certified Solutions Architect. I care deeply about paved roads, productivity, and documenting implementations so knowledge scales with the team (yes, I'm an engineer who likes to document ๐งโ๐ป).
You can check my experience here.
Personal Blog - cangulo.github.io
GitHub - Carlos Angulo Mascarell - cangulo
LinkedIn - Carlos Angulo Mascarell