11name : Reusable Deploy
22
33on :
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
1338jobs :
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
0 commit comments