Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to call an AWS Step Function using the definitions in the serverless-step-functions plugin?

I'm using Serverless Framework to create my Lambda functions and the serverless-step-functions plugin to define my step functions.

Is it possible to call an step function directly from one of the lambda functions using the name defined into the serverless.yml file?

like image 487
melqui Avatar asked Feb 03 '17 23:02

melqui


2 Answers

I was trying to solve the same problem and this question and the self answer were very helpful. However, I want to add another answer with more details and a working example to help future readers.


There are two things that you may need:

1- Start a State Machine
2- Invoke one specific function from a State Machine (usually for testing purposes)

The following demo uses both cases.

First, we need to configure the serverless.yml file to declare the State Machine, the Lambda functions and the correct IAM permissions.

service: test-state-machine

provider:
  name: aws
  runtime: nodejs4.3
  region: us-east-1
  stage: dev
  environment:
    AWS_ACCOUNT: 1234567890 # use your own AWS ACCOUNT number here

    # define the ARN of the State Machine
    STEP_FUNCTION_ARN: "arn:aws:states:${self:provider.region}:${self:provider.environment.AWS_ACCOUNT}:stateMachine:${self:service}-${self:provider.stage}-lambdaStateMachine"

    # define the ARN of function step that we want to invoke
    FUNCTION_ARN: "arn:aws:lambda:${self:provider.region}:${self:provider.environment.AWS_ACCOUNT}:function:${self:service}-${self:provider.stage}-stateMachineFirstStep"  

functions:
  # define the Lambda function that will start the State Machine
  lambdaStartStateMachine: 
    handler: handler.lambdaStartStateMachine  
    role: stateMachine # we'll define later in this file

  # define the Lambda function that will execute an arbitrary step
  lambdaInvokeSpecificFuncFromStateMachine: 
    handler: handler.lambdaInvokeSpecificFuncFromStateMachine
    role: specificFunction # we'll define later in this file

  stateMachineFirstStep:
    handler: handler.stateMachineFirstStep

# define the State Machine
stepFunctions:
  stateMachines:
    lambdaStateMachine:
      Comment: "A Hello World example"
      StartAt: firstStep
      States: 
        firstStep: 
          Type: Task
          Resource: stateMachineFirstStep
          End: true    

# define the IAM permissions of our Lambda functions
resources:
  Resources:
    stateMachine:
      Type: AWS::IAM::Role
      Properties:
        RoleName: stateMachine
        AssumeRolePolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Principal:
                Service:
                  - lambda.amazonaws.com
              Action: sts:AssumeRole
        Policies:
          - PolicyName: stateMachine
            PolicyDocument:
              Version: '2012-10-17'
              Statement:
                - Effect: "Allow"
                  Action:
                    - "states:StartExecution"
                  Resource: "${self:provider.environment.STEP_FUNCTION_ARN}"
    specificFunction:
      Type: AWS::IAM::Role
      Properties:
        RoleName: specificFunction
        AssumeRolePolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Principal:
                Service:
                  - lambda.amazonaws.com
              Action: sts:AssumeRole
        Policies:
          - PolicyName: specificFunction
            PolicyDocument:
              Version: '2012-10-17'
              Statement:
                - Effect: "Allow"
                  Action:
                    - "lambda:InvokeFunction"
                  Resource: "${self:provider.environment.FUNCTION_ARN}"

package:
  exclude:
    - node_modules/**
    - .serverless/**

plugins:
  - serverless-step-functions

Define the Lambda functions inside the handler.js file.

const AWS = require('aws-sdk');

module.exports.lambdaStartStateMachine = (event, context, callback) => {
  const stepfunctions = new AWS.StepFunctions();
  const params = {
    stateMachineArn: process.env.STEP_FUNCTION_ARN,
    input: JSON.stringify({ "msg": "some input" })
  };

  // start a state machine
  stepfunctions.startExecution(params, (err, data) => {
    if (err) {
      callback(err, null);
      return;
    }

    const response = {
      statusCode: 200,
      body: JSON.stringify({
        message: 'started state machine',
        result: data
      })
    };

    callback(null, response);
  });
};

module.exports.lambdaInvokeSpecificFuncFromStateMachine = (event, context, callback) => {  
  const lambda = new AWS.Lambda();
  const params = {
    FunctionName: process.env.FUNCTION_ARN,
    Payload: JSON.stringify({ message: 'invoked specific function' })
  };

  // invoke a specific function of a state machine
  lambda.invoke(params, (err, data) => {
    if (err) {
      callback(err, null);
      return;
    }

    const response = {
      statusCode: 200,
      body: JSON.stringify({
        message: 'invoke specific function of a state machine',
        result: data
      })
    };

    callback(null, response);
  });
};

module.exports.stateMachineFirstStep = (event, context, callback) => {
  const response = {
    statusCode: 200,
    body: JSON.stringify({
      message: 'state machine first step',
      input: event
    }),
  };

  callback(null, response);
};

Deploy executing:

serverless deploy stepf  
serverless deploy  

Test using:

serverless invoke -f lambdaStartStateMachine  
serverless invoke -f lambdaInvokeSpecificFuncFromStateMachine
like image 180
Zanon Avatar answered Sep 18 '22 13:09

Zanon


Solved using serverless environment variables:

environment:
  MYFUNCTION_ARN: "arn:aws:states:${self:provider.region}:${self:provider.environment.AWS_ACCOUNT}:stateMachine:${self:service}-${self:provider.stage}-myFunction"

In the function:

var params = {
  stateMachineArn: process.env.MYFUNCTION_ARN
};
like image 24
melqui Avatar answered Sep 20 '22 13:09

melqui