Skip to content

Commit 9581327

Browse files
dependabot[bot]samgibsonmoj
authored andcommitted
ECS deployment workflow
WIP: Refactors deployment workflow for ECS Configures health check endpoints + request timeouts + output caching This ensures consistent configuration across all services. Adds AWS task and execution role ARNs to deployment workflows and task definitions Fetch IAM Role ARNs from Secrets Manager and update ECS task definition references Add workflow_dispatch inputs for environment and AWS region in deploy workflow Add dynamic secrets handling for ECS deployments in workflow Enhance ECS task definitions with EFS configuration and update IAM role fetching Add dbinteractions service and task definition for ECS deployment Update ECS service and cluster naming conventions based on environment Add DMSFilesBasePath secret to multiple services in services.json Update image repository structure and tagging format in deploy workflow Add debug logging for deployment information in reusable deploy workflow Add debug logging to confirm AWS credentials in deploy workflow Remove obsolete Aspire.Hosting.AppHost package version from Directory.Packages.props - causing conflict in workflow Inject IAM roles into ECS task definition in deploy workflow Testing: Working through wrokflow to ensure steps are correct. First checking if secrets are retrieved correctly Testing secret retrieval Testing: Add ECS secrets building step in deploy workflow Testing: Removing quotes to correctly parse services json Testing: Fix secret retrieval loop to use while read for better parsing Testing: Refactor ECS secrets building step for improved string handling Testing: Add role injection step in task definition Testing: Remove quotes for task definition path in role injection step Testing: fixed spelling error in lookup Testing: Add ECS task definition rendering step with secrets and preview output Testing: Add image parameter for ECS task definition (required) Testing: Saving task def to file so it can be rendered correctly Testing: Update ECS task definition preview step to use updated task definition file Testing: Enable test execution and image publishing in deployment workflow Testing: Update secret handling and task definition processing in deployment workflow Testing: Enhance deployment workflow with EFS parameters and update task definitions to use placeholders Fix: Update family name and service/cluster naming conventions to lowercase in deployment workflow Fix: Update task definitions to replace placeholders with environment-specific values for logging configuration Fix: Ensure directories are created for delius and offloc inputs/outputs during FileLocations initialisation Temp: Stop filesync from processing files entriely Fix: Add API_Client_ID to secrets in services configuration. Previously set to Visualiser Client ID incorrectly causing audience conflict Config: Enable processing on completion and timer in FileSync configuration Change: Updated gihub actions to use full SHA instead of tags as required by MOJ Refactor: Update Offloc staging procedure to use SqlBulkCopy for file imports due to BULK INSERT not working with containerisation Update: Altered FileSync ProcessOnStartup to true Testing: Update GetTableColumns and ReadPipeDelimitedFile methods to return column max lengths and enforce length constraints Testing: Enhance GetTableColumns method to include DataType and update ReadPipeDelimitedFile for type handling Testig: Change Delius to also use SqlBulkCopy like Offloc does Fix: Add missing END statement to StageDelius stored procedure Fix: Ensure empty values are input as NULL instead of empty strings Rebuild: Added new line to trigger rebuild of images to assess Inspector findings Update OpenTelemetry package versions to latest releases Revert "Bulk Copy" functionality This caused significant issues with some SQL data types and resulted in imported failures. Add a separate base path configuration DbInteractions
1 parent 7e0886a commit 9581327

22 files changed

Lines changed: 1326 additions & 159 deletions
Lines changed: 227 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,242 @@
11
name: Reusable Deploy
22

33
on:
4+
workflow_call:
5+
inputs:
6+
environment:
7+
required: true
8+
type: string
9+
description: 'Environment name (preprod or prod)'
10+
aws-region:
11+
required: false
12+
type: string
13+
default: 'eu-west-2'
14+
secrets:
15+
AWS_ROLE_ARN:
16+
required: true
17+
AWS_SECRETS_ARN:
18+
required: true
419
workflow_dispatch:
520
inputs:
621
environment:
722
required: true
823
type: choice
24+
description: 'Environment to deploy to'
925
options:
1026
- preprod
1127
- prod
28+
aws-region:
29+
required: false
30+
type: string
31+
default: 'eu-west-2'
32+
description: 'AWS region'
33+
34+
permissions:
35+
id-token: write
36+
contents: read
1237

