Skip to content

Deploy the cost service + budget alerts

The estimator service is tiny — a stateless calculator. The load-bearing deploy work is the budget alert per cloud so an over-run gets caught quickly.

Layout

examples/cost/
  service/                        # pure-arithmetic estimator
    estimator.py                  # units × rates -> report
    main.py                       # FastAPI POST /estimate
    Dockerfile
    requirements.txt
  chart/                          # tiny Helm chart
    Chart.yaml
    values.yaml
    templates/{deployment,service,serviceaccount}.yaml
  terraform/
    azure/   (module "baseline" + azurerm_consumption_budget_resource_group)
    gcp/     (google_billing_budget — no baseline needed; billing_account is user-scoped)
    aws/     (aws_budgets_budget — no baseline needed; account-scoped)

The GCP + AWS budget resources don't need module "baseline" because they attach to a billing account / AWS account, not a per-recipe resource group. Azure budgets bind to a resource group, so that path composes baseline.

Terraform

module "baseline" {
  source  = "../../../foundations/azure"
  name    = var.name
  region  = var.region
  compute = var.compute
}

resource "azurerm_consumption_budget_resource_group" "chiron" {
  name              = "${var.name}-monthly"
  resource_group_id = module.baseline.resource_group_id
  amount            = var.monthly_usd_budget
  time_grain        = "Monthly"

  time_period {
    start_date = var.budget_start_date          # e.g. "2026-09-01T00:00:00Z"
  }

  notification {
    enabled        = true
    threshold      = 80
    operator       = "GreaterThan"
    threshold_type = "Actual"
    contact_emails = var.alert_emails
  }

  notification {
    enabled        = true
    threshold      = 100
    operator       = "GreaterThan"
    threshold_type = "Forecasted"
    contact_emails = var.alert_emails
  }
}

Note: the E1 baseline output resource_group_id is not currently exported. If you're wiring this into a real recipe, add an output to examples/foundations/azure/outputs.tf — this is a small drop-in change tracked as a follow-up.

No baseline dependency — budgets attach to a billing account, not a project resource.

resource "google_billing_budget" "chiron" {
  billing_account = var.billing_account_id
  display_name    = "${var.name}-monthly"

  amount {
    specified_amount {
      currency_code = "USD"
      units         = tostring(var.monthly_usd_budget)
    }
  }

  budget_filter {
    projects = ["projects/${var.project_id}"]
  }

  threshold_rules { threshold_percent = 0.5 }
  threshold_rules { threshold_percent = 0.9 }
  threshold_rules { threshold_percent = 1.0 }
}

No baseline dependency — budgets are account-scoped.

resource "aws_budgets_budget" "chiron" {
  name         = "${var.name}-monthly"
  budget_type  = "COST"
  limit_unit   = "USD"
  limit_amount = tostring(var.monthly_usd_budget)
  time_unit    = "MONTHLY"

  notification {
    comparison_operator        = "GREATER_THAN"
    threshold                  = 80
    threshold_type             = "PERCENTAGE"
    notification_type          = "ACTUAL"
    subscriber_email_addresses = var.alert_emails
  }

  notification {
    comparison_operator        = "GREATER_THAN"
    threshold                  = 100
    threshold_type             = "PERCENTAGE"
    notification_type          = "FORECASTED"
    subscriber_email_addresses = var.alert_emails
  }
}

The service

The container is the smallest one in Chiron — no cloud SDK, no vendor rates, just Python arithmetic + FastAPI. It has one purpose: give any downstream tool (a build step, a dashboard, a shell prompt) a repeatable way to price the units the reader already tracks.

Verify (validate-only)

for p in azure gcp aws; do
  ( cd examples/cost/terraform/$p && terraform init -backend=false && terraform validate )
done

helm lint examples/cost/chart
helm template cost examples/cost/chart > /dev/null

python -m compileall examples/cost/service

Live apply

The budget-alert resources are the whole point of Phase-3 apply for this recipe — deploy them first, on real accounts, before anything else in this chapter matters.