Skip to main content
idle · aws

Step Functions state machines that never execute cost $0

resource types
1
rule IDs covered
1
severity
low

What does ZopNight detect here?

Step Functions bills $0.025 per 1,000 state transitions and nothing while idle, so a state machine with zero ExecutionsStarted over 30 days already costs $0. ZopNight retired this rule in place, with Evaluate returning nil on every input, rather than ship a $0 delete card under a cost category.

Signal and threshold

How ZopNight evaluates Step Functions state machines that never execute cost $0.
Field Value
Rule IDsRC-183
Categoryidle
Severitylow
Metricnone — pure configuration read
Sourcestep_functions_idle.go

Per-transition pricing leaves nothing to reclaim

A Step Functions state machine is billed on movement: roughly $0.025 per 1,000 state transitions, metered as executions run. A definition that sits there with zero ExecutionsStarted triggers zero transitions and lands on the bill at exactly $0. There is no rack rate to stop paying, no provisioned capacity to shrink, no off-hours window to schedule, and no commitment to re-tier. Every axis a cost recommendation could quantify comes back empty. Deleting an already-free state machine saves nothing, so under ZopNight’s concrete-or-abstain contract for cost findings, the only honest output is no output.

From a $0 advisory card to an honest abstain

The previous implementation knew all of this and emitted anyway: a SavingsUSD 0 finding filed under advisory, a category the low-savings filter does not treat as cost-driven. That routing existed purely so the $0 card would survive the filter that exists to drop $0 cards. The retirement removed the mask: the category is now idle, the honest cost-driven label, and Evaluate returns nil unconditionally. The rule is retired in place, kept in the registry for identity stability but incapable of producing a recommendation.

Where ExecutionsStarted still flows

The rule still declares ExecutionsStarted (one of the execution counters Step Functions publishes, dimensioned by the state machine’s ARN over a 30-day window) as a required metric, so the aggregator keeps harvesting it. Two plumbing fixes ride along: the resource type key is step-function, matching what the discoverer actually emits (the old stepfunctions literal matched nothing, leaving the rule’s type-join dead), and the metric lands on live resources as a result.

Counting executions yourself

Terminal window
aws cloudwatch get-metric-statistics \
--namespace AWS/States --metric-name ExecutionsStarted \
--dimensions Name=StateMachineArn,Value=arn:aws:states:us-east-1:111122223333:stateMachine:my-machine \
--start-time "$(date -u -v-30d +%Y-%m-%dT%H:%M:%SZ)" \
--end-time "$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
--period 86400 --statistics Sum

All-zero sums for a month means nothing started an execution.

Retiring an abandoned state machine

An unused state machine is free but not harmless: its IAM role keeps whatever permissions the workflow once needed, and EventBridge rules or API Gateway integrations may still point at it. If the workflow is genuinely dead, remove the triggers first, then the state machine, then the role, in that order, so nothing fires mid-cleanup.

See it fire on your bill.

Connect an account read-only. The first findings land in minutes.

417 rule families across 353 resource types on 22 platforms. Every threshold, metric, and IAM action is documented on these pages before you grant anything.

417 rule families documented
353 resource types covered
read-only default access level
Multi-cloud automation· Production-ready in 30 min· SOC 2 · ISO 27001· 20–60% off the bill, first month· 4 platforms · 1 console· Multi-cloud automation· Production-ready in 30 min· SOC 2 · ISO 27001· 20–60% off the bill, first month· 4 platforms · 1 console·