콘텐츠로 이동

Infracost Cheat Sheet

Overview

Infracost is a cloud cost estimation tool that integrates with Terraform and OpenTofu to show how infrastructure changes will impact monthly cloud spending before they are applied. It parses HCL code and Terraform plan files to generate detailed cost breakdowns for AWS, Azure, and Google Cloud resources, enabling engineers to make cost-conscious decisions during the development process rather than discovering cost surprises after deployment.

Infracost integrates natively into CI/CD pipelines and pull request workflows, posting cost difference comments directly on GitHub, GitLab, Bitbucket, and Azure DevOps pull requests. This shift-left approach to FinOps gives teams immediate visibility into the financial impact of infrastructure changes. The tool supports usage-based cost estimation, custom price books, organization-wide policies, and tagging governance through its cloud dashboard.

Installation

CLI Installation

# macOS
brew install infracost

# Linux
curl -fsSL https://raw.githubusercontent.com/infracost/infracost/master/scripts/install.sh | sh

# Windows (Scoop)
scoop install infracost

# Docker
docker pull infracost/infracost:latest

# Verify installation
infracost --version

# Register and authenticate
infracost auth login
# Or set API key manually
export INFRACOST_API_KEY="your-api-key"

CI/CD Setup

# GitHub Actions
# Add INFRACOST_API_KEY to repository secrets

# GitLab CI
# Add INFRACOST_API_KEY to CI/CD variables

# Verify API key
infracost configure get api_key

# Set default currency
infracost configure set currency USD

# Set pricing API endpoint (self-hosted)
infracost configure set pricing_api_endpoint https://pricing.internal.com

Core Commands

Basic Cost Estimation

# Generate cost breakdown for a Terraform directory
infracost breakdown --path /path/to/terraform

# Generate from a Terraform plan file
terraform plan -out=plan.tfplan
terraform show -json plan.tfplan > plan.json
infracost breakdown --path plan.json

# Output in different formats
infracost breakdown --path . --format json > costs.json
infracost breakdown --path . --format table
infracost breakdown --path . --format html > costs.html

# Show monthly cost breakdown with usage
infracost breakdown --path . --show-skipped

# Filter by specific Terraform var file
infracost breakdown --path . --terraform-var-file="production.tfvars"

# Pass Terraform variables
infracost breakdown --path . --terraform-var="instance_type=m5.xlarge"

Cost Diff (Comparing Changes)

# Compare current branch to main
infracost diff --path . --compare-to infracost-base.json

# Generate baseline for comparison
git checkout main
infracost breakdown --path . --format json --out-file infracost-base.json

git checkout feature-branch
infracost diff --path . --compare-to infracost-base.json

# Diff with percentage threshold
infracost diff --path . --compare-to infracost-base.json \
  --format json --out-file infracost-diff.json

Multi-Project Workspaces

# Create infracost config file for monorepo
cat > infracost.yml << 'EOF'
version: 0.1
projects:
  - path: terraform/production
    name: production
    terraform_var_files:
      - production.tfvars
    
  - path: terraform/staging
    name: staging
    terraform_var_files:
      - staging.tfvars

  - path: terraform/networking
    name: networking
    
  - path: modules/database
    name: database-module
    terraform_vars:
      instance_class: db.r6g.xlarge
      multi_az: true
EOF

# Run with config file
infracost breakdown --config-file infracost.yml

# Generate diff for all projects
infracost diff --config-file infracost.yml --compare-to baseline.json

Configuration

Usage File (Estimate Usage-Based Costs)

# infracost-usage.yml
version: 0.1
resource_usage:
  # AWS Lambda
  aws_lambda_function.api:
    monthly_requests: 10000000
    request_duration_ms: 250

  # AWS S3
  aws_s3_bucket.data:
    standard:
      storage_gb: 5000
      monthly_tier_1_requests: 1000000
      monthly_tier_2_requests: 5000000
    glacier:
      storage_gb: 50000

  # AWS NAT Gateway
  aws_nat_gateway.main:
    monthly_data_processed_gb: 500

  # AWS CloudWatch
  aws_cloudwatch_log_group.app:
    monthly_data_ingested_gb: 100
    monthly_data_scanned_gb: 50

  # AWS RDS
  aws_db_instance.primary:
    monthly_additional_backup_storage_gb: 200

  # AWS ELB
  aws_lb.main:
    monthly_data_processed_gb: 1000
    new_connections: 100000
    active_connections: 5000
    rule_evaluations: 500000

  # Resource type defaults (apply to all of this type)
  aws_instance["*"]:
    monthly_hrs: 730  # full month
# Use the usage file
infracost breakdown --path . --usage-file infracost-usage.yml

# Generate usage template from terraform
infracost breakdown --path . --sync-usage-file --usage-file infracost-usage.yml

GitHub Actions Integration

# .github/workflows/infracost.yml
name: Infracost
on:
  pull_request:
    paths:
      - 'terraform/**'

