Skip to main content

ECS Task Definition Logging Enabled

Overview

This check verifies that all containers within your Amazon ECS task definitions have logging configured. Specifically, it ensures each container has a logConfiguration with a non-null logDriver in the latest active revision.

Container logging is essential for monitoring application behavior, troubleshooting issues, and maintaining security visibility across your containerized workloads.

Risk

Without container logging enabled:

  • Blind spots in security monitoring: Intrusions and data exfiltration can go undetected
  • No audit trail: Missing logs compromise compliance posture and make forensic investigations impossible
  • Slower incident response: Increased mean time to recovery (MTTR) when issues occur
  • Operational blindness: Application errors and performance issues are harder to diagnose

Severity: High

Remediation Steps

Prerequisites

  • Access to the AWS Console with permissions to modify ECS task definitions
  • An existing CloudWatch Log Group (or permission to create one)
IAM permissions needed

Your IAM user or role needs these permissions:

  • ecs:RegisterTaskDefinition
  • ecs:DescribeTaskDefinition
  • logs:CreateLogGroup (if creating a new log group)
  • iam:PassRole (if the task uses an execution role)

The ECS task execution role needs:

  • logs:CreateLogStream
  • logs:PutLogEvents

The managed policy AmazonECSTaskExecutionRolePolicy includes these permissions.

AWS Console Method

  1. Open the Amazon ECS console
  2. In the left navigation, click Task definitions
  3. Select the task definition you need to update
  4. Click Create new revision
  5. Scroll down to the Container definitions section
  6. For each container listed:
    • Click the container name to expand its settings
    • Scroll to Log configuration
    • For Log driver, select awslogs
    • Enter the following options:
      • awslogs-group: /ecs/<your-task-family> (create this log group first if it does not exist)
      • awslogs-region: us-east-1
      • awslogs-stream-prefix: ecs
  7. Click Create to register the new revision
  8. Update your ECS service to use the new task definition revision
AWS CLI (optional)

First, create a CloudWatch Log Group if you do not have one:

aws logs create-log-group \
--log-group-name /ecs/my-app \
--region us-east-1

Then register a new task definition revision with logging enabled:

aws ecs register-task-definition \
--family my-app \
--execution-role-arn arn:aws:iam::<account-id>:role/ecsTaskExecutionRole \
--network-mode awsvpc \
--requires-compatibilities FARGATE \
--cpu 256 \
--memory 512 \
--container-definitions '[
{
"name": "my-container",
"image": "nginx:latest",
"essential": true,
"portMappings": [
{
"containerPort": 80,
"protocol": "tcp"
}
],
"logConfiguration": {
"logDriver": "awslogs",
"options": {
"awslogs-group": "/ecs/my-app",
"awslogs-region": "us-east-1",
"awslogs-stream-prefix": "ecs"
}
}
}
]' \
--region us-east-1

Replace <account-id> with your AWS account ID and adjust the container configuration as needed.

To update an existing service to use the new revision:

aws ecs update-service \
--cluster my-cluster \
--service my-service \
--task-definition my-app \
--region us-east-1
CloudFormation (optional)
AWSTemplateFormatVersion: '2010-09-09'
Description: ECS Task Definition with logging enabled

Parameters:
TaskFamily:
Type: String
Default: my-app
Description: The family name for the task definition
ContainerImage:
Type: String
Default: nginx:latest
Description: The Docker image for the container

Resources:
LogGroup:
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub /ecs/${TaskFamily}
RetentionInDays: 30

TaskExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: ecs-tasks.amazonaws.com
Action: sts:AssumeRole
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy

