Terraform AWS ECS Cluster Deployment on EC2 Instances

Infrastructure sprawl is a natural consequence of scaling Amazon Elastic Container Service. The Elastic Container Service from Amazon Web Services is a fully-managed cloud Container Orchestration Service. It runs multiple Docker containers on the cluster using AWS EC2 instances. Optionally, these containers can also run on Fargate. The more AWS ECS Clusters we deploy, the more complex the infrastructure management becomes. Especially when deploying and scaling a large cluster, the process can become time-consuming, as it involves many repetitive tasks that lead to human errors, creating configuration drift and increasing the risk of security breaches. Using Terraform is the perfect solution to simplify the deployment of the AWS ECS Cluster. Terraform provides an automated way to manage AWS ECS Clusters, ensuring a consistent, repeatable deployment process.

In this post, we will focus on how to set up an ECS cluster of EC2 instances using Terraform. We will cover how to deploy a service on ECS clusters. There are mainly two ways to deploy a service on ECS clusters: Fargate and EC2 instances. This depends on the underlying infrastructure used to run the container workloads of any ECS service. AWS Fargate is a more cloud-native approach where the compute instances are automatically managed by AWS. Running the ECS service on EC2 instances provides more control over the infrastructure.

ECS on EC2 versus Fargate Decision Matrix

Choosing the right compute for a workload shapes cost, control and operational overhead.

Feature ECS with EC2 ECS with Fargate
Compute management Customer manages EC2 instances via Auto Scaling Group Serverless, AWS manages instances
Control More control over underlying EC2 instances, including ability to customize instance type, operating system, and networking settings Limited control, abstracted
Cost profile Generally more cost-effective than Fargate for long-running workloads, especially if you have spare EC2 capacity in your environment Pay per vCPU and memory used
Operational familiarity If you are already using EC2 instances for other workloads, then it may be easier to use ECS with EC2 as it’s similar to what you’re already using No server management required
Scaling model Capacity provider with managed scaling Automatic per task

The selection is not purely technical. Teams that require custom AMIs, specific instance families, host networking, or persistent block storage benefits from EC2. Teams that prioritize speed to market and minimal infrastructure overhead benefit from Fargate.

Terraform Resources for an EC2 Backed ECS Cluster

A repeatable EC2 backed ECS cluster in Terraform consists of networking, compute, capacity provider binding, cluster definition, task definition, and service.

Cluster and Capacity Provider Binding

The ECS cluster itself is created with the awsecscluster resource. Capacity providers bind the Auto Scaling Group to the cluster. Whereas “awsecsclustercapacityproviders” binds the ASG capacity provider with the ECS cluster created in Step 1.

```hcl
resource "awsecscapacityprovider" "ecscapacityprovider" {
name = "test1"
auto
scalinggroupprovider {
autoscalinggrouparn = awsautoscalinggroup.ecsasg.arn
managedscaling {
maximum
scalingstepsize = 1000
minimumscalingstepsize = 1
status = "ENABLED"
target
capacity = 3
}
}
}

resource "awsecsclustercapacityproviders" "example" {
clustername = awsecscluster.ecscluster.name
capacityproviders = [awsecscapacityprovider.ecscapacityprovider.name]
defaultcapacityproviderstrategy {
base = 1
weight = 100
capacity
provider = awsecscapacityprovider.ecscapacity_provider.name
}
}
```

The capacity provider defines managed scaling behavior. The target_capacity of 3 is the desired capacity units for the Auto Scaling Group. Maximum and minimum scaling step size control how aggressively the group scales in response to task demand.

Task Definition for EC2 Launch Template

In Terraform, it is managed using the awsecstask_definition resource. Every ECS service references a task definition to know what to run and how.

Step 4: Create ECS task definition with Terraform. As described in the ECS overview section above, we now define the container task template to run on the ECS cluster using the image we pushed in Step 1.

Important points to note here are:

  • We have defined the network mode to be “awsvpc”. This tells the ECS cluster to use the VPC networking we have defined in the “VPC setup” section.
  • We have provided the task definition with the ecsTaskExecutionRole.
  • Defined CPU resource requirement as 256.
  • The runtime platform is an important attribute. Since we are using Amazon Linux AMI for our EC2 instances, the operatingsystemfamily is specified as “LINUX,” and the CPU architecture is set as “X86_64”.

The awsvpc mode provides each task with its own Elastic Network Interface, enabling security group enforcement at the task level and direct public or private IP assignment. The ecsTaskExecutionRole grants the task permission to pull images from Amazon ECR and write logs. CPU resource requirement as 256 defines the reservation for scheduling.

Service Placement and Load Balancing

Do I need a load balancer to deploy ECS with Terraform? No, a load balancer is optional for getting an ECS service running. However, without one, you would need to access containers directly via EC2 instance IPs, which are not stable across deployments. For any real workload, an Application Load Balancer is strongly recommended as it handles traffic distribution, health checks, and allows zero-downtime deployments.

