Send and receive email for serverless developers

Slides:

SES in 30 seconds

SES is Amazon’s scalable email service. It can send and receive email at large scale and at a low cost. You can use it programmatically to send notifications, marketing, transactional emails, etc. You can also accept or reject incoming email according to your custom needs. SES handles a lot of the heavy lifting for you. It was originally built to handle Amazon.com’s email needs so you can be sure that SES will meet your reliability requirements.

Sending your first email

Verify an email address

By default your account is in a sandbox. That means that for now you can only send to addresses that you have verified. So, the first step is to verify an address. Go to the SES console and follow the steps in the screenshot. You may use an address from any email provider:

https://console.aws.amazon.com/ses

Click the link in the email:

The said address would then appear as verified:

Send from your domain

Also, you may only use sender addresses on domains that you have verified. So you need to verify a domain as shown in the screenshot:

Let us trust you

You may want to take an additional step to secure your emails. You as a new email sender does not have credibility. Emails may have been intercepted by attackers. For the rest of us, you may just be a new spammer. Email providers may reject you based on arbitrary rules.

So you need to authenticate your emails. What I’m talking about here is DKIM, meaning: DomainKeys Identified Mail. Like the name suggests, you set it up at domain level by adding DNS records. That’s why I ticked the Generate DKIM Settings at number 4 above.

On the next screen, you’ll get DNS records to enter at your DNS registrar. Enter all records shown at your registrar.

For example, I would enter my MX record at GoDaddy as follows:

After DNS changes propagates, you may move on in this SES tutorial.

Create the lambda function

https://console.aws.amazon.com/lambda

Author from scratch:

Choose a name and choose a role that can send or receive emails. Or create a new role with:

In the opening tab:

Paste the following policy document:

{

    "Version": "2012-10-17",

    "Statement": [

        {

            "Sid": "Stmt1509870273000",

            "Effect": "Allow",

            "Action": [

                "ses:SendBounce",

                "ses:SendEmail"

            ],

            "Resource": [

                "*"

            ]

        }

    ]

}

(Note that the ses:sendBounce permission will be used later in the tutorial; so you can put it there right now)

Back to the lambda console:

Paste the code at the following Github page as function body:

https://github.com/jeshan/lambdatv/blob/master/ses-intro/send-email.js

The 2 salient points in the function are numbered here. The first is to get a reference to the SES class, then call the sendEmail API.

To test this function, create a test event at the top right and pass in

Click save, then test and you should receive an email like the following:

Congrats, you’ve sent your first email using Amazon SES via Lambda.

Remember that toAddresses property can also be an array. So, this tells you that you can have multiple receivers for the email. Feel free to try it before proceeding.

Sending templated emails

You may want to keep your email designs separate from your code. In this case, you can use email templates. Usually, you could use a library to handle the templates but it turns out that SES has built-in support for email templates. For example, you can write a template like this:

<h1>Hello {{name}},</h1><p>Your favourite colour is {{colour}}.</p>

What does the name placeholder above tell you? That this template can be used for multiple recipients with each having different values in the placeholders. This is useful when sending personalised emails. Placeholders can also be part of the subject.

The first step is to define a template in a json file. For this tutorial, I saved the following json in a file called example-email-template.json.

{

  "Template": {

    "TemplateName": "Template1",

    "SubjectPart": "Hey, {{name}}!",

    "HtmlPart": "<h1>Hello {{name}},</h1><p>Your favourite colour is {{colour}}.</p>"

  }

}

You create an email template by calling the createTemplate api.

aws ses create-template --cli-input-json file://example-email-template.json

You can locate all your created templates at the following page (though you currently can’t do much about it from there):

Then create a second json file that will contain the remaining data needed to send an email, like recipients and placeholder values. Here, I put the following json in a file called example-email.json:

{

  "Source": "from@email.com",

  "Template": "Template1",

  "Destinations": [

    {

      "Destination": {

        "ToAddresses": [

          "first@email.com"

        ]

      },

      "ReplacementTemplateData": "{ \"name\":\"Geek\", \"colour\":\"blue\" }"

    },

    {

      "Destination": {

        "ToAddresses": [

          "second@email.com"

        ]

      },

      "ReplacementTemplateData": "{}"

    }

  ],

  "DefaultTemplateData": "{ \"name\":\"fellow nerd\", \"colour\":\"red\" }"

}

The "ReplacementTemplateData" will be read and will take place of the placeholders. You’ll notice that I have 2 recipients, first@email.com and second@email.com and the second one doesn’t have any template data. In this case, the “DefaultTemplateData” will be used as default values for the placeholders.

To send this email, invoke the sendBulkTemplatedEmail api:

aws ses send-bulk-templated-email --cli-input-json file://example-email.json

As expected, I got 2 emails due to the latter:

The second one had the default values of "fellow nerd" and “red” populated.

See the Github link for the template email commands:

https://github.com/jeshan/lambdatv/blob/master/ses-intro/email-template.sh

So far, you’ve seen how to send emails to one or more people and you have also seen how to send personalised emails using templates. Now, you’re going to see how to take action on received email on SES; an area that is far less talked about online.

Receiving email

This is a powerful feature from SES. You can do take actions on the emails like archive them, send notifications. Besides, the ability to integrate with Lambda means that you arbitrarily decide what emails you want to accept, reject, forward, and many more things that you may imagine.

Verify a domain

To receive emails, you’ll need a domain that you control and SES needs to know about it.

Verify a domain as in the screenshot:

Logging emails

I’ll start with a simple example that simply logs incoming emails.

Create a function based on the event-invocation.js file on Github:

https://github.com/jeshan/lambdatv/blob/master/ses-intro/event-invocation.js

