After correctly creating a REST API trigger through API gateway and linking it with a lambda function, I am trying to add SES as the destination.
The purpose of the function would be to automatically forward messages received on a HTML contact form on a static website to an email address through SES service.
As per the caption I have created a lambda function I will show here below (just hid the domain and the email addresses for privacy).
var AWS = require('aws-sdk');
var ses = new AWS.SES();
var RECEIVER = 'XXX#gmail.com';
var SENDER = 'XXX#gmail.com';
var response = {
"isBase64Encoded": false,
"headers": { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': 'XXX.com'},
"statusCode": 200,
"body": "{\"result\": \"Success.\"}"
};
exports.handler = function (event, context) {
console.log('Received event:', event);
sendEmail(event, function (err, data) {
context.done(err, null);
});
};
function sendEmail (event, done) {
var params = {
Destination: {
ToAddresses: [
RECEIVER
]
},
Message: {
Body: {
Text: {
Data: 'name: ' + event.name + '\nphone: ' + event.phone + '\nemail: ' + event.email + '\ndesc: ' + event.desc,
Charset: 'UTF-8'
}
},
Subject: {
Data: 'Website Referral Form: ' + event.name,
Charset: 'UTF-8'
}
},
Source: SENDER
};
ses.sendEmail(params, done);
}
The relevant permissions to SES have been granted to the lambda function.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "ses:SendEmail",
"Resource": "*"
}
]
}
The testing of the function is indicated below:
Response
null
Function Logs
START RequestId: 765f6d27-9810-4d6c-a01b-a883eb429cd8 Version: $LATEST
2021-03-03T15:39:05.849Z 765f6d27-9810-4d6c-a01b-a883eb429cd8 INFO Received event: { key1: 'value1', key2: 'value2', key3: 'value3' }
END RequestId: 765f6d27-9810-4d6c-a01b-a883eb429cd8
REPORT RequestId: 765f6d27-9810-4d6c-a01b-a883eb429cd8 Duration: 928.32 ms Billed Duration: 929 ms Memory Size: 128 MB Max Memory Used: 87 MB Init Duration: 449.71 ms
Request ID
765f6d27-9810-4d6c-a01b-a883eb429cd8
Unfortunately, when I click on add destinations I cannot find the SES service to be selected.
The email address has already been validated in SES.
What else am I missing?
Thank you
SES isn't a supported destination. Lambda destinations are for sending the output of a Lambda function to another service, without writing code in the function for connecting directly to the service.
You don't need Lambda to send anything to an SES destination because you already sent the request directly in your code via ses.sendEmail().
Related
I'm trying to send a GCM notification from an AWS lambda function.
I have two questions:
What permissions does the exection role require?
Is the behavioud described below the best I can expect from AWS notification, do I need to look at another service?
I've followed info from another question:
Send a notification by Lambda function with AWS Pinpoint
My lambda is as follows:
async function sendMessage() {
try {
const pinpoint = new AWS.Pinpoint();
let users = {};
users["---"] = {};
const params = {
ApplicationId: applicationId,
SendUsersMessageRequest: {
Users: users,
MessageConfiguration: {
'GCMMessage': {
Action: 'OPEN_APP',
Title: "Lambda User Msg",
SilentPush: false,
Body: "Lambda User Send Message Test"
}
}
}
};
console.log("Params:", params);
let rspnData = await pinpoint.sendUsersMessages(params).promise();
console.log("sendUsersMessages:rspnData:", rspnData);
} catch (err) {
console.log("sendMessage error:", err);
}
}
Can someone advise as to the minimal permissions required by the execution role of the lambda?
I've currently allowed everything pinpoint related, but would like
to be certain what should actually be allows?
2.
When I test the code from the AWS lambda code console I get the following execution log:
2022-02-11T08:57:26.446Z --- INFO SendMessage
2022-02-11T08:57:26.466Z --- INFO Params: { ApplicationId: '---',
SendUsersMessageRequest: {
Users: { '---': {} },
MessageConfiguration: { GCMMessage: [Object] } } } END RequestId:
There is no output from the sendUserMessage!
ASIDE: I've increased maximum execution time to 20secs, but this feels wrong!
Or I get an error of the form:
2022-02-11T09:38:32.721Z --- INFO sendMessage error: Error: Client network socket disconnected before secure TLS connection was established
at connResetException (internal/errors.js:639:14)
at TLSSocket.onConnectEnd (_tls_wrap.js:1570:19)
at TLSSocket.emit (events.js:412:35)
at TLSSocket.emit (domain.js:475:12)
at endReadableNT (internal/streams/readable.js:1334:12)
at processTicksAndRejections (internal/process/task_queues.js:82:21) {
code: 'TimeoutError',
path: null,
host: 'pinpoint.eu-west-2.amazonaws.com',
port: 443,
localAddress: undefined,
time: 2022-02-11T09:38:32.721Z,
region: 'eu-west-2',
hostname: 'pinpoint.eu-west-2.amazonaws.com',
retryable: true
}
END RequestId: ---
REPORT RequestId: --- Duration: 173.82 ms Billed Duration: 174 ms Memory Size: 128 MB Max Memory Used: 83 MB
Intermitently in the Cloudwatch log I get the following output:
2022-02-11T08:54:11.847Z --- INFO sendUsersMessages:rspnData: {
SendUsersMessageResponse: {
ApplicationId: '---',
RequestId: '---',
Result: { '---': [Object] }
}
}
indicating the notification was actually successfully sent!
On some occasions I get a notification in the app!
Usually very delayed!
If I use the same Id's via aws cli:
aws pinpoint send-users-messages --cli-input-json file://pinpoint-send-users-messages.json
I get a prompt notification, as expected!
Is this the best I can hope for from AWS send notifications, do I need to look at another
service, or am I doing something wrong?
In my case the lambda has the following role.
I don't know if it will be of any help, but please take a look.
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"logs:CreateLogStream",
"logs:PutLogEvents",
"logs:CreateLogGroup"
],
"Resource": "arn:aws:logs:*:*:*"
},
{
"Effect": "Allow",
"Action": "mobiletargeting:SendUsersMessages",
"Resource": "arn:aws:mobiletargeting:us-west-2:{your aws account id}:apps/{pinpoint project id}/messages"
},
{
"Effect": "Allow",
"Action": [
"mobiletargeting:GetEndpoint",
"mobiletargeting:UpdateEndpoint",
"mobiletargeting:PutEvents"
],
"Resource": "arn:aws:mobiletargeting:us-west-2:{your aws account id}:apps/{pinpoint project id}/endpoints/*"
}
]
}
I'm trying to publish a message from a lambda function to my AWS IoT Thing, by using the aws-sdk which should use HTTP by default instead of MQTT to publish a message.
Then the Function gets invoked no error message is logged to cloudwatch unfortunately which makes it even harder to debug.
Steps I took:
created a Policy for the message
created a small React frontend to read messages and test it with IoT test client.
wrote lambda and passed the Thing url as env var like this: "https://xxxxxdi.eu-central-1.amazonaws.com"
The Lambda function has also a attached Policy which should grant it to write to AWS IoT.
{
"arn": null,
"document": {
"Version": "2012-10-17",
"Statement": [
{
"Action": "iot:Publish",
"Resource": "arn:aws:iot:eu-central-1:*:*:topic/PairCreated",
"Effect": "Allow"
}
]
},
"id": null,
"name": "PublishMessage",
"type": "inline"
}
Here is the code of the lambda function. The credentials for the object of type IotData are looking ok
import wrap from '#dazn/lambda-powertools-pattern-basic'
import Log from '#dazn/lambda-powertools-logger'
import { IotData } from 'aws-sdk'
export const handler = wrap(async (event: any = {}, context: any) => {
Log.info('event', event)
const iotData = new IotData({endpoint: process.env.IOT_ENDPOINT, region: 'eu-central-1', logger: new Log})
const params: IotData.PublishRequest = {
topic: process.env.IOT_THING_NAME || '',
payload: JSON.stringify(event),
qos: 0,
retain: false
}
Log.debug('params', { params })
iotData.publish(params, (err, res) => {
if (err) {
Log.error("Error publishing to IOT", { err })
return context.fail(err)
};
Log.info('res', res)
return context.succeed();
});
})
The version I'm using: "aws-sdk": "^2.1053.0"
What am I missing?
I found out why the message was not sent.
The callback inside of iotData.publish was not executed correctly because the lambda function was terminated before it could be executed. Here are my changes to the code.
await iotData.publish(params, (err, res) => {
if (err) {
Log.error("Error publishing to IOT", { err })
return context.fail(err)
};
return context.succeed(res);
}).promise()
This returns a Promise we can await so the callback gets executed
I keep getting the following error in Express:
MessageRejected: Email address is not verified. The following identities failed the check in region EU-WEST-1: email#gmail.com
Here is my code:
// Set the region
AWS.config.update({region: 'eu-west-1'});
// Create sendEmail params
var params = {
Destination: { /* required */
CcAddresses: [
'email#gmail.com',
],
ToAddresses: [
'a#stack.overflow',
]
},
Message: { /* required */
Body: { /* required */
Html: {
Charset: "UTF-8",
Data: "HTML_FORMAT_BODY"
},
Text: {
Charset: "UTF-8",
Data: "TEXT_FORMAT_BODY"
}
},
Subject: {
Charset: 'UTF-8',
Data: 'Test email'
}
},
Source: 'address#email.com', /* required */
};
// 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
sendPromise.then(
function(data) {
res.send(data.MessageId);
}).catch(
function(err) {
console.error(err, err.stack);
})
a#stack.overflow and address#email are verified, but email#gmail is not. How can I send to users if I have to verify them? Am I using the wrong AWS service?
When you're using SES sandbox, addresses, to which you send emails, should be verified by SES - it is done for your wallet security. It won't require that in production mode.
See: Moving Out of the Amazon SES Sandbox - Amazon Simple Email Service
I have a static form-based HTML contact page, where the user needs to drop their Name, email address, subject and message and ultimately my script will reroute all the required value via AWS API --> Lambda --> my Gmail.
So, for that, I have a verified my GMAIL account in SES. And my AWS Lambda function is as below, (here, I used the same confirmed email address for to and send to deliver the email.)
'use strict';
console.log('Loading function');
const AWS = require('aws-sdk');
const sesClient = new AWS.SES();
const sesConfirmedAddress = "XXXX#gmail.com";
/**
* Lambda to process HTTP POST for a contact form with the following body
* {
"email": <contact-email>,
"subject": <contact-subject>,
"message": <contact-message>
}
*
*/
exports.handler = (event, context, callback) => {
console.log('Received event:', JSON.stringify(event, null, 2));
var emailObj = JSON.parse(event.body);
var params = getEmailMessage(emailObj);
var sendEmailPromise = sesClient.sendEmail(params).promise();
var response = {
statusCode: 200
};
sendEmailPromise.then(function(result) {
console.log(result);
callback(null, response);
}).catch(function(err) {
console.log(err);
response.statusCode = 500;
callback(null, response);
});
};
function getEmailMessage (emailObj) {
var emailRequestParams = {
Destination: {
ToAddresses: [ sesConfirmedAddress ]
},
Message: {
Body: {
Text: {
Data: emailObj.message
}
},
Subject: {
Data: emailObj.subject
}
},
Source: sesConfirmedAddress,
ReplyToAddresses: [ emailObj.email ]
};
return emailRequestParams;
}
Now in IAM, I have created a policy and attached to this lambda function. Then I have created API to use this function to serve my purpose.
Now when I click the TEST button in API, it gives me return code 200. which is great. But the problem is I don't see any email in my verified Gmail account.
I'm using, this as my test message,
{
"email": XXXX#example.com,
"subject": Test,
"message": This is a Test message
}
I tried also using postman and postman says everything is fine with 200 as return code. But still no email to my Gmail account. I checked in cloud watch logs everything is Green. So no idea why my verified Gmail is not receiving any kind of test message. Can anyome shed any lights here?
customized Role looks like this:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"ses:SendEmail",
"ses:SendTemplatedEmail",
"ses:SendRawEmail"
],
"Resource": "*"
}
]
}
I would you recommend two things, first try to deploy your code using serverless framework which can help you with the dependencies like IAM roles.
Second, As an alternative and complementary steps you may use the following reference from AWS:
Open the Amazon SES console at https://console.aws.amazon.com/ses/.
In the column on the left, choose either Email Addresses or Domains.
Select a verified email address or domain, and then choose Send a
Test Email. For To:, type bounce#simulator.amazonses.com. For
Subject and Body, type some sample text. Choose Send Test Email.
Repeat steps 3 and 4 to create another test message, but this time,
for To:, type complaint#simulator.amazonses.com.
Open the Amazon SQS
console at https://console.aws.amazon.com/sqs/. The Messages
Available column should indicate that 2 messages are available.
can you print the complete response to see if you get message id in the response ? if so, there seems to be no problem with the permission.
1. You can enable SNS notification for Bounce/Delivery/complaint for the verified identity in SES console and see if you receive any notification after making sendmail api call.
https://docs.aws.amazon.com/ses/latest/DeveloperGuide/notifications-via-sns.html
Additionally, also check recipient spam box.
I have selected "Allow access to one or more AWS accounts or IAM users"
My access policy
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::12345678910:user/elastic"
},
"Action": "es:*",
"Resource": "arn:aws:es:eu-west-1:123456789:domain/elastic-cluster/*"
}
]
}
I have created an IAM profile -
user - elastic
password -hisdfdsfds
Access key Id - sdsfdssdfdsfdsfdsfsdfsd
Secret Access Key - sdsfdsfdsfsdfdsfds
when I try to connect
$params = array();
$params['hosts'] = array (
'search-elastic-cluster-sdfsdfsdfs.eu-east.es.amazonaws.com:80',
);
$client = new Elasticsearch\Client($params);
It throws the following error:
{"Message":"User: anonymous is not authorized to perform: es:ESHttpPost on resource: arn:aws:es:eu-west-1:dsfdsfsdfsdsd:domain/elastic-cluster/sdsfsfds/sdfdsfdssd/_search"}
I found it can be accessed by signed version 4 signature requests. I tried doing it, but could not . Maybe the way is wrong.
I would be happy if some one suggests ideas in creating signed version 4 request to elasticsearch domain. An example using parameters I stated above would be very helpful. Thanks in advance.
The application needs to sign the requests going to Elasticsearch. The AWS SDK for your language of choice should have a method which creates the credentials for the sign request.
When you provide your requests with the credentials, it should be ok and good to go.
This is a code snippet using the javascript sdk:
var AWS = require('aws-sdk');
var creds = new AWS.EnvironmentCredentials('AWS');
var esDomain = {
region: 'us-east-1',
endpoint: 'yoursearchdomain.region.amazonaws.com',
index: 'myindex',
doctype: 'mytype'
};
var endpoint = new AWS.Endpoint(esDomain.endpoint);
var req = new AWS.HttpRequest(endpoint);
req.method = 'POST';
req.path = path.join('/', esDomain.index, esDomain.doctype);
req.region = esDomain.region;
req.headers['presigned-expires'] = false;
req.headers['Host'] = endpoint.host;
req.headers['Content-Type'] = 'application/json';
req.body = doc;
var signer = new AWS.Signers.V4(req , 'es');
signer.addAuthorization(creds, new Date());
var send = new AWS.NodeHttpClient();
send.handleRequest(req, null, function(httpResp) {
var respBody = '';
httpResp.on('data', function (chunk) {
respBody += chunk;
});
httpResp.on('end', function (chunk) {
console.log('Response: ' + respBody);
context.succeed('Lambda added document ' + doc);
});
}, function(err) {
console.log('Error: ' + err);
context.fail('Lambda failed with error ' + err);
});