jobs:
  infracost:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write
    steps:
      - uses: actions/checkout@v4

      - name: Setup Infracost
        uses: infracost/actions/setup@v3
        with:
          api-key: ${{ secrets.INFRACOST_API_KEY }}

      - name: Checkout base branch
        uses: actions/checkout@v4
        with:
          ref: ${{ github.event.pull_request.base.ref }}
          path: base

      - name: Generate base cost
        run: infracost breakdown --path base/terraform --format json --out-file /tmp/base.json

      - name: Checkout PR branch
        uses: actions/checkout@v4

      - name: Generate diff
        run: infracost diff --path terraform --compare-to /tmp/base.json --format json --out-file /tmp/diff.json

      - name: Post PR comment
        run: |
          infracost comment github \
            --path /tmp/diff.json \
            --repo $GITHUB_REPOSITORY \
            --github-token ${{ secrets.GITHUB_TOKEN }} \
            --pull-request ${{ github.event.pull_request.number }} \
            --behavior update

GitLab CI Integration

# .gitlab-ci.yml
infracost:
  image: infracost/infracost:latest
  stage: plan
  script:
    - git clone $CI_REPOSITORY_URL --branch $CI_MERGE_REQUEST_TARGET_BRANCH_NAME --single-branch base
    - infracost breakdown --path base/terraform --format json --out-file /tmp/base.json
    - infracost diff --path terraform --compare-to /tmp/base.json --format json --out-file /tmp/diff.json
    - infracost comment gitlab
        --path /tmp/diff.json
        --repo $CI_PROJECT_PATH
        --merge-request $CI_MERGE_REQUEST_IID
        --gitlab-server-url $CI_SERVER_URL
        --gitlab-token $GITLAB_TOKEN
        --behavior update
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
      changes:
        - terraform/**/*

Advanced Usage

Cost Policies

# Create policy file (OPA/Rego)
cat > policy.rego << 'EOF'
package infracost

deny[msg] {
  maxMonthlyCost := 5000.0
  msg := sprintf(
    "Total monthly cost $%.2f exceeds maximum $%.2f",
    [to_number(input.totalMonthlyCost), maxMonthlyCost]
  )
  to_number(input.totalMonthlyCost) > maxMonthlyCost
}

deny[msg] {
  r := input.projects[_].breakdown.resources[_]
  r.name == "aws_instance"
  contains(r.metadata["instanceType"], "x2")
  msg := sprintf("Resource %s uses expensive x2 instance type", [r.name])
}
EOF

# Evaluate policies
infracost breakdown --path . --format json | infracost policy eval --policy-file policy.rego

# Use in CI with exit code
infracost breakdown --path . --format json > costs.json
if infracost policy eval --policy-file policy.rego --path costs.json; then
  echo "Cost policy passed"
else
  echo "Cost policy violated"
  exit 1
fi

Terraform Cloud/Enterprise Integration

# Use with Terraform Cloud remote state
export INFRACOST_TERRAFORM_CLOUD_TOKEN="your-tfc-token"
export INFRACOST_TERRAFORM_CLOUD_HOST="app.terraform.io"

infracost breakdown --path . \
  --terraform-workspace production

# With Terraform Enterprise
export INFRACOST_TERRAFORM_CLOUD_HOST="tfe.company.com"
infracost breakdown --path .

Bulk Estimation and Reporting

# Generate costs for all workspaces
for dir in terraform/*/; do
  name=$(basename "$dir")
  infracost breakdown --path "$dir" \
    --format json \
    --out-file "/tmp/infracost-${name}.json"
done

# Combine multiple outputs
infracost output \
  --path "/tmp/infracost-*.json" \
  --format table

# Generate HTML report
infracost output \
  --path "/tmp/infracost-*.json" \
  --format html \
  --out-file cost-report.html

# Upload to Infracost Cloud dashboard
infracost upload --path /tmp/infracost-production.json

Terragrunt Support

# Single Terragrunt module
infracost breakdown --path /path/to/terragrunt/module

# All Terragrunt modules in a directory
infracost breakdown --path /path/to/terragrunt \
  --terraform-binary terragrunt \
  --terraform-workspace default

Troubleshooting

IssueCauseSolution
Resources showing $0Usage-based pricing not estimatedAdd usage file with expected consumption
Error: No valid Terraform files foundWrong path or missing .tf filesVerify —path points to directory with .tf files
Slow executionLarge Terraform stateUse plan JSON instead of HCL parsing
Missing resources in outputResource type not supportedCheck supported resources at infracost.io/docs/supported_resources
API rate limit exceededToo many parallel runsUse --no-cache sparingly; batch CI runs
Incorrect region pricingProvider region not detectedSet provider_region_override in config
Terraform init failingMissing backend credentialsSet required env vars or use --terraform-init-flags
PR comment not postingToken permissions insufficientEnsure GitHub token has pull-requests: write
# Debug: show detailed logs
INFRACOST_LOG_LEVEL=debug infracost breakdown --path .

# Debug: show which resources are supported
infracost breakdown --path . --show-skipped --format json | jq '.summary'

# Check configured settings
infracost configure get api_key
infracost configure get currency
infracost configure get pricing_api_endpoint

# Clear cache
rm -rf ~/.config/infracost/.cache

# Validate config file
infracost breakdown --config-file infracost.yml --log-level debug 2>&1 | head -20