Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 🚀 Created cloudformation-stackset workflow and readme #95

Merged
merged 10 commits into from
Oct 30, 2023
188 changes: 188 additions & 0 deletions .github/workflows/deploy-cloudformation-stackset.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
---
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 <ParameterName>=<ParameterValue> or <ParameterName>="<ParameterValue>,<ParameterValue>".'
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 }} \
--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
...
9 changes: 8 additions & 1 deletion .github/workflows/deploy-cloudformation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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 <ParameterName>=<ParameterValue> or <ParameterName>="<ParameterValue>,<ParameterValue>".'
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
Expand Down Expand Up @@ -107,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 }}
...
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
53 changes: 53 additions & 0 deletions docs/deploy-cloudformation-stackset.md
Original file line number Diff line number Diff line change
@@ -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:
```
2 changes: 1 addition & 1 deletion docs/deploy-cloudformation.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down