Skip to content

Commit ec63149

Browse files
Merge branch 'main' into chore/SR-ELI-762-GitHubActions-CCOE-Compliance
2 parents e0ee462 + db099e3 commit ec63149

15 files changed

Lines changed: 1345 additions & 65 deletions

File tree

.github/workflows/base-deploy.yml

Lines changed: 234 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -153,21 +153,246 @@ jobs:
153153
./dist/lambda.zip \
154154
--region eu-west-2
155155
156-
- name: "Upload lambda artifact for the current workflow"
156+
- name: "Upload lambda artifact for downstream jobs"
157157
uses: actions/upload-artifact@v6
158158
with:
159159
name: lambda-${{ needs.metadata.outputs.tag }}
160160
path: ./dist/lambda.zip
161161

162-
deploy:
163-
name: "Deploy to ${{ needs.metadata.outputs.environment }}"
162+
sign-lambda-artifact:
163+
name: "Sign lambda artifact for PreProd"
164+
if: ${{ needs.metadata.outputs.environment == 'preprod' }}
165+
runs-on: ubuntu-latest
166+
needs: [ metadata, download-lambda-artifact ]
167+
timeout-minutes: 45
168+
permissions:
169+
id-token: write
170+
contents: read
171+
environment: preprod
172+
steps:
173+
- name: "Checkout repository at ref"
174+
uses: actions/checkout@v6
175+
with:
176+
ref: ${{ needs.metadata.outputs.ref }}
177+
fetch-depth: 0
178+
179+
- name: "Setup Terraform"
180+
uses: hashicorp/setup-terraform@v3
181+
with:
182+
terraform_version: ${{ needs.metadata.outputs.terraform_version }}
183+
184+
- name: "Configure AWS Credentials"
185+
uses: aws-actions/configure-aws-credentials@v6
186+
with:
187+
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/service-roles/github-actions-api-deployment-role
188+
aws-region: eu-west-2
189+
190+
- name: "Download unsigned lambda artifact from current workflow"
191+
uses: actions/download-artifact@v7
192+
with:
193+
name: lambda-${{ needs.metadata.outputs.tag }}
194+
path: ./dist
195+
196+
- name: "Terraform Init (PREPROD api-layer)"
197+
env:
198+
ENVIRONMENT: preprod
199+
WORKSPACE: "default"
200+
run: |
201+
echo "Running: make terraform env=$ENVIRONMENT workspace=$WORKSPACE stack=api-layer tf-command=init"
202+
make terraform env=$ENVIRONMENT stack=api-layer tf-command=init workspace=$WORKSPACE
203+
working-directory: ./infrastructure
204+
205+
- name: "Extract PREPROD Terraform outputs"
206+
id: preprod_tf_output
207+
run: |
208+
BUCKET=$(terraform output -raw lambda_artifact_bucket)
209+
PROFILE=$(terraform output -raw signing_profile_name)
210+
echo "bucket_name=$BUCKET" >> $GITHUB_OUTPUT
211+
echo "signing_profile_name=$PROFILE" >> $GITHUB_OUTPUT
212+
working-directory: ./infrastructure/stacks/api-layer
213+
214+
- name: "Upload unsigned lambda artifact to PREPROD S3"
215+
run: |
216+
aws s3 cp ./dist/lambda.zip \
217+
s3://${{ steps.preprod_tf_output.outputs.bucket_name }}/artifacts/${{ needs.metadata.outputs.tag }}/lambda.zip \
218+
--region eu-west-2
219+
220+
- name: "Get uploaded source object version"
221+
id: source_object
222+
run: |
223+
VERSION_ID=$(aws s3api head-object \
224+
--bucket "${{ steps.preprod_tf_output.outputs.bucket_name }}" \
225+
--key "artifacts/${{ needs.metadata.outputs.tag }}/lambda.zip" \
226+
--query 'VersionId' \
227+
--output text \
228+
--region eu-west-2)
229+
echo "version_id=$VERSION_ID" >> $GITHUB_OUTPUT
230+
231+
- name: "Start signing job"
232+
id: signing
233+
env:
234+
SIGNING_PROFILE_NAME: ${{ steps.preprod_tf_output.outputs.signing_profile_name }}
235+
run: |
236+
JOB_ID=$(aws signer start-signing-job \
237+
--source "s3={bucketName=${{ steps.preprod_tf_output.outputs.bucket_name }},key=artifacts/${{ needs.metadata.outputs.tag }}/lambda.zip,version=${{ steps.source_object.outputs.version_id }}}" \
238+
--destination "s3={bucketName=${{ steps.preprod_tf_output.outputs.bucket_name }},prefix=signed-artifacts/${{ needs.metadata.outputs.tag }}/}" \
239+
--profile-name "$SIGNING_PROFILE_NAME" \
240+
--query 'jobId' \
241+
--output text \
242+
--region eu-west-2)
243+
echo "job_id=$JOB_ID" >> $GITHUB_OUTPUT
244+
245+
- name: "Wait for signing job"
246+
run: |
247+
aws signer wait successful-signing-job \
248+
--job-id "${{ steps.signing.outputs.job_id }}" \
249+
--region eu-west-2
250+
251+
- name: "Resolve signed artifact location"
252+
id: signed_object
253+
run: |
254+
SIGNED_BUCKET=$(aws signer describe-signing-job \
255+
--job-id "${{ steps.signing.outputs.job_id }}" \
256+
--region eu-west-2 \
257+
--query 'signedObject.s3.bucketName' \
258+
--output text)
259+
260+
SIGNED_KEY=$(aws signer describe-signing-job \
261+
--job-id "${{ steps.signing.outputs.job_id }}" \
262+
--region eu-west-2 \
263+
--query 'signedObject.s3.key' \
264+
--output text)
265+
266+
echo "bucket_name=$SIGNED_BUCKET" >> $GITHUB_OUTPUT
267+
echo "object_key=$SIGNED_KEY" >> $GITHUB_OUTPUT
268+
269+
- name: "Download signed lambda artifact"
270+
run: |
271+
aws s3 cp \
272+
"s3://${{ steps.signed_object.outputs.bucket_name }}/${{ steps.signed_object.outputs.object_key }}" \
273+
./dist/lambda.zip \
274+
--region eu-west-2
275+
276+
- name: "Upload signed lambda artifact for current workflow"
277+
uses: actions/upload-artifact@v6
278+
with:
279+
name: lambda-signed-${{ needs.metadata.outputs.tag }}
280+
path: ./dist/lambda.zip
281+
282+
283+
deploy-preprod:
284+
name: "Deploy to preprod"
285+
if: ${{ needs.metadata.outputs.environment == 'preprod' }}
286+
runs-on: ubuntu-latest
287+
needs: [metadata, download-lambda-artifact, sign-lambda-artifact]
288+
timeout-minutes: 45
289+
permissions:
290+
id-token: write
291+
contents: write
292+
environment: preprod
293+
steps:
294+
- name: "Checkout repository at ref"
295+
uses: actions/checkout@v6
296+
with:
297+
ref: ${{ needs.metadata.outputs.ref }}
298+
fetch-depth: 0
299+
300+
- name: "Setup Terraform"
301+
uses: hashicorp/setup-terraform@v3
302+
with:
303+
terraform_version: ${{ needs.metadata.outputs.terraform_version }}
304+
305+
- name: "Download signed Lambda Artifact"
306+
uses: actions/download-artifact@v7
307+
with:
308+
name: lambda-signed-${{ needs.metadata.outputs.tag }}
309+
path: ./dist
310+
311+
- name: "Configure AWS Credentials (IAM Bootstrap Role)"
312+
uses: aws-actions/configure-aws-credentials@v6
313+
with:
314+
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/service-roles/github-actions-iam-bootstrap-role
315+
aws-region: eu-west-2
316+
317+
- name: "Deploy IAM roles (iams-developer-roles stack)"
318+
working-directory: ./infrastructure
319+
run: |
320+
make terraform env=preprod stack=iams-developer-roles tf-command=apply workspace=default
321+
322+
- name: "Configure AWS Credentials (Main Deployment Role)"
323+
uses: aws-actions/configure-aws-credentials@v6
324+
with:
325+
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/service-roles/github-actions-api-deployment-role
326+
aws-region: eu-west-2
327+
328+
- name: "Terraform Apply"
329+
env:
330+
ENVIRONMENT: preprod
331+
WORKSPACE: "default"
332+
TF_VAR_API_CA_CERT: ${{ secrets.API_CA_CERT }}
333+
TF_VAR_API_CLIENT_CERT: ${{ secrets.API_CLIENT_CERT }}
334+
TF_VAR_API_PRIVATE_KEY_CERT: ${{ secrets.API_PRIVATE_KEY_CERT }}
335+
TF_VAR_SPLUNK_HEC_TOKEN: ${{ secrets.SPLUNK_HEC_TOKEN }}
336+
TF_VAR_SPLUNK_HEC_ENDPOINT: ${{ secrets.SPLUNK_HEC_ENDPOINT }}
337+
TF_VAR_OPERATOR_EMAILS: ${{ vars.SECRET_ROTATION_OPERATOR_EMAILS }}
338+
TF_VAR_PROXYGEN_PRIVATE_KEY_PTL: ${{ secrets.PROXYGEN_PRIVATE_KEY_PTL }}
339+
TF_VAR_PROXYGEN_PRIVATE_KEY_PROD: ${{ secrets.PROXYGEN_PRIVATE_KEY_PROD }}
340+
working-directory: ./infrastructure
341+
shell: bash
342+
run: |
343+
set -euo pipefail
344+
mkdir -p ./build
345+
echo "Running: make terraform env=$ENVIRONMENT workspace=$WORKSPACE stack=networking tf-command=apply"
346+
make terraform env=$ENVIRONMENT stack=networking tf-command=apply workspace=$WORKSPACE
347+
echo "Running: make terraform env=$ENVIRONMENT workspace=$WORKSPACE stack=api-layer tf-command=apply"
348+
make terraform env=$ENVIRONMENT stack=api-layer tf-command=apply workspace=$WORKSPACE
349+
350+
- name: "Extract S3 bucket name from Terraform output"
351+
id: tf_output
352+
run: |
353+
BUCKET=$(terraform output -raw lambda_artifact_bucket)
354+
echo "bucket_name=$BUCKET" >> $GITHUB_OUTPUT
355+
working-directory: ./infrastructure/stacks/api-layer
356+
357+
- name: "Validate Feature Toggles"
358+
env:
359+
ENV: preprod
360+
run: |
361+
pip install boto3
362+
python scripts/feature_toggle/validate_toggles.py
363+
364+
- name: "Tag and Release"
365+
env:
366+
ENVIRONMENT: preprod
367+
REF: ${{ needs.metadata.outputs.ref }}
368+
INPUT_RELEASE_TYPE: ${{ inputs.release_type }}
369+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
370+
GITHUB_REPOSITORY: ${{ github.repository }}
371+
run: |
372+
pip install requests
373+
python scripts/workflow/tag_and_release.py
374+
375+
- name: "Capture release tag"
376+
id: release_tag
377+
run: |
378+
echo "release_tag=$(cat release_tag.txt)" >> $GITHUB_OUTPUT
379+
380+
- name: "Upload lambda artifact to S3"
381+
run: |
382+
aws s3 cp ./dist/lambda.zip \
383+
s3://${{ steps.tf_output.outputs.bucket_name }}/artifacts/${{ steps.release_tag.outputs.release_tag }}/lambda.zip \
384+
--region eu-west-2
385+
386+
deploy-prod:
387+
name: "Deploy to prod"
388+
if: ${{ needs.metadata.outputs.environment == 'prod' }}
164389
runs-on: ubuntu-latest
165390
needs: [metadata, download-lambda-artifact]
166391
timeout-minutes: 45
167392
permissions:
168393
id-token: write
169394
contents: write
170-
environment: ${{ needs.metadata.outputs.environment }}
395+
environment: prod
171396
steps:
172397
- name: "Checkout repository at ref"
173398
uses: actions/checkout@v6
@@ -195,7 +420,7 @@ jobs:
195420
- name: "Deploy IAM roles (iams-developer-roles stack)"
196421
working-directory: ./infrastructure
197422
run: |
198-
make terraform env=${{ needs.metadata.outputs.environment }} stack=iams-developer-roles tf-command=apply workspace=default
423+
make terraform env=prod stack=iams-developer-roles tf-command=apply workspace=default
199424
200425
- name: "Configure AWS Credentials (Main Deployment Role)"
201426
uses: aws-actions/configure-aws-credentials@v6
@@ -205,7 +430,7 @@ jobs:
205430

