terraform

Introducing Gruntwork’s AWS Landing Zone Solution

Create and manage AWS accounts with a customizable security baseline defined in Terraform.
Introducing Gruntwork’s AWS Landing Zone Solution
Rob Morgan
Published March 19, 2020

Create and manage AWS accounts with a customizable security baseline defined in Terraform.

Setting up AWS accounts for production is hard. You need to create multiple accounts and configure each one with a variety of authentication, access controls and security features by using AWS Organizations, IAM Roles, IAM Users, IAM Groups, IAM Password Policies, Amazon GuardyDuty, AWS CloudTrail, AWS Config, and a whole lot more. Doing this from scratch is time-consuming and complicated, and all the pre-built “Landing Zone” solutions we tried were expensive, low-quality, UI-driven, and/or didn’t allow customization. That’s why today, I’m excited to announce Gruntwork’s AWS Landing Zone solution, which allows you to set up production-grade AWS accounts out of the box in minutes, but it’s also 100% defined as code, so you can fully customize it to your needs.

In addition, we are able to leverage the work we’ve invested in developing our compliance offering meaning new accounts can have the added benefit of complying with the CIS AWS Foundations Benchmark. Let me show you exactly how it works!

How to get started

Here’s the AWS account structure we’re going to create:

A production-grade AWS Account Structure

Check out our AWS landing zone deployment guide for why this is the recommended account structure.

To create this account structure, we are going to use the following new modules in the IaC Library:

  • account-baseline-root: A security baseline for configuring the root account (also known as the master account) of an AWS Organization including setting up child accounts.
  • account-baseline-security: A security baseline for configuring the security account where all of your IAM users and IAM groups are defined.
  • account-baseline-app: A security baseline for configuring accounts designed to run your Apps and store logs.

Under the hood, these use a variety of other modules such as our new GuardDuty module, AWS Config, IAM roles, and so on. The best news is all of these modules are available to our customers at no extra cost as part of the Gruntwork for AWS subscription. If you’re not already a subscriber then check out our pricing page for more information. With the new modules, we’ll set up a production-grade account structure using Terragrunt and Terraform.

Let’s Deploy the Root Account

The first AWS account you create is the root account (sometimes also called the master account). This will be the parent account for your organization and won’t be used to run any infrastructure; instead, it is used solely to create child accounts and manage billing. This account has powerful permissions over all child accounts, so you should strictly limit access to this account to a small number of trusted admins — essentially if you apply a policy to the root account, it applies to all accounts in the organization!

The AWS Root Account

To show how easy it is to set up a security baseline in the root account and create all of the necessary child accounts, let’s create a terragrunt.hcl file to deploy the resources using the account-baseline-root module:

Note: We are using Terragrunt to deploy these examples, but they work just fine with pure Terraform too!

terraform {
source = "git::git@github.com:gruntwork-io/terraform-aws-service-catalog.git//modules/landingzone/account-baseline-root?ref=v0.36.11"
}
include {
path = find_in_parent_folders()
}
inputs = {
# Prefix all resources with this name
name_prefix = "ref-arch-acme-root"
# The child AWS accounts to create in this AWS organization
child_accounts = {
logs = {
email = "root-accounts+logs@acme.com"
is_logs_account = true
}
security = {
email = "root-accounts+security@acme.com"
}
shared-services = {
email = "root-accounts+shared-services@acme.com"
}
dev = {
email = "root-accounts+dev@acme.com"
}
stage = {
email = "root-accounts+stage@acme.com"
}
prod = {
email = "root-accounts+prod@acme.com"
}
}
# The IAM users to create in this account. Since this is the root
# account, we should only create IAM users for a small handful of
# trusted admins.
users = {
alice = {
groups = ["full-access"]
pgp_key = "keybase:alice_on_keybase"
create_login_profile = true
create_access_keys = false
}
}
}

After running terragrunt apply we should now have successfully configured the root account of an AWS Organization, including setting up child accounts, AWS Config, AWS CloudTrail, Amazon Guard Duty, IAM users, IAM groups, IAM password policy, and more. The baseline will also authenticate to the logs account via an IAM role and create S3 buckets for storing AWS Config and CloudTrail data. Next, we’ll deploy the security baseline in the security account.

Let’s Deploy the Logs Account

The logs account is used for aggregating log data. All of the other accounts will send their AWS Config and CloudTrail data to this account so that you have a single, central place where all logs are stored and can be viewed.

The AWS Logs Account

We’ll deploy a security baseline to the logs account using the account-baseline-app module, including setting up AWS Config, AWS CloudTrail, Amazon Guard Duty and the respective S3 buckets to store the logs.

terraform {
source = "git::git@github.com:gruntwork-io/terraform-aws-service-catalog.git//modules/landingzone/account-baseline-app?ref=v0.36.11"
}
include {
path = find_in_parent_folders()
}
inputs = {
# Prefix all resources with this name
name_prefix = "ref-arch-acme-logs"
# Use the S3 bucket and KMS key that were already created in this
# logs account by account-baseline-root
cloudtrail_s3_bucket_name = "<CLOUDTRAIL_BUCKET_NAME>"
cloudtrail_kms_key_arn    = "<CLOUDTRAIL_KMS_KEY_ARN>"

# Use the S3 bucket that was already created in this logs account
# by account-baseline-root
config_s3_bucket_name = "<CONFIG_BUCKET_NAME>"
config_central_account_id                        = null
config_aggregate_config_data_in_external_account = false

# Allow access from other AWS accounts
allow_read_only_access_from_other_account_arns = "01234567890"
allow_full_access_from_other_account_arns      = "01234567890"
allow_logs_access_from_other_account_arns      = "01234567890"
}

