The management of container workloads on Amazon Elastic Container Service through Terraform centers on the task definition as the authoritative blueprint for how a container runs. Terraform codifies that blueprint with the awsecstask_definition resource, and the definition becomes the single source of truth that ECS services reference when they launch tasks. The practice of declaring task definitions in Terraform enables reusable, parameter-driven infrastructure that can be versioned, audited, and promoted across environments without manual console changes. The reference facts provided illustrate the scope of that resource: container image selection, CPU and memory allocation, port mappings, IAM execution role and task role attachment, logging configuration, operating system platform, and the overall container workload shape. Every ECS service references a task definition to know what to run and how, which makes the task definition the central control point for deployment safety and operational consistency.
OpenTofu is an open-source version of Terraform that expands on Terraform’s existing concepts and offerings. It is a viable alternative to HashiCorp’s Terraform, being forked from Terraform version 1.5.6. Teams that adopt OpenTofu can continue to author awsecstask_definition resources with the same HCL semantics while benefiting from an open-source governance model. Discover better way to manage Terraform at scale is a common operational theme, and Spacelift helps manage Terraform state, build more complex workflows, supports policy as code, programmatic configuration, context sharing, drift detection, resource visualization, and many more. Those capabilities become relevant when task definitions are updated frequently, because drift detection and policy as code can prevent unintended changes to CPU, memory, IAM roles, or container definitions from reaching production.
What Is an ECS Task Definition in Terraform
A task definition is a blueprint for your container workload. It defines the container image, CPU and memory allocation, port mappings, IAM execution role, logging configuration, and the operating system platform. In Terraform, it is managed using the awsecstask_definition resource. Every ECS service references a task definition to know what to run and how.
The impact of treating the task definition as a blueprint is that changes to the blueprint do not immediately affect running tasks. A service continues to run the previous revision until the service is instructed to adopt the new revision. That decoupling allows teams to test new images, new environment variables, or new resource sizes in a controlled manner. The contextual layer connects this to CI/CD pipelines, where a Terraform apply that updates a task definition produces a new revision and a subsequent service update can be gated by automated tests.
Do I need a load balancer to deploy ECS with Terraform? No, a load balancer is optional for getting an ECS service running. The absence of a load balancer simplifies initial task definition validation because the service can be deployed with a desired count of one and inspected via logs and health checks without networking dependencies.
ECS Launch Types and Task Definition Implications
What is the difference between ECS on EC2 and ECS on Fargate? With the EC2 launch type, you provision and manage the underlying EC2 instances yourself — giving you more control over instance type, pricing, and configuration. Fargate is serverless: AWS manages the compute entirely, and you only define CPU and memory at the task level. Fargate is simpler to operate; EC2 gives you more flexibility and is often more cost-effective at scale.
The launch type choice influences task definition fields. For Fargate, networkmode must be awsvpc and requirescompatibilities must include FARGATE. For EC2 launch type, the task definition can reference a bridge network mode and the task relies on the host network configuration. The CPU and memory values in the task definition are enforced by Fargate at the task level, whereas on EC2 they are advisory relative to the underlying instance capacity.
The real-world consequence for the user is operational ownership. With EC2, teams must manage instance scaling, placement strategies, and capacity. With Fargate, the task definition becomes the only compute declaration, reducing infrastructure burden at the cost of per-task pricing.
Core Anatomy of awsecstask_definition
By utilizing Terraform, you can:
- Simplify the creation of reusable ECS task definitions.
- Pass dynamic variables to tasks and services.
- Seamlessly integrate with other AWS services like Secrets Manager and CloudWatch.
The resource supports defining container settings such as CPU, memory, image, etc. The resource also manages ECS services with the number of tasks, scaling policies, and integration with ALBs. AWS services integration connects to Secrets Manager for sensitive data and enables CloudWatch logging and metrics.
A typical parameterized task definition in Terraform appears as follows:
resource "aws_ecs_task_definition" "app" {
family = var.task_family
container_definitions = jsonencode([
{
name = "${var.container_name}"
image = "${var.container_image}"
cpu = var.cpu
memory = var.memory
essential = true
environment = var.environment_variables
logConfiguration = {
logDriver = "awslogs"
options = {
"awslogs-group" = var.log_group
"awslogs-region" = var.region
"awslogs-stream-prefix" = var.log_stream_prefix
}
}
}
])
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
execution_role_arn = aws_iam_role.ecs_task_execution.arn
task_role_arn = aws_iam_role.ecs_task.arn
cpu = var.task_cpu
memory = var.task_memory
}
Variables to make the task definition reusable include taskfamily, containername, containerimage, cpu, memory, and environmentvariables with type definition.
The impact layer is reuse across services. By parameterizing family, image tag, CPU, and memory, the same module can deploy development, staging, and production workloads with different values. The contextual layer ties this to policy as code, where Spacelift can enforce that executionrolearn and taskrolearn are always set and that logConfiguration is present.
Fargate Task Definition Patterns
This guide covers creating task definitions in Terraform for Fargate, including single and multi-container tasks, secrets management, health checks, and resource sizing.
Basic Task Definition for Fargate is expressed as:
```
resource "awsecstaskdefinition" "app" {
family = "myapp"
requirescompatibilities = ["FARGATE"]
network_mode = "awsvpc" # Required for Fargate
Task-level resources
cpu = 512 # 0.5 vCPU (256, 512, 1024, 2048, 4096)
memory = 1024 # 1 GB
IAM roles
executionrolearn = awsiamrole.ecsexecution.arn
taskrolearn = awsiamrole.ecstask.arn
Container definitions
containerdefinitions = jsonencode([
{
name = "app"
image = "${var.ecrrepositoryurl}:${var.imagetag}"
essential = true
portMappings = [
{
containerPort = 8080
hostPort = 8080
protocol = "tcp"
}
]
Logging
logConfiguration = {
logDriver = "awslogs"
options = {
"awslogs-group" = awscloudwatchloggroup.app.name
"awslogs-region" = var.awsregion
"awslogs-stream-prefix" = "app"
}
}
Environment variables
environment = [
{ name = "PORT", value = "8080" },
{ name = "NODEENV", value = var.environment },
{ name = "LOGLEVEL", value = "info" }
]
Secrets from SSM Parameter Store or Secrets Manager
secrets = [
{
name = "DATABASEURL"
valueFrom = awsssmparameter.dburl.arn
},
{
name = "APIKEY"
valueFrom = "${awssecretsmanagersecret.apikey.arn}:api_key::"
}
]
Health check
healthCheck = {
command = ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"]
interval = 30
timeout = 5
retries =
```
Resource sizing for Fargate is constrained to valid CPU and memory combinations. The reference facts list 256, 512, 1024, 2048, 4096 for CPU and illustrate memory as 1024 for 1 GB. The table below summarizes the documented sizing options mentioned.
| CPU vCPU | Memory |
|---|---|
| 256 | 0.5 vCPU |
| 512 | 0.5 vCPU |
| 1024 | |
| 2048 | |
| 4096 |
The impact of choosing CPU and memory at the task level is direct cost control. Fargate bills per vCPU and memory per second, so precise sizing in the task definition reduces waste. The contextual layer connects to environment variables such as NODEENV and LOGLEVEL, where the task definition provides runtime configuration without rebuilding the image.
Secrets management is enforced via valueFrom referencing AWS Systems Manager Parameter Store or Secrets Manager. The reference facts show DATABASEURL sourced from awsssmparameter.dburl.arn and APIKEY sourced from awssecretsmanagersecret.apikey.arn. Never put credentials in environment variables directly. The use of secrets avoids credential leakage in Terraform state and CloudWatch logs.
Health checks are defined inside container definitions with command, interval, timeout, and retries. A health check such as curl -f http://localhost:8080/health || exit 1 enables ECS to mark a task unhealthy and replace it. The real-world consequence is faster failure detection and self-healing services.
Task Definition Versioning and Service References
Each time you change a task definition, Terraform creates a new revision. The old revisions are kept:
output "task_definition_arn" {
description = "Full ARN including revision number"
value = aws_ecs_task_definition.app.arn
}
output "task_definition_revision" {
description = "Current revision number"
value = aws_ecs_task_definition.app.revision
}
Services reference the task definition family, and you control whether they use the latest revision or a specific one.
Outputs commonly exposed include:
output "task_definition_family" {
description = "Task definition family name"
value = aws_ecs_task_definition.app.family
}
output "task_definition_arn" {
description = "Task definition ARN"
value = aws_ecs_task_definition.app.arn
}
output "container_name" {
description = "Main container name"
value = "app"
}
output "container_port" {
description = "Main container port"
value = 8080
}
The impact layer is safe rollbacks. Because revisions are immutable, a service can be pinned to a known good revision ARN. The contextual layer ties to drift detection, where Spacelift can alert when a manual console change creates a revision not tracked in Terraform.
Summary points from the reference facts state that ECS task definitions in Terraform specify everything about how containers run: the image, CPU and memory allocation, port mappings, logging, environment variables, secrets, health checks, and volumes. For Fargate, use network_mode = "awsvpc" and choose from the valid CPU/memory combinations. Use SSM Parameter Store or Secrets Manager for sensitive values - never put credentials in environment variables directly. Multi-container tasks let you run sidecars for monitoring, logging, or proxying alongside your application.
Modules and Community Patterns
A Terraform module for creating Amazon ECS Task Definitions is documented. THIS MODULE IS NOT COMPATIBLE WITH VERSIONS OF TERRAFORM LESS THAN v0.12.x. PLEASE REFER TO THE OFFICIAL DOCUMENTATION FOR UPGRADING TO THE LATEST VERSION OF TERRAFORM.
The purpose of this module is to generate a valid Amazon ECS Task Definition dynamically. A task definition is required to run Docker containers in Amazon ECS. A task definition contains a list of container definitions received by the Docker daemon to create a container instance.
- Have Terraform generate valid task definitions dynamically
- Update the ECS task definition and trigger new service deployments automatically
This module uses the same parameters as the ContainerDefinition object.
Deploying ECS services using Terraform allows you to maintain a modular, reusable, and parameter-driven infrastructure. In this guide, we will focus on creating and reusing ECS task definitions, configuring services, and integrating other AWS components effectively with Terraform.
Creating a robust ECS deployment often involves repetitive configurations and complex setups.
The impact of a community module is reduced boilerplate. Teams can standardize container definition shapes, logging defaults, and IAM role attachments. The contextual layer connects to programmatic configuration in Spacelift, where module inputs can be validated and shared across teams.
Migration Paths and Consul Integration
Register existing ECS tasks with Terraform. To migrate existing tasks to Consul, rewrite the existing Terraform code for your tasks so that the container definitions include the mesh-task Terraform module.
Your tasks must already be defined in Terraform using the ecstaskdefinition resource so that they can then be converted to use the mesh-task module.
The following example shows an existing task definition configured in Terraform:
resource "aws_ecs_task_definition" "my_task" {
family = "my_task"
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc"
cpu = 256
memory = 512
execution_role_arn = "arn:aws:iam::111111111111:role/execution-role"
task_role_arn = "arn:aws:iam::111111111111:role/task-role"
container_definitions = jsonencode(
[{
name = "example-client-app"
image = "docker.io/org/my_task:v0.0.1"
essential = true
portMappings = [
{
containerPort = 9090
hostPort = 9090
protocol = "tcp"
}
]
cpu = 0
mountPoints = []
volumesFrom = []
}]
)
}
resource "aws_ecs_service" "my_task" {
name = "my_task"
cluster = "arn:aws:ecs:us-east-1:111111111111:cluster/my-cluster"
task_definition = aws_ecs_task_definition.my_task.arn
desired_count = 1
network_configuration {
subnets = ["subnet-abc123"]
}
launch_type = "FARGATE"
}
Replace the awsecstask_definition resource with the mesh-task module so that Consul adds the necessary dataplane containers that enable your task to join the mesh.
The impact layer is service mesh adoption without rebuilding container images. The contextual layer ties to migration strategies where existing Terraform task definitions are refactored to include sidecar containers via the mesh-task module, preserving family names and revision history.
Operational Best Practices and Scale Considerations
Define a parameterized task definition in Terraform. Pass dynamic variables to tasks and services. Seamlessly integrate with other AWS services like Secrets Manager and CloudWatch.
ECS Task Definitions define container settings such as CPU, memory, image, etc. Use variables to make definitions reusable across multiple services.
ECS Services manage the number of tasks, scaling policies, and integration with ALBs.
AWS Services Integration connects to Secrets Manager for sensitive data and enables CloudWatch logging and metrics.
Creating a robust ECS deployment often involves repetitive configurations and complex setups. The use of variables for taskfamily, containername, containerimage, cpu, memory, and environmentvariables makes definitions reusable.
The real-world consequence for teams is reduced configuration drift and faster environment promotion. The contextual layer connects to policy as code and drift detection, where changes to cpu, memory, executionrolearn, or container_definitions can be validated before apply.
Conclusion
Terraform ECS task definition management represents the intersection of declarative infrastructure and container workload definition. The awsecstask_definition resource captures image, CPU, memory, network mode, IAM roles, logging, environment variables, secrets, health checks, and port mappings in a single versioned artifact. That artifact becomes the input for ECS services, and each change produces a new revision while preserving historical revisions for rollback and audit.
Fargate and EC2 launch types impose different constraints on the task definition, with Fargate requiring awsvpc network mode and task-level CPU and memory sizing from the set of valid combinations such as 256, 512, 1024, 2048, 4096. Parameterization of family, image, resources, and environment variables enables reuse across development, staging, and production. Integration with Secrets Manager and SSM Parameter Store protects sensitive values, while awslogs configuration ensures observability.
Modules and community patterns reduce repetition and enforce standards, and tools such as Spacelift add state management, workflow orchestration, policy as code, and drift detection around task definition changes. Migration scenarios, including Consul mesh-task adoption, demonstrate that existing task definitions defined in Terraform can be refactored to include additional containers without losing revision history.
The operational outcome is a modular, reusable, and parameter-driven infrastructure where task definitions are treated as immutable blueprints, services reference specific revisions, and changes are controlled through Terraform plans and applies. That discipline yields consistent deployments, safer rollbacks, and auditable container workloads at scale.