SMS text not received but no errors from AWS/Lamda - node.js

Migrating and EC2 LAMP stack into serverless architecture with Lambda handling the SMS text messages.
My PHP code also works fine and from the console:
aws sns publish --phone-number 0044xxxxxxxxxx --message boo --region eu-west-1
Also works
When I run the NodeJS in Lambda from the command line I get "Status": 202 but no text.
When I run that same code from the TEST button in the Inline Code Editor on AWS I get "Successful", clean logs but no text..
The code failing is:
// dependencies
var AWS = require('aws-sdk');
var sns = new AWS.SNS();
exports.handler = function(event, context) {
var params = {
MessageStructure: 'string',
PhoneNumber: '0044xxxxxxxxxx',
Message:'An error occurred'
};
sns.publish(params, function(err, data) {
if (err) {
console.log(err, err.stack); // an error occurred
context.done(err, 'Finished with Errors!');
return;
}
else console.log(data); // successful response
});
context.done(null,'finished');
};
This is also code I got from another question in SO which was shown to work for another user.
Any idea what could be the issue?
Many thanks
Kevin

A few things you might want to look into:
You'll need to make sure you're using either Node.js runtime v6.10 or v4.3. (Node v0.10.42 is currently marked as deprecated. AWS recommends migrating existing functions to the newer Node.js runtime versions as soon as possible)
The IAM role for your lambda function needs to have an Allow rule for the sns:Publish action.
AWS recommends that specify the phone number using the E.164 format. For example: +44xxxxxxxxxx. (more info)
Also, AWS strongly recommends updating any use of the context method and replacing it with the callback approach (more info). For example:
const AWS = require("aws-sdk");
const sns = new AWS.SNS({apiVersion: "2010-03-31"});
exports.handler = (event, context, callback) => {
const params = {
PhoneNumber: "+44xxxxxxxxxx", // E.164 format.
Message: "STRING_VALUE",
MessageStructure: "STRING_VALUE"
}
sns.publish(params, (err, data) => {
if (err) {
console.error(`Error ${err.message}`);
callback(err);
} else {
console.log("Success");
callback(null, data); // callback instead of context.
}
}
};

Please refer to the answer SO Link, it worked for me. By default the message type is set as Promotional and you have to override it as transactional.

Related

creating s3 bucket and folders inside it using node js in aws lambda function

I am both new to Node js and AWS. I am trying to create a bucket in S3 using node js in lambda function. Consequently, I am trying to create folders inside this S3 bucket.
I followed all the questions answered before and tried different iterations of code, but none of them seem to be working. Following is my code which is executing without giving any issues, yet the bucket and the folders are not getting created.
const AWS = require('aws-sdk');
let s3Client = new AWS.S3({
accessKeyId: '<access_key_id>',
secretAccessKey: '<secret_access_key>'
});
var params = {
Bucket : 'pshycology06'
};
exports.handler = async (event, context, callback) => {
// call spaces to create the bucket
s3Client.createBucket(params, function(err, data) {
if (err) {
console.log("\r\n[ERROR] : ", err);
} else {
console.log("\r\n[SUCCESS] : data = ",data);
}
});
};
The code for creating folders inside the Lambda function is as following --
var AWS = require('aws-sdk');
AWS.config.region = 'us-east-1';
var s3Client = new AWS.S3({apiVersion: '2006-03-01'});
exports.handler = async (event, context) => {
let params1 = { Bucket: 'travasko', Key: '2/dir1/dir2', Body:'body does not matter' };
s3Client.putObject(params1, function (err, data) {
if (err) {
console.log("Error creating the folder: ", err);
} else {
console.log("Successfully created a folder on S3");
}
});
Both of them doesn't work. I read a lot of documents on this issue and answers previously asked, but none of them are working for me.
The lambda function has a timeout of 1 minute. It has following policies for the IAM role -
1. AmazonRDSFullAccess
2. AmazonS3FullAccess
3. AWSLambdaVPCExecutionRole
The VPC security group is the default one.
Also, when I am trying to create the same bucket using the following AWS CLI command, it creates the bucket.
aws s3api create-bucket --bucket psychology06 --region us-east-1
I am not sure, where am i making a mistake.
Make sure the bucket with same name is not present.Please share log if possible.
You need to chain the .promise() method to your aws-sdk calls and await on them because you are creating async functions.
await s3Client.createBucket(params).promise();
await s3Client.putObject(params1).promise();
Furthermore, S3 doesn't work with directories although you may be thrown over by the way the S3 console looks like when you add / to your filenames. You can read more about it here
As you are new, always try aws cli(not recommended) and then search for search for equivalent sdk function while implementing.As it(your code) is async it won't wait until the call back function executes , so you can try something like below.(This is not actual solution , it just tells how to wait until the call back does it's work.)
'use strict'
var AWS = require('aws-sdk');
AWS.config.region = 'us-east-1';
var s3Client = new AWS.S3({ apiVersion: '2006-03-01' });
exports.handler = async (event, context) => {
let params1 = { Bucket: 'travasko', Key: '2/dir1/dir2', Body: 'body does not matter' };
try {
let obj = await something(params1);
callback(null, obj);
}
catch (err) {
callback('error', err)
}
}
async function something(params1) {
return new Promise(async (resolve, reject) => {
await s3Client.putObject(params1, function (err, data) {
if (err) {
console.log('Error creating the folder:', err);
reject('error during putObject');
} else {
console.log('success' + JSON.stringify(data));
resolve('success');
}
});
});
}
To your question in the comments :
Hi Vinit , let me give you little background , the question you have asked is very generic. Firstly VPC is something which you create where you will have your organization private and public subnets that are used to run your ec2 or any hosted services (non-managed services by aws). But as lambda is managed service it runs in aws vpc , they usually take your code and lambda configurations and execute the code.Now coming to your question if we attach vpc in your lambda configurations ( only if your lambda needs to use services hosted in your vpc, else don't use it) then as we discussed lambda runs in aws vpc , so during cold start it created an ENI(think of it as elastic IP) and tries to communicate with your VPC. Before Re-invent an ENI was created for each lambda that was the reason it takes time for the first time and lambda used to time out even though your execution takes lesser time. Now after re-invent EIP's are created per subnet per security group. So now coming to your question when u have attached vpc, if lambda execution is taking more time or not working as expected, then you have to see how your vpc(configs, routes, subnets) is set up and it's very hard to answer as so many parameters are involved unless we debug. So short answer do not attach vpc if your function(code) does not need to talk to any of your own managed instances in vpc (usually private subnet
) etc.
Since, you are using async functionality. Thus, you have to use await on calling "s3Client.createBucket". Then resolve the received promise.
For creating folders, use trailing "/". For example "pshycology06/travasko/".
Do post error logs if these doesn't work.

I get 'Error [ConfigError]: Missing region in config' each time I attempt AWS lambda function call from NodeJS

I'm new to AWS lambda functions and was given some sample code (below -- in the actual code I fill in the placeholders correctly, of course). But each time I call the lambda function, I get
Error [ConfigError]: Missing region in config
at Request.VALIDATE_REGION (/Users/abc/Documents/projects/bot/node_modules/aws-sdk/lib/event_listeners.js:92:45)
Even though the region is set in aws.config.update. I'm not sure what to do to fix this. I've tried removing the call to aws.config.update, removing the reference to region, but nothing helps.
I'd also be keen to know if there's a way to load the credentials from the shared file in ~/.aws/credentials, instead of having to enter them directly here.
Thanks for any help!
const aws = require('aws-sdk');
// do this only in dev, in prod you will not need to put your keys...
aws.config.update({accessKeyId: '<YOU ACCESS KEY>', secretAccessKey: '<YOUR SECRET>', region:'ap-northeast-1'});
const lambda = new aws.Lambda();
function invokeLambda(options) {
return new Promise((resolve, reject) => {
lambda.invoke(options, (err, data) => {
if (err) {
return reject(err);
}
return resolve(data);
});
});
}
const sampleCall = async () => {
try {
const options = {
FunctionName: '<NAME OF THE FUNCTION YOU WANT TO INVOKE>',
Payload: '<The JSON.stringify of the object you want to provide as parameter>',
}
const result = await invokeLambda(options);
console.log(result);
} catch (err) {
console.error(err);
}
};
You can configure the region before using any services.
like this:
var AWS = require('aws-sdk');
AWS.config.update({region:'us-east-1'});
const lambda = new aws.Lambda();
AWS services such as lambda has the ability to assume an IAM Role. Because the applications does not need to store credentials.
here is what you should do:
The IAM role is attached to the lambda
Permissions are then attached to the IAM role
Basically you can attach the same policy that your IAM user is using to the IAM role. After doing that, you can remove credentials from the code.
Reference:
https://aws.amazon.com/blogs/security/how-to-create-an-aws-iam-policy-to-grant-aws-lambda-access-to-an-amazon-dynamodb-table/
Hey No need for configure the whole AWS setttings. if you want to keep it unconfigured and not for all of your request to use the region.
The lambda constructor receive 'region' param just set it to your desired region.
Example:
const lambda = new Lambda({
region: 'us-east-1'
});
lambda.invoke({ FunctionName: 'your-function-name'},
function(err, data) {
if(err) {
console.log(err, err.stack);
}
else {
console.log(data);
}
}
);

AWS Lambda :: How to test my code on my local ubuntu machine?

I have this Lambda function code which is invoked by an SQS.
SQS triggers my Lambda function ( in nodeJS).
Lambda will also send out an SES email. Is there a way I can test this on my local Ubuntu rather than always using AWS web console?
Any help is appreciated.
Here is my Lambda NodeJS code: This code works only on AWS Lambda. When I run
$node index.js , it does not send out SES email.
var aws = require("aws-sdk");
var nodemailer = require("nodemailer");
aws.config.loadFromPath('aws_config.json');
var ses = new aws.SES();
var s3 = new aws.S3();
// Set the region
aws.config.update({region: 'us-west-2'});
exports.handler = function (event, context, callback) {
const response = {
statusCode: 200,
body: JSON.stringify({
message: 'SQS event processed.',
input: event,
}),
};
console.log('event: ', JSON.stringify(event.Records));
result = JSON.stringify(event.Records)
result = result.replace(/(^\[)/, '');
result = result.replace(/(\]$)/, '');
var resultObj = JSON.parse(result);
var idCustomer = resultObj.body;
console.log('===SENDING EMAIL====');
// Create sendEmail paramssd
var params = {
Destination: {
/* required */
CcAddresses: [
'XXXXX#gmail.com',
/* more items */
]
},
Message: {
/* required s*/
Body: {
/* required */
Html: {
Charset: "UTF-8",
Data: "BODY:"
},
Text: {
Charset: "UTF-8",
Data: "TEXT_FORMAT_BODY"
}
},
Subject: {
Charset: 'UTF-8',
Data: idCustomer
}
},
Source: 'xxxx#eeeee.com', /* required */
ReplyToAddresses: [
'wwwwww#wwwwwwwww.com',
/* more items */
],
};
// Create the promise and SES service object
var sendPromise = new aws.SES({apiVersion: '2010-12-01'}).sendEmail(params).promise();
// Handle promise's fulfilled/rejected states s
sendPromise.then(
function (data) {
console.log("Successfully sent using SES");
console.log(data.MessageId);
}).catch(
function (err) {
console.log("An Error occured while senting using using SES");
console.error(err, err.stack);
});
};
You should definetely take a look at SAM LOCAL. It is a tool developed by the AWS team specifically for testing lambdas.
https://github.com/awslabs/aws-sam-cli
Publishes a version of your function from the current snapshot of
$LATEST. That is, AWS Lambda takes a snapshot of the function code and
configuration information from $LATEST and publishes a new version.
The code and configuration cannot be modified after publication. For
information about the versioning feature, see
It is easy to use, you just type
sam local invoke --event event.json
And behind the scenes it will run a docker cotnainer for your lambda and call it.
Regarding your SES, you should put a small if(SAM_LOCAL) condition in the code and call the real one only if not in local mode. Note that SAM_LOCAL is env variable set by the SAM LOCAL tool when you run a function locally.
Good luck !
If you want to use aws as a backend - serverless framework is probably what you looking for https://serverless.com/ If you want to test your code without executing lambda on aws backend take a look at localastack framework https://github.com/localstack/localstack

AWS SES create template with lambda function always return null

so on my first time learning AWS stuff (it is a beast), I'm trying to create e-mail templates, I have this lambda function:
// Load the AWS SDK for Node.js
var AWS = require('aws-sdk');
// Set the region
AWS.config.update({ region: "us-east-1" });
exports.handler = async (event, context, callback) => {
// Create createTemplate params
var params = {
Template: {
TemplateName: "notification" /* required */,
HtmlPart: "HTML_CONTENT",
SubjectPart: "SUBJECT_LINE",
TextPart: "sending emails with aws lambda"
}
};
// Create the promise and SES service object
const templatePromise = new AWS.SES({ apiVersion: "2010-12-01" })
.createTemplate(params)
.promise();
// Handle promise's fulfilled/rejected states
templatePromise
.then((data) => {
console.log(data);
callback(null, JSON.stringify(data) );
// also tried callback(null, data);
}, (err) => {
console.error(err, err.stack);
callback(JSON.stringify(err) );
});
as far as I am understanding, this function should return me a template? an object, anything? when I use the lambda test functionality I always got null in the request response
does anyone know what I am doing wrong here?
edit: and It is not creating the e-mail template, I check the SES Panel - email templates and it is empty
edit2: if I try to return a string eg: callback(null, "some success message"); it does return the string, so my guess is something wrong with the SES, but this function is exactly what we have in the AWS docs, so I assume it should just work..
Try not to resolve the Promise and change your code to just returning it as-is:
return await templatePromise;
which should present you some more detail of what is really going wrong in your code - it might be some hidden access issue - so you might need to adjust the role your lambda function is using. createTemplate on the other side should not return much in case of successful execution but just create the template.
Also try to follow the following try/catch pattern when using async (as described here in more detail: https://aws.amazon.com/de/blogs/compute/node-js-8-10-runtime-now-available-in-aws-lambda/)
exports.handler = async (event) => {
try {
data = await lambda.getAccountSettings().promise();
}
catch (err) {
console.log(err);
return err;
}
return data;
};

AWS node.js automatic retry on failed batchWrite()

According to this aws doc http://docs.aws.amazon.com/general/latest/gr/api-retries.html automatic retry feature is build in the aws sdk in my case node.js aws sdk. I configured the DocumentClient object like this:
var dynamodb = new AWS.DynamoDB.DocumentClient({
region: 'us-west-2',
retryDelayOptions: {base: 50},
maxRetries: 20
});
but I still cannot make it auto-retry for me. I want to auto-retry with all UnprocessedItems as well.
Can you point me to where is my mistake?
Thanks
The retryDelayOptions and maxRetries are the options present on AWS.DynamoDB. The DocumentClient has to be configured by setting the DynamoDB service.
var dynamodb = new AWS.DynamoDB({maxRetries: 5, retryDelayOptions: {base: 300} });
var docClient = new AWS.DynamoDB.DocumentClient({service : dynamodb});
The AWS Client SDKs all have built-in mechanisms for retry indeed, however those retries are at the request level. That means that any request that gets rejected by the server with a 500-level error, or in some cases, a 400-level throttling error will get automatically retried based on the configured settings.
What you are asking for is business-layer retry behavior which is NOT built into the SDK. The UnprocessedItems collection contains items that were rejected by the service for various reasons and you have to write your own logic to handle those.
After sending Response we can handle unprocessed Item's background Process until all unprocessed Items should be complete. below code is useful for you
var AWS= require('aws-sdk');
var docClient = new AWS.DynamoDB.DocumentClient();
router.post('/someBatchWrites',(req,res)=>{
docClient.batchWrite(params, function (error, data) {
res.send(error, data);
handler(error, data);//handling unprocessed items /back ground
})
});
//handle Method
function handler(err, data) {
if (err) {
console.log("Error", err);
} else {
console.log("Success", data);
if (Object.keys(data.UnprocessedItems).length) {
setTimeout(() => { docClient.batchWrite({ RequestItems: data.UnprocessedItems }, handler);
}, 100000);
}
}
}

Resources