Terraform Amazon ECS Task Definition Patterns for Fargate Deployments

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 |
| requires
compatibilities | 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 |
| task
role_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.

Sources

  1. Best practices for ECS deployment using Terraform
  2. Create ECS task definitions in Terraform
  3. Register existing ECS tasks with Terraform
  4. Terraform Foundation ECS Task Definition Module
  5. Opstimus Terraform AWS Task Definition

Related Posts