The construction of Amazon Elastic Container Service task definitions with Terraform represents a core pattern for codifying how containers are launched, sized, observed and secured in a Fargate environment. The reference material centers on parameterized definitions, reusable variables, integration with Secrets Manager and CloudWatch, versioning behavior, module encapsulation and migration paths for mesh-enabled workloads. Each of those facets carries operational weight for teams that move from ad-hoc console edits to declarative, repeatable infrastructure.
The task definition resource is the authoritative description of a containerized workload. It encodes the family name, compatibility requirements, network mode, CPU and memory reservations at the task level, IAM roles for execution and task permissions, and the container definitions that describe image, environment, logging, secrets, health checks and port mappings. When Terraform manages this object, changes produce a new revision that is retained by ECS while services can be pinned to a specific revision or follow the latest revision. That behavior underpins safe rollout and rollback without manual ARN tracking.
Parameterized Task Definition Construction
The example parameterized definition defines a task definition with a family drawn from a variable, container name and image drawn from variables, and CPU and memory drawn from variables.
hcl
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
}
The direct fact is the use of jsonencode to build container_definitions from variables, the requirement of requires_compatibilities = ["FARGATE"] and network_mode = "awsvpc", and the attachment of execution and task role ARNs. The impact layer is that teams can store a single module and render distinct task definitions for development, staging and production by changing input values, reducing copy-paste drift. The contextual layer connects this pattern to ECS Services that reference the task definition ARN, meaning a variable change to CPU or memory propagates to service deployments without editing the service resource itself.
Variable Reusability Patterns
Variables are declared to make the definition reusable across multiple services.
hcl
variable "task_family" {}
variable "container_name" {}
variable "container_image" {}
variable "cpu" {}
variable "memory" {}
variable "environment_variables" { type =
The direct fact is the presence of task_family, container_name, container_image, cpu, memory, and environment_variables. The impact layer is that a single task definition template can be instantiated for different applications, preventing duplication of log configuration, IAM role wiring and compatibility settings. The contextual layer ties this to the broader Terraform principle of modularity described in the best-practices material, where reusable ECS task definitions simplify creation and pass dynamic variables to tasks and services.
Fargate Compatibility and Network Mode Requirements
Basic task definition for Fargate includes explicit compatibility and network mode.
hcl
resource "aws_ecs_task_definition" "app" {
family = "myapp"
requires_compatibilities = ["FARGATE"]
network_mode = "awsvpc" # Required for Fargate
cpu = 512 # 0.5 vCPU (256, 512, 1024, 2048, 4096)
memory = 1024 # 1 GB
execution_role_arn = aws_iam_role.ecs_execution.arn
task_role_arn = aws_iam_role.ecs_task.arn
container_definitions = jsonencode([...])
}
The direct fact is requires_compatibilities = ["FARGATE"], network_mode = "awsvpc", and task-level cpu = 512 and memory = 1024 with a comment listing valid CPU values. The impact layer is that Fargate enforces awsvpc networking and restricts CPU/memory combinations; specifying them in Terraform prevents runtime validation failures and ensures the task can be scheduled. The contextual layer links to the summary statement that for Fargate use network_mode = "awsvpc" and choose from valid CPU/memory combinations.
A structured view of the Fargate sizing reference mentioned in the material is:
| Parameter | Example Value | Note |
| cpu | 512 | 0.5 vCPU |
| cpu options | 256, 512, 1024, 2048, 4096 | Valid vCPU sizes |
| memory | 1024 | 1 GB |
| networkmode | awsvpc | Required for Fargate |
| requirescompatibilities | FARGATE | Launch type constraint |
Container Definition Composition
Container definitions specify name, image, essential flag, port mappings, logging, environment and secrets.
The example container definition includes:
hcl
{
name = "app"
image = "${var.ecr_repository_url}:${var.image_tag}"
essential = true
portMappings = [
{
containerPort = 8080
hostPort = 8080
protocol = "tcp"
}
]
logConfiguration = {
logDriver = "awslogs"
options = {
"awslogs-group" = aws_cloudwatch_log_group.app.name
"awslogs-region" = var.aws_region
"awslogs-stream-prefix" = "app"
}
}
environment = [
{ name = "PORT", value = "8080" },
{ name = "NODE_ENV", value = var.environment },
{ name = "LOG_LEVEL", value = "info" }
]
secrets = [
{
name = "DATABASE_URL"
valueFrom = aws_ssm_parameter.db_url.arn
},
{
name = "API_KEY"
valueFrom = "${aws_secretsmanager_secret.api_key.arn}:api_key::"
}
]
healthCheck = {
command = ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"]
interval = 30
timeout = 5
retries =
}
The direct fact is the presence of portMappings with containerPort 8080 and hostPort 8080, logConfiguration with awslogs driver, environment variables for PORT, NODEENV, LOGLEVEL, secrets referencing SSM Parameter Store and Secrets Manager, and a healthCheck with CMD-SHELL curl. The impact layer is that port mappings enable service discovery and load balancer integration, logging configuration ensures CloudWatch capture, and secrets avoid credential exposure in environment variables. The contextual layer connects to AWS Services Integration where connecting to Secrets Manager for sensitive data and enabling CloudWatch logging and metrics are highlighted as core capabilities.
Logging and CloudWatch Integration
Log configuration is consistently expressed as:
hcl
logConfiguration = {
logDriver = "awslogs"
options = {
"awslogs-group" = var.log_group
"awslogs-region" = var.region
"awslogs-stream-prefix" = var.log_stream_prefix
}
}
The direct fact is the use of logDriver awslogs with options for group, region and stream prefix. The impact layer is centralized log aggregation, metric filtering and alerting without sidecar containers. The contextual layer ties to the summary that ECS task definitions in Terraform specify everything about how containers run including logging, and that enabling CloudWatch logging is a recommended integration.
Secrets Management with Secrets Manager and SSM Parameter Store
Secrets are injected via the secrets block:
hcl
secrets = [
{
name = "DATABASE_URL"
valueFrom = aws_ssm_parameter.db_url.arn
},
{
name = "API_KEY"
valueFrom = "${aws_secretsmanager_secret.api_key.arn}:api_key::"
}
]
The direct fact is that DATABASEURL is sourced from an SSM parameter ARN and APIKEY from a Secrets Manager secret ARN with a JSON key path. The impact layer is that credentials never appear in plaintext environment variables or task definition JSON, reducing risk of leakage in state files or logs. The contextual layer reinforces the best-practice guidance to use SSM Parameter Store or Secrets Manager for sensitive values and never put credentials in environment variables directly.
Health Checks and Port Mappings
Health check definition:
hcl
healthCheck = {
command = ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"]
interval = 30
timeout = 5
retries =
}
Port mappings define containerPort 8080, hostPort 8080, protocol tcp. The direct fact is the health check command, interval 30, timeout 5 and the port mapping values. The impact layer is that ECS can detect unhealthy containers and trigger replacement, improving availability. The contextual layer links health checks to service stability and to ALB integration where the container port must match target group configuration.
Task Definition Versioning and Outputs
Each change to a task definition creates a new revision and old revisions are kept.
Outputs provided in the material:
hcl
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
}
output "task_definition_family" {
description = "Task definition family name"
value = aws_ecs_task_definition.app.family
}
output "container_name" {
description = "Main container name"
value = "app"
}
output "container_port" {
description = "Main container port"
value = 8080
}
The direct fact is that Terraform outputs the ARN with revision, revision number, family name, container name and container port. The impact layer is that downstream services and CI pipelines can reference the exact revision, enabling reproducible deployments. The contextual layer connects to the statement that services reference the task definition family and you control whether they use the latest revision or a specific one.
ECS Service Coupling and Desired Count
Service definition example:
hcl
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"
}
The direct fact is service name mytask, cluster ARN, taskdefinition referencing the task definition ARN, desiredcount 1, networkconfiguration with subnets, launch_type FARGATE. The impact layer is that the service manages task count, scaling policies and integration with ALBs while delegating container behavior to the task definition. The contextual layer shows that ECS Services manage number of tasks, scaling policies and integration with ALBs as described in the best-practices material.
Terraform Module Approaches for Task Definitions
Module documentation describes a Terraform module for creating Amazon ECS Task Definitions.
Inputs table from the material:
| Name | Description | Type | Default | Required |
| project | Project name | string | - | yes |
| environment | Environment name | string | - | yes |
| service | Service name | string | - | yes |
| executionrolearn | ARN of execution role | string | - | yes |
| cpu | CPU size | number | 256 | no |
| memory | Memory size | number | 512 | no |
| containerdefinitions | JSON formatted container definitions | string | - | yes |
| taskrole_policy | Inline policy for task role | string | "" | no |
Outputs table:
| Name | Description |
| taskdefinitionarn | The ARN of the ECS task definition |
| taskrolearn | ARN of the ECS task IAM role this module created, or null when taskrolepolicy was empty |
The direct fact is the module requires project, environment, service and executionrolearn, supports optional cpu and memory defaults, and creates a task definition with optional task role policy. The impact layer is standardized task definition creation across teams with enforced naming conventions. The contextual layer connects to the motivation to have Terraform generate valid task definitions dynamically and update ECS task definition and trigger new service deployments automatically.
The TerraformFoundation module notes compatibility requires Terraform >= v0.12.x and uses parameters matching ContainerDefinition object.
The Opstimus module notes migration to monorepo at opstimus/terraform-modules at modules/aws-task-definition with source git::https://github.com/opstimus/terraform-modules.git//modules/aws-task-definition?ref=aws-task-definition/v2.1.0. Requirements include terraform >= 1.3.0 and aws >= 6.0.
Consul Mesh Migration Considerations
Migration guidance states:
To migrate existing tasks to Consul, rewrite existing Terraform code for tasks so container definitions include the mesh-task module. Tasks must already be defined in Terraform using the ecstaskdefinition resource so they can be converted to use the mesh-task module.
Existing task definition example:
hcl
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 = []
}]
)
}
The direct fact is the task definition with cpu 256 memory 512, FARGATE compatibility, awsvpc network mode, container port 9090, and container-level cpu 0. The impact layer is that existing definitions must be Terraform-managed before adding Consul sidecar containers. The contextual layer shows replacement of awsecstask_definition resource with mesh-task module so Consul adds necessary dataplane containers.
Best Practices and Operational Impact
Deploying ECS services using Terraform allows modular, reusable, parameter-driven infrastructure. Creating a robust ECS deployment involves repetitive configurations and complex setups.
The material highlights simplifying creation of reusable ECS task definitions, passing dynamic variables to tasks and services, and seamless integration with AWS services like Secrets Manager and CloudWatch.
The impact of these practices is reduced configuration drift, faster environment provisioning, and auditable changes. The contextual layer ties parameterized definitions, variable reuse, secrets integration, logging, health checks and module encapsulation into a cohesive workflow where task definition changes automatically trigger service updates and new revisions are tracked via outputs.
Conclusion
Task definition management in Terraform for ECS Fargate is a composition of compatibility constraints, resource sizing, IAM wiring, container definition details and lifecycle outputs. The reference material demonstrates parameterized definitions with variables for family, container name, image, CPU and memory, and reusable log configuration. It shows explicit Fargate requirements of requirescompatibilities FARGATE and networkmode awsvpc, with valid CPU values such as 256, 512, 1024, 2048, 4096. Container definitions include port mappings, awslogs configuration, environment variables, secrets sourced from SSM Parameter Store and Secrets Manager, and health checks. Versioning behavior retains old revisions and exposes ARN, revision number, family and container metadata via outputs. Services reference the task definition ARN and manage desired count and networking. Module approaches standardize inputs for project, environment, service, executionrolearn, cpu, memory, containerdefinitions and optional taskrole_policy. Migration to Consul mesh requires pre-existing Terraform-managed task definitions and replacement with a mesh-task module. Together these patterns enable declarative, auditable and reusable ECS deployments.