tutorial

Monitoring AWS Step Functions with Vigilmon: Workflow Health Checks, Execution Heartbeats & Serverless Pipeline Uptime

Practical guide to uptime monitoring for AWS Step Functions — Lambda health endpoints, execution heartbeat pings, failed-execution alerting, and multi-region workflow uptime with Vigilmon.

AWS Step Functions orchestrates your business logic across Lambda functions, ECS tasks, and other AWS services — but failed executions don't make noise by default. A workflow that enters the FAILED state, a state machine that stops receiving triggers, or an execution stuck waiting for a callback: none of these create an external alert unless you've wired one up. CloudWatch Alarms help inside your AWS account, but Vigilmon gives you an external, independent signal that fires even when your AWS account itself has a problem.

This tutorial shows you how to wire Step Functions into Vigilmon for workflow availability monitoring, execution heartbeats, and smart alert thresholds.

What You'll Build

  • A Lambda health handler that checks Step Functions state machine status
  • A Vigilmon HTTP monitor for state machine availability
  • A heartbeat ping at the end of your Step Functions workflows
  • A failed-execution alerting strategy

Prerequisites

  • An AWS account with at least one Step Functions state machine
  • AWS CLI or CDK configured locally
  • A free account at vigilmon.online

Step 1: Add a Health Lambda for Your State Machine

Deploy a Lambda function that checks your state machine's recent execution status and returns a health response Vigilmon can probe via API Gateway.

# handlers/health.py
import os
import json
import boto3
from datetime import datetime, timezone, timedelta

sfn = boto3.client("stepfunctions")

STATE_MACHINE_ARN = os.environ["STATE_MACHINE_ARN"]

def handler(event, context):
    checks = {}
    ok = True

    # Check recent executions for failures
    try:
        # Get the last 5 executions
        resp = sfn.list_executions(
            stateMachineArn=STATE_MACHINE_ARN,
            maxResults=5,
        )
        executions = resp.get("executions", [])

        if not executions:
            checks["executions"] = "no recent executions"
        else:
            latest = executions[0]
            checks["latest_status"] = latest["status"]
            checks["latest_start"] = latest["startDate"].isoformat()

            # Flag if the latest execution failed
            if latest["status"] in ("FAILED", "TIMED_OUT", "ABORTED"):
                ok = False
                checks["failure_reason"] = f"latest execution status: {latest['status']}"

            # Flag if no successful execution in the last hour
            one_hour_ago = datetime.now(timezone.utc) - timedelta(hours=1)
            recent_successes = [
                e for e in executions
                if e["status"] == "SUCCEEDED" and e["startDate"] > one_hour_ago
            ]
            checks["recent_successes"] = len(recent_successes)

    except Exception as e:
        checks["sfn_error"] = str(e)
        ok = False

    # Check state machine reachability
    try:
        sfn.describe_state_machine(stateMachineArn=STATE_MACHINE_ARN)
        checks["state_machine_reachable"] = True
    except Exception as e:
        checks["state_machine_error"] = str(e)
        ok = False

    return {
        "statusCode": 200 if ok else 503,
        "headers": {"Content-Type": "application/json"},
        "body": json.dumps({"status": "ok" if ok else "degraded", "checks": checks}),
    }

Wire this as an API Gateway endpoint using SAM:

# template.yaml (excerpt)
HealthFunction:
  Type: AWS::Serverless::Function
  Properties:
    Handler: handlers/health.handler
    Runtime: python3.12
    Environment:
      Variables:
        STATE_MACHINE_ARN: !Ref MyStateMachine
    Policies:
      - Statement:
          - Effect: Allow
            Action:
              - states:ListExecutions
              - states:DescribeStateMachine
            Resource: !Ref MyStateMachine
    Events:
      HealthApi:
        Type: HttpApi
        Properties:
          Path: /health
          Method: GET

Deploy:

sam build && sam deploy

Step 2: Configure the Vigilmon HTTP Monitor

  1. Log in to VigilmonAdd Monitor → HTTP.
  2. URL: https://<api-id>.execute-api.<region>.amazonaws.com/health
  3. Check interval: 60 seconds.
  4. Response timeout: 10 seconds.
  5. Expected status: 200.
  6. JSON assertion: path status, expected value ok.

Tip: The health Lambda above checks the last 5 executions. For workflows that run less than once per hour (e.g. nightly), adjust the lookback window in the Lambda to avoid false positives during quiet periods.


Step 3: Heartbeat at the End of Your Workflow

For workflows that run on a schedule (triggered by EventBridge), add a heartbeat task as the final state in your state machine. This confirms the entire workflow completed successfully, not just that the Lambda was invoked.

