Here’s an AWS CloudFormation template and deployment script to set up
Organizational Units
and
Accounts
for Prod and Dev for a new project or department.
Deploying a CloudFormation stack from this template will require a user in the
Organization’s
management account
with the AdministratorAccess permission set.
The created Organizational Unit (OU) and Account structure will look this:
Organization Root [pre-existing]
╚═ Management Account [pre-existing]
╚═ Project Parent OU
╚═ Project Prod OU
╚═ Project Prod Account
╚═ Project Dev OU
╚═ Project Dev Account
The OUs are not strictly necessary, but they help to group the whole project
under a single OU, and to allow for other accounts to be added later within the
Prod and Dev OUs if necessary.
The CloudFormation template looks like this:
---
AWSTemplateFormatVersion: '2010-09-09'
Description: >
Create AWS Organizational Units and an Accounts for a project.
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-organizations-organizationalunit.html
# https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/aws-resource-organizations-account.html
Parameters:
OrganizationRootId:
Type: String
Description: ID of the root Organization of the management account.
AllowedPattern: "^(r-[0-9a-z]{4,32})$"
ProjectName:
Type: String
Description: Overall name for this project, used in OU and Account names.
AllowedPattern: "^([a-zA-Z]+)$"
EmailDomain:
Type: String
Description: Domain name for AWS Account email addresses.
AllowedPattern: "^([a-zA-Z0-9]+).([a-zA-Z0-9\.]+)$"
Resources:
ProjectParentOrganizationalUnit:
Type: AWS::Organizations::OrganizationalUnit
Properties:
Name: !Sub "${ProjectName}ParentOU"
ParentId: !Ref OrganizationRootId
ProdOrganizationalUnit:
Type: AWS::Organizations::OrganizationalUnit
Properties:
Name: !Sub "${ProjectName}ProdOU"
ParentId: !Ref ProjectParentOrganizationalUnit
ProdAccount:
Type: AWS::Organizations::Account
Properties:
AccountName: !Sub "${ProjectName}Prod"
Email: !Sub "${ProjectName}_aws_prod@${EmailDomain}"
RoleName: !Sub "${ProjectName}ProdAdmin"
ParentIds:
- !Ref ProdOrganizationalUnit
DevOrganizationalUnit:
Type: AWS::Organizations::OrganizationalUnit
Properties:
Name: !Sub "${ProjectName}DevOU"
ParentId: !Ref ProjectParentOrganizationalUnit
DevAccount:
Type: AWS::Organizations::Account
Properties:
AccountName: !Sub "${ProjectName}Dev"
Email: !Sub "${ProjectName}_aws_dev@${EmailDomain}"
RoleName: !Sub "${ProjectName}DevAdmin"
ParentIds:
- !Ref DevOrganizationalUnit
Outputs:
ProjectParentOrganizationalUnitArn:
Description: ARN of the Project Parent Organizational Unit
Value: !GetAtt ProjectParentOrganizationalUnit.Arn
ProdOrganizationalUnitArn:
Description: ARN of the Prod Organizational Unit
Value: !GetAtt ProdOrganizationalUnit.Arn
ProdAccountArn:
Description: ARN of the Prod Account
Value: !GetAtt ProdAccount.Arn
DevOrganizationalUnitArn:
Description: ARN of the Dev Organizational Unit
Value: !GetAtt DevOrganizationalUnit.Arn
DevAccountArn:
Description: ARN of the Dev Account
Value: !GetAtt DevAccount.Arn
Here’s a sample deployment script to deploy the above template:
#!/usr/bin/env bash
set -e, -u, -x, -o pipefail
PARENT_PATH=$( cd "$(dirname "${BASH_SOURCE[0]}")" || exit; pwd -P )
TEMPLATE_PATH="${PARENT_PATH}/cloudformation.yml"
PARAMS_PATH="${PARENT_PATH}/config/organizational.params"
ls "${TEMPLATE_PATH}"
if [ ! -f "${TEMPLATE_PATH}" ]; then
echo "Cloudformation template file is missing"
fi
ls "${PARAMS_PATH}"
if [ ! -f "${PARAMS_PATH}" ]; then
echo "Params file is missing"
fi
if [ -z ${AWS_PROFILE+x} ]; then
echo "AWS_PROFILE is not set"
exit 1
fi
# shellcheck disable=SC1090
source "${PARAMS_PATH}"
if [ -z ${OrganizationRootId+x} ]; then
echo "OrganizationRootId is not set"
exit 1
fi
# shellcheck disable=SC2154
echo "OrganizationRootId: ${OrganizationRootId}"
# shellcheck disable=SC2154
echo "ProjectName: ${ProjectName}"
# https://docs.aws.amazon.com/cli/latest/reference/cloudformation/deploy/
# shellcheck disable=SC2046
aws cloudformation deploy \
--profile "${AWS_PROFILE}" \
--template-file "${PARENT_PATH}/cloudformation.yml" \
--stack-name "${ProjectName}-Organizational" \
--parameter-overrides OrganizationRootId="${OrganizationRootId}" $(cat "${PARAMS_PATH}")
That requires a params file at ./config/organizational.params , which provides
the ProjectName and EmailDomain params, for example:
ProjectName=FoobarProject
EmailDomain=kensiosoftware.co.uk
This assumes that a catch-all email is set up on the specified domain, so that
the CloudFormation template can make up specific email addresses for each
account.
View post:
Creating AWS Organizational Units and Accounts for Prod and Dev in CloudFormation
|