My First Lambda Function

May 19, 2015 12:36 · 974 words · 5 minute read cloud aws lambda

I thought is was about time I got familiar with AWS Lambda.

AWS Lambda is an event-driven computing service, which at that time of writing is available in US-EAST (Virginia) , US-WEST (Oregon) and EU (Ireland).

Lambda allows custom functions, written in NodeJs (server side JavaScript), to be executed, on-demand and in response to particular triggers, or events.

My Problem: We have an application which sends out thousands of emails a day. Our application uses another AWS Service SES (Simple Email Service).

When sending hundreds of emails, it’s not un-common to get a lot of delivery failures, or bounces. These bounces will eventually make their way back to a service mail box which someone or something can trawl through to build up a list of dead email addresses.

This to me seemed like a perfect candidate process for a bit of “cloudification”, and so we begin.

My project uses the following services,

NOTE: I’m using region N.Virginia for all non-global services.

SES (N.Virginia)
SNS (N.Virginia)
DynamoDB (N.Virginia)
Lambda (N.Virginia)
IAM (Global)

SES (Simple Email Service)

The first thing we need to do is setup SES to forward bounce and complaint notifications to an SNS topic.  You’ll need to go to your verified domain or email address to modify the notification settings.

If you don’t have an SNS topic setup, you can click on the “Click here to create a new Amazon SNS topic” click.

Simply enter a Topic Name and Display Name

Once you’ve done that, pick the SNS topic from the drop down list. I didn’t bother with Deliveries notifications because it would generate a lot to notifications.

DynamoDB 

The next step in my little project was to create a new DynamoDB table in which to store the information I’m going to gather from my bounce notifications.

The details of DynamoDb table creation are beyond the scope of this article, but I’ve included some high level steps below.

Create a new table, I called mine sesNotifications. I set a hash and range primary key type for which I’ll be using the SNS Topic Arn (Amazon Resource Name) as the hash and the snsPublishTime stamp as the range. This will give me a nice sorted range index based based on the time the notification was received.

I also added the messageId as a global secondary index so that in the future, I could search for notifications based on the SES Message IDs (another project we’re working on).

You’ll next need to specific the read and write capacity for the database … I’ll leave that one to you.

Click continue through the remainder of the screens and your DynamoDB table will be created for you.

Lambda 

Whilst we wait for our DynamoDB table to create, we can now move on to the exciting part of the project. Lambda.

Jump to the Lambda management consoleand click on the massive blue button which says “Get Started Now” to begin building your first Lambda function.

Some points to note, Lambda functions are written in javascript, they can be developed locally and uploaded as Zip files, including all of the necessary packages. I’m not there yet, so I’m going to do everything inline, via the GUI.

First things first, give the function a name and a description:

In the function code window, you can choose from a number of templates, or simply create your own:

My function was initially based on the SNS Message template, and here it is.

var aws \= require('aws-sdk');  
var ddb \= new aws.DynamoDB({params: {TableName: 'sesNotification'}});  
   
exports.handler \= function(event, context) {  
  var SnsMessageId \= event.Records\[0\].Sns.MessageId;  
  var SnsPublishTime \= event.Records\[0\].Sns.Timestamp;  
  var SnsTopicArn \= event.Records\[0\].Sns.TopicArn;  
  var SnsMessage \= event.Records\[0\].Sns.Message;  
  var LambdaReceiveTime \= new Date().toString();  
    
  var MessageContent \= JSON.parse(SnsMessage);  
  var SesNotify \= MessageContent\['notificationType'\];  
  var SesFailedTarget \= MessageContent\['bounce'\]\['bouncedRecipients'\]\[0\]\['emailAddress'\];  
  var SesFailedCode \= MessageContent\['bounce'\]\['bouncedRecipients'\]\[0\]\['diagnosticCode'\];

var SesMessageId = MessageContent[‘mail’][‘messageId’];

var itemParams = {Item: {SnsTopicArn: {S: SnsTopicArn},
SnsPublishTime: {
S: SnsPublishTime},
SnsMessageId: {S: SnsMessageId},
LambdaReceiveTime: {S: LambdaReceiveTime},
SnsMessage: {S: SnsMessage},
SesNotificationType: {S: SesNotify},
SesTarget: {S: SesFailedTarget},
SesError: {S: SesFailedCode},

```
        SesError: {S: SesMessageId}
```}};   ddb.putItem(itemParams, function() {     context.done(null,'');   }); }; 
```

  
With code in place, we need to assign a role to the Lambda function to allow it to interact with the DynamoDB table we've created.  
  

[![](http://2.bp.blogspot.com/-o9VQlz8L36E/VVrIWaLIwJI/AAAAAAAAJ0c/BqZJJe25jQs/s400/Screenshot%2B2015-05-19%2B15.21.11.png)](http://2.bp.blogspot.com/-o9VQlz8L36E/VVrIWaLIwJI/AAAAAAAAJ0c/BqZJJe25jQs/s1600/Screenshot%2B2015-05-19%2B15.21.11.png)

  
  
To keep things simple at this stage, you could choose "Basic with Dynamo", this role will allow the function all of the rights it needs to interact with DynamoDB.  
  
If you want to be a little more granular, you can use the "Basic execution role" and add a role policy that looks a bit like this ...  
  

```
{  
  "Version": "2012-10-17",  
  "Statement":\[  
    {  
      "Sid":"AllowDynamoDbAccess",  
      "Effect":"Allow",  
      "Action":\["dynamodb:\*"\],  
      "Resource":\["arn:aws:dynamodb:us-east-1:<blahblahblah>:table/sesNotification"\]    }  
  \]  
}  