1338
jobs:
14-
placeholder:
39+
load-services:
1540
runs-on: ubuntu-latest
41+
outputs:
42+
services: ${{ steps.load.outputs.services }}
1643
steps:
17-
- run: echo "Triggered from feature branch. This placeholder should not run."
44+
- name: Checkout
45+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
46+
47+
- name: Load service mappings
48+
id: load
49+
run: |
50+
services=$(jq -c '.services' infra/services.json)
51+
echo "services=${services}" >> $GITHUB_OUTPUT
52+
echo "Loaded services:"
53+
echo "${services}" | jq '.'
54+
55+
deploy:
56+
needs: load-services
57+
runs-on: ubuntu-latest
58+
environment: ${{ inputs.environment }}
59+
60+
strategy:
61+
matrix:
62+
service: ${{ fromJson(needs.load-services.outputs.services) }}
63+
fail-fast: false
64+
65+
steps:
66+
- name: Checkout
67+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
68+
69+
- name: Configure AWS credentials
70+
uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0
71+
with:
72+
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
73+
aws-region: ${{ inputs.aws-region }}
74+
75+
- name: Login to Amazon ECR
76+
id: login-ecr
77+
uses: aws-actions/amazon-ecr-login@183a1442edf41672e66566b7fc560e297a290896 # v2.1.1
78+
79+
- name: Setup .NET
80+
uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0
81+
with:
82+
dotnet-version: 10.0.x
83+
84+
- name: Run tests
85+
run: dotnet test --configuration Release
86+
87+
- name: Build and Publish Image
88+
id: build-image
89+
env:
90+
REGISTRY: ${{ steps.login-ecr.outputs.registry }}
91+
REPOSITORY: dms-${{ inputs.environment }}-apps
92+
IMAGE_TAG: ${{ matrix.service.name }}-${{ github.sha }}
93+
run: |
94+
PROJECT_PATH="src/${{ matrix.service.project }}"
95+
96+
if [ ! -f "$PROJECT_PATH" ]; then
97+
echo "Project not found: $PROJECT_PATH - Skipping"
98+
echo "skip=true" >> $GITHUB_OUTPUT
99+
exit 0
100+
fi
101+
102+
echo "Building and publishing: $PROJECT_PATH"
103+
104+
dotnet publish "$PROJECT_PATH" \
105+
--configuration Release \
106+
--target:PublishContainer \
107+
--property:ContainerRegistry=$REGISTRY \
108+
--property:ContainerRepository=$REPOSITORY \
109+
--property:ContainerImageTag=$IMAGE_TAG
110+
111+
IMAGE="${REGISTRY}/${REPOSITORY}:${IMAGE_TAG}"
112+
echo "IMAGE=${IMAGE}" >> $GITHUB_OUTPUT
113+
echo "skip=false" >> $GITHUB_OUTPUT
114+
echo "Published image: ${IMAGE}"
115+
116+
- name: Testing workflow steps
117+
run: echo "The testing / deployment of the image have been skipped for this demo, but the workflow is correctly iterating over the services and would deploy the built image to ECS if those steps were uncommented."
118+
119+
- name: Fetch required secrets
120+
id: fetch-secrets
121+
run: |
122+
SECRET_JSON=$(aws secretsmanager get-secret-value \
123+
--secret-id ${{ secrets.AWS_SECRETS_ARN }} \
124+
--query 'SecretString' \
125+
--output text)
126+
127+
TASK_ROLE=$(echo "$SECRET_JSON" | jq -r '.Task_Role_ARN')
128+
EXEC_ROLE=$(echo "$SECRET_JSON" | jq -r '.Execution_Role_ARN')
129+
EFS_FS_ID=$(echo "$SECRET_JSON" | jq -r '.EFS_File_System_ID')
130+
EFS_AP_ID=$(echo "$SECRET_JSON" | jq -r '.EFS_Access_Point_ID')
131+
132+
echo "task_role=$TASK_ROLE" >> $GITHUB_OUTPUT
133+
echo "exec_role=$EXEC_ROLE" >> $GITHUB_OUTPUT
134+
echo "efs-fs-id=$EFS_FS_ID" >> $GITHUB_OUTPUT
135+
echo "efs-ap-id=$EFS_AP_ID" >> $GITHUB_OUTPUT
136+
137+
- name: Build secrets list for service
138+
id: build-secrets
139+
run: |
140+
SECRETS_JSON='${{ toJson(matrix.service.secrets) }}'
141+
142+
SECRETS_STRING=""
143+
144+
declare -A SECRET_MAP=(
145+
["API_Base_URL"]="API__BaseUrl"
146+
["Authentication_ApiKey"]="Authentication__ApiKey"
147+
["S3_BucketName"]="AWS__S3__BucketName"
148+
["Client_ID"]="AzureAd__ClientId"
149+
["API_Client_ID"]="AzureAd__ClientId"
150+
["Client_Secret"]="AzureAd__ClientSecret"
151+
["AuditDb"]="ConnectionStrings__AuditDb"
152+
["ClusterDb"]="ConnectionStrings__ClusterDb"
153+
["DeliusRunningPictureDb"]="ConnectionStrings__DeliusRunningPictureDb"
154+
["DeliusStagingDb"]="ConnectionStrings__DeliusStagingDb"
155+
["MatchingDb"]="ConnectionStrings__MatchingDb"
156+
["OfflocRunningPictureDb"]="ConnectionStrings__OfflocRunningPictureDb"
157+
["OfflocStagingDb"]="ConnectionStrings__OfflocStagingDb"
158+
["CatsRabbitMQ"]="ConnectionStrings__CatsRabbitMQ"
159+
["RabbitMQ"]="ConnectionStrings__RabbitMQ"
160+
["DMSFilesBasePath"]="DMSFilesBasePath"
161+
["DMSFilesBasePath_DbInteractions"]="DMSFilesBasePath"
162+
["Sentry_Dsn"]="Sentry_Dsn"
163+
)
164+
165+
# Get the full secret ARN
166+
SECRET_ARN="${{ secrets.AWS_SECRETS_ARN }}"
167+
168+
for secret in $(echo "$SECRETS_JSON" | jq -r '.[]'); do
169+
env_var_name="${SECRET_MAP[$secret]}"
170+
if [ -n "$env_var_name" ]; then
171+
SECRETS_STRING+="${env_var_name}=${SECRET_ARN}:${secret}::"$'\n'
172+
fi
173+
done
174+
175+
echo "Built secrets string:"
176+
echo "$SECRETS_STRING"
177+
178+
SECRETS_STRING=$(echo "$SECRETS_STRING" | sed '/^$/d')
179+
echo "secrets<<EOF" >> $GITHUB_OUTPUT
180+
echo "$SECRETS_STRING" >> $GITHUB_OUTPUT
181+
echo "EOF" >> $GITHUB_OUTPUT
182+
183+
- name: Replace EFS placeholders and inject roles in task definition
184+
id: inject-roles
185+
run: |
186+
TASK_DEF_PATH="${{ matrix.service.taskDef }}"
187+
188+
if [ ! -f "$TASK_DEF_PATH" ]; then
189+
echo "Task definition not found: $TASK_DEF_PATH - Skipping"
190+
echo "skip=true" >> $GITHUB_OUTPUT
191+
exit 0
192+
fi
193+
194+
echo "Processing task definition: $TASK_DEF_PATH"
195+
196+
# Step 1: Replace placeholders (EFS, CloudWatch)
197+
LOG_GROUP_NAME="dms-${{ inputs.environment == 'prod' && 'prod' || 'preprod' }}/ecs"
198+
199+
sed -i.bak \
200+
-e 's/EFS_FILE_SYSTEM_ID/${{ steps.fetch-secrets.outputs.efs-fs-id }}/g' \
201+
-e 's/EFS_ACCESS_POINT_ID/${{ steps.fetch-secrets.outputs.efs-ap-id }}/g' \
202+
-e "s|PLACEHOLDER|$LOG_GROUP_NAME|g" \
203+
"$TASK_DEF_PATH"
204+
205+
# Step 2: Inject IAM roles and set family name to match Terraform convention
206+
FAMILY_NAME="dms-${{ inputs.environment == 'prod' && 'prod' || 'preprod' }}-${{ matrix.service.name }}-task"
207+
208+
UPDATED_TASK_DEF=$(jq \
209+
--arg task_role "${{ steps.fetch-secrets.outputs.task_role }}" \
210+
--arg exec_role "${{ steps.fetch-secrets.outputs.exec_role }}" \
211+
--arg family "$FAMILY_NAME" \
212+
'.taskRoleArn = $task_role | .executionRoleArn = $exec_role | .family = $family' \
213+
"$TASK_DEF_PATH")
214+
215+
UPDATED_TASK_DEF_FILE="rendered-task-def.json"
216+
echo "$UPDATED_TASK_DEF" > "$UPDATED_TASK_DEF_FILE"
217+
218+
echo "Updated task definition:"
219+
cat "$UPDATED_TASK_DEF_FILE" | jq '.'
220+
221+
echo "updated_task_def_file=$UPDATED_TASK_DEF_FILE" >> $GITHUB_OUTPUT
222+
echo "skip=false" >> $GITHUB_OUTPUT
223+
224+
- name: Render ECS task definition with secrets
225+
id: render-task-def
226+
uses: aws-actions/amazon-ecs-render-task-definition@77954e213ba1f9f9cb016b86a1d4f6fcdea0d57e # v1.8.4
227+
with:
228+
task-definition: ${{ steps.inject-roles.outputs.updated_task_def_file }}
229+
container-name: ${{ matrix.service.container }}
230+
image: ${{ steps.build-image.outputs.IMAGE }}
231+
environment-variables: |
232+
DOTNET_ENVIRONMENT=${{ inputs.environment == 'prod' && 'Production' || 'PreProduction' }}
233+
secrets: ${{ steps.build-secrets.outputs.secrets }}
234+
235+
- name: Deploy ECS service
236+
if: steps.build-image.outputs.skip != 'true'
237+
uses: aws-actions/amazon-ecs-deploy-task-definition@fc8fc60f3a60ffd500fcb13b209c59d221ac8c8c # v2.6.1
238+
with:
239+
task-definition: ${{ steps.render-task-def.outputs.task-definition }}
240+
service: dms-${{ inputs.environment == 'prod' && 'prod' || 'preprod' }}-${{ matrix.service.name }}-service
241+
cluster: dms-${{ inputs.environment == 'prod' && 'prod' || 'preprod' }}-cluster
242+
wait-for-service-stability: true