State machine definition (ASL)

{
  "Comment": "Data processing workflow with Vigilmon heartbeat",
  "StartAt": "ProcessData",
  "States": {
    "ProcessData": {
      "Type": "Task",
      "Resource": "arn:aws:states:::lambda:invoke",
      "Parameters": {
        "FunctionName": "${ProcessDataFunction}",
        "Payload.$": "$"
      },
      "Next": "TransformData"
    },
    "TransformData": {
      "Type": "Task",
      "Resource": "arn:aws:states:::lambda:invoke",
      "Parameters": {
        "FunctionName": "${TransformFunction}",
        "Payload.$": "$"
      },
      "Next": "PingVigilmon"
    },
    "PingVigilmon": {
      "Type": "Task",
      "Resource": "arn:aws:states:::lambda:invoke",
      "Parameters": {
        "FunctionName": "${HeartbeatFunction}"
      },
      "End": true
    }
  }
}

Heartbeat Lambda

# handlers/heartbeat.py
import os
import urllib.request

def handler(event, context):
    heartbeat_url = os.environ["VIGILMON_HEARTBEAT_URL"]
    req = urllib.request.Request(heartbeat_url, method="POST")
    with urllib.request.urlopen(req, timeout=5) as resp:
        print(f"Vigilmon heartbeat sent: HTTP {resp.status}")
    return {"status": "heartbeat_sent"}

Store the heartbeat URL in SSM Parameter Store:

aws ssm put-parameter \
  --name "/myapp/prod/vigilmon-heartbeat-url" \
  --value "https://vigilmon.online/api/heartbeat/<your-id>" \
  --type SecureString

Reference it in the heartbeat Lambda's environment:

HeartbeatFunction:
  Type: AWS::Serverless::Function
  Properties:
    Handler: handlers/heartbeat.handler
    Runtime: python3.12
    Environment:
      Variables:
        VIGILMON_HEARTBEAT_URL: "{{resolve:ssm:/myapp/prod/vigilmon-heartbeat-url}}"

In Vigilmon, create a Heartbeat monitor with a grace period matching your workflow schedule:

| EventBridge schedule | Recommended grace period | |---|---| | Every 15 minutes | 20 minutes | | Hourly | 75 minutes | | Daily at 2 AM | 26 hours |


Step 4: Alerting for Failed Executions

Step Functions integrates with CloudWatch for execution metrics, but Vigilmon adds an external layer for when CloudWatch itself is degraded or your state machine stops receiving events entirely.

Combine both approaches:

  1. CloudWatch Alarm on ExecutionsFailed metric → SNS → PagerDuty (catches failures within executions)
  2. Vigilmon HTTP monitor → checks state machine health from outside AWS (catches AWS account issues)
  3. Vigilmon Heartbeat monitor → confirms scheduled workflows are completing (catches trigger failures)

| Alert type | Recommended channel | |---|---| | Execution failed | PagerDuty (immediate) | | Scheduled workflow missed | Slack data-engineering channel | | State machine unreachable | On-call email + Slack | | Status page | Public Vigilmon status page |


Step 5: Multi-Region Step Functions Monitoring

If you run state machines in multiple AWS regions for redundancy, monitor each independently.

Deploy the health Lambda and API Gateway to each region, then create one Vigilmon monitor per region:

| Region | Monitor name | URL | |---|---|---| | us-east-1 | Step Functions US | https://<api>.execute-api.us-east-1.amazonaws.com/health | | eu-west-1 | Step Functions EU | https://<api>.execute-api.eu-west-1.amazonaws.com/health |

Group regional monitors under a single Status Page so stakeholders see one aggregate status.


What Vigilmon Catches That CloudWatch Misses

| Scenario | CloudWatch | Vigilmon | |---|---|---| | Execution fails | ExecutionsFailed metric fires | HTTP monitor catches via recent-execution check | | Scheduled workflow never triggers | No metric | Heartbeat grace period fires alert | | AWS account has IAM issue | Alarms may not fire | External check — unaffected | | State machine deleted or inactive | No alarm by default | HTTP monitor catches immediately | | Regional degradation | Needs per-region alarms | One monitor per region |


Step Functions makes complex workflows look simple — until one silently fails at 2 AM. External monitoring from Vigilmon gives your team the independent signal that catches failures before they cascade into business-visible outages.

Start monitoring your Step Functions workflows in under 5 minutes — register free at vigilmon.online.

Monitor your app with Vigilmon

Free plan — 5 monitors, no credit card required. Up and running in 60 seconds.

Start free →