```

  
Once the role / policy has been created, assign it to the function and click the big blue "Create Lambda Function" button at the bottom of the screen.  
  

SNS (Simple Notification Service) 
----------------------------------

  
The final step in the project to bring it all together is to push the SNS notifications to our newly created Lambda function.  
  
Hop on over to the SNS management console[![](http://1.bp.blogspot.com/-HNEOFiSrvfk/VVrJEUSvKyI/AAAAAAAAJ0w/ExscDklK6-0/s1600/Screenshot%2B2015-05-19%2B15.24.09.png)](http://1.bp.blogspot.com/-HNEOFiSrvfk/VVrJEUSvKyI/AAAAAAAAJ0w/ExscDklK6-0/s1600/Screenshot%2B2015-05-19%2B15.24.09.png)  and track down the SNS topic we created earlier.  
  

[![](http://2.bp.blogspot.com/-iAK5Lsptlo8/VVrI3dfiNFI/AAAAAAAAJ0k/j7oLfQm6uu0/s640/Screenshot%2B2015-05-19%2B15.22.54.png)](http://2.bp.blogspot.com/-iAK5Lsptlo8/VVrI3dfiNFI/AAAAAAAAJ0k/j7oLfQm6uu0/s1600/Screenshot%2B2015-05-19%2B15.22.54.png)

  
  
Click on the highlighted ARN to view the topic details. Click on the "Create Subscription" button  
  

[![](http://4.bp.blogspot.com/-snXrZ3Cr2Vo/VVrJiaqqDDI/AAAAAAAAJ08/Bpn4-frhg4M/s1600/Screenshot%2B2015-05-19%2B15.26.24.png)](http://4.bp.blogspot.com/-snXrZ3Cr2Vo/VVrJiaqqDDI/AAAAAAAAJ08/Bpn4-frhg4M/s1600/Screenshot%2B2015-05-19%2B15.26.24.png)

  
The topic ARN will be auto populated, choose AWS Lambda from the protocol list and choose the new Lambda Function from the Endpoint drop list.  
  

[![](http://1.bp.blogspot.com/-rzUtFXLBg38/VVrJ5QwKW9I/AAAAAAAAJ1E/KcilzUHYwTo/s640/Screenshot%2B2015-05-19%2B15.27.18.png)](http://1.bp.blogspot.com/-rzUtFXLBg38/VVrJ5QwKW9I/AAAAAAAAJ1E/KcilzUHYwTo/s1600/Screenshot%2B2015-05-19%2B15.27.18.png)

  
  
Click Create Subscription.  
  
And that's it. We should now be able to send a few emails to addresses which don't exist and wait for the bounces to start showing up in DynamoDb  
  
I have a small PHP script which I use to test sending emails via SES, I modified a few parameters with a non-existent email address and hey presto, this is what we get.  
  

[![](http://1.bp.blogspot.com/-OhARVFGu9Ys/VVvBgm3gMJI/AAAAAAAAJ24/o77zHWqXp0c/s640/Screenshot%2B2015-05-20%2B09.00.01.png)](http://1.bp.blogspot.com/-OhARVFGu9Ys/VVvBgm3gMJI/AAAAAAAAJ24/o77zHWqXp0c/s1600/Screenshot%2B2015-05-20%2B09.00.01.png)

  

  
We've got the table items containing the SES Message ID, the failed target address and the Error Code. All very useful.  
  

[![](http://4.bp.blogspot.com/-ufAzGKC12nM/VVrL6sY5P2I/AAAAAAAAJ1M/gUrXMI_i2_g/s640/Screenshot%2B2015-05-19%2B15.36.28.png)](http://4.bp.blogspot.com/-ufAzGKC12nM/VVrL6sY5P2I/AAAAAAAAJ1M/gUrXMI_i2_g/s1600/Screenshot%2B2015-05-19%2B15.36.28.png)