This project describes step-by-step configuration of AWS Organization with AWS Organization Formation IaC tool.
With npm installed, get aws-organization-formation
package:
npm install -g aws-organization-formation
This is the tool commonly known under ofn
abbreviation.
- Credentials with profile for master aws account are configured (named profile is for convenience).
- Clone this repository, checkout code
The repository designed in form of bare-bone main branch with two feature branches that add incremental functionality.
The Readme describes how to build up this functionality by checking out branches and running org-formation
AWS Organization diagram for this exercise:
flowchart TB
subgraph root
ma["master account"]
end
subgraph sharedou["Shared OU"]
cicd["cicd account"]
end
subgraph appx["ApplicationX OU"]
dev["AppX dev account"]
prod["AppX prod account"]
end
ma --> sharedou
cicd --> dev
cicd --> prod
Checkout main
branch where organization.yml
config will be updated.
The first step for org-formation
is to initialize config file, run this command: org-formation init organization.yml --profile master-account --region us-east-1 --verbose
ofn
will pick up existing accounts and OUs in the AWS Organization and save them into organization.yml file for review and further changes. It does not make any changes in AWS at this stage.
Next step, update organizations.yml
to configure AWS Organizations according to the diagram above with own email.
Next step, create change-set: org-formation create-change-set organization.yml --profile master-account
Review change set and if everything looks correct, apply changes: org-formation update organization.yml --profile master-account
Commit and push changes to the branch main
.
While ofn can work well as a cli, it is more practical to configure automation, so it can apply changes from git repository. Using branch init-pipeline
, let's bootstrap pipeline and apply changes with git push.
There will be multiple steps, ofn creates multiple resources, the major ones are:
- Cloudformation stack
- Codecommit repository
- CodepiPeline
- Codebuild
Run org-formation init-pipeline organization.yml --profile master-account --region your-region --build-account-id {master_account_id} --verbose
The output should be similar to this:
org-formation init-pipeline --profile master-account --region us-east-1 --build-account-id 208334959160 --verbose --print-stack
INFO: uploading initial commit to S3 organization-formation-208334959160/initial-commit.zip...
INFO: creating codecommit / codebuild and codepipeline resources using CloudFormation...
DEBG: putting object to S3:
{
"Bucket": "organization-formation-208334959160",
"Key": "state.json"
}
INFO:
INFO: Your pipeline and initial commit have been created in AWS.
INFO: Hope this will get you started!
INFO:
INFO: Take your time and browse through the source, there is some additional guidance as comments.
INFO:
INFO: Have fun!
INFO:
INFO: --OC
Ok, it completed. So what did just happen?
- ofn created Cloudformation stack that deployed resources for the pipeline
- It created simple CodePipeline
- There is a Codecommit repository, which triggers pipeline
- Codebuild that runs org-formation cli
The ofn
pushed some code to Codecommit, lets review it.
ofn
puts the code in Codecommit repository, so there some complexity of working with it. AWS provides a helper git-remote-codecommit
for Codecommit to make git cli work transparently. The helper is a Python package, unfortunately it brings the whole Python ecosystem as a dependency (please let me know if there is a better way to do it).
pip install -r requirements.txt
Then set a remote tracking main branch from Codecommit: git remote add origin-cc codecommit::us-east-1://master-account@organization-formation
(orgin-cc
can be any string, but make it memorable so origins for github and codecommit repositories are easy to distinguish).
Next, pull changes that init-pipeline made with git pull origin-cc main --allow-unrelated-histories
which may cause some merge conflicts.
At this point, a commit and git push orgin-cc
will trigger the pipeline to deploy changes.
The main branch at the Codecommit origin-cc
should have a structure similar to this:
tree .
.
├── 000-organization-build
│ ├── org-formation-build.yml
│ └── organization-tasks.yml
├── README.md
├── buildspec.yml
├── organization-parameters.yml
├── organization-tasks.yml
└── organization.yml
1 directory, 7 files
It includes templates both for the pipeline and AWS organization - very convenient!
Now lets add a simple SCP to the root of the org. The SCP will restrict access to specific regions.
Switch to branch scp
and rebase main
(git rebase main
), it will bring all the changes from main
branch, the one that should be in sync with Codecommit. We will use it in a moment to apply changes with SCP policies.
Add SCP policy in the end of organization.yml
(the policy is already there, uncomment it)
RestrictUnusedRegionsSCP:
Type: OC::ORG::ServiceControlPolicy
Properties:
PolicyName: RestrictUnusedRegions
Description: Restrict Unused regions
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: DenyUnsupportedRegions
Effect: Deny
NotAction:
- 'cloudfront:*'
- 'iam:*'
- 'route53:*'
- 'support:*'
Resource: '*'
Condition:
StringNotEquals:
'aws:RequestedRegion':
- us-east-1
- us-east-2
Add reference to the policy in the OrganizationRoot key (uncommented respective reference):
OrganizationRoot:
Type: OC::ORG::OrganizationRoot
Properties:
DefaultOrganizationAccessRoleName: OrganizationAccountAccessRole
ServiceControlPolicies:
- !Ref RestrictUnusedRegionsSCP
Commit changes, compare with main: git diff main organization.yml
Now switch back to the main branch (git checkout main
), and merge scp
branch (git merge scp
).
Last step is to push changes to the Codecommit (git push --set-upstream origin-cc main
).
In a few minutes changes will be deployed!
Through the above steps, ofn
helped setup up AWS Organization with multiple OUs and account, bootstrap CICD pipeline to apply changes via commits to a repository, and allowed to configure a basic SCP policy.
It is time to setup something useful for the application development. Route53 domain delegation is one of such tasks: it is simple enough and fits in a small Cloudformation template, yet there is enough complexity for cross-account resource exchanges (NS records) to test drive ofn
in a real-world scenario.
Branch tasks-cdk-bootstrap
contains configurations for CDK Bootstrap and the subdomains setup using tasks.
The project has tasks folders enumerated to maintain logic order how tasks run. This is what each does:
000-organization-build
- the pipeline for Org Formation, upon changes it will update itself, just as any other task.100-cdk-bootstrap
- to run Cloudformation for CDK bootstrap, an equivalent ofcdk bootstrap <options>
command, however withTrustedAccountsForLookup
parameters CDK bootstrap can be configured for any number of accounts, including new ones added later.110-subdomains
- to create subdomains for tagged accounts in organization.yaml (Properties.Tags.subdomain: appx-dev
)
tree .
.
├── 000-organization-build
│ ├── org-formation-build.yml
│ └── organization-tasks.yml
├── 100-cdk-bootstrap
│ ├── cdk-bootstrap-template.yaml
│ └── organization-tasks.yml
├── 110-subdomains
│ ├── organization-tasks.yml
│ └── subdomains.yml
├── README.md
├── buildspec.yml
├── organization-parameters.yml
├── organization-tasks.yml
├── organization.yml
└── requirements.txt
4 directories, 16 files
If something is not working as expected, please check few things:
npm install
inbuildspec.yml
has the same version as on local machine- Add
--perform-cleanup
toperform-tasks
in thebuildspec.yml
to allow org-formation clean up stack
AWS Organization Formation is a really powerful tool, these links can help with concepts and examples.
- Automation examples We used subdomains example in this repo.
- Reference implementation. This repository is a simplified version of this detailed and advanced reference implementation.