206431
- name: "Terraform Apply"
207432
env:
208-
ENVIRONMENT: ${{ needs.metadata.outputs.environment }}
433+
ENVIRONMENT: prod
209434
WORKSPACE: "default"
210435
TF_VAR_API_CA_CERT: ${{ secrets.API_CA_CERT }}
211436
TF_VAR_API_CLIENT_CERT: ${{ secrets.API_CLIENT_CERT }}
@@ -215,7 +440,6 @@ jobs:
215440
TF_VAR_OPERATOR_EMAILS: ${{ vars.SECRET_ROTATION_OPERATOR_EMAILS }}
216441
TF_VAR_PROXYGEN_PRIVATE_KEY_PTL: ${{ secrets.PROXYGEN_PRIVATE_KEY_PTL }}
217442
TF_VAR_PROXYGEN_PRIVATE_KEY_PROD: ${{ secrets.PROXYGEN_PRIVATE_KEY_PROD }}
218-
219443
working-directory: ./infrastructure
220444
shell: bash
221445
run: |
@@ -235,15 +459,14 @@ jobs:
235459

236460
- name: "Validate Feature Toggles"
237461
env:
238-
ENV: ${{ needs.metadata.outputs.environment }}
462+
ENV: prod
239463
run: |
240464
pip install boto3
241465
python scripts/feature_toggle/validate_toggles.py
242466
243467
- name: "Tag and Release"
244-
if: ${{ needs.metadata.outputs.environment == 'preprod' || needs.metadata.outputs.environment == 'prod' }}
245468
env:
246-
ENVIRONMENT: ${{ needs.metadata.outputs.environment }}
469+
ENVIRONMENT: prod
247470
REF: ${{ needs.metadata.outputs.ref }}
248471
INPUT_RELEASE_TYPE: ${{ inputs.release_type }}
249472
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -266,7 +489,7 @@ jobs:
266489
regression-tests:
267490
name: "Regression Tests"
268491
if: ${{ needs.metadata.outputs.environment == 'preprod' }}
269-
needs: deploy
492+
needs: deploy-preprod
270493
permissions:
271494
id-token: write
272495
contents: read

