Schedule icon
Webhook icon
WorkingDirectory icon
Commands icon
Docker icon
Script icon
If icon
Loop icon
Create icon
SlackIncomingWebhook icon
Log icon

Find Wasted Coverage in Underutilized AWS Savings Plans

Scan AWS Savings Plans for low utilization, price the wasted commitment, and escalate with a GitHub issue, Slack alert, and a waste report.

Categories
BusinessCloud

How it works

  1. weekly_waste_scan (io.kestra.plugin.core.trigger.Schedule, cron 0 6 * * 1) is the weekly safety net and ships disabled: true, so nothing runs until an operator switches it on. on_demand_audit (io.kestra.plugin.core.trigger.Webhook, keyed) accepts an on-demand audit, for example when finance wants a number before a renewal decision.
  2. coverage_workspace (io.kestra.plugin.core.flow.WorkingDirectory) opens one shared filesystem so the coverage query, the waste analysis, and the report all see the same files, and only runnable script tasks live inside it.
  3. fetch_coverage (io.kestra.plugin.scripts.shell.Commands on the io.kestra.plugin.scripts.runner.docker.Docker runner, image amazon/aws-cli:latest) writes a two-plan fixture with a heredoc, then runs aws ce get-savings-plans-coverage --output json > coverage.json || true. When the API cannot be reached the redirect comes up empty and the fixture is copied in, so the analysis always has data. entryPoint: [] clears the image entrypoint so the shell script runs instead of the bundled aws command, and AWS_DEFAULT_REGION stays on us-east-1 because the Cost Explorer endpoint is only served from us-east-1 and us-west-2.
  4. analyze_coverage (io.kestra.plugin.scripts.python.Script on python:3.12-slim) reads the coverage payload, skips plans with no remaining commitment, computes waste_usd = commitment * (1 - utilization / 100) for every plan under utilization_threshold, writes report.txt, and emits the verdict through the outputs protocol.
  5. check_waste (io.kestra.plugin.core.flow.If) runs right after the workspace closes and compares the waste total with min_waste_usd. Above it, append_advice (io.kestra.plugin.core.flow.Loop, concurrencyLimit: 1) walks the underutilized plans, append_plan_advice records one recommended move per plan through the outputs protocol, open_exchange_issue (io.kestra.plugin.github.issues.Create) files a labeled issue with the plan list, and notify_waste posts the total to Slack. At or below it, log_all_clear records the all-clear.
  6. The errors block alerts Slack whenever the query or the analysis breaks, because a dead audit looks exactly like a cheap bill.

What you get

  • Gateable results: verdict, waste_usd, low_util_plans, and report_uri are flow outputs any downstream flow or dashboard can branch on.
  • A per-plan waste figure that finance can argue with: commitment times one minus utilization, summed only across plans under the threshold.
  • Escalation where the work happens, a labeled GitHub issue carrying the plan list plus a Slack ping with the monthly total.
  • A report that lists every underutilized plan with its wasted dollars plus a recommended move, ready to attach to the issue or a renewal review.

Who it's for

  • FinOps engineers who own the AWS bill and must justify every dollar of committed spend.
  • Finance partners deciding whether to exchange, reassign, or let a Savings Plan lapse at renewal.
  • Platform leads who suspect coverage was bought for a migration that never landed.

Why orchestrate this with Kestra

Cost Explorer will happily hand you a coverage number once a quarter and nobody looks at it again. Kestra wraps the query in a cadence that finance can rely on: a disabled weekly schedule as the safety net, a keyed webhook for on-demand audits, a hermetic container so the CLI version lives in the flow file, a threshold branch that separates escalation from the all-clear, a loop that records a recommended move for each plan at a time, and routing into GitHub and Slack from a single definition. The same file doubles as the audit evidence for anyone asking what was reviewed and when.

Prerequisites

  • A worker with Docker access, the query runs in amazon/aws-cli:latest and both Python steps in python:3.12-slim.
  • An IAM principal with ce:GetSavingsPlansCoverage and cost explorer read access. Without reachable credentials the flow still completes on the bundled fixture, which is what makes it safe to run in CI or on a laptop.
  • A GitHub token that can open issues in the repository named on open_exchange_issue (classic PAT scope repo, or fine-grained Issues: write).
  • A Slack incoming webhook for the waste alert and for failure alerts.

Secrets

  • SAVINGSPLAN_WEBHOOK_KEY: key segment guarding the on-demand webhook URL, anyone holding it can trigger an audit.
  • AWS_ACCESS_KEY_ID: AWS access key with Cost Explorer read permission, optional when the bundled fixture is enough.
  • AWS_SECRET_ACCESS_KEY: secret key matching the access key.
  • GITHUB_TOKEN: token used to open the exchange issue.
  • SLACK_WEBHOOK_URL: Slack incoming webhook URL for waste and failure alerts.

Quick start

  1. Add the five secrets to your Kestra namespace.
  2. Run the flow once with the default inputs and confirm verdict reads WASTE_FOUND, waste_usd reads 1920.0, and report_uri points at the report.
  3. Point open_exchange_issue.repository at a repository where issues are welcome, then re-run with min_waste_usd at 50 to watch the escalation branch open the issue and fire the Slack alert.
  4. Trigger on_demand_audit at /api/v1/{tenant}/executions/webhook/{namespace}/aws-savings-plan-waste-finder/{SAVINGSPLAN_WEBHOOK_KEY}, or set disabled: false on weekly_waste_scan once the credentials are in place.

How to extend

  • Split the waste by account or region by adding --group-by to the coverage query once a multi-account estate needs per-owner numbers.
  • Add a second If on a single-plan floor so one badly utilized plan escalates even when the total stays under the dollar floor.
  • Ship report.txt to S3 or attach it to the issue as renewal evidence with io.kestra.plugin.aws.s3.Upload.
  • Turn a breach into a red run by adding io.kestra.plugin.core.execution.Fail under check_waste.

Links

See How

New to Kestra?

Use blueprints to kickstart your first workflows.