Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 44 additions & 26 deletions .github/workflows/aws-cdk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@ on:
workflow_call:
inputs:
# Core Configuration
stack-name:
description: "CDK stack name (overrides STACK_NAME variable if provided)"
type: string
required: false
default: ""
aws-region:
description: "AWS region for deployment"
type: string
Expand All @@ -14,7 +19,7 @@ on:
type: string
required: false
default: ""

# Deployment Control
bootstrap:
description: "Bootstrap CDK environment before deployment"
Expand All @@ -36,7 +41,7 @@ on:
type: boolean
required: false
default: false

# Advanced Configuration
context-values:
description: "CDK context values as JSON object"
Expand Down Expand Up @@ -174,6 +179,7 @@ jobs:
environment: ${{ inputs.github-environment }}
outputs:
context-args: ${{ steps.context-config.outputs.args }}
stack-name: ${{ steps.resolve-stack-name.outputs.stack-name }}
sanitised-cdk-stack-name: ${{ steps.sanitise.outputs.sanitised-cdk-stack-name }}
cdk-bootstrap-cmd: ${{ steps.parse-cdk-config.outputs.bootstrap-cmd }}
cdk-synth-cmd: ${{ steps.parse-cdk-config.outputs.synth-cmd }}
Expand All @@ -197,12 +203,23 @@ jobs:
echo "diff-cmd=${{ inputs.diff-command }}" >> $GITHUB_OUTPUT
echo "deploy-cmd=${{ inputs.deploy-command }}" >> $GITHUB_OUTPUT

- name: Resolve stack name
id: resolve-stack-name
run: |
# Input takes priority over variable
if [ -n "${{ inputs.stack-name }}" ]; then
STACK_NAME="${{ inputs.stack-name }}"
else
STACK_NAME="${{ vars.STACK_NAME }}"
fi
echo "stack-name=$STACK_NAME" >> $GITHUB_OUTPUT

- name: Validate required inputs
run: |
echo "🔍 Validating deployment configuration..."

ENVIRONMENT="${{ inputs.github-environment }}"
STACK_NAME="${{ vars.STACK_NAME }}"
STACK_NAME="${{ steps.resolve-stack-name.outputs.stack-name }}"
AWS_ACCESS_KEY_ID="${{ vars.AWS_ACCESS_KEY_ID }}"
AWS_SECRET_ACCESS_KEY="${{ secrets.AWS_SECRET_ACCESS_KEY }}"

Expand All @@ -213,11 +230,11 @@ jobs:
echo "ℹ️ Using variables from $ENVIRONMENT environment"

if [ -z "$STACK_NAME" ]; then
echo "❌ Error: STACK_NAME is not defined as a variable in your $ENVIRONMENT environment"
echo "❌ Error: STACK_NAME must be provided via input or as a variable in your $ENVIRONMENT environment"
exit 1
fi

echo "ℹ️ STACK_NAME set from variable: $STACK_NAME"
echo " STACK_NAME: $STACK_NAME"

if [ -z "$AWS_ACCESS_KEY_ID" ]; then
echo "❌ Error: AWS_ACCESS_KEY_ID is not defined as a variable in your $ENVIRONMENT environment"
Expand Down Expand Up @@ -246,7 +263,7 @@ jobs:
exit 1
;;
esac

# Validate context JSON if provided
if [ "${{ inputs.context-values }}" != "{}" ]; then
echo '${{ inputs.context-values }}' | jq . > /dev/null
Expand All @@ -261,7 +278,7 @@ jobs:
echo "❌ Error: At least one of synth, diff, or deploy must be true"
exit 1
fi

echo "✅ All inputs validated successfully"

- name: Configure CDK context
Expand All @@ -287,7 +304,7 @@ jobs:
- name: Sanitise stack name
id: sanitise
run: |
sanitised_cdk_stack_name=$(echo "${{ vars.STACK_NAME }}" | tr -cd '[:alnum:]-_')
sanitised_cdk_stack_name=$(echo "${{ steps.resolve-stack-name.outputs.stack-name }}" | tr -cd '[:alnum:]-_')
echo "sanitised-cdk-stack-name=$sanitised_cdk_stack_name" >> $GITHUB_OUTPUT

# Bootstrap CDK environment if required
Expand Down Expand Up @@ -469,7 +486,7 @@ jobs:

SYNTH_CMD="${{ needs.prepare.outputs.cdk-synth-cmd }}"

$SYNTH_CMD ${{ vars.STACK_NAME }} \
$SYNTH_CMD ${{ needs.prepare.outputs.stack-name }} \
${{ needs.prepare.outputs.context-args }} \
${{ inputs.extra-arguments }} \
$verbose
Expand Down Expand Up @@ -567,11 +584,11 @@ jobs:

DIFF_CMD="${{ needs.prepare.outputs.cdk-diff-cmd }}"