A typical Terraform service attaches to the cluster, references the task definition, specifies the desired count, and associates the ALB target group. The ALB provides stable DNS, health checking, and supports rolling updates.

Deployment Workflow and Validation

The terraform code assumes that you have a route53 domain directed to the AWS account where it’ll be deployed. To make the necessary changes, navigate to the “variables.tf” file and update the values of both the “domainname” and “r53zone_id” variables to your own domain. Additionally, it utilizes the AWS credentials that have been set up under the “blog” profile. You have the option to modify the profile name or configure your profile with: aws configure --profile

After that, run the following command to get terraform dependencies downloaded

bash terraform init

And finally, you can apply the code:

bash terraform apply

Output during apply will include data source reads for user data and task definition JSON and IAM policy documents.

PS ECS-EC2> terraform apply data.template_file.user_data: Reading... data.template_file.task_definition_json: Reading... data.template_file.user_data: Read complete after 0s [id=806b79ba9b20cfdb8ec89a3c36c92254a6429fa678c49c196f836d22fed8d3b6] data.template_file.task_definition_json: Read complete after 0s [id=bca4ec17cec71fb09cde60c7f21e049df1b52a551a8bdad3182c87c7f2b30c1b] data.aws_iam_policy_document.ecs_agent: Reading... data.aws_iam_policy_document.ecs-instance-policy: Reading…

After a couple of seconds, it’ll prompt for confirmation to start creating resources:

Do you want to perform these actions? Terraform will perform the actions described above. Only 'yes' will be accepted to approve. Enter a value:

It’ll be completed in a few minutes:

aws_route53_record.www: Still creating..

Testing the ECS deployment with Terraform

When the Terraform code is successfully applied, as confirmed from the terminal output, log in to the AWS account and check for the existence of the following resources.

It should have created the VPC, associated subnets, route table, and internet gateway, as shown in the resource map below.

It should create a couple of EC2 instances as defined in the launch template in appropriate VPC subnets, and appropriate security groups should have been assigned.

Confirm this by navigating to the EC2 dashboard.

Navigate to EC2 > Load balancers. It should have created the Application load balancer with appropriate VPC and subnet association, as shown in the screenshot below.

Navigate to EC2 > Autoscaling groups.

It should have created the autoscaling group with the attribute values we supplied in the resource configuration.

Navigate to Amazon ECS service.

Module Outputs and Related Patterns

A production-ready cluster module typically exposes identifiers for downstream consumption.

Name Description
arn ECS cluster arn
id ECS cluster id
name ECS cluster name
role_name IAM role name

Check out these related projects.

  • terraform-aws-ecs-web-app - Terraform module that implements a web app on ECS and supports autoscaling, CI/CD, monitoring, ALB integration, and much more.
  • terraform-aws-ecs-alb-service-task - Terraform module which implements an ECS service which exposes a web service via ALB.
  • terraform-aws-ecs-atlantis - Terraform module for deploying Atlantis as an ECS Task.

For additional context, refer to some of these links.

  • Amazon Elastic Container Service - Amazon Elastic Container Service is a highly scalable and fast container management service.
  • Amazon ECS capacity providers - Amazon ECS capacity providers are used to manage the infrastructure the tasks in your clusters use.
  • Amazon ECS Fargate - AWS Fargate is a technology that you can use with Amazon ECS to run containers without having to manage servers or clusters of Amazon EC2 instances.
  • Amazon EC2 Auto Scaling groups - An Auto Scaling group contains a collection of EC2 instances that are treated as a logical grouping for the purposes of automatic scaling and management.
  • terraform-aws-ec2-autoscale-group - Terraform module to provision Auto Scaling Group and Launch Template on AWS.

Tip

Use Cloud Posse's ready-to-go terraform architecture.

Conclusion

Building an ECS cluster on EC2 with Terraform converts an error-prone manual process into a versioned, repeatable pipeline. The core value lies in binding compute through a capacity provider to the cluster, expressing scaling policy as code, and defining task definitions with precise networking and resource requirements. EC2 backed clusters remain relevant where cost efficiency for steady state workloads, custom host configuration, and direct instance control are priorities. Fargate remains attractive for ephemeral or bursty workloads where operational overhead must be minimized.

The Terraform workflow enforces consistency across VPC, launch template, Auto Scaling Group, capacity provider, cluster, task definition, service, and load balancer. Validation steps in the AWS console confirm that the VPC fabric, EC2 instances, ALB, and autoscaling group materialize as defined. With variables for domain name, route53 zone, and AWS profile, the same codebase deploys across environments without drift. Reusing community modules accelerates delivery while preserving the ability to customize scaling, IAM roles, and task definitions for production workloads.

Sources

  1. spacelift.io/blog/terraform-ecs
  2. folderit.net/deploying-an-ecs-cluster-on-ec2-instances-with-terraform/
  3. github.com/cloudposse/terraform-aws-ecs-cluster

Related Posts