AWS lambda destination not triggering - amazon-web-services

My current Lambda Setup
My child lambda in destination not triggering by Main Lambda. It only triggers when i use --invocation-type Event in aws cli. It is not triggering when testing or in live mode. Main lambda works fine in both ways, but it just not triggers destination lambda without cli command.
Full aws cli that works.(But i won't use aws-cli to trigger. It should be triggered by Cloudfront)
aws lambda invoke --function-name main-lambda --cli-binary-format raw-in-base64-out --payload '{"test": 15}' --invocation-type Event response.json
Main Lambda
const https = require("https");
exports.handler = async (event, context, callback) => {
await post("https://example.com/notify", {
type: "viewer-response",
event,
context,});
let response = "";
try {
response = event.Records[0].cf.response;
} catch (e) {
response = event; //For debug purposes
}
// Added this, but not working either.
//response.headers['x-amz-invocation-type'] = [{key: 'X-Amz-Invocation-Type', value: 'Event'}];
return response;
};
Child Lambda
const https = require('https');
exports.handler = async (event,context,callback) => {
console.log("Triggered Child Lambda");
await post("https://example.com/notify", {
type: "onSuccess-response",
event,
context, });
const response = {
statusCode: 200,
body: JSON.stringify('Hello from Lambda!'),
};
return response; // I don't need this. This comes from base
}
I understand that Main Lambda should be invoked with Event type because it needs to be async in order to call destination lambda but i can't be able to find a way to do it. I tried adding Environment variables to lambda function but it didn't worked either.

That is the expected behaviour. CloudFront invokes Lambdas using the synchronous invocation mode. The service waits for a response from the Lambda. Destinations are ignored.

Related

AWS SDK function is not being called when run used on a lambda function deployed by Amplify

I'm trying to add a user to a group in my Amplify project programatically. I created a lambda function as shown below and deployed it using amplify functions. I setup the permissions to the functions's role following this article: https://www.linkedin.com/pulse/aws-amplify-adding-user-group-programmatically-guillermo-misa-iii. The function get's executed. No errors. It doesn't even run the console log which means the callback is not being called. The user is not being added to the group either. There's no error logs in the lambda function logs. Any idea what's happening?
const AWS = require('aws-sdk');
exports.handler = async (event, context, callback) => {
const cognitoIdentityServiceProvider = new AWS.CognitoIdentityServiceProvider();
cognitoIdentityServiceProvider.adminAddUserToGroup({
GroupName: 'abcd', //your confirmed user gets added to this group
UserPoolId: event.arguments.userPoolId,
Username: event.arguments.userName
}, function(err, data) {
console.log('Ran the function')
if (err) {
callback(err) // uh oh, an error
}
callback(null, event); // yay! success
})
// return response;
};
In the guide I followed, they have used a normal function as the lambda handler:
exports.handler = (event, context, callback) => {})
But I have used an async function:
exports.handler = async (event, context, callback) => {})
It seems in an async function, lambda does not wait till the callback is called. It expects us to use the async await syntax in our code. So, I have to convert the function like this:
try {
await cognitoIdentityServiceProvider.adminAddUserToGroup({
GroupName: 'abcd', //your confirmed user gets added to this group
UserPoolId: event.arguments.userPoolId,
Username: event.arguments.userName
}).promise()
callback(null, event); // yay! success
} catch (error) {
console.log(error)
}
Now everything works! Notice that we can get a promise out of aws-sdk functions if we call .promise().

Sending an SQS message via an SQS lambda

