Lifting The Hood On AWS Control Tower – Part 1

, ,

This article series is based on a presentation I gave and will present at an AWS User Group soon. Turning this into a blog article means others can refer to this content should the presentation be a bit too fast-paced.

  • Part 1
    • Review of AWS Control Tower
    • Managing Accounts
    • AWS Guardrails
  • Part 2
    • Integrating GuardDuty with Organizations
    • Integrating SecurityHub with Organizations
    • Monitoring environment API Events with CloudTrail
  • Part 3
    • Automation and scaling with Control Tower
    • Introduction to The Landing Zone Accelerator
    • Additional considerations with Control Tower

Review of AWS Control Tower

At it’s core, AWS Control Tower is a service that abstracts other services. This means Control Tower is fundamentally just an orchestrator and manager for other AWS services that do the actual work, similar to Security Hub and how it really just aggregates and configures other services.

Control Tower’s purpose is to give you a boiler-plate AWS environment that’s configured according to best-practices, making it a great starting point for developing your own Landing Zone.

Control Tower Architecture

AWS Control Tower configures your environment with the bare minimum required for a modern-day Landing Zone, but what it does configure is very important, and it makes any Cloud Engineer’s life significantly easier, so it’s highly recommended that Control Tower be one of, if not the first service that you deploy in AWS.

When configured properly, Control Tower will give you:

  1. An AWS Organization with two additional child accounts, a log archive and an audit account.
  2. AWS IAM Identity Center configured with one user account, which has full administrative rights to all accounts.
  3. Centralised and encrypted CloudTrail logging across all accounts which is aggregated in the management account and archived in the log archive account.
  4. A config aggregator in the audit account, used for monitoring detective guardrails.

While Control Tower configures our environment with fundamental best-practices, it’s also missing some key service integrations that should be enabled for any production environment.

Managing Accounts

Control Tower uses a component called Account Factory to provision accounts. Account Factory is really just a front for AWS Service Catalog, in-fact, tools such as Account Factory for Terraform (AFT), which is used to create AWS Account using Terraform just directly call Service Catalog underneath.

You may think after creating an account, that you’re all good to go, but you need to pay attention to the enrolment status as well.

Understanding enrolment

When you create a new Control Tower environment, the accounts and OUs it creates are enrolled in Control Tower, meaning they are managed by Control Tower and have Control Tower’s policies applied to them, but if you create new OUs or accounts outside of Control Tower, they may not be managed by Control Tower.

You can view your organization from Control Tower and you will see the enrolment status of accounts and OUs. It’s especially important to check this as you won’t get any other warning signs that accounts aren’t being managed by Control Tower.

Updating accounts

AWS Control Tower’s changes are versioned, so in a production environment you can update accounts individually to support the new Control Tower version.

Updating your accounts is recommended to ensure that they are running the latest configuration.

Managing accounts through Organizations

You can perform several actions through AWS Organizations to manage your accounts remotely. Some of the actions you can do are:

  • Moving the AWS Account to a new OU (make sure the OU is enrolled in Control Tower)
  • Closing the AWS Account
  • Changing the AWS Account’s root email address

Using Organizations for these activities makes account management significantly more simple, you almost never have to login to an AWS Account once you’ve created it through Account Factory.

AWS Guardrails

AWS Guardrails are used to enforce compliance or detect non-compliance across your AWS Control Tower environment. AWS Control Tower deploys and manages these Guardrails for you. You can view the list of all available guardrails here.

Under the hood, guardrails use the following AWS Services:

  • AWS Config is used for detective guardrails
  • Organization SCPs are used for preventative guardrails

I’ll explain how these work in more detail below.

Detective guardrails

If you look in the audit account, you’ll see an AWS Control Tower Config Aggregator like below:

This aggregator is used to aggregate Config Rule status across your environment, so the findings can be queried centrally from this account. You can click on the aggregator to view the status of all the Config Rules deployed by Control Tower.

Let’s click on one of the Config Rules as an example:

You can see how the name aligns with one of the guardrails we’ve deployed (we deployed all of the Strongly Recommended Guardrails).

So, to recap the process:

  • We enabled the AWS-GR_RESTRICTED_COMMON_PORTS guardrail in AWS Control Tower
  • In the background, AWS Control Tower created this config rule for each account we selected
  • The config aggregator created by Control Tower monitors the status of the rule, which Control Tower uses to report the compliance of your Control Tower environment

Preventative guardrails

Preventative guardrails are a bit more simple than detective ones. AWS Organizations supports creating Service Control Policies (SCPs), that are similar to IAM policies but applied at the account and OU level. They are often used to restrict certain API activity across an entire account or a set of accounts, such as deploying certain types of EC2 instances or accessing services outside of a given set of regions.

We can view our SCPs and look for ones that contain the guardrail keyword, these polices will have been created by Control Tower.

If we click on one of these guardrail SCPs, we can see the policy document they apply.

Looking at this policy, we can identify a few things:

  • The policy itself, is quite simple, it just prevents the root user from creating any access keys.
  • We can see the guardrail name, in this case AWS-GR_RESTRICT_ROOT_USER_ACCESS_KEYS, this is the name of the guardrail we enabled in Control Tower.

Monitoring guardrail compliance status

While we can view our compliance status in Control Tower, we ideally want to receive some kind of alert when we enter a non-compliant state. The good news is since preventative guardrails are, well, preventative, we don’t need to monitor them, and since detective guardrails are just AWS Config Rules, we can monitor them with a simple EventBridge Rule.

Create the following rule in each AWS Account:

"source": [
   "aws.config"
]
"detail-config": [
   "Config Rules Compliance Change"
]

By using the above event pattern in a rule in every account, we can monitor our guardrail compliance status, as shown in the diagram above.

Deploying guardrails

Here’s a Terraform snippet you can copy or even better, refactor, into your own codebase to deploy AWS Control Tower Guardrails programmatically.

# define an OU name var - the OU we want to apply our guardrails to. 
variable "ou_name" {
   type = string
}
variable "guardrail" {
   type = string
   default = "AWS-GR_RESTRICT_ROOT_USER_ACCESS_KEYS" # prevent the root user from making access keys
}

# grab our current region
data "aws_region" "current" {}

# grab the the child OUs (guardrails can't be applied to the root OU)
data "aws_organizations_organization" "my_org" {}

data "aws_organizations_organizational_units" "my_org" {
   parent_id = data.aws_organizations_organization.my_org.roots[0].id
}

locals {
   # get the OU ID for our ou_name, or get all OUs
   target_identifier = [for ou in data.aws_organizations_organizational_units.my_org.children : ou.arn if ou.name == var.ou][0]
}

resource "aws_controltower_control" "guardrail" {
   control_identifier = "arn:aws:controltower:${data.aws_region.current.name}::control/var.guardrail"
   target_identifier = local.target_identifier
}

Final Notes

I hope you found part 1 of this series helpful. You gained a fundamental understanding of some core Control Tower concepts here, and in the next part I’ll cover integrating more services into your Control Tower environment.


Leave a Reply

Your email address will not be published. Required fields are marked *