Two Main Ways to Write Step Functions in CDK: Which is Better?

Ronyyosef
CyberArk Engineering
4 min read6 days ago

Developing complex workflows and business-critical applications in the cloud can be challenging. AWS Step Functions offers a powerful solution for orchestrating multiple AWS services, but figuring out the best way to develop and manage these workflows can be daunting. Should you use the native CDK libraries or write your Step Functions directly in Amazon States Language (ASL)?

This post aims to solve this problem by comparing these two main approaches, providing practical examples, and offering recommendations based on real-world experiences. Whether you are new to AWS Step Functions or looking to optimize your existing workflows, this guide will help you make an informed decision.

What are AWS Step Functions?

For a comprehensive tutorial on the two main ways to develop Step Functions using CDK, you can watch this YouTube video by FooBar Serverless.

photo of AWS Step Functions Icon

Use Cases for AWS Step Functions

AWS Step Functions is ideal for:

  • Data Processing: Process and transform data at scale with services such as AWS Lambda, AWS Glue, and Amazon S3.
  • Machine Learning: Orchestrate machine learning workflows, including data preprocessing, model training, and evaluation.
  • Microservice Orchestration: Coordinate multiple microservices to handle complex transactions and workflows.
  • IT and Security Automation: Automate IT tasks and security operations, such as provisioning resources, patching systems, and responding to incidents.

For more detailed use cases, visit the AWS Step Functions use cases documentation.

When developing AWS Step Functions with CDK, you have two main options:

  1. CDK Native Approach: Using the native CDK aws_stepfunctions and aws_stepfunctions_tasks libraries to create the step function.
  2. ASL JSON: Writing the Step Function in Amazon states language directly in JSON and using DefinitionBody.from_fileto load it.

This blog compares these methods, providing examples and discussing their pros and cons to help you choose the best approach for your needs. I recommend using the AWS CDK with ASL directly.

My Personal Journey with AWS Step Functions and CDK

Our team had been using the CDK native approach for creating Step Functions using the aws_stepfunctions and aws_stepfunctions_tasks libraries for a long time. These libraries provided a high-level, object-oriented interface that integrated well with other CDK constructs. However, we encountered some issues when AWS introduced the Step Functions distributed map feature. The library did not support this new feature, and we had to use CustomStateto work around this limitation. This experience led us to explore the ASL approach, which offers complete access to all Step Functions features, and more.

Method 1: CDK Native Approach

The first approach uses the CDK native aws_stepfunctions and aws_stepfunctions_tasks libraries for AWS Step Functions, which provide a high-level, object-oriented interface.

from aws_cdk import aws_lambda as _lambda
from aws_cdk import aws_stepfunctions as sfn
from aws_cdk import aws_stepfunctions_tasks as tasks
from constructs import Construct

class HelloStateMachineConstructUsingLibrary(Construct):

def __init__(self, scope: Construct, id_: str) -> None:
super().__init__(scope, id_)

# Define a Lambda function
hello_function = _lambda.Function(self, "HelloFunction",
runtime=_lambda.Runtime.PYTHON_3_11,
handler="lambda_handlers.handler",
code=_lambda.Code.from_asset(LAMBDA_CODE_FILES_DIR))

# Define LambdaInvoke task step
hello_task = tasks.LambdaInvoke(
self, "HelloTask",
lambda_function=hello_function,
output_path="$.Payload"
)

sfn.StateMachine(
self, "HelloStateMachine",
definition_body=sfn.DefinitionBody.from_chainable(hello_task)
)

The second method, which I recommend, involves writing the Step Function in the ASL JSON and using the DefinitionBody.from_fileto load it.

Example:

{
"StartAt": "HelloTask",
"States": {
"HelloTask": {
"Type": "Task",
"Resource": "${HelloFunctionArn}",
"End": true
}
}
}
import os

from aws_cdk import aws_iam as iam
from aws_cdk import aws_lambda as _lambda
from aws_cdk import aws_stepfunctions as sfn
from constructs import Construct

class HelloStateMachineConstructUsingASLJson(Construct):
def __init__(self, scope: Construct, id_: str) -> None:
super().__init__(scope, id_)

# Define a Lambda function
hello_function = _lambda.Function(self, "HelloFunction",
runtime=_lambda.Runtime.PYTHON_3_11,
handler="lambda_handlers.handler",
code=_lambda.Code.from_asset(LAMBDA_CODE_FILES_DIR))
# Role for the state machine
role = self._create_hello_state_machine_role(hello_function)

sfn.StateMachine(
self, "HelloStateMachine",
definition_body=sfn.DefinitionBody.from_file(f'{os.path.dirname(__file__)}/hello_machine_template.asl.json'),
definition_substitutions={
"HelloFunctionArn": hello_function.function_arn
},
role=role
)
The image shows the AWS Step Functions workflow designer interface.

Comparison of ASL JSON vs. CDK Native Approach

Let’s look at the two different methods in terms of different features.

The image is a comparison table between ASL JSON and CDK Native Approach for AWS Step Functions

The Winning ASL JSON Solution for Step Functions

Both the CDK native approach using the aws_stepfunctions and aws_stepfunctions_tasks libraries and the ASL JSON method have their pros and cons. The CDK native approach offers a more CDK-integrated experience, while ASL JSON provides access to all Step Functions features and delivers a better developer environment, readability, and maintainability.

Given the need for full feature access and the importance of keeping code readable and maintainable, I recommend using the AWS CDK with ASL JSON for your Step Functions development. This approach ensures that you leverage the full power of AWS Step Functions, maintain a developer-friendly environment using the AWS console and the VSCode AWS Toolkit and keep your workflows clear and easy to manage. Additionally, ASL JSON allows for easy sharing and reuse of your Step Function definitions across different projects and teams.

Feel free to share your opinion and how you write and deploy your Step Function code. Enjoy! 😊

--

--