infrastructure/modules/lambda/lambda.tf

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
resource "aws_lambda_function" "eligibility_signposting_lambda" {
22
#checkov:skip=CKV_AWS_116: No deadletter queue is configured for this Lambda function, as the requests are synchronous
33
#checkov:skip=CKV_AWS_115: Concurrent execution limit will be set at APIM level, not at Lambda level
4-
#checkov:skip=CKV_AWS_272: Skipping code signing but flagged to create ticket to investigate on ELI-238
4+
#checkov:skip=CKV_AWS_272: Code signing not yet enforced in prod - tracked for removal when prod enforcement is enabled
55
# If the file is not in the current working directory you will need to include a
66
# path.module in the filename.
77
filename = var.file_name
@@ -11,6 +11,8 @@ resource "aws_lambda_function" "eligibility_signposting_lambda" {
1111

1212
source_code_hash = filebase64sha256(var.file_name)
1313

14+
code_signing_config_arn = contains(var.environments_with_signing, var.environment) ? aws_lambda_code_signing_config.signing_config.arn : null
15+
1416
runtime = var.runtime
1517
timeout = 30
1618
memory_size = 2048
@@ -39,10 +41,8 @@ resource "aws_lambda_function" "eligibility_signposting_lambda" {
3941
}
4042

4143
layers = compact([
42-
var.environment == "prod" || var.environment == "preprod" ?
43-
"arn:aws:lambda:${var.region}:580247275435:layer:LambdaInsightsExtension:${var.lambda_insights_extension_version}"
44-
:
45-
null
44+
# LambdaInsightsExtension excluded: incompatible with Lambda code signing enforcement.
45+
# AWS signs the layer with an internal profile not available via the API.
4646
])
4747

4848

@@ -66,4 +66,3 @@ resource "aws_lambda_provisioned_concurrency_config" "campaign_pc" {
6666
qualifier = aws_lambda_alias.campaign_alias[0].name
6767
provisioned_concurrent_executions = var.provisioned_concurrency_count
6868
}
69-

infrastructure/modules/lambda/variables.tf

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,6 @@ variable "provisioned_concurrency_count" {
7474
type = number
7575
}
7676

77-
variable "lambda_insights_extension_version" {
78-
description = "version number of LambdaInsightsExtension"
79-
type = number
80-
}
81-
8277
variable "api_domain_name" {
8378
description = "api domain name - env variable for status endpoint response"
8479
type = string
@@ -88,3 +83,9 @@ variable "hashing_secret_name" {
8883
description = "hashing secret name"
8984
type = string
9085
}
86+
87+
variable "environments_with_signing" {
88+
description = "List of environments where Lambda code signing is enabled; enforcement behaviour depends on the configured code signing policy"
89+
type = list(string)
90+
default = ["test"]
91+
}

infrastructure/stacks/api-layer/iam_policies.tf

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -250,12 +250,6 @@ resource "aws_iam_role_policy_attachment" "lambda_logs_policy_attachment" {
250250
policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
251251
}
252252

253-
#Attach CloudWatchLambdaInsightsExecutionRolePolicy to lambda for enhanced monitoring
254-
resource "aws_iam_role_policy_attachment" "lambda_insights_policy" {
255-
role = aws_iam_role.eligibility_lambda_role.name
256-
policy_arn = "arn:aws:iam::aws:policy/CloudWatchLambdaInsightsExecutionRolePolicy"
257-
}
258-
259253
# Policy document to read from Kinesis Source stream
260254
data "aws_iam_policy_document" "kinesis_source_access" {
261255
statement {

0 commit comments

Comments
 (0)