Security is important. You know it’s true. But implementing good security practices is a challenge. In particular, when you’re managing resources in a shared cloud environment, you need to keep particular considerations and best practices in mind. Examples include locking down your accounts with multi-factor authentication, minimizing your blast radius by segregating resources, and using the principle of least privilege when assigning user permissions.
Secure AWS Account Structure
To achieve a more secure AWS account structure, we at Liatrio recently reviewed our AWS footprint and security practices. We decided that we wanted to take advantage of AWS Organizations to structure our AWS accounts and resources in a more manageable way with more granular security controls. After reviewing suggestions from Amazon about possible multiple account strategies, we chose to implement a hybrid structure that provides substantial security benefits by separating Identity and Access Management (IAM) from actual AWS resources.
In this secure AWS account structure, a Master account manages the billing for the organization. All IAM users exist in an InfoSec account, and users use role assumption to access resources in the various accounts. Resources are provisioned in either the Production or Non-Production accounts. CloudTrail is configured in each account for auditing activity within the account, and a log trail stores the logs in the InfoSec account. We then lock down the root credentials with multi-factor authentication. (Soon, we’ll be adding alarms and notifications to let us know when potentially nefarious activity occurs in the logs, such as anytime a root user logs in.)
We also wanted a reproducible way to set up a secure AWS account structure and associated policies in order to share them with our clients and easily make changes in the future. Terraformwas an obvious choice. In addition, we decided to leverage the awesome Terragrunt tool from Gruntwork. Terragrunt makes it easier to write DRY (don’t repeat yourself) Terraform code and manage remote state.
With Terragrunt, we organize our Terraform configurations into subfolders to make them easier to manage; however, they can still share some common settings and variables. The configurations for the initial organization setup and the temp-admin user are in the master folder. The accounts folder has all the configurations for each of the three sub-accounts (infosec, non-prod, and prod). The environments folder is where we’ll eventually put configurations for the actual resources. The modules and utility folders contain some additional configurations that are used across accounts (they could probably be pulled out into their own repo at some point).
In trying to automate a solution, we quickly discovered a “chicken or egg” dilemma. Well, actually two related dilemmas. First, in order to run Terraform modules to create accounts, policies, users, etc., we needed to already have a user attached to policies that allow Terraform to perform the required actions.
Second, in order to configure an S3 bucket to store the Terraform state, we needed the account where that bucket would be stored to exist before applying the configurations. Our goal was to have no resources actually managed within the Master account, but that was the account where our initial Terraform would be run. (Terragrunt automatically creates the S3 bucket on init, but the S3 bucket would be created in the Master account, which isn’t what we wanted.)
Create the AWS account that will serve as the Master account.
Lock down the root credentials immediately.
Create an IAM policy that allows organization, account management, and role assumption in the child accounts.
Create an “init” user that will be used to run a base setup script.
That’s it for manual steps. We created a bash script that takes in the credentials of the init user and does the rest of the work. That’s where the fun happens.
The Initialization Script
The initialization script first configures Terragrunt to use a local backend for state and applies the Terraform configurations that create the sub-accounts by using an override file and a specific Terragrunt config. The script then configures Terragrunt to use the S3 remote backend and re-inits Terraform to copy the state, which solves the problem of getting the remote state stored in the right account.
# run with local backend cp overrides/backend_local_override.tf . terragrunt init --terragrunt-config terraform-local.tfvars terragrunt apply --terragrunt-config terraform-local.tfvars # re-init with remote backend rm ./backend_local_override.tf || true terragrunt init
The script runs the temp-admin configurations to create the temp-admin user. It then uses the output of the apply to retrieve the secret key and encrypted secret access key for the temp-admin user.
Next, it applies all the configurations in the three accounts folders as the temp-admin user. These configurations create the resources we need within the three sub-accounts, including the initial set of users, groups and policies as well as the cross-account roles and CloudTrail logging.
Be sure to customize shared.tfvars and accounts/infosec/users.tf before you run the script. Then once you’ve run the script, you’ll have three sub-accounts within your Master account’s organization — Infosec, Production, and Non-Production — with users assigned to one or more of the following groups that restrict their access to the particular accounts you’ve specified:
InfosecAdmins group – access to manage users and policies
ProdAdmins group – access to manage production account
NonProdAdmins group – access to manage non-prod account
ProdDevelopers group – access to production resources
NonProdDevelopers group – access to non-prod resources
MasterBilling group – access to manage billing for the Master account
We’ve made the basic configurations and scripts available for creating a secure AWS account structure in a public repo on GitHub called aws-accounts-terraform. We look forward to feedback and suggestions for improvement!
Liatrio is a collaborative, end-to-end Enterprise Delivery Acceleration consulting firm that helps enterprises transform the way they work. We work as boots-on-the-ground change agents, helping our clients improve their development practices, react more quickly to market shifts, and get better at delivering value from conception to deployment.
Ready to Accelerate Delivery and Transform Your Organization?