Send and receive email for serverless developers
Article originally published on: https://www.lambdatv.com/send-and-receive-email-for-serverless-developers
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
- toAddresses (which can be a string or an array of strings), and a
- source (which is an address that the email will appear to originate from).
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 stop_rule_set 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
- More security: can reject emails based on company policy. Example: only authenticated emails from certain domains may be received.
- Support team: you can create support tickets on receiving emails at a certain address with the description as whatever was in the email body.
- Receiving files: You can make it easy for people to send you files, e.g give them an address to send attachments and use Lambda to process them.
- Archive emails: incoming emails can be saved to S3. for this one, you don’t need to write a function, it’s supported directly by SES.
To see example functions from AWS that control mail flow:
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 =
- success
- bounce
- complaint
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:
- 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.
- 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.
- SES is a per-region service so any configuration applies per region, e.g requesting sending limit and verified emails and domains.
- 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, head over to the site and subscribe and I’ll send you more great serverless tutorials soon. Thanks!