Skip to content

Commit 939cf9c

Browse files
Merge branch 'main' into tests/ELID-371-Suitability-Grouping-Issue
2 parents 7970aed + 9b7c3e8 commit 939cf9c

9 files changed

Lines changed: 770 additions & 105 deletions

File tree

.github/workflows/cicd-3-deploy.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ on:
1919
required: true
2020
type: choice
2121
options:
22-
- test
2322
- preprod
2423
- prod
2524
release_type:

.github/workflows/cicd-4-test.yaml

Lines changed: 101 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,129 @@
1-
name: "CI/CD E2E Tests"
1+
# Deploys a given tag to test environment
2+
# Does not tag or create a release
3+
4+
name: "CI/CD deploy to TEST"
5+
6+
concurrency:
7+
group: terraform-deploy-${{ github.event.inputs.environment }}
8+
cancel-in-progress: false
29

310
on:
411
workflow_dispatch:
512
inputs:
13+
tag:
14+
description: "This is the tag that is going to be deployed"
15+
required: true
16+
default: "latest"
617
environment:
7-
description: Target environment
18+
description: "Target environment (test only)"
819
required: true
20+
default: "test"
921
type: choice
10-
options: [dev, test, preprod]
22+
options:
23+
- test
1124

1225
jobs:
13-
listS3:
26+
metadata:
27+
name: "Set CI/CD metadata"
28+
runs-on: ubuntu-latest
29+
timeout-minutes: 1
30+
outputs:
31+
build_datetime: ${{ steps.variables.outputs.build_datetime }}
32+
build_timestamp: ${{ steps.variables.outputs.build_timestamp }}
33+
build_epoch: ${{ steps.variables.outputs.build_epoch }}
34+
nodejs_version: ${{ steps.variables.outputs.nodejs_version }}
35+
python_version: ${{ steps.variables.outputs.python_version }}
36+
terraform_version: ${{ steps.variables.outputs.terraform_version }}
37+
version: ${{ steps.variables.outputs.version }}
38+
tag: ${{ steps.variables.outputs.tag }}
39+
steps:
40+
- name: "Checkout tag"
41+
uses: actions/checkout@v5
42+
with:
43+
ref: ${{ github.event.inputs.tag }}
44+
45+
- name: "Set CI/CD variables"
46+
id: variables
47+
run: |
48+
datetime=$(date -u +'%Y-%m-%dT%H:%M:%S%z')
49+
echo "build_datetime=$datetime" >> $GITHUB_OUTPUT
50+
echo "build_timestamp=$(date --date=$datetime -u +'%Y%m%d%H%M%S')" >> $GITHUB_OUTPUT
51+
echo "build_epoch=$(date --date=$datetime -u +'%s')" >> $GITHUB_OUTPUT
52+
echo "nodejs_version=$(grep "^nodejs" .tool-versions | cut -f2 -d' ')" >> $GITHUB_OUTPUT
53+
echo "python_version=$(grep "^nodejs" .tool-versions | cut -f2 -d' ')" >> $GITHUB_OUTPUT
54+
echo "terraform_version=$(grep "^terraform" .tool-versions | cut -f2 -d' ')" >> $GITHUB_OUTPUT
55+
# TODO: Get the version, but it may not be the .version file as this should come from the CI/CD Pull Request Workflow
56+
echo "version=$(head -n 1 .version 2> /dev/null || echo unknown)" >> $GITHUB_OUTPUT
57+
echo "tag=${{ github.event.inputs.tag }}" >> $GITHUB_OUTPUT
58+
- name: "List variables"
59+
run: |
60+
export BUILD_DATETIME="${{ steps.variables.outputs.build_datetime }}"
61+
export BUILD_TIMESTAMP="${{ steps.variables.outputs.build_timestamp }}"
62+
export BUILD_EPOCH="${{ steps.variables.outputs.build_epoch }}"
63+
export NODEJS_VERSION="${{ steps.variables.outputs.nodejs_version }}"
64+
export PYTHON_VERSION="${{ steps.variables.outputs.python_version }}"
65+
export TERRAFORM_VERSION="${{ steps.variables.outputs.terraform_version }}"
66+
export VERSION="${{ steps.variables.outputs.version }}"
67+
export TAG="${{ steps.variables.outputs.tag }}"
68+
make list-variables
69+
deploy:
70+
name: "Deploy to an environment"
1471
runs-on: ubuntu-latest
72+
needs: [metadata]
1573
environment: ${{ inputs.environment }}
74+
timeout-minutes: 30
1675
permissions:
1776
id-token: write
18-
contents: read
19-
77+
contents: write
2078
steps:
21-
- name: Checkout
22-
uses: actions/checkout@v5
79+
- name: "Setup Terraform"
80+
uses: hashicorp/setup-terraform@v3
81+
with:
82+
terraform_version: ${{ needs.metadata.outputs.terraform_version }}
2383

