AWS Step Functions provides serverless workflow orchestration, enabling you to coordinate Lambda functions, ECS tasks, API calls, and human approvals in a visual, maintainable way. Unlike orchestrating via code (calling Lambda from Lambda), Step Functions handles retries, error handling, and state management durably. This guide covers Standard vs Express workflows, advanced patterns like Map and Parallel states, and integration with 200+ AWS services via SDK integrations.
Standard vs Express Workflows
| Feature | Standard | Express |
|---|---|---|
| Duration | Up to 1 year | 5 minutes max |
| Pricing | Per state transition | Per execution + duration |
| Execution History | Stored in Step Functions | CloudWatch Logs only |
| Exactly-Once | Yes | At-least-once |
| Use Case | Long-running, auditable | High-volume, short |
Workflow Definition (ASL)
Step Functions uses Amazon States Language (ASL), a JSON-based DSL:
{
"Comment": "Order Processing Workflow",
"StartAt": "ValidateOrder",
"States": {
"ValidateOrder": {
"Type": "Task",
"Resource": "arn:aws:lambda:us-east-1:123456789012:function:validate-order",
"Next": "CheckInventory",
"Retry": [
{
"ErrorEquals": ["Lambda.ServiceException"],
"IntervalSeconds": 2,
"MaxAttempts": 3,
"BackoffRate": 2.0
}
],
"Catch": [
{
"ErrorEquals": ["ValidationError"],
"Next": "RejectOrder"
}
]
},
"CheckInventory": {
"Type": "Choice",
"Choices": [
{
"Variable": "$.inventoryAvailable",
"BooleanEquals": true,
"Next": "ProcessPayment"
}
],
"Default": "BackorderItem"
},
"ProcessPayment": {
"Type": "Task",
"Resource": "arn:aws:states:::lambda:invoke.waitForTaskToken",
"Parameters": {
"FunctionName": "process-payment",
"Payload": {
"orderId.$": "$.orderId",
"taskToken.$": "$$.Task.Token"
}
},
"Next": "ShipOrder"
},
"ShipOrder": {
"Type": "Task",
"Resource": "arn:aws:lambda:us-east-1:123456789012:function:ship-order",
"End": true
},
"RejectOrder": {
"Type": "Fail",
"Error": "OrderRejected",
"Cause": "Validation failed"
},
"BackorderItem": {
"Type": "Wait",
"Seconds": 86400,
"Next": "CheckInventory"
}
}
}
Advanced Patterns
Map State: Parallel Processing
Process an array of items concurrently (up to 40 concurrent iterations):
{
"ProcessAllItems": {
"Type": "Map",
"ItemsPath": "$.items",
"MaxConcurrency": 10,
"Iterator": {
"StartAt": "ProcessItem",
"States": {
"ProcessItem": {
"Type": "Task",
"Resource": "arn:aws:lambda:...:process-item",
"End": true
}
}
},
"Next": "AggregateResults"
}
}
SDK Integrations: No Lambda Needed
Call 200+ AWS services directly without Lambda:
{
"SendNotification": {
"Type": "Task",
"Resource": "arn:aws:states:::sns:publish",
"Parameters": {
"TopicArn": "arn:aws:sns:us-east-1:123456789012:order-notifications",
"Message.$": "$.notificationMessage"
},
"Next": "UpdateDynamoDB"
},
"UpdateDynamoDB": {
"Type": "Task",
"Resource": "arn:aws:states:::dynamodb:updateItem",
"Parameters": {
"TableName": "Orders",
"Key": {
"orderId": {"S.$": "$.orderId"}
},
"UpdateExpression": "SET #s = :status",
"ExpressionAttributeNames": {"#s": "status"},
"ExpressionAttributeValues": {":status": {"S": "COMPLETED"}}
},
"End": true
}
}
Human Approval Pattern
Use Task Tokens for human-in-the-loop workflows:
flowchart LR
Start["Start"] --> Request["Send Approval Request"]
Request --> Wait["Wait for Human (Task Token)"]
Wait --> Decision{Approved?}
Decision -->|Yes| Proceed["Continue Workflow"]
Decision -->|No| Reject["Reject & Notify"]
style Wait fill:#FFF3E0,stroke:#E65100
# Lambda sends email with approval link containing task token
def send_approval_request(event, context):
task_token = event['taskToken']
order_id = event['orderId']
# Store token in DynamoDB for later retrieval
table.put_item(Item={
'orderId': order_id,
'taskToken': task_token,
'status': 'PENDING'
})
# Send email with approve/reject links
approval_url = f"https://api.example.com/approve?orderId={order_id}"
# ... send email
# API endpoint when human approves
def approve_order(event, context):
order_id = event['queryStringParameters']['orderId']
# Retrieve task token
item = table.get_item(Key={'orderId': order_id})['Item']
# Send success to Step Functions
sfn.send_task_success(
taskToken=item['taskToken'],
output=json.dumps({'approved': True})
)
Key Takeaways
- Use Standard workflows for long-running, auditable processes
- Use Express workflows for high-volume, short-duration workloads
- SDK integrations eliminate Lambda for simple AWS service calls
- Map state enables parallel processing of arrays
- Task Tokens enable human-in-the-loop workflows
- Built-in retry and error handling reduces custom code
Discover more from C4: Container, Code, Cloud & Context
Subscribe to get the latest posts sent to your email.