Skip to content

Commit 2853b72

Browse files
authored
Merge branch 'main' into PRM-134-v2
2 parents 4041084 + 10731f0 commit 2853b72

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+715
-171
lines changed

.github/workflows/terraform-dev-to-main-ci.yml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,16 @@ jobs:
7272
run: |
7373
terraform plan -input=false -no-color -var-file="${{vars.TF_VARS_FILE}}" -out tf.plan > plan_output.txt 2>&1
7474
terraform show -no-color tf.plan > tfplan.txt 2>&1
75+
76+
# Mask PEM certificates (BEGIN...END CERTIFICATE)
77+
awk 'BEGIN{cert=""}
78+
/-----BEGIN CERTIFICATE-----/{cert=$0; in_cert=1; next}
79+
/-----END CERTIFICATE-----/{cert=cert"\n"$0; print cert; cert=""; in_cert=0; next}
80+
in_cert{cert=cert"\n"$0}' tfplan.txt | while IFS= read -r cert_block; do
81+
if [ -n "$cert_block" ]; then
82+
echo "::add-mask::$cert_block"
83+
fi
84+
done || echo "No certificate blocks found to mask."
7585
7686
# Mask sensitive URLs in the Terraform Plan output
7787
grep -Eo 'https://[a-zA-Z0-9.-]+\.execute-api\.[a-zA-Z0-9.-]+\.amazonaws\.com/[a-zA-Z0-9/._-]*' tfplan.txt | while read -r api_url; do
@@ -125,6 +135,7 @@ jobs:
125135
PLAN_FULL=$(echo "$PLAN_FULL" | sed -E 's/[0-9]{12}/[REDACTED_AWS_ACCOUNT_ID]/g')
126136
PLAN_FULL=$(echo "$PLAN_FULL" | sed -E 's#https://[a-zA-Z0-9.-]+\.lambda\.amazonaws\.com/[a-zA-Z0-9/._-]+#[REDACTED_LAMBDA_URL]#g')
127137
PLAN_FULL=$(echo "$PLAN_FULL" | sed -E 's#https://[a-zA-Z0-9.-]+\.execute-api\.[a-zA-Z0-9.-]+\.amazonaws\.com/[a-zA-Z0-9/._-]*#[REDACTED_API_GATEWAY_URL]#g')
138+
PLAN_FULL=$(echo "$PLAN_FULL" | sed -E '/-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/s/.*/[REDACTED_PEM_CERT]/')
128139
129140
echo "PLAN<<EOF" >> $GITHUB_ENV
130141
echo "${PLAN_FULL::$LENGTH}" >> $GITHUB_ENV

infrastructure/README.md

Lines changed: 33 additions & 2 deletions
Large diffs are not rendered by default.

infrastructure/api-key-pdm.tf

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
resource "aws_api_gateway_usage_plan" "api_key_pdm" {
2+
name = "${terraform.workspace}_pdm-usage-plan"
3+
api_stages {
4+
api_id = aws_api_gateway_rest_api.ndr_doc_store_api.id
5+
stage = aws_api_gateway_stage.ndr_api.stage_name
6+
}
7+
}
8+
9+
resource "aws_api_gateway_api_key" "api_key_pdm" {
10+
name = "${terraform.workspace}_pdm-api-key"
11+
}
12+
13+
resource "aws_api_gateway_usage_plan_key" "api_key_pdm" {
14+
key_id = aws_api_gateway_api_key.api_key_pdm.id
15+
key_type = "API_KEY"
16+
usage_plan_id = aws_api_gateway_usage_plan.api_key_pdm.id
17+
}