24-
- name: Set up Python
84+
- name: "Set up Python"
2585
uses: actions/setup-python@v5
2686
with:
27-
python-version: "3.11"
87+
python-version: "3.13"
2888

29-
- name: Install Poetry
30-
run: |
31-
curl -sSL https://install.python-poetry.org | python3 -
32-
export PATH="$HOME/.local/bin:$PATH"
89+
- name: "Checkout Repository"
90+
uses: actions/checkout@v5
3391

34-
- name: Install dependencies with Poetry
92+
- name: "Build lambda artefact"
3593
run: |
36-
poetry install --no-root
94+
make dependencies install-python
95+
make build
96+
97+
- name: "Upload lambda artefact"
98+
uses: actions/upload-artifact@v4
99+
with:
100+
name: lambda
101+
path: dist/lambda.zip
37102

38-
- name: Configure AWS Credentials
103+
- name: "Download Built Lambdas"
104+
uses: actions/download-artifact@v5
105+
with:
106+
name: lambda
107+
path: ./build
108+
109+
- name: "Configure AWS Credentials"
39110
uses: aws-actions/configure-aws-credentials@v4
40111
with:
41112
role-to-assume: arn:aws:iam::${{ secrets.AWS_ACCOUNT_ID }}:role/service-roles/github-actions-api-deployment-role
42113
aws-region: eu-west-2
43114

44-
- name: List S3 bucket
45-
run: |
46-
aws s3 ls s3://eligibility-signposting-api-${{ inputs.environment }}-tfstate
115+
- name: "Terraform Apply"
116+
env:
117+
ENVIRONMENT: ${{ inputs.environment }}
118+
WORKSPACE: "default"
119+
TF_VAR_API_CA_CERT: ${{ secrets.API_CA_CERT }}
120+
TF_VAR_API_CLIENT_CERT: ${{ secrets.API_CLIENT_CERT }}
121+
TF_VAR_API_PRIVATE_KEY_CERT: ${{ secrets.API_PRIVATE_KEY_CERT }}
47122

48-
- name: Run Behave tests
49123
run: |
50-
mkdir -p reports
51-
poetry run behave --format json --outfile reports/behave-report.json
52-
53-
- name: Upload Behave test results
54-
uses: actions/upload-artifact@v4
55-
with:
56-
name: behave-test-results
57-
path: reports/
124+
mkdir -p ./build
125+
echo "Running: make terraform env=$ENVIRONMENT workspace=$WORKSPACE stack=networking tf-command=apply"
126+
make terraform env=$ENVIRONMENT stack=networking tf-command=apply workspace=$WORKSPACE
127+
echo "Running: make terraform env=$ENVIRONMENT workspace=$WORKSPACE stack=api-layer tf-command=apply"
128+
make terraform env=$ENVIRONMENT stack=api-layer tf-command=apply workspace=$WORKSPACE
129+
working-directory: ./infrastructure

infrastructure/stacks/api-layer/assumed_role_permissions_boundary.tf