.github/workflows/deploy.yml

Lines changed: 25 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -6,71 +6,30 @@ on:
66
- main
77

88
permissions:
9-
id-token: write # required for OIDC authentication with AWS
9+
id-token: write
10+
contents: read
1011

1112
jobs:
12-
deploy:
13-
runs-on: ubuntu-latest
14-
15-
environment: ${{ github.ref_name == 'main' && 'prod' || 'dev' }}
16-
17-
env:
18-
REPOSITORY: dms_visualiser
19-
IMAGE_TAG: ${{ github.ref_name == 'main' && 'prod' || 'dev' }}-${{ github.sha }}
20-
21-
steps:
22-
- name: Checkout
23-
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
24-
25-
- name: Configure AWS credentials
26-
uses: aws-actions/configure-aws-credentials@8df5847569e6427dd6c4fb1cf565c83acfa8afa7 # v6.0.0
27-
with:
28-
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
29-
aws-region: eu-west-2
30-
31-
- name: Login to Amazon ECR
32-
id: login-ecr
33-
uses: aws-actions/amazon-ecr-login@183a1442edf41672e66566b7fc560e297a290896 # v2.1.1
34-
35-
- name: Setup .NET
36-
uses: actions/setup-dotnet@c2fa09f4bde5ebb9d1777cf28262a3eb3db3ced7 # v5.2.0
37-
with:
38-
dotnet-version: 10.0.x
39-
40-
- name: Run tests
41-
run: dotnet test --configuration Release
42-
43-
- name: Build and Publish Image
44-
id: build-image
45-
run: |
46-
dotnet publish src/Visualiser/Visualiser.csproj \
47-
--configuration Release \
48-
--target:PublishContainer \
49-
--property:ContainerRegistry=${{ steps.login-ecr.outputs.registry }} \
50-
--property:ContainerRepository=$REPOSITORY \
51-
--property:ContainerImageTag=$IMAGE_TAG
52-
53-
IMAGE=${{ steps.login-ecr.outputs.registry }}/${REPOSITORY}:${IMAGE_TAG}
54-
echo "IMAGE=${IMAGE}" >> $GITHUB_OUTPUT
55-
56-
- name: Render ECS task definition
57-
id: render-task-def
58-
uses: aws-actions/amazon-ecs-render-task-definition@77954e213ba1f9f9cb016b86a1d4f6fcdea0d57e # v1.8.4
59-
with:
60-
task-definition: infra/dms-visualiser-task-def.json
61-
container-name: dms-visualiser-container
62-
image: ${{ steps.build-image.outputs.image }}
63-
environment-variables: |
64-
VERY_IMPORTANT=DO_NOT_DELETE_ME
65-
DOTNET_ENVIRONMENT=PreProduction
66-
secrets: |
67-
AzureAd__ClientId=${{ secrets.AWS_SECRETS_ARN }}:Client_ID::
68-
AzureAd__ClientSecret=${{ secrets.AWS_SECRETS_ARN }}:Client_Secret::
69-
API__BaseUrl=${{ secrets.AWS_SECRETS_ARN }}:API_Base_URL::
70-
71-
- name: Deploy ECS service
72-
uses: aws-actions/amazon-ecs-deploy-task-definition@fc8fc60f3a60ffd500fcb13b209c59d221ac8c8c # v2.6.1
73-
with:
74-
task-definition: ${{ steps.render-task-def.outputs.task-definition }}
75-
service: dms-visualiser-service-1
76-
cluster: dms-visualiser-cluster
13+
deploy-preprod:
14+
if: github.ref == 'refs/heads/develop'
15+
uses: ./.github/workflows/deploy-reusable.yml
16+
with:
17+
environment: preprod
18+
aws-region: eu-west-2
19+
secrets:
20+
AWS_ROLE_ARN: ${{ secrets.AWS_ROLE_ARN }}
21+
AWS_SECRETS_ARN: ${{ secrets.AWS_SECRETS_ARN }}
22+
AWS_TASK_ROLE_ARN: ${{ secrets.AWS_TASK_ROLE_ARN }}
23+
AWS_EXECUTION_ROLE_ARN: ${{ secrets.AWS_EXECUTION_ROLE_ARN }}
24+
25+
deploy-prod:
26+
if: github.ref == 'refs/heads/main'
27+
uses: ./.github/workflows/deploy-reusable.yml
28+
with:
29+
environment: prod
30+
aws-region: eu-west-2
31+
secrets:
32+
AWS_ROLE_ARN: ${{ secrets.AWS_ROLE_ARN }}
33+
AWS_SECRETS_ARN: ${{ secrets.AWS_SECRETS_ARN }}
34+
AWS_TASK_ROLE_ARN: ${{ secrets.AWS_TASK_ROLE_ARN }}
35+
AWS_EXECUTION_ROLE_ARN: ${{ secrets.AWS_EXECUTION_ROLE_ARN }}

0 commit comments

Comments
 (0)