CLI

l4 estimate

Estimate the monthly cost of Terraform and CloudFormation before resources are created.

l4 estimate prices the Terraform and CloudFormation in your workspace and estimates the monthly cost of the resources they define. It needs no terraform plan and no deployed stack. Point it at directories, individual files, or a mix. It reads a directory recursively.

l4 estimate
l4 estimate ./infra/
l4 estimate main.tf variables.tf
l4 estimate template.yaml --parameter Env=prod

Each Terraform root module is priced once, with the modules it calls. Each CloudFormation template is priced with the nested stack templates, AWS::Include snippets and samconfig files it uses. CloudFormation covers what leaves your machine.

Flags

FlagDescription
--region <r>AWS region for pricing. Order: --region, then region_override in .levelfour/config.yml, then the template's samconfig (for Terraform, its provider block), then us-east-1
--gcp-region <r>GCP region (default: auto-detect or us-central1)
--azure-region <r>Azure region (default: auto-detect or eastus)
--var <key=value>Terraform variable (repeatable)
--var-file <path>Terraform variable file (repeatable)
--download-modulesDownload remote modules for accurate estimates (default true)
--parameter <Key=Value>CloudFormation parameter value, given to every root template that declares Key (repeatable). A nested stack gets the parameters its parent passes
--parameters-file <path>CloudFormation parameters file for the root templates, in the AWS CLI JSON format or the CodePipeline format
--config-env <env>The samconfig environment whose parameters and region apply (default default)
--out-file <path>Save the Terraform resource snapshot for later use with l4 diff
--fail-above <amount>Exit code 2 if monthly cost exceeds this threshold
--formattable, json, or github-comment (default table)
Pricing is regional. When no flag, config, provider block or samconfig names a region, every AWS resource is priced in us-east-1, which returns a plausible total that is wrong for any other region.

Global flags apply, including --quiet and --token. See CLI overview.

CloudFormation

l4 estimate reads YAML, JSON and SAM templates. It tells a template apart from other YAML and JSON by its content, so a Kubernetes manifest or a package.json in the same directory is left alone.

The CLI parses Terraform on your machine and sends only the resources to the API. CloudFormation templates are uploaded to the LevelFour API, which evaluates their parameters, conditions, intrinsic functions and nested stacks, then prices them. The upload holds:

  • the templates the CLI found
  • the nested stack templates and AWS::Include snippets they reference
  • the samconfig beside each template
  • the --parameters-file

Nothing outside the git repository goes up. Outside a repository, nothing outside the directory you point at goes up. A file a template references is uploaded only when git tracks it and it is not hidden, vendored or a package manifest, so an ignored credentials.json or a .env.json stays on your machine. The CLI never follows a symlink to a file it uploads. Before the request goes out, it lists every file it uploads on stderr.

A parameter of a root template takes the first value it finds: --parameter, then --parameters-file, then the samconfig environment --config-env names, then the template's Default. A nested stack takes the parameters its parent passes down, and its own Default for the rest. When the estimate rests on an assumption, such as a parameter priced at its default or a condition LevelFour could not settle, the output lists it under Assumptions.

AWS CDK

The CLI does not run cdk synth. Run it yourself, then name each synthesized template:

cdk synth
l4 estimate cdk.out/MyStack.template.json

A directory walk skips cdk.out, so name the templates as files.

Pull request bot lists the rest of what LevelFour cannot price in a template.

CI guardrail pattern

Fail the build when the estimated monthly cost is above $500:

l4 estimate --fail-above 500 -q ./infra/
A wired gate exits 2 once the estimate goes over the threshold. That is the guardrail firing rather than the command breaking, and the two carry different codes, so a job can branch on the difference. Exit codes has the full set.

--fail-above gates on the total here, so a workspace that sits above the line fails every build, whether or not the change moved it. l4 diff gates on the delta instead.

Saving a snapshot for later comparison

Outside a git repo, save the current Terraform estimate as a baseline you can diff against later:

l4 estimate --out-file baseline.json ./infra/
# ... make changes ...
l4 diff baseline.json ./infra/

In a git repo you need no snapshot at all. See l4 diff for the default baseline.

Posting to a pull request

The github-comment format is what the LevelFour GitHub Action uses. Post it by hand with:

l4 estimate --format github-comment ./infra/ \
  | gh pr comment "$PR_NUMBER" --body-file -
  • l4 diff compares an estimate against a branch or a saved baseline
  • CI/CD integration has the GitHub Actions and GitLab CI jobs that run this command
  • Output formats covers the global --json, --jq, --template and --quiet flags

On this page

Ask the FinOps Agent about your cloud spend