AWS Messaging & Targeting Blog
Maintain consistency in emails with custom content using Amazon SES templates
When sending emails, content creators often want to add custom content such as images or videos while maintaining consistency in their messages. They also want to send those emails automatically once new content is ready. In this blog, we will show you how to create templates for emails with a common theme by combining Amazon Simple Email Service (Amazon SES) templates, AWS Lambda, Amazon Simple Storage Service (Amazon S3), and Amazon SES templates.
Promotional content (such as logos, images, videos, and more) can be stored, managed, and hosted in Amazon S3. You can then embed this content into promotional emails without making any changes to email templates or email processing. You can trigger a Lambda function to send promotional emails with the newly added content through using the Amazon SES SDK.
This post shows readers how to:
- Create an Amazon SES email template with tags to be replaced by image URLs
- Upload those templates to Amazon SES
- Setup an AWS CloudFormation stack using the AWS Cloud Development Kit(AWS CDK)
- Create a Lambda function and Amazon S3 bucket to send emails using the AWS SDK for Javascript
Solution Architecture
The proceeding image shows the architecture you will build as part of this post. You will use the AWS CDK to provision an Amazon S3 bucket, Lambda, and AWS Identity and Access Management(IAM) permissions. You will also use the AWS Command Line Interface(AWS CLI) to upload and manage our Amazon SES templates.
The architecture allows you to upload content to S3 which will trigger a Lambda function. That Lambda will form an Amazon SES request using the template you have uploaded and embed the S3 content as a parameter which will be sent to the user and render in their email client.
Metadata
Time to Read: ~ 20 minutes
Time to Complete: ~ 15 minutes
Cost to Complete: Free Tier
Learning Level: Intermediate
Services Used: Amazon Simple Email Service, Amazon Simple Storage Service, AWS Lambda
Prerequisites
For this walkthrough, you should have the following prerequisites:
- An AWS account
- The AWS Command Line Interface
- The AWS Cloud Development Kit
- A text editor, you may prefer an IDE such as AWS Cloud9
- Basic knowledge of Lambda functions and the AWS SDK for Javascript
- A verified email address in Amazon SES by following the Amazon SES Guide
Solution Overview
You will walk through creating Amazon SES templates and then a CloudFormation stack using the AWS CDK. You will then create a template file, a lambda directory, and a CDK application directory which should all be in the same level in your package structure in order to follow these steps explicitly.
Step 1: Create an Amazon SES template in JSON with a tag representing your image URL and upload via the CLI
Step 2: Initialize an AWS CDK package using the AWS CDK CLI and add necessary dependencies
Step 3: Initialize a NodeJS AWS Lambda package
Step 4: Provision an Amazon S3 bucket and Lambda in your AWS CDK app
Step 5: Configure your Lambda to be triggered when objects are added to S3
Step 6: Configure your Lambda’s IAM role to allow sending emails via Amazon SES
Step 7: Write necessary code for AWS Lambda to send emails via Amazon SES
Step 8: Deploy and Test!
Step 1: Create an Amazon SES Email template
Amazon SES Email templates are defined as a JSON object containing:
- TemplateName – name of the template, must be unique across email templates and will be used by our Lambda to pass to Amazon SES
- SubjectPart – represents the subject of the email
- HtmlPart – represents the body of the email
- TextPart – when email clients cannot render HTML, this is displayed instead of HtmlPart
More detailed information about email templates can be found in the Amazon SES Developer Guide.
1. Open your text editor and save the empty file as email-template.json
2. Paste the following into your json file and save your changes
{
"Template": {
"TemplateName": "MyTemplate",
"SubjectPart": "Greetings Customer",
"HtmlPart": "<img src={{imageURL}} alt=\"logo\" width=\"100\" height=\"100\">",
"TextPart": "Dear Customer,\r\nCheck out our website for new promotional content."
}
}
This template has a single tag called imageURL which will be replaced during execution with our content’s S3 URL.
3. Run the following AWS CLI command to upload your template to Amazon SES
aws ses create-template --cli-input-json file://email-template.json
4. Once your template has been uploaded, you can confirm its creation by logging into the AWS Console, navigating to Amazon SES, and then selecting Email Templates
Step 2: Initialize an AWS CDK package and add necessary dependencies
In this section, you will be using the AWS CDK CLI to initialize a new code package and add the dependencies for Lambda and Amazon S3.
1. Create a new directory at the same level as email-template.json called email-infrastructure
2. Navigate to the promotional-email-infrastructure directory and run the following CDK CLI command to generate the skeleton for your cdk application
cdk init app --language=typescript
3. Add dependencies for Amazon S3, Lambda, and IAM by adding the following lines to your dependencies section of your package.json and then run npm install
"@aws-cdk/aws-lambda": "1.86.0"
"@aws-cdk/aws-lambda-event-sources": "1.86.0"
"@aws-cdk/aws-s3": "1.86.0"
"@aws-cdk/aws-iam": "1.86.0"
Make sure to install the version of these dependencies that matches the version of the aws-cdk stack so you don’t run into compatibility issues.
Step 3: Create a NodeJS Lambda package
In this step, you will create the barebones for our Lambda function that will call Amazon SES and revisit in step 7 to implement the handler
1. Create a new directory at the same level as email-template.json called email-lambda
2. Add a package.json file in the email-lambda directory that looks like the following
{
"name": "email-lambda",
"version": "1.0.0",
"main": "index.js",
"dependencies": {
"aws-sdk": "2.831.0"
}
}
3. Add a file called index.js, this will be our Lambda handler and will look like the following for now. Make sure to insert your verified email address into the testAddress variable, this will be used later as both your to and from address for testing.
var AWS = require("aws-sdk");
var ses = new AWS.SES({apiVersion: "2010-12-01"});
var testAddress = "INSERT_VERIFIED_EMAIL_HERE";
exports.handler = async function(event) {
console.log(JSON.stringify(event));
return "200";
}
4. Finish this step by running npm install
in the email-lambda directory to install the aws-sdk dependencies you will use in a later step. Your top-level directory structure should look like the following:
- email-template.json – contains your email template from step 1
- email-infrastructure – contains your CDK stack from step 2
- email-lambda – contains your email lambda function code from step 3
Step 4: Provision an Amazon S3 bucket and Lambda function in your CDK app
In this step, you will add an Amazon S3 bucket and a NodeJS Lambda function into our CDK application based on what you setup in previous steps. After this, you will connect all the pieces together.
1. Import all the services you need into our CDK stack construct. The imports you will need are listed below, copy them into your editor in the imports section.
import * as s3 from '@aws-cdk/aws-s3';
import * as lambda from '@aws-cdk/aws-lambda';
import * as lambdaEventSource from '@aws-cdk/aws-lambda-event-sources';
import * as iam from '@aws-cdk/aws-iam';
import path = require('path');
2. Add S3 bucket to CDK App by adding an instance of the Bucket construct
const promotionalContentBucket = new s3.Bucket(this, "DOC-EXAMPLE-BUCKET");
3. Similarly, add your Lambda function by creating an instance of the Function construct referencing your Lambda function package by path
const emailLambda = new lambda.Function(this, "EmailLambda", {
code: lambda.Code.fromAsset(path.join(__dirname, "../../email-lambda")),
handler: "index.handler",
runtime: lambda.Runtime.NODEJS_12_X
});
4. Execute npm run build
in your CDK directory to ensure you’ve setup the package correctly. You can get additional help from the Troubleshooting Guide for CDK
Step 5: Configure Lambda with an S3 Event Source
In this step, you will configure your Lambda function to be triggered when objects are added to your Amazon S3 bucket by using the Lambda event sources module for CDK.
1. Create an instance of the S3EventSource construct in your stack for OBJECT_CREATED events only because you don’t want to trigger a lambda invocation when an object is removed for this post
const s3EventSource = new lambdaEventSource.S3EventSource(promotionalContentBucket, {
events: [s3.EventType.OBJECT_CREATED]
});
2. Now that you have an event source defined, you need to add the event source to your Lambda function
emailLambda.addEventSource(s3EventSource);
3. Add the Amazon S3 bucket’s domain name as an environment variable so you can reference objects by URL by adding the environment property to your Lambda function like below.
environment: {
"BUCKET_DOMAIN_NAME": promotionalContentBucket.bucketDomainName
}
Step 6: Configure the email Lambda IAM role
In this step, you will add an IAM policy statement to your email Lambda’s execution role so it can call Amazon SES.
1. Add an IAM PolicyStatement construct with effect ALLOW on all resources with SendTemplatedEmail action
const emailPolicyStatement = new iam.PolicyStatement({
effect: iam.Effect.ALLOW,
actions: ["ses:SendTemplatedEmail"],
resources: ["*"]
});
2. Finish this step by adding your newly created policy to the Lambda execution role
emailLambda.addToRolePolicy(emailPolicyStatement)
Step 7: Add Lambda implementation to send templated emails
You will use the AWS NodeJS Amazon SES SDK to send emails using the SendTemplatedEmail API. Our implementation will assume a batch of size 1 for each Lambda invocation for simplicity.
1. Replace your function handler in the email Lambda function with the code below. This will read the S3Event’s first record, prepare parameters for the Amazon SES SDK call and invoke the sendTemplatedEmail function with the imageURL embedded into your previously created template.
exports.handler = async function(event) {
console.log(JSON.stringify(event));
let s3Object = event.Records[0];
let sendEmailParams = {
Destination: {
ToAddresses: [testAddress]
},
Template: 'MyTemplate',
TemplateData: JSON.stringify({
"imageURL": process.env.BUCKET_DOMAIN_NAME + "/" + s3Object.s3.object.key,
}),
Source: testAddress
};
let response = await ses.sendTemplatedEmail(sendEmailParams).promise();
return response;
}
2. Deploy your stack with the CDK CLI by running cdk deploy
, this may take a couple minutes. If you run into problems, see the Troubleshooting Guide for CDK.
Step 8: Test your System
At this point, you should have an Amazon SES template uploaded to your account as well as a CloudFormation stack that contains an Amazon S3 bucket and a Lambda function that is triggered when objects are added to that bucket and had permissions to invoke Amazon SES APIs. Now you will test the system by adding an image into our Amazon S3 bucket.
1. Log in to the AWS Console
2. Navigate to Amazon S3
3. Select your promotional content bucket from the list of buckets
4. Click Upload on the right-hand side of the screen
5. Add an image from your computer by clicking Add files
6. Scroll down to the bottom and expand Additional Upload Options
7. Scroll down to Access Control List
8. Select the check boxes for Read for Everyone(public access) so the images are accessible when the user opens their email
9. Scroll down to the bottom and select Upload
10. Done! You should have an email in your inbox shortly that renders the image you just uploaded. Check the Lambda logs and errors in case you don’t see your email.
Cleaning up
To avoid incurring future charges, delete your CloudFormation stack by running cdk destroy
or manually through the AWS Console. Keep in mind, by default Amazon S3 buckets won’t be deleted so you will need to navigate to Amazon S3 in the AWS Console, clear the bucket of any objects and then manually delete the resource.
Conclusion
Congratulations! You now have an understanding of how to combine Amazon SES templates with Amazon S3 and Lambda to inject custom images into emails without the need for any servers and have launched this stack using the AWS Cloud Development Kit.
Author Bio
My name is Seth Theeke, I work as a Software Development Engineer in Amazon Freight. I’ve been working with AWS since 2016 and hold a Developer Associate Certification. I love soccer and I love software engineering, the simplest things in life!