This example creates an ECS cluster with an EC2 Auto Scaling Group (ASG) capacity provider using the standalone cluster and service sub-modules.
What is created
ECS cluster with an EC2 ASG capacity provider (managed scaling enabled)
EC2 Auto Scaling Group with ECS-optimized AMI
ECS service configured for EC2 launch type
Application Load Balancer
VPC with private/public subnets
IAM roles for EC2 instances
Prerequisites
You must create an Auto Scaling Group before attaching it as a capacity provider. This example uses the terraform-aws-autoscaling module for that purpose.
Code
Cluster
Auto Scaling Groups
Service
module "ecs_cluster" {
source = "terraform-aws-modules/ecs/aws//modules/cluster"
name = local . name
default_capacity_provider_strategy = {
one = { weight = 60 , base = 20 }
two = { weight = 40 }
}
capacity_providers = {
one = {
auto_scaling_group_provider = {
auto_scaling_group_arn = module.autoscaling[ "one" ].autoscaling_group_arn
managed_draining = "ENABLED"
managed_termination_protection = "ENABLED"
managed_scaling = {
maximum_scaling_step_size = 5
minimum_scaling_step_size = 1
status = "ENABLED"
target_capacity = 60
}
}
}
two = {
auto_scaling_group_provider = {
auto_scaling_group_arn = module.autoscaling[ "two" ].autoscaling_group_arn
managed_draining = "ENABLED"
managed_termination_protection = "ENABLED"
managed_scaling = {
maximum_scaling_step_size = 15
minimum_scaling_step_size = 5
status = "ENABLED"
target_capacity = 90
}
}
}
}
tags = local . tags
}
data "aws_ssm_parameter" "ecs_optimized_ami" {
name = "/aws/service/ecs/optimized-ami/amazon-linux-2023/recommended"
}
module "autoscaling" {
source = "terraform-aws-modules/autoscaling/aws"
version = "~> 9.0"
for_each = {
one = { instance_type = "t3.micro" }
two = { instance_type = "t3.small" }
}
name = " ${ local . name } - ${ each . key } "
image_id = jsondecode (data . aws_ssm_parameter . ecs_optimized_ami . value )[ "image_id" ]
instance_type = each . value . instance_type
security_groups = [ module . autoscaling_sg . security_group_id ]
user_data = base64encode ( <<- EOT
#!/bin/bash
cat <<'EOF' >> /etc/ecs/ecs.config
ECS_CLUSTER= ${ local . name }
ECS_LOGLEVEL=debug
ECS_ENABLE_TASK_IAM_ROLE=true
EOF
EOT)
ignore_desired_capacity_changes = true
create_iam_instance_profile = true
iam_role_name = " ${ local . name } - ${ each . key } "
iam_role_policies = {
AmazonEC2ContainerServiceforEC2Role = "arn:aws:iam::aws:policy/service-role/AmazonEC2ContainerServiceforEC2Role"
AmazonSSMManagedInstanceCore = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}
vpc_zone_identifier = module.vpc.private_subnets
min_size = 1
max_size = 5
desired_capacity = 1
# Required for AmazonECSManaged tag used by capacity provider
autoscaling_group_tags = { AmazonECSManaged = true }
# Required for managed_termination_protection = "ENABLED"
protect_from_scale_in = true
tags = local.tags
}
module "ecs_service" {
source = "terraform-aws-modules/ecs/aws//modules/service"
name = local . name
cluster_arn = module . ecs_cluster . arn
cpu = 512
memory = 512
# EC2 launch type
requires_compatibilities = [ "EC2" ]
capacity_provider_strategy = {
one = {
capacity_provider = "one"
weight = 60
base = 20
}
}
container_definitions = {
(local . container_name ) = {
image = "public.ecr.aws/aws-containers/ecsdemo-frontend:776fd50"
cpu = 256
memory = 256
portMappings = [{
name = local.container_name
containerPort = local.container_port
protocol = "tcp"
}]
readonlyRootFilesystem = false
}
}
load_balancer = {
service = {
target_group_arn = module.alb.target_groups[ "ex-ecs" ].arn
container_name = local.container_name
container_port = local.container_port
}
}
subnet_ids = module . vpc . private_subnets
vpc_id = module . vpc . vpc_id
security_group_ingress_rules = {
alb_port = {
from_port = local.container_port
description = "Service port"
referenced_security_group_id = module.alb.security_group_id
}
}
security_group_egress_rules = {
all = { cidr_ipv4 = "0.0.0.0/0" , ip_protocol = "-1" }
}
tags = local . tags
}
Key highlights
AmazonECSManaged = true tag : The ASG must have this tag for ECS managed scaling to work.
protect_from_scale_in = true : Required when managed_termination_protection = "ENABLED" — prevents AWS from terminating instances before ECS drains tasks.
ignore_desired_capacity_changes = true : Prevents Terraform from resetting ASG desired capacity that ECS manages.
Multiple ASGs : The example uses two ASGs with different instance types and capacity provider weights.
EC2 Autoscaling Guide Detailed guide for EC2 Auto Scaling capacity providers.
Managed Instances Example Simpler fleet management without managing ASGs.