I have created a very basic simple function on AWS Lambda which will be used to accept form submissions.
Part of the function will be to send an email to a particular person, pretty simple. I am trying to use AWS SES in order to do this. I have setup the SES service etc, and verified the account I wish to send to and have been able to send out a test email. All works!!
Now when I try and do the same within AWS Lambda and use the aws sdk it doesn't send out the email. I don't get an error or anything.
Below is the code that I am using for the AWS Lambda function.
Has anyone had any experience using lambda and sending emails via ses, through a lambda function? Or even just using the node.js aws sdk would more than likely be helpful.
var aws = require('aws-sdk');
var ses = new aws.SES({
accessKeyId: 'myAccessKey',
secretAccesskey: 'mySecretKey',
region: 'eu-west-1'
});
exports.handler = function(event, context) {
console.log("Incoming: ", event);
var output = querystring.parse(event);
var eParams = {
Destination: {
ToAddresses: ["toAddress#email.com"]
},
Message: {
Body: {
Text: {
Data: output.Key1
}
},
Subject: {
Data: "Ses Test Email"
}
},
Source: "mysource#source.com"
};
console.log('===SENDING EMAIL===');
var email = ses.sendEmail(eParams, function(err, data){
if(err) console.log(err);
else {
console.log("===EMAIL SENT===");
console.log(data);
}
});
console.log("EMAIL CODE END");
console.log('EMAIL: ', email);
context.succeed(event);
};
It would appear that I had the context.succeed(event) placed in the wrong area of code.
Once I moved it into the sendEmail callback all worked.
var aws = require('aws-sdk');
var ses = new aws.SES({
accessKeyId: 'myAccessKey',
secretAccesskey: 'mySecretKey',
region: 'eu-west-1'
});
exports.handler = function(event, context) {
console.log("Incoming: ", event);
var output = querystring.parse(event);
var eParams = {
Destination: {
ToAddresses: ["toAddress#email.com"]
},
Message: {
Body: {
Text: {
Data: output.Key1
}
},
Subject: {
Data: "Ses Test Email"
}
},
Source: "mysource#source.com"
};
console.log('===SENDING EMAIL===');
var email = ses.sendEmail(eParams, function(err, data){
if(err) {
console.log(err);
context.fail(err);
} else {
console.log("===EMAIL SENT===");
console.log("EMAIL CODE END");
console.log('EMAIL: ', email);
console.log(data);
context.succeed(event);
}
});};
var aws = require("aws-sdk");
var ses = new aws.SES({ region: "us-west-2" });
exports.handler = async function (event) {
var params = {
Destination: {
ToAddresses: ["RecipientEmailAddress", ...],
},
Message: {
Body: {
Text: { Data: "Test" },
},
Subject: { Data: "Test Email" },
},
Source: "SourceEmailAddress",
};
return ses.sendEmail(params).promise()
};
This is because Lambda freezes the container when the function exits and any async processes are frozen, such as your email. This is especially true with Node. See Lambda Programming Model. http://docs.aws.amazon.com/lambda/latest/dg/lambda-introduction.html
My case is: when you set VPC, the issue happens cause of internet limitation access.
If you remove VPC, everything works fine.
It seems a AWS bug for me.
I opened today a AWS Support for it.
No anwers yet.
Related
When I make a call to ses.sendEmail the promise is never called. This is function in my lambda layer being called by my lambda function.
Here's the sendEmail function I have..
var aws = require ('aws-sdk')
var ses = new aws.SES ({region: 'us-west-2'});
exports.sendEmail = async (to, from, subject, body) => {
var params = {
Destination: {
ToAddresses : [to]
},
Message: {
Body: {
Html: {
Charset: "UTF-8",
Data : body
}
},
Subject: {
Charset: "UTF-8",
Data : subject
}
},
Source: from
}
console.log ("Sending email with params (stringify) : ", JSON.stringify(params));
console.log ("SES = ", ses);
await ses.sendEmail (params, function (err, data) {
console.log ("inside email");
});
}
I see the "Sending email with params (stringify)" log output and the "SES = " log output (which shows what appears to be a valid SES). But I never see "inside email" in the log nor do I receive any emails.
I'm also outside of the ses sandbox as I've gotten approval after setting up my domain and successfully verifying it all. I am on the us-west-2 region. I have not verified any email addresses. Just the domain (ie. name#user.host... user.host is verified) as I assumed if the domain is verified then any email from that domain should be good for use in the "from".
I have used async await instead of promises. I was facing the same issue. Email is dispatched successfully in my local computer but when I deploy the lambda and start testing it live. Emails were not sent to the email address mentioned.
async function sendMail(content, email) {
const params = {
Destination: {
ToAddresses: [email.toLowerCase()],
},
Message: {
Body: {
Text: {
Charset: "UTF-8",
Data: content.text,
},
Html: {
Charset: "UTF-8",
Data: content.html,
},
},
Subject: {
Charset: "UTF-8",
Data: content.subject,
},
},
Source: secretManagerData.senderEmail,
};
const sendPromise = await AWS_SES.sendEmail(params).promise();
console.log(sendPromise);
}
There are mistakes in calling the functions. When you call the function, you don't need to give a space with it.
var aws = require('aws-sdk'); // these are functions, call them without space
var ses = new aws.SES({region: 'us-west-2'}); // this
exports.sendEmail = async (to, from, subject, body) => {
try {
var params = {
Destination: {
ToAddresses : [to]
},
Message: {
Body: {
Html: {
Charset: "UTF-8",
Data : body
}
},
Subject: {
Charset: "UTF-8",
Data : subject
}
},
Source: from
}
console.log("Sending email with params (stringify) : ", JSON.stringify(params));
console.log("SES = ", ses);
const data = await sendmail(params);
return data;
} catch (e) {
console.log(e);
}
}
const sendmail = (params) => {
return new Promise((res, rej) => {
sendEmail(params = {}, (err, data)=>{
if (err) rej(err);
else res(data);
})
})
}
When you have to use async/await, you don't need to use callback and always use try/catch with async/await.
I got somehow same issue, it was resolved after used await before invoke sendEmail with promise and added API-Version for SES object.
const AWS = require ('aws-sdk')
const SES = new AWS.SES({apiVersion: '2010-12-01'});
try{
await SES.sendEmail(params).promise();
console.log("Email sent successfully");
}catch(err){
console.log("Failed to send email");
}
I am trying to fetch data from dynamodb from my lambda.I have written this code
exports.handler = async (event) => {
// TODO implement
var AWS = require('aws-sdk');
AWS.config.update({region: 'ap-south-1'});
var ddb = new AWS.DynamoDB({apiVersion: '2012-08-10'});
var params = {
TableName: 'my_table',
Key: {
'serial_number': {S: '17AB-574C-C1'}
},
};
ddb.getItem(params, function(err, data) {
if (err) {
console.log("Error", err);
} else {
console.log("Success", data.Item);
console.log(data);
}
});
};
This code works fine when I run locally but I get a null response when I run it on lambda.My lambda has dynamoDbfullAccess policy attached to it. Can anyone tell me what could be the reason?
I have the following two JS file. My problem is when i call the Calls.js which calls the Archive.js for archiving logs into DynamoDB the request times out.
I have tried out, many things, read about many things, tried in local/AWS environment without luck. What am i missing?
Link1, Link2, Link3, Link4, Link5,
Archive.js
module.exports.archive = archive;
...
function archive(input, callback){
AWS.config.update({
region: "eu-west-1",
endpoint: "http://localhost:8000"
});
var documentClient = new AWS.DynamoDB.DocumentClient({
httpOptions: {
agent: new https.Agent({
rejectUnauthorized: true,
secureProtocol: "TLSv1_method",
ciphers: "ALL"
})
}
});
...
var paramsPUT = {
TableName: "Logging",
Item: {
HashKey: dbID,
archiveEntry: archiveEntry
}
};
...
documentClient.put(paramsPUT, function(err, data) {
if (err) console.log(err);
if (data) console.log(data);
...
callback(data);
});
}
Calls.js
exports.handler(event, context, callback) => {
const archive = require("./..path..").archive;
...
context.callbackWaitsForEmptyEventLoop = false;
...
archive(input, callback);
...
}
I can not reproduce a timeout condition with your code. Your code is talking to an AWS endpoint at http://localhost:8000, so I assume you have DynamoDB local up and running, don't you ? Failling to have local DynamoDB running would cause the timeout.
That being said, I would strongly suggest to refactor your code to use Promise and the new async/await provided by NodeJS 8 instead of passing the Lambda callback around.
Here is the modified code.
const AWS = require("aws-sdk");
async function archive(input) {
return new Promise( (resolve, reject) => {
AWS.config.update({
region: "eu-west-1",
endpoint: 'http://localhost:8000'
});
//use client specific AWS configuration instead of the global one
const documentClient = new AWS.DynamoDB.DocumentClient();
var paramsPUT = {
TableName: "Logging",
Item: {
HashKey: "123",
archiveEntry: input
}
};
documentClient.put(paramsPUT, function (err, data) {
if (err) {
console.log("ERROR " + err);
reject(err);
}
console.log("Returned from DDB " + JSON.stringify(data, null,2));
resolve(data);
});
});
}
exports.handler = async (event, context, callback) => {
const result = await archive("abc");
callback(result);
}
// stuffs to test locally
callback = function (data) {
console.log("callback called with " + JSON.stringify(data,null,2));
}
event = context = {}
exports.handler(event, context, callback);
I'm new with Lambda & SQS and I'm trying to create a function to send emails, queued in an SQS service, but I don't understand how to call the process function that contains the send + delete queue methods.
Here bellow I paste my code:
'use strict';
const AWS = require('aws-sdk');
const SQS = new AWS.SQS({ apiVersion: '2012-11-05' });
const Lambda = new AWS.Lambda({ apiVersion: '2015-03-31' });
const ses = new AWS.SES({ accessKeyId: "xxxxxxxx", secretAccesskey: "xxxxxxx/xxxxxxxxx" });
const s3 = new AWS.S3({ apiVersion: "2006-03-01", region: "us-west-2" });
const QUEUE_URL = 'https://sqs.us-west-2.amazonaws.com/xxxxxxx/queue';
const PROCESS_MESSAGE = 'process-message';
function getPieceOfMail (path, mapObj, replace) {
return new Promise(function (resolve, reject) {
s3.getObject({
Bucket: "myBucket",
Key: "myKey/" + path
}, function (err, data) {
if (err) {
reject(err);
} else {
if (replace === true) {
var re = new RegExp(Object.keys(mapObj).join("|"), "gi");
data = data.Body.toString().replace(re, function (matched) {
return mapObj[matched.toLowerCase()];
});
resolve(data);
} else {
resolve(data.Body.toString());
}
}
});
});
}
function getRegisterSource (nickname, activate_link) {
var activate_link, pieces;
pieces = [
getPieceOfMail("starts/start.html", {}, false),
getPieceOfMail("headers/a.html", {}, false),
getPieceOfMail("footers/a.html", {}, false),
];
return Promise.all(pieces)
.then(function (data) {
return (data[0] + data[1] + data[2]);
})
.catch(function (err) {
return err;
});
}
function sendEmail (email, data) {
return new Promise(function (resolve, reject) {
var params = {
Destination: { ToAddresses: [email] },
Message: {
Body: {
Html: {
Data: data
},
Text: {
Data: data
}
},
Subject: {
Data: "myData"
}
},
Source: "someone <noreply#mydomain.co>",
};
ses.sendEmail(params, function (err, data) {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}
function process(message, callback) {
console.log(message);
// process message
getRegisterSource(event['nickname'], event['user_id'])
.then(function (data) {
return sendEmail(event["email"], data);
})
.catch(function (err) {
console.log("==ERROR==");
callback(err, err);
})
.finally(function () {});
// delete message
const params = {
QueueUrl: QUEUE_URL,
ReceiptHandle: message.ReceiptHandle,
};
SQS.deleteMessage(params, (err) => callback(err, message));
}
function invokePoller(functionName, message) {
const payload = {
operation: PROCESS_MESSAGE,
message,
};
const params = {
FunctionName: functionName,
InvocationType: 'Event',
Payload: new Buffer(JSON.stringify(payload)),
};
return new Promise((resolve, reject) => {
Lambda.invoke(params, (err) => (err ? reject(err) : resolve()));
});
}
function poll(functionName, callback) {
const params = {
QueueUrl: QUEUE_URL,
MaxNumberOfMessages: 10,
VisibilityTimeout: 10,
};
// batch request messages
SQS.receiveMessage(params, (err, data) => {
if (err) {
return callback(err);
}
// for each message, reinvoke the function
const promises = data.Messages.map((message) => invokePoller(functionName, message));
// complete when all invocations have been made
Promise.all(promises).then(() => {
const result = `Messages received: ${data.Messages.length}`;
callback(null, result);
});
});
}
exports.handler = (event, context, callback) => {
try {
if (event.operation === PROCESS_MESSAGE) {
console.log("Invoked by poller");
process(event.message, callback);
} else {
console.log("invoked by schedule");
poll(context.functionName, callback);
}
} catch (err) {
callback(err);
}
};
can somebody throw me some light to this?
Thanks in advice.
UPDATE
After so much misconception, I've decided to start looking on how the example of polling-SQS works provided by AWS.
There I've found that I lacked some basic SQS permissions, but solved now by adding the right policy:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": [
"lambda:InvokeFunction"
],
"Resource": ["*"]
}]
}
This allows Lambda.invoke() to call process().
When the process(message, callback) is called, if I console.log(message);, it seems that there's no message, although the queue is being cleared by the line SQS.deleteMessage(params, (err) => callback(err, message));
What I was trying was to combine my sendMail function that is currently working with a SQS service so I only have to push each message to the queue.
This is a common requirement where AWS SES has its own limitations in sending emails at once. If these limitations are violated, the SES account will sandbox itself. It seems like you have solved the problem using proper access credentials.
This code contains a Python3 Lambda code that can be used to handle a situation like this, where a Lambda polls from SQS using threading, and sends emails using SES, without exceeding the given limitations.
Link to Github Project.
You can also consider using the new feature in SQS, which is capable of invoking lambdas, when a new message is placed within SQS. But, be careful not to exceed the maximum number of lambda functions within the AWS Account region. (See this document)
I create Lambda function in AWS and use SES NodeJs to sending an email. At first, I was put my code in my localhost and it works very well and I had receive an email from it, but when I try put in Lambda, it does not give any respone and there is no email that I get in my email inbox.
Here is error message from Lambda :
{
"errorMessage": "2016-08-30T07:47:13.46Z afca06-6e85-a522-471b6c7 Task timed out after 20.00 seconds"
}
is there something wrong with my code if I want to put it in Lambda? or is there something that I missing code? or is there any way to solve it? Thanks for any help!
Here is my code :
exports.handler = function(event, context, callback) {
var aws = require('aws-sdk');
var ses = new aws.SES({
accessKeyId: 'AKIXXXXXXXXXXXXXXX',
secretAccessKey: 't1XXXXXXXXXXXXXXXXXXXXXXXXX',
region: 'us-west-2'
});
// send to
var to = ['my_email#gmail.com'];
// verified SES account
var from = 'my.verifiedmail#gmail.com';
ses.sendEmail( {
Source: from,
Destination: {
ToAddresses: to
},
Message: {
Subject: {
Data: 'A Message To You'
},
Body: {
Text: {
Data: 'Stop your messing around'
}
}
}
}
, function(err, data) {
if(err) {
//console.log(err);
context.done(null, { err });
}
else {
//console.log(data);
context.done(null, { data });
}
});
};