Cloud/ Terraform / Basics & Fundamentals / Cost Management in Terraform: Estimate and Control Cloud Spending 2025

IaC Terraform Guide 15 of 17 50 guides ยท updated 2026

Infrastructure as code done right โ€” providers, state, reusable modules, and the workflow patterns that keep multi-cloud deployments sane in 2026.

Cost Management in Terraform

One of Terraformโ€™s advantages over manual provisioning is the ability to understand, control, and automate cost management. Every resource you define in HCL is a potential cost โ€” and Terraformโ€™s ecosystem gives you tools to estimate, tag, and govern that cost before a single dollar is spent.


The Cost Management Stack

Infracost โ†’ Cost estimation from terraform plan
Resource Tagging โ†’ Attribute costs to teams/projects
AWS Budgets โ†’ Alert when spending exceeds thresholds
Terraform lifecycle โ†’ prevent_destroy on expensive resources
Module abstractions โ†’ Enforce cost-efficient defaults
Scheduled pipelines โ†’ Detect orphaned expensive resources

Infracost: Cost Estimation Before Apply

Infracost integrates with Terraform to show cost estimates from plan output โ€” before you apply anything.

Install and Run

Terminal window
# macOS
brew install infracost
infracost auth login
# Generate a plan file
terraform plan -out=tfplan
# Get cost breakdown
infracost breakdown --path tfplan
# Output:
# Name Monthly Qty Unit Monthly Cost
# aws_instance.app
# โ”œโ”€ Instance usage (Linux/UNIX, on-demand, t3.large) 730 hours $60.74
# โ””โ”€ root_block_device
# โ””โ”€ Storage (general purpose SSD, gp3) 20 GB $1.60
# aws_db_instance.production
# โ”œโ”€ Database instance (db.r6g.large) 730 hours $175.20
# โ””โ”€ Storage (general purpose SSD, gp2) 100 GB $11.50
#
# OVERALL TOTAL $249.04/mo

CI/CD Integration

.github/workflows/infracost.yml
- name: Infracost
uses: infracost/actions/setup@v3
with:
api-key: ${{ secrets.INFRACOST_API_KEY }}
- name: Generate Infracost diff
run: |
terraform plan -out=tfplan
infracost diff \
--path tfplan \
--format json \
--out-file infracost-diff.json
- name: Post cost estimate to PR
run: |
infracost comment github \
--path infracost-diff.json \
--repo $GITHUB_REPOSITORY \
--pull-request ${{ github.event.pull_request.number }} \
--github-token ${{ secrets.GITHUB_TOKEN }}

The PR comment shows: existing cost, new cost, and the delta โ€” so the team sees โ€œthis change adds $45/monthโ€ before approving.


Resource Tagging for Cost Attribution

Tags are the foundation of cloud cost management. Without them, you canโ€™t answer โ€œwhich team is spending the most?โ€ or โ€œwhat does our auth service cost?โ€:

variables.tf
variable "cost_center" {
description = "Accounting cost center code"
type = string
}
variable "team" {
description = "Owning team name"
type = string
}
# locals.tf
locals {
required_tags = {
Environment = var.environment
Team = var.team
CostCenter = var.cost_center
Service = var.service_name
ManagedBy = "terraform"
}
}
# Apply to all resources
resource "aws_instance" "app" {
# ...
tags = merge(local.required_tags, {
Name = "${var.service_name}-app"
})
}

Enforce Tags via AWS Config or OPA

# Deny resources without required tags (using OPA / Conftest)
package main
required_tags = {"Team", "CostCenter", "Environment"}
deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_instance"
missing := required_tags - {key | resource.change.after.tags[key]}
count(missing) > 0
msg := sprintf("aws_instance %v is missing required tags: %v", [resource.address, missing])
}

AWS Budgets with Terraform

Create budget alerts as code so youโ€™re automatically notified of overspending:

resource "aws_budgets_budget" "monthly_cost" {
name = "monthly-${var.environment}-budget"
budget_type = "COST"
limit_amount = "500"
limit_unit = "USD"
time_unit = "MONTHLY"
notification {
comparison_operator = "GREATER_THAN"
threshold = 80 # Alert at 80% of budget
threshold_type = "PERCENTAGE"
notification_type = "ACTUAL"
subscriber_email_addresses = ["platform-team@company.com"]
}
notification {
comparison_operator = "GREATER_THAN"
threshold = 100
threshold_type = "PERCENTAGE"
notification_type = "FORECASTED" # Alert when forecast exceeds budget
subscriber_email_addresses = ["platform-team@company.com", "cto@company.com"]
}
}

Right-Sizing with Terraform Variables

Donโ€™t hardcode expensive instance types โ€” make them configurable and default to cost-effective options:

variables.tf
variable "app_instance_type" {
description = "EC2 instance type for application servers"
type = string
default = "t3.micro" # Cheap default โ€” override in production.tfvars
validation {
condition = contains([
"t3.micro", "t3.small", "t3.medium", # Dev/staging
"t3.large", "t3.xlarge", # Production
"m6i.large", "m6i.xlarge" # High-memory workloads
], var.app_instance_type)
error_message = "Must use an approved instance type."
}
}
environments/dev/terraform.tfvars
app_instance_type = "t3.micro"
# environments/production/terraform.tfvars
app_instance_type = "t3.large"

Automatically Clean Up Ephemeral Environments

# Create a TTL tag on dev resources
resource "aws_instance" "dev_server" {
tags = {
Environment = "dev"
TTL = formatdate("YYYY-MM-DD", timeadd(timestamp(), "168h")) # 7 days from now
}
}
# Schedule: find and destroy expired dev environments
#!/bin/bash
today=$(date +%Y-%m-%d)
aws ec2 describe-instances \
--filters "Name=tag:Environment,Values=dev" \
--query "Reservations[].Instances[?Tags[?Key=='TTL'&&Value<='${today}']].InstanceId" \
--output text | xargs -I {} terraform destroy -target=aws_instance.{} -auto-approve

Cost-Saving Terraform Patterns

# Use Spot instances for non-critical workloads
resource "aws_instance" "batch_worker" {
instance_market_options {
market_type = "spot"
spot_options {
max_price = "0.05" # Max hourly price
}
}
}
# S3 intelligent tiering for unknown access patterns
resource "aws_s3_bucket_intelligent_tiering_configuration" "data_lake" {
bucket = aws_s3_bucket.data_lake.id
name = "EntireBucket"
tiering {
access_tier = "DEEP_ARCHIVE_ACCESS"
days = 180
}
}
# RDS: start/stop on schedule for non-production
resource "aws_db_instance" "dev" {
# ...
# Use AWS EventBridge scheduler to stop at night and weekends
# Saves ~65% on dev database costs
}
Was this helpful?

Questions & Comments

Loading commentsโ€ฆ