How to Set Up Amazon SES to Receive Emails and Store Them in S3

Olga Shabalina
4 min readFeb 13, 2024

How to use Route 53 Hosted Zone and Amazon SES to receive emails and save them to S3 for further processing.

The illustration generated by ChatGPT that visually represents the concept of Amazon SES receiving emails from the internet and saving them in an S3 bucket.
Amazon SES receives an email and saves it in S3. Generated by DALL-E.

Welcome to my quick guide on setting up Amazon SES to receive and store emails in an S3 bucket. Amazon SES is a powerful tool for handling emails, and today, I’ll be focusing on how to easily capture incoming emails and keep them safe in the cloud. This setup is perfect for anyone looking to organise their emails better, keep a secure backup or build a data pipeline for source files received via an email.

Prerequisites

  • An AWS account.
  • A domain name registered in Route53 (you can also transfer an existing domain from another registrar if you already have one) with associated hosted zone.

Step 1: Define Parameters for Your Stack

As the hosted zone is normally created by Route53 Registrar, this is the only parameter that is required for the stack. However, you can parameterise all the other resources for improved readability of the cloud formation template as demonstrated with the S3 Bucket name in the example below.

Parameters:
HostedZoneName:
Description: "Public apex DNS Domain/HostedZone name registered in Route53"
Type: String
BucketName:
Description: "Bucket to store emails"
Type: String

Step 2: Verify Your Domain with SES

We need to make sure Amazon SES can get emails for your domain. This means adding an MX (Mail Exchange) record to Record Set Group for the Hosted Zone. MX record is a type of DNS record that specifies the mail server responsible for receiving emails on behalf of your domain. By setting the MX record to point to Amazon SES’s inbound SMTP server, you are configuring your domain to direct incoming emails to SES. This process is essential for SES to accept and process emails sent to your domain.

MailboxRecordSetGroup:
Type: AWS::Route53::RecordSetGroup
Properties:
Comment: "SES Verified Domain Identity DKIM Token record set"
HostedZoneName: !Sub "${HostedZoneName}."
RecordSets:
- Name: !Sub ${HostedZoneName}
ResourceRecords:
- !Sub "10 inbound-smtp.${AWS::Region}.amazonaws.com"
TTL: "300"
Type: "MX"

Step 3: Configure SES to Handle Emails

We configure Amazon SES to manage the emails it receives for your domain. This involves creating a configuration set, which is a set of rules that SES follows when processing your emails. We emphasise security by requiring TLS (Transport Layer Security) for all incoming emails, ensuring they are encrypted during transmission. Additionally, we disable the option for SES to send emails, focusing the service purely on receiving and processing.

VerifiedIdentityConfigurationSet:
Type: AWS::SES::ConfigurationSet
Properties:
DeliveryOptions:
TlsPolicy: REQUIRE
Name: email-verified-id
SendingOptions:
SendingEnabled: false #sending emails is beyond the scope of this guide

Step 4: Create an S3 Bucket and a Bucket Policy

We set up an S3 bucket to save the emails upon receipt and add a bucket policy to make sure only Amazon SES can put emails into this bucket. This way, the bucket is ready to receive and store emails without exposing them to the outside world.

MailReceivedBucket:
Type: "AWS::S3::Bucket"
Properties:
BucketName: !Sub "${BucketName}"
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true

MailReceivedBucketPolicy:
Type: 'AWS::S3::BucketPolicy'
Properties:
Bucket: !Ref MailReceivedBucket
PolicyDocument:
Statement:
- Action:
- 's3:PutObject'
Condition:
StringEquals:
'AWS:SourceAccount': !Ref 'AWS::AccountId'
StringLike:
'AWS:SourceArn': !Sub >-
arn:aws:ses:${AWS::Region}:${AWS::AccountId}:receipt-rule-set/${EmailReceiptRuleSet}:receipt-rule/*
Effect: Allow
Principal:
Service: ses.amazonaws.com
Resource:
- !Sub '${MailReceivedBucket.Arn}/*'
Sid: AllowSESPut
Version: "2012-10-17"

Step 5: Setting Up SES Ruleset and Receipt Rule

We set up a rule set and a receipt rule in SES for emails sent to a specific address, directing them to be stored under a designated prefix in the S3 bucket.

EmailReceiptRuleSet:
Type: AWS::SES::ReceiptRuleSet
Properties:
RuleSetName: !Sub "${HostedZoneName}-ruleset"

ReceiptRule:
Type: 'AWS::SES::ReceiptRule'
DependsOn: MailReceivedBucketPolicy
Properties:
Rule:
Actions:
- S3Action:
BucketName: !Ref MailReceivedBucket
ObjectKeyPrefix: received/
Enabled: true
Name: test-email-rule
Recipients:
- !Sub 'your-name@${HostedZoneName}'
ScanEnabled: true
RuleSetName: !Ref EmailReceiptRuleSet

After configuring, it’s necessary to activate the rule set for it to start receiving emails. Note that activating a receipt rule set is different from simply enabling individual receipt rules within that set. This activation step isn’t directly available through CloudFormation. However, you can incorporate the AWS CLI command as part of your deployment process to activate the rule set:

aws ses set-active-receipt-rule-set \
--rule-set-name "${YOUR_RULESET}"

Remember, only one rule set can be active at any given time.

Step 6: Monitoring and Alarms

This step is not mandatory but certainly recommended. It involves configuring an alarm to alert on errors or unusual activities. This proactive monitoring is key to maintaining a smooth and reliable email system, allowing for quick action on any detected issues.

SESErrorsAlarms:
Type: AWS::CloudWatch::Alarm
Properties:
AlarmDescription: Alarm for SES related errors
MetricName: PublishExpired # PublishFailure didnt work as expected.
Namespace: AWS/SES
Statistic: Sum
Period: 300
EvaluationPeriods: 1
Threshold: 0
ComparisonOperator: GreaterThanThreshold
Dimensions:
- Name: RuleSetName
Value: !Ref EmailReceiptRuleSet

Conclusion

This guide has shown you the steps to integrate these powerful AWS services, providing a solid foundation for organizing, backing up, or processing email data. With this configuration, you can now build complex pipelines triggered by an email.

As always, feel free to reach out if you have any questions or want to share how you built your own email-triggered architecture. Happy emailing!

--

--