CI/CD Integration
Overview
The LevelFour CLI (l4) integrates into CI/CD pipelines to catch cost regressions before they reach production. Two key commands:
l4 estimate- Estimate monthly cost of Terraform infrastructurel4 diff- Show cost impact of infrastructure changes vs. a baseline
Both commands support a --fail-above threshold that exits with code 2 when exceeded, letting you gate deployments on cost.
Installing the CLI
Download the latest release binary in your CI job:
curl -sSL https://github.com/LevelFourAI/levelfour-cli/releases/latest/download/l4_linux_amd64.tar.gz | tar xz
sudo mv l4 /usr/local/bin/Authentication
Pass your API token via the LEVELFOUR_TOKEN environment variable or the --token flag:
export LEVELFOUR_TOKEN="l4_live_..."
l4 whoamiFor CI, store the token as a secret and pass it via environment variable. The CLI resolves tokens in this order:
--tokenflag (highest priority)LEVELFOUR_TOKENenvironment variable- System keychain (interactive use only)
Cost Estimation
Estimate the monthly cost of your Terraform infrastructure:
l4 estimate ./infra/Example output:
Name Quantity Unit Monthly Cost
aws_instance.bastion
├─ Instance usage (Linux, t4g.micro) 730 hours $6.13
└─ root_block_device
└─ Storage (General Purpose, gp3) 30 GB $2.40
aws_ecs_service.api
└─ Fargate vCPU 0.25 vCPU $7.39
aws_db_instance.this
└─ Instance usage (db.t4g.micro) 730 hours $12.41
Total $52.29CI Gate with Threshold
Exit with code 2 if the estimated monthly cost exceeds a threshold:
l4 estimate ./infra/ --fail-above 500 -q
echo $?0With a lower threshold:
l4 estimate ./infra/ --fail-above 1 -q
echo $?Cost delta $52.29 exceeds threshold $1.00
2The -q (quiet) flag suppresses the table output and communicates only via exit code.
Save a Snapshot
Save the resource snapshot for later comparison with l4 diff:
l4 estimate ./infra/ --out-file baseline.jsonOutput Formats
| Format | Flag | Use Case |
|---|---|---|
| Table | --format table | Terminal output (default) |
| JSON | --format json | Programmatic consumption |
| GitHub Comment | --format github-comment | Markdown for PR comments |
The github-comment format outputs a Markdown table suitable for PR comments:
## Cost Estimate
| Module | Resource | Type | Cost/mo | Delta |
|--------|----------|------|---------|-------|
| | + bastion | aws_instance | $8.53 | +$8.53 |
| | + api | aws_ecs_service | $7.39 | +$7.39 |
| `module.db` | + this | aws_db_instance | $13.98 | +$13.98 |Cost Diff
Show how Terraform changes affect monthly costs by comparing against a baseline.
Git-Based Diff (Default)
By default, l4 diff compares the current branch against the git merge-base with main or master:
l4 diff ./infra/Custom Base Branch
Compare against a specific git ref:
l4 diff --base develop ./infra/Snapshot-Based Diff
Compare against a previously saved snapshot:
l4 diff baseline.json ./infra/CI Gate with Threshold
Exit with code 2 if the cost delta exceeds a threshold:
l4 diff ./infra/ --fail-above 100 --format github-commentGitHub Actions
Cost Estimate on Every PR
name: Cost Estimate
on:
pull_request:
paths:
- 'infra/**'
jobs:
cost-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install LevelFour CLI
run: |
curl -sSL https://github.com/LevelFourAI/levelfour-cli/releases/latest/download/l4_linux_amd64.tar.gz | tar xz
sudo mv l4 /usr/local/bin/
- name: Estimate costs
id: estimate
env:
LEVELFOUR_TOKEN: ${{ secrets.LEVELFOUR_TOKEN }}
run: |
l4 estimate ./infra/ --format github-comment > cost-estimate.md
- name: Comment on PR
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const body = fs.readFileSync('cost-estimate.md', 'utf8');
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: body,
});Cost Diff with Threshold Gate
name: Cost Diff
on:
pull_request:
paths:
- 'infra/**'
jobs:
cost-diff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Install LevelFour CLI
run: |
curl -sSL https://github.com/LevelFourAI/levelfour-cli/releases/latest/download/l4_linux_amd64.tar.gz | tar xz
sudo mv l4 /usr/local/bin/
- name: Cost diff
id: diff
env:
LEVELFOUR_TOKEN: ${{ secrets.LEVELFOUR_TOKEN }}
run: |
l4 diff ./infra/ --format github-comment --fail-above 100 > cost-diff.md || echo "exit_code=$?" >> $GITHUB_OUTPUT
- name: Comment on PR
if: always()
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const body = fs.readFileSync('cost-diff.md', 'utf8');
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: body,
});
- name: Fail if over threshold
if: steps.diff.outputs.exit_code == '2'
run: exit 1GitLab CI
cost-estimate:
stage: validate
image: ubuntu:latest
before_script:
- curl -sSL https://github.com/LevelFourAI/levelfour-cli/releases/latest/download/l4_linux_amd64.tar.gz | tar xz
- mv l4 /usr/local/bin/
script:
- l4 estimate ./infra/ --fail-above 500 --format table
variables:
LEVELFOUR_TOKEN: $LEVELFOUR_TOKEN
rules:
- changes:
- infra/**
cost-diff:
stage: validate
image: ubuntu:latest
before_script:
- curl -sSL https://github.com/LevelFourAI/levelfour-cli/releases/latest/download/l4_linux_amd64.tar.gz | tar xz
- mv l4 /usr/local/bin/
script:
- l4 diff ./infra/ --fail-above 100 --format table
variables:
LEVELFOUR_TOKEN: $LEVELFOUR_TOKEN
GIT_DEPTH: 0
rules:
- changes:
- infra/**Estimate Flags
| Flag | Default | Description |
|---|---|---|
--format | table | Output format: table, json, github-comment |
--out-file | - | Save resource snapshot to file |
--fail-above | 0 | Exit code 2 if monthly cost exceeds threshold |
--region | us-east-1 | AWS region for pricing |
--gcp-region | auto-detect | GCP region for pricing |
--azure-region | auto-detect | Azure region for pricing |
--var-file | - | Terraform variable files (repeatable) |
--var | - | Terraform variables as key=value (repeatable) |
--download-modules | true | Download and resolve remote Terraform modules |
--max-resources | 500 | Maximum number of resources to include |
Diff Flags
| Flag | Default | Description |
|---|---|---|
--format | table | Output format: table, json, github-comment |
--fail-above | 0 | Exit code 2 if monthly delta exceeds threshold |
--base | auto-detect | Git ref to diff against (defaults to main/master merge-base) |
--region | us-east-1 | AWS region for pricing |
--gcp-region | auto-detect | GCP region for pricing |
--azure-region | auto-detect | Azure region for pricing |
--var-file | - | Terraform variable files (repeatable) |
--var | - | Terraform variables as key=value (repeatable) |
--download-modules | true | Download and resolve remote Terraform modules |
--max-resources | 500 | Maximum number of resources to include |
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | General error |
| 2 | --fail-above threshold exceeded |
| 4 | Authentication required |
| 130 | Interrupted (Ctrl+C) |