How to invoke a Lambda

In this guide we will demonstrate how to use to AttiniLambdaInvoke step to call a Lambda function from our deployment plan.

Introduction

Needing to invoke a Lambda during a deployment is a common and straightforward use case. It could be for testing purposes or to trigger some other task. Calling a Lambda from a StepFunction is not very complicated and can be done without the help of an Attini type. However, the AttiniLambdaInvoke step is still convenient to use because:

  1. It removes a lot of boilerplate configuration, making it easier to use and much more readable.
  2. It makes sure that the payload and output is handled properly.

How to use

The step itself looks like this:

Type: AttiniLambdaInvoke
Parameters:
  FunctionName: String
  Payload: Object

The only mandatory field is “FunctionName”. By default, the entire payload of the deployment plan is passed to the function as input. This can be overridden by setting the “Payload” field. It takes an object that uses json-path syntax to map values from the deployment plan payload, just like normal AWS State Language. So for example, if we would like to only pass two selected values to the Lambda, we could do:

Type: AttiniLambdaInvoke
Parameters:
  FunctionName: my-function
  Payload:
    FirstValue.$: $.output.Step1.FirstValue
    SecondValue.$: $.output.Step1.SecondValue

The above example would pass an object to a Lambda looking like this:

{
  "FirstValue": "the-first-value",
  "SecondValue": "the-second-value"
}

We will look more at how to work with the payload in the following examples.


Demo

In this demo we will create a Distribution containing a deployment plan with two steps. It has the following file structure:

.
├── attini-config.yaml
├── deployment-plan.yaml
└── lambda.yaml

The first step will create a Lambda using CloudFormation. The Lambda will simply return whatever value it receives. How to build a Lambda is a bit out of scope for this demo, but you can find the template together with the rest of the files we are using at the end of this page.

Our deployment plan looks like this:

  EchoLambda:
    Type: Attini::Deploy::DeploymentPlan
    Properties:
      DeploymentPlan:
        - Name: Deploy_EchoLambda
          Type: AttiniCfn
          Properties:
            StackName: !Sub ${AttiniEnvironmentName}-echo-lambda
            Template: /lambda.yaml
        - Name: Invoke_EchoLambda
          Type: AttiniLambdaInvoke
          Parameters:
            FunctionName.$: $.output.Deploy_EchoLambda.FunctionName

The first step deploys the Lambda using the AttiniCfn step. Because the template will output the function name we can read it from the payload and use it to invoke the Lambda.

You can read more about deploying CloudFormation with the AttiniCfn step here.

Let’s deploy it by running the “deploy run” command from the root of the project.

attini deploy run .

And success! We called the Lambda and as we can see in the image below it did return the entire deployment plan payload.

example with entire payload

However, that’s a lot of data, and we probably don’t need all of it. So we can use the “Payload” field to be a bit more selective about what we pass. Let’s change the deployment plan to look like this:

  EchoLambda:
    Type: Attini::Deploy::DeploymentPlan
    Properties:
      DeploymentPlan:
        - Name: Deploy_EchoLamda
          Type: AttiniCfn
          Properties:
            StackName: !Sub ${AttiniEnvironmentName}-echo-lambda
            Template: /lambda.yaml
        - Name: Invoke_EchoLamda
          Type: AttiniLambdaInvoke
          Parameters:
            FunctionName.$: $.output.Deploy_EchoLambda.FunctionName
            Payload:
              DistributionName.$: $.deploymentOriginData.distributionName

Instead of passing the entire payload to the Lambda we choose to simply pass the distribution name. We get the name from the metadata in the payload.

When we run it again, we get the following result:

example with entire payload

Success! We selected some data and passed it to Lambda. The keys of the result is determined by the keys in the “Payload”. We only used one field here, but you can add as many as needed. If you don’t want to read the value from the deployment plan payload, simply remove the “.$” at the end of the key and the “$.” at the start of the value.

You can also find the complete demo on Github.


Example files

Below are all the files we used in the example.

deployment-plan.yaml
AWSTemplateFormatVersion: "2010-09-09"
Transform:
  - AttiniDeploymentPlan
  - AWS::Serverless-2016-10-31

Parameters:
  AttiniEnvironmentName:
    Type: String

Resources:

  EchoLambda:
    Type: Attini::Deploy::DeploymentPlan
    Properties:
      DeploymentPlan:
        - Name: Deploy_EchoLambda
          Type: AttiniCfn
          Properties:
            StackName: !Sub ${AttiniEnvironmentName}-echo-lambda
            Template: /lambda.yaml
        - Name: Invoke_EchoLambda
          Type: AttiniLambdaInvoke
          Parameters:
            FunctionName.$: $.output.Deploy_EchoLambda.FunctionName
            Payload:
              DistributionName.$: $.deploymentOriginData.distributionName
lambda.yaml
AWSTemplateFormatVersion: 2010-09-09
Transform: AWS::Serverless-2016-10-31

Parameters:
  AttiniEnvironmentName:
    Type: String

Resources:

  HelloWorldLambda:
    Type: AWS::Serverless::Function
    Properties:
      Description: A Lambda that returns whatever us passed to it.
      FunctionName: !Sub ${AttiniEnvironmentName}-echo-function
      InlineCode: |
        def lambda_handler(event, context):

          return event;        
      Handler: index.lambda_handler
      Runtime: python3.9


  HelloWorldLambdaLogGroup:
    Type: AWS::Logs::LogGroup
    Properties:
      LogGroupName: !Sub /aws/lambda/${HelloWorldLambda}
      RetentionInDays: 30


Outputs:
  FunctionName:
    Value: !Ref HelloWorldLambda
attini-config.yaml

distributionName: invoke-lambda-demo
initDeployConfig:
  template: deployment-plan.yaml
  stackName: ${environment}-${distributionName}-deployment-plan

package:
  prePackage:
    commands:
      - attini configure set-dist-id --random