I wrote this code in a quick and dirty way, just to send it to a few companies that were trying to decide between the age-old AWS Control Tower v.s. Landing Zone v.s. AWS Deployment Framework vs. Terraform vs. Other debate.
You should be able to understand this code within a day or two, and not have account creation be much more complicated than say, S3 bucket creation.
If you look at e.g. AWS Control Tower Account Factory for Terraform I am sure you will not be able to understand it all within a day or two, nor customize it easily to your heart's content.
There are more details below, but at a high-level:
- Creates an account
- Writes Terraform file under
aws-organizations/accounts/ - Outputs the
terraform importcommand needed to import the generated Terraform file.
python -m create_aws_account --help
usage: create_aws_account [-h] --account_type ACCOUNT_TYPE --data_classification
DATA_CLASSIFICATION --project PROJECT --description DESCRIPTION
[--ou DESIRED_OU]
Creates an AWS account, writes Terraform and gives import command
options:
-h, --help show this help message and exit
--account_type ACCOUNT_TYPE
Specify the Account Type (Production/Development/Sandbox)
--data_classification DATA_CLASSIFICATION
Specify the Data Classification (Public/Internal/Confidential)
--project PROJECT Specify the Project
--description DESCRIPTION
Give a one-liner about the account.
--ou DESIRED_OU Give a valid OU (parent_id) for the account.
This creates 2 folders with generated Terraform in them that each call a respective module.
One is an SCP baseline, which will be applied in the context of your management account. The other is a configuration baseline, which will be applied in the context of the subaccount.
See subaccounts/smoky-production as an example.
python -m generate_subaccount_tf --help
usage: generate_subaccount_tf [-h] [--only-scp-baseline] [--only-configuration-baseline]
--account-name ACCOUNT_NAME
Creates baseline Terraform using 2 different modules
options:
-h, --help show this help message and exit
--only-scp-baseline Only generate SCP baseline
--only-configuration-baseline
Only generate configuration baseline
--account-name ACCOUNT_NAME
Give a valid account name.
This does the following:
- Creates the AWS account with a name of
{args.project}-{args.account_type}(will append-2if there is already an account with that name,-5if there are 4, etc.) The name of the - Add tags of
"account_type","data_classification","project","description" - In all regions enables EBS encryption by default and deletes all default VPCs
- Edits the
adminrole trust policy to include thests:SetSourceIdentitypermission - Adds the managed
IAMFullAccess,ReadOnlyAccess,SecurityAudit,ViewOnlyAccesspolicies, and a customEnableS3AccountPublicAccessBlockpolicy, toadminrole - Remove
AdministratorAccess(* on *) fromadminrole - Moves new account to the given OU, if one was given.
- Writes Terraform / displays
importinstructions
All this code is straightforwardly read from __main__.py which is only 100 lines of code.
Note: We need to change ADMIN_NAME in constants.py if you do not want admin to be the name of the role made upon account creation. (This defaults to OrganizationAccountAccessRole, a mouthful, if left unspecified.)
The scps baseline module will turn on a bunch of account-specific SCPs by default (the variables.tf of the module are a bunch of bool values)
Any SCP inheritance can be leveraged separately, by specifying a parent_id argument to the aws_account module (example).
The configuration baseline module will just setup IAM roles and turn on aws_s3_account_public_access_block
The only import necessary, is that of the admin IAM role. As it is automatically made upon account creation and we have to use it to get into the account in the first place.
Note: This assumes you setup GuardDuty and CloudTrail at the org-level, such that you do not need to re-do it for each individual account.
(I could have made it so you also need to import the e.g. 4 policy attachments of IAMFullAccess/ReadOnlyAccess/SecurityAudit/ViewOnlyAccess/EnableS3AccountPublicAccessBlock mentioned above, but since it is the admin role this felt unnecessary.)
Two reasons: first is the resources that are created automatically due to AWS. The other reason (for the case of EBS encryption region-wide) is because doing something in all regions is painful in Terraform.
Assuming you run this automation before handing an account to a customer, the SCP blocking e.g. "ec2:DisableEbsEncryptionByDefault" will be applied prior to a customer ever getting access to the account.
The only case where I was ambivalent as to what to do is: s3:PutAccountPublicAccessBlock.
We could
- Do it upon account creation, and never Terraform it.
- Do it upon account creation, and Terraform import it.
- Give
adminpermission to turn it on upon account creation, Terraform the setting viaaws_s3_account_public_access_block.
I chose option 3, as it seemed the least bad.
As with EBS encryption, an SCP blocking "s3:PutAccountPublicAccessBlock" will be applied prior to a customer getting access.
I wrote this code in a sloppy way just to demonstrate the idea, not use it in production. (Though I have written very similar code in a production setting, and this approach worked well.)
- Change the email list value to be configurable rather than hard-coded
- Add tests
- Add more SCPs to
scp_baseline(https://github.com/ScaleSec/terraform_aws_scp/tree/main/security_controls_scp/modules can be mined for SCPs.) - Terraform a
baseline_outhat can be used in addition to thescp_baselinemodule, in the case that we run into theMaximum attached per account(5) limit. - Add better IAM policies for
adminandoperator, such as service-specific IAM policies that take advantage of condition keys. - Handling the case wherein you enable a new region at the org-level that all subaccounts can use (We would want to e.g. delete the default VPC, enable EBS encryption, in newly enabled regions.)
This lays the ground work for automatically setting up:
- Networking
- AWS Config
- SSM
- Permissions for e.g. your security team, Terraform CI/CD pipeline etc.
and many other things, upon account creation.