AWS SES service issue in sending mail using lambda - node.js

I have a Basic AWS account in which we have deployed a lambda function. Also we have configured AWS SES service within the lambda function to send email (also our SES service is moved out of the sandbox & limit increased).
Approximately we are sending two emails per minute but we found that rarely we are getting mail, but most of the time we are not getting any email.
Also we tried deploying the application in two region but we found none to be successful.
Sample code
const AWS = require('aws-sdk');
//AWS Options
const options = {
region: 'us-east-1',
// accessKeyId not required because of server less app (SWS policy added in role)
// secretAccessKey not required because of server less app (SWS policy added in role)
}
const ses = new AWS.SES(options);
const sendEmail = (sender, receivers, subject, content) => {
console.log("Sending From", sender);
console.log("REceiver Email", receivers);
const promise = new Promise((resolve, reject) => {
ses.sendEmail({
Source: sender,
Destination: {
ToAddresses: receivers
},
Message: {
Subject: {
Data: subject
},
Body: {
Html: {
Data: content
}
}
}
}, (err, data) => {
if (err) {
console.log(err)
reject(err)
}
resolve(data)
});
});
return promise
};

I think there are a couple of things going on here:
JavaScript functions that return promises need to be async
your Lambda function may be timing out (the default is 3 seconds)

Related

AWS-SNS publish() method not sending any messages to phone number

