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.
Verified 2026-08-08
azurerm_consumption_budget_resource_group— registry.terraform.io/…/azurerm/latest/docs/resources/consumption_budget_resource_groupgoogle_billing_budget— registry.terraform.io/…/google/latest/docs/resources/billing_budgetaws_budgets_budget— registry.terraform.io/…/aws/latest/docs/resources/budgets_budget
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.