Cost Optimization Strategies with Terraform on AWS

Learn how to implement cost optimization strategies for your AWS infrastructure using Terraform, including resource management, tagging, and budgeting

Cost Optimization Strategies with Terraform on AWS

Cost optimization is crucial for maintaining efficient cloud infrastructure. This guide demonstrates how to implement cost optimization strategies using Terraform on AWS.

Video Tutorial

Learn more about Cost Optimization with Terraform in AWS in this comprehensive video tutorial:

Prerequisites

  • AWS CLI configured with appropriate permissions
  • Terraform installed (version 1.0.0 or later)
  • Basic understanding of AWS pricing
  • Access to AWS Cost Explorer

Project Structure

terraform-cost/
├── main.tf
├── variables.tf
├── outputs.tf
├── modules/
│   └── cost/
│       ├── main.tf
│       ├── variables.tf
│       └── outputs.tf
└── policies/
    └── budget.json

Cost Management Configuration

Create modules/cost/main.tf:

# AWS Budgets
resource "aws_budgets_budget" "monthly" {
  name              = "${var.project_name}-monthly-budget"
  budget_type       = "COST"
  limit_amount      = var.monthly_budget_amount
  limit_unit        = "USD"
  time_period_start = "2025-01-01_00:00"
  time_unit         = "MONTHLY"

  notification {
    comparison_operator        = "GREATER_THAN"
    threshold                 = 80
    threshold_type            = "PERCENTAGE"
    notification_type         = "ACTUAL"
    subscriber_email_addresses = var.notification_emails
  }

  notification {
    comparison_operator        = "GREATER_THAN"
    threshold                 = 100
    threshold_type            = "PERCENTAGE"
    notification_type         = "FORECASTED"
    subscriber_email_addresses = var.notification_emails
  }

  cost_filters = {
    TagKeyValue = "user:Environment$${var.environment}"
  }
}

# Cost and Usage Report
resource "aws_cur_report_definition" "main" {
  report_name                = "${var.project_name}-cost-report"
  time_unit                 = "HOURLY"
  format                    = "Parquet"
  compression               = "Parquet"
  additional_schema_elements = ["RESOURCES"]
  s3_bucket                 = aws_s3_bucket.cost_reports.id
  s3_region                 = data.aws_region.current.name
  additional_artifacts      = ["ATHENA"]
  report_versioning        = "OVERWRITE_REPORT"
}

# S3 Bucket for Cost Reports
resource "aws_s3_bucket" "cost_reports" {
  bucket = "${var.project_name}-cost-reports-${data.aws_caller_identity.current.account_id}"

  tags = merge(
    var.tags,
    {
      Name = "${var.project_name}-cost-reports"
    }
  )
}

# S3 Lifecycle Rules
resource "aws_s3_bucket_lifecycle_configuration" "cost_reports" {
  bucket = aws_s3_bucket.cost_reports.id

  rule {
    id     = "archive-old-reports"
    status = "Enabled"

    transition {
      days          = 90
      storage_class = "GLACIER"
    }

    expiration {
      days = 365
    }
  }
}

# Cost Allocation Tags
resource "aws_ce_tags" "main" {
  tags = [
    "Environment",
    "Project",
    "Owner",
    "CostCenter"
  ]
}

# Cost Category
resource "aws_ce_cost_category" "environment" {
  name         = "Environment"
  rule_version = "CostCategoryExpression.v1"

  rules {
    value = "Production"
    rule {
      tags {
        key    = "Environment"
        values = ["prod"]
      }
    }
  }

  rules {
    value = "Development"
    rule {
      tags {
        key    = "Environment"
        values = ["dev"]
      }
    }
  }
}

# Cost Anomaly Detection
resource "aws_ce_anomaly_monitor" "main" {
  name              = "${var.project_name}-anomaly-monitor"
  monitor_type      = "DIMENSIONAL"
  monitor_dimension = "SERVICE"
}

resource "aws_ce_anomaly_subscription" "main" {
  name      = "${var.project_name}-anomaly-subscription"
  threshold = 100
  frequency = "DAILY"

  monitor_arn_list = [
    aws_ce_anomaly_monitor.main.arn
  ]

  subscriber {
    type    = "EMAIL"
    address = var.cost_admin_email
  }
}

Cost Optimization Strategies

  1. Resource Rightsizing
resource "aws_instance" "optimized" {
  instance_type = var.environment == "prod" ? "t3.medium" : "t3.micro"
  
  credit_specification {
    cpu_credits = "unlimited"
  }

  metadata_options {
    http_endpoint = "enabled"
    http_tokens   = "required"
  }

  root_block_device {
    volume_size = var.environment == "prod" ? 100 : 30
    volume_type = "gp3"
  }
}
  1. Auto Scaling
resource "aws_autoscaling_group" "cost_optimized" {
  mixed_instances_policy {
    instances_distribution {
      on_demand_base_capacity                  = 1
      on_demand_percentage_above_base_capacity = 25
      spot_allocation_strategy                 = "capacity-optimized"
    }

    launch_template {
      launch_template_specification {
        launch_template_id = aws_launch_template.main.id
        version           = "$Latest"
      }

      override {
        instance_type     = "t3.micro"
        weighted_capacity = "1"
      }

      override {
        instance_type     = "t3.small"
        weighted_capacity = "2"
      }
    }
  }
}
  1. Storage Optimization
