Code Snip: This sites ASG

Saturday, Feb 7, 2026

Code Snip: The Terraform configuration the ASG

Part of the Terraform for this site This Website

data "aws_ami" "awsecslinux" {
  most_recent = true
  owners = [ "amazon" ]

  filter {
    name   = "name"
    values = ["al2023-ami-ecs-hvm-2023*-x86_64"]
  }

  filter {
    name = "virtualization-type"
    values = ["hvm"]
  }

  filter {
    name = "root-device-type"
    values = [ "ebs" ]
  }

  filter {
    name = "architecture"
    values = [ "x86_64" ]
  }

}

resource "aws_iam_instance_profile" "ecs_server_profile" {
  name = "ecs_server_profile"
  role = aws_iam_role.ecs_role.name
}

data "aws_iam_policy_document" "ecs_assume_role" {
  statement {
    effect = "Allow"

    principals {
      type        = "Service"
      identifiers = ["ec2.amazonaws.com"]
    }

    actions = ["sts:AssumeRole"]
  }
}

resource "aws_iam_role" "ecs_role" {
  name               = "ecs_server_role"
  path               = "/"
  assume_role_policy = data.aws_iam_policy_document.ecs_assume_role.json
}

resource "aws_iam_role_policy_attachment" "ecs_policy_attachment" {
  policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role"
  role = aws_iam_role.ecs_role.name
}


resource "aws_launch_template" "ecs_server_template" {
  name_prefix   = "server_template-ecs"
  image_id      = data.aws_ami.awsecslinux.id
  instance_type = "t3.small"
  key_name = "anaconda"

  user_data = base64encode(<<-EOF
    #!/bin/bash
    echo ECS_CLUSTER=${aws_ecs_cluster.server_cluster.name} >> /etc/ecs/ecs.config
  EOF
  )

  iam_instance_profile {
    name = aws_iam_instance_profile.ecs_server_profile.name
  }

  network_interfaces {
    associate_public_ip_address = true
    security_groups = [ aws_security_group.ecs_instance_sg.id ]
  }

  monitoring {
    enabled = true
  }

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

  block_device_mappings {
    device_name = "/dev/xvda"

    ebs {
      volume_size = 30
    }
  }

  tag_specifications {
    resource_type = "instance"
    tags = {
      Name = "AWS Linux Server"
      Server = "Main"
    }
  }


  credit_specification {
    cpu_credits = "standard"
  }

  lifecycle {
    create_before_destroy = true
  }

  depends_on = [aws_ecs_cluster.server_cluster]

}

resource "aws_autoscaling_group" "ecs_asg" {
  name = "ecs_asg"
  vpc_zone_identifier = [for ps in aws_subnet.private_subnets : ps.id]
  min_size = 1
  max_size = 20
  health_check_type = "EC2"
  health_check_grace_period = 300
  default_cooldown = 3600
  capacity_rebalance = true
  max_instance_lifetime = 518400 # 6 days


  lifecycle {
    create_before_destroy = true
    ignore_changes = [ target_group_arns, desired_capacity, desired_capacity_type ]
  }
  mixed_instances_policy {
    launch_template {
      launch_template_specification {
        launch_template_id = aws_launch_template.ecs_server_template.id
        version = "$Latest"
      }
      override {
        instance_requirements {
          memory_mib {
            min = 1500
            # max = 8500
          }
          vcpu_count {
            min = 2
            max = 4
          }
          burstable_performance = "included"
          cpu_manufacturers = ["intel"]
          instance_generations = ["current"]
          local_storage = "excluded"
          accelerator_count {
            max = 0
          }
        }
      }
    }

    instances_distribution {
      on_demand_allocation_strategy = "lowest-price"
      on_demand_base_capacity = 0
      on_demand_percentage_above_base_capacity = 0
      spot_allocation_strategy = "price-capacity-optimized"
    }
  }
  tag {
    key                 = "AmazonECSManaged"
    value               = true
    propagate_at_launch = true
  }

}


resource "aws_ecs_cluster_capacity_providers" "asg_instances_capacity" {
  cluster_name = aws_ecs_cluster.server_cluster.name

  capacity_providers = [aws_ecs_capacity_provider.asg_capacity.name]

  default_capacity_provider_strategy {
    capacity_provider = aws_ecs_capacity_provider.asg_capacity.name
    weight            = 100
    base              = 1
  }
}

resource "aws_ecs_capacity_provider" "asg_capacity" {
  name = "asg_capacity"

  auto_scaling_group_provider {
    auto_scaling_group_arn = aws_autoscaling_group.ecs_asg.arn
    managed_termination_protection = "DISABLED"

    managed_scaling {
      status = "ENABLED"
      instance_warmup_period = 3600
      maximum_scaling_step_size = 2
      minimum_scaling_step_size = 1
      target_capacity = 100
    }
  }
}


resource "aws_appautoscaling_target" "ecs_service" {
  for_each = var.backend_tasks

  max_capacity = 100
  min_capacity = 1
  resource_id = "service/${aws_ecs_cluster.server_cluster.name}/${aws_ecs_service.server_services[each.key].name}"
  scalable_dimension = "ecs:service:DesiredCount"
  service_namespace = "ecs"
}

resource "aws_appautoscaling_policy" "ecs_service_cpu" {
  for_each = var.backend_tasks

  name = "${each.key}_cpu_scaling_policy"
  resource_id = aws_appautoscaling_target.ecs_service[each.key].resource_id
  scalable_dimension = aws_appautoscaling_target.ecs_service[each.key].scalable_dimension
  service_namespace = aws_appautoscaling_target.ecs_service[each.key].service_namespace
  policy_type = "TargetTrackingScaling"

  target_tracking_scaling_policy_configuration {
    predefined_metric_specification {
      predefined_metric_type = "ECSServiceAverageCPUUtilization"
    }

    target_value = 75.0
    scale_in_cooldown = 300
    scale_out_cooldown = 60
  }
}

resource "aws_appautoscaling_policy" "ecs_service_memory" {
  for_each = var.backend_tasks

  name = "${each.key}_memory_scaling_policy"
  resource_id = aws_appautoscaling_target.ecs_service[each.key].resource_id
  scalable_dimension = aws_appautoscaling_target.ecs_service[each.key].scalable_dimension
  service_namespace = aws_appautoscaling_target.ecs_service[each.key].service_namespace
  policy_type = "TargetTrackingScaling"

  target_tracking_scaling_policy_configuration {
    predefined_metric_specification {
      predefined_metric_type = "ECSServiceAverageMemoryUtilization"
    }

    target_value = 75.0
    scale_in_cooldown = 300
    scale_out_cooldown = 60
  }
}