Build Infracost Cloud
KINDAreplaces $1000/mosaves $12,000/yrback to the verdict
A cost estimation tool for infrastructure changes whose value depends entirely on being honest about coverage: the fixed costs are computable from the definitions, the usage-driven ones are not, and conflating them produces confident wrong numbers people act on.
Before step 1
Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.
- installfree
Why Runs the parser, the pricing layer and the reporting.
Get it nodejs.org, or nvm install 22. open ↗
Verify
node --version prints v22 or higher - installfree
Why Parsing the source text is far less reliable than parsing a resolved plan, and the plan format is what resolves variables and counts.
Get it The tool itself, plus a repository whose plan you can generate.
Verify
A plan output is generated and parsed into concrete resources - accountfree to read
Why Prices vary by region and change, and hard-coding a sample produces estimates that drift without breaking.
Get it The provider's own pricing interface, and record the date you fetched.
Verify
Prices for one resource kind differ correctly between two regions - have readyfree
Why Modules, variables and counts are where resolution actually fails, and a single-file example exercises none of it.
Get it A repository with modules, variables and at least one resource whose cost is dominated by usage.
Verify
The repository parses with at least one unresolved and one usage-driven resource - have readyfree if you own the infrastructure
Why The honesty of the coverage reporting is only assessable by comparing an estimate against what was actually charged.
Get it A recent bill for the environment your repository describes, broken down by service.
Verify
The estimate and the bill are compared and the gap is explained by the stated coverage
Data model
Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.
- runs(id, repository, commit_sha, base_sha, ran_at, definitions_hash) - resources(run_id, address, kind, region, attributes_json, support_state) · support_state is priced, usage_driven or unsupported - prices(provider, region, sku, unit, amount_minor, currency, effective_from, fetched_at) - estimates(run_id, resource_address, fixed_minor, usage_minor, usage_assumptions_json) - usage_profiles(id, name, assumptions_json, source) · source is declared or default, never inferred - diffs(run_id, base_run_id, resource_address, change_kind, delta_minor, coverage_changed) - An estimate stores fixed and usage-driven components separately, so a total is never reported without saying how much of the cost is not in it.
Environment variables
These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.
| Variable | Needed | Example | Where the value comes from |
|---|---|---|---|
PRICE_CACHE_PATH | required | ./cache/prices.json | Dated per fetch. A stale catalogue refuses to run silently. |
PRICE_MAX_AGE_DAYS | required | 14 | Beyond this an estimate requires an explicit acknowledgement. |
USAGE_PROFILE_PATH | required | ./infracost-usage.yml | Declared usage. Absent means usage-driven resources report no figure. |
INFER_USAGE | required | never | Present as a constant. Inferred egress is a number with no basis. |
REPORT_COVERAGE | required | always | Present as a constant. A total without coverage is not interpretable. |
DEFAULT_REGION | required | eu-west-1 | Used only where a resource does not state one, and reported when it is. |
The build, in order
Resolving what the definitions actually create
A plan resolves to concrete resources with their shapes.
A defaulted size is an estimate for infrastructure nobody is deploying.
Files
src/parse.tsWhere the resource count actually comes from.
Files
src/expand.tsAlongside the total, not in a log.
Files
src/report.ts
done when · tick each as it passeswatch out- Assuming a default size for a resource whose shape depends on an unresolved variable produces an estimate for infrastructure nobody is deploying, and it looks identical to a correct one. Report unresolved shapes as unresolved and exclude them from the total rather than guessing.
Prices with a date on them
Every price used is dated and its staleness is visible.
A stale catalogue is a wrong estimate that looks entirely normal.
Files
src/prices.tsThe same resource differs substantially between regions.
Files
src/prices.tsTreating an absent price as zero understates the total silently.
Files
src/estimate.ts
done when · tick each as it passeswatch out- A price catalogue cached indefinitely produces estimates that drift from reality without anything appearing to break, and the first sign is a bill that does not match a report from six months ago. Date the catalogue, show its age, and refuse to run silently on a stale one.
Saying what is not in the number
Every estimate states its coverage explicitly.
Priced, usage-driven, unsupported. Three states, all reported.
Files
src/coverage.tsNot in a footnote. It is what makes the total interpretable.
Files
src/report.tsBy cost is impossible, since the excluded cost is what you do not know.
Files
src/coverage.ts
done when · tick each as it passeswatch out- Skipping resources the tool does not support, and reporting the total of the rest, produces a number that is confidently too low, and the resources most likely to be unsupported are often the expensive new ones. Count them, name them, and put the count where the total is.
Usage assumptions, declared rather than guessed
Usage-driven costs appear only where someone stated the usage.
In the repository, reviewable alongside the change.
Files
src/usage.tsSo a wrong number is traceable to a wrong assumption.
Files
src/report.tsEgress from instance size is a number with no basis.
Files
tests/no-inference.test.ts
done when · tick each as it passeswatch out- Inferring egress volume from an instance size produces a number with no basis that will nonetheless be quoted in a decision, and data transfer is precisely where estimates go most badly wrong. Require a declared profile, and where none exists report the resource as usage-driven with no figure rather than estimating one.
The diff, where the decision happensproduct builder
A change shows its cost delta before it is merged.
The total delta hides the resource that changed.
Files
src/diff.tsA refactor to an unsupported resource looks like a saving otherwise.
Files
src/diff.tstests/coverage-change.test.tsA module rename should not read as deleting everything.
Files
src/matching.ts
done when · tick each as it passeswatch out- Reporting a cost delta without reporting a coverage change lets a refactor that swaps a supported resource for an unsupported one appear as a cost reduction, when in fact the cost simply stopped being counted. Report coverage changes as prominently as cost changes.
That is the whole plan for Infracost Cloud. What it deliberately does not cover is below · check the gaps before you call it a replacement.
- Billing reconciliation against actual usage.
- Provider and resource breadth.
- Commitment and discount modelling.
- Any claim that an estimate predicts a bill.
- AWS, Azure, and Google Cloud coverage across the full Infracost resource catalog
- Terragrunt, CloudFormation, AWS CDK, modules, and complex Terraform expression coverage
- custom price books, negotiated discounts, and organization-specific pricing
- Issue Explorer, campaigns, AutoFix, dashboards, audit trails, and team workflows
- enterprise identity, security, support, and broad CI/CD integrations
- Comparing estimates against the eventual bill, which is the only way to calibrate coverage honestly
- Policy checks such as refusing a resource kind, which the resource graph already supports
- Usage profiles derived from the previous month's actual usage, declared rather than inferred
Need the files? The project pack on the verdict page hands your agent the whole brief · more dev tools.