Receiving email with SES and Slack
Published: 2019-01-15 01:31:54

When getting new domain, one of the crucial thing we need to have is to be able to receive email on that domain. This either for things like getting ssl cert for that domain, or simply having a way for people to reach us with email address such as support@ourdomain.com.

Some domain registry provide simple email redirection, where you can forward incoming email to that domain to an address you provided. But sometimes, you might want to have more control on how to receive the email.

In our case, we want to forward the email to our Slack channel. Amazon SES (Simple Email Service) support receiving email. Configuring our domain for that is simply by adding a TXT record into the domain DNS record (for domain validation) and MX record to give a hint to any mail client who want to send email to our domain, to send it through SES server.

Once the email was passed through SES, how do we access and read it? SES does not provide any interface like Gmail or Yahoo for us to read the email. But it allow us to forward that email into SNS (Simple Notification Service) topic. From here we can subscribe to the topic and has it to deliver to another email address. Problem is the email sent in JSON format, not a plain email format. Reading the email content is next to impossible except for a few simple emails. This also mean simply forwarding the email to Slack won't work.

So we need to do some pre-processing to the incoming email. Fortunately, SNS also can be configured so that it trigger a Lambda Function upon receiving the email. This allow us to write some python code for the pre-processing.

But there's another problem. SNS only support receiving email not larger than 170KB. Larger than that it will bounce. For larger email, AWS suggest us to write it to S3 first. From there, we can configure it to trigger SNS notification, which in turn trigger our Lambda Function. From there, we will read the email from S3, using the object key passed in by SNS.

Most of the time actually spent on figuring out how to allow our Lambda function to have permission to read the S3 bucket. The interface is very confusing but in the end I manage to get the correct setup. The Lambda designer console should be like this in the end:-

![](https://thepracticaldev.s3.amazonaws.com/i/ltlgpv2hemv1z4741xq7.png)

And below is the whole code to read and forward the email into Slack:-

```python
import os
import json
import boto3
import email
import logging
import smtplib

logger = logging.getLogger()
logger.setLevel(logging.INFO)

mailgun_host = os.environ['MAILGUN_HOST']
mailgun_user = os.environ['MAILGUN_USER']
mailgun_pass = os.environ['MAILGUN_PASS']

def lambda_handler(event, context):
    s3 = boto3.client("s3")
    logging.info("My Event is : %s", event)
    file_obj = event["Records"][0]
    data_json = json.loads(event['Records'][0]['Sns']['Message'])
    filename = data_json['receipt']['action']['objectKey']
    logging.info("filename: %s", filename)
    fileObj = s3.get_object(Bucket=os.environ['S3_BUCKET'], Key=filename)
    logging.info("file has been gotten!")
    msg = email.message_from_bytes(fileObj['Body'].read())
    domain_dest = msg['To'].split('@')[1]
    logging.info('%s %s %s domain_dest:%s', msg['From'], msg['To'], msg['Subject'], domain_dest)
    
    smtp_client = smtplib.SMTP(mailgun_host, 587)
    smtp_client.login(mailgun_user, mailgun_pass)
    smtp_client.sendmail(mailgun_user, [os.environ['SLACK_EMAIL']],
                         msg.as_string().encode('utf-8'))
    return
```

Cover photo via [Good Free Photos](https://www.goodfreephotos.com/).