Cost Optimization

Cloud Cost Optimization with Terraform: Cut Your AWS, Azure, and GCP Bill

By CloudFormation Team ยท 2025-06-16 ยท 13 min read

The Cost of Unmanaged Cloud

Cloud waste is a $200 billion annual problem. Studies consistently find that 30-35% of cloud spend is wasted on idle resources, over-provisioned instances, forgotten test environments, and data transfer fees. Terraform addresses this by making infrastructure changes deliberate and reviewable โ€” you see the cost impact before you apply.

Right-Sizing Instances

The biggest single win is usually instance right-sizing. Use aws_cloudwatch_metric_alarm to monitor utilisation and catch over-provisioned instances:

resource "aws_cloudwatch_metric_alarm" "high_cpu" {
  alarm_name          = "${var.name}-high-cpu"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = 2
  metric_name         = "CPUUtilization"
  namespace           = "AWS/EC2"
  period              = 300
  statistic           = "Average"
  threshold           = 80
  alarm_description   = "EC2 instance CPU consistently high โ€” consider scaling up"
  dimensions          = { InstanceId = aws_instance.app.id }
}

resource "aws_cloudwatch_metric_alarm" "low_cpu" {
  alarm_name          = "${var.name}-low-cpu"
  comparison_operator = "LessThanThreshold"
  evaluation_periods  = 6
  metric_name         = "CPUUtilization"
  namespace           = "AWS/EC2"
  period              = 300
  statistic           = "Average"
  threshold           = 10
  alarm_description   = "EC2 instance CPU consistently low โ€” consider right-sizing down"
  dimensions          = { InstanceId = aws_instance.app.id }
}

Spot Instances for Non-Critical Workloads

Spot instances are up to 90% cheaper than on-demand. Use them for stateless, interruptible workloads:

resource "aws_launch_template" "app" {
  name_prefix   = "${var.project}-app-"
  image_id      = data.aws_ami.app.id
  instance_type = "m5.large"

  instance_market_options {
    market_type = "spot"
    spot_options {
      max_price          = "0.05"  # max hourly price
      interruption_behavior = "terminate"
    }
  }
}

resource "aws_autoscaling_group" "app" {
  mixed_instances_policy {
    instances_distribution {
      on_demand_base_capacity                  = 1      # always keep 1 on-demand
      on_demand_percentage_above_base_capacity = 0      # rest are spot
      spot_allocation_strategy                 = "capacity-optimized"
    }
    launch_template {
      launch_template_specification {
        launch_template_id = aws_launch_template.app.id
      }
      override { instance_type = "m5.large" }
      override { instance_type = "m5a.large" }
      override { instance_type = "m4.large" }
    }
  }
}

S3 Intelligent-Tiering and Lifecycle Policies

resource "aws_s3_bucket_lifecycle_configuration" "main" {
  bucket = aws_s3_bucket.main.id

  rule {
    id     = "move-to-cheaper-storage"
    status = "Enabled"

    transition {
      days          = 30
      storage_class = "STANDARD_IA"   # 46% cheaper than Standard
    }
    transition {
      days          = 90
      storage_class = "GLACIER_IR"    # 68% cheaper than Standard
    }
    transition {
      days          = 365
      storage_class = "DEEP_ARCHIVE"  # 95% cheaper than Standard
    }
    expiration {
      days = 2555  # 7 years, then delete
    }
  }

  rule {
    id     = "delete-incomplete-multipart"
    status = "Enabled"
    abort_incomplete_multipart_upload { days_after_initiation = 7 }
  }
}

Cost Tagging Strategy

You cannot optimise what you cannot measure. A consistent tagging strategy turns your cloud bill from a mystery into a clear cost breakdown by team, product, and environment:

locals {
  common_tags = {
    Project     = var.project
    Environment = var.environment
    Team        = var.team
    CostCenter  = var.cost_center
    ManagedBy   = "terraform"
    Repository  = var.git_repo
  }
}

resource "aws_instance" "app" {
  ami           = data.aws_ami.app.id
  instance_type = var.instance_type
  tags          = merge(local.common_tags, { Name = "${var.project}-app" })
}

Scheduled Scaling for Dev Environments

Dev and staging environments don't need to run 24/7. Auto-schedule scale-down at end of business day:

resource "aws_autoscaling_schedule" "scale_down_nights" {
  count                  = var.environment != "production" ? 1 : 0
  scheduled_action_name  = "scale-down-after-hours"
  autoscaling_group_name = aws_autoscaling_group.app.name
  recurrence             = "0 20 * * MON-FRI"  # 8pm weekdays
  min_size               = 0
  max_size               = 0
  desired_capacity       = 0
}

resource "aws_autoscaling_schedule" "scale_up_mornings" {
  count                  = var.environment != "production" ? 1 : 0
  scheduled_action_name  = "scale-up-mornings"
  autoscaling_group_name = aws_autoscaling_group.app.name
  recurrence             = "0 7 * * MON-FRI"   # 7am weekdays
  min_size               = 1
  max_size               = 4
  desired_capacity       = 2
}
Shutting down non-production environments after hours typically saves 65% on their compute costs โ€” and is one of the easiest wins available.

Try CloudFormation Free

Design infrastructure visually and generate production-ready Terraform in seconds โ€” 693 resources across 9 cloud providers.

๐Ÿš€ Open the Designer โ†’