Step Functions Workflow Studio with Serverless Framework

Step Functions Workflow Studio with Serverless Framework

ยท

3 min read

The serverless-step-functions plugin is a nifty Serverless Framework extension that allows you to define step functions directly within the serverless.yml. However, the definitions for step functions tend to get messy fast and it's hard to visualize how a workflow actually looks and flows just from looking at the YAML. The Step Functions Workflow Studio helps you with exactly this by allowing you to interactively sketch out your state machine. In this post, I'll give you a brief walkthrough of how we can use the Workflow Studio together with the Step Functions plugin.

Creating a State Machine

To get started, navigate to the Step Functions page in the AWS Console and press "Create state machine".

image-20210716221315868

Next, choose "Design your workflow visually", then go to Next.

image-20210716221446618

From here, you can drag and drop Actions and Flows, tinker with the properties, flow, and error handling until you're happy with the design.

image-20210716221527803

Set any resources that you want to eventually define in your serverless app to refer to a made-up value in the meantime - setting the Lambda Function name to just a string name for example. Once you're done, click "Next".

image-20210811082837045

At last, we have our generated JSON schema for the state machine! But wait. Our Serverless template is in YAML, not JSON - how do we translate this into something that we can actually use?

image-20210716221635101

As it turns out, YAML is just a superset of JSON, meaning all JSON is valid YAML. The JSON that was generated is in the form of Amazon State Languages spec, and that same spec is used by our serverless-step-functions plugin. This means that we can translate our JSON snippet to YAML using an online tool, such as Code Beatify, or a CLI tool such as yq.

image-20210716221957330

Now that we've got the state machine defined in YAML, all we need to do is actually use it in the serverless.yml.

Putting it together

If you don't already have a serverless project set up, check out my guide to a simple esbuild setup or just run serverless in your terminal to choose from one of the boilerplates.

The serverless-step-functions plugin extends the serverless.yml format with a root stepFunctions property. We'll use that to define our step function by pasting in our YAML snippet as the definition of the state machine.

// serverless.yml
[...]

stepFunctions:
  stateMachines:
    myStateMachine:
        name: MyStateMachine
        [your generated state machine goes here]

Keep in mind that any references to Lambda functions or other services that we used placeholder references for needs to be updated to refer to the actual resources we want them to point to. As an example, a Lambda definition might look like this when generated:

FirstFunction:
  Type: Task
  Resource: arn:aws:states:::lambda:invoke
  OutputPath: $.Payload
  Parameters:
    Payload.$: $
    FunctionName: firstFunction

and we can change that to instead refer to a firstFunction Lambda defined in our stack by changing it to:

FirstFunction:
  Type: Task
  Resource: !GetAtt firstFunction.Arn
  OutputPath: "$.Payload"
  Parameters:
        Payload.$: "$"

For the workflow from the examples above, this is what the full serverless.yml might look like.

service: step-functions-demo

plugins:
  - serverless-step-functions

provider:
  name: aws
  runtime: nodejs14.x

stepFunctions:
  stateMachines:
    myStateMachine:
      name: MyStateMachine
      definition:
        Comment: This is your state machine
        StartAt: First function
        States:
          First function:
            Type: Task
            Resource: !GetAtt firstFunction.Arn
            OutputPath: "$.Payload"
            Parameters:
              Payload.$: "$"
            Retry:
            - ErrorEquals:
              - Lambda.ServiceException
              - Lambda.AWSLambdaException
              - Lambda.SdkClientException
              IntervalSeconds: 2
              MaxAttempts: 6
              BackoffRate: 2
            Next: Wait
          Wait:
            Type: Wait
            Seconds: 5
            Next: Second function
          Second function:
            Type: Task
            Resource: !GetAtt secondFunction.Arn
            OutputPath: "$.Payload"
            Parameters:
              Payload.$: "$"
            Retry:
            - ErrorEquals:
              - Lambda.ServiceException
              - Lambda.AWSLambdaException
              - Lambda.SdkClientException
              IntervalSeconds: 2
              MaxAttempts: 6
              BackoffRate: 2
            End: true

functions:
  firstFunction:
    handler: src/functions/first.handler
  secondFunction:
    handler: src/functions/second.handler

If you enjoyed this post and want to see more, follow me on Twitter at @TastefulElk where I frequently write about serverless tech, AWS, and developer productivity! ๐Ÿ‘‹

Did you find this article valuable?

Support Sebastian Bille by becoming a sponsor. Any amount is appreciated!

ย