I have used aws-sdk in node express to send the verification code to the phone number I referred this docs to implement it. here I get the response but do not get any messages on the phone.
const AWS = require("aws-sdk");
AWS.config.update({
region: "region",
accessKeyId: "ACCESS_KEY",
secretAccessKey: "SECRET_KEY",
});
router.post("/", async (req, res) => {
let phone_no = "+91876214****";
let random = Math.floor(100000 + Math.random() * 900000);
const YOUR_MESSAGE = `Your verification code is ${random}`;
let params = {
Message: YOUR_MESSAGE,
PhoneNumber: phone_no,
MessageAttributes: {
"AWS.SNS.SMS.SMSType": {
DataType: "String",
StringValue: "Transactional",
},
};
let publishTextPromise = new AWS.SNS({ apiVersion: "2010-03-31" })
.publish(params)
.promise();
publishTextPromise
.then(function (data) {
return res.json({ id: data.MessageId, otp: random });
})
.catch(function (err) {
return res.send(err.stack);
});
});
is anything I'm doing wrong, new to this aws-sns concept.
here i logged the publishTextPromise i get response as Promise { <pending> }
If you get Success result from api but message is not received. You need to check SNS logs.
On SNS console, you can see Text messaging (SMS) under Mobile section. If you don't enable status logging, firstly edit preferences on Delivery status logs section then on new page create an IAM Role (SNSSuccessFeedback) with Success sample rate 100%.
You can find error on cloudwatch.
Potential problem. You account is in the SMS sandbox in the region which you used.
If you see any message like ☝️, You can add your phone number on sandbox destination phone number.
Then sandbox will send you a verify code, when you put it on AWS console. Then you will be able to receive messages from SNS.

Subscribing SNS topic with HTTP End point in NodeJS

The question is not regarding subscribing to SNS Topic via HTTP endpoint. I was successfully able to configure and also confirm the subscription. Also, I am able to receive messages at the endpoint.
The requirement is to have another subscriber of the topic in nodejs backend app. I tweaked the code in this article for setting up the application, however couldn't manage to do so.
const AWS = require('aws-sdk');
const credentials = new AWS.SharedIniFileCredentials({ profile: '<profile-name>' });
const sns = new AWS.SNS({ credentials: credentials, region: 'eu-central-1' });
let params = {
Protocol: 'HTTP',
TopicArn: 'arn:aws:sns:eu-central-1:TOPIC:NAME',
Endpoint: 'http://mockbin.org/bin/bin-id'
};
sns.subscribe(params, (err, data) => {
if (err) {
console.log(err);
} else {
console.log(data);
}
});
The response is
{
ResponseMetadata: { RequestId: '0af83f58-c562-592f-b493-65fef627ac28' },
SubscriptionArn: 'pending confirmation'
}
However, in the console, i see this endpoint as confirmed
Can someone suggest here.

How to fix sending emails with AWS SES and Lambda?

I'm setting up a simple NodeJS Lambda function on AWS to send emails.
The code below is working when I run it locally. I have received all the credentials from AWS, verified both sender and recipient emails and granted all permissions to SES for my Lamba
const aws = require("aws-sdk");
const config = require('../config')
aws.config.update({
accessKeyId: config.mailUser,
secretAccessKey: config.mailPassword,
region:'us-east-1'
});
const ses = new aws.SES({apiVersion: '2010-12-01'});
const sendEmail = async (mailOptions) => {
const {
from,
to,
subject,
html
} = mailOptions
console.log('foo')
ses.sendEmail({
Source: from,
Destination: {
ToAddresses: [to]
},
Message: {
Subject: {
Data: subject,
Charset: 'UTF-8'
},
Body: {
Html: {
Data: html,
Charset: 'UTF-8'
}
}
}
},
(err, data) => {
console.log('baz')
if (err) {
console.error(err);
} else {
console.log('Email sent:');
console.log(data);
}
});
};
console.log('bar')
module.exports = {
sendEmail
};
It seems that ses.sendEmail() never fires when the function is deployed - I get foo and bar in the CloudWatch logs but never baz. Again, everything is running smoothly if run locally.
What is it that I am missing?
https://www.reddit.com/r/aws/comments/bf2iss/lambda_function_not_able_to_send_email_using_ses/elb8vzr/
Here is a very good explanation - apparently you need to wrap your ses.sendMail call in a promise for it to work with AWS Lambda.
All credit goes to https://www.reddit.com/user/jsdfkljdsafdsu980p/ and https://www.reddit.com/user/Enoxice/

Send a notification by Lambda function with AWS Pinpoint

I have an iOS app with a backend build by MobileHub with API Gateway, Lambda, and DynamoDB.
I noticed that the SNS function of MobileHub has been replaced by Pinpoint, and I would like to create a notification system using this new service.
When a user creates a new post through API Gateway, a lambda function will be triggered and I suppose I can send a notification to the subscribers by Pinpoint.
But I cannot find any example or reference doc in the official website of Pinpoint.
Do you have any resource for this scenario or any idea? Thank you very much!
Depends what you mean by notification, I assume you would like to send a push notification to a particular user (Pinpoint endpoint).
Pinpoint stores each device associated with a user as an "endpoint", generally created by the AWS client side analytics library (e.g. amplify analytics).
The client
With the amplify analytics library, I call updateEndpoint so I can specify a userId that is available to Lambda, as well as the device token and remove optOut so user can receive the push notification:
Address - The token generated from user accepting push notification permission (iOS)
optOut - NONE so they can receive push notifications
userId - unique id for user (Cognito's sub)
Lambda (node.js)
Now you can send a push notification, using the userId and the Pinpoint SDK.
Example:
const sendMessagesParams = {
ApplicationId: process.env.PINPOINT_APP_ID,
SendUsersMessageRequest: {
Users: {
[receiverUserId]: {}
},
MessageConfiguration: {
APNSMessage: {
Action: 'OPEN_APP',
Title: 'Message received',
SilentPush: false,
Body: `You have a new message`
},
GCMMessage: {
Action: 'OPEN_APP',
Title: 'Message received',
SilentPush: false,
Body: `You have a new message`
}
}
}
};
console.log('sendMessagesParams', JSON.stringify(sendMessagesParams));
pinpoint.sendUsersMessages(sendMessagesParams, (sendMessagesErr, sendMessagesData) => console.log('push sent')
For your particular scenario, I set up a DynamoDB stream and trigger a Lambda when a record changes within the table. You may need to add the IAM permissions manually once the lambda is created.
Sources
Full list of pinpoint methods you can use in lambda (Node.JS)
Update endpoint using Amplify Analytics (JS)
Dynamodb streams - trigger a lambda
I was struggling a lot getting a lambda function working, so please see this answer as an addition to the answer from Dylan w.
Client
import PushNotification from '#aws-amplify/pushnotification';
import Analytics from '#aws-amplify/analytics';
PushNotification.onRegister((token) => {
Analytics.updateEndpoint({
address: token,
channelType: 'APNS',
optOut: 'NONE',
// Customized userId
userId: "e236e3ea-bas9-4eae-967e-0eb9bcaca26d" // Example
})
});
Lambda function
'use strict';
const AWS = require('aws-sdk');
exports.handler = async (event, context) => {
var pinpoint = new AWS.Pinpoint();
const sendMessagesParams = {
ApplicationId: <YOUR_APPLICATION_ID>, // Find it in Pinpoint->All projects
SendUsersMessageRequest: {
Users:{<USER_ID>:{}}, // The same userId as set on the client. This way you can "follow" people if they switch device
MessageConfiguration:{
APNSMessage:{
Action:"OPEN_APP",
Title:"Message received",
Body:"You have a new message"
}
}
}
};
return await new Promise( (resolve, reject) => {
pinpoint.sendUsersMessages(sendMessagesParams, (sendMessagesErr, sendMessagesData) => {
if(sendMessagesErr) reject(sendMessagesErr)
if(sendMessagesData) resolve(sendMessagesData)
});
});
};
Note that the call to pinpoint is wrapped in a promise. Because pinpoint.sendUserMessages accepts a callback, the execution is continued (async nature of Node), which will shut down the lambda function and you will get no output from the callback function or receive a notification, without awaiting the function to finish.
Finally, I've got something that perfectly works.
The answer is that you have to use "targetClient" to update endpoint inside "didRegisterForRemoteNotificationsWithDeviceToken" function.
let client = self.pinpoint!.targetingClient
let profile = client.currentEndpointProfile()
print("EndpointId = \(profile.endpointId)")
profile.user?.userId = <YOUR_CUSTOM_ID>
client.update(profile)
Client Side(XCODE)
Here is my [AppDelegate.swift] looks like:
(Important part is inside "didRegisterForRemoteNotificationsWithDeviceToken" function)
import UserNotifications
import AWSPinpoint
class AppDelegate: UIResponder, UIApplicationDelegate {
var pinpoint: AWSPinpoint?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Instantiate Pinpoint
let pinpointConfiguration = AWSPinpointConfiguration.defaultPinpointConfiguration(launchOptions: launchOptions)
// Set debug mode to use APNS sandbox, make sure to toggle for your production app
pinpointConfiguration.debug = true
self.pinpoint = AWSPinpoint(configuration: pinpointConfiguration)
// Present the user with a request to authorize push notifications
self.registerForPushNotifications()
return true
}
func registerForPushNotifications() {
UNUserNotificationCenter.current()
.requestAuthorization(options: [.alert, .sound, .badge]) { [weak self] granted, _ in
print("Permission granted: \(granted)")
guard granted else { return }
// Only get the notification settings if user has granted permissions
self?.getNotificationSettings()
}
}
func getNotificationSettings() {
UNUserNotificationCenter.current().getNotificationSettings { settings in
print("Notification settings: \(settings)")
guard settings.authorizationStatus == .authorized else { return }
DispatchQueue.main.async {
// Register with Apple Push Notification service
UIApplication.shared.registerForRemoteNotifications()
}
}
}
func application(_: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
print("DidRegisterForRemoteNotificationsWithDeviceToken: Start")
let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) }
let token = tokenParts.joined()
print("Device Token: \(token)")
// Register the device token with Pinpoint as the endpoint for this user
self.pinpoint!.notificationManager.interceptDidRegisterForRemoteNotifications(withDeviceToken: deviceToken)
//set custom userId and update endpoint
let client = self.pinpoint!.targetingClient
let profile = client.currentEndpointProfile()
print("EndpointId = \(profile.endpointId)")
profile.user?.userId = <YOUR_CUSTOM_ID>
client.update(profile)
}
func application(_: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print("Failed to register: \(error)")
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
print("didReceiveRemoteNotification:\(userInfo)")
// Pass this remote notification event to pinpoint SDK to keep track of notifications produced by AWS Pinpoint campaigns.
self.pinpoint!.notificationManager.interceptDidReceiveRemoteNotification(userInfo)
}
}
Backend (Lambda with Nodejs)
And here is a code to send a notification to specific user.
'use strict';
const AWS = require('aws-sdk');
const pinpoint = new AWS.Pinpoint({region: 'us-west-2'});
exports.handler = (event, context, callback) => {
const done = (err, data) => {
if(err){
console.log('ERROR:', err);
const response = {
statusCode: 400,
body: JSON.stringify(err)
};
callback(response);
}else{
console.log('SUCCESS:', data);
const response = {
statusCode: 200,
body: JSON.stringify(data)
};
callback(null, response);
}
};
let users = {};
users[<YOUR_CUSTOM_ID>] = {};
const params = {
ApplicationId: PINPOINT_PROJECT_ID,
SendUsersMessageRequest: {
Users: users,
MessageConfiguration: {
APNSMessage: {
Action: 'OPEN_APP',
Title: "Hi, I am AWS Pinpoint.",
SilentPush: false,
Body: "You've got a nice message."
}
}
}
};
pinpoint.sendUsersMessages(params, (err, data)=>{
if(err){
done(err);
}else{
done(null, data);
}
});
};
Hope those work for you too.
This is certainly possible with Amazon Pinpoint. You can find the Javascript SDK documentation here.
There are 2 modes of sending with Pinpoint.
Direct send - This is effectively the same as what SNS has traditionally been. You need a device token and you can send directly to your push provider using that token.
Segmentation sends - This mode is slightly different and assumes that you have loaded all your devices into Pinpoint via the Mobile SDK as part of your app, or via an S3 Import. The benefit here is that you can segment your devices and send to that segment (e.g. 'Ryans Friends').
So in your Lambda backed API you can choose to either send directly to subscribers (if you have their addresses) or potentially an entire segment of subscribers (if you have loaded your endpoints into Pinpoint).

