diff --git a/infrastructure/dns_email_auth.tf b/infrastructure/dns_email_auth.tf deleted file mode 100644 index 126bfbc1..00000000 --- a/infrastructure/dns_email_auth.tf +++ /dev/null @@ -1,9 +0,0 @@ -# resource "aws_route53_record" "dmarc" { -# count = local.is_shared_workspace ? 1 : 0 -# zone_id = module.route53_fargate_ui.zone_id -# name = "_dmarc.${var.domain}" -# type = "TXT" -# ttl = 300 -# -# records = ["v=DMARC1; p=none; adkim=s; aspf=s"] -# } diff --git a/infrastructure/iam.tf b/infrastructure/iam.tf index 71f445cf..b9d54aa6 100644 --- a/infrastructure/iam.tf +++ b/infrastructure/iam.tf @@ -355,37 +355,37 @@ resource "aws_iam_policy" "s3_document_data_policy_post_document_review_lambda" }) } -# data "aws_iam_policy_document" "reporting_ses" { -# statement { -# sid = "SESAccess" -# effect = "Allow" -# -# actions = [ -# "ses:SendEmail", -# "ses:SendRawEmail" -# ] -# -# resources = [ -# "arn:aws:ses:${var.region}:${data.aws_caller_identity.current.account_id}:identity/*", -# "arn:aws:ses:${var.region}:${data.aws_caller_identity.current.account_id}:configuration-set/${aws_ses_configuration_set.reporting.name}" -# ] -# -# condition { -# test = "StringEquals" -# variable = "ses:FromAddress" -# values = [local.reporting_ses_from_address_value] -# } -# } -# } -# -# data "aws_iam_policy_document" "ses_feedback_s3_put" { -# statement { -# effect = "Allow" -# actions = [ -# "s3:PutObject" -# ] -# resources = [ -# "${module.ses-feedback-store.bucket_arn}/ses-feedback/*" -# ] -# } -# } +data "aws_iam_policy_document" "reporting_ses" { + statement { + sid = "SESAccess" + effect = "Allow" + + actions = [ + "ses:SendEmail", + "ses:SendRawEmail" + ] + + resources = [ + "arn:aws:ses:${var.region}:${data.aws_caller_identity.current.account_id}:identity/*", + "arn:aws:ses:${var.region}:${data.aws_caller_identity.current.account_id}:configuration-set/${aws_ses_configuration_set.reporting.name}" + ] + + condition { + test = "StringEquals" + variable = "ses:FromAddress" + values = [module.ses.report_email_address] + } + } +} + +data "aws_iam_policy_document" "ses_feedback_s3_put" { + statement { + effect = "Allow" + actions = [ + "s3:PutObject" + ] + resources = [ + "${module.ses-feedback-store.bucket_arn}/ses-feedback/*" + ] + } +} diff --git a/infrastructure/kms_sns.tf b/infrastructure/kms_sns.tf index f1296348..aec8efb1 100644 --- a/infrastructure/kms_sns.tf +++ b/infrastructure/kms_sns.tf @@ -4,7 +4,6 @@ module "sns_encryption_key" { kms_key_description = "Custom KMS Key to enable server side encryption for sns subscriptions" environment = var.environment owner = var.owner - # service_identifiers = ["sns.amazonaws.com", "cloudwatch.amazonaws.com", "ses.amazonaws.com"] - service_identifiers = ["sns.amazonaws.com", "cloudwatch.amazonaws.com"] + service_identifiers = ["sns.amazonaws.com", "cloudwatch.amazonaws.com", "ses.amazonaws.com"] kms_deletion_window = var.kms_deletion_window } diff --git a/infrastructure/lambda-report-distribution.tf b/infrastructure/lambda-report-distribution.tf index 7ab0e5c3..4134dfe9 100644 --- a/infrastructure/lambda-report-distribution.tf +++ b/infrastructure/lambda-report-distribution.tf @@ -1,30 +1,30 @@ -# module "report-distribution-lambda" { -# source = "./modules/lambda" -# name = "ReportDistribution" -# handler = "handlers.report_distribution_handler.lambda_handler" -# lambda_timeout = 300 -# -# iam_role_policy_documents = [ -# module.ndr-report-store.s3_read_policy_document, -# module.bulk_upload_contact_lookup_table.dynamodb_read_policy_document, -# data.aws_iam_policy_document.reporting_ses.json, -# data.aws_iam_policy.aws_lambda_vpc_access_execution_role.policy, -# ] -# -# lambda_environment_variables = { -# APPCONFIG_APPLICATION = module.ndr-app-config.app_config_application_id -# APPCONFIG_ENVIRONMENT = module.ndr-app-config.app_config_environment_id -# APPCONFIG_CONFIGURATION = module.ndr-app-config.app_config_configuration_profile_id -# WORKSPACE = terraform.workspace -# -# REPORT_BUCKET_NAME = module.ndr-report-store.bucket_id -# CONTACT_TABLE_NAME = module.bulk_upload_contact_lookup_table.table_name -# -# PRM_MAILBOX_EMAIL = data.aws_ssm_parameter.prm_mailbox_email.value -# SES_FROM_ADDRESS = local.reporting_ses_from_address_value -# SES_CONFIGURATION_SET = aws_ses_configuration_set.reporting.name -# } -# -# is_gateway_integration_needed = false -# is_invoked_from_gateway = false -# } +module "report-distribution-lambda" { + source = "./modules/lambda" + name = "ReportDistribution" + handler = "handlers.report_distribution_handler.lambda_handler" + lambda_timeout = 300 + + iam_role_policy_documents = [ + module.ndr-report-store.s3_read_policy_document, + module.bulk_upload_contact_lookup_table.dynamodb_read_policy_document, + data.aws_iam_policy_document.reporting_ses.json, + data.aws_iam_policy.aws_lambda_vpc_access_execution_role.policy, + ] + + lambda_environment_variables = { + APPCONFIG_APPLICATION = module.ndr-app-config.app_config_application_id + APPCONFIG_ENVIRONMENT = module.ndr-app-config.app_config_environment_id + APPCONFIG_CONFIGURATION = module.ndr-app-config.app_config_configuration_profile_id + WORKSPACE = terraform.workspace + + REPORT_BUCKET_NAME = module.ndr-report-store.bucket_id + CONTACT_TABLE_NAME = module.bulk_upload_contact_lookup_table.table_name + + PRM_MAILBOX_EMAIL = data.aws_ssm_parameter.prm_mailbox_email.value + SES_FROM_ADDRESS = module.ses.report_email_address + SES_CONFIGURATION_SET = aws_ses_configuration_set.reporting.name + } + + is_gateway_integration_needed = false + is_invoked_from_gateway = false +} diff --git a/infrastructure/lambda-send-feedback.tf b/infrastructure/lambda-send-feedback.tf index 43f16c48..3a46ef96 100644 --- a/infrastructure/lambda-send-feedback.tf +++ b/infrastructure/lambda-send-feedback.tf @@ -105,7 +105,7 @@ module "send-feedback-lambda" { depends_on = [ aws_api_gateway_rest_api.ndr_doc_store_api, module.send-feedback-gateway, - module.ndr-feedback-mailbox, + module.ses, module.ndr-app-config ] } diff --git a/infrastructure/lambda-ses-feedback-monitor.tf b/infrastructure/lambda-ses-feedback-monitor.tf index 8ec7b88a..bb340f20 100644 --- a/infrastructure/lambda-ses-feedback-monitor.tf +++ b/infrastructure/lambda-ses-feedback-monitor.tf @@ -1,32 +1,28 @@ -# module "ses-feedback-monitor-lambda" { -# source = "./modules/lambda" -# name = "SesFeedbackMonitor" -# handler = "handlers.ses_feedback_monitor_handler.lambda_handler" -# lambda_timeout = 60 -# -# iam_role_policy_documents = [ -# data.aws_iam_policy_document.ses_feedback_s3_put.json, -# data.aws_iam_policy_document.reporting_ses.json, -# data.aws_iam_policy.aws_lambda_vpc_access_execution_role.policy, -# ] -# -# lambda_environment_variables = { -# APPCONFIG_APPLICATION = module.ndr-app-config.app_config_application_id -# APPCONFIG_ENVIRONMENT = module.ndr-app-config.app_config_environment_id -# APPCONFIG_CONFIGURATION = module.ndr-app-config.app_config_configuration_profile_id -# WORKSPACE = terraform.workspace -# -# SES_FEEDBACK_BUCKET_NAME = module.ses-feedback-store.bucket_id -# SES_FEEDBACK_PREFIX = "ses-feedback/" -# PRM_MAILBOX_EMAIL = data.aws_ssm_parameter.prm_mailbox_email.value -# SES_FROM_ADDRESS = local.reporting_ses_from_address_value -# ALERT_ON_EVENT_TYPES = "BOUNCE,REJECT" -# } -# -# is_gateway_integration_needed = false -# is_invoked_from_gateway = false -# -# depends_on = [ -# module.ses-feedback-store -# ] -# } +module "ses-feedback-monitor-lambda" { + source = "./modules/lambda" + name = "SesFeedbackMonitor" + handler = "handlers.ses_feedback_monitor_handler.lambda_handler" + lambda_timeout = 60 + + iam_role_policy_documents = [ + data.aws_iam_policy_document.ses_feedback_s3_put.json, + data.aws_iam_policy_document.reporting_ses.json, + data.aws_iam_policy.aws_lambda_vpc_access_execution_role.policy, + ] + + lambda_environment_variables = { + APPCONFIG_APPLICATION = module.ndr-app-config.app_config_application_id + APPCONFIG_ENVIRONMENT = module.ndr-app-config.app_config_environment_id + APPCONFIG_CONFIGURATION = module.ndr-app-config.app_config_configuration_profile_id + WORKSPACE = terraform.workspace + + SES_FEEDBACK_BUCKET_NAME = module.ses-feedback-store.bucket_id + SES_FEEDBACK_PREFIX = "ses-feedback/" + PRM_MAILBOX_EMAIL = data.aws_ssm_parameter.prm_mailbox_email.value + SES_FROM_ADDRESS = module.ses.report_email_address + ALERT_ON_EVENT_TYPES = "BOUNCE,REJECT" + } + + is_gateway_integration_needed = false + is_invoked_from_gateway = false +} diff --git a/infrastructure/modules/ses/README.md b/infrastructure/modules/ses/README.md index c2e1478f..44717710 100644 --- a/infrastructure/modules/ses/README.md +++ b/infrastructure/modules/ses/README.md @@ -42,21 +42,26 @@ module "ses_identity" { | Name | Type | |------|------| +| [aws_route53_record.dmarc](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource | | [aws_route53_record.ndr_ses_dkim_record](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource | +| [aws_route53_record.ses_mail_from_mx](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource | +| [aws_route53_record.ses_mail_from_spf](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource | | [aws_ses_domain_dkim.ndr_dkim](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ses_domain_dkim) | resource | | [aws_ses_domain_identity.ndr_ses](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ses_domain_identity) | resource | | [aws_ses_domain_identity_verification.ndr_ses_domain_verification](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ses_domain_identity_verification) | resource | +| [aws_ses_domain_mail_from.reporting](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ses_domain_mail_from) | resource | ## Inputs | Name | Description | Type | Default | Required | |------|-------------|------|---------|:--------:| | [domain](#input\_domain) | The root domain name to be registered with SES and used for verification. | `string` | n/a | yes | -| [domain\_prefix](#input\_domain\_prefix) | The subdomain or prefix used to construct the full SES identity domain. | `string` | n/a | yes | -| [enable](#input\_enable) | Whether to enable the creation of SES identity, DKIM, and DNS records. | `bool` | n/a | yes | +| [is\_sandbox](#input\_is\_sandbox) | Whether the workspace being created is a sandbox. | `bool` | n/a | yes | | [zone\_id](#input\_zone\_id) | The Route53 hosted zone ID where DNS verification records will be created. | `string` | n/a | yes | ## Outputs -No outputs. +| Name | Description | +|------|-------------| +| [report\_email\_address](#output\_report\_email\_address) | n/a | diff --git a/infrastructure/modules/ses/main.tf b/infrastructure/modules/ses/main.tf index 73df60e2..00216014 100644 --- a/infrastructure/modules/ses/main.tf +++ b/infrastructure/modules/ses/main.tf @@ -1,29 +1,65 @@ resource "aws_ses_domain_identity" "ndr_ses" { - domain = var.domain - count = var.enable ? 1 : 0 + domain = var.is_sandbox ? "${terraform.workspace}.dev.${var.domain}" : "${terraform.workspace}.${var.domain}" } resource "aws_ses_domain_dkim" "ndr_dkim" { - domain = aws_ses_domain_identity.ndr_ses[0].domain + domain = aws_ses_domain_identity.ndr_ses.domain - count = var.enable ? 1 : 0 - depends_on = [aws_ses_domain_identity.ndr_ses[0]] + depends_on = [aws_ses_domain_identity.ndr_ses] } resource "aws_route53_record" "ndr_ses_dkim_record" { + count = 3 + zone_id = var.zone_id - name = "${aws_ses_domain_dkim.ndr_dkim[0].dkim_tokens[count.index]}._domainkey.${var.domain_prefix}" + name = var.is_sandbox ? "${aws_ses_domain_dkim.ndr_dkim.dkim_tokens[count.index]}._domainkey.${terraform.workspace}.dev" : "${aws_ses_domain_dkim.ndr_dkim.dkim_tokens[count.index]}._domainkey.${terraform.workspace}" type = "CNAME" ttl = 1800 - records = ["${aws_ses_domain_dkim.ndr_dkim[0].dkim_tokens[count.index]}.dkim.amazonses.com"] + records = ["${aws_ses_domain_dkim.ndr_dkim.dkim_tokens[count.index]}.dkim.amazonses.com"] - count = var.enable ? 3 : 0 - depends_on = [aws_ses_domain_dkim.ndr_dkim[0]] + depends_on = [aws_ses_domain_dkim.ndr_dkim] } resource "aws_ses_domain_identity_verification" "ndr_ses_domain_verification" { - domain = aws_ses_domain_identity.ndr_ses[0].domain + domain = aws_ses_domain_identity.ndr_ses.domain + + depends_on = [aws_route53_record.ndr_ses_dkim_record] +} + +resource "aws_ses_domain_mail_from" "reporting" { + domain = aws_ses_domain_identity.ndr_ses.domain + mail_from_domain = "mail.${aws_ses_domain_identity.ndr_ses.domain}" + + behavior_on_mx_failure = "UseDefaultValue" +} + +resource "aws_route53_record" "ses_mail_from_mx" { + zone_id = var.zone_id + name = "mail.${aws_ses_domain_identity.ndr_ses.domain}" + type = "MX" + ttl = 600 + + records = [ + "10 feedback-smtp.eu-west-2.amazonses.com" + ] +} + +resource "aws_route53_record" "ses_mail_from_spf" { + zone_id = var.zone_id + name = "mail.${aws_ses_domain_identity.ndr_ses.domain}" + type = "TXT" + ttl = 600 + + records = [ + "v=spf1 include:amazonses.com -all" + ] +} + +resource "aws_route53_record" "dmarc" { + zone_id = var.zone_id + name = "_dmarc.${aws_ses_domain_identity.ndr_ses.domain}" + type = "TXT" + ttl = 300 - count = var.enable ? 1 : 0 - depends_on = [aws_route53_record.ndr_ses_dkim_record[0]] + records = ["v=DMARC1; p=none; adkim=s; aspf=s"] } diff --git a/infrastructure/modules/ses/moved-1.6.15.tf b/infrastructure/modules/ses/moved-1.6.15.tf new file mode 100644 index 00000000..07792817 --- /dev/null +++ b/infrastructure/modules/ses/moved-1.6.15.tf @@ -0,0 +1,14 @@ +moved { + from = aws_ses_domain_identity.ndr_ses[0] + to = aws_ses_domain_identity.ndr_ses +} + +moved { + from = aws_ses_domain_dkim.ndr_dkim[0] + to = aws_ses_domain_dkim.ndr_dkim +} + +moved { + from = aws_ses_domain_identity_verification.ndr_ses_domain_verification[0] + to = aws_ses_domain_identity_verification.ndr_ses_domain_verification +} diff --git a/infrastructure/modules/ses/output.tf b/infrastructure/modules/ses/output.tf new file mode 100644 index 00000000..c2193880 --- /dev/null +++ b/infrastructure/modules/ses/output.tf @@ -0,0 +1,3 @@ +output "report_email_address" { + value = "ndr-reports@${aws_ses_domain_identity.ndr_ses.domain}" +} diff --git a/infrastructure/modules/ses/variable.tf b/infrastructure/modules/ses/variable.tf index c60bc911..8234cebe 100644 --- a/infrastructure/modules/ses/variable.tf +++ b/infrastructure/modules/ses/variable.tf @@ -1,8 +1,3 @@ -variable "domain_prefix" { - description = "The subdomain or prefix used to construct the full SES identity domain." - type = string -} - variable "domain" { description = "The root domain name to be registered with SES and used for verification." type = string @@ -13,7 +8,7 @@ variable "zone_id" { type = string } -variable "enable" { - description = "Whether to enable the creation of SES identity, DKIM, and DNS records." +variable "is_sandbox" { + description = "Whether the workspace being created is a sandbox." type = bool } diff --git a/infrastructure/modules/sns/README.md b/infrastructure/modules/sns/README.md index 226ce239..9f2c4d9e 100644 --- a/infrastructure/modules/sns/README.md +++ b/infrastructure/modules/sns/README.md @@ -79,8 +79,10 @@ module "sns_topic" { | Name | Type | |------|------| | [aws_sns_topic.sns_topic](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic) | resource | +| [aws_sns_topic_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_policy) | resource | | [aws_sns_topic_subscription.sns_subscription_list](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_subscription) | resource | | [aws_sns_topic_subscription.sns_subscription_single](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/sns_topic_subscription) | resource | +| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | ## Inputs @@ -89,6 +91,7 @@ module "sns_topic" { | [delivery\_policy](#input\_delivery\_policy) | Attach delivery or IAM policy. (Legacy name; used as topic policy JSON in this module.) | `string` | n/a | yes | | [enable\_deduplication](#input\_enable\_deduplication) | Prevent content based duplication in notification queue. | `bool` | `false` | no | | [enable\_fifo](#input\_enable\_fifo) | Attach first in first out policy to notification queue. | `bool` | `false` | no | +| [enable\_ses\_publish](#input\_enable\_ses\_publish) | If true, module appends a statement allowing ses.amazonaws.com to SNS:Publish to this topic. | `bool` | `false` | no | | [is\_topic\_endpoint\_list](#input\_is\_topic\_endpoint\_list) | Whether to use the topic\_endpoint\_list instead of a single topic\_endpoint. | `bool` | `false` | no | | [raw\_message\_delivery](#input\_raw\_message\_delivery) | Whether to enable raw message delivery for the SNS subscription. | `bool` | `false` | no | | [sns\_encryption\_key\_id](#input\_sns\_encryption\_key\_id) | The ARN (or ID) of the KMS key used for encrypting the SNS topic. | `string` | n/a | yes | @@ -96,6 +99,7 @@ module "sns_topic" { | [topic\_endpoint](#input\_topic\_endpoint) | A single endpoint (e.g., SQS queue or Lambda function ARN) to subscribe to the topic. | `any` | `null` | no | | [topic\_endpoint\_list](#input\_topic\_endpoint\_list) | A list of endpoints (e.g., SQS ARNs) to subscribe to the topic. | `any` | `[]` | no | | [topic\_name](#input\_topic\_name) | Name of the SNS topic. | `string` | n/a | yes | +| [topic\_policy\_json](#input\_topic\_policy\_json) | Optional SNS topic access policy JSON. If set, it overrides delivery\_policy. | `string` | `null` | no | | [topic\_protocol](#input\_topic\_protocol) | The protocol to use for the subscription (e.g., 'sqs', 'lambda'). | `string` | n/a | yes | ## Outputs diff --git a/infrastructure/modules/sns/main.tf b/infrastructure/modules/sns/main.tf index 5fac11a7..a1b51ae6 100644 --- a/infrastructure/modules/sns/main.tf +++ b/infrastructure/modules/sns/main.tf @@ -1,49 +1,50 @@ -# locals { -# base_topic_policy_json = var.topic_policy_json != null ? var.topic_policy_json : var.delivery_policy -# base_topic_policy_obj = jsondecode(local.base_topic_policy_json) -# normalized_statements = [ -# for s in try(local.base_topic_policy_obj["Statement"], []) : merge( -# s, -# { -# Resource = ( -# try(s["Resource"], null) == "*" || try(s["Resource"], null) == null -# ? aws_sns_topic.sns_topic.arn -# : s["Resource"] -# ) -# } -# ) -# ] -# -# ses_publish_statement = var.enable_ses_publish ? { -# Sid = "AllowSESPublish" -# Effect = "Allow" -# Principal = { -# Service = "ses.amazonaws.com" -# } -# Action = "SNS:Publish" -# Resource = aws_sns_topic.sns_topic.arn -# Condition = { -# StringEquals = { -# "AWS:SourceAccount" = var.ses_source_account_id -# } -# } -# } : null -# -# effective_topic_policy_obj = merge( -# local.base_topic_policy_obj, -# { -# Statement = concat( -# local.normalized_statements, -# var.enable_ses_publish ? [local.ses_publish_statement] : [] -# ) -# } -# ) -# effective_topic_policy_json = jsonencode(local.effective_topic_policy_obj) -# } +data "aws_caller_identity" "current" {} + +locals { + base_topic_policy_json = var.topic_policy_json != null ? var.topic_policy_json : var.delivery_policy + base_topic_policy_obj = jsondecode(local.base_topic_policy_json) + normalized_statements = [ + for s in try(local.base_topic_policy_obj["Statement"], []) : merge( + s, + { + Resource = ( + try(s["Resource"], null) == "*" || try(s["Resource"], null) == null + ? aws_sns_topic.sns_topic.arn + : s["Resource"] + ) + } + ) + ] + + ses_publish_statement = var.enable_ses_publish ? { + Sid = "AllowSESPublish" + Effect = "Allow" + Principal = { + Service = "ses.amazonaws.com" + } + Action = "SNS:Publish" + Resource = aws_sns_topic.sns_topic.arn + Condition = { + StringEquals = { + "AWS:SourceAccount" = data.aws_caller_identity.current.account_id + } + } + } : null + + effective_topic_policy_obj = merge( + local.base_topic_policy_obj, + { + Statement = concat( + local.normalized_statements, + var.enable_ses_publish ? [local.ses_publish_statement] : [] + ) + } + ) + effective_topic_policy_json = jsonencode(local.effective_topic_policy_obj) +} resource "aws_sns_topic" "sns_topic" { name_prefix = "${terraform.workspace}-sns-${var.topic_name}" - policy = var.delivery_policy fifo_topic = var.enable_fifo content_based_deduplication = var.enable_deduplication kms_master_key_id = var.sns_encryption_key_id @@ -53,10 +54,10 @@ resource "aws_sns_topic" "sns_topic" { sqs_success_feedback_sample_rate = try(var.sqs_feedback.success_sample_rate, null) } -# resource "aws_sns_topic_policy" "this" { -# arn = aws_sns_topic.sns_topic.arn -# policy = local.effective_topic_policy_json -# } +resource "aws_sns_topic_policy" "this" { + arn = aws_sns_topic.sns_topic.arn + policy = local.effective_topic_policy_json +} resource "aws_sns_topic_subscription" "sns_subscription_single" { count = var.is_topic_endpoint_list ? 0 : 1 @@ -65,7 +66,7 @@ resource "aws_sns_topic_subscription" "sns_subscription_single" { endpoint = var.topic_endpoint raw_message_delivery = var.raw_message_delivery - # depends_on = [aws_sns_topic_policy.this] + depends_on = [aws_sns_topic_policy.this] } resource "aws_sns_topic_subscription" "sns_subscription_list" { @@ -75,7 +76,7 @@ resource "aws_sns_topic_subscription" "sns_subscription_list" { endpoint = each.value raw_message_delivery = var.raw_message_delivery - # depends_on = [aws_sns_topic_policy.this] + depends_on = [aws_sns_topic_policy.this] } output "arn" { diff --git a/infrastructure/modules/sns/variable.tf b/infrastructure/modules/sns/variable.tf index eb585720..428e3629 100644 --- a/infrastructure/modules/sns/variable.tf +++ b/infrastructure/modules/sns/variable.tf @@ -60,20 +60,14 @@ variable "is_topic_endpoint_list" { default = false } -# variable "topic_policy_json" { -# description = "Optional SNS topic access policy JSON. If set, it overrides delivery_policy." -# type = string -# default = null -# } -# -# variable "enable_ses_publish" { -# description = "If true, module appends a statement allowing ses.amazonaws.com to SNS:Publish to this topic." -# type = bool -# default = false -# } -# -# variable "ses_source_account_id" { -# description = "AWS account ID used in the AWS:SourceAccount condition for SES publishing." -# type = string -# default = "" -# } +variable "topic_policy_json" { + description = "Optional SNS topic access policy JSON. If set, it overrides delivery_policy." + type = string + default = null +} + +variable "enable_ses_publish" { + description = "If true, module appends a statement allowing ses.amazonaws.com to SNS:Publish to this topic." + type = bool + default = false +} diff --git a/infrastructure/moved-resources-v1.6.14.tf b/infrastructure/moved-resources-v1.6.14.tf deleted file mode 100644 index daae1209..00000000 --- a/infrastructure/moved-resources-v1.6.14.tf +++ /dev/null @@ -1,4 +0,0 @@ -moved { - from = module.route53_fargate_ui.aws_route53_record.ndr_fargate_record - to = module.route53_fargate_ui.aws_route53_record.ndr_fargate_record_cname[0] -} diff --git a/infrastructure/moved-resources-v1.6.15.tf b/infrastructure/moved-resources-v1.6.15.tf new file mode 100644 index 00000000..660190c5 --- /dev/null +++ b/infrastructure/moved-resources-v1.6.15.tf @@ -0,0 +1,4 @@ +moved { + from = module.ndr-feedback-mailbox + to = module.ses +} diff --git a/infrastructure/ses.tf b/infrastructure/ses.tf index f133de78..dcfbad93 100644 --- a/infrastructure/ses.tf +++ b/infrastructure/ses.tf @@ -1,12 +1,6 @@ -locals { - domain_prefix = terraform.workspace == "ndr-test" ? "" : terraform.workspace - domain = terraform.workspace == "ndr-test" ? var.domain : "${terraform.workspace}.${var.domain}" -} - -module "ndr-feedback-mailbox" { - source = "./modules/ses" - domain_prefix = local.domain_prefix - domain = local.domain - zone_id = module.route53_fargate_ui.zone_id - enable = !local.is_sandbox +module "ses" { + source = "./modules/ses" + domain = var.domain + zone_id = module.route53_fargate_ui.zone_id + is_sandbox = local.is_sandbox } diff --git a/infrastructure/ses_feedback_events.tf b/infrastructure/ses_feedback_events.tf index b578c535..4805a65d 100644 --- a/infrastructure/ses_feedback_events.tf +++ b/infrastructure/ses_feedback_events.tf @@ -1,23 +1,22 @@ -# resource "aws_ses_configuration_set" "reporting" { -# name = "${terraform.workspace}-reporting" -# } -# -# resource "aws_ses_event_destination" "reporting_to_sns" { -# name = "sns-feedback" -# configuration_set_name = aws_ses_configuration_set.reporting.name -# enabled = true -# -# matching_types = [ -# "bounce", -# "reject" -# ] -# -# sns_destination { -# topic_arn = module.ses_feedback_topic.arn -# } -# -# depends_on = [ -# module.ses_feedback_topic, -# aws_lambda_permission.allow_sns_invoke_ses_feedback_monitor -# ] -# } +resource "aws_ses_configuration_set" "reporting" { + name = "${terraform.workspace}-reporting" +} + +resource "aws_ses_event_destination" "reporting_to_sns" { + name = "sns-feedback" + configuration_set_name = aws_ses_configuration_set.reporting.name + enabled = true + + matching_types = [ + "bounce", + "reject" + ] + + sns_destination { + topic_arn = module.ses_feedback_topic.arn + } + + depends_on = [ + aws_lambda_permission.allow_sns_invoke_ses_feedback_monitor + ] +} diff --git a/infrastructure/ses_reporting.tf b/infrastructure/ses_reporting.tf deleted file mode 100644 index bfb964f9..00000000 --- a/infrastructure/ses_reporting.tf +++ /dev/null @@ -1,78 +0,0 @@ -# resource "aws_ses_domain_identity" "reporting" { -# count = local.is_shared_workspace ? 1 : 0 -# domain = var.domain -# } -# -# resource "aws_route53_record" "ses_domain_verification" { -# count = local.is_shared_workspace ? 1 : 0 -# zone_id = module.route53_fargate_ui.zone_id -# name = "_amazonses.${var.domain}" -# type = "TXT" -# ttl = 600 -# records = [aws_ses_domain_identity.reporting[0].verification_token] -# } -# -# resource "aws_ses_domain_identity_verification" "reporting" { -# count = local.is_shared_workspace ? 1 : 0 -# domain = aws_ses_domain_identity.reporting[0].domain -# depends_on = [aws_route53_record.ses_domain_verification] -# } -# -# resource "aws_ses_domain_dkim" "reporting" { -# count = local.is_shared_workspace ? 1 : 0 -# domain = aws_ses_domain_identity.reporting[0].domain -# } -# -# resource "aws_route53_record" "ses_dkim" { -# count = local.is_shared_workspace ? 3 : 0 -# zone_id = module.route53_fargate_ui.zone_id -# name = "${aws_ses_domain_dkim.reporting[0].dkim_tokens[count.index]}._domainkey.${var.domain}" -# type = "CNAME" -# ttl = 600 -# -# records = ["${aws_ses_domain_dkim.reporting[0].dkim_tokens[count.index]}.dkim.amazonses.com"] -# } -# -# resource "aws_ses_domain_mail_from" "reporting" { -# count = local.is_shared_workspace ? 1 : 0 -# domain = aws_ses_domain_identity.reporting[0].domain -# mail_from_domain = "mail.${var.domain}" -# -# behavior_on_mx_failure = "UseDefaultValue" -# } -# -# resource "aws_route53_record" "ses_mail_from_mx" { -# count = local.is_shared_workspace ? 1 : 0 -# zone_id = module.route53_fargate_ui.zone_id -# name = "mail.${var.domain}" -# type = "MX" -# ttl = 600 -# -# records = [ -# "10 feedback-smtp.${var.region}.amazonses.com" -# ] -# } -# -# resource "aws_route53_record" "ses_mail_from_spf" { -# count = local.is_shared_workspace ? 1 : 0 -# zone_id = module.route53_fargate_ui.zone_id -# name = "mail.${var.domain}" -# type = "TXT" -# ttl = 600 -# -# records = [ -# "v=spf1 include:amazonses.com -all" -# ] -# } -# -# resource "aws_route53_record" "spf_root" { -# count = local.is_shared_workspace ? 1 : 0 -# zone_id = module.route53_fargate_ui.zone_id -# name = var.domain -# type = "TXT" -# ttl = 300 -# -# records = [ -# "v=spf1 include:amazonses.com -all" -# ] -# } diff --git a/infrastructure/sns_ses_feedback.tf b/infrastructure/sns_ses_feedback.tf index 979807a4..0e9e4fbf 100644 --- a/infrastructure/sns_ses_feedback.tf +++ b/infrastructure/sns_ses_feedback.tf @@ -1,43 +1,42 @@ -# module "ses_feedback_topic" { -# source = "./modules/sns" -# topic_name = "ses-feedback-events" -# topic_protocol = "lambda" -# topic_endpoint = module.ses-feedback-monitor-lambda.lambda_arn -# sns_encryption_key_id = module.sns_encryption_key.kms_arn -# raw_message_delivery = false -# -# delivery_policy = jsonencode({ -# Version = "2012-10-17" -# Statement = [ -# { -# Sid = "DefaultOwnerPermissions" -# Effect = "Allow" -# Principal = { -# AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root" -# } -# Action = [ -# "sns:GetTopicAttributes", -# "sns:SetTopicAttributes", -# "sns:AddPermission", -# "sns:RemovePermission", -# "sns:DeleteTopic", -# "sns:Subscribe", -# "sns:ListSubscriptionsByTopic", -# "sns:Publish" -# ] -# Resource = "*" -# } -# ] -# }) -# -# enable_ses_publish = true -# ses_source_account_id = data.aws_caller_identity.current.account_id -# } -# -# resource "aws_lambda_permission" "allow_sns_invoke_ses_feedback_monitor" { -# statement_id = "AllowSNSInvokeSesFeedbackMonitor" -# action = "lambda:InvokeFunction" -# function_name = module.ses-feedback-monitor-lambda.lambda_arn -# principal = "sns.amazonaws.com" -# source_arn = module.ses_feedback_topic.arn -# } +module "ses_feedback_topic" { + source = "./modules/sns" + topic_name = "ses-feedback-events" + topic_protocol = "lambda" + topic_endpoint = module.ses-feedback-monitor-lambda.lambda_arn + sns_encryption_key_id = module.sns_encryption_key.kms_arn + raw_message_delivery = false + + delivery_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Sid = "DefaultOwnerPermissions" + Effect = "Allow" + Principal = { + AWS = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root" + } + Action = [ + "sns:GetTopicAttributes", + "sns:SetTopicAttributes", + "sns:AddPermission", + "sns:RemovePermission", + "sns:DeleteTopic", + "sns:Subscribe", + "sns:ListSubscriptionsByTopic", + "sns:Publish" + ] + Resource = "*" + } + ] + }) + + enable_ses_publish = true +} + +resource "aws_lambda_permission" "allow_sns_invoke_ses_feedback_monitor" { + statement_id = "AllowSNSInvokeSesFeedbackMonitor" + action = "lambda:InvokeFunction" + function_name = module.ses-feedback-monitor-lambda.lambda_arn + principal = "sns.amazonaws.com" + source_arn = module.ses_feedback_topic.arn +} diff --git a/infrastructure/ssm_reporting.tf b/infrastructure/ssm_reporting.tf deleted file mode 100644 index 4fe98965..00000000 --- a/infrastructure/ssm_reporting.tf +++ /dev/null @@ -1,14 +0,0 @@ -# # locals { -# # reporting_from_domain = (local.is_production -# # ? var.domain -# # : "${var.shared_infra_workspace}.${var.domain}" -# # ) -# locals { -# reporting_from_domain = ( -# local.is_production && terraform.workspace != "pre-prod" -# ? var.domain -# : "${var.shared_infra_workspace}.${var.domain}" -# ) -# -# reporting_ses_from_address_value = "ndr-reports@${local.reporting_from_domain}" -# } diff --git a/infrastructure/step-function-reporting.tf b/infrastructure/step-function-reporting.tf index ded274c6..2970509f 100644 --- a/infrastructure/step-function-reporting.tf +++ b/infrastructure/step-function-reporting.tf @@ -21,7 +21,7 @@ data "aws_iam_policy_document" "reporting_sfn_permissions" { actions = ["lambda:InvokeFunction"] resources = [ module.report-orchestration-lambda.lambda_arn, - # module.report-distribution-lambda.lambda_arn, + module.report-distribution-lambda.lambda_arn, ] } } @@ -53,53 +53,51 @@ resource "aws_sfn_state_machine" "reporting_daily_reports" { "keys.$" = "$.Payload.keys" }, ResultPath = "$", - # Next = "Process Reports (Map)" - End = true + Next = "Process Reports (Map)" }, - # "Process Reports (Map)" = { - # Type = "Map", - # ItemsPath = "$.keys", - # MaxConcurrency = 25, - # - # ItemSelector = { - # "bucket.$" = "$.bucket", - # "key.$" = "$$.Map.Item.Value" - # }, - # - # ItemProcessor = { - # ProcessorConfig = { - # Mode = "INLINE" - # }, - # StartAt = "Distribute One Report", - # States = { - # "Distribute One Report" = { - # Type = "Task", - # Resource = "arn:aws:states:::lambda:invoke", - # Parameters = { - # FunctionName = module.report-distribution-lambda.lambda_arn, - # Payload = { - # "action" = "process_one", - # "bucket.$" = "$.bucket", - # "key.$" = "$.key" - # } - # }, - # Retry = [ - # { - # ErrorEquals = ["States.ALL"], - # IntervalSeconds = 2, - # MaxAttempts = 3, - # BackoffRate = 2.0, - # JitterStrategy = "FULL" - # } - # ], - # End = true - # } - # } - # }, - # End = true - # } + "Process Reports (Map)" = { + Type = "Map", + ItemsPath = "$.keys", + MaxConcurrency = 25, + + ItemSelector = { + "bucket.$" = "$.bucket", + "key.$" = "$$.Map.Item.Value" + }, + ItemProcessor = { + ProcessorConfig = { + Mode = "INLINE" + }, + StartAt = "Distribute One Report", + States = { + "Distribute One Report" = { + Type = "Task", + Resource = "arn:aws:states:::lambda:invoke", + Parameters = { + FunctionName = module.report-distribution-lambda.lambda_arn, + Payload = { + "action" = "process_one", + "bucket.$" = "$.bucket", + "key.$" = "$.key" + } + }, + Retry = [ + { + ErrorEquals = ["States.ALL"], + IntervalSeconds = 2, + MaxAttempts = 3, + BackoffRate = 2.0 + JitterStrategy = "FULL" + } + ], + End = true + } + } + }, + End = true + } } }) -} \ No newline at end of file +}