Amazon SES Reporting: Analyzing Bounces and Complaints with CloudWatch
Optimizing Amazon SES monitoring
Why Monitor Bounces and Complaints on Amazon SES?
Maintaining high deliverability is critical for any email service. Amazon SES (Simple Email Service) closely monitors two key metrics:
- Bounce: The email could not be delivered.
- Soft Bounce: A temporary issue (full mailbox, server unavailable).
- Hard Bounce: A permanent issue (invalid email address). Warning: A Hard Bounce rate > 10% can lead to the suspension of your AWS account.
- Complaint: The recipient marked your message as SPAM. A rate > 0.1% is considered critical by Amazon.
The Stakes: Sender Reputation
Failure to handle these notifications will cause your sender reputation to drop, leading your emails directly to the spam folder or resulting in the revocation of your SES access. The goal here is to implement a serverless architecture to analyze these events cost-effectively.
Solution Architecture: SES, SNS, and CloudWatch
The idea is to create an automated pipeline to centralize logs without managing servers.
- Amazon SES captures the event (Bounce/Complaint).
- An Amazon SNS topic receives the notification.
- An AWS Lambda function processes the JSON payload and sends it to CloudWatch Logs.
- CloudWatch Logs Insights is used to query and visualize the data.
Step 1: Configuring SES Notifications
To interconnect SES with SNS, follow the official Amazon SES documentation. In the AWS Console, navigate to Configuration Sets or directly to your verified identity (domain), under the Notifications section, to link your feedback types to your SNS topic.
Step 2: Data Processing with AWS Lambda
Here is the Python code for the Lambda function. Its role is to extract the SNS message and make it readable for CloudWatch.
import json
import logging
logger = logging.getLogger()
logger.setLevel(logging.INFO)
def lambda_handler(event, context):
# Extract the SNS message
message = event['Records'][0]['Sns']['Message']
# Structured log for CloudWatch Insights
print(message)
return {
'statusCode': 200,
'body': json.dumps('Log processed successfully')
}
Security: IAM Roles and Permissions
For this workflow to function, your Lambda needs the appropriate permissions.
Trust Policy (AssumeRole): Allows AWS Lambda to execute the function.
IAM Policy for CloudWatch:
Ensure the Lambda has logs:CreateLogGroup, logs:CreateLogStream, and logs:PutLogEvents permissions.
Step 3: Analysis with CloudWatch Logs Insights
This is where the magic happens. Once your logs reach CloudWatch, you can use the query language to generate reports.
Query to Isolate Bounces
fields @timestamp, notificationType, mail.sourceIp as sourceIp, mail.source as Sender, mail.destination.0 as Recipient
| filter notificationType = "Bounce"
| sort @timestamp desc
Query for Complaints (Spam Reports)
fields @timestamp, notificationType, mail.sourceIp as sourceIp, mail.source as Sender, mail.destination.0 as Recipient
| filter notificationType = "Complaint"
| sort @timestamp desc
Conclusion and Next Steps
This simple, Cloud Native architecture allows you to monitor your email activity without infrastructure overhead. To take it further, you could:
- Create a CloudWatch Dashboard to visualize rates in real-time.
- Automate the blacklisting of “Hard Bounce” addresses in your database via the Lambda function.
- Use Terraform to deploy this entire stack with a single command.
Actuellement ingénieur DevOps/Architecte Cloud, j’étais initialement interessé par l’administration système et grâce aux entreprises dans lesquelles j’ai pu travailler Oxalide et maintenant Claranet j’ai eu la chance de découvrir l’univers du Cloud et de l’automatisation.
Je me suis décidé a publier ce blog pour vous faire partager ma passion mais également pour enrichir avec modestie tout ce que l’on peut trouver sur internet. Bonne lecture !