Cost is decided in the pull request, not in the invoice review
Infracost turns a Terraform plan into a monthly delta on the pull request. What it estimates well, what it cannot estimate at all, and how to keep the comment from becoming wallpaper.
The monthly cost review is a post-mortem with a spreadsheet. Someone notices the bill went up 18 percent, spends two days working out which change did it, and finds a merge from five weeks ago that swapped a db.t3.medium for a db.r6g.2xlarge because a load test was slow one afternoon. Nobody objected at the time, because at the time nobody knew.
The decision that mattered happened in a pull request, in front of a reviewer who had every ability to ask "does this need to be that big?" and no information suggesting they should. That is the gap Infracost fills: it reads the Terraform plan, prices the resources against the provider rate cards, and posts the monthly delta as a comment on the pull request, before the merge.
It is a small tool with a large behavioural effect, and most of the work in adopting it is not technical.
Where the numbers come from
Infracost runs against terraform plan output, usually the JSON from terraform show -json. It walks the planned resources, matches them to a pricing catalogue built from the providers' public rate cards, and produces a per-resource monthly figure plus a total diff against current state. You get a table: this change adds these resources, removes those, and moves the monthly total by this much.
It can also apply negotiated rates if you feed it usage files or an enterprise discount, which matters once your effective rate diverges from list price. The output is a delta, not a forecast. That distinction is the point, and the source of every argument you will have about it.
What it cannot see
It is accurate on anything with a fixed price attached to a resource that exists: instance sizes, database instance classes, provisioned IOPS, load balancers, NAT gateways, allocated disk, fixed cluster fees. If the change is "this VM gets bigger", the number will be close to right.
It cannot see usage. Requests and invocations, so a Lambda or an API Gateway shows as zero. Data transfer, the largest gap by far, because a change that adds a cross-AZ hop can cost more than the resource itself and appears as nothing. Storage growth, since it prices the bucket at the size you declare rather than the size it becomes. And anything outside Terraform: resources created by the application, by a Kubernetes controller, or clicked in a console.
A usage file that states assumed monthly requests, GB transferred and storage volumes closes part of this. Do it for the handful of resources where usage dominates, skip it for the rest.
Be explicit with your teams about the boundary. An estimate that is confidently wrong about egress does more damage to trust than no estimate at all. The framing that holds up: this number covers the fixed cost of the shape you are provisioning, and the usage-driven part still needs a human to think, the same way reading the Terraform plan before apply tells you what will change but not what it will do under load.
The comment is a culture change, not a CI step
Wiring it up takes an hour. Making it matter takes longer.
The mechanism is simple: the pull request now carries a visible, quantified consequence, attached to the person who can still change their mind for free. A reviewer who sees "+$1,840/month" next to an instance class bump asks a question they would never have asked otherwise. Sometimes the answer is "yes, we need it", and that is a fine outcome. The value is that the conversation happened when changing the decision cost nothing.
For that, the plan has to be reproducible, which means CI plans against real state with a real backend. If your plans run from laptops, fix that first; the patterns in Terraform state and pipelines on AWS are a prerequisite, not an optional extra.
Thresholds that block without becoming a rubber stamp
The obvious next step is failing the build above a threshold, and this is where teams overreach. A hard block on any increase becomes a rubber stamp within two sprints.
What works is tiered. Below a small absolute threshold, comment only, no gate. Above a meaningful monthly increase, or above a percentage of that stack's current total, require explicit approval from a named reviewer. For a very large increase, require the description to state the expected benefit and a review date.
Pick the absolute threshold relative to the stack's current spend, not a company-wide number. A 300 dollar increase is noise on a platform account and a serious question on an internal tool. Infracost supports policy checks driven by the estimate, and you can equally implement the rule yourself against the JSON. The logic matters more than the tool.
Keeping the comment readable
Every automated pull request comment eventually gets ignored. The failure modes are predictable:
- Post one comment and update it in place. A new comment on every push produces a thread nobody scrolls through.
- Show the diff, not the inventory. Nobody reads sixty rows of unchanged resources.
- Suppress zero-delta comments. Most pull requests do not change cost. If the bot speaks on all of them, it is noise on all of them.
- Show the percentage next to the absolute. "+$400" means nothing without knowing whether the stack costs 800 or 80,000.
- Own the false positives. When someone says the number is wrong, check it and either fix the usage file or document the gap. Two unexplained wrong numbers and the team stops reading.
The things people forget
- Modules multiply. A change to a shared module lands in every consumer. The estimate on the module's own pull request can be tiny while the fleet-wide effect is not.
- Deletions are also a delta. Negative numbers are the ones worth celebrating, and the proof a cleanup actually worked.
- Discounts distort the ranking. With reservations applied, list pricing can rank two options in the wrong order. Feed in effective rates when the decision is close.
- Non-production counts. A staging environment sized like production is a real recurring line, in exactly the same comment.
- Monthly estimate, multi-year commitment. A database instance class is a decision with an exit cost, not a monthly rental.
Cost stops being a report someone produces and becomes a property of the change, visible at the moment the change is made. We wire this into the delivery pipeline during the cost phase of a cloud engagement, usually in the same week we fix the state backend.
What to do this week
Run Infracost locally against the plan for your largest stack, with no CI involved. Look at the total, then at the three most expensive resources in it. If any of them surprises you, you have just proved the case for the pull request comment without having to argue for it.