I have a SeedIndicatorInformationSQS-dev.fifo queue (FiFo) that connects to a SeedIndicatorInformationSQS-dev-d13dfe0 lambda. I'd like to send a message within the SeedIndicatorInformationSQS-dev-d13dfe0 lambda to the EvaluationConfigSQS-dev standard queue. But no messages are being sent/received. Whereas if I try sending it from a non-SQS connected lambda (via AppSync) it works.
The SeedIndicatorInformationSQS-dev-d13dfe0 lambda has the following permissions:
I have checked:
That the lambda has the right access to send SQS messages (you can see it there).
That the EvaluationConfigSQS-devstandard queue is correctly configured as I've successfully sent messages to it from another lambda (non-SQS).
That the SQS URL is correct.
That no errors are shown in the console.
That async/await are correctly placed (I've tried both with and without them)
Here's the CloudWatch log for the SeedIndicatorInformationSQS-dev-d13dfe0lambda trying to dispatch the content: Successfully sent to the right URL, JSON parsed to string, but nothing.
Here's the CloudWatch Log: You can see. SeedIndicatorInformationSQS-dev-d13dfe0 successfully receives the message from another lambda function and processes it, but no further messages are sent.
No errors reported within SeedIndicatorInformationSQS-dev-d13dfe0
No logs within EvaluationConfigSQS-dev
But, if I try to send it within a non-SQS lambda, it works.
Received event:
This is the classes-dev-eefa2af lambda that sends successfully to EvaluationConfigSQS-dev (and coincidentally is the one which triggers the SeedIndicatorInfromationSQS-dev.fifo SQS.
Here are the permissions for EvaluationConfigSQS-dev-6da8b90 (lambda that the EvaluationConfigSQS-dev standard queue triggers)
By any chance, do I need to add special permissions to the SeedIndicatorInformatioNSQS-dev.fifo queue?
Here's the JS that gets dispatched (I'm using a mediator pattern, and it's successfully getting dispatched, you can see it in the logs above "Dispatching CREATED_INSTITUTION_CLASS". I have also managed to print the URL and verified that it's actually the one that corresponds to it.
export async function institutionClassCreatedEventHandler(
evt: InstitutionClassCreatedEvent
) {
const json = JSON.stringify({
...evt,
type: "CLASS_CREATED",
});
sqsDispatchMessage(
"InstitutionClassCreatedEvent",
evt.tenantId + evt.subject.id,
json,
Config.SQS.evaluationConfigSQS.url,
false
);
}
Here's the sqsDispatchMessage function. As you can see, there's a catch block that will print me whenever there's an error (and it works). But so far, no error has been recorded.
export async function sqsDispatchMessage(
eventName: string,
uniqueId: string,
jsonObjStringifiedToSend: string,
sqsURL: string,
isFifoQueue: boolean = true
) {
try {
await sqs
.sendMessage({
MessageAttributes: {
EventName: {
DataType: "String",
StringValue: eventName,
},
},
...(isFifoQueue && { MessageGroupId: eventName }),
MessageBody: jsonObjStringifiedToSend,
QueueUrl: sqsURL,
...(isFifoQueue && { MessageDeduplicationId: uniqueId }),
})
.promise();
} catch (e) {
console.error(`Error While Sending the ${eventName}`);
console.error(e.message);
console.log(jsonObjStringifiedToSend);
}
}
Any ideas? Is it even possible?
The problem was in my dispatcher:
It used to be like this:
export async function dispatchOfEvents({
type,
evtArgs,
}: MediatorEvents): Promise<void> {
logTime(type);
(events as any)[type].forEach((evt: Function) => {
evt(evtArgs);
});
}
I changed it to:
export async function dispatchOfEvents({
type,
evtArgs,
}: MediatorEvents): Promise<void> {
logTime(type);
const evts: Promise<any>[] = [];
for (const evt of (events as any)[type]) {
evts.push(evt(evtArgs));
}
await Promise.all(evts);
}

Not able to connect to AWS documentDb from Lambda

I'm trying to connect to AWS documentDB from Lambda function but, not able to connect.
MongoClient.connect never calls the callback function connected.
TLS is off on documentDB Cluster. I'm able to connect via mongo shell.
Lambda & documentDB are in same VPC & Security group.
'use strict';
module.exports.search = async (event, context, callback) => {
const MongoClient = require('mongodb').MongoClient;
const url = "mongodb://xxx:xxxx#xxx-db.cluster-xxx.us-east-2.docdb.amazonaws.com:27017";
console.log("Starting");
MongoClient.connect(url,
{
useNewUrlParser: true
},
function(err, client) {
if(err)
throw err;
console.log("Connected");
db = client.db('mydb');
col = db.collection('mycollection');
col.find({}).toArray().then(result => {
console.log(result);
return { statusCode: 200, body: result };
}).catch(err => {
console.log('=> an error occurred: ', err);
return { statusCode: 500, body: 'error' };
});
});
};
Output only prints starting which was consoled before calling Mongo.Connect.
How to identify or debug the issue ?
Just from looking at the current code I am pretty sure your function exit before it is able to complete. Therefore, your callback is not executed
Because MongoClient.connect runs asynchronously
Try to take a look at some resource around async/await/promise and Lambda
https://medium.com/tensult/async-await-on-aws-lambda-function-for-nodejs-2783febbccd9
How to wait for async actions inside AWS Lambda?

NodeJS script works locally but not in the handler of lambda

I have a NodeJS Lambda function that reads from a Kinesis stream, does some processing and writes it to another Kinesis stream. The writing part is causing an error on lambda. We use the aws-sdk npm module to write the data. When we call putRecord no callback is fired and lambda times out.
This writing code works well when run locally as a node app on a laptop.
Local config:
- The credentials of a programmatic user are with Kinesis Full Access policy are stored in the credentials file and the putRecord runs successfully returning the shardId, sequence number, etc.
Lambda function:
- The code for putRecord does not return and it hangs. No errors are thrown either. Lambda has a role with the Kinesis Full Access policy.
Code:
var AWS = require('aws-sdk');
var kinesis = new AWS.Kinesis({
region: 'us-east-1',
});
var randomNumber = Math.floor(Math.random() * 100000);
var data = 'data-' + randomNumber;
var partitionKey = 'pk-' + randomNumber;
var recordParams = {
Data: data,
PartitionKey: partitionKey,
StreamName: streamName
};
kinesis.putRecord(recordParams, function(err, data) {
console.log(data);
if (err) {
console.error(err);
}
});
Any idea what could be causing the issue. VPC or security group related maybe?
Thoughts and suggestions appereciated.
Thanks.
If you have uploaded the exact Node.js script code above to Lambda, it will definitely not work.
Lamda requires you to export a handler function that it will call.
So, your script should be written like this if you want it to be a Lambda function...
'use strict';
var AWS = require('aws-sdk');
var kinesis = new AWS.Kinesis({
region: 'us-east-1',
});
exports.handler = function (event, context, callback) {
var randomNumber = Math.floor(Math.random() * 100000);
var data = 'data-' + randomNumber;
var partitionKey = 'pk-' + randomNumber;
var recordParams = {
Data: data,
PartitionKey: partitionKey,
StreamName: streamName,
};
kinesis.putRecord(recordParams, function (err, data) {
callback(null, data);
if (err) {
callback(err);
}
});
};
Take note that instead of using console.log or console.error, you should call callback instead.
When you add a function to a VPC it only has access to resources inside that VPC. Any attempt to access resources outside the VPC will hang and eventually timeout. Since Kinesis doesn't exist inside your VPC, you can't access it.
The fix is to either run the Lambda function outside the VPC, or add a NAT Gateway to your VPC.

aws lambda: invoke function via other lambda function

I have an AWS Lambda function, that need's ~ 30 seconds.
When I connect it to the API Gateway, it's sending a 504 because of the 5 second timeout. So my easyCron Job is failing and will not try it again (I only have a free plan)
So I need an API, that sends a correct 200 status.
My Idea:
Invoke the long term lambda via a short term lambda.
The policy is allowing the invocation.
Here is the code
var AWS = require('aws-sdk'),
params = {
FunctionName: 'cctv',
InvocationType: 'RequestResponse',
LogType: 'Tail'
},
lambda;
AWS.config.update({region: 'us-east-1'});
lambda = new AWS.Lambda();
exports.handler = function (event, context) {
'use strict';
lambda.invoke(params, function (err, data) {
if (err) {
console.log(err, err.stack);
}
else {
console.log(data);
}
});
context.succeed('hey cron job, I think my lambda function is not called');
};
But I think, context.succeed() aborts the execution of lambda.invoke()
Do you have any idea how to solve this?
This is incorrect
InvocationType: 'RequestResponse'
You should use
InvocationType: 'Event'
From http://docs.aws.amazon.com/lambda/latest/dg/API_Invoke.html#API_Invoke_RequestSyntax
By default, the Invoke API assumes "RequestResponse" invocation type. You can optionally request asynchronous execution by specifying "Event" as the InvocationType.
Rather than directly calling your 30+ second Lambda function, you could trigger it from an SNS or S3 PutObject event. This would be asynchronous to your API Gateway route, so it can return very quickly. Of course, you would not have the outcome of the Lamdba job at that time.