Skip to content

Deploy HRM Lambda

Deploy HRM Lambda #627

# Copyright (C) 2021-2023 Technology Matters
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see https://www.gnu.org/licenses/.
name: 'Deploy HRM Lambda'
on:
workflow_dispatch:
inputs:
environment:
description: HRM environment to deploy.
default: development
required: true
type: choice
options:
- development
- staging
- production
lambda_path:
description: 'Lambda path'
required: true
type: choice
options:
- hrm-domain/lambdas/files-urls
- hrm-domain/lambdas/contact-retrieve-transcript
- hrm-domain/lambdas/search-index-consumer
- lambdas/job-complete
- resources-domain/lambdas/import-consumer
- resources-domain/lambdas/import-producer
- resources-domain/lambdas/search-index
workflow_call:
secrets:
AWS_ACCESS_KEY_ID:
required: true
AWS_SECRET_ACCESS_KEY:
required: true
inputs:
environment:
description: HRM environment to deploy. E.G = development, staging, production (must match with the AWS environment value). Default value = development
type: string
default: development
required: true
lambda_path:
description: 'Lambda path'
required: true
type: string
send-slack-message:
description: 'Specifies if should send a Slack message at the end of successful run. Defaults to true'
required: false
default: 'true'
type: string
env:
# if anything is set as a secret, it can't be used in outputs. So we need to set it as an env var
PRIMARY_AWS_REGION: us-east-1
jobs:
build_lambda:
name: Build Lambda Image
runs-on: ubuntu-latest
outputs:
matrix_json: ${{ steps.generate-output.outputs.matrix_json }}
docker_image: ${{ steps.generate-output.outputs.docker_image}}
steps:
- name: Checkout
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-region: ${{ env.PRIMARY_AWS_REGION }}
# this plugin sets the AWS account ID to a secret which is not allowed in outputs
# we have to disable that so repo output will work
mask-aws-account-id: 'no'
- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v2
- name: Set Build Args
run: |
lambda_full_path=${{ inputs.lambda_path }}
# Extracting directory and lambda name from the lambda path
build_lambda_dir=$(dirname $lambda_full_path)
build_lambda_name=$(basename $lambda_full_path)
repo_name="${lambda_full_path//-domain\/lambdas\//-}"
repo_name="${repo_name#hrm-}"
repo_name="${repo_name//lambdas\//}"
repo_name="${repo_name//\//-}"
echo "ecr_repository=${{ inputs.environment }}/hrm-$repo_name" >> $GITHUB_ENV
echo "build_lambda_name=$build_lambda_name" >> $GITHUB_ENV
echo "build_lambda_dir=$build_lambda_dir" >> $GITHUB_ENV
- name: Build and Push Docker Image
uses: docker/build-push-action@v6
with:
context: ./
file: ./lambdas/Dockerfile
build-args: |
lambda_name=${{ env.build_lambda_name }}
lambda_dir=${{ env.build_lambda_dir }}
push: true
tags: ${{ steps.login-ecr.outputs.registry }}/${{ env.ecr_repository }}:live,${{ steps.login-ecr.outputs.registry }}/${{ env.ecr_repository }}:${{ github.sha }}
# The matrix build supports `lambda-groups` defined in the environment-region-map.json file
# This allows us to deploy multiple lambdas to specific env/region combos that use a common docker image
# That is some pretty intense JQ, but it works.
# The current behavior is: If a key that matches the lambda name is found in the lambda-groups object, then
# created separate entries for each region/lambda combo. Otherwise, just use the lambda name and deploy to all
# regions that exist for that environment.
# matrices don't support complex object structures, so we generate a flat array of objects that contain the
# region and lambda name. If there are multiple lambdas, the region will be repeated for each lambda.
- name: Generate output
id: generate-output
run: |
# output to logs
matrix_json=$(jq -c --arg env "${{ inputs.environment }}" '[.[$env][] | {region: .}]' ./.github/workflows/config/environment-region-map.json)
echo "matrix_json=$matrix_json" >> $GITHUB_OUTPUT
echo "docker_image=${{ steps.login-ecr.outputs.registry }}/${{ env.ecr_repository }}:${{ github.sha }}" >> $GITHUB_OUTPUT
deploy_lambdas:
needs: build_lambda
name: Deploy to Amazon ECS
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include: ${{ fromJson(needs.build_lambda.outputs.matrix_json) }}
env:
DOCKER_IMAGE: ${{ needs.build_lambda.outputs.docker_image }}
steps:
- 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-region: ${{ matrix.region }}
# this plugin sets the AWS account ID to a secret which is not allowed in outputs
# we have to disable that so repo output will work
mask-aws-account-id: 'no'
# The search and replace is kinda ugly but works. We need to replace the primary region with the region we're deploying to.
# because lambdas don't support ECR images in different regions. We use cross region replication in ECR to make the image
# that is pushed in the primary region available in all other regions.
- name: Update Lambda and Publish
run: |
DOCKER_IMAGE=$(echo "${{ env.DOCKER_IMAGE }}" | sed -r 's/${{ env.PRIMARY_AWS_REGION }}/${{ matrix.region }}/g')
DOCKER_IMAGE_BASE=$(echo "$DOCKER_IMAGE" | cut -d':' -f1)
LAMBDA_FUNCTIONS=$(aws lambda list-functions --query "Functions[?contains(Description, 'ecr_image:${DOCKER_IMAGE_BASE}')].FunctionName" --output text)
if [ -z "$LAMBDA_FUNCTIONS" ]; then
echo "Warning: No Lambda functions found for image $DOCKER_IMAGE_BASE"
exit 0
fi
for LAMBDA in $LAMBDA_FUNCTIONS; do
OUTPUT=$(aws lambda update-function-code --function-name $LAMBDA --image-uri "$DOCKER_IMAGE" --publish)
NEW_VERSION=$(echo "$OUTPUT" | jq -r '.Version')
ALIAS_EXISTS=$(aws lambda get-alias --function-name $LAMBDA --name live || echo "not_exists")
if [ "$ALIAS_EXISTS" == "not_exists" ]; then
aws lambda create-alias --function-name $LAMBDA --name live --function-version $NEW_VERSION
else
aws lambda update-alias --function-name $LAMBDA --name live --function-version $NEW_VERSION
fi
done
# reconfigure AWS credentials to use the default region for SSM Parameter Store.
# aws-actions/configure-aws-credentials@v4 overrides env.AWS_DEFAULT_REGION, so
# we name our env var PRIMARY_AWS_REGION to avoid that.
- 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-region: ${{ env.PRIMARY_AWS_REGION }}
# Set any env vars needed from Parameter Store here
- name: Set GITHUB_ACTIONS_SLACK_BOT_TOKEN
uses: 'marvinpinto/action-inject-ssm-secrets@latest'
with:
ssm_parameter: 'GITHUB_ACTIONS_SLACK_BOT_TOKEN'
env_variable_name: 'GITHUB_ACTIONS_SLACK_BOT_TOKEN'
- name: Set ASELO_DEPLOYS_CHANNEL_ID
uses: 'marvinpinto/action-inject-ssm-secrets@latest'
with:
ssm_parameter: 'ASELO_DEPLOYS_CHANNEL_ID'
env_variable_name: 'ASELO_DEPLOYS_CHANNEL_ID'
# Send Slack notifying success
- name: Slack Aselo channel
id: slack
uses: slackapi/[email protected]
with:
channel-id: ${{ env.ASELO_DEPLOYS_CHANNEL_ID }}
slack-message: '`[HRM lambdas - ${{ inputs.lambda_path }}]` Deployment of ${{ github.ref_type }} `${{ github.ref_name }}` requested by `${{ github.triggering_actor }}` completed with SHA ${{ github.sha }} to region `${{ matrix.region }}`, environment `${{ inputs.environment }}` :rocket:.'
env:
SLACK_BOT_TOKEN: ${{ env.GITHUB_ACTIONS_SLACK_BOT_TOKEN }}
if: ${{ inputs.send-slack-message != 'false' }}
# Update deployment matrix with region as identifier and lambda path as service repo
- name: Update deployment matrix
id: update-deployment-matrix
uses: techmatters/flex-plugins/.github/actions/deployment-matrix@master
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.PRIMARY_AWS_REGION }}
identifier: ${{ matrix.region }}
environment: ${{ inputs.environment }}
service_repo: ${{ inputs.lambda_path }}
version_tag: ${{ github.ref_name }}