Lines changed: 47 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -6,35 +6,53 @@ data "aws_iam_policy_document" "assumed_role_permissions_boundary" {
66
effect = "Allow"
77

88
actions = [
9-
"acm:*",
10-
"application-autoscaling:*",
11-
"apigateway:*",
12-
"cloudtrail:*",
13-
"cloudwatch:*",
14-
"config:*",
15-
"dynamodb:*",
16-
"ec2:*",
17-
"events:*",
18-
"firehose:*",
19-
"glue:*",
20-
"health:*",
21-
"iam:*",
22-
"kms:*",
23-
"lambda:*",
24-
"logs:*",
25-
"network-firewall:*",
26-
"pipes:*",
27-
"s3:*",
28-
"schemas:*",
29-
"sns:*",
30-
"servicequotas:*",
31-
"ssm:*",
32-
"states:*",
33-
"support:*",
34-
"sqs:*",
35-
"tag:*",
36-
"trustedadvisor:*",
37-
"xray:*"
9+
# DynamoDB - table operations for Lambda and external write roles
10+
"dynamodb:GetItem",
11+
"dynamodb:Query",
12+
"dynamodb:Scan",
13+
"dynamodb:PutItem",
14+
"dynamodb:UpdateItem",
15+
"dynamodb:DeleteItem",
16+
"dynamodb:BatchWriteItem",
17+
18+
# S3 - bucket and object operations for Lambda and Firehose
19+
"s3:GetObject",
20+
"s3:ListBucket",
21+
"s3:PutObject",
22+
"s3:PutObjectAcl",
23+
"s3:AbortMultipartUpload",
24+
"s3:GetBucketLocation",
25+
"s3:ListBucketMultipartUploads",
26+
27+
# KMS - encryption/decryption for DynamoDB and S3
28+
"kms:Encrypt",
29+
"kms:Decrypt",
30+
"kms:ReEncrypt*",
31+
"kms:GenerateDataKey",
32+
"kms:GenerateDataKey*",
33+
"kms:DescribeKey",
34+
35+
# CloudWatch Logs - Lambda execution and Firehose logging
36+
"logs:CreateLogGroup",
37+
"logs:CreateLogStream",
38+
"logs:PutLogEvents",
39+
"logs:DescribeLogGroups",
40+
"logs:DescribeLogStreams",
41+
42+
# EC2 - VPC access for Lambda (from AWSLambdaVPCAccessExecutionRole)
43+
"ec2:CreateNetworkInterface",
44+
"ec2:DescribeNetworkInterfaces",
45+
"ec2:DeleteNetworkInterface",
46+
"ec2:AttachNetworkInterface",
47+
"ec2:DetachNetworkInterface",
48+
49+
# Kinesis Firehose - Lambda writing audit data
50+
"firehose:PutRecord",
51+
"firehose:PutRecordBatch",
52+
53+
# X-Ray - Lambda tracing
54+
"xray:PutTraceSegments",
55+
"xray:PutTelemetryRecords"
3856
]
3957

4058
resources = ["*"]

infrastructure/stacks/iams-developer-roles/github_actions_policies.tf

Lines changed: 60 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,8 @@ resource "aws_iam_policy" "s3_management" {
163163
"arn:aws:s3:::*eligibility-signposting-api-${var.environment}-truststore/*",
164164
"arn:aws:s3:::*eligibility-signposting-api-${var.environment}-truststore-access-logs",
165165
"arn:aws:s3:::*eligibility-signposting-api-${var.environment}-truststore-access-logs/*",
166+
"arn:aws:s3:::*eligibility-signposting-api-${var.environment}-eli-splunk-backup",
167+
"arn:aws:s3:::*eligibility-signposting-api-${var.environment}-eli-splunk-backup/*"
166168
]
167169
}
168170
]
@@ -196,20 +198,62 @@ resource "aws_iam_policy" "api_infrastructure" {
196198
# ACM for certs
197199
"acm:DescribeCertificate",
198200
"acm:GetCertificate",
199-
"acm:ListCertificates",
200-
# S3 for mTLS truststore
201-
"s3:GetObject",
202-
# CloudWatch Logs for logging
203-
"logs:CreateLogGroup",
204-
"logs:CreateLogStream",
205-
"logs:PutLogEvents",
206-
# IAM PassRole for logging role association (if needed)
207-
"iam:PassRole"
201+
"acm:ListCertificates"
208202

209203
],
210204
Resource = "*"
211205
#checkov:skip=CKV_AWS_289: Actions require wildcard resource
212206
},
207+
{
208+
Effect = "Allow",
209+
Action = [
210+
# CloudWatch Logs creation and management
211+
"logs:CreateLogGroup",
212+
"logs:CreateLogStream",
213+
"logs:PutLogEvents"
214+
],
215+
Resource = [
216+
# VPC Flow Logs
217+
"arn:aws:logs:${var.default_aws_region}:${data.aws_caller_identity.current.account_id}:log-group:/aws/vpc/*",
218+
# Lambda function logs
219+
"arn:aws:logs:${var.default_aws_region}:${data.aws_caller_identity.current.account_id}:log-group:/aws/lambda/*",
220+
# API Gateway logs
221+
"arn:aws:logs:${var.default_aws_region}:${data.aws_caller_identity.current.account_id}:log-group:/aws/apigateway/*",
222+
# Kinesis Firehose logs
223+
"arn:aws:logs:${var.default_aws_region}:${data.aws_caller_identity.current.account_id}:log-group:/aws/kinesisfirehose/*"
224+
]
225+
},
226+
{
227+
Effect = "Allow",
228+
Action = [
229+
# IAM PassRole for specific service roles only
230+
"iam:PassRole"
231+
],
232+
Resource = [
233+
# Lambda execution roles
234+
"arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/eligibility_lambda-role*",
235+
# API Gateway CloudWatch logging role
236+
"arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/*-api-gateway-*-role",
237+
# VPC Flow Logs role
238+
"arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/vpc-flow-logs-role*",
239+
# EventBridge to Firehose role
240+
"arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/eventbridge-firehose-role*",
241+
# Kinesis Firehose S3 backup roles
242+
"arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/*firehose*role*",
243+
"arn:aws:iam::${data.aws_caller_identity.current.account_id}:role/splunk-firehose-assume-role*"
244+
],
245+
Condition = {
246+
StringEquals = {
247+
"iam:PassedToService" = [
248+
"lambda.amazonaws.com",
249+
"apigateway.amazonaws.com",
250+
"vpc-flow-logs.amazonaws.com",
251+
"events.amazonaws.com",
252+
"firehose.amazonaws.com"
253+
]
254+
}
255+
}
256+
},
213257
{
214258
Effect = "Allow",
215259
Action = [
@@ -299,24 +343,22 @@ resource "aws_iam_policy" "kms_creation" {
299343
{
300344
Effect = "Allow",
301345
Action = [
346+
# Key creation and listing actions require wildcard resource
302347
"kms:CreateKey",
303-
"kms:DescribeKey",
304348
"kms:CreateAlias",
305349
"kms:List*",
306-
"kms:ListAliases",
307-
"kms:Decrypt",
308-
"kms:Encrypt",
309-
"kms:ReEncrypt*",
350+
"kms:ListAliases"
310351
],
311352
Resource = "*"
312353
},
313354
{
314355
Effect = "Allow",
315356
Action = [
357+
# Key management actions on account-specific keys only
358+
"kms:DescribeKey",
316359
"kms:Describe*",
317360
"kms:GetKeyPolicy*",
318361
"kms:GetKeyRotationStatus",
319-
"kms:Decrypt*",
320362
"kms:DeleteAlias",
321363
"kms:UpdateKeyDescription",
322364
"kms:CreateGrant",
@@ -325,8 +367,9 @@ resource "aws_iam_policy" "kms_creation" {
325367
"kms:ScheduleKeyDeletion",
326368
"kms:PutKeyPolicy",
327369
"kms:Encrypt",
328-
"kms:TagResource",
329-
"kms:GenerateDataKey",
370+
"kms:Decrypt",
371+
"kms:ReEncrypt*",
372+
"kms:GenerateDataKey"
330373
],
331374
Resource = [
332375
"arn:aws:kms:${var.default_aws_region}:${data.aws_caller_identity.current.account_id}:key/*",

0 commit comments

Comments
 (0)