Cloud  /  Terraform

IaC Terraform 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
}