diff_output=$($DIFF_CMD ${{ vars.STACK_NAME }} \
diff_output=$($DIFF_CMD ${{ needs.prepare.outputs.stack-name }} \
${{ needs.prepare.outputs.context-args }} \
${{ inputs.extra-arguments }} \
--no-color 2>&1 || true)

# Save diff to file for analysis
echo "$diff_output" > deployment-diff.txt

Expand Down Expand Up @@ -603,7 +620,7 @@ jobs:

# Deploy CDK stack
deploy:
name: '🚀 CDK Deploy'
name: "🚀 CDK Deploy"
runs-on: ubuntu-latest
needs: [setup-node, prepare, synth, diff]
if: |
Expand Down Expand Up @@ -687,11 +704,11 @@ jobs:
verbose="--verbose"
fi

echo "🚀 Deploying CDK stack: ${{ vars.STACK_NAME }}"
echo "🚀 Deploying CDK stack: ${{ needs.prepare.outputs.stack-name }}"

DEPLOY_CMD="${{ needs.prepare.outputs.cdk-deploy-cmd }}"

$DEPLOY_CMD ${{ vars.STACK_NAME }} \
$DEPLOY_CMD ${{ needs.prepare.outputs.stack-name }} \
${{ needs.prepare.outputs.context-args }} \
${{ inputs.extra-arguments }} \
--require-approval never \
Expand Down Expand Up @@ -786,12 +803,12 @@ jobs:

# Check stack status
stack_status=$(aws cloudformation describe-stacks \
--stack-name ${{ vars.STACK_NAME }} \
--stack-name ${{ needs.prepare.outputs.stack-name }} \
--query 'Stacks[0].StackStatus' \
--output text)

echo "Stack status: $stack_status"

if [[ "$stack_status" =~ ^(CREATE_COMPLETE|UPDATE_COMPLETE)$ ]]; then
echo "✅ Stack deployment validated successfully"
else
Expand All @@ -805,24 +822,24 @@ jobs:

# Initiate drift detection
drift_id=$(aws cloudformation detect-stack-drift \
--stack-name ${{ vars.STACK_NAME }} \
--stack-name ${{ needs.prepare.outputs.stack-name }} \
--query 'StackDriftDetectionId' \
--output text)

echo "Drift detection initiated: $drift_id"

# Wait for drift detection to complete
aws cloudformation wait stack-drift-detection-complete \
--stack-drift-detection-id $drift_id

# Get drift detection results
drift_status=$(aws cloudformation describe-stack-drift-detection-status \
--stack-drift-detection-id $drift_id \
--query 'StackDriftStatus' \
--output text)

echo "Stack drift status: $drift_status"

case $drift_status in
"IN_SYNC")
echo "✅ No infrastructure drift detected"
Expand All @@ -839,12 +856,13 @@ jobs:
run: |
echo "📋 Deployment Summary"
echo "===================="
echo "Stack Name: ${{ vars.STACK_NAME }}"
echo "Stack Name: ${{ needs.prepare.outputs.stack-name }}"
echo "Environment: ${{ inputs.github-environment }}"
echo "Region: ${{ inputs.aws-region }}"
echo "Status: ${{ needs.deploy.outputs.deployment-status }}"
echo "Node Version: ${{ needs.setup-node.outputs.node-version }}"
echo "Package Manager: ${{ needs.setup-node.outputs.package-manager }}"

echo ""
echo "🎉 Deployment completed successfully!"
echo "🎉 Deployment completed successfully!"

3 changes: 2 additions & 1 deletion docs/aws-cdk.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ A streamlined AWS CDK workflow supporting multi-environment infrastructure synth
| Name | Required | Type | Default | Description |
|------|----------|------|---------|-------------|
| **Core Configuration** |
| stack-name | ❌ | string | | CDK stack name (overrides `STACK_NAME` variable if provided) |
| aws-region | ❌ | string | ap-southeast-2 | AWS region for deployment |
| github-environment | ❌ | string | Repository| GitHub Environment name for secrets/variables (e.g., Staging, Production) |
| **Deployment Control** |
Expand All @@ -42,7 +43,7 @@ These should be configured in your GitHub Environment (or at the repository leve

| Name | Required | Type | Description |
|------|----------|------|-------------|
| `STACK_NAME` | | Variable | The name of the CloudFormation stack to deploy |
| `STACK_NAME` | | Variable | The name of the CloudFormation stack to deploy (required unless `stack-name` input is provided) |
| `AWS_ACCESS_KEY_ID` | ✅ | Variable | AWS Access Key ID for authentication |
| `AWS_SECRET_ACCESS_KEY` | ✅ | Secret | AWS Secret Access Key for authentication |
| `CFN_EXECUTION_ROLE` | ❌ | Secret | CloudFormation execution role ARN (optional, for cross-account deployments) |
Expand Down