Skip to content

CDD-2442 Set Up CI/CD Workflow for auth-dev and auth-test Environments #2811

CDD-2442 Set Up CI/CD Workflow for auth-dev and auth-test Environments

CDD-2442 Set Up CI/CD Workflow for auth-dev and auth-test Environments #2811

Workflow file for this run

name: Pull Request Workflow
on:
pull_request:
branches:
- "*"
env:
AWS_REGION: "eu-west-2"
permissions:
id-token: write
contents: read
jobs:
build_base:
name: Build base env
runs-on: ubuntu-latest
outputs:
public_env: ${{ steps.set_env.outputs.PUBLIC_ENV }}
auth_env: ${{ steps.set_env.outputs.AUTH_ENV }}
steps:
- name: Checkout Repository (Main)
uses: actions/checkout@v4
with:
ref: main
fetch-depth: 0
persist-credentials: true
- uses: ./.github/actions/setup-terraform
- uses: ./.github/actions/setup-zsh
- uses: ./.github/actions/short-sha
- name: Export SHORT_SHA Globally
run: |
echo "SHORT_SHA=${{ env.SHORT_SHA }}" >> $GITHUB_ENV
echo "SHORT_SHA=${{ env.SHORT_SHA }}" >> $GITHUB_OUTPUT
- name: Verify Short SHA
run: |
if [[ -z "${{ env.SHORT_SHA }}" ]]; then
echo "Error: SHORT_SHA is not set!"
exit 1
fi
echo "SHORT_SHA=${{ env.SHORT_SHA }}"
- name: Apply Terraform for Main Branch
run: |
source uhd.sh || { echo "ERROR: uhd.sh not found!"; exit 1; }
uhd terraform init:layer "20-app"
uhd terraform apply:layer "20-app" "ci-${{ github.event.pull_request.number }}-$SHORT_SHA"
shell: zsh {0}
- name: Checkout Feature Branch
run: |
git fetch origin ${{ github.head_ref }} --depth=1 || echo "No remote branch found"
git checkout -B ${{ github.head_ref }} origin/${{ github.head_ref }} || echo "Branch exists only locally"
- name: Debug Repository Files
run: |
echo "Current Directory: $(pwd)"
ls -la .github/actions
ls -la .github/actions/mask-secrets || echo "mask-secrets directory is missing!"
- name: Mask Secrets
uses: ./.github/actions/mask-secrets
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
terraform_iam_role: ${{ secrets.UHD_TERRAFORM_IAM_ROLE }}
terraform_role_public: ${{ secrets.UHD_TERRAFORM_ROLE_PUBLIC }}
terraform_role_auth: ${{ secrets.UHD_TERRAFORM_ROLE_AUTH }}
terraform_role_test: ${{ secrets.UHD_TERRAFORM_ROLE_TEST }}
terraform_role_auth_test: ${{ secrets.UHD_TERRAFORM_ROLE_AUTH_TEST }}
- name: Ensure UHD Script Exists
run: |
if [[ ! -f uhd.sh ]]; then
echo "uhd.sh script missing. Exiting."
exit 1
fi
chmod +x uhd.sh
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.UHD_TERRAFORM_IAM_ROLE }}
aws-region: ${{ env.AWS_REGION }}
- name: Validate AWS Assume Role Permissions
run: |
aws sts get-caller-identity || { echo "ERROR: AWS credentials invalid!"; exit 1; }
- name: Apply Terraform for Feature Branch
run: |
source uhd.sh
uhd terraform apply:layer 20-app ci-${{ github.event.pull_request.number }}-$SHORT_SHA
shell: zsh {0}
- name: Determine Environment
id: set_env
run: |
echo "PUBLIC_ENV=ci-${{ github.event.pull_request.number }}-$SHORT_SHA" >> $GITHUB_ENV
echo "AUTH_ENV=ci-a-${{ github.event.pull_request.number }}-$SHORT_SHA" >> $GITHUB_ENV
echo "PUBLIC_ENV=ci-${{ github.event.pull_request.number }}-$SHORT_SHA" >> $GITHUB_OUTPUT
echo "AUTH_ENV=ci-a-${{ github.event.pull_request.number }}-$SHORT_SHA" >> $GITHUB_OUTPUT
fast_forward_env_branches:
name: Fast forward env branches
runs-on: ubuntu-latest
permissions:
contents: write
outputs:
deploy_auth_dev: ${{ steps.fast_forward_merge.outputs.deploy_auth_dev }}
deploy_auth_test: ${{ steps.fast_forward_merge.outputs.deploy_auth_test }}
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ github.base_ref }}
fetch-depth: 1
sparse-checkout-cone-mode: true
fetch-tags: false
show-progress: true
lfs: false
submodules: false
set-safe-directory: true
- uses: ./.github/actions/setup-zsh
- name: Fast forward env branches
id: fast_forward_merge
run: |
echo "Starting fast-forward merge..."
if scripts/fast-forward-env-branches.sh; then
echo "Fast forward merge completed successfully."
else
echo "Fast forward merge failed!" >&2
exit 1
fi
shell: zsh {0}
env:
CI: "true"
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
deploy_auth_dev:
needs: fast_forward_env_branches
if: ${{ needs.fast_forward_env_branches.outputs.deploy_auth_dev == 'true' }}
uses: ./.github/workflows/well-known-environment.yml
with:
branch: env/auth-dev/auth-dev
secrets: inherit
deploy_auth_test:
needs: fast_forward_env_branches
if: ${{ needs.fast_forward_env_branches.outputs.deploy_auth_test == 'true' }}
uses: ./.github/workflows/well-known-environment.yml
with:
branch: env/auth-test/auth-test
secrets: inherit
unit_test_functions:
needs: [ "build_base" ]
strategy:
fail-fast: false
matrix:
function: [ lambda-producer-handler, lambda-db-password-rotation, lambda-alarm-notification, legacy-dashboard-redirect-viewer-request, public-api-cloud-front-viewer-request ]
name: Unit test functions
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
pull-requests: write
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ github.base_ref }}
fetch-depth: 1
sparse-checkout-cone-mode: true
fetch-tags: false
show-progress: true
lfs: false
submodules: false
set-safe-directory: true
- name: Test ${{ matrix.function }}
uses: ./.github/actions/npm-test
with:
function-name: ${{ matrix.function }}
unit_test_report:
name: Unit test coverage report
runs-on: ubuntu-latest
needs: [ "unit_test_functions" ]
permissions:
contents: read
id-token: write
pull-requests: write
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ github.base_ref }}
fetch-depth: 1
sparse-checkout-cone-mode: true
fetch-tags: false
show-progress: true
lfs: false
submodules: false
set-safe-directory: true
- name: Download test coverage
uses: actions/download-artifact@v4.1.8
with:
path: ./reports
- name: Unit test report
uses: ukhsa-internal/jest-coverage-comment-action@v1
with:
multiple-files: |
lambda-producer-handler, ./reports/lambda-producer-handler-coverage-summary/coverage-summary.json
lambda-db-password-rotation, ./reports/lambda-db-password-rotation-coverage-summary/coverage-summary.json
lambda-alarm-notification, ./reports/lambda-alarm-notification-coverage-summary/coverage-summary.json
legacy-dashboard-redirect-viewer-request, ./reports/legacy-dashboard-redirect-viewer-request-coverage-summary/coverage-summary.json
public-api-cloud-front-viewer-request, ./reports/public-api-cloud-front-viewer-request-coverage-summary/coverage-summary.json
multiple-junitxml-files: |
lambda-producer-handler, ./reports/lambda-producer-handler-coverage-report/junit.xml
lambda-db-password-rotation, ./reports/lambda-db-password-rotation-coverage-report/junit.xml
lambda-alarm-notification, ./reports/lambda-alarm-notification-coverage-report/junit.xml
legacy-dashboard-redirect-viewer-request, ./reports/legacy-dashboard-redirect-viewer-request-coverage-report/junit.xml
public-api-cloud-front-viewer-request, ./reports/public-api-cloud-front-viewer-request-coverage-report/junit.xml
title: unit test coverage report
terraform_plan:
name: Terraform plan (${{ matrix.environment }})
runs-on: ubuntu-latest
needs: [ "build_base" ]
strategy:
fail-fast: false
matrix:
environment: [ public, auth ]
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ github.base_ref }}
fetch-depth: 1
sparse-checkout-cone-mode: true
fetch-tags: false
show-progress: true
lfs: false
submodules: false
set-safe-directory: true
- name: Set TARGET_ENV
run: |
if [[ "${{ matrix.environment }}" == "public" ]]; then
echo "TARGET_ENV=${{ needs.build_base.outputs.public_env }}" >> $GITHUB_ENV
echo "TERRAFORM_ROLE=${{ secrets.UHD_TERRAFORM_ROLE_PUBLIC }}" >> $GITHUB_ENV
else
echo "TARGET_ENV=${{ needs.build_base.outputs.auth_env }}" >> $GITHUB_ENV
echo "TERRAFORM_ROLE=${{ secrets.UHD_TERRAFORM_ROLE_AUTH }}" >> $GITHUB_ENV
fi
- name: Terraform plan (${{ matrix.environment }})
run: |
source uhd.sh
uhd terraform init
uhd terraform plan:layer 10-account $TARGET_ENV -refresh=true
uhd terraform plan:layer 20-app $TARGET_ENV -refresh=true
shell: zsh {0}
- name: Terraform Plan Summary
run: |
echo "### Terraform Plan Summary ###"
PLAN_FILE="terraform-plan-${{ matrix.environment }}.json"
terraform show -json tfplan-${{ matrix.environment }}.out | jq '.' > "$PLAN_FILE" || { echo "⚠ No Terraform output found!"; exit 0; }
if [[ ! -s "$PLAN_FILE" ]]; then
echo "⚠ No changes detected in Terraform plan. Skipping summary."
exit 0
fi
ADDED_RESOURCES=$(jq '[.resource_changes[] | select(.change.actions | contains(["create"]))] | length' "$PLAN_FILE")
CHANGED_RESOURCES=$(jq '[.resource_changes[] | select(.change.actions | contains(["update"]))] | length' "$PLAN_FILE")
DELETED_RESOURCES=$(jq '[.resource_changes[] | select(.change.actions | contains(["delete"]))] | length' "$PLAN_FILE")
echo "Resources to be added: $ADDED_RESOURCES"
echo "Resources to be changed: $CHANGED_RESOURCES"
if [[ "$DELETED_RESOURCES" -gt 0 ]]; then
echo "WARNING: Terraform plan contains deletions! ($DELETED_RESOURCES)"
echo "Proceeding with Terraform apply as deletions may be intentional."
else
echo "No deletions detected. Safe to proceed!"
fi
- name: Upload Terraform Plan Artifact
uses: actions/upload-artifact@v4
with:
name: terraform-plan-${{ matrix.environment }}
path: terraform-plan-${{ matrix.environment }}.json
retention-days: 7
- name: Validate Terraform Plan Output
run: |
echo "Checking Terraform Plan..."
PLAN_FILE=tfplan-${{ matrix.environment }}.out
terraform show -json $PLAN_FILE | jq '.' > terraform-plan-${{ matrix.environment }}.json || { echo "⚠ No Terraform plan output found!"; exit 0; }
DELETED_RESOURCES=$(jq -r '.resource_changes[] | select(.change.actions | contains(["delete"]))' terraform-plan-${{ matrix.environment }}.json 2>/dev/null || echo "")
if [[ -n "$DELETED_RESOURCES" ]]; then
echo "ERROR: Terraform plan contains deletions!"
echo "Resources marked for deletion:"
echo "$DELETED_RESOURCES"
exit 1
else
echo "Terraform plan is safe to apply!"
fi
- name: Debug Terraform Setup
if: ${{ env.DESTROY_SKIPPED != 'true' }}
run: |
terraform version
[[ -n "$TARGET_ENV" ]] && terraform workspace show
- name: Select Terraform Workspace
uses: ./.github/actions/select-terraform-workspace
with:
target_env: $TARGET_ENV
- name: Debug Terraform State
run: |
echo "Current Terraform State in $TARGET_ENV:"
terraform state list || {
echo "No Terraform state found! Retrying with refresh...";
terraform refresh || echo "Terraform refresh failed!"
terraform state list || { echo "Still no state! Check backend permissions."; exit 1; }
}
- name: Configure AWS credentials (${{ matrix.environment }})
uses: ./.github/actions/configure-aws-credentials
with:
aws-region: ${{ env.AWS_REGION }}
test-account-role: ${{ env.TERRAFORM_ROLE }}
- name: Debug AWS Role & Permissions
run: |
echo "Verifying AWS Role Identity..."
aws sts get-caller-identity || { echo "ERROR: Failed to fetch AWS identity"; exit 1; }
echo "Validating AWS Permissions..."
aws s3 ls s3://nonexistent-bucket >/dev/null 2>&1 && echo "S3 Access OK" || echo "Limited S3 Access (Expected for some roles)"
- uses: ./.github/actions/setup-terraform
- uses: ./.github/actions/setup-zsh
- uses: ./.github/actions/short-sha
- name: Validate Terraform Configuration
run: |
terraform validate || { echo "Terraform configuration is invalid!"; exit 1; }
- name: Validate AWS Credentials Before Terraform
run: |
echo "Checking AWS credentials before Terraform Apply..."
if ! aws sts get-caller-identity; then
echo "ERROR: AWS credentials are invalid! Terraform cannot proceed."
exit 1
fi
- name: Auto-Fix Terraform Formatting
run: |
terraform fmt -recursive -diff
git diff --exit-code || (echo "ERROR: Terraform formatting issues detected! Run 'terraform fmt' locally to fix." && exit 1)
terraform_apply:
name: Terraform apply (${{ matrix.environment }})
runs-on: ubuntu-latest
needs: [ "terraform_plan" ]
strategy:
fail-fast: false
matrix:
environment: [ public, auth ]
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ github.base_ref }}
fetch-depth: 1
sparse-checkout-cone-mode: true
fetch-tags: false
show-progress: true
lfs: false
submodules: false
set-safe-directory: true
- name: Set TARGET_ENV
run: |
if [[ "${{ matrix.environment }}" == "public" ]]; then
echo "TARGET_ENV=${{ needs.build_base.outputs.public_env }}" >> $GITHUB_ENV
echo "TERRAFORM_ROLE=${{ secrets.UHD_TERRAFORM_ROLE_PUBLIC }}" >> $GITHUB_ENV
else
echo "TARGET_ENV=${{ needs.build_base.outputs.auth_env }}" >> $GITHUB_ENV
echo "TERRAFORM_ROLE=${{ secrets.UHD_TERRAFORM_ROLE_AUTH }}" >> $GITHUB_ENV
fi
- uses: actions/setup-python@v5
- name: Debug Terraform Setup
if: ${{ env.DESTROY_SKIPPED != 'true' }}
run: |
terraform version
[[ -n "$TARGET_ENV" ]] && terraform workspace show
- name: Select Terraform Workspace
uses: ./.github/actions/select-terraform-workspace
with:
target_env: $TARGET_ENV
- name: Configure AWS credentials (${{ matrix.environment }})
uses: ./.github/actions/configure-aws-credentials
with:
aws-region: ${{ env.AWS_REGION }}
test-account-role: ${{ secrets[format('UHD_TERRAFORM_ROLE_{0}', matrix.environment == 'public' && 'PUBLIC' || 'AUTH')] }}
- name: Retry Terraform Apply (${{ matrix.environment }})
run: |
for attempt in {1..3}; do
set -e
source uhd.sh
uhd terraform init
layers=("10-account" "20-app")
declare -A pids errors logs start_times end_times durations
for layer in "${layers[@]}"; do
logs[$layer]="terraform-apply-$layer.log"
start_times[$layer]=$(date +%s)
echo "Applying Terraform for $layer (log: ${logs[$layer]})..."
(uhd terraform apply:layer $layer $TARGET_ENV | tee "${logs[$layer]}") &
pids[$layer]=$!
done
for layer in "${!pids[@]}"; do
if ! wait "${pids[$layer]}"; then
errors[$layer]=1
echo "Terraform Apply Failed for $layer! Attempting to unlock..."
terraform force-unlock -force || echo "Warning: Could not force-unlock Terraform state."
fi
end_times[$layer]=$(date +%s)
durations[$layer]=$((end_times[$layer] - start_times[$layer]))
done
if [[ ${#errors[@]} -gt 0 ]]; then
echo "Terraform apply failed for layers: ${!errors[@]}"
if [[ $attempt -lt 3 ]]; then
echo "Retrying Terraform Apply (Attempt $((attempt+1))) in 15s..."
sleep 15
else
echo "Final Attempt Failed. Exiting..."
exit 1
fi
else
echo "Terraform Apply Succeeded!"
break
fi
done
shell: zsh {0}
- name: Upload Terraform Apply Logs
uses: actions/upload-artifact@v4
with:
name: terraform-apply-${{ matrix.environment }}
path: terraform-apply-*.log
retention-days: 7
- name: Terraform Apply Summary
run: |
echo "### Terraform Apply Summary ###"
for layer in "10-account" "20-app"; do
if [[ -f "terraform-apply-$layer.log" ]]; then
echo "----- Terraform Apply Log for $layer (Last 50 lines) -----"
tail -n 50 "terraform-apply-$layer.log" || echo "No log file found."
echo "--------------------------------------------------------"
echo "Changes Applied:"
CREATED=$(grep "created" "terraform-apply-$layer.log" || echo "No new resources created.")
UPDATED=$(grep "updated" "terraform-apply-$layer.log" || echo "No resources updated.")
DESTROYED=$(grep "destroyed" "terraform-apply-$layer.log" || echo "No resources deleted.")
echo "Terraform Apply Summary for $layer"
echo "----------------------------------------"
echo "Created Resources:"
echo "$CREATED"
echo ""
echo "Updated Resources:"
echo "$UPDATED"
echo ""
echo "Deleted Resources:"
echo "$DESTROYED"
echo "----------------------------------------"
echo "Checking Terraform Apply status for $layer..."
if grep -q "Apply complete!" "terraform-apply-$layer.log" || ! grep -iq "error" "terraform-apply-$layer.log"; then
echo "Terraform Apply Completed Successfully for $layer"
else
echo "ERROR: Terraform Apply failed for $layer!"
ERROR_LINES=$(grep -i "error" "terraform-apply-$layer.log" | tail -n 10)
if [[ -z "$ERROR_LINES" ]]; then
echo "No specific errors found, check full logs."
else
echo "Last 10 error messages:"
echo "$ERROR_LINES"
fi
exit 1
fi
else
echo "Warning: Log file for $layer not found!"
fi
done
shell: zsh {0}
push_docker_images:
name: Push docker images (${{ matrix.environment }})
runs-on: ubuntu-latest
needs: [ "terraform_apply" ]
strategy:
fail-fast: false
matrix:
environment: [ public, auth ]
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ github.base_ref }}
fetch-depth: 1
- name: Set TARGET_ENV
run: |
if [[ "${{ matrix.environment }}" == "public" ]]; then
echo "TARGET_ENV=${{ needs.build_base.outputs.public_env }}" >> $GITHUB_ENV
else
echo "TARGET_ENV=${{ needs.build_base.outputs.auth_env }}" >> $GITHUB_ENV
fi
- name: Configure AWS credentials (${{ matrix.environment }})
uses: ./.github/actions/configure-aws-credentials
with:
aws-region: ${{ env.AWS_REGION }}
test-account-role: ${{ secrets[format('UHD_TERRAFORM_ROLE_{0}', matrix.environment == 'public' && 'PUBLIC' || 'AUTH')] }}
- uses: ./.github/actions/setup-zsh
- uses: ./.github/actions/short-sha
- name: Set SHORT_SHA Environment Variable
run: |
echo "SHORT_SHA=${{ env.SHORT_SHA }}" >> $GITHUB_ENV
echo "SHORT_SHA=${{ env.SHORT_SHA }}"
- name: Retrieve ECR Repository Name
id: get_ecr_repo
run: |
REPO_NAME="ukhsa-data-dashboard/back-end"
echo "Using ECR Repository: $REPO_NAME"
echo "REPO_NAME=$REPO_NAME" >> $GITHUB_ENV
- name: Login to AWS ECR
run: |
AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
aws ecr get-login-password --region ${{ env.AWS_REGION }} | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.${{ env.AWS_REGION }}.amazonaws.com
- name: Check if Docker image exists in ECR
id: check_docker_image
run: |
IMAGE_TAG="ci-${{ github.event.pull_request.number }}-${{ env.SHORT_SHA }}"
AWS_ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
echo "Checking if image $IMAGE_TAG exists in AWS ECR ($REPO_NAME)..."
if aws ecr describe-images --repository-name "$REPO_NAME" \
--image-ids imageTag="$IMAGE_TAG" \
--registry-id "$AWS_ACCOUNT_ID" &> /dev/null; then
echo "Docker image $IMAGE_TAG already exists. Skipping push."
echo "SKIP_PUSH=true" >> $GITHUB_ENV
else
echo "Docker image $IMAGE_TAG not found. Proceeding with push."
echo "SKIP_PUSH=false" >> $GITHUB_ENV
fi
- name: Pull / Push Docker image
if: ${{ env.SKIP_PUSH != 'true' }}
run: |
source uhd.sh
uhd docker update ${{ matrix.environment }} ci-${{ github.event.pull_request.number }}-${{ env.SHORT_SHA }}
restart_services:
name: Restart services (${{ matrix.environment }})
runs-on: ubuntu-latest
needs: [ "push_docker_images" ]
strategy:
fail-fast: false
matrix:
environment: [ public, auth ]
env:
TARGET_ENV:
${{ needs.build_base.outputs[matrix.environment == 'public' && 'public_env' || 'auth_env'] }}
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ github.base_ref }}
fetch-depth: 1
sparse-checkout-cone-mode: true
fetch-tags: false
show-progress: true
lfs: false
submodules: false
set-safe-directory: true
- name: Configure AWS credentials (${{ matrix.environment }})
uses: ./.github/actions/configure-aws-credentials
with:
aws-region: ${{ env.AWS_REGION }}
test-account-role: ${{ secrets[format('UHD_TERRAFORM_ROLE_{0}', matrix.environment == 'public' && 'PUBLIC' || 'AUTH')] }}
- uses: ./.github/actions/setup-terraform
- uses: ./.github/actions/setup-zsh
- uses: ./.github/actions/short-sha
- name: Terraform output
run: |
source uhd.sh
uhd terraform init:layer 20-app
uhd terraform output:layer 20-app $PUBLIC_ENV
uhd terraform output:layer 20-app $AUTH_ENV
shell: zsh {0}
- name: Restart ECS services (${{ matrix.environment }})
run: |
source uhd.sh
uhd ecs restart-services
shell: zsh {0}
- name: Redeploy lambda functions
run: |
source uhd.sh
uhd lambda restart-functions
shell: zsh {0}
terraform_destroy:
name: Terraform destroy (${{ matrix.environment }})
runs-on: ubuntu-latest
timeout-minutes: 30
if: ${{ success() }}
needs: [ "terraform_plan" ]
strategy:
fail-fast: false
matrix:
environment: [ public, auth ]
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ github.base_ref }}
fetch-depth: 1
sparse-checkout-cone-mode: true
fetch-tags: false
show-progress: true
lfs: false
submodules: false
set-safe-directory: true
- name: Set TARGET_ENV
run: |
if [[ "${{ matrix.environment }}" == "public" ]]; then
echo "TARGET_ENV=${{ needs.build_base.outputs.public_env }}" >> $GITHUB_ENV
echo "TERRAFORM_ROLE=${{ secrets.UHD_TERRAFORM_ROLE_PUBLIC }}" >> $GITHUB_ENV
else
echo "TARGET_ENV=${{ needs.build_base.outputs.auth_env }}" >> $GITHUB_ENV
echo "TERRAFORM_ROLE=${{ secrets.UHD_TERRAFORM_ROLE_AUTH }}" >> $GITHUB_ENV
fi
- uses: actions/setup-python@v5
- name: Debug Terraform Setup
if: ${{ env.DESTROY_SKIPPED != 'true' }}
run: |
terraform version
[[ -n "$TARGET_ENV" ]] && terraform workspace show
- name: Select Terraform Workspace
uses: ./.github/actions/select-terraform-workspace
with:
target_env: $TARGET_ENV
- name: Configure AWS credentials (${{ matrix.environment }})
uses: ./.github/actions/configure-aws-credentials
with:
aws-region: ${{ env.AWS_REGION }}
test-account-role: ${{ secrets[format('UHD_TERRAFORM_ROLE_{0}', matrix.environment == 'public' && 'PUBLIC' || 'AUTH')] }}
- name: Cleanup Terraform Workspace on Failure
if: failure()
run: |
echo "Terraform Destroy failed. Cleaning up workspace..."
terraform workspace select default
terraform workspace delete $TARGET_ENV || echo "Warning: Failed to delete workspace"
- name: Retry Terraform Destroy (${{ matrix.environment }})
run: |
for attempt in {1..3}; do
set -x
source uhd.sh
uhd terraform init:layer 20-app
layers=("20-app" "10-account")
declare -A pids errors logs start_times end_times durations
for layer in "${layers[@]}"; do
logs[$layer]="terraform-destroy-$layer.log"
start_times[$layer]=$(date +%s)
echo "Destroying Terraform for $layer (log: ${logs[$layer]})..."
(uhd terraform destroy:layer $layer $TARGET_ENV > >(tee "${logs[$layer]}") 2> >(tee -a "${logs[$layer]}" >&2)) &
pids[$layer]=$!
done
for layer in "${!pids[@]}"; do
if ! wait "${pids[$layer]}"; then
errors[$layer]=1
echo "Terraform Destroy Failed for $layer! Unlocking state..."
terraform force-unlock -force || echo "Warning: Could not force-unlock Terraform state."
fi
end_times[$layer]=$(date +%s)
durations[$layer]=$((end_times[$layer] - start_times[$layer]))
done
if [[ ${#errors[@]} -gt 0 ]]; then
echo "Terraform destroy failed for layers: ${!errors[@]}"
if [[ $attempt -lt 3 ]]; then
echo "Retrying Terraform Destroy (Attempt $((attempt+1)))..."
sleep 10
else
echo "Final attempt failed. Exiting..."
exit 1
fi
else
echo "Terraform Destroy Succeeded!"
break
fi
done
shell: zsh {0}
- name: Upload Terraform Destroy Logs
uses: actions/upload-artifact@v4
with:
name: terraform-destroy-${{ matrix.environment }}
path: terraform-destroy-${{ matrix.environment }}.log
clean_up_remaining_resources:
name: Clean up remaining resources
runs-on: ubuntu-latest
needs: [ "terraform_destroy" ]
if: always()
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
ref: ${{ github.base_ref }}
fetch-depth: 1
sparse-checkout-cone-mode: true
fetch-tags: false
show-progress: true
lfs: false
submodules: false
set-safe-directory: true
- name: Configure AWS credentials for tools account
uses: ./.github/actions/configure-aws-credentials
with:
aws-region: ${{ env.AWS_REGION }}
tools-account-role: ${{ secrets.UHD_TERRAFORM_IAM_ROLE }}
- uses: ./.github/actions/setup-zsh
- uses: ./.github/actions/short-sha
- name: Cleanup Terraform Workspaces
continue-on-error: true
run: |
terraform init -upgrade
terraform workspace list || { echo "Warning: Failed to list workspaces"; exit 1; }
if ! terraform workspace list | grep -q "default"; then
terraform workspace new default
fi
WORKSPACES=$(terraform workspace list | grep -E '^ci-|^auth-ci-' | tr -d '* ' || echo "")
if [[ -z "$WORKSPACES" ]]; then
echo "No CI workspaces found. Skipping cleanup."
exit 0
fi
echo "Deleting Terraform workspaces in parallel..."
for workspace in $WORKSPACES; do
(
echo "Attempting to delete workspace: $workspace"
terraform workspace select default
terraform workspace delete "$workspace" || (echo "Workspace $workspace still in use. Retrying in 10s..." && sleep 10 && terraform workspace delete "$workspace" || echo "Final attempt failed for $workspace.")
) &
done
wait # Ensure all background jobs finish
- name: Delete secrets safely
continue-on-error: true
run: |
source uhd.sh
uhd secrets delete-all-secrets ci-$SHORT_SHA || echo "⚠ Warning: Secret deletion failed!"
shell: zsh {0}