From 99cee14c37ed4507075ae0249747e9ea18b17bbe Mon Sep 17 00:00:00 2001 From: Marek Skrajnowski Date: Mon, 4 May 2020 19:34:30 +0200 Subject: [PATCH 1/9] docs(ses/domain): added a simple spammer script to generate some SES traffic --- ses/domain/example/bin/spammer | 24 ++++++++++++ ses/domain/example/bin/spammer.py | 65 +++++++++++++++++++++++++++++++ ses/domain/example/main.tf | 4 ++ 3 files changed, 93 insertions(+) create mode 100755 ses/domain/example/bin/spammer create mode 100644 ses/domain/example/bin/spammer.py diff --git a/ses/domain/example/bin/spammer b/ses/domain/example/bin/spammer new file mode 100755 index 00000000..ac36f885 --- /dev/null +++ b/ses/domain/example/bin/spammer @@ -0,0 +1,24 @@ +#!/bin/sh + +set -e + +image_id=$( +docker build -q -f - bin < Date: Tue, 5 May 2020 15:35:46 +0200 Subject: [PATCH 2/9] feat(ses/domain): added a configuration set for cloudwatch metrics --- ses/domain/README.md | 8 ++++++++ ses/domain/main.tf | 40 +++++++++++++++++++++++++++++++++++++++- ses/domain/outputs.tf | 12 ++++++++++++ 3 files changed, 59 insertions(+), 1 deletion(-) diff --git a/ses/domain/README.md b/ses/domain/README.md index 8cefd7cd..22da68d8 100644 --- a/ses/domain/README.md +++ b/ses/domain/README.md @@ -100,6 +100,14 @@ Registers a domain with AWS SES and verifies it ## Outputs +* `configuration_set` + + Configuration set to use to track metrics for this domain + +* `email_headers` + + Headers that should be included in each email + * `sender_policy_arn` IAM policy ARN for email senders diff --git a/ses/domain/main.tf b/ses/domain/main.tf index 449b7172..5009d98a 100644 --- a/ses/domain/main.tf +++ b/ses/domain/main.tf @@ -121,7 +121,7 @@ resource "aws_route53_record" "dmarc" { records = [join(";", [for k, v in local.dmarc_options : "${k}=${v}"])] } -# sender policy +# sender policy --------------------------------------------------------------- data "aws_iam_policy_document" "sender" { count = var.create ? 1 : 0 @@ -150,3 +150,41 @@ resource "aws_iam_policy" "sender" { description = "Allows sending emails from @${local.domain}" policy = data.aws_iam_policy_document.sender[0].json } + +# configuration set for cloudwatch metrics ------------------------------------ + +resource "aws_ses_configuration_set" "domain" { + count = var.create ? 1 : 0 + + name = replace(local.domain, ".", "-") +} + +locals { + configuration_set_name = var.create ? aws_ses_configuration_set.domain[0].name : "" +} + +resource "aws_ses_event_destination" "metrics" { + count = var.create ? 1 : 0 + + configuration_set_name = local.configuration_set_name + name = "metrics" + enabled = true + + # track all types + matching_types = [ + "send", + "reject", + "bounce", + "complaint", + "delivery", + "open", + "click", + "renderingFailure" + ] + + cloudwatch_destination { + value_source = "messageTag" + dimension_name = "ses:from-domain" + default_value = local.domain + } +} diff --git a/ses/domain/outputs.tf b/ses/domain/outputs.tf index 71670052..763d45c8 100644 --- a/ses/domain/outputs.tf +++ b/ses/domain/outputs.tf @@ -12,3 +12,15 @@ output "sender_policy_arn" { description = "IAM policy ARN for email senders" value = var.create ? aws_iam_policy.sender[0].arn : null } + +output "configuration_set" { + description = "Configuration set to use to track metrics for this domain" + value = local.configuration_set_name +} + +output "email_headers" { + description = "Headers that should be included in each email" + value = { + "X-SES-CONFIGURATION-SET" = local.configuration_set_name + } +} From b287dcbf299b12ca72c964bc12fc63c42f514ee0 Mon Sep 17 00:00:00 2001 From: Marek Skrajnowski Date: Tue, 5 May 2020 15:36:45 +0200 Subject: [PATCH 3/9] docs(ses/domain): a slightly more sophisticated spammer using the SES simulator --- ses/domain/example/bin/spammer | 1 + ses/domain/example/bin/spammer.py | 118 ++++++++++++++++++++++++------ ses/domain/example/main.tf | 4 + 3 files changed, 101 insertions(+), 22 deletions(-) diff --git a/ses/domain/example/bin/spammer b/ses/domain/example/bin/spammer index ac36f885..780a4317 100755 --- a/ses/domain/example/bin/spammer +++ b/ses/domain/example/bin/spammer @@ -20,5 +20,6 @@ docker run \ -e AWS_ACCESS_KEY_ID="$AWS_ACCESS_KEY_ID" \ -e AWS_SECRET_ACCESS_KEY="$AWS_SECRET_ACCESS_KEY" \ -e SPAMMER_DEFAULT_SENDER="no-reply@$(terraform output domain)" \ + -e SPAMMER_DEFAULT_CONFIGURATION_SET="$(terraform output configuration_set)" \ -v "$HOME/.aws:/root/.aws:ro" \ "$image_id" "$@" diff --git a/ses/domain/example/bin/spammer.py b/ses/domain/example/bin/spammer.py index 96ab3854..39b6d091 100644 --- a/ses/domain/example/bin/spammer.py +++ b/ses/domain/example/bin/spammer.py @@ -3,9 +3,12 @@ import random import time from itertools import count +from textwrap import dedent +from email.message import EmailMessage import boto3 +EICAR_TEST_FILE = b"X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*" parser = argparse.ArgumentParser( description=""" @@ -13,50 +16,121 @@ for example cloudwatch dashboards """, ) - parser.add_argument( - "recipients", - nargs="+", - help="email addresses of the recipients", + "--success", + type=int, + default=5, + help="Successful delivery weight", +) +parser.add_argument( + "--success-email", + default="success@simulator.amazonses.com", + help="Where to send emails which should succeed", +) +parser.add_argument( + "--bounce", + type=int, + default=2, + help="Hard bounce weight", +) +parser.add_argument( + "--complaint", + type=int, + default=1, + help="Complaint weight", +) +parser.add_argument( + "--reject", + type=int, + default=1, + help="Anti-virus rejection weight", ) parser.add_argument( - "-i", "--interval", + "--interval", type=int, default=5, help="delay in seconds between each email", ) parser.add_argument( - "-s", "--sender", + "--sender", default=os.environ["SPAMMER_DEFAULT_SENDER"], help="sender email address", ) +parser.add_argument( + "--configuration-set", + default=os.environ["SPAMMER_DEFAULT_CONFIGURATION_SET"], + help="configuration set to use", +) + +def build_email_message(i, args): + email_type = random.choices( + population=["success", "bounce", "complaint", "reject"], + weights=[args.success, args.bounce, args.complaint, args.reject], + k=1, + )[0] + + email_message = EmailMessage() + email_message["subject"] = f"{email_type} email" + email_message["from"] = args.sender + + if email_type == "reject": + # there's no reject@simulator.amazonses.com address + email_message["to"] = "success@simulator.amazonses.com" + else: + email_message["to"] = f"{email_type}@simulator.amazonses.com" + + email_message["X-SES-CONFIGURATION-SET"] = args.configuration_set + + email_message.set_content( + dedent( + f""" + {email_type} email {i} + https://github.com/codequest-eu/terraform-modules + """ + ), + subtype="plain", + ) + + email_message.set_content( + dedent( + f""" + + + +

