Nodejs - Invoke an AWS.Lambda function from within another lambda function

109,503

Solution 1

Invoking a Lambda Function from within another Lambda function is quite simple using the aws-sdk which is available in every Lambda.

I suggest starting with something simple first.
This is the "Hello World" of intra-lambda invocation:

Lambda_A invokes Lambda_B with a Payload containing a single parameter name:'Alex'.
Lambda_B responds with Payload: "Hello Alex".

lambda invoke

First create Lambda_B which expects a name property on the event parameter
and responds to request with "Hello "+event.name:

Lambda_B

exports.handler = function(event, context) {
  console.log('Lambda B Received event:', JSON.stringify(event, null, 2));
  context.succeed('Hello ' + event.name);
};

Ensure that you give Lambda_B and Lambda_A the same role.
E.g: create a role called lambdaexecute which has AWSLambdaRole, AWSLambdaExecute and AWSLambdaBasicExecutionRole (All are required):

lambda-role-for-intra-lambda-execution

Lambda_A

var AWS = require('aws-sdk');
AWS.config.region = 'eu-west-1';
var lambda = new AWS.Lambda();

exports.handler = function(event, context) {
  var params = {
    FunctionName: 'Lambda_B', // the lambda function we are going to invoke
    InvocationType: 'RequestResponse',
    LogType: 'Tail',
    Payload: '{ "name" : "Alex" }'
  };

  lambda.invoke(params, function(err, data) {
    if (err) {
      context.fail(err);
    } else {
      context.succeed('Lambda_B said '+ data.Payload);
    }
  })
};

Once you have saved both these Lambda functions, Test run Lambda_A:

lambda invoke-lambda_a-execution-result

Once you have the basic intra-lambdda invocation working you can easily extend it to invoke more elaborate Lambda functions.

The main thing you have to remember is to set the appropriate ARN Role for all functions.

Solution 2

As of Dec 3, 2016, you can simply use an AWS Step function to put Lambda function Lambda_B as the sequential step of Lambda_A.

With AWS Step Functions, you define your application as a state machine, a series of steps that together capture the behavior of the app. States in the state machine may be tasks, sequential steps, parallel steps, branching paths (choice), and/or timers (wait). Tasks are units of work, and this work may be performed by AWS Lambda functions, Amazon EC2 instances of any type, containers, or on premises servers—anything that can communicate with the Step Functions API may be assigned a task.

So the following state machine should meet your need.

enter image description here

Here is the code corresponding to the state machine.

{
  "Comment": "A simple example of the Amazon States Language using an AWS Lambda Function",
  "StartAt": "Lambda_A",

  "States": {
    "Lambda_A": {
      "Type": "Task",
      "Resource": "arn:aws:lambda:REGION:ACCOUNT_ID:function:FUNCTION_NAME",
      "Next": "Lambda_B"
    },
    "Lambda_B":{
      "Type": "Task",
      "Resource": "arn:aws:lambda:REGION:ACCOUNT_ID:function:FUNCTION_NAME",
      "End": true
    }

  }
}

Moreover, you can add much more sophisticated logics in a state machine, such as parallel steps and catch failures. It even logs the details of every single execution which makes debugging a much better experience, especially for lambda functions.

enter image description here

Solution 3

Everything mentioned by @nelsonic is correct, except for the roles.

I tried choosing the roles that he mentioned above:

  • AWSLambdaExecute
  • AWSLambdaBasicExecutionRole

But it did not allow me to invoke my other lambda function, so I changed the role to the below:

  • AWSLambdaRole
  • AWSLambdaBasicExecutionRole

The reason behind is AWSLambdaExecute only provides Put, Get access to S3 and full access to CloudWatch Logs. but AWSLambdaRole provides Default policy for AWS Lambda service role. if you observe its permission policy it will talk about the invokeFunction

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "lambda:InvokeFunction"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}

Note: it is OK to proceed without AWSLambdaBasicExecutionRole policy as it only enables the logging in the cloud watch nothing much. But AWSLambdaRole is absolutely necessary.

Solution 4

It's easier to invoke a lambda using the AWS.Lambda promises interface in aws-sdk than using callbacks.

