Terraform aws_ecs_task_definition Resource for Amazon ECS Fargate Task Blueprints

A task definition is the blueprint for containers on ECS. It defines which container images to run, how much CPU and memory to allocate, what ports to expose, where to send logs, and how to inject environment variables and secrets. Every time ECS runs a task, it uses a task definition to know what to do. Getting the task definition right is important because mistakes here cascade into everything else - services that will not start, containers that run out of memory, or applications that cannot reach their dependencies.

The Terraform resource aws_ecs_task_definition manages an Ecs Task Definition resource. A minimal configuration to get started is shown in reference material. Refer to the Terraform Registry docs for all available arguments.

hcl resource "aws_ecs_task_definition" "example" { name = "my-ecs-task-definition" }

This guide covers creating task definitions in Terraform for Fargate, including single and multi-container tasks, secrets management, health checks, and resource sizing.

Basic Single Container Fargate Task Definition

The canonical Fargate example uses family, requirescompatibilities, networkmode, cpu, memory, executionrolearn, taskrolearn and container_definitions.

hcl resource "aws_ecs_task_definition" "app" { family = "myapp" requires_compatibilities = ["FARGATE"] network_mode = "awsvpc" cpu = 512 memory = 1024 execution_role_arn = aws_iam_role.ecs_execution.arn task_role_arn = aws_iam_role.ecs_task.arn container_definitions = jsonencode([ { 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 = } } ]) }

For Fargate, use networkmode = "awsvpc". The network mode choice directly determines how ENI attachment and security group isolation work for the task. Using awsvpc for Fargate ensures each task receives its own elastic network interface and can be reached via security groups. An incorrect networkmode prevents the service from being created and results in deployment failures with no clear indication of the root cause until the service scheduler is inspected.

The container_definitions block is JSON encoded because Terraform expects a single string argument. Encoding keeps the nested map structure intact for the AWS API. The impact is that any syntax error in the JSON encoding causes a plan failure rather than a runtime failure. The encoding step also means Terraform cannot perform deep diffs on individual container fields, so changes trigger a new task definition revision even for small edits.

Resource Sizing CPU and Memory Allocation

Task-level resources are set at the task definition level for Fargate. The reference example uses cpu = 512 and memory = 1024 with a comment 0.5 vCPU and 1 GB. Acceptable values referenced are 256, 512, 1024, 2048, 4096 for CPU.

Container-level CPU and memory can be further split inside the task. In the multi-container example the task is cpu 1024 memory 2048 while the main application container receives cpu 768 memory 1536 and the sidecar receives cpu 256 memory 512. This split demonstrates that the sum of container allocations should fit within the task allocation.

Incorrect sizing leads to OOM kills, throttling, or failed service deployments. Over allocation wastes cost because Fargate pricing is per vCPU and memory per second. Under allocation causes the health check to fail repeatedly and the container to be marked unhealthy.

Example Task CPU Task Memory Container CPU Container Memory
Single app Fargate 512 1024 - -
Multi app Fargate 1024 2048 768 1536
Datadog sidecar - - 256 512
Mongo module - - - 512

IAM Roles Execution and Task

The task definition references executionrolearn and taskrolearn. The execution role allows ECS to pull images from ECR, write logs to CloudWatch, and read secrets. The task role provides the containers with AWS permissions at runtime.

Binding roles at the task definition level means every container in the task inherits the ability to assume the task role. If the role is missing or has insufficient permissions, containers start but fail to access AWS services, producing silent failures in application logs. The impact is especially visible with secrets retrieval and log delivery.

Container Definitions Encoding and Attributes

Container definitions contain name, image, essential, portMappings, logConfiguration, environment, secrets, healthCheck.

Essential true means the task stops if the container exits. The main app container is essential true. A sidecar can be essential false so the task continues if the sidecar crashes. This pattern is used for monitoring agents that should not take down the application.

Port mappings define containerPort 8080 hostPort 8080 protocol tcp. For Fargate hostPort must match containerPort. Mismatch causes task start failures.

Logging uses logDriver awslogs with options awslogs-group, awslogs-region, awslogs-stream-prefix. This routes stdout and stderr to CloudWatch Logs. Without correct log configuration, troubleshooting becomes impossible because no logs are emitted to a central store.

Environment Variables and Secrets Injection

Environment variables are provided as a list of name value pairs. The example includes PORT 8080, NODEENV var.environment, LOGLEVEL info.

Secrets are injected via the secrets block. The example uses name DATABASEURL valueFrom awsssmparameter.dburl.arn and name APIKEY valueFrom "${awssecretsmanagersecret.apikey.arn}:api_key::". Use SSM Parameter Store or Secrets Manager for sensitive values - never put credentials in environment variables directly.

Injecting secrets via valueFrom keeps secrets out of plaintext in the task definition and out of the environment variable surface visible to inspect tools. The impact is reduced credential exposure in CloudTrail and task definition history. Storing secrets in environment variables would cause them to be visible in the ECS console and in Terraform state.

Health Check Configuration

Health checks are defined inside the container definition. The example command is ["CMD-SHELL", "curl -f http://localhost:8080/health || exit 1"] with interval 30 and timeout 5.

Health checks determine when ECS considers the container healthy for service load balancing. A too aggressive interval causes flapping. A too long timeout delays detection of failures. The health check command must succeed with exit code 0. A failing health check causes the container to be marked unhealthy and replaced by the service scheduler.

