serverless-step-functions deploys state machines as CloudFormation from serverless.yml
AWS Step Functions plugin for Serverless Framework ⚡️
At a glance
- What is it?
- serverless-step-functions is a Serverless Framework plugin that lets you define AWS Step Functions state machines in Amazon States Language inside serverless.yml, with CloudFormation intrinsic functions so a state machine can call a Lambda function declared in the same file. It adds API Gateway, Schedule and CloudWatch Event triggers, alarms, blue-green deployment and a validator that checks the ASL before deploy.
- Who is it for?
- serverless-step-functions fits a team that already deploys with the Serverless Framework and wants its state machines in the same file and the same deploy as its functions, and it fits a team that wants API Gateway and schedule triggers attached to a workflow without hand-writing CloudFormation.
- Can I use it commercially?
- Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
- Is it still maintained?
- Yes. The repository last received commits 96 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 7, 2026, and from our analysis. They are not legal advice.
Star history
- serverless-operations/serverless-step-functions1,048 ★ · +0
| Repository | 2026-10-21 | 2026-11-07 |
|---|---|---|
| serverless-operations/serverless-step-functions | 1k | 1k |
0 Stars in the last 17 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
The state machine lives in serverless.yml
serverless-step-functions is the Serverless Framework plugin for AWS Step Functions, and the core idea is that a workflow is declared in the same file as the functions it calls.
The requirement is stated up front. Serverless Framework v2.32.0 or later is required.
Installation is a dev dependency and a plugins entry.
npm install --save-dev serverless-step-functionsplugins:
- serverless-step-functionsThe declaration is a stepFunctions block with stateMachines, and each machine has events, a name and a definition written in Amazon States Language.
stepFunctions:
stateMachines:
hellostepfunc1:
events:
- http:
path: gofunction
method: GET
- schedule:
rate: rate(10 minutes)
enabled: true
input:
key1: value1
key2: value2
stageParams:
stage: dev
name: myStateMachine
definition:
Comment: "A Hello World example of the Amazon States Language using an AWS Lambda Function"
StartAt: HelloWorld1
States:
HelloWorld1:
Type: Task
Resource:
Fn::GetAtt: [hello, Arn]
End: true
dependsOn: CustomIamRoleThe important line is the Resource. It is Fn::GetAtt: [hello, Arn], which is a CloudFormation intrinsic function pointing at the hello function declared in the same serverless.yml. So the workflow refers to the function by logical id, and the deploy resolves it to the real ARN.
The README is careful about a subtlety here, and it is a real one. Since Ref returns different things, an ARN, an ID, a resource name or whatever, depending on the type of CloudFormation resource, please refer to that page to see whether you need to use Ref or Fn::GetAtt. Using the wrong one produces a state machine that references a function name where an ARN is required, and the failure is at runtime rather than at deploy time.
The alternative, which the README recommends when you need to construct the ARN by hand, is to use the Fn::Sub intrinsic function directly with pseudo-parameters such as ${AWS::Region} or ${AWS::AccountId}, and it notes that no extra plugins are required for that.
The plugin reads the stack it deploys into
The dependency list shows that this plugin does more than emit CloudFormation. It talks to AWS while it deploys.
"dependencies": {
"@aws-sdk/client-cloudformation": "^3.700.0",
"@aws-sdk/client-sfn": "^3.700.0",
"@osls/compose": "^1.4.0",
"asl-validator": "^3.11.0",
"bluebird": "^3.4.0",
"chalk": "^4.1.2",
"joi": "^17.7.0",
"lodash": "^4.17.11"
}Three of these are worth pulling out.
asl-validator is the important one, because it means the plugin validates your Amazon States Language before it deploys it. A state machine definition with a typo in a state name or a malformed Catch block is rejected at deploy time by the plugin rather than at runtime by Step Functions, and the README has a section on pre-deployment validation.
@aws-sdk/client-cloudformation and @aws-sdk/client-sfn are the two service clients, and their presence tells you the plugin both creates the stack and interacts with Step Functions directly. That is what makes the features that need to read the deployed state possible, most obviously the blue-green deployment and the retain property.
@osls/compose is the Serverless Compose plugin, and the peer dependencies list osls at >=3 and serverless at >=3.0.0, with both marked optional in peerDependenciesMeta. The optional marking is a pragmatic choice, since the plugin can be used with either the classic Serverless Framework or the newer osls binary, and requiring one would break the other.
The config and validation layer is joi for the schema, lodash for the general work, bluebird for promises and chalk for the terminal output.
The tests match the shape. The unit tests are mocha with mocha-param and sinon, run over lib/**/*.test.js, with c8 for coverage, and the integration test is a real deploy, osls deploy --stage test.
Triggers, alarms and the gotchas page
The table of contents is long and it is essentially a list of the ways a workflow can be entered and observed.
On the trigger side there is API Gateway with a long list of variations. A simple HTTP endpoint, a custom Step Functions action, an HTTP endpoint with a custom IAM role, sharing API Gateway and API resources between state machines, enabling CORS, HTTP endpoints with AWS_IAM authorizers, HTTP endpoints with custom authorizers, a shared authorizer, the Lambda proxy request template, customising request body mapping templates, customising response headers and templates, sending a request to an API, setting API keys for a Rest API, and request schema validators.
The Schedule section has the same structure, with enabling and disabling, specifying a name and description, a scheduled events IAM role, an InputTransformer, and an option to use EventBridge Scheduler instead of EventBridge rules.
The CloudWatch Event section adds a custom event bus for both CloudWatch and EventBridge, a dead letter queue and a retry policy.
Then the observability features. CloudWatch Alarms, with SNS topics per state and a metrics list, CloudWatch Notifications, CloudWatch Logs, and X-Ray.
And the two deployment features that go beyond the normal. Blue-green deployments, where a new state machine is deployed alongside the old one and traffic is switched, and the retain property, which keeps a state machine across a deploy instead of replacing it.
The README has a section called Current Gotcha, and the presence of a section with that name is itself informative. A plugin that wraps a framework and a service at the same time accumulates edge cases, and a document that names them rather than hiding them is doing the reader a favour. The Express Workflow section is in the same list, and it is a newer Step Functions feature that a plugin predating it will handle differently.
The commands are deploy and invoke, and the IAM Role section covers the permission the state machine needs to run.
LocalStack is the integration test environment
The docker-compose.yml at the repository root is a single service, and its configuration list is the best description of what the plugin actually touches.
services:
localstack:
image: localstack/localstack:4
network_mode: host
environment:
SERVICES: cloudformation,stepfunctions,iam,lambda,s3,logs,sns,cloudwatch,sqs,events,scheduler,apigateway
volumes:
- /var/run/docker.sock:/var/run/docker.sockSo the integration tests run against a LocalStack container emulating twelve AWS services, and the list is a map of the plugin's surface area. CloudFormation because that is how the stack is created, stepfunctions because that is the target, iam because roles are involved, lambda because the workflows call functions, s3 and logs and sns and cloudwatch because of the observability features, sqs for the queue and dead letter queue options, events and scheduler for the two event trigger styles, and apigateway for the HTTP triggers.
LocalStack 4 with host networking and the Docker socket mounted is the standard way to run these tests, and mounting the socket means the container can start other containers, which LocalStack needs for some of the service emulations.
The repository top level listing backs up the picture of a plugin with a mature test setup. There is a lib/ directory with the implementation and the co-located test files, a fixtures/ directory for test data, a serverless-compose.js at the root, a docker-compose.yml, and a .github/ directory with a workflow named config.yml in the CI badge. The release tooling is release-please with a .release-please-manifest.json and a release-please-config.json, plus a commitlint.config.js with conventional commits.
The three most recent releases, v3.31.0 on 2026-07-13, v3.31.1 on 2026-07-21 and v3.31.2 on 2026-08-03, show a patch release every week or two, and the last push to master was on 2026-08-03.
Where serverless-step-functions is the wrong choice
The first wrong case is a team that does not use the Serverless Framework. The plugin's entire value is that a workflow is declared in serverless.yml alongside the functions it calls, and it deploys with the same command. A team on Terraform or SAM has a different source of truth for its infrastructure and would be introducing a second mechanism for the same stack.
The second wrong case is a project that does not want its state machines in the service repository. Some organisations require that workflows be owned by a platform team, in a separate repository, reviewed on their own schedule. Putting the definition in serverless.yml ties the workflow to the service, and no amount of configuration changes that.
The third wrong case is a team that has moved off the Serverless Framework to osls or to another deployment tool and has no intention of using either. The peer dependencies are both optional, which is a courtesy, but the plugin's assumptions are about Serverless Framework's plugin model, its variable resolution and its deploy lifecycle.
The fourth wrong case is a team that needs Step Functions features the plugin has not caught up with. The Express Workflow section in the table of contents and the Current Gotcha section both point at the same thing, that a plugin wrapping a fast moving service has lag, and a team whose workflow uses a recent Step Functions feature should check the gotchas before assuming the plugin handles it.
How it differs from writing CloudFormation for a state machine
The comparison worth making is with the alternative, which is a CloudFormation template with an AWS::StepFunctions::StateMachine resource and the definition pasted in as a string.
That works and it has one clear advantage, which is that CloudFormation is the native way to describe AWS infrastructure and it is what everything else in an AWS stack is written in. A team with a single workflow and a good CloudFormation tooling chain has no reason to add a plugin.
The difference in approach is in the references. In a raw template, the state machine definition has to refer to a Lambda ARN, and the ARN of a function in the same template is not known until the stack creates it, so the definition either needs a hardcoded name, which breaks when the stack name changes, or a GetAtt through a Sub, which is verbose and easy to get wrong. The plugin's whole reason to exist is that the definition can use Fn::GetAtt: [hello, Arn] and have the resolution happen for you.
The second difference is the trigger wiring. Adding an API Gateway endpoint in front of a state machine, with the request mapping, the authorizer and the responses, is a substantial amount of CloudFormation. The plugin's API Gateway section lists a dozen variations of that wiring, each of which would otherwise be a template fragment the user writes.
The third difference is the validation. A CloudFormation template with a malformed ASL definition deploys, CloudFormation accepts the string as a property, and the state machine fails at the first execution. The plugin runs asl-validator before the deploy, so a broken definition is caught on the developer's machine.
The fourth difference is the deployment features. The blue-green deployment and the retain property, keeping a state machine across a deploy rather than replacing it, are things that are easy to get wrong in raw CloudFormation and are exactly the kind of thing a plugin exists to make correct. The cost of all this is a dependency on the Serverless Framework's lifecycle, and the plugin's own list of gotchas is the bill.
Editorial conclusion
serverless-step-functions fits a team that already deploys with the Serverless Framework and wants its state machines in the same file and the same deploy as its functions, and it fits a team that wants API Gateway and schedule triggers attached to a workflow without hand-writing CloudFormation. It does not fit a project that manages infrastructure with Terraform or SAM, and it does not fit a team that needs the Express Workflow path or the blue-green deployment without reading the gotchas page first. Before adopting it, note that it requires Serverless Framework v2.32.0 or later, that the peer dependencies on serverless and osls are both marked optional, and that the newest release is v3.31.2 from 2026-08-03 with the last push on the same day.
Frequently asked questions
How do I install serverless-step-functions?
Run npm install --save-dev serverless-step-functions in your Serverless project and add serverless-step-functions to the plugins list in serverless.yml. Serverless Framework v2.32.0 or later is required, and the peer dependencies on serverless and osls are both marked optional.
How does a state machine reference a Lambda function in serverless.yml?
With the CloudFormation intrinsic function Fn::GetAtt, for example Resource: Fn::GetAtt: [hello, Arn]. The README warns that Ref returns different things depending on the resource type, so you have to choose between Ref and Fn::GetAtt, and it points out that Fn::Sub with pseudo-parameters such as ${AWS::Region} is an alternative that needs no extra plugin.
Does serverless-step-functions validate the state machine definition?
Yes. The dependency list includes asl-validator, and the README has a section on pre-deployment validation, so a malformed Amazon States Language definition is caught before the deploy rather than at runtime by Step Functions.
What events can trigger a serverless-step-functions state machine?
API Gateway HTTP endpoints with many variations including AWS_IAM and custom authorizers, CORS, request and response template customisation and request schema validators. Schedules with a name, description, IAM role and InputTransformer, with the option to use EventBridge Scheduler instead of EventBridge rules. And CloudWatch Events with a custom event bus, a dead letter queue and a retry policy.
How is serverless-step-functions tested?
Unit tests run with mocha over lib/**/*.test.js, with c8 for coverage and sinon for mocking. Integration tests deploy for real with osls deploy --stage test, against a LocalStack 4 container emulating twelve AWS services including cloudformation, stepfunctions, iam, lambda, s3, logs, sns, cloudwatch, sqs, events, scheduler and apigateway.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/serverless-operations-serverless-step-functions)Star history chart for your README
[](https://hysenlabs.com/projects/serverless-operations-serverless-step-functions)Was this useful?