diff --git a/.gitignore b/.gitignore index 8210ef9..176d1a5 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,7 @@ node_modules/ .serverless/ .DS_Store .printed-stacks/ -.vscode/launch.json +.vscode/ initial-commit.zip +reference.zip +sar/initial-commit/bin/ diff --git a/.org-formationrc b/.org-formationrc index 4e23976..41d4b7e 100644 --- a/.org-formationrc +++ b/.org-formationrc @@ -1,4 +1,4 @@ organizationFile = ./src/organization.yml -masterAccountId = {{management-account-id}} -stateObject = {{state-object}} -stateBucketName = {{state-bucket-name}} \ No newline at end of file +masterAccountId = {{ ManagementAcctId }} +stateObject = state.json +stateBucketName = {{ StateBucketName }} \ No newline at end of file diff --git a/README.md b/README.md index 68da642..2c1be7e 100644 --- a/README.md +++ b/README.md @@ -3,21 +3,25 @@ This reference architecture and also the steps outlined in this review are still under development and wont work as-is. Please do - - watch this project if you would like to get updates on the process. - - contribute best practices / ideas / documentation / code through issues or PRs <3 + +- watch this project if you would like to get updates on the process. +- contribute best practices / ideas / documentation / code through issues or PRs <3 thanks! # Introduction + This is a reference architecture to get started quickly with using `org-formation` as well as provide examples of best practices and demonstrates the capabilities. The reference architecture is built with the following core principles: -* _Batteries included:_ it should deploy with as little dependencies as possible -* _Always relevant:_ every organization will get value out of every part of the reference architecture. This means two things - * it should not contain services that might not be used. Examples: **AWS ECS**, **AWS Step Functions** - * it should not be too opinionated about choosing an AWS service as the implementation for a function that can also be sourced externally. Examples: **AWS SecurityHub**, **AWS CloudWatch** logs and metrics + +- _Batteries included:_ it should deploy with as little dependencies as possible +- _Always relevant:_ every organization will get value out of every part of the reference architecture. This means two things + - it should not contain services that might not be used. Examples: **AWS ECS**, **AWS Step Functions** + - it should not be too opinionated about choosing an AWS service as the implementation for a function that can also be sourced externally. Examples: **AWS SecurityHub**, **AWS CloudWatch** logs and metrics To use this reference architecture, follow the [Getting Started](#getting-started). If you want to deploy additional stacks using org-formation on top of this reference architecture, then it is advised to do that using _delegated builds_. Within the org-formation GitHub there will be example stacks configured as delegated builds that are usable, but are not eligeble to be part of the reference architecture because they either depend on an external system (not batteries included) or not always relevent (uses an AWS service that does something that is not always relevant). Examples are: -* Break glass notifications based on events -* DNS and Domain management + +- Break glass notifications based on events +- DNS and Domain management # Getting Started @@ -31,6 +35,7 @@ To create an AWS Organization based on this reference architecture, managed by o 6. [Clone your new `org-formation` repository](#6-clone-your-new-org-formation-repository) ## Prerequisites + 1. A valid credit card 2. A working phone number 3. Four unique email addresses within your domain (management-root, compliance-root and orgbuild-root) @@ -38,14 +43,15 @@ To create an AWS Organization based on this reference architecture, managed by o > _Hint: if you are using Google as an email provider, you can use team+something@domain.com to create unique addresses that all arrive in the same email box_ ## 1. Create the AWS Management Account + Create an AWS Account. This will be the management account of your AWS Organization 1. Navigate [here](https://portal.aws.amazon.com/billing/signup?nc2=h_ct&src=default&redirect_url=https%3A%2F%2Faws.amazon.com%2Fregistration-confirmation#/start) to sign up to an AWS account. Or sign-up on the page [here](https://aws.amazon.com/) 2. Gather the following data that you will need in step [Clone and modify this repository](#4.-clone-and-modify-this-repository) -| Parameter | Where to find it | Example -| ----- | ----------- | --- -| {{management-account-id}} | Top right -> your account -> My Account | 341696816352 +| Parameter | Where to find it | Example | +| ------------------------- | --------------------------------------- | ------------ | +| {{management-account-id}} | Top right -> your account -> My Account | 341696816352 | ## 2. Create the AWS Organization @@ -53,49 +59,21 @@ Create an AWS Account. This will be the management account of your AWS Organizat 2. [Enable all features](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_org_support-all-features.html) 3. [Enable all policy types](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_enable-disable.html) -## 3. Configure AWS SSO -You will use AWS SSO to obtain access to the AWS Accounts after we are done with the initial setup. IAM Users including the root user should not be used as a security best practice for human access. - -1. [Enable AWS SSO](https://docs.aws.amazon.com/singlesignon/latest/userguide/step1.html) -2. [Create 4 groups](https://docs.aws.amazon.com/singlesignon/latest/userguide/addgroups.html) - - Admin - - Developer - - Auditor - - Supporter -3. [Create 1 user](https://docs.aws.amazon.com/singlesignon/latest/userguide/addusers.html) - - Set a password - - Enroll 2FA - - Add user to the Administrator group -4. Gather the following data you will need in steps [Clone and modify this repository](#4.-clone-and-modify-this-repository) and [Clone your new `org-formation` repository](#6.-clone-your-new-org-formation-repository) - -| Parameter | Description | Example -| ----- | ----------- | --- -| SSO start URL | Go to AWS SSO -> Settings -> User Portal -> User Portal URL | https://dgega332fa.awsapps.com/start -| {{sso-instance-arn}} | Go to AWS SSO -> Settings -> ARN | arn:aws:sso:::instance/ssoins-6987b39db64e1ecd -| {{sso-admin-group-id}} | Go to AWS SSO -> Groups -> Administrator -> Details -> Group ID | 99672ac0cf-8495fd69-c57e-4214-88a4-b9f41eed0d32 -| {{sso-auditor-group-id}} | Go to AWS SSO -> Groups -> Auditor -> Details -> Group ID | 99672ac0cf-8495fd69-c57e-4214-88a4-b9f41eed0d32 -| {{sso-developer-group-id}} | Go to AWS SSO -> Groups -> Developer -> Details -> Group ID | 99672ac0cf-8495fd69-c57e-4214-88a4-b9f41eed0d32 -| {{sso-supporter-group-id}} | Go to AWS SSO -> Groups -> Supporter -> Details -> Group ID | 99672ac0cf-8495fd69-c57e-4214-88a4-b9f41eed0d32 - -Configuring AWS SSO manually using AWS SSO itself as an identity provider is the quickest and easiest way to get started (and is batteries included). At the time of writing AWS SSO does not support automating this via an API, as soon as it does, this guide will be updated accordingly. If you want to configure an external Identity Provider, then start [here](https://docs.aws.amazon.com/singlesignon/latest/userguide/supported-idps.html). ## 4. Clone and modify this repository + 1. Clone this repository locally 2. Search and replace the following values globally in all files -| Parameter | Description | Source | Example -| --- | --- | --- | --- -| {{management-account-id}} | 12 digit identifier of the management account | [Create the AWS Management Account](#1-create-the-aws-management-account) | 341696816352 -| {{state-bucket-name}} | S3 bucket where the IaC state will be stored | choose | organization-formation-state-341696816352-prd -| {{organization-name}} | Alias of the management account | choose | bee-awesome -| {{primary-aws-region}} | The primary AWS region to deploy to | choose | us-east-1 -| {{management-root-email-address}} | Email address used to register the management account | [Create the AWS Management Account](#1-create-the-aws-management-account) | platform.team@bee.awesome -| {{compliance-root-email-address}} | Email address for the compliance account | [Prerequisites](#prerequisites) | platform.team+compliance@bee.awesome -| {{orgbuild-root-email-address}} | Email address for the org build account | [Prerequisites](#prerequisites) | platform.team+org-build@bee.awesome -| {{sso-instance-arn}} | AWS SSO instance ARN | [Configure AWS SSO](#3-configure-aws-sso) | arn:aws:sso:::instance/ssoins-6987b39db64e1ecd -| {{sso-admin-group-id}} | Principal ID from Identity Provider's group used by administrators | [Configure AWS SSO](#3-configure-aws-sso) |99672ac0cf-8495fd69-c57e-4214-88a4-b9f41eed0d32 -| {{sso-auditor-group-id}} | Principal ID from Identity Provider's group used by auditors | [Configure AWS SSO](#3-configure-aws-sso) | 99672ac0cf-8495fd69-c57e-4214-88a4-b9f41eed0d32 -| {{sso-developer-group-id}} | Principal ID from Identity Provider's group used by auditors | [Configure AWS SSO](#3-configure-aws-sso) | 99672ac0cf-8495fd69-c57e-4214-88a4-b9f41eed0d32 -| {{sso-supporter-group-id}} | Principal ID from Identity Provider's group used by supporters | [Configure AWS SSO](#3-configure-aws-sso) | 99672ac0cf-8495fd69-c57e-4214-88a4-b9f41eed0d32 +| Parameter | Description | Source | Example | +| ------------------------------ | ----------------------------------------------------- | ------------------------------------------------------------------------- | --------------------------------------------- | +| {{ManagementAcctId}} | 12 digit identifier of the management account | [Create the AWS Management Account](#1-create-the-aws-management-account) | 341696816352 | +| {{StateBucketName}} | S3 bucket where the IaC state will be stored | choose | organization-formation-state-341696816352-prd | +| {{OrganizationName}} | Alias of the management account | choose | bee-awesome | +| {{PrimaryAwsRegion}} | The primary AWS region to deploy to | choose | us-east-1 | +| {{management-acct-root-email}} | Email address used to register the management account | [Create the AWS Management Account](#1-create-the-aws-management-account) | platform.team@bee.awesome | +| {{security-acct-root-email}} | Email address for the compliance account | [Prerequisites](#prerequisites) | platform.team+compliance@bee.awesome | +| {{logarchive-acct-root-email}} | Email address for the compliance account | [Prerequisites](#prerequisites) | platform.team+compliance@bee.awesome | +| {{orgbuild-acct-root-email}} | Email address for the org build account | [Prerequisites](#prerequisites) | platform.team+org-build@bee.awesome | ## 5. Initialize `org-formation` @@ -106,9 +84,11 @@ In this step, you run OrgFormation locally using the credentials of the root use
1. Install OrgFormation on your local machine + ``` npm install -g aws-organization-formation ``` +
I already have an existing AWS Organization @@ -119,21 +99,29 @@ npx org-formation init organization.yml --region _Primary Region_ --cross-accoun ``` You can then merge that with the ./src/organization.yml file in the way you like and continue with this guide. +

2. Apply the organization.yml file to AWS Organizations. This means creating accounts and OUs and ordering them. This will also create the OrgBuild account where the org-formation build pipeline will reside. + ``` npx org-formation update ./src/organization.yml --verbose ``` + 3. Create the role that the `org-formation` uses inside of the Management Account + ``` aws cloudformation create-stack --stack-name org-formation-role --template-body file://src/templates/000-org-build/org-formation-role.yml ``` + 4. Zip this local repository into `000-org-build` to be used as the initial commit for the OrgBuild CodeCommit repository. From the top level of this repository, execute: + ``` zip ./src/templates/000-org-build/initial-commit src/**/*.* .gitignore .org-formationrc *.yml *.json README.md ``` + 5. Deploy the stacks in `000-org-build`. This creates the build roles, state bucket and file and the OrgFormation build pipeline in the OrgBuild account. It also uploads this entire local repository as an initial commit to the Git repository that the build pipeline will then execute. + ``` npx org-formation perform-tasks ./src/templates/000-org-build/_tasks.yml --organization-file ./src/organization.yml --max-concurrent-stacks 50 --max-concurrent-tasks 1 --print-stack --verbose ``` @@ -141,28 +129,31 @@ npx org-formation perform-tasks ./src/templates/000-org-build/_tasks.yml --organ When you have finished the setup, we will need to delete the S3 bucket containing the state in the management account. This was created in step 2 because at that time the OrgBuild account, was assumed to not exist yet. ## 6. Clone your new `org-formation` repository + Here you will configure command line access to the CodeCommit repository that will trigger the Build Pipeline in the OrgBuild Account. It requires you to configure the aws cli to use SSO and the clone the repository using HTTPS git-remote-codecommit (GRC) 1. Configure AWS CLI with AWS SSO by following [this guide](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-sso.html#sso-configure-profile-auto) You will need to provide the following details: -| Parameter | Description | Example -| ----- | ----------- | --- -| SSO start URL | The landing page URL to be found in the AWS SSO of the management account | https://dgega332fa.awsapps.com/start -| SSO region | The default region | us-east-1 -| SSO account id | Select the OrgBuild account from the drop-down | 222140350420 -| SSO role name | Select a role with write permission the drop-down | Administrator -| CLI default client Region | The default region | us-east-1 -| CLI default output format | Whatever format you prefer | yaml -| CLI profile name | Name of the profile, choose wisely | ba-orgbuild-admin +| Parameter | Description | Example | +| ------------------------- | ------------------------------------------------------------------------- | ------------------------------------ | +| SSO start URL | The landing page URL to be found in the AWS SSO of the management account | https://dgega332fa.awsapps.com/start | +| SSO region | The default region | us-east-1 | +| SSO account id | Select the OrgBuild account from the drop-down | 222140350420 | +| SSO role name | Select a role with write permission the drop-down | Administrator | +| CLI default client Region | The default region | us-east-1 | +| CLI default output format | Whatever format you prefer | yaml | +| CLI profile name | Name of the profile, choose wisely | ba-orgbuild-admin | 2. Configure support for CodeCommit [git-remote-codecommit](https://docs.aws.amazon.com/codecommit/latest/userguide/setting-up-git-remote-codecommit.html?icmpid=docs_acc_console_connect#setting-up-git-remote-codecommit-install) 3. Using your IDP (either AWS SSO itself or an external IDP), log into the OrgBuild account, navigate to AWS CodeCommit, find the repository URL and clone It looks something like this + ``` git clone codecommit::eu-central-1://@organization-formation ``` + 4. You can now make changes, commit and push and that will be effectuated by the build pipeline in the OrgBuild Account diff --git a/buildspec-pr.yml b/buildspec-pr.yml index ef4b4b5..c6ee1bf 100644 --- a/buildspec-pr.yml +++ b/buildspec-pr.yml @@ -2,7 +2,7 @@ version: 0.2 phases: install: commands: - - npm ci + - npm i - echo installed dependencies - npx org-formation -v - pip install cfn-lint diff --git a/buildspec.yml b/buildspec.yml index 2148f7a..874feae 100644 --- a/buildspec.yml +++ b/buildspec.yml @@ -2,7 +2,7 @@ version: 0.2 phases: install: commands: - - npm ci + - npm i - echo installed dependencies - npx org-formation -v build: diff --git a/docs/reference-arch.drawio b/docs/reference-arch.drawio new file mode 100644 index 0000000..43cc4a0 --- /dev/null +++ b/docs/reference-arch.drawio @@ -0,0 +1 @@ +7Vzrc9o4EP9rMnP3IRm/MPCRR9JmpnfhSnuvLxlhC6NGtlxZDqF//a1sGT9JnQABcqTNxF69Vqvd3+7Ksi/Mkf/0gaNw8RtzMb0wNPfpwhxfGIauazb8kZRVSulpRkrwOHFVpZwwJT+wImqKGhMXR6WKgjEqSFgmOiwIsCNKNMQ5W5arzRktjxoiD9cIUwfROvUv4oqFmoXRzekfMfEW2ci63U9LfJRVVjOJFshlywLJvL4wR5wxkV75TyNMpfAyuaTtbjaUrhnjOBBtGmAx8j9MHm+7I//HF+OvwYJ+p5em4vYR0VjNWHErVpkI5oTSEaOMw23AAqAOI8HZA64QXRQtsBxMg5tHzAUBMQ4o8QKgCRYCdc4CMVU9a+o+6+XCMOHfDVyYQ4pmmA6R8+BxFgduoco8+YEq9ekriciR8VOBpMTxATMfC76CKqrUViujVNNSt8t8nbvZ4i0Ka9zTFBEp3fLWPefihwu1Ai9YjUzpC8LHLqijumVcLJjHAkSvc+owkdBa7nmdT0yKfKwD8RsWYqVsC8WCAWkhfKpKN0oyYjF38HP8KoNE3MPiuXqdtKKczLMLwzFFgjyWba9JzKrphBHgeb2gZtcqrahu98pdpJyqVvliDThHq0K1UFaInhlH75fGMcyK6f2Er0p9uEg5yDVnLZMtlKnBsm0qpPWGKJCQSVGUTtj+HksUGob0UurA+h6uPPl3ugDlcO/irAPgJ+0jLa4pLYsFJQHgQ4bJm2wdZjP0OHIJzssUmhRRB6oPR7rZsV8EPZ8kiExYRARhEoJ84rqJxVSxaV2AFIHiuSjbSAJcqQHpRgOQoShMZzonT5KRYaJCmF8/YqlJaSfgAULZwH/ypLO8QsvIumLcQwH5gSST0X3xDtH7OCAig8PCTHgKRrvAQECzkmqaWgMK9uogaHf3hYF1B3TUGGgcPQZWvNWOMNCyTwADjR1ioEudMwK+QwS0zGNDQPO0ENA8egTcTxTY6Z8AApq7Q8AJZ+cY8D0iYMc2jwsB+6cFgN3/KQDq1gkAYHeHIWAMFi6VLEfBGc8B8IyL7wwXdf3IcFHvnRYw9g4GjFuJubcRMurmnlOmowlgpfYLCkNKsLwEQRra3VfJYuBKpFkQ6iakaHOHMa1SKMkoYxysPssHBjnW5IXN1T9J2V+PjNsgEihwcPSCtl+DKA5D0BjsfsaetMGWjaeYziFaFNLsn2sBxOJ094+URrer6+8RKUNGibPac6LcK8Nh58BoaFinhYa61RYO9xImbvuQI9vw2+9DC2t34dod94YxIO7AcWDRZQXky4eQwSwKGxG8DQS/mpsRc/GEhFhC2guix1Z4uxVTI+b7RBwVS8myHQ1HU4EEsOQ8YPETDhpZ3QtPqWKDA5ojR0THx5yrXBbEQ1hezeLo19csXyVCOKdY+02xkILK/cYRhl12LGZDIGHVA4lub7OH2y6t6uzU6WTiLEbq72WfID3GU9XgGROC+QUNdrDUvsPocLttgjWHO1Bo0+yVFLp/6G2C7okFxvZp7hNkfBdw4zcUIA/7CTRo68DzJEz77J825Ll608HAt/RPmb98W3vGAQSYPDmxqtQIKDdEsp50+Xp7l7Xa2bt1VPae8V2w9yl2Yk7EKs8x3zKn/BAj7o5jsTpQZrQe/wuPI4Hd27CSihw8d4N1wVE0AL1f/cB8N2y9+a7lGdz3l3x0Dp18GAcJ1vYI7m2PA2VoeizgXj8p84l5A+4sYOjDwPuIstj9whGhR7bBs8b9I0N7AN058QaxS8QuOTsD/jsCfPvggH+Q000FwE/siTglzN/yBKjWEvO7u4b8DQeTOkZpzS2jcgY+ndELDkCVVnwXxzK1eiohzw+Yg9f5GPm4HsyPr8SCBN5xodn5ofsW8NXXmjX5UHuLZv083SBYFTe7NCKd9h33Xq3ME8CnJePuJJVvu6DhWq5ZyEmEYfRpem6lZdMBhaYBErIlKL6Qj9ReHVRNMX8kjuzqDwhOUNsjM8MYMHGLYfNYsW2DJFb6jB0QdJ4Z/6TR72yM5yim4s/JqO26zKLrwOGrUNqGat36HJGptGoSz0AV0mR+SJnz0HZlyV0o7mJR1qSWwj2HfbsCSmeBnQdKonWoV3+1W03y7ZC00/CUxrbfMA4068cMAcMIhD6SaT95kODC1Uze1lX3NphzBIoSOyLmOGVQfgbBxf9zVdW3UlUZOfsoZfFwutrVyzmL0crp701VrfrGd8iWmOf6CYnd5ZxxJTlD0676V/K08ibATfW0IbWfU/x0maQvkE7wOHBSt+zOitm+JohQyp0RRyxcJe7b99PDtkkPI0rCGUPcre8VBKEvpy3FtYwui4lpcSJrT5AyvA9fAGXWuAuFhbIxwICj1CqQ2V7dAuHHGvZrFlj8RMbxeYxGk+M4TcduncTo4Da9qviQdGXvpaDvE2OeIwcf0kgrb8537QaH0vD5kOr72Luz0voO5kKIME0qb+C/R8QinklJwk3FYm9eF6qXerl0koI2gZm0uWzdL0POHgkEoW3DwvKgHM8BiQIHbxHMZXpJ/ORzQEUd/6ndPK9/G60qGWqQGUij01LcjMurGM6iK7EkvqcWEqQH0AA2IWuDCG90S7f6fa1jW5bWA99h26DHN9z+B/uPN/17S9Oe4PfqW+g9Yx8Vo9/DRtxb2ss4xrb+8bOr/R38+4d8C2M46Te8GusyHCUpLMffY5JEVQFepu5NBmHm4Nea6sDsK6i4A1lV36zTe3VZ6U3CMl8uLKkb649Qpftb+ae8zOv/AA== \ No newline at end of file diff --git a/docs/reference-arch.png b/docs/reference-arch.png new file mode 100644 index 0000000..442455c Binary files /dev/null and b/docs/reference-arch.png differ diff --git a/package.json b/package.json index b5e2f2e..8296dee 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,8 @@ "print-tasks": "npx org-formation print-tasks ./src/_tasks.yml --output yaml --max-concurrent-stacks 100 --max-concurrent-tasks 100", "perform-tasks": "npx org-formation perform-tasks ./src/_tasks.yml --max-concurrent-stacks 50 --max-concurrent-tasks 1", "validate-tasks": "npx org-formation validate-tasks ./src/_tasks.yml --failed-tasks-tolerance 0 --max-concurrent-stacks 100 --max-concurrent-tasks 100", - "cfn-lint": "if [ -d .printed-stacks ]; then cfn-lint ./.printed-stacks/**/*.yaml -i W2001,E3001,E1019,W1020,W2509,E3021,W8001; else echo skipping linting; fi" + "cfn-lint": "if [ -d .printed-stacks ]; then cfn-lint ./.printed-stacks/**/*.yaml -i W2001,E3001,E1019,W1020,W2509,E3021,W8001; else echo skipping linting; fi", + "package-reference": "rm org-formation-reference.zip || echo ok && zip -r org-formation-reference.zip src/* buildspec* package* .org-formationrc template.json -x src/organization.yml && aws s3 cp org-formation-reference.zip s3://org-formation-reference/org-formation-reference.zip && aws s3api put-object-acl --bucket org-formation-reference --key org-formation-reference.zip --acl public-read && rm org-formation-reference.zip" }, "repository": { "type": "git", @@ -28,6 +29,6 @@ }, "homepage": "https://github.com/org-formation/org-formation-reference#readme", "dependencies": { - "aws-organization-formation": "^0.9.14" + "aws-organization-formation": "^0.9.19-beta.1" } } diff --git a/sar/full.yml b/sar/full.yml new file mode 100644 index 0000000..dd470b0 --- /dev/null +++ b/sar/full.yml @@ -0,0 +1,378 @@ +AWSTemplateFormatVersion: "2010-09-09" +Description: "Organization Formation Reference project bootstrap" +Transform: "AWS::Serverless-2016-10-31" + +Metadata: + AWS::ServerlessRepo::Application: + Name: org-formation-reference + Description: Organization Formation Reference (full) + Author: Org Formation + SpdxLicenseId: Apache-2.0 + Labels: ["org-formation", "multi-account", "cicd", "devops"] + HomePageUrl: https://github.com/org-formation/org-formation-reference + SemanticVersion: 1.0.0 + SourceCodeUrl: https://github.com/org-formation/org-formation-reference + +Parameters: + ResourcePrefix: + Type: String + Default: "orgformation-bootstrap" + Description: The prefix used for AWS created by this application + + CrossAccountRoleName: + Type: String + Default: "OrganizationAccountAccessRole" + Description: The name of the IAM Role used for cross account access within the AWS Organization + + BuildAccountRootEmail: + Type: String + Description: The root email of the account you would like to use as build account + + SecurityAccountRootEmail: + Type: String + Description: The root email of the account you would like to use as security account + + LogArchiveAccountRootEmail: + Type: String + Description: The root email of the account you would like to use as log archive account + + EmailForBudgetAlarms: + Type: String + Description: Email used for sending budget alarms + + PrimaryAwsRegion: + Type: String + DefaultValue: !Ref AWS::Region + Description: Primary AWS Region for your organization setup + +Resources: + OrgBuildLogGroup: + Type: "AWS::Logs::LogGroup" + Properties: + RetentionInDays: 7 + LogGroupName: !Sub "/codebuild/${ResourcePrefix}-build" + + OrgBuildRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Principal: + Service: codebuild.amazonaws.com + Action: + - sts:AssumeRole + Path: / + ManagedPolicyArns: + - "arn:aws:iam::aws:policy/AdministratorAccess" # see: https://github.com/org-formation/org-formation-cli/blob/master/docs/least-priviledge.md + Policies: + - PolicyName: !Sub "${ResourcePrefix}-build-service-role-policy" + PolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Resource: + - !GetAtt OrgBuildLogGroup.Arn + - !Sub "${OrgBuildLogGroup.Arn}:*" + Action: + - logs:CreateLogGroup + - logs:CreateLogStream + - logs:PutLogEvents + - Effect: Allow + Resource: + - !Sub "arn:aws:s3:::organization-formation-${AWS::AccountId}" + - !Sub "arn:aws:s3:::organization-formation-${AWS::AccountId}/*" + Action: + - s3:PutObject + - s3:GetObject + - s3:GetObjectVersion + - s3:GetBucketAcl + - s3:GetBucketLocation + - Effect: Allow + Resource: + - !GetAtt OrgRepo.Arn + Action: + - codecommit:GitPull + + OrgPipelineRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Principal: + Service: codepipeline.amazonaws.com + Action: + - sts:AssumeRole + Path: / + Policies: + - PolicyName: !Sub "${ResourcePrefix}-codepipeline-service-role-policy" + PolicyDocument: + Version: "2012-10-17" + Statement: + - Action: + - iam:PassRole + Resource: "*" + Effect: Allow + Condition: + StringEqualsIfExists: + iam:PassedToService: + - cloudformation.amazonaws.com + - elasticbeanstalk.amazonaws.com + - ec2.amazonaws.com + - ecs-tasks.amazonaws.com + - Action: + - codecommit:CancelUploadArchive + - codecommit:GetBranch + - codecommit:GetCommit + - codecommit:GetUploadArchiveStatus + - codecommit:UploadArchive + Resource: "*" + Effect: Allow + - Action: + - codedeploy:CreateDeployment + - codedeploy:GetApplication + - codedeploy:GetApplicationRevision + - codedeploy:GetDeployment + - codedeploy:GetDeploymentConfig + - codedeploy:RegisterApplicationRevision + Resource: "*" + Effect: Allow + - Action: + - elasticbeanstalk:* + - ec2:* + - elasticloadbalancing:* + - autoscaling:* + - cloudwatch:* + - s3:* + - sns:* + - cloudformation:* + - rds:* + - sqs:* + - ecs:* + Resource: "*" + Effect: Allow + - Action: + - lambda:InvokeFunction + - lambda:ListFunctions + Resource: "*" + Effect: Allow + - Action: + - opsworks:CreateDeployment + - opsworks:DescribeApps + - opsworks:DescribeCommands + - opsworks:DescribeDeployments + - opsworks:DescribeInstances + - opsworks:DescribeStacks + - opsworks:UpdateApp + - opsworks:UpdateStack + Resource: "*" + Effect: Allow + - Action: + - cloudformation:CreateStack + - cloudformation:DeleteStack + - cloudformation:DescribeStacks + - cloudformation:UpdateStack + - cloudformation:CreateChangeSet + - cloudformation:DeleteChangeSet + - cloudformation:DescribeChangeSet + - cloudformation:ExecuteChangeSet + - cloudformation:SetStackPolicy + - cloudformation:ValidateTemplate + Resource: "*" + Effect: Allow + - Action: + - codebuild:BatchGetBuilds + - codebuild:StartBuild + Resource: "*" + Effect: Allow + - Effect: Allow + Action: + - devicefarm:ListProjects + - devicefarm:ListDevicePools + - devicefarm:GetRun + - devicefarm:GetUpload + - devicefarm:CreateUpload + - devicefarm:ScheduleRun + Resource: "*" + - Effect: Allow + Action: + - servicecatalog:ListProvisioningArtifacts + - servicecatalog:CreateProvisioningArtifact + - servicecatalog:DescribeProvisioningArtifact + - servicecatalog:DeleteProvisioningArtifact + - servicecatalog:UpdateProduct + Resource: "*" + - Effect: Allow + Action: + - cloudformation:ValidateTemplate + Resource: "*" + - Effect: Allow + Action: + - ecr:DescribeImages + Resource: "*" + + OrgRepo: + Type: AWS::CodeCommit::Repository + Properties: + RepositoryName: !Sub "${ResourcePrefix}-repo" + RepositoryDescription: AWS Organization Formation repository + Code: + BranchName: main + S3: + Bucket: org-formation-reference + Key: initial-commit.zip + + OrgFormationInitPipeline: + Type: AWS::S3::Bucket + DeletionPolicy: Retain + Properties: + VersioningConfiguration: + Status: Enabled + BucketEncryption: + ServerSideEncryptionConfiguration: + - ServerSideEncryptionByDefault: + SSEAlgorithm: AES256 + OwnershipControls: + Rules: + - ObjectOwnership: BucketOwnerPreferred + + OrgPipeline: + Type: AWS::CodePipeline::Pipeline + Properties: + ArtifactStore: + Type: S3 + Location: !Ref OrgFormationInitPipeline + Name: !Sub "${ResourcePrefix}-pipeline" + RoleArn: !GetAtt OrgPipelineRole.Arn + Stages: + - Name: Source + Actions: + - InputArtifacts: [] + Name: Source + Region: !Ref AWS::Region + ActionTypeId: + Category: Source + Owner: AWS + Provider: CodeCommit + Version: "1" + OutputArtifacts: + - Name: SourceArtifact + Configuration: + PollForSourceChanges: "false" + BranchName: main + RepositoryName: !GetAtt OrgRepo.Name + RunOrder: 1 + - Name: Build + Actions: + - InputArtifacts: + - Name: SourceArtifact + Name: Build + Region: !Ref AWS::Region + ActionTypeId: + Category: Build + Owner: AWS + Provider: CodeBuild + Version: "1" + OutputArtifacts: + - Name: BuildArtifact + Configuration: + ProjectName: !Ref Accounts + RunOrder: 1 + + OrgPipelineEventRule: + Type: AWS::Events::Rule + Properties: + Description: !Sub Listens for changes to the ${OrgRepo.Name} repository and runs the pipeline + EventPattern: + source: + - aws.codecommit + detail-type: + - CodeCommit Repository State Change + resources: + - !GetAtt OrgRepo.Arn + detail: + event: + - referenceCreated + - referenceUpdated + referenceType: + - branch + referenceName: + - main + Name: !Sub "${ResourcePrefix}-pipeline-event-rule" + State: ENABLED + Targets: + - Arn: !Sub arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:${OrgPipeline} + RoleArn: !GetAtt OrgPipelineEventRuleRole.Arn + Id: OrgPipeline + + OrgPipelineEventRuleRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Principal: + Service: + - events.amazonaws.com + Action: + - sts:AssumeRole + Description: !Sub Used to trigger the pipeline attached to the ${OrgRepo.Name} repository + Path: / + Policies: + - PolicyName: !Sub ${ResourcePrefix}-pipeline-event-rule-role-policy + PolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Action: + - codepipeline:StartPipelineExecution + Resource: + - !Sub arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:${OrgPipeline} + +# renaming the logical name here seems the only way to influence the SAR parameter group label. hence this weird name here + Accounts: + Type: AWS::CodeBuild::Project + Properties: + Name: !Sub "${ResourcePrefix}-build" + Description: AWS Organization Formation Build Project + Artifacts: { Type: NO_ARTIFACTS } + Environment: + Type: LINUX_CONTAINER + Image: aws/codebuild/standard:4.0 + ComputeType: BUILD_GENERAL1_SMALL + ImagePullCredentialsType: CODEBUILD + EnvironmentVariables: + - Name: BUILD_ACCT_ROOTEMAIL + Value: !Ref BuildAccountRootEmail + - Name: LOGARCHIVE_ACCT_ROOTEMAIL + Value: !Ref LogArchiveAccountRootEmail + - Name: SECURITY_ACCT_ROOTEMAIL + Value: !Ref SecurityAccountRootEmail + - Name: X_ACCT_ROLE_NAME + Value: !Ref CrossAccountRoleName + - Name: TEMPLATE_PACKAGE_URL + Value: https://org-formation-reference.s3.amazonaws.com/org-formation-reference.zip + - Name: EMAIL_FOR_BUDGET_ALARMS + Value: !Ref EmailForBudgetAlarms + - Name: PRIMARY_REGION + Value: !Ref PrimaryAwsRegion + - Name: RESOURCE_PREFIX + Value: !Ref ResourcePrefix + - Name: MGMT_ACCT_ID + Value: !Ref AWS::AccountId + QueuedTimeoutInMinutes: 480 + ServiceRole: !Ref OrgBuildRole + Source: + GitCloneDepth: 1 + Location: !GetAtt OrgRepo.CloneUrlHttp + Type: CODECOMMIT + LogsConfig: + CloudWatchLogs: + GroupName: !Ref OrgBuildLogGroup + Status: ENABLED + SourceVersion: refs/heads/main + TimeoutInMinutes: 180 diff --git a/sar/initial-commit/buildspec.yml b/sar/initial-commit/buildspec.yml new file mode 100644 index 0000000..ffbdd60 --- /dev/null +++ b/sar/initial-commit/buildspec.yml @@ -0,0 +1,11 @@ +version: 0.2 +phases: + install: + commands: + - npm ci + build: + commands: + - node bin/index.js + post_build: + commands: + - echo Build completed diff --git a/sar/initial-commit/package-lock.json b/sar/initial-commit/package-lock.json new file mode 100644 index 0000000..4620e82 --- /dev/null +++ b/sar/initial-commit/package-lock.json @@ -0,0 +1,1057 @@ +{ + "name": "org-formation-bootstrap", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@aws-sdk/shared-ini-file-loader": { + "version": "3.36.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.36.0.tgz", + "integrity": "sha512-4Xb40+nmfT+qjmHfC17bC96bTME01k+axhSX1GkPH6PlrZrR3ICuq59JLn7SJprw8x7/HHa1HmYpCR1tbkXjNw==", + "requires": { + "tslib": "^2.3.0" + } + }, + "@cspotcode/source-map-consumer": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-consumer/-/source-map-consumer-0.8.0.tgz", + "integrity": "sha512-41qniHzTU8yAGbCp04ohlmSrZf8bkf/iJsl3V0dRGsQN/5GFfx+LbCSsCpp2gqrqjTVg/K6O8ycoV35JIwAzAg==" + }, + "@cspotcode/source-map-support": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.6.1.tgz", + "integrity": "sha512-DX3Z+T5dt1ockmPdobJS/FAsQPW4V4SrWEhD2iYQT2Cb2tQsiMnYxrcUH9By/Z3B+v0S5LMBkQtV/XOBbpLEOg==", + "requires": { + "@cspotcode/source-map-consumer": "0.8.0" + } + }, + "@mhlabs/aws-sdk-sso": { + "version": "0.0.15", + "resolved": "https://registry.npmjs.org/@mhlabs/aws-sdk-sso/-/aws-sdk-sso-0.0.15.tgz", + "integrity": "sha512-TBsxCqexOSh2LHwSxHZddlY8W8cFL4pWiKtHsGkyEmY64ECycIeDV3LIKM6Wh/JeylPqsjegy4mKDSaecE8oKg==", + "requires": { + "@aws-sdk/shared-ini-file-loader": "^3.7.0", + "open": "^7.1.0", + "sha1": "^1.1.1" + } + }, + "@tsconfig/node10": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", + "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==" + }, + "@tsconfig/node12": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", + "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==" + }, + "@tsconfig/node14": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", + "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==" + }, + "@tsconfig/node16": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.2.tgz", + "integrity": "sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA==" + }, + "@types/node": { + "version": "16.10.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.10.3.tgz", + "integrity": "sha512-ho3Ruq+fFnBrZhUYI46n/bV2GjwzSkwuT4dTf0GkuNFmnb8nq4ny2z9JEVemFi6bdEJanHLlYfy9c6FN9B9McQ==" + }, + "@types/node-fetch": { + "version": "2.5.12", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.12.tgz", + "integrity": "sha512-MKgC4dlq4kKNa/mYrwpKfzQMB5X3ee5U6fSprkKpToBqBmX4nFZL9cW5jl6sWn+xpRJ7ypWh2yyqqr8UUCstSw==", + "requires": { + "@types/node": "*", + "form-data": "^3.0.0" + } + }, + "@types/nunjucks": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@types/nunjucks/-/nunjucks-3.2.0.tgz", + "integrity": "sha512-1FM36Hm3EdidJmWlZZafkg/kZME0UZ/0vQ46JE8R7R0JqQafah0r+d4i6d/MJg5DnKxEeAOAeifEVkzo7fEvGg==" + }, + "@types/rc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@types/rc/-/rc-1.2.0.tgz", + "integrity": "sha512-eEQ6Hq0K0VShe00iDzG1DKxA5liTsk7jgcR5eDZ5d5cnivLjPqqcDgqurS5NlQJNfgTNg51dp7zFGWHomr5NJQ==" + }, + "a-sync-waterfall": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz", + "integrity": "sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==" + }, + "acorn": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.5.0.tgz", + "integrity": "sha512-yXbYeFy+jUuYd3/CDcg2NkIYE991XYX/bje7LmjJigUciaeO1JR4XxXgCIV1/Zc/dRuFEyw1L0pbA+qynJkW5Q==" + }, + "acorn-walk": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", + "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==" + }, + "archiver": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-3.1.1.tgz", + "integrity": "sha512-5Hxxcig7gw5Jod/8Gq0OneVgLYET+oNHcxgWItq4TbhOzRLKNAFUb9edAftiMKXvXfCB0vbGrJdZDNq0dWMsxg==", + "requires": { + "archiver-utils": "^2.1.0", + "async": "^2.6.3", + "buffer-crc32": "^0.2.1", + "glob": "^7.1.4", + "readable-stream": "^3.4.0", + "tar-stream": "^2.1.0", + "zip-stream": "^2.1.2" + } + }, + "archiver-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", + "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", + "requires": { + "glob": "^7.1.4", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^2.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + } + } + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" + }, + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "requires": { + "lodash": "^4.17.14" + } + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "aws-organization-formation": { + "version": "0.9.19-alpha.1", + "resolved": "https://registry.npmjs.org/aws-organization-formation/-/aws-organization-formation-0.9.19-alpha.1.tgz", + "integrity": "sha512-PSXsUHXxMhsO35aNs2G6fm08OemZvJ7MybJ3lKCiTFI6DgWcsm1Ukn1omiZdWFQlcij8PR7Rnd5SKVfjbsHmgw==", + "requires": { + "@mhlabs/aws-sdk-sso": "^0.0.15", + "@types/node-fetch": "^2.5.8", + "@types/nunjucks": "^3.1.4", + "@types/rc": "^1.1.0", + "archiver": "^3.1.1", + "aws-sdk": "^2.895.0", + "commander": "^2.20.0", + "ini": "^1.3.5", + "js-yaml": "^4.0.0", + "md5": "^2.2.1", + "md5-file": "^4.0.0", + "memory-streams": "^0.1.3", + "node-fetch": "^2.6.1", + "nunjucks": "^3.2.3", + "pascal-case": "^3.1.2", + "rc": "^1.2.8", + "tmp": "^0.2.1", + "unzipper": "^0.10.11", + "uuid": "^8.3.2", + "yamljs": "^0.3.0" + } + }, + "aws-sdk": { + "version": "2.1004.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.1004.0.tgz", + "integrity": "sha512-dXcE6HnhV8DH/Jde0lklnZIf3XECFBUX77nPE3ibVU+T8cXdd6gISVR2CEbXANxyx8JVF5l1jdv1nBsUPQtkCA==", + "requires": { + "buffer": "4.9.2", + "events": "1.1.1", + "ieee754": "1.1.13", + "jmespath": "0.15.0", + "querystring": "0.2.0", + "sax": "1.2.1", + "url": "0.10.3", + "uuid": "3.3.2", + "xml2js": "0.4.19" + }, + "dependencies": { + "buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + } + } + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "big-integer": { + "version": "1.6.49", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.49.tgz", + "integrity": "sha512-KJ7VhqH+f/BOt9a3yMwJNmcZjG53ijWMTjSAGMveQWyLwqIiwkjNP5PFgDob3Snnx86SjDj6I89fIbv0dkQeNw==" + }, + "binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=", + "requires": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + } + }, + "bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "requires": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM=" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" + }, + "buffer-indexof-polyfill": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", + "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==" + }, + "buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s=" + }, + "chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=", + "requires": { + "traverse": ">=0.3.0 <0.4" + } + }, + "charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "compress-commons": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-2.1.1.tgz", + "integrity": "sha512-eVw6n7CnEMFzc3duyFVrQEuY1BlHR3rYsSztyG32ibGMW722i3C6IizEGMFmfMU+A+fALvBIwxN3czffTcdA+Q==", + "requires": { + "buffer-crc32": "^0.2.13", + "crc32-stream": "^3.0.1", + "normalize-path": "^3.0.0", + "readable-stream": "^2.3.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + } + } + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, + "crc": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", + "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", + "requires": { + "buffer": "^5.1.0" + } + }, + "crc32-stream": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-3.0.1.tgz", + "integrity": "sha512-mctvpXlbzsvK+6z8kJwSJ5crm7yBwrQMTybJzMw1O4lLGJqjlDCXY2Zw7KheiA6XBEcBmfLx1D88mjRGVJtY9w==", + "requires": { + "crc": "^3.4.4", + "readable-stream": "^3.4.0" + } + }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" + }, + "crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=" + }, + "deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" + }, + "duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", + "requires": { + "readable-stream": "^2.0.2" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + } + } + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, + "events": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", + "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" + }, + "form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "dependencies": { + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "graceful-fs": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", + "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==" + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" + }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==" + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "requires": { + "is-docker": "^2.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "jmespath": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", + "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "requires": { + "argparse": "^2.0.1" + } + }, + "lazystream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.0.tgz", + "integrity": "sha1-9plf4PggOS9hOWvolGJAe7dxaOQ=", + "requires": { + "readable-stream": "^2.0.5" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + } + } + }, + "listenercount": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", + "integrity": "sha1-hMinKrWcRyUyFIDJdeZQg0LnCTc=" + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=" + }, + "lodash.difference": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", + "integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw=" + }, + "lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=" + }, + "lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=" + }, + "lodash.union": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", + "integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=" + }, + "lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "requires": { + "tslib": "^2.0.3" + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + }, + "md5": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", + "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==", + "requires": { + "charenc": "0.0.2", + "crypt": "0.0.2", + "is-buffer": "~1.1.6" + } + }, + "md5-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/md5-file/-/md5-file-4.0.0.tgz", + "integrity": "sha512-UC0qFwyAjn4YdPpKaDNw6gNxRf7Mcx7jC1UGCY4boCzgvU2Aoc1mOGzTtrjjLKhM5ivsnhoKpQVxKPp+1j1qwg==" + }, + "memory-streams": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/memory-streams/-/memory-streams-0.1.3.tgz", + "integrity": "sha512-qVQ/CjkMyMInPaaRMrwWNDvf6boRZXaT/DbQeMYcCWuXPEBf1v8qChOc9OlEVQp2uOvRXa1Qu30fLmKhY6NipA==", + "requires": { + "readable-stream": "~1.0.2" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, + "mime-db": { + "version": "1.50.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.50.0.tgz", + "integrity": "sha512-9tMZCDlYHqeERXEHO9f/hKfNXhre5dK2eE/krIvUjZbS2KPcqGDfNShIWS1uW9XOTKQKqK6qbeOci18rbfW77A==" + }, + "mime-types": { + "version": "2.1.33", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.33.tgz", + "integrity": "sha512-plLElXp7pRDd0bNZHw+nMd52vRYjLwQjygaNg7ddJ2uJtTlmnTCjWuPKxVu6//AdaRuME84SvLW91sIkBqGT0g==", + "requires": { + "mime-db": "1.50.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + }, + "no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "requires": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node-fetch": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.5.tgz", + "integrity": "sha512-mmlIVHJEu5rnIxgEgez6b9GgWXbkZj5YZ7fx+2r94a2E+Uirsp6HsPTPlomfdHtpt/B0cdKviwkoaM6pyvUOpQ==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "nunjucks": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.3.tgz", + "integrity": "sha512-psb6xjLj47+fE76JdZwskvwG4MYsQKXUtMsPh6U0YMvmyjRtKRFcxnlXGWglNybtNTNVmGdp94K62/+NjF5FDQ==", + "requires": { + "a-sync-waterfall": "^1.0.0", + "asap": "^2.0.3", + "commander": "^5.1.0" + }, + "dependencies": { + "commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==" + } + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "requires": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + } + }, + "pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "requires": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "punycode": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", + "integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" + }, + "querystring": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz", + "integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=" + }, + "rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "sax": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", + "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "sha1": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/sha1/-/sha1-1.1.1.tgz", + "integrity": "sha1-rdqnqTFo85PxnrKxUJFhjicA+Eg=", + "requires": { + "charenc": ">= 0.0.1", + "crypt": ">= 0.0.1" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=" + }, + "tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "requires": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + } + }, + "tmp": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz", + "integrity": "sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ==", + "requires": { + "rimraf": "^3.0.0" + } + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=" + }, + "traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=" + }, + "ts-node": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.2.1.tgz", + "integrity": "sha512-hCnyOyuGmD5wHleOQX6NIjJtYVIO8bPP8F2acWkB4W06wdlkgyvJtubO/I9NkI88hCFECbsEgoLc0VNkYmcSfw==", + "requires": { + "@cspotcode/source-map-support": "0.6.1", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "yn": "3.1.1" + } + }, + "tslib": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz", + "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" + }, + "typescript": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz", + "integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==" + }, + "unzipper": { + "version": "0.10.11", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.11.tgz", + "integrity": "sha512-+BrAq2oFqWod5IESRjL3S8baohbevGcVA+teAIOYWM3pDVdseogqbzhhvvmiyQrUNKFUnDMtELW3X8ykbyDCJw==", + "requires": { + "big-integer": "^1.6.17", + "binary": "~0.3.0", + "bluebird": "~3.4.1", + "buffer-indexof-polyfill": "~1.0.0", + "duplexer2": "~0.1.4", + "fstream": "^1.0.12", + "graceful-fs": "^4.2.2", + "listenercount": "~1.0.1", + "readable-stream": "~2.3.6", + "setimmediate": "~1.0.4" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + } + } + }, + "url": { + "version": "0.10.3", + "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", + "integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", + "requires": { + "punycode": "1.3.2", + "querystring": "0.2.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, + "webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0=", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" + } + }, + "xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" + }, + "yamljs": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/yamljs/-/yamljs-0.3.0.tgz", + "integrity": "sha512-C/FsVVhht4iPQYXOInoxUM/1ELSf9EsgKH34FofQOp6hwCPrW4vG4w5++TED3xRUo8gD7l0P1J1dLlDYzODsTQ==", + "requires": { + "argparse": "^1.0.7", + "glob": "^7.0.5" + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + } + } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==" + }, + "zip-stream": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-2.1.3.tgz", + "integrity": "sha512-EkXc2JGcKhO5N5aZ7TmuNo45budRaFGHOmz24wtJR7znbNqDPmdZtUauKX6et8KAVseAMBOyWJqEpXcHTBsh7Q==", + "requires": { + "archiver-utils": "^2.1.0", + "compress-commons": "^2.1.1", + "readable-stream": "^3.4.0" + } + } + } +} diff --git a/sar/initial-commit/package.json b/sar/initial-commit/package.json new file mode 100644 index 0000000..f0f63bd --- /dev/null +++ b/sar/initial-commit/package.json @@ -0,0 +1,22 @@ +{ + "name": "org-formation-bootstrap", + "version": "1.0.0", + "description": "", + "main": "bootstrap.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "refzip": "", + "ref:upload": "aws s3 cp initial-commit.zip s3://org-formation-reference/initial-commit.zip" + }, + "author": "", + "license": "ISC", + "dependencies": { + "aws-organization-formation": "^0.9.19-alpha.1", + "nunjucks": "^3.2.3", + "ts-node": "^10.2.1", + "typescript": "^4.4.3" + }, + "devDependencies": { + "@types/node": "^16.10.3" + } +} diff --git a/sar/initial-commit/src/ensure-running-mgmt-acct.ts b/sar/initial-commit/src/ensure-running-mgmt-acct.ts new file mode 100644 index 0000000..9b5650e --- /dev/null +++ b/sar/initial-commit/src/ensure-running-mgmt-acct.ts @@ -0,0 +1,27 @@ +import { ConsoleUtil } from "aws-organization-formation/dist/src/util/console-util"; +import * as AWS from "aws-sdk"; + +export const ensureRunningInManagementAccount = async (): Promise => { + const orgs = new AWS.Organizations({ region: "us-east-1" }); + const sts = new AWS.STS(); + + const me = await sts.getCallerIdentity().promise(); + const organization = await orgs.describeOrganization().promise(); + + if (!me.Account) { + ConsoleUtil.LogError("unable to get the current account id from STS"); + return false; + } + + if ( + !organization.Organization || + !organization.Organization.MasterAccountId + ) { + ConsoleUtil.LogError( + "unable to get the management account id from Organizations" + ); + return false; + } + + return me.Account === organization.Organization.MasterAccountId; +}; diff --git a/sar/initial-commit/src/ensure.ts b/sar/initial-commit/src/ensure.ts new file mode 100644 index 0000000..ef71b83 --- /dev/null +++ b/sar/initial-commit/src/ensure.ts @@ -0,0 +1,53 @@ +import { AwsOrganization } from "aws-organization-formation/dist/src/aws-provider/aws-organization"; +import { AwsOrganizationReader } from "aws-organization-formation/dist/src/aws-provider/aws-organization-reader"; +import { AwsOrganizationWriter } from "aws-organization-formation/dist/src/aws-provider/aws-organization-writer"; +import { OrganizationalUnitResource } from "aws-organization-formation/dist/src/parser/model"; +import { ConsoleUtil } from "aws-organization-formation/dist/src/util/console-util"; +import * as AWS from "aws-sdk"; + +const orgs = new AWS.Organizations({ region: "us-east-1" }); +let _org: undefined | AwsOrganization; + +export const ensureInitialized = async (): Promise => { + if (_org) return _org; + const reader = new AwsOrganizationReader(orgs); + const org = new AwsOrganization(reader); + await org.initialize(); + ConsoleUtil.LogDebug("initialization done"); + _org = org; + return _org; +}; + +export const ensureAccountExists = async ( + rootEmail: string, + accountName: string, + crossAccountRoleName: string, + logicalId: string +): Promise => { + const org = await ensureInitialized(); + const writer = new AwsOrganizationWriter(orgs, org); + + const accountId = await writer.createAccount({ + rootEmail, + accountName, + organizationAccessRoleName: crossAccountRoleName, + logicalId, + } as any); + + return accountId; +}; + +export const ensureOuExists = async ( + ouName: string, + logicalId: string +): Promise => { + const org = await ensureInitialized(); + const writer = new AwsOrganizationWriter(orgs, org); + + const ouId = await writer.createOrganizationalUnit({ + organizationalUnitName: ouName, + logicalId, + } as OrganizationalUnitResource); + + return ouId; +}; diff --git a/sar/initial-commit/src/index.ts b/sar/initial-commit/src/index.ts new file mode 100644 index 0000000..d3e2260 --- /dev/null +++ b/sar/initial-commit/src/index.ts @@ -0,0 +1,187 @@ +import { ConsoleUtil } from "aws-organization-formation/dist/src/util/console-util"; +import { ensureAccountExists, ensureOuExists } from "./ensure"; +import { ensureRunningInManagementAccount } from "./ensure-running-mgmt-acct"; +import { performBootstrap } from "./perform-bootstrap"; +ConsoleUtil.verbose = false; +process.env.X_ACCT_ROLE_NAME = "OrganizationAccountAccessRole"; +process.env.LOGARCHIVE_ACCT_ROOTEMAIL = + "ccount+log-archive@olafconijn.awsapps.com"; +process.env.BUILD_ACCT_ROOTEMAIL = "ccount+a@olafconijn.awsapps.com"; +process.env.TEMPLATE_PACKAGE_URL = + "https://org-formation-reference.s3.amazonaws.com/org-formation-reference.zip"; +process.env.SECURITY_ACCT_ROOTEMAIL = "ccount+security@olafconijn.awsapps.com"; + +const bootstrap = async () => { + ConsoleUtil.LogInfo( + "Hi there! this process will set up org-formation within your organization. i'm excited. " + ); + + const templatePackageUrl = process.env["TEMPLATE_PACKAGE_URL"]; + const buildAcctRootEmail = process.env["BUILD_ACCT_ROOTEMAIL"]; + const buildAcctName = process.env["BUILD_ACCT_NAME"] + ? process.env["BUILD_ACCT_NAME"] + : "Organization Build Account"; + + const securityAcctRootEmail = process.env["SECURITY_ACCT_ROOTEMAIL"]; + const securityAcctName = process.env["SECURITY_ACCT_NAME"] + ? process.env["SECURITY_ACCT_NAME"] + : "Security Account"; + + const logArchiveAcctRootEmail = process.env["LOGARCHIVE_ACCT_ROOTEMAIL"]; + const logArchiveAcctName = process.env["LOGARCHIVE_ACCT_NAME"] + ? process.env["LOGARCHIVE_ACCT_NAME"] + : "Log Archive Account"; + + const crossAccountRoleName = process.env["X_ACCT_ROLE_NAME"] + ? process.env["X_ACCT_ROLE_NAME"] + : "OrganizationAccountAccessRole"; + + const logicalNameToIdMap: Record = {}; + const logicalNameToRootEmailMap: Record = {}; + + ConsoleUtil.LogInfo( + `The following parameters have been passed: BUILD_ACCT_ROOTEMAIL = ${buildAcctRootEmail}, LOGARCHIVE_ACCT_ROOTEMAIL = ${logArchiveAcctRootEmail}, SECURITY_ACCT_ROOTEMAIL = ${securityAcctRootEmail}.` + ); + + if (!buildAcctRootEmail) { + ConsoleUtil.LogError("parameter BUILD_ACCT_ROOTEMAIL is missing. "); + process.exitCode = 1; + return; + } + + if (!securityAcctRootEmail) { + ConsoleUtil.LogError("parameter SECURITY_ACCT_ROOTEMAIL is missing. "); + process.exitCode = 1; + return; + } + + if (!logArchiveAcctRootEmail) { + ConsoleUtil.LogError("parameter LOGARCHIVE_ACCT_ROOTEMAIL is missing. "); + process.exitCode = 1; + return; + } + + ConsoleUtil.LogInfo( + "Step 1: check whether you are running the organization management account..." + ); + const runningInManagement = await ensureRunningInManagementAccount(); + if (!runningInManagement) { + ConsoleUtil.LogError( + "not running in the organization management account. " + ); + process.exitCode = 1; + return; + } + ConsoleUtil.LogInfo( + "Step 2: We will now check whether the build account already exists and if not we will create it..." + ); + + const buildAccountId = await ensureAccountExists( + buildAcctRootEmail, + buildAcctName, + crossAccountRoleName, + "OrgBuildAccount" + ); + + if (!buildAccountId) { + ConsoleUtil.LogError("unable to get or create build account details. "); + process.exitCode = 1; + return; + } + logicalNameToIdMap["OrgBuildAccount"] = buildAccountId; + logicalNameToRootEmailMap["OrgBuildAccount"] = buildAcctRootEmail; + + ConsoleUtil.LogInfo( + `- AWS account id of the build account: ${buildAccountId}` + ); + + const securityAccountId = await ensureAccountExists( + securityAcctRootEmail, + securityAcctName, + crossAccountRoleName, + "SecurityAccount" + ); + + if (!securityAccountId) { + ConsoleUtil.LogError("unable to get or create security account details. "); + process.exitCode = 1; + return; + } + logicalNameToIdMap["SecurityAccount"] = securityAccountId; + logicalNameToRootEmailMap["SecurityAccount"] = securityAcctRootEmail; + + ConsoleUtil.LogInfo( + `- AWS account id of the security account: ${securityAccountId}` + ); + + const logArchiveAccountId = await ensureAccountExists( + logArchiveAcctRootEmail, + logArchiveAcctName, + crossAccountRoleName, + "LogArchiveAccount" + ); + + if (!logArchiveAccountId) { + ConsoleUtil.LogError( + "unable to get or create log archive account details. " + ); + process.exitCode = 1; + return; + } + logicalNameToIdMap["LogArchiveAccount"] = logArchiveAccountId; + logicalNameToRootEmailMap["LogArchiveAccount"] = logArchiveAcctRootEmail; + + ConsoleUtil.LogInfo( + `- AWS account id of the log archive account: ${logArchiveAccountId}` + ); + + logicalNameToIdMap["SuspendedOu"] = await ensureOuExists( + "suspended", + "SuspendedOu" + ); + ConsoleUtil.LogInfo(`- AWS OU suspended exists`); + logicalNameToIdMap["SharedOu"] = await ensureOuExists("shared", "SharedOu"); + ConsoleUtil.LogInfo(`- AWS OU shared exists`); + logicalNameToIdMap["SdlcOu"] = await ensureOuExists("sdlc", "SdlcOu"); + ConsoleUtil.LogInfo(`- AWS OU sdlc exists`); + logicalNameToIdMap["ProdOu"] = await ensureOuExists("prod", "ProdOu"); + ConsoleUtil.LogInfo(`- AWS OU prod exists`); + + ConsoleUtil.LogInfo("Step 3: initializing org-formation ..."); + + const packageParameters: Record = { + EmailForBudgetAlarms: + process.env["EMAIL_FOR_BUDGET_ALARMS"] ?? "someone@your-org.com", + PrimaryAwsRegion: process.env["PRIMARY_REGION"] ?? "us-east-1", + ResourcePrefix: process.env["RESOURCE_PREFIX"] ?? "orgformation", + StateBucketName: "organization-formation-" + buildAccountId, + ManagementAcctId: process.env["MGMT_ACCT_ID"] as string, + }; + + const initSucceeded = await performBootstrap( + buildAccountId, + crossAccountRoleName, + templatePackageUrl, + logicalNameToIdMap, + logicalNameToRootEmailMap, + packageParameters + ); + + if (!initSucceeded) { + process.exitCode = 1; + return; + } + + ConsoleUtil.LogInfo("done!"); + ConsoleUtil.LogInfo( + "org-formation and this reference architecture seems have have successfully installed." + ); + ConsoleUtil.LogInfo( + `best to head over to account ${buildAccountId} (using role ${crossAccountRoleName}) to check out the CodeCommit repository.` + ); +}; + +bootstrap().catch((x) => { + console.error(x); + process.exitCode = 1; +}); diff --git a/sar/initial-commit/src/perform-bootstrap.ts b/sar/initial-commit/src/perform-bootstrap.ts new file mode 100644 index 0000000..d15486f --- /dev/null +++ b/sar/initial-commit/src/perform-bootstrap.ts @@ -0,0 +1,68 @@ +import { InitPipelineCommand } from "aws-organization-formation/dist/src/commands/init-organization-pipeline"; +import { ConsoleUtil } from "aws-organization-formation/dist/src/util/console-util"; + +export const performBootstrap = async ( + buildAccountId: string, + crossAccountRoleName: string, + templatePackageUrl: string | undefined, + logicalNameToIdMap: Record, + logicalNameToRootEmailMap: Record, + packageParameters: Record +): Promise => { + try { + return performAndRetryIfNeeded(async () => { + const command = new InitPipelineCommand(undefined as any); + await command.performCommand({ + buildAccountId, + crossAccountRoleName: "OrganizationAccountAccessRole", + roleStackName: "organization-formation-role", + stackName: "organization-formation-build", + resourcePrefix: "organization-formation", + repositoryName: "organization-formation", + stateObject: "state.json", + stateBucketName: "organization-formation-${AWS::AccountId}", + buildProcessRoleName: crossAccountRoleName, + delegateToBuildAccount: true, + region: undefined as unknown as string, + templatePackageUrl, + logicalNameToIdMap, + logicalNameToRootEmailMap, + packageParameters, + }); + return true; + }); + } catch (err) { + ConsoleUtil.LogError("error running init", err as Error); + return false; + } +}; + +export const performAndRetryIfNeeded = async ( + fn: () => Promise +): Promise => { + let shouldRetry = false; + let retryCount = 0; + do { + shouldRetry = false; + try { + return await fn(); + } catch (err) { + if (retryCount < 3) { + retryCount = retryCount + 1; + shouldRetry = true; + const wait = Math.pow(retryCount, 2) + Math.random(); + ConsoleUtil.LogDebug( + `retrying ${err}. wait ${wait} and retry-count ${retryCount}` + ); + await sleep(wait * 2000); + continue; + } + throw err; + } + } while (shouldRetry); + throw Error(); +}; + +export const sleep = (time: number): Promise => { + return new Promise((resolve) => setTimeout(resolve, time)); +}; diff --git a/sar/initial-commit/tsconfig.json b/sar/initial-commit/tsconfig.json new file mode 100644 index 0000000..c2d7de4 --- /dev/null +++ b/sar/initial-commit/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "es2018", + "module": "commonjs", + "declaration": true, + "outDir": "./bin", + "lib": ["es2018", "es2019"], + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "sourceMap": true + } +} diff --git a/sar/minimal.yml b/sar/minimal.yml new file mode 100644 index 0000000..51ab8ce --- /dev/null +++ b/sar/minimal.yml @@ -0,0 +1,401 @@ +AWSTemplateFormatVersion: "2010-09-09" +Description: "Organization Formation Reference project bootstrap" +Transform: "AWS::Serverless-2016-10-31" + +Metadata: + AWS::ServerlessRepo::Application: + Name: org-formation-minimal + Description: Organization Formation Reference + Author: Org Formation + SpdxLicenseId: Apache-2.0 + Labels: ["org-formation", "multi-account", "cicd", "devops"] + HomePageUrl: https://github.com/org-formation/org-formation-reference + SemanticVersion: 1.0.0 + SourceCodeUrl: https://github.com/org-formation/org-formation-reference + AWS::CloudFormation::Interface: + ParameterGroups: + - Label: + default: "Bootstrapping" + Parameters: + - bootstrapCleanup + - Label: + default: "Organization Formation Build Account" + Parameters: + - BuildAccount + - buildAccountRootEmail + - buildAccountName + - buildAccountId + - Label: + default: "Organization Formation" + Parameters: + - buildAccountRootEmail + - buildAccountName + - crossAccountRoleName + +Parameters: + resourcePrefix: + Type: String + Default: "orgformation-bootstrap" + + bootstrapCleanup: + Type: String + Default: "YES" + AllowedValues: ["YES", "NO"] + Description: Should the Bootstrapping process clean itself up? + + buildAccountRootEmail: + Type: String + Description: What is the root email of the account you would like to use as build account + + buildAccountName: + Type: String + Default: "Organization Build" + Description: What should be the name of the build account? + + crossAccountRoleName: + Type: String + Default: "OrganizationAccountAccessRole" + Description: For new accounts that that will be created, what would be the cross account access role name? + + + # buildAccountId: + # Type: String + # Default: "-" + # Description: If using existing account, what account id? + + # formationResourcePrefix: + # Type: String + # Default: orgformation + # Description: How should org-formation resources be prefixed? + + # formationRepositoryName: + # Type: String + # Default: organization-formation + # Description: How should the org-formation repository be named? + + # formationStateBucketName: + # Type: String + # Default: organization-formation-${AWS::AccountId} + # Description: How should the org-formation state bucket be named? + +Resources: + OrgBuildLogGroup: + Type: "AWS::Logs::LogGroup" + Properties: + RetentionInDays: 7 + LogGroupName: !Sub "/codebuild/${resourcePrefix}-build" + + OrgBuildRole: + Type: AWS::IAM::Role + Properties: + RoleName: !Sub "${resourcePrefix}-build-service-role" + AssumeRolePolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Principal: + Service: codebuild.amazonaws.com + Action: + - sts:AssumeRole + Path: / + ManagedPolicyArns: + - "arn:aws:iam::aws:policy/AdministratorAccess" # see: https://github.com/org-formation/org-formation-cli/blob/master/docs/least-priviledge.md + Policies: + - PolicyName: !Sub "${resourcePrefix}-build-service-role-policy" + PolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Resource: + - !GetAtt OrgBuildLogGroup.Arn + - !Sub "${OrgBuildLogGroup.Arn}:*" + Action: + - logs:CreateLogGroup + - logs:CreateLogStream + - logs:PutLogEvents + - Effect: Allow + Resource: + - !Sub "arn:aws:s3:::organization-formation-${AWS::AccountId}" + - !Sub "arn:aws:s3:::organization-formation-${AWS::AccountId}/*" + Action: + - s3:PutObject + - s3:GetObject + - s3:GetObjectVersion + - s3:GetBucketAcl + - s3:GetBucketLocation + - Effect: Allow + Resource: + - !GetAtt OrgRepo.Arn + Action: + - codecommit:GitPull + + OrgPipelineRole: + Type: AWS::IAM::Role + Properties: + RoleName: !Sub "${resourcePrefix}-codepipeline-service-role" + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Principal: + Service: codepipeline.amazonaws.com + Action: + - sts:AssumeRole + Path: / + Policies: + - PolicyName: !Sub "${resourcePrefix}-codepipeline-service-role-policy" + PolicyDocument: + Version: "2012-10-17" + Statement: + - Action: + - iam:PassRole + Resource: "*" + Effect: Allow + Condition: + StringEqualsIfExists: + iam:PassedToService: + - cloudformation.amazonaws.com + - elasticbeanstalk.amazonaws.com + - ec2.amazonaws.com + - ecs-tasks.amazonaws.com + - Action: + - codecommit:CancelUploadArchive + - codecommit:GetBranch + - codecommit:GetCommit + - codecommit:GetUploadArchiveStatus + - codecommit:UploadArchive + Resource: "*" + Effect: Allow + - Action: + - codedeploy:CreateDeployment + - codedeploy:GetApplication + - codedeploy:GetApplicationRevision + - codedeploy:GetDeployment + - codedeploy:GetDeploymentConfig + - codedeploy:RegisterApplicationRevision + Resource: "*" + Effect: Allow + - Action: + - elasticbeanstalk:* + - ec2:* + - elasticloadbalancing:* + - autoscaling:* + - cloudwatch:* + - s3:* + - sns:* + - cloudformation:* + - rds:* + - sqs:* + - ecs:* + Resource: "*" + Effect: Allow + - Action: + - lambda:InvokeFunction + - lambda:ListFunctions + Resource: "*" + Effect: Allow + - Action: + - opsworks:CreateDeployment + - opsworks:DescribeApps + - opsworks:DescribeCommands + - opsworks:DescribeDeployments + - opsworks:DescribeInstances + - opsworks:DescribeStacks + - opsworks:UpdateApp + - opsworks:UpdateStack + Resource: "*" + Effect: Allow + - Action: + - cloudformation:CreateStack + - cloudformation:DeleteStack + - cloudformation:DescribeStacks + - cloudformation:UpdateStack + - cloudformation:CreateChangeSet + - cloudformation:DeleteChangeSet + - cloudformation:DescribeChangeSet + - cloudformation:ExecuteChangeSet + - cloudformation:SetStackPolicy + - cloudformation:ValidateTemplate + Resource: "*" + Effect: Allow + - Action: + - codebuild:BatchGetBuilds + - codebuild:StartBuild + Resource: "*" + Effect: Allow + - Effect: Allow + Action: + - devicefarm:ListProjects + - devicefarm:ListDevicePools + - devicefarm:GetRun + - devicefarm:GetUpload + - devicefarm:CreateUpload + - devicefarm:ScheduleRun + Resource: "*" + - Effect: Allow + Action: + - servicecatalog:ListProvisioningArtifacts + - servicecatalog:CreateProvisioningArtifact + - servicecatalog:DescribeProvisioningArtifact + - servicecatalog:DeleteProvisioningArtifact + - servicecatalog:UpdateProduct + Resource: "*" + - Effect: Allow + Action: + - cloudformation:ValidateTemplate + Resource: "*" + - Effect: Allow + Action: + - ecr:DescribeImages + Resource: "*" + + OrgRepo: + Type: AWS::CodeCommit::Repository + Properties: + RepositoryName: !Sub "${resourcePrefix}-repo" + RepositoryDescription: AWS Organization Formation repository + Code: + BranchName: main + S3: + Bucket: org-formation-reference + Key: initial-commit.zip + + OrgFormationInitPipeline: + Type: AWS::S3::Bucket + DeletionPolicy: Retain + Properties: + VersioningConfiguration: + Status: Enabled + BucketEncryption: + ServerSideEncryptionConfiguration: + - ServerSideEncryptionByDefault: + SSEAlgorithm: AES256 + OwnershipControls: + Rules: + - ObjectOwnership: BucketOwnerPreferred + + OrgPipeline: + Type: AWS::CodePipeline::Pipeline + Properties: + ArtifactStore: + Type: S3 + Location: !Ref OrgFormationInitPipeline + Name: !Sub "${resourcePrefix}-pipeline" + RoleArn: !GetAtt OrgPipelineRole.Arn + Stages: + - Name: Source + Actions: + - InputArtifacts: [] + Name: Source + Region: !Ref AWS::Region + ActionTypeId: + Category: Source + Owner: AWS + Provider: CodeCommit + Version: "1" + OutputArtifacts: + - Name: SourceArtifact + Configuration: + PollForSourceChanges: "false" + BranchName: main + RepositoryName: !GetAtt OrgRepo.Name + RunOrder: 1 + - Name: Build + Actions: + - InputArtifacts: + - Name: SourceArtifact + Name: Build + Region: !Ref AWS::Region + ActionTypeId: + Category: Build + Owner: AWS + Provider: CodeBuild + Version: "1" + OutputArtifacts: + - Name: BuildArtifact + Configuration: + ProjectName: !Ref OrgBuildProject + RunOrder: 1 + + OrgPipelineEventRule: + Type: AWS::Events::Rule + Properties: + Description: !Sub Listens for changes to the ${OrgRepo.Name} repository and runs the pipeline + EventPattern: + source: + - aws.codecommit + detail-type: + - CodeCommit Repository State Change + resources: + - !GetAtt OrgRepo.Arn + detail: + event: + - referenceCreated + - referenceUpdated + referenceType: + - branch + referenceName: + - main + Name: !Sub "${resourcePrefix}-pipeline-event-rule" + State: ENABLED + Targets: + - Arn: !Sub arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:${OrgPipeline} + RoleArn: !GetAtt OrgPipelineEventRuleRole.Arn + Id: OrgPipeline + + OrgPipelineEventRuleRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: Allow + Principal: + Service: + - events.amazonaws.com + Action: + - sts:AssumeRole + Description: !Sub Used to trigger the pipeline attached to the ${OrgRepo.Name} repository + Path: / + Policies: + - PolicyName: !Sub ${resourcePrefix}-pipeline-event-rule-role-policy + PolicyDocument: + Version: 2012-10-17 + Statement: + - Effect: Allow + Action: + - codepipeline:StartPipelineExecution + Resource: + - !Sub arn:aws:codepipeline:${AWS::Region}:${AWS::AccountId}:${OrgPipeline} + RoleName: !Sub ${resourcePrefix}-pipeline-event-rule-role + + OrgBuildProject: + Type: AWS::CodeBuild::Project + Properties: + Name: !Sub "${resourcePrefix}-build" + Description: AWS Organization Formation Build Project + Artifacts: { Type: NO_ARTIFACTS } + Environment: + Type: LINUX_CONTAINER + Image: aws/codebuild/standard:4.0 + ComputeType: BUILD_GENERAL1_SMALL + ImagePullCredentialsType: CODEBUILD + EnvironmentVariables: + - Name: BUILD_ACCT_ROOTEMAIL + Value: !Ref buildAccountRootEmail + - Name: BUILD_ACCT_NAME + Value: !Ref buildAccountName + - Name: X_ACCT_ROLE_NAME + Value: !Ref crossAccountRoleName + QueuedTimeoutInMinutes: 480 + ServiceRole: !Ref OrgBuildRole + Source: + GitCloneDepth: 1 + Location: !GetAtt OrgRepo.CloneUrlHttp + Type: CODECOMMIT + LogsConfig: + CloudWatchLogs: + GroupName: !Ref OrgBuildLogGroup + Status: ENABLED + SourceVersion: refs/heads/main + TimeoutInMinutes: 180 diff --git a/package-lock.json b/sar/package-lock.json similarity index 64% rename from package-lock.json rename to sar/package-lock.json index f4100bd..4ac7406 100644 --- a/package-lock.json +++ b/sar/package-lock.json @@ -1,14 +1,76 @@ { - "name": "org-formation-reference", - "version": "0.1.0", + "name": "bootstrap", + "version": "1.0.0", "lockfileVersion": 1, "requires": true, "dependencies": { + "@aws-sdk/shared-ini-file-loader": { + "version": "3.22.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/shared-ini-file-loader/-/shared-ini-file-loader-3.22.0.tgz", + "integrity": "sha512-qLTSqjqFc98POGg0W+VsWdDcO/6lMHDB5wAvjULIQ+ra93FybL0jG6eA8Ds1CJ8Ibz446l25P0QZsNHkpKX2EA==", + "requires": { + "tslib": "^2.0.0" + } + }, + "@mhlabs/aws-sdk-sso": { + "version": "0.0.15", + "resolved": "https://registry.npmjs.org/@mhlabs/aws-sdk-sso/-/aws-sdk-sso-0.0.15.tgz", + "integrity": "sha512-TBsxCqexOSh2LHwSxHZddlY8W8cFL4pWiKtHsGkyEmY64ECycIeDV3LIKM6Wh/JeylPqsjegy4mKDSaecE8oKg==", + "requires": { + "@aws-sdk/shared-ini-file-loader": "^3.7.0", + "open": "^7.1.0", + "sha1": "^1.1.1" + } + }, + "@tsconfig/node10": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.8.tgz", + "integrity": "sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==" + }, + "@tsconfig/node12": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.9.tgz", + "integrity": "sha512-/yBMcem+fbvhSREH+s14YJi18sp7J9jpuhYByADT2rypfajMZZN4WQ6zBGgBKp53NKmqI36wFYDb3yaMPurITw==" + }, + "@tsconfig/node14": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz", + "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==" + }, + "@tsconfig/node16": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.1.tgz", + "integrity": "sha512-FTgBI767POY/lKNDNbIzgAX6miIDBs6NTCbdlDb8TrWovHsSvaVIZDlTqym29C6UqhzwcJx4CYr+AlrMywA0cA==" + }, + "@types/node": { + "version": "16.3.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-16.3.3.tgz", + "integrity": "sha512-8h7k1YgQKxKXWckzFCMfsIwn0Y61UK6tlD6y2lOb3hTOIMlK3t9/QwHOhc81TwU+RMf0As5fj7NPjroERCnejQ==" + }, + "@types/node-fetch": { + "version": "2.5.11", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.11.tgz", + "integrity": "sha512-2upCKaqVZETDRb8A2VTaRymqFBEgH8u6yr96b/u3+1uQEPDRo3mJLEiPk7vdXBHRtjwkjqzFYMJXrt0Z9QsYjQ==", + "requires": { + "@types/node": "*", + "form-data": "^3.0.0" + } + }, + "@types/nunjucks": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/@types/nunjucks/-/nunjucks-3.1.5.tgz", + "integrity": "sha512-0zEdmQNNvQ+xyV9kqQvAV93UVroTwhE78toVUDT0GBnGcW2jQBZnB4al9qq2LqI5qHOqROy/DvvAY/UwrbvV1A==" + }, "@types/rc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@types/rc/-/rc-1.1.0.tgz", "integrity": "sha512-qw1q31xPnaeExbOA1daA3nfeKW2uZQN4Xg8QqZDM3vsXPHK/lyDpjWXJQIcrByRDcBzZJ3ccchSMMTDtCWgFpA==" }, + "a-sync-waterfall": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/a-sync-waterfall/-/a-sync-waterfall-1.0.1.tgz", + "integrity": "sha512-RYTOHHdWipFUliRFMCS4X2Yn2X8M87V/OpSqWzKKOGhzqyUxzyVmhHDH9sAvG+ZuQf/TAOFsLCpMw09I1ufUnA==" + }, "archiver": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/archiver/-/archiver-3.1.1.tgz", @@ -56,13 +118,20 @@ } } }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + }, "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "requires": { - "sprintf-js": "~1.0.2" - } + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=" }, "async": { "version": "2.6.3", @@ -72,29 +141,39 @@ "lodash": "^4.17.14" } }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, "aws-organization-formation": { - "version": "0.9.14", - "resolved": "https://registry.npmjs.org/aws-organization-formation/-/aws-organization-formation-0.9.14.tgz", - "integrity": "sha512-wPQ/ZscDnFeFVR1DWcebRWFxLuhVSj4ClSTC2dT8Up3PL74i6YIc+NyBPdfHqLqZ2DeDwkNVa9FPF9zcS9ZXAg==", + "version": "0.9.16", + "resolved": "https://registry.npmjs.org/aws-organization-formation/-/aws-organization-formation-0.9.16.tgz", + "integrity": "sha512-NPUtXu8BTHLfABVFVHxaeN/WU8L0AwR+t1FV+2Gh51mGyEgvu0rLSu/vma1/gW3U4plVVtf+Wcq7eRWGDZKI7Q==", "requires": { + "@mhlabs/aws-sdk-sso": "^0.0.15", + "@types/node-fetch": "^2.5.8", + "@types/nunjucks": "^3.1.4", "@types/rc": "^1.1.0", "archiver": "^3.1.1", "aws-sdk": "^2.786.0", "commander": "^2.20.0", "ini": "^1.3.5", - "js-yaml": "^3.13.1", + "js-yaml": "^4.0.0", "md5": "^2.2.1", "md5-file": "^4.0.0", "memory-streams": "^0.1.3", + "node-fetch": "^2.6.1", + "nunjucks": "^3.2.3", "rc": "^1.2.8", "uuid": "^3.3.3", "yamljs": "^0.3.0" } }, "aws-sdk": { - "version": "2.827.0", - "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.827.0.tgz", - "integrity": "sha512-71PWS1dqJ65/SeNgDQWEgbJ6oKCuB+Ypq30TM3EyzbAHaxl69WjQRK71oJ2bjhdIHfGQJtOV0G9wg4zpge4Erg==", + "version": "2.948.0", + "resolved": "https://registry.npmjs.org/aws-sdk/-/aws-sdk-2.948.0.tgz", + "integrity": "sha512-UJaCwccNaNNFtbhlvg+BmcaVWNI7RPonZA16nca0s3O+UnHm5y5H/nN6XpuJp+NUrxrLgTFaztPvjmBp5q6p+g==", "requires": { "buffer": "4.9.2", "events": "1.1.1", @@ -130,9 +209,9 @@ } }, "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "base64-js": { "version": "1.5.1", @@ -140,9 +219,9 @@ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" }, "bl": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.3.tgz", - "integrity": "sha512-fs4G6/Hu4/EE+F75J8DuN/0IpQqNjAdC7aEQv7Qt8MHGUH7Ckv2MwTEEeN9QehD0pfIDkMI1bkHYkKy7xHyKIg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "requires": { "buffer": "^5.5.0", "inherits": "^2.0.4", @@ -172,11 +251,24 @@ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, "charenc": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=" }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, "commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", @@ -236,6 +328,11 @@ "readable-stream": "^3.4.0" } }, + "create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==" + }, "crypt": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", @@ -246,6 +343,16 @@ "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==" }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" + }, "end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", @@ -254,16 +361,21 @@ "once": "^1.4.0" } }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" - }, "events": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" }, + "form-data": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", + "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, "fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", @@ -275,9 +387,9 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -288,9 +400,9 @@ } }, "graceful-fs": { - "version": "4.2.4", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", - "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" + "version": "4.2.6", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", + "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" }, "ieee754": { "version": "1.2.1", @@ -321,6 +433,19 @@ "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" }, + "is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==" + }, + "is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "requires": { + "is-docker": "^2.0.0" + } + }, "isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -332,12 +457,11 @@ "integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" }, "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "argparse": "^2.0.1" } }, "lazystream": { @@ -365,9 +489,9 @@ } }, "lodash": { - "version": "4.17.20", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", - "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "lodash.defaults": { "version": "4.2.0", @@ -394,6 +518,11 @@ "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", "integrity": "sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=" }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + }, "md5": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", @@ -440,6 +569,19 @@ } } }, + "mime-db": { + "version": "1.48.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.48.0.tgz", + "integrity": "sha512-FM3QwxV+TnZYQ2aRqhlKBMHxk10lTbMt3bBkMAp54ddrNeVSfcQYOOKuGuy3Ddrm38I04If834fOUSq1yzslJQ==" + }, + "mime-types": { + "version": "2.1.31", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.31.tgz", + "integrity": "sha512-XGZnNzm3QvgKxa8dpzyhFTHmpP3l5YNusmne07VUOXxou9CqUqYa/HBy124RqtVh/O2pECas/MOcsDgpilPOPg==", + "requires": { + "mime-db": "1.48.0" + } + }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -453,11 +595,33 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" }, + "node-fetch": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz", + "integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==" + }, "normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" }, + "nunjucks": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-3.2.3.tgz", + "integrity": "sha512-psb6xjLj47+fE76JdZwskvwG4MYsQKXUtMsPh6U0YMvmyjRtKRFcxnlXGWglNybtNTNVmGdp94K62/+NjF5FDQ==", + "requires": { + "a-sync-waterfall": "^1.0.0", + "asap": "^2.0.3", + "commander": "^5.1.0" + }, + "dependencies": { + "commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==" + } + } + }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", @@ -466,6 +630,15 @@ "wrappy": "1" } }, + "open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "requires": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + } + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -517,6 +690,29 @@ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.1.tgz", "integrity": "sha1-e45lYZCyKOgaZq6nSEgNgozS03o=" }, + "sha1": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/sha1/-/sha1-1.1.1.tgz", + "integrity": "sha1-rdqnqTFo85PxnrKxUJFhjicA+Eg=", + "requires": { + "charenc": ">= 0.0.1", + "crypt": ">= 0.0.1" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", @@ -547,6 +743,28 @@ "readable-stream": "^3.1.1" } }, + "ts-node": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.1.0.tgz", + "integrity": "sha512-6szn3+J9WyG2hE+5W8e0ruZrzyk1uFLYye6IGMBadnOzDh8aP7t8CbFpsfCiEx2+wMixAhjFt7lOZC4+l+WbEA==", + "requires": { + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "source-map-support": "^0.5.17", + "yn": "3.1.1" + } + }, + "tslib": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz", + "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==" + }, "url": { "version": "0.10.3", "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", @@ -592,8 +810,23 @@ "requires": { "argparse": "^1.0.7", "glob": "^7.0.5" + }, + "dependencies": { + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "requires": { + "sprintf-js": "~1.0.2" + } + } } }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==" + }, "zip-stream": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-2.1.3.tgz", diff --git a/sar/package.json b/sar/package.json new file mode 100644 index 0000000..198ef39 --- /dev/null +++ b/sar/package.json @@ -0,0 +1,13 @@ +{ + "name": "bootstrap", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "bootstrap:zip": "cd initial-commit && npx tsc && zip -vr initial-commit.zip . -x \"node_modules/*\"", + "bootstrap:upload": "cd initial-commit && aws s3 cp initial-commit.zip s3://org-formation-reference/initial-commit.zip && aws s3api put-object-acl --bucket org-formation-reference --key initial-commit.zip --acl public-read" + }, + "author": "", + "license": "ISC", + "dependencies": {} +} diff --git a/src/_parameters.yml b/src/_parameters.yml index e1b3951..054fe1e 100644 --- a/src/_parameters.yml +++ b/src/_parameters.yml @@ -1,11 +1,11 @@ # if you use this resource prefix in all stacks deployed by this project they are easily identifiable resourcePrefix: Type: String - Default: {{organization-name}} + Default: {{ResourcePrefix}} primaryRegion: Type: String - Default: {{primary-aws-region}} + Default: {{PrimaryAwsRegion}} organizationPrincipalId: Type: String @@ -15,4 +15,4 @@ organizationPrincipalId: allRegions: Type: List Default: - - {{primary-aws-region}} + - {{PrimaryAwsRegion}} diff --git a/src/_tasks.yml b/src/_tasks.yml index cf5fa53..a15c8ca 100644 --- a/src/_tasks.yml +++ b/src/_tasks.yml @@ -1,5 +1,5 @@ Parameters: - <<: !Include './_parameters.yml' + <<: !Include "./_parameters.yml" OrganizationUpdate: Type: update-organization @@ -17,45 +17,48 @@ Types: SeviceControlPolicies: Type: include - DependsOn: [ OrganizationBuild, Types ] + DependsOn: [OrganizationBuild, Types] Path: ./templates/010-scps/_tasks.yml +AlternateContacts: + Type: update-stacks + Skip: true # once you updated the contact information in `./alternate-contacts.yml` you can remove this line + DependsOn: [OrganizationBuild, Types] + Template: ./alternate-contacts.yml + StackName: !Sub '${resourcePrefix}-alternate-contacts' + DefaultOrganizationBindingRegion: us-east-1 + SecureDefaults: Type: include - DependsOn: [ OrganizationBuild, Types ] + DependsOn: [OrganizationBuild, Types] Path: ./templates/020-secure-defaults/_tasks.yml ServiceQuotas: Type: include - DependsOn: [ OrganizationBuild, Types ] + DependsOn: [OrganizationBuild, Types] Path: ./templates/030-service-quotas/_tasks.yml Budgets: Type: include - DependsOn: [ OrganizationBuild ] + DependsOn: [OrganizationBuild] Path: ./templates/040-budgets/_tasks.yml AccountCreation: Type: include - DependsOn: [ OrganizationBuild ] + DependsOn: [OrganizationBuild] Path: ./templates/050-account-creation/_tasks.yml CloudTrail: Type: include - DependsOn: [ OrganizationBuild ] + DependsOn: [OrganizationBuild] Path: ./templates/060-cloud-trail/_tasks.yml GuardDuty: Type: include - DependsOn: [ OrganizationBuild, Types ] + DependsOn: [OrganizationBuild, Types] Path: ./templates/070-guard-duty/_tasks.yml AwsConfigInventory: Type: include - DependsOn: [ OrganizationBuild ] + DependsOn: [OrganizationBuild] Path: ./templates/080-aws-config-inventory/_tasks.yml - -AwsSso: - Type: include - DependsOn: [ OrganizationBuild, Types, SeviceControlPolicies ] - Path: ./templates/100-aws-sso/_tasks.yml diff --git a/src/alternate-contacts.yml b/src/alternate-contacts.yml new file mode 100644 index 0000000..b746a9b --- /dev/null +++ b/src/alternate-contacts.yml @@ -0,0 +1,50 @@ +AWSTemplateFormatVersion: 2010-09-09 + +# alternate contacts can only be set in us-east-1. +DefaultOrganizationBindingRegion: us-east-1 + +Resources: + BillingContact: + Type: 'Community::Account::AlternateContact' + OrganizationBinding: + # This binding assumes all AWS accounts in the org have the same contacts + # You can add different 'AlternateContact' resources in this file and target different accounts + # For more information see: https://github.com/org-formation/org-formation-cli/blob/master/docs/cloudformation-resources.md#organizationbinding-where-to-create-which-resource + IncludeMasterAccount: true + Account: '*' + Properties: + AlternateContactType: BILLING + Title: CFO + Name: Bill + PhoneNumber: +1 123 123 123 111 + EmailAddress: bill@company.com + + OperationsContact: + Type: 'Community::Account::AlternateContact' + OrganizationBinding: + # This binding assumes all AWS accounts in the org have the same contacts + # You can add different 'AlternateContact' resources in this file and target different accounts + # For more information see: https://github.com/org-formation/org-formation-cli/blob/master/docs/cloudformation-resources.md#organizationbinding-where-to-create-which-resource + IncludeMasterAccount: true + Account: '*' + Properties: + AlternateContactType: OPERATIONS + Title: COO + Name: Oliver + PhoneNumber: +1 123 123 123 222 + EmailAddress: oliver@company.com + + SecurityContact: + Type: 'Community::Account::AlternateContact' + OrganizationBinding: + # This binding assumes all AWS accounts in the org have the same contacts + # You can add different 'AlternateContact' resources in this file and target different accounts + # For more information see: https://github.com/org-formation/org-formation-cli/blob/master/docs/cloudformation-resources.md#organizationbinding-where-to-create-which-resource + IncludeMasterAccount: true + Account: '*' + Properties: + AlternateContactType: SECURITY + Title: CISO + Name: Sam + PhoneNumber: +1 123 123 123 333 + EmailAddress: sam@company.com \ No newline at end of file diff --git a/src/organization.yml b/src/organization.yml index 4ff0358..cd7536d 100644 --- a/src/organization.yml +++ b/src/organization.yml @@ -1,19 +1,18 @@ -AWSTemplateFormatVersion: '2010-09-09-OC' +AWSTemplateFormatVersion: "2010-09-09-OC" Description: Default organization blueprint # https://aws.amazon.com/blogs/mt/best-practices-for-organizational-units-with-aws-organizations/ Organization: - ManagementAccount: Type: OC::ORG::MasterAccount Properties: - Alias: '{{organization-name}}-management' - RootEmail: aws+management@{{email-domain}} + Alias: "{{ResourcePrefix}}-management" + RootEmail: "{{management-acct-root-email}}" AccountName: Management Account - AccountId: '{{management-account-id}}' + AccountId: "{{management-account-id}}" Tags: budget-alarm-threshold: 200 - budget-alarm-threshold-email-recipient: me@{{email-domain}} + budget-alarm-threshold-email-recipient: "{{management-acct-root-email}}" OrganizationRoot: Type: OC::ORG::OrganizationRoot @@ -55,29 +54,29 @@ Organization: SecurityAccount: Type: OC::ORG::Account Properties: - Alias: '{{organization-name}}-security' + Alias: "{{ResourcePrefix}}-security" AccountName: Security Account - RootEmail: aws+security@{{email-domain}} + RootEmail: "{{security-acct-root-email}}" Tags: budget-alarm-threshold: 200 - budget-alarm-threshold-email-recipient: security@{{email-domain}} + budget-alarm-threshold-email-recipient: "{{security-acct-root-email}}" LogArchiveAccount: Type: OC::ORG::Account Properties: - Alias: '{{organization-name}}-log-archive' + Alias: "{{ResourcePrefix}}-log-archive" AccountName: Log Archive Account - RootEmail: aws+log-archive@{{email-domain}} + RootEmail: "{{logarchive-acct-root-email}}" Tags: budget-alarm-threshold: 200 - budget-alarm-threshold-email-recipient: log-archive@{{email-domain}} + budget-alarm-threshold-email-recipient: "{{logarchive-acct-root-email}}" OrgBuildAccount: Type: OC::ORG::Account Properties: - Alias: '{{organization-name}}-org-build' + Alias: "{{ResourcePrefix}}-org-build" AccountName: Organization Build Account - RootEmail: aws+org-build@{{email-domain}} + RootEmail: "{{orgbuild-acct-root-email}}" Tags: budget-alarm-threshold: 200 - budget-alarm-threshold-email-recipient: me@{{email-domain}} + budget-alarm-threshold-email-recipient: "{{orgbuild-acct-root-email}}" diff --git a/src/templates/000-org-build/_tasks.yml b/src/templates/000-org-build/_tasks.yml index 8938c44..170ae60 100644 --- a/src/templates/000-org-build/_tasks.yml +++ b/src/templates/000-org-build/_tasks.yml @@ -14,7 +14,7 @@ Parameters: OrganizationFormationRoleMaster: Type: update-stacks Template: ./role.yml - StackName: !Sub '${appName}-role' + StackName: '{{BuildRoleManagementAcctStackName}}' StackDescription: Organization Formation Build Infrastructure (IAM Role used for cross account access by build process) TerminationProtection: true DefaultOrganizationBindingRegion: !Ref primaryRegion diff --git a/src/templates/000-org-build/initial-commit.zip b/src/templates/000-org-build/initial-commit.zip deleted file mode 100644 index e69de29..0000000 diff --git a/src/templates/005-types/_tasks.yml b/src/templates/005-types/_tasks.yml index b3a5744..97600af 100644 --- a/src/templates/005-types/_tasks.yml +++ b/src/templates/005-types/_tasks.yml @@ -64,6 +64,16 @@ EnableAWSServiceRp: IncludeMasterAccount: true Region: us-east-1 # Only compatible to us-east-1 region +AlternateContactsRP: + Type: register-type + SchemaHandlerPackage: s3://community-resource-provider-catalog/community-account-alternatecontact-0.1.0.zip + ResourceType: 'Community::Account::AlternateContact' + MaxConcurrentTasks: 10 + OrganizationBinding: + IncludeMasterAccount: true + Account: '*' + Region: us-east-1 # Only compatible to us-east-1 region + PasswordPolicyRp: Type: register-type ResourceType: "Community::IAM::PasswordPolicy" @@ -112,3 +122,4 @@ SsoAssignmentGroupRp: OrganizationBinding: IncludeMasterAccount: true Region: !Ref primaryRegion + diff --git a/src/templates/010-scps/_tasks.yml b/src/templates/010-scps/_tasks.yml index 4831165..45dceef 100644 --- a/src/templates/010-scps/_tasks.yml +++ b/src/templates/010-scps/_tasks.yml @@ -52,3 +52,13 @@ DenyLargeEc2Instances: OrganizationBindings: TargetBinding: Account: "*" + +DenyEverything: + Type: update-stacks + Template: ./deny-everything.yml + StackName: !Sub "${resourcePrefix}-deny-everything" + DefaultOrganizationBindingRegion: us-east-1 + DefaultOrganizationBinding: + OrganizationBindings: + TargetBinding: + OrganizationalUnit: !Ref SuspendedOu diff --git a/src/templates/010-scps/deny-everything.yml b/src/templates/010-scps/deny-everything.yml new file mode 100644 index 0000000..61b99c4 --- /dev/null +++ b/src/templates/010-scps/deny-everything.yml @@ -0,0 +1,18 @@ +AWSTemplateFormatVersion: "2010-09-09-OC" + +Resources: + Scp: + Type: Community::Organizations::Policy + Properties: + Name: DenyEverything + Description: Denies all permissions + PolicyType: SERVICE_CONTROL_POLICY + TargetIds: + - Fn::EnumTargetAccounts TargetBinding ${account} + PolicyDocument: + Version: "2012-10-17" + Statement: + - Sid: DenyEverything + Effect: Deny + Action: "*" + Resource: "*" \ No newline at end of file diff --git a/src/templates/100-aws-sso/README.md b/src/templates/100-aws-sso/README.md deleted file mode 100644 index 2ce4690..0000000 --- a/src/templates/100-aws-sso/README.md +++ /dev/null @@ -1,20 +0,0 @@ -### Purpose of these templates -The templates in this folder enable [AWS SSO](https://docs.aws.amazon.com/singlesignon/latest/userguide/what-is.html) in the Management account. SSO is used to enable human access to accounts within the organization, both to the console as well as through the cli. - -This is one of the few folders that does not come with '**batteries included**' as it requires you to manually create Groups and Users. By default it comes with four profiles: -1. Auditor: tied to the default policy `arn:aws:iam::aws:policy/SecurityAudit` -2. Developer: tied to the default policy `arn:aws:iam::aws:policy/PowerUserAccess` -3. Supporter: tied to the default policies `arn:aws:iam::aws:policy/PowerUserAccess` and `arn:aws:iam::aws:policy/job-function/ViewOnlyAccess` -4. Administrator: tied to the default policy `arn:aws:iam::aws:policy/AdministratorAccess` - -You will notice that there are more than 4 tasks, that is because for production access we want to limit the session time to 1 hour, while for non-production it can be 12 hours. This can only be done by deploying different permission sets. - -Some notable resources in this folder: - -| Resource | Description | -| - | - | -| Managed policies | An example stack to design your own IAM policies to assign to Groups | -| SSO | Deploys a set of permissions (roles basically) and connects that to groups that you create earlier | - -It is recommended to re-use AWS provided policies where possible because of the maintenance benefit. The AWS environment constantly changes and AWS ensures that the managed policies they provide give access to new AWS Services and new actions within existing services. - diff --git a/src/templates/100-aws-sso/_tasks.yml b/src/templates/100-aws-sso/_tasks.yml deleted file mode 100644 index acc810a..0000000 --- a/src/templates/100-aws-sso/_tasks.yml +++ /dev/null @@ -1,172 +0,0 @@ -Parameters: - <<: !Include '../../_parameters.yml' - - appName: - Type: String - Default: 'sso' - - # AWS SSO instance ARN - instanceArn: - Type: String - Default: '{{sso-instance-arn}}' - - # Principal ID from Identity Provider's group used by administrators - adminGroup: - Type: String - Default: '{{sso-admin-group-id}}' - - # Principal ID from Identity Provider's group used by auditors - auditorGroup: - Type: String - Default: '{{sso-auditor-group-id}}' - - # Principal ID from Identity Provider's group used by developers - developerGroup: - Type: String - Default: '{{sso-developer-group-id}}' - - # Principal ID from Identity Provider's group used by supporters - supporterGroup: - Type: String - Default: '{{sso-supporter-group-id}}' - -SsoAdministrator: - Type: update-stacks - Template: ./aws-sso.yml - StackName: !Sub '${resourcePrefix}-${appName}-admin' - StackDescription: 'Full permission role used by Admin group within whole organization' - TerminationProtection: false - DefaultOrganizationBindingRegion: !Ref primaryRegion - DefaultOrganizationBinding: - IncludeMasterAccount: true - OrganizationBindings: - TargetBinding: - Account: '*' - Parameters: - instanceArn: !Ref instanceArn - principalId: !Ref adminGroup - permissionSetName: 'Administrator' - managedPolicies: [ 'arn:aws:iam::aws:policy/AdministratorAccess' ] - sessionDuration: 'PT1H' - masterAccountId: !Ref ManagementAccount - -SsoAdministratorSupporter: - Type: update-stacks - Template: ./aws-sso.yml - StackName: !Sub '${resourcePrefix}-${appName}-admin-supporter' - StackDescription: 'Full permission role used by Supporter group within Production organizational units' - TerminationProtection: false - DefaultOrganizationBindingRegion: !Ref primaryRegion - DefaultOrganizationBinding: - IncludeMasterAccount: true - OrganizationBindings: - TargetBinding: - OrganizationalUnit: - - !Ref SharedOu - - !Ref ProdOu - Parameters: - instanceArn: !Ref instanceArn - principalId: !Ref supporterGroup - permissionSetArn: !CopyValue [ !Sub '${resourcePrefix}-${appName}-admin-permission-set-arn' ] - -SsoWriter: - Type: update-stacks - Template: ./aws-sso.yml - StackName: !Sub '${resourcePrefix}-${appName}-writer' - StackDescription: 'Read and Write role used by Supporter group within Production organizational units' - TerminationProtection: false - DefaultOrganizationBindingRegion: !Ref primaryRegion - DefaultOrganizationBinding: - IncludeMasterAccount: true - OrganizationBindings: - TargetBinding: - OrganizationalUnit: - - !Ref SharedOu - - !Ref ProdOu - Parameters: - instanceArn: !Ref instanceArn - principalId: !Ref supporterGroup - permissionSetName: 'Writer' - managedPolicies: [ 'arn:aws:iam::aws:policy/PowerUserAccess' ] - sessionDuration: 'PT1H' - -SsoDeveloper: - Type: update-stacks - Template: ./aws-sso.yml - StackName: !Sub '${resourcePrefix}-${appName}-developer' - StackDescription: 'Read and Write role used by Developer group within SDLC organizational units' - TerminationProtection: false - DefaultOrganizationBindingRegion: !Ref primaryRegion - DefaultOrganizationBinding: - IncludeMasterAccount: true - OrganizationBindings: - TargetBinding: - OrganizationalUnit: - - !Ref SdlcOu - Parameters: - instanceArn: !Ref instanceArn - principalId: !Ref developerGroup - permissionSetName: 'Developer' - managedPolicies: [ 'arn:aws:iam::aws:policy/PowerUserAccess' ] - sessionDuration: 'PT12H' - -SsoAuditor: - Type: update-stacks - Template: ./aws-sso.yml - StackName: !Sub '${resourcePrefix}-${appName}-auditor' - StackDescription: 'Audit role used by Auditor group within whole organization' - TerminationProtection: false - DefaultOrganizationBindingRegion: !Ref primaryRegion - DefaultOrganizationBinding: - IncludeMasterAccount: true - OrganizationBindings: - TargetBinding: - Account: '*' - Parameters: - instanceArn: !Ref instanceArn - principalId: !Ref auditorGroup - permissionSetName: 'Auditor' - managedPolicies: [ 'arn:aws:iam::aws:policy/SecurityAudit' ] - sessionDuration: 'PT1H' - -SsoViewer: - Type: update-stacks - Template: ./aws-sso.yml - StackName: !Sub '${resourcePrefix}-${appName}-viewer' - StackDescription: 'Read-only role used by Admin group within whole organization' - TerminationProtection: false - DefaultOrganizationBindingRegion: !Ref primaryRegion - DefaultOrganizationBinding: - IncludeMasterAccount: true - OrganizationBindings: - TargetBinding: - OrganizationalUnit: - - !Ref SharedOu - - !Ref SdlcOu - - !Ref ProdOu - Parameters: - instanceArn: !Ref instanceArn - principalId: !Ref adminGroup - permissionSetName: 'Viewer' - managedPolicies: [ 'arn:aws:iam::aws:policy/job-function/ViewOnlyAccess' ] - sessionDuration: 'PT12H' - masterAccountId: !Ref ManagementAccount - -SsoViewerSupporter: - Type: update-stacks - Template: ./aws-sso.yml - StackName: !Sub '${resourcePrefix}-${appName}-viewer-supporter' - StackDescription: 'Read-only role used by Supporter group within Production organizational units' - TerminationProtection: false - DefaultOrganizationBindingRegion: !Ref primaryRegion - DefaultOrganizationBinding: - IncludeMasterAccount: true - OrganizationBindings: - TargetBinding: - OrganizationalUnit: - - !Ref SharedOu - - !Ref ProdOu - Parameters: - instanceArn: !Ref instanceArn - principalId: !Ref supporterGroup - permissionSetArn: !CopyValue [ !Sub '${resourcePrefix}-${appName}-viewer-permission-set-arn' ] diff --git a/src/templates/100-aws-sso/aws-sso.yml b/src/templates/100-aws-sso/aws-sso.yml deleted file mode 100644 index 0e4c5f3..0000000 --- a/src/templates/100-aws-sso/aws-sso.yml +++ /dev/null @@ -1,76 +0,0 @@ -AWSTemplateFormatVersion: '2010-09-09-OC' - -Parameters: - - instanceArn: - Type: String - - principalId: - Type: String - - permissionSetArn: - Type: String - Default: '' - - permissionSetName: - Type: String - Default: AdministratorAccess - - managedPolicies: - Type: CommaDelimitedList - Default: arn:aws:iam::aws:policy/AdministratorAccess - - sessionDuration: - Type: String - Default: PT1H - - masterAccountId: - Type: String - Default: '' - -Conditions: - - includePermissionSet: !Equals [ '', !Ref permissionSetArn ] - includeMaster: !Not [ !Equals [ '', !Ref masterAccountId ] ] - -Resources: - - PermissionSet: - Type: AWS::SSO::PermissionSet - Condition: includePermissionSet - Properties: - Name: !Ref permissionSetName - Description: !Sub '${permissionSetName} access to AWS organization' - InstanceArn: !Ref instanceArn - ManagedPolicies: !Ref managedPolicies - SessionDuration: !Ref sessionDuration - - AssignmentMaster: - Type: AWS::SSO::Assignment - Condition: includeMaster - Properties: - InstanceArn: !Ref instanceArn - PermissionSetArn: !If [ includePermissionSet, !GetAtt PermissionSet.PermissionSetArn, !Ref permissionSetArn ] - PrincipalId: !Ref principalId - PrincipalType: GROUP - TargetType: AWS_ACCOUNT - TargetId: !Ref masterAccountId - - AssignmentGroup: - Type: Community::SSO::AssignmentGroup - Properties: - InstanceArn: !Ref instanceArn - PermissionSets: - - !If [ includePermissionSet, !GetAtt PermissionSet.PermissionSetArn, !Ref permissionSetArn ] - PrincipalId: !Ref principalId - PrincipalType: GROUP - Targets: - - TargetType: AWS_ACCOUNT - TargetIds: - - Fn::EnumTargetAccounts TargetBinding ${account} - -Outputs: - PermissionSetArn: - Value: !If [ includePermissionSet, !GetAtt PermissionSet.PermissionSetArn, !Ref permissionSetArn ] - Export: - Name: !Sub ${AWS::StackName}-permission-set-arn diff --git a/template-vars.txt b/template-vars.txt deleted file mode 100644 index ab0ab9f..0000000 --- a/template-vars.txt +++ /dev/null @@ -1,9 +0,0 @@ -the following template keys must be replaced - -* {{organization-name}} -* {{email-domain}} -* {{management-account-id}} -* {{build-account-id}} -* {{state-bucket-name}} -* {{state-object}} -* {{primary-aws-region}} \ No newline at end of file diff --git a/template.json b/template.json new file mode 100644 index 0000000..7305a64 --- /dev/null +++ b/template.json @@ -0,0 +1,94 @@ +{ + "organizationFilePath": "/src/organization.yml", + "parameters": [ + { + "name": "ManagementAcctId", + "required": true + }, + { + "name": "BuildRoleManagementAcctStackName", + "required": true + }, + { + "name": "StateBucketName", + "required": true + }, + { + "name": "ResourcePrefix", + "required": true + }, + { + "name": "PrimaryAwsRegion", + "required": true + }, + { + "name": "EmailForBudgetAlarms", + "required": true + } + ], + "templateGenerationSettings": { + "predefinedOUs": [ + { + "logicalName": "ProdOu", + "properties": { + "OrganizationalUnitName": "prod" + } + }, + { + "logicalName": "SdlcOu", + "properties": { + "OrganizationalUnitName": "sdlc" + } + }, + { + "logicalName": "SharedOu", + "properties": { + "OrganizationalUnitName": "shared", + "Accounts": [ + "!Ref OrgBuildAccount", + "!Ref SecurityAccount", + "!Ref LogArchiveAccount" + ] + } + }, + { + "logicalName": "SuspendedOu", + "properties": { + "OrganizationalUnitName": "suspended" + } + } + ], + "predefinedAccounts": [ + { + "logicalName": "SecurityAccount", + "properties": { + "AccountName": "Security Account", + "Tags": { + "budget-alarm-threshold": 200, + "budget-alarm-threshold-email-recipient": "{{EmailForBudgetAlarms}}" + } + } + }, + { + "logicalName": "LogArchiveAccount", + "properties": { + "AccountName": "Log Archive Account", + "Tags": { + "budget-alarm-threshold": 200, + "budget-alarm-threshold-email-recipient": "{{EmailForBudgetAlarms}}" + } + } + }, + { + "logicalName": "OrgBuildAccount", + "properties": { + "AccountName": "Organization Build Account", + "Tags": { + "budget-alarm-threshold": 200, + "budget-alarm-threshold-email-recipient": "{{EmailForBudgetAlarms}}" + } + } + } + ] + } +}