Task Definition Versioning and Outputs

Each time you change a task definition, Terraform creates a new revision. The old revisions are kept.

Outputs referenced include taskdefinitionarn, taskdefinitionrevision, taskdefinitionfamily, containername, containerport.

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 }

Services reference the task definition family, and you control whether they use the latest revision or a specific one. Keeping revisions immutable allows safe rollbacks. The ARN includes the revision number, so pinning to a revision guarantees reproducible deployments. Changing the family name creates a new family and breaks existing service references.

Multi Container Tasks and Sidecar Patterns

Common patterns include an application container with a sidecar for logging, proxying, or metrics.

hcl resource "aws_ecs_task_definition" "multi_container" { family = "myapp-multi" requires_compatibilities = ["FARGATE"] network_mode = "awsvpc" cpu = 1024 memory = 2048 execution_role_arn = aws_iam_role.ecs_execution.arn task_role_arn = aws_iam_role.ecs_task.arn container_definitions = jsonencode([ { name = "app" image = "${var.ecr_repository_url}:${var.image_tag}" essential = true cpu = 768 memory = 1536 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" } ] dependsOn = [{ containerName = "datadog-agent" condition = "START" }] }, { name = "datadog-agent" image = "public.ecr.aws/datadog/agent:latest" essential = false cpu = 256 memory = 512 environment = [ { name = "ECS_FARGATE", value = "true" }, { name = "DD_APM_ENABLED", value = "true" } ] secrets = [ { name = "DD_API_KEY" valueFrom = aws_ssm_parameter.dd_api_key.arn } ] logConfiguration = { logDriver = "awslogs" options = { "awslogs-group" = aws_cloudwatch_log_group.app.name "awslogs-region" = } } } ]) }

dependsOn with condition START ensures the app container waits for the sidecar to start. Essential false for the sidecar prevents task failure if the sidecar crashes. The sidecar shares the awsvpc network namespace, allowing localhost communication without extra networking.

Multi-container tasks let you run sidecars for monitoring, logging, or proxying alongside your application. The impact is tighter coupling of lifecycle and shared resources. The cost is increased task size and more complex CPU and memory budgeting.

Module Based Task Definition Generation

A Terraform module for creating Amazon ECS Task Definitions exists. THIS MODULE IS NOT COMPATIBLE WITH VERSIONS OF TERRAFORM LESS THAN v0.12.x.

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.

Features include Have Terraform generate valid task definitions dynamically and Update the ECS task definition and trigger new service deployments automatically.

The module uses the same parameters as the ContainerDefinition object.

Example invocation:

hcl provider "aws" {} module "mongo-task-definition" { source = "github.com/mongodb/terraform-aws-ecs-task-definition" family = "mongo" image = "mongo:3.6" memory = 512 name = "mongo" portMappings = [ { containerPort = 27017 }, ] }

Invoking terraform init and terraform apply creates an ECS task definition with the following containerDefinitions:

[
{
"command": null,
"cpu": null,
"disableNetworking": false,
"dnsSearchDomains": null,
"dnsServers": null,
"dockerLabels": null,
"dockerSecurityOptions": null,
"entryPoint": null,
"environment": null,
"essential": true,
"extraHosts": null,
"healthCheck": null,
"hostname": null,
"image": "mongo:3.6",
"interactive": false,
"links": null,
"linuxParameters": null,
"logConfiguration": null,
"memory": 512,
"memoryReservation": null,
"mountPoints": null,
"name": "mongo",
"portMappings": [{"containerPort":27017}],
"privileged": false,
"pseudoTerminal": false,
"readonlyRootFilesystem": false,
"repositoryCredentials": null,
"resourceRequirements": null,
"secrets": null,
"systemControls": null,
"ulimits": null,
"user": null,
"volumesFrom": null,
"workingDirectory": null
}
]

By default, this module creates a task definition with a single container definition.

Using a module reduces duplication and enforces consistent defaults across services. The trade-off is abstraction hiding, which can make debugging container definition fields harder because they are generated dynamically.

Minimal Declaration and Summary

ECS task definitions in Terraform specify everything about how your 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.

The minimal resource declaration shows the name attribute is sufficient to start. Real workloads require family, compatibility, network mode, CPU, memory, roles, and container definitions.

Conclusion

The awsecstaskdefinition resource is the control plane for container execution characteristics in ECS. The family identifies the task definition lineage and revisions provide immutable versioning. CPU and memory set the Fargate billable footprint and must align with container level allocations. Requirescompatibilities FARGATE and network_mode awsvpc lock the task into Fargate networking semantics. Execution and task roles govern image pulling and runtime AWS API access. Container definitions encoded via jsonencode carry the operational details of image, ports, logging, environment, secrets, health checks, and dependencies. Secrets via SSM Parameter Store or Secrets Manager keep sensitive data out of state and environment. Health checks provide liveness signals to the service scheduler. Multi-container definitions enable sidecar patterns with essential flags and dependsOn ordering. Module based generation abstracts boilerplate but follows the same ContainerDefinition parameter surface. Outputs expose ARN, revision, family, container name and port for downstream service wiring. Changes create new revisions rather than mutating existing ones, enabling safe rollouts and rollbacks while services continue to reference a stable family.

Sources

  1. Create ECS Task Definitions in Terraform
  2. Terraform ECS Task Definition
  3. Terraform AWS ECS Task Definition Module

Related Posts