Skip to main content
This module provides a set of composable Terraform sub-modules for provisioning Amazon ECS infrastructure. Understanding how the pieces fit together will help you choose the right module structure for your use case.

Module structure

Root module

Combines the cluster and service sub-modules into a single integrated entrypoint. One module block provisions a cluster and any number of services together.

Cluster sub-module

Creates an ECS cluster, capacity providers, CloudWatch log group, task execution IAM role, and — for ECS Managed Instances — the infrastructure IAM role, node IAM role, and security group.

Service sub-module

Creates one ECS service, its task definition, container definitions, autoscaling resources, security group, service IAM role, task execution IAM role (optionally), and tasks IAM role.

Container definition sub-module

A building block invoked internally by the service sub-module for each container entry. Produces the JSON-encoded container definition and optionally manages the CloudWatch log group.

Express service sub-module

Creates an aws_ecs_express_gateway_service — a simplified, fully-managed ECS service type with built-in autoscaling. Use this when you want a minimal configuration surface without managing task definitions directly.

Construct hierarchy

Amazon ECS resources follow a strict parent-child hierarchy. The module mirrors this hierarchy exactly:
LevelAWS constructModule that creates it
1ECS Clustercluster sub-module
2ECS Serviceservice sub-module
3Task Definition / Task Setservice sub-module
4Container Definition (1–10 per task)container-definition sub-module
  • A cluster may contain one or more services.
  • A service manages one task definition or task set (this module assumes one per service).
  • A task wraps one to ten container definitions. Think of a task as a Kubernetes pod and the task definition as a pod spec.

Root module vs. separate sub-modules

Use the root module when you want to manage the cluster and all of its services in a single Terraform configuration. The root module accepts a services map and iterates over it with for_each, passing the cluster ARN and the cluster-level task execution role ARN to each service automatically.
module "ecs" {
  source  = "terraform-aws-modules/ecs/aws"
  version = "~> 7.0"

  cluster_name = "my-cluster"

  # Cluster-level task execution role shared by all services
  create_task_exec_iam_role = true

  cluster_capacity_providers = ["FARGATE", "FARGATE_SPOT"]
  default_capacity_provider_strategy = {
    fargate = { weight = 1 }
  }

  services = {
    api = {
      cpu    = 512
      memory = 1024

      container_definitions = {
        app = {
          image = "public.ecr.aws/aws-containers/ecsdemo-frontend:776fd50"
          portMappings = [{ containerPort = 3000 }]
        }
      }

      subnet_ids         = ["subnet-abc", "subnet-def"]
      security_group_ids = []
    }
  }

  tags = { Environment = "production" }
}
Best for: Single-team clusters where one Terraform workspace owns the cluster and all services.

Capacity provider types

The cluster sub-module supports four capacity provider types. You cannot mix Fargate-based providers with EC2-based providers on the same cluster.
TypeHow compute is provisionedKey tradeoff
FARGATEAWS manages serverless computePredictable pricing; no EC2 management
FARGATE_SPOTSpot capacity for FargateUp to 70% cheaper; tasks can be interrupted
EC2 Auto Scaling GroupYou supply an ASG (e.g., via terraform-aws-autoscaling); ECS manages scaling via managed scalingFull EC2 control; you own the AMI and instance lifecycle
ECS Managed InstancesECS provisions and manages EC2 instances via a fleet API; uses managed_instances_provider blockFully managed EC2 nodes; requires infrastructure and node IAM roles
You cannot mix EC2-based capacity providers (Auto Scaling Group or Managed Instances) with Fargate capacity providers on the same ECS cluster.

Resources created by each module

ResourceTerraform resource typeCondition
ECS clusteraws_ecs_clusterAlways
Cluster capacity providersaws_ecs_cluster_capacity_providersAlways
EC2 / Managed Instances capacity provideraws_ecs_capacity_providerWhen capacity_providers is set
CloudWatch log groupaws_cloudwatch_log_groupcreate_cloudwatch_log_group = true
Task execution IAM roleaws_iam_role (task_exec)create_task_exec_iam_role = true
Task execution IAM policyaws_iam_policy (task_exec)create_task_exec_policy = true
Infrastructure IAM roleaws_iam_role (infrastructure)Managed Instances enabled
Infrastructure IAM policyaws_iam_policy (infrastructure)Managed Instances enabled
Node IAM roleaws_iam_role (node)Managed Instances enabled
Node IAM policyaws_iam_policy (node)Managed Instances enabled
Node instance profileaws_iam_instance_profileManaged Instances enabled
Security group (for managed nodes)aws_security_groupManaged Instances enabled
Security group rulesaws_vpc_security_group_ingress_rule / aws_vpc_security_group_egress_ruleManaged Instances enabled
ResourceTerraform resource typeCondition
ECS serviceaws_ecs_service (this or ignore_task_definition)create_service = true
ECS task definitionaws_ecs_task_definitioncreate_task_definition = true
ECS task setaws_ecs_task_setExternal deployment controller
Container definitions (via sub-module)module.container_definitionOne per entry in container_definitions
Task execution IAM roleaws_iam_role (task_exec)create_task_exec_iam_role = true
Task execution IAM policyaws_iam_policy (task_exec)create_task_exec_policy = true
Tasks IAM roleaws_iam_role (tasks)create_tasks_iam_role = true
Tasks IAM policyaws_iam_policy (tasks)Statements or ECS Exec enabled
Service IAM roleaws_iam_role (service)Non-awsvpc mode with load balancer
Service IAM policyaws_iam_policy (service)Non-awsvpc mode with load balancer
Autoscaling targetaws_appautoscaling_targetenable_autoscaling = true
Autoscaling policiesaws_appautoscaling_policyenable_autoscaling = true
Autoscaling scheduled actionsaws_appautoscaling_scheduled_actionWhen configured
Security group (for tasks)aws_security_groupcreate_security_group = true
Security group rulesaws_vpc_security_group_ingress_rule / aws_vpc_security_group_egress_ruleWhen configured
ResourceTerraform resource typeCondition
CloudWatch log groupaws_cloudwatch_log_groupcreate_cloudwatch_log_group = true and enable_cloudwatch_logging = true
The sub-module’s primary output is the container_definition local — a map that is JSON-encoded by the service sub-module and passed to aws_ecs_task_definition.container_definitions.
ResourceTerraform resource typeCondition
Express gateway serviceaws_ecs_express_gateway_serviceAlways
Execution IAM roleaws_iam_role (execution)create_execution_iam_role = true
Infrastructure IAM roleaws_iam_role (infrastructure)create_infrastructure_iam_role = true
Security groupaws_security_groupcreate_security_group = true
Security group rulesaws_vpc_security_group_ingress_rule / aws_vpc_security_group_egress_ruleWhen configured

Build docs developers (and LLMs) love