TaskDefinition:
Type: AWS::ECS::TaskDefinition
Properties:
Family: !Ref TaskFamily
ExecutionRoleArn: !GetAtt TaskExecutionRole.Arn
NetworkMode: awsvpc
RequiresCompatibilities:
- FARGATE
Cpu: '256'
Memory: '512'
ContainerDefinitions:
- Name: !Ref TaskFamily
Image: !Ref ContainerImage
Essential: true
PortMappings:
- ContainerPort: 80
Protocol: tcp
LogConfiguration:
LogDriver: awslogs
Options:
awslogs-group: !Ref LogGroup
awslogs-region: !Ref AWS::Region
awslogs-stream-prefix: ecs

Outputs:
TaskDefinitionArn:
Description: The ARN of the task definition
Value: !Ref TaskDefinition
LogGroupName:
Description: The name of the CloudWatch log group
Value: !Ref LogGroup
Terraform (optional)
variable "task_family" {
description = "The family name for the task definition"
type = string
default = "my-app"
}

variable "container_image" {
description = "The Docker image for the container"
type = string
default = "nginx:latest"
}

resource "aws_cloudwatch_log_group" "ecs_logs" {
name = "/ecs/${var.task_family}"
retention_in_days = 30
}

resource "aws_iam_role" "ecs_task_execution" {
name = "${var.task_family}-execution-role"

assume_role_policy = jsonencode({
Version = "2012-10-17"
Statement = [
{
Effect = "Allow"
Principal = {
Service = "ecs-tasks.amazonaws.com"
}
Action = "sts:AssumeRole"
}
]
})
}

resource "aws_iam_role_policy_attachment" "ecs_task_execution" {
role = aws_iam_role.ecs_task_execution.name
policy_arn = "arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"
}

resource "aws_ecs_task_definition" "main" {
family = var.task_family
execution_role_arn = aws_iam_role.ecs_task_execution.arn
network_mode = "awsvpc"
requires_compatibilities = ["FARGATE"]
cpu = "256"
memory = "512"

container_definitions = jsonencode([
{
name = var.task_family
image = var.container_image
essential = true
portMappings = [
{
containerPort = 80
protocol = "tcp"
}
]
logConfiguration = {
logDriver = "awslogs"
options = {
"awslogs-group" = aws_cloudwatch_log_group.ecs_logs.name
"awslogs-region" = "us-east-1"
"awslogs-stream-prefix" = "ecs"
}
}
}
])
}

output "task_definition_arn" {
description = "The ARN of the task definition"
value = aws_ecs_task_definition.main.arn
}

output "log_group_name" {
description = "The name of the CloudWatch log group"
value = aws_cloudwatch_log_group.ecs_logs.name
}

Verification

After enabling logging, verify the configuration:

  1. Go to ECS > Task definitions in the AWS Console
  2. Select your task definition and view the latest revision
  3. Confirm each container shows awslogs as the log driver
  4. Run a task using the new definition
  5. Check CloudWatch > Log groups to confirm logs are appearing
CLI verification commands

Describe the task definition to verify logging configuration:

aws ecs describe-task-definition \
--task-definition my-app \
--query 'taskDefinition.containerDefinitions[*].{Name:name,LogDriver:logConfiguration.logDriver,LogGroup:logConfiguration.options."awslogs-group"}' \
--output table \
--region us-east-1

Check that logs are being written to CloudWatch:

aws logs describe-log-streams \
--log-group-name /ecs/my-app \
--order-by LastEventTime \
--descending \
--limit 5 \
--region us-east-1

Additional Resources

Notes

  • Task definition revisions are immutable: You cannot modify an existing revision. You must create a new revision with logging enabled, then update your services to use it.
  • Execution role required: When using the awslogs driver, your task needs an execution role with permissions to write to CloudWatch Logs. The managed policy AmazonECSTaskExecutionRolePolicy provides these permissions.
  • Log retention costs: CloudWatch Logs charges for storage. Set an appropriate retention period (e.g., 30 days) to manage costs.
  • Alternative log drivers: While awslogs is the most common choice, ECS also supports splunk, fluentd, gelf, and other drivers depending on your logging infrastructure.
  • All containers need logging: This check validates that every container in the task definition has logging configured, not just the primary container.