Skip to content

[PRMP-1090] Update Review Status lambda infra to use correct env vars… #632

[PRMP-1090] Update Review Status lambda infra to use correct env vars…

[PRMP-1090] Update Review Status lambda infra to use correct env vars… #632

name: 'Z-AUTOMATED: Deploy - Dev'
on:
push:
branches:
- main
pull_request:
branches:
- main
permissions:
pull-requests: write
actions: read # This is required for Plan comment
id-token: write # This is required for requesting the JWT
contents: write # This is required for SBOM action
jobs:
terraform_plan_apply:
name: Terraform Plan/Apply (ndr-dev)
runs-on: ubuntu-latest
environment: development
steps:
- name: Checkout
uses: actions/checkout@v5
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v5
with:
role-to-assume: ${{ secrets.AWS_ASSUME_ROLE }}
role-skip-session-tagging: true
aws-region: ${{ vars.AWS_REGION }}
mask-aws-account-id: true
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: 1.13.3
terraform_wrapper: true
- name: Initialise Terraform
id: init
run: terraform init -backend-config=backend.conf -no-color
working-directory: ./infrastructure
shell: bash
- name: Select Terraform Workspace
id: workspace
run: terraform workspace select ${{ secrets.AWS_WORKSPACE }}
working-directory: ./infrastructure
shell: bash
- name: Check Terraform Formatting
id: fmt
run: terraform fmt -check
working-directory: ./infrastructure
- name: Check Terraform Validation
id: validate
run: terraform validate -no-color
- name: Run Terraform Plan
id: plan
run: |
terraform plan -lock-timeout=20m -input=false -no-color -var-file="${{vars.TF_VARS_FILE}}" -out tf.plan > plan_output.txt 2>&1
terraform show -no-color tf.plan > tfplan.txt 2>&1
# Mask PEM certificates (BEGIN...END CERTIFICATE)
awk 'BEGIN{cert=""}
/-----BEGIN CERTIFICATE-----/{cert=$0; in_cert=1; next}
/-----END CERTIFICATE-----/{cert=cert"\n"$0; print cert; cert=""; in_cert=0; next}
in_cert{cert=cert"\n"$0}' tfplan.txt | while IFS= read -r cert_block; do
if [ -n "$cert_block" ]; then
echo "::add-mask::$cert_block"
fi
done || echo "No certificate blocks found to mask."
# Mask sensitive URLs in the Terraform Plan output
grep -Eo 'https://[a-zA-Z0-9.-]+\.execute-api\.[a-zA-Z0-9.-]+\.amazonaws\.com/[a-zA-Z0-9/._-]*' tfplan.txt | while read -r api_url; do
if [ -n "$api_url" ]; then
echo "::add-mask::$api_url"
fi
done || echo "No api URLs found to mask."
# Mask Lambda invocation URLs
grep -Eo 'https://[a-zA-Z0-9.-]+\.lambda\.amazonaws\.com/[a-zA-Z0-9/._-]+' tfplan.txt | while read -r lambda_url; do
if [ -n "$lambda_url" ]; then
echo "::add-mask::$lambda_url"
fi
done || echo "No Lambda URLs found to mask."
# Mask AWS account IDs (12-digit numbers)
grep -Eo '[0-9]{12}' tfplan.txt | while read -r account_id; do
if [ -n "$account_id" ]; then
echo "::add-mask::$account_id"
fi
done || echo "No Account IDs found to mask."
# Mask GitHub secrets
echo "::add-mask::${{ secrets.AWS_ASSUME_ROLE }}"
echo "::add-mask::${{ secrets.GITHUB_TOKEN }}"
# Mask Terraform variables
echo "::add-mask::${{ vars.TF_VARS_FILE }}"
# Output the sanitized plan to logs
cat plan_output.txt
echo "summary=$(grep -E 'Plan: [0-9]+ to add, [0-9]+ to change, [0-9]+ to destroy\.|No changes\. Your infrastructure matches the configuration\.' tfplan.txt | sed 's/.*No changes\. Your infrastructure matches the configuration/Plan: no changes/g' | sed 's/.*Plan: //g' | sed 's/\..*//g')" >> $GITHUB_OUTPUT
working-directory: ./infrastructure
shell: bash
- name: Truncate Plan Output
id: plan-truncated
if: success() || failure()
env:
LENGTH: 64512
run: |
PLAN_FULL=$(grep -v 'Refreshing state...' <<'EOF'
${{ steps.plan.outputs.stdout }}
${{ steps.plan.outputs.stderr }}
EOF
)
# Optionally redact sensitive strings in the PLAN_FULL variable
PLAN_FULL=$(echo "$PLAN_FULL" | sed -E 's#arn:aws:iam::[0-9]{12}:role/[a-zA-Z0-9_-]+#[REDACTED_IAM_ROLE_ARN]#g')
PLAN_FULL=$(echo "$PLAN_FULL" | sed -E 's/[0-9]{12}/[REDACTED_AWS_ACCOUNT_ID]/g')
PLAN_FULL=$(echo "$PLAN_FULL" | sed -E 's#https://[a-zA-Z0-9.-]+\.lambda\.amazonaws\.com/[a-zA-Z0-9/._-]+#[REDACTED_LAMBDA_URL]#g')
PLAN_FULL=$(echo "$PLAN_FULL" | sed -E 's#https://[a-zA-Z0-9.-]+\.execute-api\.[a-zA-Z0-9.-]+\.amazonaws\.com/[a-zA-Z0-9/._-]*#[REDACTED_API_GATEWAY_URL]#g')
PLAN_FULL=$(echo "$PLAN_FULL" | sed -E '/-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/s/.*/[REDACTED_PEM_CERT]/')
echo "PLAN<<EOF" >> $GITHUB_ENV
echo "${PLAN_FULL::$LENGTH}" >> $GITHUB_ENV
[ ${#PLAN_FULL} -gt $LENGTH ] && echo "(truncated - see workflow logs for full output)" >> $GITHUB_ENV
echo "EOF" >> $GITHUB_ENV
working-directory: ./infrastructure
shell: bash
- name: Add PR Comment
uses: actions/github-script@v8
if: github.event_name == 'pull_request' && (success() || failure())
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
// 1. Retrieve existing bot comments for the PR
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
})
const botComment = comments.find(comment => {
return comment.user.type === 'Bot' && comment.body.includes('Report for environment: ndr-dev')
})
// 2. Prepare format of the comment
const output = `### Report for environment: ndr-dev
#### Terraform Initialization ⚙️\`${{ steps.init.outcome }}\`
<details><summary>Initialization Output</summary>
\`\`\`\n
${{ steps.init.outputs.stdout }}
\`\`\`
</details>
#### Terraform Validation 🤖\`${{ steps.validate.outcome }}\`
<details><summary>Validation Output</summary>
\`\`\`\n
${{ steps.validate.outputs.stdout }}
\`\`\`
</details>
#### Terraform Plan 📖\`${{ steps.plan.outcome }}\`
<details><summary>Show Plan (${{ steps.plan.outputs.summary }})</summary>
\`\`\`\n
${{ env.PLAN }}
\`\`\`
</details>`;
// 3. If we have a comment, update it, otherwise create a new one
if (botComment) {
github.rest.issues.deleteComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body: output
})
}
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: output
})
# Terraform apply will only occur on a push (merge request completion)
- name: Run Terraform Apply
if: github.ref == 'refs/heads/main'
run: terraform apply -lock-timeout=20m -auto-approve -input=false tf.plan
working-directory: ./infrastructure
deploy_lambdas:
name: Deploy Lambdas (ndr-dev)
needs: ['terraform_plan_apply']
if: github.ref == 'refs/heads/main'
uses: NHSDigital/national-document-repository/.github/workflows/lambdas-dev-to-main-ci.yml@main
secrets:
AWS_ASSUME_ROLE: ${{ secrets.AWS_ASSUME_ROLE }}
deploy_ui:
name: Deploy UI (ndr-dev)
needs: ['terraform_plan_apply']
if: github.ref == 'refs/heads/main'
uses: NHSDigital/national-document-repository/.github/workflows/ui-dev-to-main-ci.yml@main
secrets:
AWS_ASSUME_ROLE: ${{ secrets.AWS_ASSUME_ROLE }}
notify-slack:
name: Notify Slack on Failure
runs-on: ubuntu-latest
environment: development
needs: [terraform_plan_apply, deploy_lambdas, deploy_ui]
if: failure() && github.event_name == 'push' && github.ref == 'refs/heads/main'
steps:
- name: Configure AWS Credentials
uses: aws-actions/configure-aws-credentials@v5
with:
role-to-assume: ${{ secrets.AWS_ASSUME_ROLE }}
role-skip-session-tagging: true
aws-region: ${{ vars.AWS_REGION }}
mask-aws-account-id: true
- name: Get slack bot token from SSM parameter store
run: |
slack_bot_token=$(aws ssm get-parameter --name "/ndr/alerting/slack/bot_token" --with-decryption --query "Parameter.Value" --output text)
echo "::add-mask::$slack_bot_token"
echo "SLACK_BOT_TOKEN=$slack_bot_token" >> $GITHUB_ENV
- name: Send Slack Notification
uses: slackapi/[email protected]
with:
method: chat.postMessage
token: ${{ env.SLACK_BOT_TOKEN }}
payload: |
{
"channel": "${{ vars.ALERTS_SLACK_CHANNEL_ID }}",
"attachments": [
{
"color": "#ff0000",
"blocks": [
{
"type": "header",
"text": {
"type": "plain_text",
"text": "❌ Workflow `${{ github.workflow }}` failed"
}
},
{
"type": "section",
"text": {
"type": "mrkdwn",
"text": "*Triggered by:* `${{ github.actor }}`\n*Branch:* `${{ github.ref_name }}`\n*Workflow:* <${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|${{ github.workflow }}>"
}
},
{
"type": "divider"
},
{
"type": "section",
"fields": [
{ "type": "mrkdwn", "text": "*terraform_plan_apply:* ${{ needs.terraform_plan_apply.result == 'success' && ':white_check_mark:' || ':x:' }}" },
{ "type": "mrkdwn", "text": "*deploy_lambdas:* ${{ needs.deploy_lambdas.result == 'success' && ':white_check_mark:' || ':x:' }}" },
{ "type": "mrkdwn", "text": "*deploy_ui:* ${{ needs.deploy_ui.result == 'success' && ':white_check_mark:' || ':x:' }}" }
]
},
{
"type": "context",
"elements": [
{ "type": "mrkdwn", "text": "Environment: `development` | Sandbox: `ndr-dev`" }
]
}
]
}
]
}