After running terragrunt apply we should now have successfully configured the logs account of an AWS Organization, including creating the S3 buckets to aggregate logs from CloudTrail and AWS Config.

Let’s Deploy the Security Account

The security account is where all of your IAM users and IAM groups are defined and also where other “security” activities will be organized.

The AWS Security Account

We’ll deploy a security baseline to the security account using the account-baseline-security module, including setting up AWS Config, AWS CloudTrail, Amazon Guard Duty, IAM users, IAM groups, IAM password policy, and more. By default, we only create the admin and cross-account IAM groups in the security account.

terraform {
source = "git::git@github.com:gruntwork-io/terraform-aws-service-catalog.git//modules/landingzone/account-baseline-security?ref=v0.36.11"
}
include {
path = find_in_parent_folders()
}
inputs = {
# Prefix all resources with this name
name_prefix = "ref-arch-acme-security"
# Send CloudTrail logs to this bucket
cloudtrail_s3_bucket_name = "ref-arch-security-logs"
# We assume the S3 bucket for AWS Config has already been created
# by account-baseline-root
config_should_create_s3_bucket        = false
config_s3_bucket_name                 = "<CONFIG_BUCKET_NAME>"
config_central_account_id             = "<LOGS_ACCOUNT_ID>"
config_aggregate_config_data_in_external_account = true
# Enable the IAM groups you want
should_create_iam_group_full_access    = true
should_create_iam_group_read_only      = true
should_create_iam_group_user_self_mgmt = true

# Configure the names for IAM groups
iam_group_name_full_access            = "full-access"
iam_group_name_read_only              = "read-only"
iam_group_name_iam_user_self_mgmt     = "self-mgmt"
iam_group_names_ssh_grunt_sudo_users  = "ssh-grunt-sudo-users"
iam_group_names_ssh_grunt_users       = "ssh-grunt-users"
# This IAM group gives access to other AWS accounts
iam_groups_for_cross_account_access = [
{
group_name   = "_account.stage-full-access"
iam_role_arn = "arn:aws:iam::012345678901:role/full-access"
},
{
group_name   = "_account.stage-read-only"
iam_role_arn = "arn:aws:iam::012345678901:role/read-only"
},
{
group_name   = "_account.prod-full-access"
iam_role_arn = "arn:aws:iam::012345678901:role/full-access"
},
{
group_name   = "_account.prod-read-only"
iam_role_arn = "arn:aws:iam::012345678901:role/read-only"
},
]
# The IAM users to create in this account. Since this is the
# security account, this is where we create all of our IAM users
# and add them to IAM groups.
users = {
Alice = {
groups               = ["full-access", "access-all-external-accounts", "ssh-grunt-sudo-users"]
pgp_key              = "keybase:alice_on_keybase"
create_login_profile = true
create_access_keys   = false
}
Bob = {
groups               = ["_account.stage-full-access", "_account.prod-read-only", "ssh-grunt-sudo-users"]
pgp_key              = "keybase:bob_on_keybase"
create_login_profile = true
create_access_keys   = false
}
}

After running terragrunt apply we should now have successfully configured the security account of an AWS Organization, including setting up the IAM users and groups. In the final step, we’ll deploy the app account.

Let’s Deploy the App Account

You can have one or more application accounts for running your software. At a bare minimum, most companies will have a production account (“prod”), for running user-facing software, and a staging account (“stage”) which is a replica of production (albeit with smaller or fewer servers to save money) used for internal testing.

The AWS App Account

For the purposes of this example, we’ll deploy a single account using the account-baseline-app module. Let’s create another terragrunt.hcl file to deploy a security baseline into the app account:

terraform {
source = "git::git@github.com:gruntwork-io/terraform-aws-service-catalog.git//modules/landingzone/account-baseline-app?ref=v0.36.11"
}
include {
path = find_in_parent_folders()
}
inputs = {
# Prefix all resources with this name
name_prefix = "ref-arch-acme-prod"
# Use the S3 bucket and KMS key that were already created in this
# logs account by account-baseline-root
cloudtrail_s3_bucket_name = "<CLOUDTRAIL_BUCKET_NAME>"
cloudtrail_kms_key_arn    = "<CLOUDTRAIL_KMS_KEY_ARN>"
# Use the S3 bucket that was already created in this logs account
# by account-baseline-root
config_s3_bucket_name               = "<CONFIG_BUCKET_NAME>"
config_central_account_id           = "<LOGS_ACCOUNT_ID>"
config_aggregate_config_data_in_external_account = true
# Allow access from other AWS accounts
# IAM permissions omitted for brevity.
<OMITTED_FOR_BREVITY>
}

After running terragrunt apply we should now have successfully configured the app account of an AWS Organization, including setting up CloudTrail, AWS Config, and any IAM roles for debugging and deployment.

Summary

That’s all it takes to configure the AWS root, logs, security and app accounts with AWS Config, CloudTrail, GuardDuty, IAM users, IAM groups, IAM password policies and more! The best part is that because we are using Terraform, under the hood, everything is 100% managed using code which means you can easily configure and extend all of these modules to add any custom requirements for your organization. For more information on the concepts and details refer to our “Configure Your Accounts with Landing Zone” production deployment guide.

In this post, we introduced our new Gruntwork for AWS Landing Zone solution. We hope that it addresses the needs of many of our existing SME and larger enterprise customers. To get access to these modules for easily creating production-grade account structures, check out our pricing page or contact us for more information.