This project is a Typescript CDK project to deploy an AWS Platform components.
It is trying to follow CDK best practices as much as possible.
The cdk.json
file tells CDK Toolkit how to execute your app.
npm run cdk:deploy
deploys these stacks to the specified AWS account/regionnpm run cdk:diff
compares deployed stack with current statenpm run cdk:synth
emits the synthesized CloudFormation templatecdk watch
watches for changes and deploy on the fly (ONLY IN DEV ENV)
- π AWS CLI Installed & Configured
- π AWS CDK version 2.x Installed & Configured
- node runtime with npm
- Create an AWS account if you never created one: this will be your management account.
- Enable AWS Organization and AWS Identity Center in your AWS management account: The AWS Organizations and SSO instance are not created by this tool. SSO instances are regional, so please create it in
eu-west-1
.
- Run
npm i
from the root folder - Set your CDK context variables: either in the cdk.context.json, in the command line or as CDKCONTEXT (see documentation about context)
- Run
yarn prepare
to enable husky pre-commit/pre-push actions
-
adminEmailsParameterName: The AWS Parameter Store parameter containing your admin email
Must be created before deploy the CDK app
-
managementAccountId: The account Id of your AWS management account
-
ssoInstanceArn: The instance ID of you Identity Center instance
-
identityStoreId: The ID of your Identity Center Identity Store
-
rootOuId: The ID of your Root Organization (should look like this:
r-12345
) -
awsOrganizationsId: The ID of your AWS Organizations (should look like this:
o-12345678
)
# Make sure you in root directory
npm i
# Check what will be deployed and deploy
npm run cdk:diff
# If changes are relevant, then deploy
npm run cdk:deploy
If you want to destroy all the resources created by the stack, Execute the below command to delete the stack, or you can delete the stack from console as well.
cdk destroy *
This is not an exhaustive list, please carry out other necessary steps as maybe applicable to your needs.
Be aware that in this particular case:
- you need to first empty the deployed buckets.
- You need to empty every Organization Units deployed by the project because non-empty OU cannot be deleted.
- If you have some CloudFormation StackSets, you must remove StackSet instances before destroying
β οΈ AWS Organizations does not support doing 2 operations at the same time on the same SCP π€·ββοΈ . So you may experience trouble deleting those resources.
This project manages :
- The account factory: Account provisionning and deprovisionning
- Organization Units (OU) creation/deletion
- Service Control Policies (SCP) creation/deletion
- Organizations management tools (Budgets, organization trails ...)
- AWS Identity Center resources (permission sets and groups)
For more information on AWS Organizations concepts, please refer to AWS documentation and best practices.
This construct extends the AWS CDK Budget Construct which is a L1 construct.
In the management account, we create 1 budget for now which is a global Budget: to sum up all cost in every platform's accounts. But it is possible to add others targetting specific services.
For example:
new Budget(this, 'APIGatewayBudget', {
limitAmountUSD: 3000,
emails: adminEmails,
costFilters: {
Service: ['Amazon API Gateway'],
},
});
Each of those budget is created with an amount limit USD and with 3 notifications.
Notifications are sent to administrators, stored in a parameter of the Parameter Store (ADMIN_EMAILS_PARAMETER_NAME environment variable).
You can find budget declarations in the FinOps stack.
This resource allows to log everything actions from all the AWS account of your organization. It needs a S3 trail bucket and a CloudTrail organization trail.
You can find this in the Logging stack.
Be careful if you want to delete this stack, the bucket must be emptied before.
This construct extends the AWS CDK NodeJS Lambda Construct.
The SSOPermissionSet extends the CfnPermissionSet CDK construct. It fixes a default name and a default session duration. Of course, those two attributes can be overwritten.
All permission sets are declared in another construct : SSOPermissionSets. Indeed, it is just to separate this part from the stack (pure esthetic stuff) and avoid having a 5000 lines file.
All inline policies are declare in ./policies folder.
The SSOAssignment takes a list of assignments and create a CDK CfnAssignment.
An assignment is the link between an AWS SSO group and a list of permission sets. As an assignment MUST target an AWS account, each permission set is associated to an account and this forms an AccountPermissionSet.
For example:
{
groupId: <GroupId>,
groupName: <GroupName>,
accountPermissionSets: [
{
permissionSet: <permissionSet>,
account: 123456789101,
},
],
},
This is an Assignment. It associates the Admin SSO group to the admin
permission set for 123456789101
account.
If I add an item to accountPermissionSets, like this:
{
groupId: <GroupId>,
groupName: <GroupName>,
accountPermissionSets: [
{
permissionSet: permissionSets.admin,
account: 123456789101,
},
{
permissionSet: permissionSets.viewOnly,
account: 918203837466,
},
],
},
This will give to the Admin
group the admin
permission to 123456789101
account and the viewOnly
permission to 918203837466
account.
SSO groups are created in the AwsIdentityCenter Stack using a custom construct:
const group = new IdentityCenterGroup(this, 'group', {
groupName: 'GroupName',
identityStoreId,
});
Multiple CloudFormation stacks are deployed by this project.
This stack deploys:
createAccount
: Creates the AWS account => waits for its creation => adds it to the right OU depending on theaccountType
parameter given => adds the billing alternate contactdeleteAccount
: Fetches the OU id of the account we want to delete, using theaccountId
given parameter => moves the account to thePENDING-DELETION
OU => closes the account.
Be aware that there is a quota for the number of deletable account in a month (20% of the total number of accounts). So you might get the
ConstraintViolationException
with the error messageYou have exceeded close account quota for the past 30 days.
. In that case, thedelete-account
lambda will just put the account in thePending Deletion
Organizational Unit where theDeny Actions
SCP blocks every action but the one to close the account. You'll have to retry to delete the account the next month.
For more information on what implies "closing account" in AWS, please refer to the AWS documentation.
You can find Lambda Typescript code in the lambda folder. And their CDK declaration in the accountFactory stack
This stack deploys billing related resources as budgets for example.
This stack deploys monitoring related resources. For now it only deploys a Global Organization trail.
This stack deploys AWS Organizations resources such as:
- Service Control Policies
- Organization Units
Those resources must be described in a Yaml file that you have to give to the stask like this:
const organizationsStack = new AwsOrganizationsStack(app, 'AwsOrganizationsStack', {
rootOrganizationId: rootOuId,
accountId: managementAccountId,
configFilePath: './config/organizations.yaml',
});
The file must contain 2 keys: serviceControlPolicies
and organizationUnits
:
organizationUnits:
- name: App
parentName: root
serviceControlPolicies:
- name: DenyLeaveOrganization
description: 'Policy forbidding leaving the Organization'
targetOUNames:
- root
contentFile: scps/denyLeaveOrganization.json
- name: DenyActions
description: 'Policy denying specific actions'
targetOUNames:
- root
contentFile: scps/denyActions.json
The root OU must not be defined in this file, it already exists by default. If you need to reference it as a parent OU for one of your Organization Units OR as a targetOU for your SCP, it must be called root
.
SCP content must be referenced either as a file: you must give the file path. Or directly the content if you prefer.
There is one Organization Unit created by default that you cannot touch in terms of SCP: the Pendind Deletion
OU, it is used by the delete account lambdas to store the account while it is suspended or scheduled for deletion.
This stack:
- instantiates all permission sets
- declares all assignment of groups to permission set/account
- create SSO groups
Those resources are described in a YAML file and the file path must be givent to the stack:
new AwsIdentityCenterStack(app, 'AwsIdentityCenterStack', {
accountId: managementAccountId,
ssoInstanceArn,
identityStoreId,
configFilePath: './config/identityCenter.yaml',
});
The file can contain this 3 keys: group
and permissionSets
and assignment
. To create the assignment, the group and permissionSet referenced must be created in the group and permissionSets sections:
groups:
- name: Administrators
- name: ReadOnly
permissionSets:
- name: Administrators
managedPolicies:
- 'arn:aws:iam::aws:policy/AdministratorAccess'
- name: ViewOnly
managedPolicies:
- 'arn:aws:iam::aws:policy/job-function/ViewOnlyAccess'
assignments:
- accountId: 'xxxxxx'
permissions:
- groupName: ReadOnly
permissionSets:
- ViewOnly
- groupName: Administrators
permissionSets:
- Administrators
To create an assignment, you must give the account ID (this is why the config file is not to be commited).
The permissionSet can also contain a description
, an inlinePolicy
(as a file path or directly the content) and a duration
.
CDK's unit tests are in ./test directory. The unit test library used is jest and its configuration is here.