From bd4d93b09669b6c9fd6e0c55cbb8d553d292d8db Mon Sep 17 00:00:00 2001 From: Tanveer Date: Mon, 16 Oct 2023 20:20:16 +0530 Subject: [PATCH 1/9] feat: Created cf-stackset workflow with readme --- .../deploy-cloudformation-stackset.yml | 187 ++++++++++++++++++ docs/deploy-cloudformation-stackset.md | 53 +++++ docs/deploy-cloudformation.md | 2 +- 3 files changed, 241 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/deploy-cloudformation-stackset.yml create mode 100644 docs/deploy-cloudformation-stackset.md diff --git a/.github/workflows/deploy-cloudformation-stackset.yml b/.github/workflows/deploy-cloudformation-stackset.yml new file mode 100644 index 00000000..bb9ed790 --- /dev/null +++ b/.github/workflows/deploy-cloudformation-stackset.yml @@ -0,0 +1,187 @@ +name: Cloudformation stack-set & stack-set-instances +on: + workflow_call: + inputs: + aws-region: + description: 'Aws region (in this region stackset enabled)' + required: false + default: 'us-east-2' + type: string + stackset-instance-region: + description: 'Stackset-instance regions where you need cloudformation stacks' + required: false + default: 'us-east-2' + type: string + stack-set-name: + description: 'Stack-set name defined here' + required: true + type: string + template-url: + description: 'Cloudformation template path add here (S3 Object URL)' + required: true + type: string + OrganizationalUnitIds: + description: 'Organization unit ID for deployment in target accounts when service_managed permission added' + required: false + type: string + account-ids: + description: 'account ids for self_managed permission added' + required: false + type: string + parameter-overrides: + description: 'The parameters to override in the stack inputs. You can pass a comma-delimited list or a file URL. The comma-delimited list has each entry formatted as = or =",".' + required: false + type: string + permission-model: + description: 'IAM role permission SERVICE_MANAGED/SELF_MANAGED choose one' + required: false + type: string + auto-deployment-enabled: + description: 'true or false (true when Service_managed policy enable else false for Self_managed)' + required: true + type: string + RetainStacksOnAccountRemoval: + description: 'true or false (true when Service_managed policy enable else false for Self_managed)' + required: true + type: string + administration-role-arn: + description: 'Administrator role arn add here for trust relation on admin and child account' + required: false + type: string + execution-role-name: + description: 'execution-role-name add here for trust relation in child account' + required: false + type: string + secrets: + AWS_ACCESS_KEY_ID: + required: false + description: 'AWS Access Key ID to install AWS CLI.' + AWS_SECRET_ACCESS_KEY: + required: false + description: 'AWS Secret access key to install AWS CLI' + AWS_SESSION_TOKEN: + required: false + description: 'AWS Session Token to install AWS CLI' + AWS_ROLE_TO_ASSUME: + required: false + description: 'AWS Role ARN defined' + GITHUB: + required: false + description: 'GitHub token' + +jobs: + deploy-cf-stackset: + runs-on: ubuntu-latest + steps: + - name: Checkout code from master branch + uses: actions/checkout@v4 + + - name: Configure AWS Credentials + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID}} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }} + aws-session-token: ${{ secrets.AWS_SESSION_TOKEN }} + aws-region: ${{ inputs.aws-region }} + role-to-assume: ${{ secrets.AWS_ROLE_TO_ASSUME }} + + - name: Check if StackSet exists or not-exist then create/update stack-set + id: check-stackset + run: | + set +e + result=$(aws cloudformation describe-stack-set --stack-set-name "${{ inputs.stack-set-name }}" 2>&1) + RC=$? + set -e + if [ "${{ inputs.permission-model }}" = "SERVICE_MANAGED" ]; then + if [ $RC -eq 0 ]; then + echo "StackSet exists, updating..." + aws cloudformation update-stack-set \ + --stack-set-name ${{ inputs.stack-set-name }} \ + --template-url ${{ inputs.template-url }} \ + --parameters ${{ inputs.parameter-overrides }} \ + --capabilities CAPABILITY_NAMED_IAM \ + --permission-model ${{ inputs.permission-model }} \ + --auto-deployment Enabled=${{ inputs.auto-deployment-enabled }},RetainStacksOnAccountRemoval=${{ inputs.RetainStacksOnAccountRemoval }} + elif [ $RC -eq 254 ]; then + if echo "$result" | grep -q "StackSetNotFoundException"; then + echo "StackSet does not exist, creating..." + aws cloudformation create-stack-set \ + --stack-set-name ${{ inputs.stack-set-name }} \ + --template-url ${{ inputs.template-url }} \ + --parameters ${{ inputs.parameter-overrides }} \ + --capabilities CAPABILITY_NAMED_IAM \ + --permission-model ${{ inputs.permission-model }} \ + o--auto-deployment Enabled=${{ inputs.auto-deployment-enabled }},RetainStacksOnAccountRemoval=${{ inputs.RetainStacksOnAccountRemoval }} + else + exit $RC + fi + else + exit $RC + fi + else + if [ $RC -eq 0 ]; then + echo "StackSet exists, updating..." + aws cloudformation update-stack-set \ + --stack-set-name ${{ inputs.stack-set-name }} \ + --template-url ${{ inputs.template-url }} \ + --capabilities CAPABILITY_NAMED_IAM \ + --parameters ${{ inputs.parameter-overrides }} \ + --administration-role-arn ${{ inputs.administration-role-arn }} + elif [ $RC -eq 254 ]; then + if echo "$result" | grep -q "StackSetNotFoundException"; then + echo "StackSet does not exist, creating..." + aws cloudformation create-stack-set \ + --stack-set-name ${{ inputs.stack-set-name }} \ + --template-url ${{ inputs.template-url }} \ + --capabilities CAPABILITY_NAMED_IAM \ + --parameters ${{ inputs.parameter-overrides }} \ + --administration-role-arn ${{ inputs.administration-role-arn }} \ + --execution-role-name AWSControlTowerExecution + else + exit $RC + fi + else + exit $RC + fi + sleep 50s + fi + + - name: Create or Update StackSet-instance + run: | + stack_instance_list=$(aws cloudformation list-stack-instances --region ${{ inputs.stackset-instance-region }} --stack-set-name ${{ inputs.stack-set-name }}) + + if [ "${{ inputs.permission-model }}" == "SERVICE_MANAGED" ]; then + if [[ "$stack_instance_list" == *'"Summaries": []'* ]]; then + echo "StackSet-instance, creating..." + aws cloudformation create-stack-instances \ + --stack-set-name ${{ inputs.stack-set-name }} \ + --deployment-targets OrganizationalUnitIds='["${{ inputs.OrganizationalUnitIds }}"]' \ + --parameter-overrides ${{ inputs.parameter-overrides }} \ + --regions ${{ inputs.stackset-instance-region }} + else + echo "StackSet-instance, updating..." + aws cloudformation update-stack-instances \ + --stack-set-name ${{ inputs.stack-set-name }} \ + --deployment-targets OrganizationalUnitIds='["${{ inputs.OrganizationalUnitIds }}"]' \ + --parameter-overrides ${{ inputs.parameter-overrides }} \ + --regions ${{ inputs.stackset-instance-region }} + fi + else + if [[ "$stack_instance_list" == *'"Summaries": []'* ]]; then + echo "StackSet-instance, creating..." + aws cloudformation create-stack-instances \ + --stack-set-name ${{ inputs.stack-set-name }} \ + --parameter-overrides ${{ inputs.parameter-overrides }} \ + --accounts ${{ inputs.account-ids }} \ + --regions ${{ inputs.stackset-instance-region }} \ + --operation-preferences FailureToleranceCount=1,MaxConcurrentCount=2 + else + echo "StackSet-instance, updating..." + aws cloudformation update-stack-instances \ + --stack-set-name ${{ inputs.stack-set-name }} \ + --parameter-overrides ${{ inputs.parameter-overrides }} \ + --accounts ${{ inputs.account-ids }} \ + --regions ${{ inputs.stackset-instance-region }} \ + --operation-preferences MaxConcurrentPercentage=1 + fi + fi \ No newline at end of file diff --git a/docs/deploy-cloudformation-stackset.md b/docs/deploy-cloudformation-stackset.md new file mode 100644 index 00000000..2ab61067 --- /dev/null +++ b/docs/deploy-cloudformation-stackset.md @@ -0,0 +1,53 @@ +## [Deploy Cloudformation Stacket & Stackset-instances](https://github.com/clouddrove/github-shared-workflows/blob/master/.github/workflows/deploy-cloudformation-stackset.yml) + The process starts with the creation of a shared workflow template. This template contains CloudFormation resource definitions, parameter declarations, and other configuration settings that are commonly used across multiple projects or environments. It serves as a blueprint for the infrastructure you want to create. `.github/workflows/deploy-cloudformation-stackset.yml` + +#### Usage + +- In this workflow we added multiple parameters like S3 bucket for source code, stack-parameters, account-ids, stackset-name using parameters we overrides from called.yml as we defined below. +- In this workflow we provide S3 Object URL where your code & template file located and deploy stackset and stackset-instances +- Most important thing is we centrally manage stacks of every account's using stackset + +#### Key Points: + In this workflow we added steps like for the below conditions: + + - If stackset are not-Exists then Create a new **stackset** + - If stackset are Exist then Updating a **stackset** + - If stackset-instance is not-Exist then Create a new **stackset-instance** + - If stackset-instance is Exist then Updating a **stackset-instance** + +#### Example + +```yaml +name: Cloudformation stack-set +on: + push: + branches: main + workflow_dispatch: + +permissions: + id-token: write + contents: read + +jobs: + deploy-cf-stackset: + uses: clouddrove/github-shared-workflows/.github/workflows/deploy-cloudformation-stackset.yml@master + with: + aws-region: # aws-configure region add, where you need stackset + stackset-instance-region: # region add where you need stacks + stack-set-name: # name of stack-set ( same name apply for stackset & instances ) + template-url: # S3 bucket Object URL add where template file is located + OrganizationalUnitIds: "" # deployment targets OrganizationalUnitIds + account-ids: # deployment targets add master account ids where you deploying stacksets + parameter-overrides: # use this format (ParameterKey=ABC,ParameterValue=XXX ParameterKey=XYZ,ParameterValue=XXX) + permission-model: # SELF_MANAGED & SERVICE_MANAGED add here + auto-deployment-enabled: false # for SELF_MANAGED-false & SERVICE_MANAGED-true + RetainStacksOnAccountRemoval: false # for SELF_MANAGED-false & SERVICE_MANAGED-true + administration-role-arn: # administration AWSControlTowerStackSetRole ARN add here + execution-role-name: # child account AWSControlTowerExecution role name add here + + secrets: + AWS_ROLE_TO_ASSUME: # Add AWS OIDC role ARN + AWS_ACCESS_KEY_ID: # Add AWS credentials + AWS_SECRET_ACCESS_KEY: + AWS_SESSION_TOKEN: +``` \ No newline at end of file diff --git a/docs/deploy-cloudformation.md b/docs/deploy-cloudformation.md index de6777e0..ccb5cfc7 100644 --- a/docs/deploy-cloudformation.md +++ b/docs/deploy-cloudformation.md @@ -1,4 +1,4 @@ -## [Deploy Cloudformation Stack](https://github.com/clouddrove/github-shared-workflows/blob/master/.github/workflows/infracost.yml) +## [Deploy Cloudformation Stack](https://github.com/clouddrove/github-shared-workflows/blob/master/.github/workflows/deploy-cloudformation.yml) The process starts with the creation of a shared workflow template. This template contains CloudFormation resource definitions, parameter declarations, and other configuration settings that are commonly used across multiple projects or environments. It serves as a blueprint for the infrastructure you want to create. `.github/workflows/deploy-cloudformation.yml` #### Usage From 24788cff923bb3a39e233b0ad3f2b6a910d3e321 Mon Sep 17 00:00:00 2001 From: Tanveer Date: Mon, 16 Oct 2023 20:22:33 +0530 Subject: [PATCH 2/9] feat: Created cf-stackset workflow with readme --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 92d69214..b1034190 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,8 @@ Above example is just a simple example to call workflow from github shared workf 7. [Checkov Workflow](https://github.com/clouddrove/github-shared-workflows/blob/master/docs/checkov.md) 8. [Terraform Workflow](https://github.com/clouddrove/github-shared-workflows/blob/master/docs/terraform_workflow.md) 9. [Infracost workflow](https://github.com/clouddrove/github-shared-workflows/blob/master/docs/infracost.md) -10. [ Deploy Cloudformation workflow](https://github.com/clouddrove/github-shared-workflows/blob/master/docs/deploy-cloudformation.md) +10. [ Deploy Cloudformation Stack workflow](https://github.com/clouddrove/github-shared-workflows/blob/master/docs/deploy-cloudformation.md) +11. [ Deploy Cloudformation Stackset workflow](https://github.com/clouddrove/github-shared-workflows/blob/master/docs/deploy-cloudformation-stackset.md) ## Feedback If you come accross a bug or have any feedback, please log it in our [issue tracker](https://github.com/clouddrove/github-shared-workflows/issues), or feel free to drop us an email at [hello@clouddrove.com](mailto:hello@clouddrove.com). From 5973a1d84dd138a35c8d65f4ca5f607b40fd10b7 Mon Sep 17 00:00:00 2001 From: Tanveer Date: Mon, 16 Oct 2023 20:50:47 +0530 Subject: [PATCH 3/9] feat: set as per yamllint rules --- .../workflows/deploy-cloudformation-stackset.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/workflows/deploy-cloudformation-stackset.yml b/.github/workflows/deploy-cloudformation-stackset.yml index bb9ed790..63d1cbe5 100644 --- a/.github/workflows/deploy-cloudformation-stackset.yml +++ b/.github/workflows/deploy-cloudformation-stackset.yml @@ -1,4 +1,5 @@ -name: Cloudformation stack-set & stack-set-instances +--- +name: Cloudformation stack-set & stack-set-instances on: workflow_call: inputs: @@ -75,7 +76,7 @@ jobs: steps: - name: Checkout code from master branch uses: actions/checkout@v4 - + - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v4 with: @@ -149,7 +150,6 @@ jobs: - name: Create or Update StackSet-instance run: | stack_instance_list=$(aws cloudformation list-stack-instances --region ${{ inputs.stackset-instance-region }} --stack-set-name ${{ inputs.stack-set-name }}) - if [ "${{ inputs.permission-model }}" == "SERVICE_MANAGED" ]; then if [[ "$stack_instance_list" == *'"Summaries": []'* ]]; then echo "StackSet-instance, creating..." @@ -174,7 +174,7 @@ jobs: --parameter-overrides ${{ inputs.parameter-overrides }} \ --accounts ${{ inputs.account-ids }} \ --regions ${{ inputs.stackset-instance-region }} \ - --operation-preferences FailureToleranceCount=1,MaxConcurrentCount=2 + --operation-preferences FailureToleranceCount=1,MaxConcurrentCount=2 else echo "StackSet-instance, updating..." aws cloudformation update-stack-instances \ @@ -182,6 +182,7 @@ jobs: --parameter-overrides ${{ inputs.parameter-overrides }} \ --accounts ${{ inputs.account-ids }} \ --regions ${{ inputs.stackset-instance-region }} \ - --operation-preferences MaxConcurrentPercentage=1 + --operation-preferences MaxConcurrentPercentage=1 fi - fi \ No newline at end of file + fi +... \ No newline at end of file From 804fc7061ae17b475ccc5fac02cee0ed15bcf4bc Mon Sep 17 00:00:00 2001 From: Tanveer Date: Mon, 16 Oct 2023 20:53:36 +0530 Subject: [PATCH 4/9] feat: set as per yamllint rules --- .github/workflows/deploy-cloudformation-stackset.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-cloudformation-stackset.yml b/.github/workflows/deploy-cloudformation-stackset.yml index 63d1cbe5..cf388606 100644 --- a/.github/workflows/deploy-cloudformation-stackset.yml +++ b/.github/workflows/deploy-cloudformation-stackset.yml @@ -174,7 +174,7 @@ jobs: --parameter-overrides ${{ inputs.parameter-overrides }} \ --accounts ${{ inputs.account-ids }} \ --regions ${{ inputs.stackset-instance-region }} \ - --operation-preferences FailureToleranceCount=1,MaxConcurrentCount=2 + --operation-preferences FailureToleranceCount=1,MaxConcurrentCount=2 else echo "StackSet-instance, updating..." aws cloudformation update-stack-instances \ From 16a15d5f4ef01fc4c83bece60644240104e4cb31 Mon Sep 17 00:00:00 2001 From: Tanveer Date: Mon, 16 Oct 2023 20:56:48 +0530 Subject: [PATCH 5/9] feat: set as per yamllint rules --- .github/workflows/deploy-cloudformation-stackset.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/deploy-cloudformation-stackset.yml b/.github/workflows/deploy-cloudformation-stackset.yml index cf388606..7930b8ed 100644 --- a/.github/workflows/deploy-cloudformation-stackset.yml +++ b/.github/workflows/deploy-cloudformation-stackset.yml @@ -185,4 +185,5 @@ jobs: --operation-preferences MaxConcurrentPercentage=1 fi fi + ... \ No newline at end of file From 916cd7b65f4e4064a464e0b811f382f990552d9d Mon Sep 17 00:00:00 2001 From: Tanveer Date: Mon, 16 Oct 2023 20:59:52 +0530 Subject: [PATCH 6/9] feat: set as per yamllint rules --- .github/workflows/deploy-cloudformation-stackset.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/deploy-cloudformation-stackset.yml b/.github/workflows/deploy-cloudformation-stackset.yml index 7930b8ed..919eda94 100644 --- a/.github/workflows/deploy-cloudformation-stackset.yml +++ b/.github/workflows/deploy-cloudformation-stackset.yml @@ -185,5 +185,4 @@ jobs: --operation-preferences MaxConcurrentPercentage=1 fi fi - -... \ No newline at end of file +... From 258325013993f9bb869ef5744f9dfacc6337a7a6 Mon Sep 17 00:00:00 2001 From: Tanveer143s <116706588+Tanveer143s@users.noreply.github.com> Date: Tue, 17 Oct 2023 18:28:01 +0530 Subject: [PATCH 7/9] feat: capabilities_IAM name are added in inputs --- .github/workflows/deploy-cloudformation.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/deploy-cloudformation.yml b/.github/workflows/deploy-cloudformation.yml index 07077d6d..f46bc305 100644 --- a/.github/workflows/deploy-cloudformation.yml +++ b/.github/workflows/deploy-cloudformation.yml @@ -52,6 +52,12 @@ on: description: 'The parameters to override in the stack inputs. You can pass a comma-delimited list or a file URL. The comma-delimited list has each entry formatted as = or =",".' required: false type: string + capabilities: + description: "The comma-delimited list of stack template capabilities to acknowledge. Defaults to 'CAPABILITY_IAM'" + required: false + default: "CAPABILITY_IAM" + type: string + secrets: AWS_ACCESS_KEY_ID: required: false From c87fa47348daec350881500a1632ea84980f2269 Mon Sep 17 00:00:00 2001 From: Tanveer143s <116706588+Tanveer143s@users.noreply.github.com> Date: Tue, 17 Oct 2023 18:34:29 +0530 Subject: [PATCH 8/9] feat: capabilities added in deploy stage --- .github/workflows/deploy-cloudformation.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy-cloudformation.yml b/.github/workflows/deploy-cloudformation.yml index f46bc305..b232bb55 100644 --- a/.github/workflows/deploy-cloudformation.yml +++ b/.github/workflows/deploy-cloudformation.yml @@ -113,5 +113,6 @@ jobs: name: ${{ inputs.stack-name }} template: ${{ inputs.template-path }} no-fail-on-empty-changeset: "1" - parameter-overrides: ${{ inputs.parameter-overrides}} + parameter-overrides: ${{ inputs.parameter-overrides }} + capabilities: ${{ inputs.capabilities }} ... From ad3a912601522aaa322315ea231510d182e7ff4e Mon Sep 17 00:00:00 2001 From: Tanveer143s <116706588+Tanveer143s@users.noreply.github.com> Date: Tue, 24 Oct 2023 19:50:05 +0530 Subject: [PATCH 9/9] feat: remove unnecessary sign --- .github/workflows/deploy-cloudformation-stackset.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy-cloudformation-stackset.yml b/.github/workflows/deploy-cloudformation-stackset.yml index 919eda94..2adfcb32 100644 --- a/.github/workflows/deploy-cloudformation-stackset.yml +++ b/.github/workflows/deploy-cloudformation-stackset.yml @@ -112,7 +112,7 @@ jobs: --parameters ${{ inputs.parameter-overrides }} \ --capabilities CAPABILITY_NAMED_IAM \ --permission-model ${{ inputs.permission-model }} \ - o--auto-deployment Enabled=${{ inputs.auto-deployment-enabled }},RetainStacksOnAccountRemoval=${{ inputs.RetainStacksOnAccountRemoval }} + --auto-deployment Enabled=${{ inputs.auto-deployment-enabled }},RetainStacksOnAccountRemoval=${{ inputs.RetainStacksOnAccountRemoval }} else exit $RC fi