infrastructure/api.tf

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ resource "aws_api_gateway_deployment" "ndr_api_deploy" {
4747
module.access-audit-lambda,
4848
module.back-channel-logout-gateway,
4949
module.back_channel_logout_lambda,
50-
module.create-doc-ref-gateway,
50+
module.document_reference_gateway,
5151
module.create-doc-ref-lambda,
5252
module.create-token-gateway,
5353
module.create-token-lambda,
@@ -91,7 +91,28 @@ resource "aws_api_gateway_stage" "ndr_api" {
9191
deployment_id = aws_api_gateway_deployment.ndr_api_deploy.id
9292
rest_api_id = aws_api_gateway_rest_api.ndr_doc_store_api.id
9393
stage_name = var.environment
94-
xray_tracing_enabled = false
94+
xray_tracing_enabled = var.enable_xray_tracing
95+
96+
depends_on = [aws_cloudwatch_log_group.api_gateway_stage]
97+
}
98+
99+
resource "aws_cloudwatch_log_group" "api_gateway_stage" {
100+
# Name must follow this format to allow execution logging
101+
# https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-logging.html
102+
name = "API-Gateway-Execution-Logs_${aws_api_gateway_rest_api.ndr_doc_store_api.id}/${var.environment}"
103+
retention_in_days = 0
104+
}
105+
106+
resource "aws_api_gateway_method_settings" "api_gateway_stage" {
107+
rest_api_id = aws_api_gateway_rest_api.ndr_doc_store_api.id
108+
stage_name = aws_api_gateway_stage.ndr_api.stage_name
109+
method_path = "*/*"
110+
111+
settings {
112+
logging_level = "INFO"
113+
metrics_enabled = true
114+
data_trace_enabled = true
115+
}
95116
}
96117

97118
resource "aws_api_gateway_gateway_response" "unauthorised_response" {
@@ -110,6 +131,10 @@ resource "aws_api_gateway_gateway_response" "unauthorised_response" {
110131
}
111132
}
112133

134+
resource "aws_api_gateway_client_certificate" "ndr_api" {
135+
description = "Client certificate used for backend authentication in HTTP integrations with the NDR API Gateway (${var.environment})"
136+
}
137+
113138
resource "aws_api_gateway_gateway_response" "bad_gateway_response" {
114139
rest_api_id = aws_api_gateway_rest_api.ndr_doc_store_api.id
115140
response_type = "DEFAULT_5XX"

infrastructure/backup-cross-account.tf

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ resource "aws_backup_selection" "cross_account_backup_selection" {
5858
module.document_reference_dynamodb_table.dynamodb_table_arn,
5959
module.lloyd_george_reference_dynamodb_table.dynamodb_table_arn,
6060
module.bulk_upload_report_dynamodb_table.dynamodb_table_arn,
61-
module.statistical-reports-store.bucket_arn
61+
module.statistical-reports-store.bucket_arn,
62+
module.pdm_dynamodb_table.dynamodb_table_arn,
63+
module.pdm-document-store.bucket_arn
6264
]
6365
}
6466

infrastructure/buckets.tf

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,22 +53,22 @@ module "ndr-lloyd-george-store" {
5353
cloudfront_enabled = true
5454
cloudfront_arn = module.cloudfront-distribution-lg.cloudfront_arn
5555
bucket_name = var.lloyd_george_bucket_name
56-
enable_cors_configuration = contains(["prod"], terraform.workspace) ? false : true
5756
enable_bucket_versioning = true
5857
environment = var.environment
5958
owner = var.owner
6059
force_destroy = local.is_force_destroy
60+
enable_cors_configuration = true
6161
cors_rules = [
6262
{
6363
allowed_headers = ["*"]
6464
allowed_methods = ["POST", "PUT", "DELETE"]
65-
allowed_origins = ["https://${terraform.workspace}.${var.domain}"]
65+
allowed_origins = [contains(["prod"], terraform.workspace) ? "https://${var.domain}" : "https://${terraform.workspace}.${var.domain}"]
6666
expose_headers = ["ETag"]
6767
max_age_seconds = 3000
6868
},
6969
{
7070
allowed_methods = ["GET"]
71-
allowed_origins = ["https://${terraform.workspace}.${var.domain}"]
71+
allowed_origins = [contains(["prod"], terraform.workspace) ? "https://${var.domain}" : "https://${terraform.workspace}.${var.domain}"]
7272
}
7373
]
7474
}
@@ -342,3 +342,25 @@ resource "aws_s3_bucket_logging" "logs_bucket_logging" {
342342
target_bucket = local.access_logs_bucket_id
343343
target_prefix = "${aws_s3_bucket.logs_bucket.id}/"
344344
}
345+
346+
module "pdm-document-store" {
347+
source = "./modules/s3/"
348+
access_logs_enabled = local.is_production
349+
access_logs_bucket_id = local.access_logs_bucket_id
350+
bucket_name = var.pdm_document_bucket_name
351+
enable_bucket_versioning = true
352+
environment = var.environment
353+
owner = var.owner
354+
force_destroy = local.is_force_destroy
355+
}
356+
357+
resource "aws_s3_bucket_lifecycle_configuration" "pdm_document_store" {
358+
bucket = module.pdm-document-store.bucket_id
359+
rule {
360+
id = "default-to-intelligent-tiering"
361+
status = "Enabled"
362+
transition {
363+
storage_class = "INTELLIGENT_TIERING"
364+
}
365+
}
366+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
locals {
2+
pds_tracking_lambdas = [
3+
"SearchPatientDetailsLambda",
4+
"BulkUploadLambda",
5+
"MNSNotificationLambda"
6+
]
7+
}
8+
9+
resource "aws_cloudwatch_log_metric_filter" "pds_tracker" {
10+
for_each = local.is_sandbox ? [] : toset(local.pds_tracking_lambdas)
11+
12+
name = "PDSUsageMetricFilter-${each.key}"
13+
pattern = "%NDR-TR1%"
14+
log_group_name = "/aws/lambda/${terraform.workspace}_${each.key}"
15+
16+
metric_transformation {
17+
name = "PDSEventCount"
18+
namespace = "NDRInsights"
19+
value = "1"
20+
}
21+
}

infrastructure/dev.tfvars

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,8 @@ cloudwatch_alarm_evaluation_periods = 5
88
poll_frequency = "3600"
99

1010
standalone_vpc_tag = "ndr-dev"
11-
standalone_vpc_ig_tag = "ndr-dev"
11+
standalone_vpc_ig_tag = "ndr-dev"
12+
13+
cloud_security_email_param_environment = "dev"
14+
15+
apim_environment = "internal-dev."

infrastructure/dynamo_db.tf

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -413,6 +413,78 @@ module "access_audit_dynamodb_table" {
413413
owner = var.owner
414414
}
415415

416+
module "pdm_dynamodb_table" {
417+
source = "./modules/dynamo_db"
418+
table_name = var.pdm_dynamodb_table_name
419+
hash_key = "ID"
420+
deletion_protection_enabled = local.is_production
421+
stream_enabled = true
422+
stream_view_type = "OLD_IMAGE"
423+
ttl_enabled = true
424+
ttl_attribute_name = "TTL"
425+
point_in_time_recovery_enabled = !local.is_sandbox
426+
427+
attributes = [
428+
{
429+
name = "ID"
430+
type = "S"
431+
},
432+
{
433+
name = "NhsNumber"
434+
type = "S"
435+
},
436+
{
437+
name = "DocumentSnomedCodeType"
438+
type = "S"
439+
},
440+
{
441+
name = "DocStatus"
442+
type = "S"
443+
},
444+
{
445+
name = "Author"
446+
type = "S"
447+
},
448+
{
449+
name = "Custodian"
450+
type = "S"
451+
}
452+
]
453+
454+
global_secondary_indexes = [
455+
456+
{
457+
name = "NhsNumberIndex"
458+
hash_key = "NhsNumber"
459+
projection_type = "ALL"
460+
},
461+
{
462+
name = "DocumentSnomedCodeTypeIndex"
463+
hash_key = "DocumentSnomedCodeType"
464+
projection_type = "ALL"
465+
},
466+
{
467+
name = "DocStatusIndex"
468+
hash_key = "DocStatus"
469+
projection_type = "ALL"
470+
},
471+
{
472+
name = "AuthorIndex"
473+
hash_key = "Author"
474+
projection_type = "ALL"
475+
},
476+
{
477+
name = "CustodianIndex"
478+
hash_key = "Custodian"
479+
projection_type = "ALL"
480+
}
481+
]
482+
483+
environment = var.environment
484+
owner = var.owner
485+
}
486+
487+
416488
module "alarm_state_history_table" {
417489
source = "./modules/dynamo_db"
418490
table_name = var.alarm_state_history_table_name
@@ -437,4 +509,4 @@ module "alarm_state_history_table" {
437509

438510
environment = var.environment
439511
owner = var.owner
440-
}
512+
}

infrastructure/firewall.tf

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,12 @@ resource "aws_wafv2_web_acl_association" "web_acl_association" {
1616
]
1717
}
1818

19+
resource "aws_wafv2_web_acl_association" "api_gateway" {
20+
resource_arn = aws_api_gateway_stage.ndr_api.arn
21+
web_acl_arn = module.firewall_waf_v2[0].arn
22+
count = local.is_sandbox ? 0 : 1
23+
depends_on = [
24+
aws_api_gateway_stage.ndr_api,
25+
module.firewall_waf_v2[0]
26+
]
27+
}

0 commit comments

Comments
 (0)