How to send SMS using Amazon SNS from a AWS lambda function

Amazon SNS provides a facility to send SMS globally.
I want to send SMS from a Lambda function were we provide the mobile number and text message and use SNS to deliver that message but I didn't find a helpful documentation or example code for NodeJS or java.
Can any one suggest a solution?
Code:
var params = {
Message: 'Hi this is message from AWS_SNS', /* required */
MessageAttributes: {
someKey: {
DataType: 'String' ,
StringValue: 'String'
},
},
MessageStructure: 'String',
PhoneNumber: '+91MyNUMBER',
Subject: 'MYSubject',
//TargetArn: 'arn:aws:sns:us-west-2:798298080689:SMS',
//TopicArn: 'arn:aws:sqs:us-west-2:798298080689:SendSMS'
};
sns.publish(params, function(err, data) {
if (err) console.log(err, err.stack); // an error occurred
else console.log(data); // successful response
});
};
So, you need to write Lambda function which is invoked somehow, let's say via HTTP request so you'll also need to setup API Gateway to route connections to your Lambda function.
Next, your Lambda function will push that data to "SNS Topic" while SMS Subscription will "poll" for any new data in this "Topic". As soon as any data gets into this topic, it will be consumed by subscription and SMS will be sent.
Few days ago I wrote a post about SNS & Lambda which might help you. Flow you wanted to achieve is pretty similar to one described in this article.
https://medium.com/#rafalwilinski/use-aws-lambda-sns-and-node-js-to-automatically-deploy-your-static-site-from-github-to-s3-9e0987a073ec#.3x6wbrz91
Documentation pages that might help:
Pushing to SNS: http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/SNS.html#publish-property
Subscribing to SNS:
http://docs.aws.amazon.com/sns/latest/dg/SubscribeTopic.html
Please try with setting the region explicitly to "us-east-1". I managed to send SMS to India by explicitly setting this region. I also tried with "ap-south-1", but was not successful.
Based on latest AWS SNS > SMS documenration, When you don't have any topicArn and you need to send a text message directly to a phone number, you need to send following params:
const AWS = require('aws-sdk');
AWS.config.update({region: 'eu-west-1'});
const sns = new AWS.SNS();
const publish = (phone, text, subject) => {
// Create publish parameters
var params = {
Message: text,
Subject: subject,
PhoneNumber: phone,
MessageAttributes: {
'AWS.SNS.SMS.SMSType' : {
DataType : 'String',
StringValue: 'Transactional'
},
},
};
console.log('------------- text message param before sending------------');
console.log(params);
console.log('----------------------------------------------------');
// Create promise and SNS service object
var publishTextPromise = sns.publish(params).promise();
// Handle promise's fulfilled/rejected states
publishTextPromise.then(
function(data) {
console.log("MessageID is " + data.MessageId);
}).catch(
function(err) {
console.error(err, err.stack);
});
}
exports.publish = publish;
Here's what I did
Create a new Lambda function Author from scratch with your Runtime of choice. (I went to latest, Node.js 12.x)
For execution role, choose Create a new role from AWS policy templates.
Type in your Role name and because you want to send SMS to any mobile number, you must set Resource to *.
Type this as your IAM Role Template.
{
"Version": "2012-10-17",
"Statement": [
{
"Action": [
"sns:Publish"
],
"Effect": "Allow",
"Resource": "*"
}
]
}
Use this code snippet
const AWS = require('aws-sdk');
const SNS = new AWS.SNS();
exports.handler = async (event) => {
let params = {
PhoneNumber: '+123xxxxxxx',
Message: 'You are receiving this from AWS Lambda'
};
return new Promise((resolve, reject) => {
SNS.publish(params, function(err, data) {
if(err) {
reject(err);
}
else {
resolve(data);
}
})
})
}
That's all. Click Deploy then Test and you should receive an SMS.
Here is a link to a tutorial for building an Alexa skill that connects with AWS SNS to send a text message.
It works fine if can make sure you have the right access to publish to SNS.
const smsParams = ()=>({
Message: getUpdateMessage(order),
PhoneNumber: `+91${order.contactNo}`,
MessageAttributes: {
'AWS.SNS.SMS.SMSType' : {
DataType : 'String',
StringValue: 'Transactional'
},
},
})
Permissions to my lambda:
- Effect: 'Allow'
Action:
- "sns:Publish"
Resource:
- '*'
Note that you have to allow all the resources to send SMS using PhoneNumber
Here is a link to all the supported SNS regions

Resources