DEV Community

Silas Stone
Silas Stone

Posted on

"Did I just spend $2,000?" Adding "Cost Guardrails" to Terraform

The "SKU Blindness" Problem

I was debugging a slow app last week and decided to bump the VM size to improve performance.

I scanned the list and Standard_D64s_v3 looked perfect. I updated my Terraform code.

But then came the context switching.

I had to Alt-Tab out of my editor, open the Azure Pricing Calculator, select my region, search for the specific SKU, and do the math... just to figure out if this change was going to cost me an extra $50 or an extra $2,000.

It felt clunky. Hunting for pricing tables every time I changed a line of code wasn't a good idea.

The "Shift Left" Gap

I looked at existing solutions (like Infracost). They are powerful, but they often feel like a separate layer on top of the workflow—requiring separate CLI binaries, API keys, or parsing JSON outputs in a CI pipeline.

If the cost estimation happens inside the Terraform lifecycle, and treats cost just like any other resource attribute, it should catch expensive mistakes naturally during the plan phase, without needing external scripts.

To solve this, I built an open-source provider: terraform-provider-plancost.

Two Layers of Defense: Awareness & Blocking

I designed this provider to help in two distinct ways:

1. The "Soft" Guardrail: Visual Awareness

Because the cost is modeled as a resource in the Terraform state, we get Terraform's native visual diff for free.

When you change that SKU from B2s to D64s, Terraform presents the financial impact side-by-side with your infrastructure changes:

~ resource "plancost_estimate" "this" {
  ~ monthly_cost: "$41.00" -> "$3,200.00" (+7700%)
}
Enter fullscreen mode Exit fullscreen mode

This immediate feedback acts as a sanity check: "Whoa, did I really mean to increase costs by 7000%?"

2. The "Hard" Guardrail: Budget Blocking

For the times when you (or a teammate) miss the visual warning, you can set a hard budget_limit.

If a planned deployment exceeds your defined threshold, the provider will block the deployment during the plan phase.

How to use it (in 30 seconds)

Here is how you can protect your wallet in 3 steps.

Step 1: Add the provider

(Currently supports Azure resources)

terraform {
  required_providers {
    plancost = {
      source = "plancost/plancost"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Define your cost budget

Let's say this is a dev environment, and I don't want anyone creating resources that cost more than $500/month. We define a plancost_estimate resource to enforce this.

resource "plancost_estimate" "this" {
  working_directory = abspath(path.module)

  # The Safety Net: Block deployment if cost > $500
  guardrail {
    condition = "monthly_cost_budget"
    threshold = 500
    action    = "block"
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 3: Run Plan

Now, if I try to provision that expensive Standard_D64s_v3 VM, Terraform will stop me:

$ terraform plan

╷
│ Error: Guardrail Violation
│ 
│   with plancost_estimate.this,
│   on main.tf line 0, in resource "plancost_estimate" "this":
│ 
│ Monthly cost $1250.00 exceeds budget $500.00.
╵
Enter fullscreen mode Exit fullscreen mode

The plan fails. The bad infrastructure is never created. Crisis averted.

Why I built this? (The "Cost as a Resource" Pattern)

I built this to experiment with the idea of treating FinOps data as a logical resource within the Terraform state (terraform.tfstate), rather than just metadata printed to the console.

It’s fully open-source (MPL 2.0) and available on the Registry.

Top comments (0)