Let’s get this simple use case working first and after that, I’m going to show you a slightly more sophisticated example.

Working with rule sets

To receive emails with SES, you need certain addresses or domains that you want to monitor. Then you tell SES what Lambda function to process this email. You do both of this in a rule set. You need to create at least one rule in the rule set.

Besides Lambda, other integrations are possible but I’m going to focus on Lambda here because, well, this channel is called LambdaTV 🙂.

Step 1 is to define which verified domains or addresses you want to monitor:

Add a Lambda action, then choose the function that you created earlier.

Leave the invocation type as Event. Event is for asynchronous processing, so that means that SES won’t wait for the function execution to complete in order to proceed. After this one is working, I’ll show you how to use RequestResponse.

You may add more actions but I’m going to cover only single-action rules in this tutorial.

Choose a rule name and finalise the rule creation:

Now your rule set is active and ready to accept emails.

Send an email to an address that matches the rule just created. Within a minute or two, you’ll see the log appear in Cloudwatch:

https://console.aws.amazon.com/cloudwatch

Good. Logging works. We can try a more sophisticated example now.

Arbitrary logic

Now I’m going to show an example on how you can accept or reject emails based on arbitrary logic. Here, I’ll demonstrate how to reject emails from a certain domain but it can be any criteria you like. Here, I’ll "reject" the email by sending a bounce back to the original sender.

Change the function body to the one given in the event-request-response.js file:

https://github.com/jeshan/lambdatv/blob/master/ses-intro/event-request-response.js

Here, we’re saying that if we get emails from the jeshan.co domain, we would like to respond with a bounce. Otherwise, proceed with executing the rule set as usual. Change this domain to any other valid one, e.g gmail.com

At the end of the handler, call the callback function with no parameters to indicate normal completion.

The sendBounce function looks as follows. You need to set a bounce type, then call the sendBounce api. Maybe you’d also want to give the receiver an explanation of why you made the email bounce. You may also set the disposition to stopruleset which won’t execute further rules or actions (but in this case it makes no difference since there’s only 1 rule in the rule set).

Before we used the Event invocation type. This called our function asynchronously. But here, we want to control the mail flow so we need to call our function synchronously. You do that by changing the invocation type to RequestResponse:

Now send an email again to a matched recipient from the email domain that you entered in the function.

After a few seconds, you should get a bounce like the following:

By the way, you’re seeing in the screenshots that sometimes the "via amazonses.com" is shown. This is because my sender address was from a domain that I didn’t have DKIM set up. If you don’t want people to see this, it will serve you well to set up DKIM from the get-go as I showed in the beginning.

Great. Now, hopefully you get the picture of how arbitrary email processing works with Lambda. I’m sure you can imagine many more use cases. Lambda and Simple Notification Service (SNS) serve as your extension points here. So anything you can do with Lambda or SNS can be combined with SES. Here are some ideas you can consider:

Other use cases

To see example functions from AWS that control mail flow:

http://docs.aws.amazon.com/ses/latest/DeveloperGuide/receiving-email-action-lambda-example-functions.html

Testing the sending

Use the mailbox simulator to test scenarios like bounces and complaints. It will especially be useful if you use SES alongside SNS. Otherwise, it won’t be that useful because SNS is the primary way of receiving notifications from the simulator.

The emails are like this: x@simulator.amazonses.com where x =

Etc.

To learn more:

http://docs.aws.amazon.com/ses/latest/DeveloperGuide/mailbox-simulator.html

Monitor activity

It is recommended that you monitor your email activity, especially for numbers like bounce, complaint and reject rates. If your emails are perceived as spam but keep on sending emails, SES or email providers will penalise you. You should have a mechanism to handle bounces, complaints and rejects. AWS will ask you if you have these when going to production.

You can do so via the API or also in the console at the following links:

https://console.aws.amazon.com/ses/home#reputation-dashboard:

https://console.aws.amazon.com/ses/home#dashboard:

To learn more, click here or watch the videos at I’ll link to at the end:

http://docs.aws.amazon.com/ses/latest/DeveloperGuide/monitor-sending-activity.html

Going to production

Restrictions apply by default because AWS needs to protect SES credibility with email providers. To send to any addresses, not just to those that you verified, you need to request production access as described in the following link:

http://docs.aws.amazon.com/ses/latest/DeveloperGuide/request-production-access.html

Notes

Some things to keep in mind while following this tutorial:

  1. Do not use your personal email domain! Don’t be as stupid as me. I used it to send and receive emails with SES, not realising that this was preventing me from receiving any emails in regular inbox at Google.

  2. Ensure priority level for MX is high enough (e.g 0). The mistake I did was using something other than 0 and waiting for hours to get my Lambda function to get invoked… only to realise later that another higher priority MX record was being chosen.

  3. SES is a per-region service so any configuration applies per region, e.g requesting sending limit and verified emails and domains.

  4. SES needs the lambda:addPermission IAM permission. This is done automatically if you’re creating rule sets via the SES console but it may not be the case through the API.

Resources

I have chosen these 2 re:Invent sessions for you to learn about itin more detail. You’ll learn many things from AWS people, including high-level discussion on implementing use cases and handling cases like bounces and complaints. The latter are very important for your credibility.

https://www.youtube.com/watch?v=BpuqLT1UGVM

https://www.youtube.com/watch?v=VDBs_J5i6Ro

Show me the code

Head over to GitHub for all code discussed here:

https://github.com/jeshan/lambdatv/tree/master/ses-intro

If you like my style and you’d like me to keep you in the loop, click the subscribe button and I’ll send you more great serverless tutorials soon. Thanks!

Stay up to date

* indicates required