Ecs service
Datadog Monitoring Configuration
This module provides comprehensive, granular control over Datadog monitoring with independent configuration for logs, APM, metrics, and container monitoring.
Quick Start
Enable all monitoring (backward compatible):
module "my_service" {
source = "./modules/ecs_service"
dd_enabled = true # Enables logs, APM, metrics, and container monitoring
# ... other variables
}
Granular Control
Control each monitoring feature independently by setting dd_enabled = true and then selectively disabling features:
Logs only (no agent):
dd_enabled = true
dd_agent_apm_enabled = false
dd_agent_metrics_enabled = false
dd_agent_container_monitoring_enabled = false
# Result: Only Fluent Bit sidecar (logs to Datadog), no agent container
APM + Logs:
dd_enabled = true
dd_agent_apm_enabled = true
dd_agent_metrics_enabled = false
dd_agent_container_monitoring_enabled = false
# Result: Fluent Bit + Datadog agent (APM traces on port 8126)
Metrics only (no logs):
dd_enabled = true
dd_logs_enabled = false
dd_agent_apm_enabled = false
dd_agent_metrics_enabled = true
dd_agent_container_monitoring_enabled = false
# Result: Only Datadog agent (DogStatsD on port 8125), logs go to CloudWatch
Full observability:
Architecture
When monitoring is enabled, the module deploys sidecar containers:
Fluent Bit (dd_logs_enabled = true):
- Collects and forwards logs to Datadog
- Supports custom configuration via S3 (dd_fluentbit_s3_config)
- Resource allocation: 256 CPU / 256 MB
- Independent of the Datadog agent
Datadog Agent (enabled when any agent feature is true):
- APM (dd_agent_apm_enabled): Distributed tracing on port 8126
- Metrics (dd_agent_metrics_enabled): DogStatsD on port 8125
- Container Monitoring (dd_agent_container_monitoring_enabled): Container-level metrics
- Resource allocation: 256 CPU / 256 MB
- Only deployed if at least one feature is enabled
Configuration Variables
| Variable | Description | Default |
|---|---|---|
dd_enabled |
Master switch. When false, all monitoring disabled. When true, enables all features unless explicitly disabled. | false |
dd_logs_enabled |
Enable log forwarding via Fluent Bit | true |
dd_agent_apm_enabled |
Enable APM (Application Performance Monitoring) | true |
dd_agent_metrics_enabled |
Enable metrics collection via DogStatsD | true |
dd_agent_container_monitoring_enabled |
Enable container-level monitoring | true |
dd_fluentbit_s3_config |
S3 ARN for custom Fluent Bit config | null |
dd_site |
Datadog site (e.g., datadoghq.eu) |
"datadoghq.eu" |
dd_api_key_parameter_name |
SSM parameter with Datadog API key | "/datadog/DD_API_KEY" |
Resource Sizing
The module automatically adjusts task CPU/memory:
- Base: Application container resources (container_cpu + container_memory)
- +256 CPU / +256 MB if Fluent Bit enabled
- +256 CPU / +256 MB if Datadog agent enabled
- Selects smallest valid Fargate CPU/memory combination
Bridge Network Mode and Service Connect
This module supports both awsvpc and bridge network modes for ECS tasks. Bridge mode is useful when running multiple services on a single EC2 instance to avoid ENI limits.
Network Mode Variables
| Variable | Description | Default |
|---|---|---|
network_mode |
Docker network mode (awsvpc or bridge) |
awsvpc |
enable_service_connect |
Enable ECS Service Connect for service discovery | false |
service_connect_namespace_arn |
ARN of the Cloud Map namespace for Service Connect | null |
Bridge Mode Configuration
When using bridge mode:
- Port mappings automatically use dynamic host ports (hostPort = 0)
- ALB target groups use instance target type instead of ip
- Service Connect is required for inter-service communication
- Subnet and security group configurations are not applied to tasks (they use the EC2 host's network)
Example: Bridge Mode with Service Connect
module "my_service" {
source = "./modules/ecs_service"
service_name = "my-service"
cluster_name = module.ecs_cluster.cluster_name
# Bridge mode configuration
network_mode = "bridge"
enable_service_connect = true
service_connect_namespace_arn = module.ecs_cluster.namespace_arn
# EC2 capacity provider
capacity_provider_strategy = {
ec2 = {
capacity_provider = module.ecs_cluster.ec2_capacity_provider_name
weight = 100
}
}
# Other required variables...
container_image = "my-image:latest"
service_port = 8080
subnet_ids = var.private_subnet_ids
security_group_ids = [var.security_group_id]
vpc_id = var.vpc_id
env = var.environment
alb_listener = var.alb_listener_arn
alb_security_group_id = var.alb_security_group_id
alb_dns_name = var.alb_dns_name
alb_zone_id = var.alb_zone_id
hosted_zone_name = var.hosted_zone_name
}
Service Connect Benefits
ECS Service Connect provides: - Transparent service-to-service communication via Envoy proxy sidecars - Same service URLs work in both Fargate (awsvpc) and EC2 (bridge) modes - No application code changes required when switching between modes - Built-in load balancing and health checking for service mesh
Modules
| Name | Source | Version |
|---|---|---|
| ecs_service | terraform-aws-modules/ecs/aws//modules/service | 6.2.1 |
Inputs
| Name | Description | Type | Default | Required |
|---|---|---|---|---|
| additional_container_definitions | Additional container definitions to add to the task definition | map(object({ |
{} |
no |
| alb_dns_name | The DNS name of the ALB. Only needed if create_record is true | string |
n/a | yes |
| alb_listener | The ARN of the ALB listener | string |
n/a | yes |
| alb_security_group_id | The security group ID of the ALB | string |
n/a | yes |
| alb_zone_id | The Route 53 zone ID of the ALB. Only needed if create_record is true | string |
n/a | yes |
| auto_discover_secrets_from_additional_containers | Automatically discover and grant task execution role access to secrets used in additional_container_definitions | bool |
true |
no |
| autoscaling_max_capacity | The maximum number of tasks to run | number |
10 |
no |
| autoscaling_min_capacity | The minimum number of tasks to run | number |
1 |
no |
| autoscaling_policies | Map of autoscaling policies to create for the service | any |
{ |
no |
| capacity_provider_strategy | Cluster-level strategy enforcement | map(object({ |
null |
no |
| cluster_name | The name of the ECS cluster | string |
n/a | yes |
| command | The command to run in the container | list(string) |
[] |
no |
| container_cpu | The number of CPU units to reserve for the container | number |
256 |
no |
| container_health_check | Health check configuration for the container | object({ |
null |
no |
| container_image | The container image to use | string |
n/a | yes |
| container_memory | The amount of memory to reserve for the container | number |
256 |
no |
| container_runtime_user | The user to run the container as (set in container definition). | string |
null |
no |
| cpu_architecture | The CPU architecture of the container | string |
"ARM64" |
no |
| create | Whether to create the ECS service | bool |
true |
no |
| create_listener_rule | Whether to create a listener rule for the service | bool |
true |
no |
| create_record | Whether to create a Route 53 record for the service | bool |
true |
no |
| dd_agent_apm_enabled | Whether to enable Datadog APM (Application Performance Monitoring) for distributed tracing. Only applies when dd_enabled is true. | bool |
true |
no |
| dd_agent_container_monitoring_enabled | Whether to enable Datadog container-level monitoring (CPU, memory, network metrics). Only applies when dd_enabled is true. | bool |
true |
no |
| dd_agent_metrics_enabled | Whether to enable Datadog metrics collection via DogStatsD. Only applies when dd_enabled is true. | bool |
true |
no |
| dd_api_key_parameter_name | The name of the Parameter Store parameter containing the Datadog API key | string |
"/datadog/DD_API_KEY" |
no |
| dd_apm_enabled | DEPRECATED: Use dd_agent_apm_enabled instead. Kept for backward compatibility. | bool |
true |
no |
| dd_apm_ignore_resources_string | Datadog APM ignore resources string, e.g. 'GET /healthcheck | string |
"" |
no |
| dd_enabled | Master switch for Datadog monitoring. When true, enables all monitoring features (logs, APM, metrics, container monitoring) unless explicitly disabled via granular flags. When false, all monitoring is disabled regardless of granular flag settings. | bool |
false |
no |
| dd_fluentbit_base_config | Base Fluent Bit configuration file path. Defaults to built-in parse-json.conf | string |
"/fluent-bit/configs/parse-json.conf" |
no |
| dd_fluentbit_enable_ecs_log_metadata | Enable ECS log metadata in Fluent Bit. Only applies when using custom Fluent Bit config. | bool |
true |
no |
| dd_fluentbit_s3_config | S3 ARN for custom Fluent Bit configuration file (e.g., arn:aws:s3:::bucket-name/path/to/fluentbit.conf). When provided, uses init-image and downloads config from S3 before starting Fluent Bit | string |
null |
no |
| dd_log_level | The log level for the Datadog agent | string |
"INFO" |
no |
| dd_logs_enabled | Whether to enable Datadog log forwarding via Fluent Bit. Only applies when dd_enabled is true. | bool |
true |
no |
| dd_site | The Datadog site to send data to | string |
"datadoghq.eu" |
no |
| dd_tags | Map of tags to apply to the Datadog agent | map(string) |
{} |
no |
| dependencies | List of dependencies for the ECS primary container | list(object({ |
[] |
no |
| deployment_controller | Configuration block for deployment controller configuration | object({ |
null |
no |
| deployment_maximum_percent | Upper limit (as a percentage of the service's desired_count) of the number of running tasks that can be running in a service during a deployment |
number |
200 |
no |
| deployment_minimum_healthy_percent | Lower limit (as a percentage of the service's desired_count) of the number of running tasks that must remain healthy during a deployment |
number |
66 |
no |
| desired_count | Desired count for service. If not set the autoscaling_min_capacity is used | number |
null |
no |
| enable_autoscaling | Whether to enable autoscaling for the ECS service | bool |
false |
no |
| enable_deployment_rollback | Whether to enable deployment rollback | bool |
true |
no |
| enable_execute_command | Whether to enable execute command for the ECS service | bool |
true |
no |
| enable_service_connect | Enable ECS Service Connect for service discovery (required for bridge mode inter-service communication) | bool |
false |
no |
| entry_point | The entry point for the container | list(string) |
[] |
no |
| env | The environment the service is running in | string |
n/a | yes |
| environment_files | List of S3 ARNs for environment files to load into the container | list(object({ |
[] |
no |
| environment_variables | Map of environment variables to set in the container | map(string) |
{} |
no |
| ephemeral_storage | The amount of ephemeral storage to allocate for the task. This parameter is used to expand the total amount of ephemeral storage available, beyond the default amount, for tasks hosted on AWS Fargate | object({ |
null |
no |
| extra_host_headers | Extra host headers to route to the service | list(string) |
[] |
no |
| extra_target_groups_count | How many extra target groups for this service should be provisioned. Useful for blue/green deployments via CodeDeploy | number |
0 |
no |
| force_new_deployment | Whether to force a new deployment of the ECS service | bool |
false |
no |
| healthcheck_matcher | The matcher to use for the healthcheck | string |
"200" |
no |
| healthcheck_path | The path to use for the healthcheck | string |
"/health" |
no |
| hosted_zone_name | The name of the Route 53 hosted zone. Only needed if create_record is true | string |
n/a | yes |
| hosts | The hosts to route to the service | list(string) |
[] |
no |
| ignore_task_definition_changes | Whether to ignore changes to the task definition | bool |
false |
no |
| ingress_container_name | the container name in the task that the ALB will route to. This container has to have a port-mapping with var.ingress_port. If not set, it will default to the service_port | string |
null |
no |
| ingress_port | the port in the task that the ALB will route to. This can be any port on any of the task's containers. If not set, it will default to the service_port | number |
null |
no |
| launch_type | Optional ECS launch type | string |
null |
no |
| mount_points | List of mount points to attach to the container | list(object({ |
[] |
no |
| network_mode | Docker network mode for the task (awsvpc or bridge). Bridge mode allows more tasks per EC2 instance but requires Service Connect for service discovery. | string |
"awsvpc" |
no |
| operating_system_family | Operating system family for the task definition and all containers | string |
"LINUX" |
no |
| parameter_store_secret_names | Map of Parameter Store secret names to attach to the ECS service | map(string) |
{} |
no |
| port_mappings | List of port mappings to attach to the container | list(object({ |
null |
no |
| secretsmanager_secret_names | Map of Secrets Manager secret names to attach to the ECS service | map(string) |
{} |
no |
| security_group_egress_rules | Security group egress rules to add to the security group created | map(object({ |
{} |
no |
| security_group_ids | The security groups to attach to the ECS service | list(string) |
n/a | yes |
| security_group_ingress_rules | Security group ingress rules to add to the security group created | map(object({ |
{} |
no |
| service_connect_namespace_arn | ARN of the Cloud Map namespace for Service Connect (required when enable_service_connect is true) | string |
null |
no |
| service_connect_namespace_name | Name of the Cloud Map namespace for Service Connect DNS resolution (e.g., 'genai-namespace'). Used to construct full DNS names like 'service.namespace'. | string |
null |
no |
| service_discovery_id | The ID of the service discovery service | string |
"" |
no |
| service_name | The name of the ECS service | string |
n/a | yes |
| service_port | The port the service listens on | number |
80 |
no |
| subnet_ids | The subnets to place the ECS service | list(string) |
n/a | yes |
| target_group_protocol_version | The protocol to use for the target group | string |
"HTTP1" |
no |
| task_exec_additional_secret_arns | Additional Secrets Manager secret ARNs to grant task execution role access to (in addition to those in secretsmanager_secret_names) | list(string) |
[] |
no |
| task_exec_additional_ssm_param_arns | Additional SSM Parameter Store ARNs to grant task execution role access to (in addition to those in parameter_store_secret_names) | list(string) |
[] |
no |
| tasks_iam_role_statements | A map of IAM policy statements for custom permission usage | list(object({ |
null |
no |
| volume | Map of volumes to attach to the ECS service | any |
{} |
no |
| vpc_id | The VPC ID | string |
n/a | yes |
Outputs
| Name | Description |
|---|---|
| ecs_service_arn | The arn of the ECS service |
| ecs_service_name | The name of the ECS service |
| security_group_ids | The security group IDs associated with the ECS service |
| service_security_group_id | The security group ID of the ECS service |
| service_target_group_arn | The arn of the created ALB target group |
| tasks_execution_iam_role_arn | The IAM execution role ARN for the ECS tasks |
| tasks_execution_iam_role_name | The IAM execution role name for the ECS tasks |
| tasks_iam_role_arn | The IAM execution role ARN for the ECS tasks. Use this to attache policies for other AWS services, e.g. access to S3 bucket. |
| tasks_iam_role_name | The IAM execution role name for the ECS tasks. Use this to attache policies for other AWS services, e.g. access to S3 bucket. |
IAM Permissions for Secrets
The module creates IAM policies with permissions ONLY for the secrets you use. No wildcards, no broad permissions.
1. Basic secrets (main container)
module "ecs_service" {
source = "./modules/ecs_service"
secretsmanager_secret_names = {
"DATABASE_PASSWORD" = aws_secretsmanager_secret.db_password.name
"API_KEY" = aws_secretsmanager_secret.api_key.name
}
parameter_store_secret_names = {
"REDIS_URL" = aws_ssm_parameter.redis_url.name
}
}
The module creates IAM permissions for these secrets automatically.
2. Automatic discovery (additional containers)
additional_container_definitions = {
datadog = {
image = "datadog/agent:latest"
secrets = [
{ name = "DD_API_KEY", valueFrom = "arn:aws:secretsmanager:us-east-1:123456789012:secret:datadog-key" }
]
}
}
The module finds secrets in additional_container_definitions automatically. IAM permissions are added for them.
To disable: auto_discover_secrets_from_additional_containers = false
3. Manual mode (advanced)
task_exec_additional_secret_arns = [
"arn:aws:secretsmanager:us-east-1:123456789012:secret:shared-secret"
]
task_exec_additional_ssm_param_arns = [
"arn:aws:ssm:us-east-1:123456789012:parameter/shared-param"
]
Use this when you need secrets that are not in the container definitions.