This example function lets you make a synchronous invocation of a lambda from another lambda (it uses 'RequestResponse' as InvocationType, so you'll can get the value returned by the invoked lambda).

If you use 'Event' (for asynchronous invocation), you can't get the value returned by the called lambda, only be able to detect whether the lambda could be invoked with success or not. It is intended for cases when you don't need to obtain a returned value from the invoked lambda.

//
// Full example of a lambda that calls another lambda
//
// (create a lambda in AWS with this code)
//
'use strict';

//
// Put here the name of the function you want to call
//
const g_LambdaFunctionName = 'PUT_HERE_THE_INVOKED_LAMBDA_NAME'; // <======= PUT THE DESIRED VALUE

const AWS    = require('aws-sdk');
const lambda = new AWS.Lambda;

//
// Expected use:
//
//   // (payload can be an object or a JSON string, for example)
//   let var = await invokeLambda(lambdaFunctionName, payload);
//
const invokeLambda = async (lambdaFunctionName, payload) => {

   console.log('>>> Entering invokeLambda');

   // If the payload isn't a JSON string, we convert it to JSON
   let payloadStr;
   if (typeof payload === 'string')
   {
       console.log('invokeLambda:  payload parameter is already a string: ', payload);
       payloadStr = payload;
   }
   else
   {
       payloadStr = JSON.stringify(payload, null, 2);
       console.log('invokeLambda: converting payload parameter to a string: ', payloadStr);
   }

   let params = {
       FunctionName   : lambdaFunctionName,               /* string type, required */
       // ClientContext  : '',                               /* 'STRING_VALUE' */
       InvocationType : 'RequestResponse',                /* string type: 'Event' (async)| 'RequestResponse' (sync) | 'DryRun' (validate parameters y permissions) */
       // InvocationType : 'Event',

       LogType        : 'None',                           /* string type: 'None' | 'Tail' */
       // LogType        : 'Tail',
       Payload        : payloadStr,                       /* Buffer.from('...') || 'JSON_STRING' */ /* Strings will be Base-64 encoded on your behalf */
       //  Qualifier      : '',                             /* STRING_VALUE' */
   };

   //
   // TODO/FIXME: add try/catch to protect this code from failures (non-existent lambda, execution errors in lambda)
   //
   const lambdaResult = await lambda.invoke(params).promise();

   console.log('Results from invoking lambda ' + lambdaFunctionName + ': ' , JSON.stringify(lambdaResult, null, 2) );

   // If you use LogType = 'Tail', you'll obtain the logs in lambdaResult.LogResult.
   // If you use 'None', there will not exist that field in the response.
   if (lambdaResult.LogResult)
   {
       console.log('Logs of lambda execution: ',  Buffer.from(lambdaResult.LogResult, 'base64').toString());
   }

   console.log('invokeLambdaSync::lambdaResult: ', lambdaResult);

   console.log('<<< Returning from invokeLambda, with lambdaResult: ', JSON.stringify(lambdaResult, null, 2));

   // The actual value returned by the lambda it is lambdaResult.Payload
   // There are other fields (some of them are optional)
   return lambdaResult;
};

//
// We'll assign this as the calling lambda handler.
//
const callingFunc = async (event) => {

   //
   // in this example We obtain the lambda name from a global variable
   //
   const lambdaFunctionName = g_LambdaFunctionName;
   
   // const payload            = '{"param1" : "value1"}';
   const payload            = event;

   //
   // invokeLambda has to be called from a async function
   // (to be able to use await)
   //
   const result = await invokeLambda(lambdaFunctionName, payload);

   console.log('result: ', result);
};

// Assing handler function
exports.handler = callingFunc;

Notice that you should use await before invokeLambda:

...
    //
    // Called from another async function
    //
    const result = await invokeLambda(lambdaFunctionName, payload);
...

Some relevant links with additional information:

Share:
109,503

Related videos on Youtube

hyprstack
Author by

hyprstack

The only people who ever fail are those who stop trying.

Updated on July 08, 2022

Comments

  • hyprstack
    hyprstack almost 2 years

    I have the following function which I use to invoke a Lambda function from within my code.

    However when I try to use it within a Lambda function, I get the following error:

    AWS lambda undefined 0.27s 3 retries] invoke({ FunctionName: 'my-function-name',
      InvocationType: 'RequestResponse',
      LogType: 'Tail',
      Payload: <Buffer > })
    

    How can I invoke a Lambda function from within a Lambda function?

    My function:

    'use strict';
    
    var AWS = require("aws-sdk");
    
    var lambda = new AWS.Lambda({
        apiVersion: '2015-03-31',
        endpoint: 'https://lambda.' + process.env.DYNAMODB_REGION + '.amazonaws.com',
        logger: console
    });
    
    var lambdaHandler = {};
    
    // @var payload - type:string
    // @var functionName - type:string
    lambdaHandler.invokeFunction = function (payload, functionName, callback) {
    
        var params = {
            FunctionName: functionName, /* required */
            InvocationType: "RequestResponse",
            LogType: "Tail",
            Payload: new Buffer(payload, 'utf8')
        };
    
        var lambdaRequestObj = lambda.invoke(params);
    
        lambdaRequestObj.on('success', function(response) {
            console.log(response.data);
        });
    
        lambdaRequestObj.on('error', function(response) {
            console.log(response.error.message);
        });
    
        lambdaRequestObj.on('complete', function(response) {
            console.log('Complete');
        });
    
        lambdaRequestObj.send();
    
        callback();
    };
    
    module.exports = lambdaHandler;
    
    • nelsonic
      nelsonic about 8 years
      is there a reason for writing your lambda.invoke as an event emitter instead of passing it a callback?
    • hyprstack
      hyprstack about 8 years
      Yes. This was for a webserver and I was asked not to wait for the response from Lambda before sending the response to the client.
    • nelsonic
      nelsonic about 8 years
      Ah, ok. you can simply fire the lambda.invoke and ignore the response.
  • Arpit Vaishnav
    Arpit Vaishnav over 7 years
    I have checked region ,vpc and every thing , but still I am not able to invoke the lambda function. I am not able to access the function . Any new updates we are missing . Can you help, @nelsonic
  • nelsonic
    nelsonic over 7 years
    @ArpitVaishnav do both your functions have the same role with AWSLambdaExecute Policy.
  • Arpit Vaishnav
    Arpit Vaishnav over 7 years
    Yes. Please refer the question link please.
  • Will Brickner
    Will Brickner about 7 years
    Wow, thank you for sharing this, I didn't know this existed, I may use this in the future! Looks really useful!
  • Michael Du
    Michael Du almost 7 years
    so does this mean we have to execute this step function now? Or is this automatically configured into Lambda_A (such that Lambda_B will automatically execute when A is triggered)?
  • C.Lee
    C.Lee almost 7 years
    @MichaelDu You need to execute the step functions instead of lambda. An easy way to trigger it is using API Gateway similarly as you would trigger your lambda function, check out the details in this SO question stackoverflow.com/questions/41188584/…
  • Mangesh Borkar
    Mangesh Borkar over 6 years
    What I did to get this working is to add all the lambda policies possible to the role. Try searching for text "lambda" and added all those autosuggested policies ;) for now and it worked ... Will dig deeper later to figure out what policies I can do away with...
  • Zigglzworth
    Zigglzworth over 6 years
    So how is this more simple than simply executing a lambda function from another lambda function? The execution of lambda B may be dependent on what happens in Lambda A and require data from there
  • C.Lee
    C.Lee over 6 years
    @Zigglzworth The major benefit of step-function is the control of the entire workflow for more complex projects. It gives you conditional execution of lambda and wait function for free, and it's not limited by the max time limit of lambda function (300s). With that said, if you only have two lambda functions that you'd like to chain, then yes, it's probably easier to not use step-function :)
  • Muhammad Usama Mashkoor
    Muhammad Usama Mashkoor about 6 years
    @nelsonic thanks for the answer can you please let us know how we can call the Lambda_B function asynchronous because as you know amazon lamda has time limit...?. Thanks again
  • nelsonic
    nelsonic about 6 years
    @usama this example is "asynchronous" ... if you are hitting time limits simply increase the execution time for your Lambda function.
  • Rashik
    Rashik over 5 years
    Is there a way to put a(or multiple) variable inside the payload?
  • romanbird
    romanbird about 5 years
    This didn't work for me, but adding InvokeFunction permission for the role did the trick
  • Denis Ivanov
    Denis Ivanov almost 5 years
    if you are making http request, getting step function output is quite tricky.
  • Christian Ulbrich
    Christian Ulbrich about 4 years
    @Zigglzworth Using StepFunction you decouple functionality; Lambda_A does now not need to know about Lambda_B and becomes reusable. This is one of the fundamental principles in software architecture, called loose coupling.
  • TanguyP
    TanguyP about 3 years
    Instead of the policies mentioned in this answer, I had to attach the AWSLambdaRole policy for the invocation to succeed.
  • Paul
    Paul over 2 years
    as far as I can tell lambda_b does not need the AWSLambdaRole, AWSLambdaExecute policies, only the AWSLambdaBasicExecutionRole