Skip to content

Commit e6ceda6

Browse files
[PRMP-1188] Create Lambda to handle MNS notifications (#212)
1 parent a532008 commit e6ceda6

File tree

10 files changed

+219
-17
lines changed

10 files changed

+219
-17
lines changed

infrastructure/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@
8787
| <a name="module_manage-nrl-pointer-alarm"></a> [manage-nrl-pointer-alarm](#module\_manage-nrl-pointer-alarm) | ./modules/lambda_alarms | n/a |
8888
| <a name="module_manage-nrl-pointer-alarm-topic"></a> [manage-nrl-pointer-alarm-topic](#module\_manage-nrl-pointer-alarm-topic) | ./modules/sns | n/a |
8989
| <a name="module_manage-nrl-pointer-lambda"></a> [manage-nrl-pointer-lambda](#module\_manage-nrl-pointer-lambda) | ./modules/lambda | n/a |
90+
| <a name="module_mns-notification-alarm"></a> [mns-notification-alarm](#module\_mns-notification-alarm) | ./modules/lambda_alarms | n/a |
91+
| <a name="module_mns-notification-alarm-topic"></a> [mns-notification-alarm-topic](#module\_mns-notification-alarm-topic) | ./modules/sns | n/a |
92+
| <a name="module_mns-notification-lambda"></a> [mns-notification-lambda](#module\_mns-notification-lambda) | ./modules/lambda | n/a |
93+
| <a name="module_mns_encryption_key"></a> [mns\_encryption\_key](#module\_mns\_encryption\_key) | ./modules/kms | n/a |
9094
| <a name="module_ndr-app-config"></a> [ndr-app-config](#module\_ndr-app-config) | ./modules/app_config | n/a |
9195
| <a name="module_ndr-bulk-staging-store"></a> [ndr-bulk-staging-store](#module\_ndr-bulk-staging-store) | ./modules/s3/ | n/a |
9296
| <a name="module_ndr-docker-ecr-ui"></a> [ndr-docker-ecr-ui](#module\_ndr-docker-ecr-ui) | ./modules/ecr/ | n/a |
@@ -119,6 +123,7 @@
119123
| <a name="module_sns_encryption_key"></a> [sns\_encryption\_key](#module\_sns\_encryption\_key) | ./modules/kms | n/a |
120124
| <a name="module_sqs-lg-bulk-upload-invalid-queue"></a> [sqs-lg-bulk-upload-invalid-queue](#module\_sqs-lg-bulk-upload-invalid-queue) | ./modules/sqs | n/a |
121125
| <a name="module_sqs-lg-bulk-upload-metadata-queue"></a> [sqs-lg-bulk-upload-metadata-queue](#module\_sqs-lg-bulk-upload-metadata-queue) | ./modules/sqs | n/a |
126+
| <a name="module_sqs-mns-notification-queue"></a> [sqs-mns-notification-queue](#module\_sqs-mns-notification-queue) | ./modules/sqs | n/a |
122127
| <a name="module_sqs-nems-queue"></a> [sqs-nems-queue](#module\_sqs-nems-queue) | ./modules/sqs | n/a |
123128
| <a name="module_sqs-nrl-queue"></a> [sqs-nrl-queue](#module\_sqs-nrl-queue) | ./modules/sqs | n/a |
124129
| <a name="module_sqs-splunk-queue"></a> [sqs-splunk-queue](#module\_sqs-splunk-queue) | ./modules/sqs | n/a |
@@ -181,6 +186,7 @@
181186
| [aws_iam_policy.dynamodb_policy_scan_bulk_report](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
182187
| [aws_iam_policy.dynamodb_stream_manifest](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
183188
| [aws_iam_policy.dynamodb_stream_stitch_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
189+
| [aws_iam_policy.kms_lambda_access](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
184190
| [aws_iam_policy.lambda_audit_splunk_sqs_queue_send_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
185191
| [aws_iam_policy.s3_document_data_policy_for_manifest_lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
186192
| [aws_iam_policy.s3_document_data_policy_for_stitch_lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
@@ -219,6 +225,7 @@
219225
| [aws_lambda_event_source_mapping.bulk_upload_lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_event_source_mapping) | resource |
220226
| [aws_lambda_event_source_mapping.dynamodb_stream_manifest](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_event_source_mapping) | resource |
221227
| [aws_lambda_event_source_mapping.dynamodb_stream_stitch](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_event_source_mapping) | resource |
228+
| [aws_lambda_event_source_mapping.mns_notification_lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_event_source_mapping) | resource |
222229
| [aws_lambda_event_source_mapping.nems_message_lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_event_source_mapping) | resource |
223230
| [aws_lambda_event_source_mapping.nrl_pointer_lambda](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_event_source_mapping) | resource |
224231
| [aws_lambda_permission.bulk_upload_metadata_schedule_permission](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_permission) | resource |
@@ -232,6 +239,7 @@
232239
| [aws_security_group.ndr_mesh_sg](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/security_group) | resource |
233240
| [aws_sns_topic.alarm_notifications_topic](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic) | resource |
234241
| [aws_sns_topic_subscription.alarm_notifications_sns_topic_subscription](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_subscription) | resource |
242+
| [aws_sqs_queue_policy.mns_sqs_access](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sqs_queue_policy) | resource |
235243
| [aws_sqs_queue_policy.nems_events_subscription](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sqs_queue_policy) | resource |
236244
| [aws_ssm_parameter.nems_events_observability](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource |
237245
| [aws_ssm_parameter.nems_events_topic_arn](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ssm_parameter) | resource |
@@ -264,6 +272,7 @@
264272
| [aws_ssm_parameter.backup_target_account](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source |
265273
| [aws_ssm_parameter.cloud_security_notification_email_list](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source |
266274
| [aws_ssm_parameter.end_user_ods_code](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source |
275+
| [aws_ssm_parameter.mns_lambda_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source |
267276
| [aws_ssm_parameter.splunk_trusted_principal](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source |
268277
| [aws_ssm_parameter.target_backup_vault_arn](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/ssm_parameter) | data source |
269278

infrastructure/kms_sns.tf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,5 @@ module "sns_encryption_key" {
55
current_account_id = data.aws_caller_identity.current.account_id
66
environment = var.environment
77
owner = var.owner
8-
identifiers = ["sns.amazonaws.com", "cloudwatch.amazonaws.com"]
8+
service_identifiers = ["sns.amazonaws.com", "cloudwatch.amazonaws.com"]
99
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
module "mns-notification-lambda" {
2+
source = "./modules/lambda"
3+
name = "MNSNotificationLambda"
4+
handler = "handlers.mns_notification_handler.lambda_handler"
5+
iam_role_policy_documents = [
6+
module.sqs-mns-notification-queue.sqs_read_policy_document,
7+
module.sqs-mns-notification-queue.sqs_write_policy_document,
8+
module.lloyd_george_reference_dynamodb_table.dynamodb_write_policy_document,
9+
module.lloyd_george_reference_dynamodb_table.dynamodb_read_policy_document,
10+
aws_iam_policy.ssm_access_policy.policy,
11+
module.ndr-app-config.app_config_policy,
12+
aws_iam_policy.kms_lambda_access.policy,
13+
]
14+
rest_api_id = null
15+
api_execution_arn = null
16+
17+
lambda_environment_variables = {
18+
APPCONFIG_APPLICATION = module.ndr-app-config.app_config_application_id
19+
APPCONFIG_ENVIRONMENT = module.ndr-app-config.app_config_environment_id
20+
APPCONFIG_CONFIGURATION = module.ndr-app-config.app_config_configuration_profile_id
21+
WORKSPACE = terraform.workspace
22+
LLOYD_GEORGE_DYNAMODB_NAME = "${terraform.workspace}_${var.lloyd_george_dynamodb_table_name}"
23+
MNS_NOTIFICATION_QUEUE_URL = module.sqs-mns-notification-queue.sqs_url
24+
PDS_FHIR_IS_STUBBED = local.is_sandbox
25+
}
26+
27+
is_gateway_integration_needed = false
28+
is_invoked_from_gateway = false
29+
lambda_timeout = 900
30+
reserved_concurrent_executions = local.mns_notification_lambda_concurrent_limit
31+
}
32+
33+
resource "aws_lambda_event_source_mapping" "mns_notification_lambda" {
34+
event_source_arn = module.sqs-mns-notification-queue.endpoint
35+
function_name = module.mns-notification-lambda.lambda_arn
36+
37+
scaling_config {
38+
maximum_concurrency = local.mns_notification_lambda_concurrent_limit
39+
}
40+
}
41+
42+
module "mns-notification-alarm" {
43+
source = "./modules/lambda_alarms"
44+
lambda_function_name = module.mns-notification-lambda.function_name
45+
lambda_timeout = module.mns-notification-lambda.timeout
46+
lambda_name = "mns_notification_handler"
47+
namespace = "AWS/Lambda"
48+
alarm_actions = [module.mns-notification-alarm-topic.arn]
49+
ok_actions = [module.mns-notification-alarm-topic.arn]
50+
}
51+
52+
module "mns-notification-alarm-topic" {
53+
source = "./modules/sns"
54+
sns_encryption_key_id = module.sns_encryption_key.id
55+
current_account_id = data.aws_caller_identity.current.account_id
56+
topic_name = "mns-notification-topic"
57+
topic_protocol = "lambda"
58+
topic_endpoint = module.mns-notification-lambda.lambda_arn
59+
delivery_policy = jsonencode({
60+
"Version" : "2012-10-17",
61+
"Statement" : [
62+
{
63+
"Effect" : "Allow",
64+
"Principal" : {
65+
"Service" : "cloudwatch.amazonaws.com"
66+
},
67+
"Action" : [
68+
"SNS:Publish",
69+
],
70+
"Condition" : {
71+
"ArnLike" : {
72+
"aws:SourceArn" : "arn:aws:cloudwatch:eu-west-2:${data.aws_caller_identity.current.account_id}:alarm:*"
73+
}
74+
}
75+
"Resource" : "*"
76+
}
77+
]
78+
})
79+
}
80+
81+
resource "aws_iam_policy" "kms_lambda_access" {
82+
name = "mns_notification_lambda_access_policy"
83+
description = "KMS policy to allow lambda to read MNS SQS messages"
84+
85+
policy = jsonencode({
86+
Version = "2012-10-17"
87+
Statement = [
88+
{
89+
Action = [
90+
"kms:Decrypt",
91+
]
92+
Effect = "Allow"
93+
Resource = module.mns_encryption_key.kms_arn
94+
},
95+
]
96+
})
97+
}

infrastructure/mns.tf

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
data "aws_ssm_parameter" "mns_lambda_role" {
2+
name = "/ndr/${var.environment}/mns/lambda_role"
3+
}
4+
5+
6+
module "mns_encryption_key" {
7+
source = "./modules/kms"
8+
kms_key_name = "alias/mns-notification-encryption-key-kms-${terraform.workspace}"
9+
kms_key_description = "Custom KMS Key to enable server side encryption for mns subscriptions"
10+
current_account_id = data.aws_caller_identity.current.account_id
11+
environment = var.environment
12+
owner = var.owner
13+
service_identifiers = ["sns.amazonaws.com"]
14+
aws_identifiers = [data.aws_ssm_parameter.mns_lambda_role.value]
15+
allow_decrypt_for_arn = true
16+
}
17+
18+
module "sqs-mns-notification-queue" {
19+
source = "./modules/sqs"
20+
name = "mns-notification-queue"
21+
max_size_message = 256 * 1024 # allow message size up to 256 KB
22+
message_retention = 60 * 60 * 24 * 14 # 14 days
23+
environment = var.environment
24+
owner = var.owner
25+
max_visibility = 1020
26+
delay = 60
27+
enable_sse = null
28+
kms_master_key_id = module.mns_encryption_key.id
29+
}
30+
31+
resource "aws_sqs_queue_policy" "mns_sqs_access" {
32+
queue_url = module.sqs-mns-notification-queue.sqs_url
33+
34+
policy = jsonencode({
35+
Version = "2012-10-17"
36+
Statement = [
37+
{
38+
Effect = "Allow",
39+
Principal = {
40+
AWS = data.aws_ssm_parameter.mns_lambda_role.value
41+
},
42+
Action = "SQS:SendMessage",
43+
Resource = module.sqs-mns-notification-queue.sqs_arn
44+
}
45+
]
46+
})
47+
}

infrastructure/modules/dynamo_db/main.tf

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ resource "aws_iam_policy" "dynamodb_policy" {
5353
Statement = concat(
5454
[
5555
{
56-
"Effect" : "Allow",
57-
"Action" : [
56+
Effect : "Allow",
57+
Action : [
5858
"dynamodb:Query",
5959
"dynamodb:Scan",
6060
"dynamodb:GetItem",
@@ -63,18 +63,18 @@ resource "aws_iam_policy" "dynamodb_policy" {
6363
"dynamodb:DeleteItem",
6464
"dynamodb:BatchWriteItem",
6565
],
66-
"Resource" : [
66+
Resource : [
6767
aws_dynamodb_table.ndr_dynamodb_table.arn,
6868
]
6969
}
7070
],
7171
length(coalesce(var.global_secondary_indexes, [])) > 0 ? [
7272
{
73-
"Effect" : "Allow",
74-
"Action" : [
73+
Effect : "Allow",
74+
Action : [
7575
"dynamodb:Query",
7676
],
77-
"Resource" : [
77+
Resource : [
7878
for index in var.global_secondary_indexes :
7979
"${aws_dynamodb_table.ndr_dynamodb_table.arn}/index/${index.name}"
8080
]

infrastructure/modules/kms/README.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,19 +18,24 @@ No modules.
1818
|------|------|
1919
| [aws_kms_alias.encryption_key_alias](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_alias) | resource |
2020
| [aws_kms_key.encryption_key](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource |
21-
| [aws_iam_policy_document.kms_key_policy_doc](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
21+
| [aws_iam_policy_document.combined_policy_documents](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
22+
| [aws_iam_policy_document.kms_key_base](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
23+
| [aws_iam_policy_document.kms_key_generate](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
2224

2325
## Inputs
2426

2527
| Name | Description | Type | Default | Required |
2628
|------|-------------|------|---------|:--------:|
29+
| <a name="input_allow_decrypt_for_arn"></a> [allow\_decrypt\_for\_arn](#input\_allow\_decrypt\_for\_arn) | n/a | `bool` | `false` | no |
30+
| <a name="input_allowed_arn"></a> [allowed\_arn](#input\_allowed\_arn) | n/a | `list(string)` | `[]` | no |
31+
| <a name="input_aws_identifiers"></a> [aws\_identifiers](#input\_aws\_identifiers) | n/a | `list(string)` | `[]` | no |
2732
| <a name="input_current_account_id"></a> [current\_account\_id](#input\_current\_account\_id) | n/a | `string` | n/a | yes |
2833
| <a name="input_environment"></a> [environment](#input\_environment) | n/a | `string` | n/a | yes |
29-
| <a name="input_identifiers"></a> [identifiers](#input\_identifiers) | n/a | `list(string)` | n/a | yes |
3034
| <a name="input_kms_key_description"></a> [kms\_key\_description](#input\_kms\_key\_description) | n/a | `string` | n/a | yes |
3135
| <a name="input_kms_key_name"></a> [kms\_key\_name](#input\_kms\_key\_name) | n/a | `string` | n/a | yes |
3236
| <a name="input_kms_key_rotation_enabled"></a> [kms\_key\_rotation\_enabled](#input\_kms\_key\_rotation\_enabled) | n/a | `bool` | `true` | no |
3337
| <a name="input_owner"></a> [owner](#input\_owner) | n/a | `string` | n/a | yes |
38+
| <a name="input_service_identifiers"></a> [service\_identifiers](#input\_service\_identifiers) | n/a | `list(string)` | n/a | yes |
3439

3540
## Outputs
3641

infrastructure/modules/kms/main.tf

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
resource "aws_kms_key" "encryption_key" {
22
description = var.kms_key_description
3-
policy = data.aws_iam_policy_document.kms_key_policy_doc.json
3+
policy = data.aws_iam_policy_document.combined_policy_documents.json
44
enable_key_rotation = var.kms_key_rotation_enabled
55

66
tags = {
@@ -17,7 +17,7 @@ resource "aws_kms_alias" "encryption_key_alias" {
1717
}
1818

1919

20-
data "aws_iam_policy_document" "kms_key_policy_doc" {
20+
data "aws_iam_policy_document" "kms_key_base" {
2121
statement {
2222
effect = "Allow"
2323
principals {
@@ -30,13 +30,42 @@ data "aws_iam_policy_document" "kms_key_policy_doc" {
3030
statement {
3131
effect = "Allow"
3232
principals {
33-
identifiers = var.identifiers
33+
identifiers = var.service_identifiers
3434
type = "Service"
3535
}
3636
actions = [
3737
"kms:Decrypt",
3838
"kms:GenerateDataKey*"
3939
]
4040
resources = ["*"]
41+
dynamic "condition" {
42+
for_each = var.allow_decrypt_for_arn ? [1] : []
43+
content {
44+
test = "ArnEquals"
45+
values = var.allowed_arn
46+
variable = "aws:SourceArn"
47+
}
48+
}
49+
}
50+
}
51+
52+
data "aws_iam_policy_document" "kms_key_generate" {
53+
count = length(var.aws_identifiers) > 0 ? 1 : 0
54+
statement {
55+
effect = "Allow"
56+
principals {
57+
identifiers = var.aws_identifiers
58+
type = "AWS"
59+
}
60+
actions = ["kms:GenerateDataKey"]
61+
resources = ["*"]
4162
}
42-
}
63+
}
64+
65+
data "aws_iam_policy_document" "combined_policy_documents" {
66+
source_policy_documents = flatten([
67+
data.aws_iam_policy_document.kms_key_base.json,
68+
length(var.aws_identifiers) > 0 ? [data.aws_iam_policy_document.kms_key_generate[0].json] : []
69+
])
70+
}
71+

infrastructure/modules/kms/variable.tf

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,29 @@ variable "owner" {
2323
type = string
2424
}
2525

26-
variable "identifiers" {
26+
variable "service_identifiers" {
2727
type = list(string)
2828
}
2929

30+
variable "aws_identifiers" {
31+
type = list(string)
32+
default = []
33+
}
34+
35+
variable "allow_decrypt_for_arn" {
36+
type = bool
37+
default = false
38+
}
39+
40+
variable "allowed_arn" {
41+
type = list(string)
42+
default = []
43+
}
44+
3045
output "kms_arn" {
3146
value = aws_kms_key.encryption_key.arn
3247
}
3348

3449
output "id" {
3550
value = aws_kms_key.encryption_key.id
36-
}
51+
}

infrastructure/queues.tf

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,3 @@ module "sqs-lg-bulk-upload-invalid-queue" {
2929
owner = var.owner
3030
max_visibility = 1020
3131
}
32-

infrastructure/variable.tf

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -226,7 +226,8 @@ locals {
226226
is_force_destroy = contains(["ndr-dev", "ndra", "ndrb", "ndrc", "ndrd", "ndr-test"], terraform.workspace)
227227
is_sandbox_or_test = contains(["ndra", "ndrb", "ndrc", "ndrd", "ndr-test"], terraform.workspace)
228228

229-
bulk_upload_lambda_concurrent_limit = 5
229+
bulk_upload_lambda_concurrent_limit = 5
230+
mns_notification_lambda_concurrent_limit = 3
230231

231232
api_gateway_subdomain_name = contains(["prod"], terraform.workspace) ? "${var.certificate_subdomain_name_prefix}" : "${var.certificate_subdomain_name_prefix}${terraform.workspace}"
232233
api_gateway_full_domain_name = contains(["prod"], terraform.workspace) ? "${var.certificate_subdomain_name_prefix}${var.domain}" : "${var.certificate_subdomain_name_prefix}${terraform.workspace}.${var.domain}"

0 commit comments

Comments
 (0)