Three actual thresholds tell you what already happened. The forecast alert fires when the month is projected to exceed budget. It is the only one of the four that arrives while there is still time to act — and the one that would have caught the $5K estimate becoming $30K.
Cost Visibility Dashboard
Cloud cost governance built as infrastructure-as-code: alerts that warn you before the money is gone, not after.
The problem
A business moves to the cloud on a $5,000/month estimate. The first bill is $8,000. Nobody notices, because nobody is watching a number that only appears once a month. Three months later it is $30,000 and the money is already spent.
Nothing failed. Nobody was breached. The bill grew where nobody was looking.
Most people respond by setting a budget alert at 100% and stopping there. That alert tells you the money is already gone. This project is about the one that arrives early enough to matter.
What gets built
Fourteen resources, all defined in Terraform so the environment can be rebuilt from source and destroyed the moment the evidence is captured.
| Component | Purpose |
|---|---|
consumption_budget_subscription | Four thresholds: 50/80/100% actual, plus 100% forecasted |
cost_anomaly_alert | Flags daily spend deviating from the learned pattern |
monitor_action_group | Alert delivery, swappable for SMS or a ticket queue later |
subscription_cost_management_export | Daily CSV of actual usage to blob storage |
storage_account | TLS 1.2 floor, no public access, versioning, 7-day soft delete |
user_assigned_identity + role | Scoped to Cost Management Reader and nothing else |
monitor_diagnostic_setting | Logs who read the billing data, not just what it said |
application_insights_workbook | The dashboard |
The choices that matter
Each of these is a question an interviewer would ask, so the reasoning is written down as it was made rather than reconstructed afterward.
The identity that reads billing data holds exactly one role. If it were compromised, the blast radius is disclosure of a bill — not the ability to create, modify, or delete anything. Reader would also work, but grants visibility into every resource's configuration, which is more than the job needs.
A monthly budget is blunt. If something starts burning money on the 3rd, the budget stays silent until the running total crosses a threshold — possibly two weeks and thousands of dollars later. Anomaly detection watches the daily shape instead of the monthly total.
The export is a daily regenerating copy of data Azure also holds. Losing a region's copy means re-running an export, not losing anything irreplaceable. Paying for geo-redundancy here would be spending money to protect against a scenario with no real consequence — which is the exact failure this project exists to catch.
How it gets built and torn down
Azurite, Azure Functions Core Tools, and SQL Server in Docker. Iterate and break things at zero cost.
terraform apply on free tier, exercise it, then
capture screenshots, logs, the cost report, and Defender for Cloud findings.
terraform destroy, confirm nothing is still billing,
then write up what happened including what failed first time.
Baseline, before anything existed
Captured first because they cannot be retaken. Subscription ID, account email, and tenant domain are blurred — there is no upside to publishing them.
NetworkWatcherRG, “Showing 1 - 1 of 1”.
Proof nothing was pre-built.
0.00, and the $200 free trial credit intact.
BUDGET: NONE and FORECAST UNAVAILABLE. Once the budget is
deployed those same two tiles read $25 and a live forecast, so the before and after sit
in a single frame.What I have run into so far
The tenant blocked my first login
Authenticating the Azure CLI with device code failed:
AADSTS530035: Access has been blocked by security defaults.
Entra ID security defaults block the device code flow. That is deliberate — device code is a known phishing vector, because an attacker can generate a code and talk a target into entering it on their own machine. The browser-based flow ties authentication to the session that started it, so it is allowed.
The tempting fix was to disable security defaults. That would have traded a real protection for convenience on the subscription holding my billing data. Used the browser flow instead.
Two deprecations caught before they became a problem
resource_manager_id on storage containers and the metric
block on diagnostic settings are both deprecated in the azurerm provider, and
metric disappears entirely in v5. Fixed at write time rather than inheriting
a break at the next upgrade.
What is left
- Finish the Terraform, deploy, and capture the working dashboard
- Test that the least-privilege identity is actually denied when it attempts a write — configuring a control is not evidence that it works
- Verify the alert path end to end, from Terraform through to an email in an inbox
- Confirm the daily cost export lands and contains real usage rows
- Tear it all down and publish the total cost of the exercise