AWS CDK: State Machine with Step Functions

December 6th, 2020 435 Words

With AWS Step Functions, you can easily orchestrate serverless functions and sequence them with other AWS services to a bundle application. You can create AWS Step Functions with CloudFormation, the AWS Cloud Development Kit, or - of course - using the visual interface available in the AWS Management Console. This post shows how to orchestrate AWS Lambda functions to a simple State Machine using AWS Step Functions.

AWS Lambda Function with CDK

The AWS Cloud Development Kit simplifies a couple of things. One is creating an AWS Lambda function with inline code. For this example, create two lambda functions: One for generating a random string and one function to reverse the provided input:

const functionGenerateID = new lambda.Function(this, "GenerateID", {
  runtime: lambda.Runtime.NODEJS_12_X,
  handler: "index.handler",
  code: lambda.Code.fromInline(`
    const generate = () => Math.random().toString(36).substring(7);

    exports.handler = async () => ({"value": generate()});
  `),
});

const functionReverseID = new lambda.Function(this, "ReverseID", {
  runtime: lambda.Runtime.NODEJS_12_X,
  handler: "index.handler",
  code: lambda.Code.fromInline(`
    const reverse = (str) => (str === '') ? '' : reverse(str.substr(1)) + str.charAt(0);

    exports.handler = async (state) => ({"value": reverse(state.value)});
  `),
});

AWS Step Functions with CDK

For AWS Step Functions, the AWS Cloud Development Kit offers a few helper functions to easily configure individual steps.

First, the State Machine invokes an AWS Lambda function to create a random string. The next step waits for a second. Aftward the second function reverses the generated string.

const first = new tasks.LambdaInvoke(this, "Generate ID", {
  lambdaFunction: functionGenerateID,
  outputPath: "$.Payload",
})
  .next(
    new sfn.Wait(this, "Wait 1 Second", {
      time: sfn.WaitTime.duration(cdk.Duration.seconds(1)),
    })
  )
  .next(
    new tasks.LambdaInvoke(this, "Reverse ID", {
      lambdaFunction: functionReverseID,
      outputPath: "$.Payload",
    })
  );

State Machine with AWS Step Functions

As a CDK Construct, both code samples together look like this:

import * as cdk from "@aws-cdk/core";

import * as lambda from "@aws-cdk/aws-lambda";
import * as sfn from "@aws-cdk/aws-stepfunctions";
import * as tasks from "@aws-cdk/aws-stepfunctions-tasks";

export class StateMachine extends cdk.Construct {
  public Machine: sfn.StateMachine;

  constructor(scope: cdk.Construct, id: string) {
    super(scope, id);

    const functionGenerateID = new lambda.Function(this, "GenerateID", {
      runtime: lambda.Runtime.NODEJS_12_X,
      handler: "index.handler",
      code: lambda.Code.fromInline(`
        const generate = () => Math.random().toString(36).substring(7);

        exports.handler = async () => ({"value": generate()});
      `),
    });

    const functionReverseID = new lambda.Function(this, "ReverseID", {
      runtime: lambda.Runtime.NODEJS_12_X,
      handler: "index.handler",
      code: lambda.Code.fromInline(`
        const reverse = (str) => (str === '') ? '' : reverse(str.substr(1)) + str.charAt(0);

        exports.handler = async (state) => ({"value": reverse(state.value)});
      `),
    });

    const definition = new tasks.LambdaInvoke(this, "Generate ID", {
      lambdaFunction: functionGenerateID,
      outputPath: "$.Payload",
    })
      .next(
        new sfn.Wait(this, "Wait 1 Second", {
          time: sfn.WaitTime.duration(cdk.Duration.seconds(1)),
        })
      )
      .next(
        new tasks.LambdaInvoke(this, "Reverse ID", {
          lambdaFunction: functionReverseID,
          outputPath: "$.Payload",
        })
      );

    this.Machine = new sfn.StateMachine(this, "StateMachine", {
      definition,
      timeout: cdk.Duration.minutes(5),
    });
  }
}