Sending Emails using AWS SES, Lambda and API Gateway

Lois T.
Geek Culture
Published in
4 min readDec 27, 2021

--

A summarized guide in Python

I’m building some SaaS and I will be using AWS SES to send emails for the ‘Reset Password’ function. There’re other options around, I’m taking this since I’m most familiar with it (have done this for clients several years back).

That said, I still find myself forgetting most of the steps. Also, while I’ve previously done this in Node.js, this time round I will be using Python (simply because I’ve been using Python recently). As usual, I’m logging the process here in case anyone (or myself) needs this.

Step 0a: Make sure you are in the right region

I actually forgot to do this. My account is created eons back when AWS didn’t have that much regions, therefore I’ve actually several things toggled in their default region at that time — us-east-1 (North Virginia). When I logged back in recently, I didn’t realize that my default is now set to my geographical region, and I went on to verify identities and request limits in my current region.

AWS then proceeded to ask me why I have to request limits in a new region.

Oops. On top of identification verification and limits, the SDK (used in Lambda) that we will need to use later will be region sensitive as well. Hence, please check that the region (in the menu bar) is correct.

Step 0b: Verify Identities

We will need some verified identities to do testing — at least two verified emails (one for sending, and one for receiving) or a verified domain. If required, please check my rant about DNS DKIM for domain verification.

Step 1: Create the Lambda function

In Lambda, click Create function. I used Author from scratch.

Enter a function name, and for Runtime, I used Python 3.9 (latest supported).

Under Change default execution role, I selected Create a new role with basic Lambda permissions.

Click Create function.

Step 2: Permissions for the Lambda role

Go to IAM (if you know the role’s name) or go to the Lambda function’s page, Configuration tab, and click on the Role name.

Under Permissions, click Attach policies. Search for AmazonSESFullAccess and add it to the role’s permissions.

Without this step, you will see “… is not authorized to perform …” message in the console.

Step 3: The function body

Under the Code tab, paste the following basic code (largely taken from AWS’ documentation) into the function body. Take note to change the SENDER and RECIPIENT addresses, as well as AWS_REGION. Here I’m setting the email addresses to take the input values from the event parameter.

import json
import boto3
from botocore.exceptions import ClientError
def lambda_handler(event, context):


# This address must be verified with Amazon SES.
SENDER = event['from_name']+' <'+event['from_address']+'>'

# If your account is still in the sandbox, this address must be verified.
RECIPIENT = event['to_address']


# the AWS Region you're using for Amazon SES.
AWS_REGION = "us-west-2"

# The subject line for the email.
SUBJECT = event["email_subject"]

# The email body for recipients with non-HTML email clients.
BODY_TEXT = ("Amazon SES Test (Python)\r\n"
"This email was sent with Amazon SES using the "
"AWS SDK for Python (Boto)."
)

# The HTML body of the email.
BODY_HTML = """<html>
<head></head>
<body>
<h1>Amazon SES Test (SDK for Python)</h1>
<p>This email was sent with
<a href='https://aws.amazon.com/ses/'>Amazon SES</a> using the
<a href='https://aws.amazon.com/sdk-for-python/'>
AWS SDK for Python (Boto)</a>.</p>
</body>
</html>
"""

# The character encoding for the email.
CHARSET = "UTF-8"
# Create SES client (edited)
ses = boto3.client('ses', region=AWS_REGION)
try:
#Provide the contents of the email.
response = ses.send_email(
Destination={
'ToAddresses': [
RECIPIENT,
],
},
Message={
'Body': {
'Html': {
'Charset': CHARSET,
'Data': BODY_HTML,
},
'Text': {
'Charset': CHARSET,
'Data': BODY_TEXT,
},
},
'Subject': {
'Charset': CHARSET,
'Data': SUBJECT,
},
},
Source=SENDER
)
# Display an error if something goes wrong.
except ClientError as e:
print(e.response['Error']['Message'])
else:
print("Email sent! Message ID:"),
print(response['MessageId'])


return {
'statusCode': 200,
'body': json.dumps('Hello from Lambda!')
}

Click Deploy to save changes.

Step 4: Test the Lambda function

Still inside Code tab, Click on the Test button. Select Configure test event.

Under Create new test event, enter a new Event name. My event template looks like this (note that I’m using verified email addresses):

{
"from_address": "sender@verifiedidentity.com",
"from_name": "Sender Name",
"to_address": "receiver@verifiedidentity.com",
"email_subject": "Test Lambda Function"
}

Click Create.

Return to the Code tab and click Test. A new tab “Execution results” will open beside the lambda_function tab.

At this point, the email should be successfully sent to your mailbox. You may need to check the spam/junk folder if you don’t see it in your inbox.

Step 5: Triggering the Lambda function

Now that the Lambda function is working, we need to place an API Gateway in front of it so that it can be triggered as an API.

Under Services, go to API Gateway. Click Create API.

For Choose an API type, I chose REST API (for “complete control over the request and response”).

Enter the API name, description and click Create API.

Inside the API’s page, Click on Actions. I chose Create Method > POST. Under Integration type, select Lambda Function, and type the name of the Lambda function created above. Click Save.

A popup saying “Add Permission to Lambda Function” appears. Click OK.

Now we are ready to test our API trigger. Because we have chosen POST, a text field appears for the Request Body.

{
"from_address": "sender@verifiedidentity.com",
"from_name": "Sender Name",
"to_address": "receiver@verifiedidentity.com",
"email_subject": "Test API Gateway"
}

Click Test.

There are probably other ways to do this, like using the HTTP API option instead. Please feel free to try alternatives (and let me know if there’s something better or mistakes here), I’m always learning from errors. Thank you for reading this far and I hope this helps!

--

--

Lois T.
Geek Culture

I make web-based systems and recently AI/ML. I write about the dev problems I meet in a simplified manner (explain-like-I’m-5) because that’s how I learn.