{email_type} email {i}

+

codequest-eu/terraform-modules

+ + + """ + ), + subtype="html", + ) + + if email_type == "reject": + email_message.add_attachment( + EICAR_TEST_FILE, + maintype="application", + subtype="octet-stream", + ) + + return email_message def main(): args = parser.parse_args() ses = boto3.client("ses") - print( - f"Will start sending emails from {args.sender} to one of " + - ", ".join(args.recipients) + - f" every {args.interval} seconds" - ) + print(f"Will start sending emails from {args.sender} every {args.interval} seconds") for i in count(start=1): time.sleep(args.interval) - recipient = random.choice(args.recipients) - subject = "Test email" - body = f"Test email {i}" + email_message = build_email_message(i, args) + subject = email_message["subject"] + recipient = email_message["to"] print(f"Sending {subject} to {recipient}...") - - message_id = ses.send_email( - Source=args.sender, - Destination=dict(ToAddresses=[recipient]), - Message=dict( - Subject=dict(Charset="utf-8", Data=subject), - Body=dict(Text=dict(Charset="utf-8", Data=body)) - ), + message_id = ses.send_raw_email( + RawMessage=dict(Data=email_message.as_bytes()), )["MessageId"] print(f"Sent {message_id}") diff --git a/ses/domain/example/main.tf b/ses/domain/example/main.tf index 566ac6d9..ad47a5e6 100644 --- a/ses/domain/example/main.tf +++ b/ses/domain/example/main.tf @@ -23,6 +23,10 @@ output "domain" { value = "ses-domain.terraform-modules-examples.${var.zone_domain}" } +output "configuration_set" { + value = module.domain.configuration_set +} + output "sender_policy_arn" { value = module.domain.sender_policy_arn } From 1270b1fac2cb548a7e5062608b311f86ae9cddcb Mon Sep 17 00:00:00 2001 From: Marek Skrajnowski Date: Tue, 5 May 2020 16:29:18 +0200 Subject: [PATCH 4/9] docs(ses/domain): fixed spammer not triggering rejections --- ses/domain/example/bin/spammer.py | 27 ++++++++++++++++++--------- 1 file changed, 18 insertions(+), 9 deletions(-) diff --git a/ses/domain/example/bin/spammer.py b/ses/domain/example/bin/spammer.py index 39b6d091..7e414e37 100644 --- a/ses/domain/example/bin/spammer.py +++ b/ses/domain/example/bin/spammer.py @@ -1,6 +1,7 @@ import argparse import os import random +import sys import time from itertools import count from textwrap import dedent @@ -8,7 +9,7 @@ import boto3 -EICAR_TEST_FILE = b"X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*" +EICAR_TEST_FILE = rb"X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*" parser = argparse.ArgumentParser( description=""" @@ -45,6 +46,10 @@ default=1, help="Anti-virus rejection weight", ) +parser.add_argument( + "--reject-email", + help="Verified email address that should be the recipient of reject emails", +) parser.add_argument( "--interval", type=int, @@ -72,13 +77,11 @@ def build_email_message(i, args): email_message = EmailMessage() email_message["subject"] = f"{email_type} email" email_message["from"] = args.sender - - if email_type == "reject": - # there's no reject@simulator.amazonses.com address - email_message["to"] = "success@simulator.amazonses.com" - else: - email_message["to"] = f"{email_type}@simulator.amazonses.com" - + email_message["to"] = getattr( + args, + f"{email_type}_email", + f"{email_type}@simulator.amazonses.com", + ) email_message["X-SES-CONFIGURATION-SET"] = args.configuration_set email_message.set_content( @@ -91,7 +94,7 @@ def build_email_message(i, args): subtype="plain", ) - email_message.set_content( + email_message.add_alternative( dedent( f""" @@ -109,6 +112,7 @@ def build_email_message(i, args): if email_type == "reject": email_message.add_attachment( EICAR_TEST_FILE, + filename="sample.txt", maintype="application", subtype="octet-stream", ) @@ -117,6 +121,11 @@ def build_email_message(i, args): def main(): args = parser.parse_args() + + if args.reject and not args.reject_email: + print("You have to specify --reject-email or disable reject emails with --reject 0") + sys.exit(1) + ses = boto3.client("ses") print(f"Will start sending emails from {args.sender} every {args.interval} seconds") From 90efb37e869d09b676a2eb8178f39b89f8913728 Mon Sep 17 00:00:00 2001 From: Marek Skrajnowski Date: Tue, 5 May 2020 17:02:45 +0200 Subject: [PATCH 5/9] feat(ses/domain): metrics and widgets scaffold --- ses/domain/README.md | 8 ++++++++ ses/domain/metrics.tf | 5 +++++ ses/domain/outputs.tf | 10 ++++++++++ ses/domain/widgets.tf | 5 +++++ 4 files changed, 28 insertions(+) create mode 100644 ses/domain/metrics.tf create mode 100644 ses/domain/widgets.tf diff --git a/ses/domain/README.md b/ses/domain/README.md index 22da68d8..0ff473f5 100644 --- a/ses/domain/README.md +++ b/ses/domain/README.md @@ -108,6 +108,10 @@ Registers a domain with AWS SES and verifies it Headers that should be included in each email +* `metrics` + + Cloudwatch metrics, see [metrics.tf](./metrics.tf) for details + * `sender_policy_arn` IAM policy ARN for email senders @@ -119,3 +123,7 @@ Registers a domain with AWS SES and verifies it * `spf_record` SPF record which you should include in the domain's TXT record in case you specified `spf = false` + +* `widgets` + + Cloudwatch dashboard widgets, see [widgets.tf](./widgets.tf) for details diff --git a/ses/domain/metrics.tf b/ses/domain/metrics.tf new file mode 100644 index 00000000..c4db1a29 --- /dev/null +++ b/ses/domain/metrics.tf @@ -0,0 +1,5 @@ +locals { + metrics = { + + } +} diff --git a/ses/domain/outputs.tf b/ses/domain/outputs.tf index 763d45c8..bf4571fc 100644 --- a/ses/domain/outputs.tf +++ b/ses/domain/outputs.tf @@ -24,3 +24,13 @@ output "email_headers" { "X-SES-CONFIGURATION-SET" = local.configuration_set_name } } + +output "metrics" { + value = local.metrics + description = "Cloudwatch metrics, see [metrics.tf](./metrics.tf) for details" +} + +output "widgets" { + value = local.widgets + description = "Cloudwatch dashboard widgets, see [widgets.tf](./widgets.tf) for details" +} diff --git a/ses/domain/widgets.tf b/ses/domain/widgets.tf new file mode 100644 index 00000000..f1ee0bc0 --- /dev/null +++ b/ses/domain/widgets.tf @@ -0,0 +1,5 @@ +locals { + widgets = { + + } +} From 40303701df95bd96b17d0e33d304f343867fdcc9 Mon Sep 17 00:00:00 2001 From: Marek Skrajnowski Date: Tue, 5 May 2020 17:58:22 +0200 Subject: [PATCH 6/9] feat(ses/domain): added per domain metrics and dashboard widgets --- ses/domain/example/main.tf | 12 ++++++++ ses/domain/metrics.tf | 58 ++++++++++++++++++++++++++++++++++++++ ses/domain/widgets.tf | 58 +++++++++++++++++++++++++++++++++++++- 3 files changed, 127 insertions(+), 1 deletion(-) diff --git a/ses/domain/example/main.tf b/ses/domain/example/main.tf index ad47a5e6..25b15d8c 100644 --- a/ses/domain/example/main.tf +++ b/ses/domain/example/main.tf @@ -30,3 +30,15 @@ output "configuration_set" { output "sender_policy_arn" { value = module.domain.sender_policy_arn } + +module "dashboard" { + source = "./../../../cloudwatch/dashboard" + + name = "terraform-modules-ses-domain-example" + widgets = [ + module.domain.widgets.delivery, + module.domain.widgets.delivery_percentage, + module.domain.widgets.spam, + module.domain.widgets.conversion, + ] +} diff --git a/ses/domain/metrics.tf b/ses/domain/metrics.tf index c4db1a29..c884f8d3 100644 --- a/ses/domain/metrics.tf +++ b/ses/domain/metrics.tf @@ -1,5 +1,63 @@ locals { metrics = { + emails = module.metrics_count.out_map.sent + rejections = module.metrics_count.out_map.rejections + bounces = module.metrics_count.out_map.bounces + spam = module.metrics_count.out_map.spam + deliveries = module.metrics_count.out_map.deliveries + opens = module.metrics_count.out_map.opens + clicks = module.metrics_count.out_map.clicks + rejections_percentage = module.metrics_percentage.out_map.rejections + bounces_percentage = module.metrics_percentage.out_map.bounces + deliveries_percentage = module.metrics_percentage.out_map.deliveries } } + +module "cloudwatch_consts" { + source = "./../../cloudwatch/consts" +} + +locals { + colors = module.cloudwatch_consts.colors +} + +locals { + domain_dimensions = { + "ses:from-domain" = local.domain + } + + metrics_count = { + sent = { name = "Send", label = "Sent", color = local.colors.grey } + rejections = { name = "Reject", label = "Rejections", color = local.colors.purple } + bounces = { name = "Bounce", label = "Bounces", color = local.colors.orange } + spam = { name = "Complaint", label = "Marked as spam", color = local.colors.red } + deliveries = { name = "Delivery", label = "Deliveries", color = local.colors.green } + opens = { name = "Open", label = "Opens", color = local.colors.light_green } + clicks = { name = "Click", label = "Link clicks", color = local.colors.green } + } +} + +module "metrics_count" { + source = "./../../cloudwatch/metric/many" + + vars_map = { for k, v in local.metrics_count : k => { + namespace = "AWS/SES" + dimensions = local.domain_dimensions + name = v.name + label = v.label + color = v.color + stat = "SampleCount" + period = 300 + } } +} + +module "metrics_percentage" { + source = "./../../cloudwatch/metric_expression/many" + + vars_map = { for k, v in local.metrics_count : k => { + expression = "100 * ${module.metrics_count.out_map[k].id} / FILL(${module.metrics_count.out_map.sent.id}, 1)" + label = v.label + color = v.color + } } +} diff --git a/ses/domain/widgets.tf b/ses/domain/widgets.tf index f1ee0bc0..d410c160 100644 --- a/ses/domain/widgets.tf +++ b/ses/domain/widgets.tf @@ -1,5 +1,61 @@ locals { widgets = { - + delivery = module.widget_delivery, + delivery_percentage = module.widget_delivery_percentage + spam = module.widget_spam + conversion = module.widget_conversion, } } + +module "widget_delivery" { + source = "./../../cloudwatch/metric_widget" + + title = "Email delivery" + stacked = true + left_metrics = [ + local.metrics.rejections, + local.metrics.bounces, + local.metrics.deliveries, + ] + left_range = [0, null] +} + +module "widget_delivery_percentage" { + source = "./../../cloudwatch/metric_widget" + + title = "Email delivery percentages" + stacked = true + left_metrics = [ + local.metrics.rejections_percentage, + local.metrics.bounces_percentage, + local.metrics.deliveries_percentage, + ] + left_range = [0, null] + hidden_metrics = [ + local.metrics.emails, + local.metrics.rejections, + local.metrics.bounces, + local.metrics.deliveries, + ] +} + +module "widget_spam" { + source = "./../../cloudwatch/metric_widget" + + title = "Email spam complaints" + left_metrics = [local.metrics.spam] + left_range = [0, null] +} + +module "widget_conversion" { + source = "./../../cloudwatch/metric_widget" + + title = "Email conversion" + left_metrics = [ + local.metrics.emails, + merge(local.metrics.deliveries, { color = local.colors.light_olive }), + local.metrics.opens, + local.metrics.clicks, + ] + left_range = [0, null] +} From 5a15f6dd9415d7e52756e55fb6c76e2c46083e74 Mon Sep 17 00:00:00 2001 From: Marek Skrajnowski Date: Tue, 5 May 2020 18:21:38 +0200 Subject: [PATCH 7/9] fix(cloudwatch/metric): metric ids can't contain dots --- cloudwatch/metric/main.tf | 2 +- cloudwatch/metric/many/main.tf | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cloudwatch/metric/main.tf b/cloudwatch/metric/main.tf index ae7c370a..5ff2b6a2 100644 --- a/cloudwatch/metric/main.tf +++ b/cloudwatch/metric/main.tf @@ -9,5 +9,5 @@ locals { var.color, ])) - id = "m_${var.name}_${local.vars_hash}" + id = "m_${replace(var.name, ".", "_")}_${local.vars_hash}" } diff --git a/cloudwatch/metric/many/main.tf b/cloudwatch/metric/many/main.tf index 397306bb..7cf711b5 100644 --- a/cloudwatch/metric/many/main.tf +++ b/cloudwatch/metric/many/main.tf @@ -7,7 +7,7 @@ module "default" { locals { out = [for v in var.vars : { - id = "m_${v.name}_${md5(jsonencode([ + id = "m_${replace(v.name, ".", "_")}_${md5(jsonencode([ v.namespace, v.name, try(v.dimensions, module.default.dimensions), @@ -27,7 +27,7 @@ locals { }] out_map = { for k, v in var.vars_map : k => { - id = "m_${v.name}_${md5(jsonencode([ + id = "m_${replace(v.name, ".", "_")}_${md5(jsonencode([ v.namespace, v.name, try(v.dimensions, module.default.dimensions), From 15e0bdaa2d0cf6ed884623082d03545451a907b3 Mon Sep 17 00:00:00 2001 From: Marek Skrajnowski Date: Tue, 5 May 2020 18:22:07 +0200 Subject: [PATCH 8/9] feat(ses/domain): added account metrics and dashboard widgets --- ses/domain/example/main.tf | 2 ++ ses/domain/metrics.tf | 22 ++++++++++++++ ses/domain/widgets.tf | 62 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 86 insertions(+) diff --git a/ses/domain/example/main.tf b/ses/domain/example/main.tf index 25b15d8c..6661b260 100644 --- a/ses/domain/example/main.tf +++ b/ses/domain/example/main.tf @@ -40,5 +40,7 @@ module "dashboard" { module.domain.widgets.delivery_percentage, module.domain.widgets.spam, module.domain.widgets.conversion, + module.domain.widgets.account_bounce_rate, + module.domain.widgets.account_spam_rate, ] } diff --git a/ses/domain/metrics.tf b/ses/domain/metrics.tf index c884f8d3..4a3f7cf6 100644 --- a/ses/domain/metrics.tf +++ b/ses/domain/metrics.tf @@ -11,6 +11,9 @@ locals { rejections_percentage = module.metrics_percentage.out_map.rejections bounces_percentage = module.metrics_percentage.out_map.bounces deliveries_percentage = module.metrics_percentage.out_map.deliveries + + account_bounce_rate = module.metrics_account_reputation.out_map.bounce + account_spam_rate = module.metrics_account_reputation.out_map.spam } } @@ -61,3 +64,22 @@ module "metrics_percentage" { color = v.color } } } + +locals { + metrics_reputation = { + bounce = { name = "Bounce", label = "Bounce" } + spam = { name = "Complaint", label = "Spam" } + } +} + +module "metrics_account_reputation" { + source = "./../../cloudwatch/metric/many" + + vars_map = { for k, v in local.metrics_reputation : k => { + namespace = "AWS/SES" + name = "Reputation.${v.name}Rate" + label = "${v.label} rate" + stat = "Average" + period = 3600 + } } +} diff --git a/ses/domain/widgets.tf b/ses/domain/widgets.tf index d410c160..5f0db399 100644 --- a/ses/domain/widgets.tf +++ b/ses/domain/widgets.tf @@ -4,6 +4,8 @@ locals { delivery_percentage = module.widget_delivery_percentage spam = module.widget_spam conversion = module.widget_conversion, + account_bounce_rate = module.widget_account_bounce_rate + account_spam_rate = module.widget_account_spam_rate } } @@ -59,3 +61,63 @@ module "widget_conversion" { ] left_range = [0, null] } + +module "annotation_warning_bounce_rate" { + source = "./../../cloudwatch/annotation" + + value = 0.05 + color = local.colors.orange + fill = "above" + label = "High rate" +} + +module "annotation_max_bounce_rate" { + source = "./../../cloudwatch/annotation" + + value = 0.1 + color = local.colors.red + fill = "above" + label = "Ban rate" +} + +module "widget_account_bounce_rate" { + source = "./../../cloudwatch/metric_widget" + + title = "SES account bounce rate" + left_metrics = [local.metrics.account_bounce_rate] + left_range = [0, null] + left_annotations = [ + module.annotation_warning_bounce_rate, + module.annotation_max_bounce_rate, + ] +} + +module "annotation_warning_spam_rate" { + source = "./../../cloudwatch/annotation" + + value = 0.001 + color = local.colors.orange + fill = "above" + label = "High rate" +} + +module "annotation_max_spam_rate" { + source = "./../../cloudwatch/annotation" + + value = 0.005 + color = local.colors.red + fill = "above" + label = "Ban rate" +} + +module "widget_account_spam_rate" { + source = "./../../cloudwatch/metric_widget" + + title = "SES account spam rate" + left_metrics = [local.metrics.account_spam_rate] + left_range = [0, null] + left_annotations = [ + module.annotation_warning_spam_rate, + module.annotation_max_spam_rate, + ] +} From b70a7ba6ce9c4bbd386f178013a1d6a3fc2edfef Mon Sep 17 00:00:00 2001 From: Marek Skrajnowski Date: Tue, 5 May 2020 18:25:51 +0200 Subject: [PATCH 9/9] refactor(ses/domain): changed rejections color --- ses/domain/metrics.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ses/domain/metrics.tf b/ses/domain/metrics.tf index 4a3f7cf6..4898ab39 100644 --- a/ses/domain/metrics.tf +++ b/ses/domain/metrics.tf @@ -32,7 +32,7 @@ locals { metrics_count = { sent = { name = "Send", label = "Sent", color = local.colors.grey } - rejections = { name = "Reject", label = "Rejections", color = local.colors.purple } + rejections = { name = "Reject", label = "Rejections", color = local.colors.red } bounces = { name = "Bounce", label = "Bounces", color = local.colors.orange } spam = { name = "Complaint", label = "Marked as spam", color = local.colors.red } deliveries = { name = "Delivery", label = "Deliveries", color = local.colors.green }