resource "aws_s3_bucket_lifecycle_configuration" "main" {
  bucket = aws_s3_bucket.main.id

  rule {
    id     = "transition-to-ia"
    status = "Enabled"

    transition {
      days          = 30
      storage_class = "STANDARD_IA"
    }

    transition {
      days          = 90
      storage_class = "GLACIER"
    }

    expiration {
      days = 365
    }
  }
}

Cost Monitoring

  1. Budget Alerts
resource "aws_budgets_budget" "service" {
  name              = "${var.project_name}-service-budget"
  budget_type       = "COST"
  limit_amount      = var.service_budget_amount
  limit_unit        = "USD"
  time_unit         = "MONTHLY"

  cost_filter {
    name = "Service"
    values = [
      "Amazon Elastic Compute Cloud - Compute",
      "Amazon Simple Storage Service"
    ]
  }

  notification {
    comparison_operator        = "GREATER_THAN"
    threshold                 = 90
    threshold_type            = "PERCENTAGE"
    notification_type         = "FORECASTED"
    subscriber_email_addresses = var.notification_emails
  }
}
  1. Cost Explorer
resource "aws_ce_cost_category" "team" {
  name         = "Team"
  rule_version = "CostCategoryExpression.v1"

  rules {
    value = "Frontend"
    rule {
      tags {
        key    = "Team"
        values = ["frontend"]
      }
    }
  }

  rules {
    value = "Backend"
    rule {
      tags {
        key    = "Team"
        values = ["backend"]
      }
    }
  }
}

Resource Scheduling

  1. EC2 Scheduling
resource "aws_autoscaling_schedule" "scale_down" {
  scheduled_action_name  = "scale-down"
  min_size              = 0
  max_size              = 0
  desired_capacity      = 0
  recurrence           = "0 20 * * 1-5"
  autoscaling_group_name = aws_autoscaling_group.main.name
}

resource "aws_autoscaling_schedule" "scale_up" {
  scheduled_action_name  = "scale-up"
  min_size              = 1
  max_size              = 4
  desired_capacity      = 2
  recurrence           = "0 8 * * 1-5"
  autoscaling_group_name = aws_autoscaling_group.main.name
}
  1. RDS Scheduling
resource "aws_rds_cluster" "dev" {
  cluster_identifier  = "${var.project_name}-dev"
  engine             = "aurora-postgresql"
  engine_mode        = "serverless"
  
  scaling_configuration {
    auto_pause               = true
    max_capacity            = 4
    min_capacity            = 2
    seconds_until_auto_pause = 300
  }
}

Reserved Instances and Savings Plans

  1. Reserved Instance Planning
resource "aws_ec2_capacity_reservation" "main" {
  instance_type           = "t3.medium"
  instance_platform       = "Linux/UNIX"
  availability_zone       = "us-west-2a"
  instance_count         = 5
  end_date_type          = "unlimited"
  end_date               = "2026-01-19T00:00:00Z"
  instance_match_criteria = "targeted"
}
  1. Savings Plans Utilization
resource "aws_cloudwatch_metric_alarm" "savings_plan" {
  alarm_name          = "${var.project_name}-savings-plan-coverage"
  comparison_operator = "LessThanThreshold"
  evaluation_periods  = "1"
  metric_name        = "SavingsPlanCoverage"
  namespace          = "AWS/SavingsPlans"
  period             = "86400"
  statistic          = "Average"
  threshold          = 80
  alarm_description  = "Alert when Savings Plan coverage drops below 80%"
  alarm_actions      = [aws_sns_topic.cost_alerts.arn]
}

Cost Analysis and Reporting

  1. Athena Integration
resource "aws_athena_workgroup" "cost_analysis" {
  name = "${var.project_name}-cost-analysis"

  configuration {
    enforce_workgroup_configuration    = true
    publish_cloudwatch_metrics_enabled = true

    result_configuration {
      output_location = "s3://${aws_s3_bucket.cost_reports.id}/athena-results/"
    }
  }
}
  1. QuickSight Dashboard
resource "aws_quicksight_data_source" "cost" {
  name                = "${var.project_name}-cost-analysis"
  aws_account_id      = data.aws_caller_identity.current.account_id
  type                = "ATHENA"
  data_source_id      = "${var.project_name}-cost-analysis"

  parameters {
    athena {
      work_group = aws_athena_workgroup.cost_analysis.id
    }
  }
}

Best Practices

  1. Tagging Strategy

    • Implement mandatory tags
    • Use cost allocation tags
    • Track resources by project
    • Monitor tag compliance
  2. Resource Management

    • Right-size resources
    • Use auto scaling
    • Implement scheduling
    • Clean up unused resources
  3. Storage Optimization

    • Use lifecycle policies
    • Implement tiered storage
    • Delete unnecessary data
    • Compress data when possible
  4. Cost Monitoring

    • Set up budgets
    • Configure alerts
    • Track anomalies
    • Regular cost reviews

Conclusion

You’ve learned how to implement cost optimization strategies using Terraform in AWS. This setup provides:

  • Cost monitoring and alerting
  • Resource optimization
  • Budget management
  • Cost analysis capabilities

Remember to:

  • Monitor costs regularly
  • Review resource utilization
  • Optimize resource sizing
  • Implement cost controls