Skip to content

Commit ca25dae

Browse files
authored
ECS Fargate Terraform MVP #2
ECS Fargate Terraform MVP
2 parents 548d0b3 + e908cba commit ca25dae

File tree

19 files changed

+1028
-1
lines changed

19 files changed

+1028
-1
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ override.tf.json
3434

3535
# Ignore CLI configuration files
3636
.terraformrc
37+
.terraform.lock.hcl
3738
terraform.rc
3839

3940
# Ignore lockfiles

README.md

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,45 @@
1-
# terraform-ecs-datadog
1+
# Datadog Terraform module for AWS ECS Tasks
2+
3+
[![License](https://img.shields.io/badge/license-Apache--2.0-blue)](https://github.com/DataDog/terraform-aws-lambda-datadog/blob/main/LICENSE)
4+
5+
Use this Terraform module to install Datadog Monitoring for AWS Elastic Container Service tasks.
6+
7+
This Terraform module wraps the [aws_ecs_task_definition](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_task_definition) resource and automatically configures your task definition for Datadog Monitoring by:
8+
9+
* Adding the Datadog Agent container
10+
* Optionally, the Fluentbit log router
11+
* Optionally, the Cloud Workload Security tracer
12+
* Configuring application containers with necessary mounts and environment variables
13+
* Enabling the collection of metrics, traces, and logs to Datadog
14+
15+
## Usage
16+
17+
```hcl
18+
module "ecs_task" {
19+
source = "../../modules/ecs_fargate"
20+
21+
# Datadog Configuration
22+
dd_api_key_secret_arn = "arn:aws:secretsmanager:us-east-1:0000000000:secret:example-secret"
23+
dd_environment = [
24+
{
25+
name = "DD_TAGS",
26+
value = "team:cont-p, owner:container-monitoring"
27+
},
28+
]
29+
30+
# Task Configuration
31+
family = "example-app"
32+
container_definitions = {
33+
dogstatsd-app = {
34+
name = "datadog-dogstatsd-app",
35+
image = "ghcr.io/datadog/apps-dogstatsd:main",
36+
essential = false,
37+
},
38+
apm-app = {
39+
name = "datadog-apm-app",
40+
image = "ghcr.io/datadog/apps-tracegen:main",
41+
essential = false,
42+
}
43+
}
44+
}
45+
```

examples/ecs_fargate/README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# ECS Fargate Example
2+
3+
A simple ECS Fargate Task Definition with out of the box Datadog instrumentation.
4+
5+
## Usage
6+
7+
* Create a [Datadog API Key](https://app.datadoghq.com/organization-settings/api-keys)
8+
* Create a `terraform.tfvars` file
9+
* Set the `dd_api_key` to the Datadog API Key (required)
10+
* Set the `dd_service` to the name of the service you want to use to filter for the resource in Datadog
11+
* Set the `dd_site` to the [Datadog destination site](https://docs.datadoghq.com/getting_started/site/) for your metrics, traces, and logs
12+
* Run the following commands:
13+
14+
```bash
15+
terraform init
16+
terraform plan
17+
terraform apply
18+
```

examples/ecs_fargate/main.tf

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
################################################################################
2+
# Task Definition: Datadog Agent Example
3+
################################################################################
4+
5+
module "ecs_task" {
6+
source = "../../modules/ecs_fargate"
7+
8+
# Configure Datadog
9+
dd_api_key = var.dd_api_key
10+
dd_api_key_secret_arn = var.dd_api_key_secret_arn
11+
dd_site = var.dd_site
12+
dd_service = var.dd_service
13+
14+
dd_environment = [
15+
{
16+
name = "DD_TAGS",
17+
value = "team:cont-p, owner:container-monitoring"
18+
},
19+
]
20+
21+
# Configure Task Definition
22+
family = "datadog-terraform-app"
23+
container_definitions = {
24+
dogstatsd = {
25+
name = "datadog-dogstatsd-app",
26+
image = "ghcr.io/datadog/apps-dogstatsd:main",
27+
essential = false,
28+
},
29+
apm = {
30+
name = "datadog-apm-app",
31+
image = "ghcr.io/datadog/apps-tracegen:main",
32+
essential = false,
33+
},
34+
cws = {
35+
name = "datadog-cws-app",
36+
image = "public.ecr.aws/ubuntu/ubuntu:22.04_stable",
37+
essential = true,
38+
entryPoint = [
39+
"/usr/bin/bash",
40+
"-c",
41+
"cp /usr/bin/bash /tmp/malware; chmod u+s /tmp/malware; apt update;apt install -y curl wget; /tmp/malware -c 'while true; do wget https://google.com; sleep 60; done'"
42+
]
43+
}
44+
}
45+
requires_compatibilities = ["FARGATE"]
46+
}

examples/ecs_fargate/variables.tf

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
variable "dd_api_key" {
2+
description = "Datadog API Key"
3+
type = string
4+
default = null
5+
}
6+
7+
variable "dd_api_key_secret_arn" {
8+
description = "Datadog API Key Secret ARN"
9+
type = string
10+
default = null
11+
}
12+
13+
variable "dd_service" {
14+
description = "Service name for resource filtering in Datadog"
15+
type = string
16+
default = null
17+
}
18+
19+
variable "dd_site" {
20+
description = "Datadog Site"
21+
type = string
22+
default = "datadoghq.com"
23+
}

examples/ecs_fargate/versions.tf

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
terraform {
2+
required_version = ">= 1.5.0"
3+
4+
required_providers {
5+
aws = {
6+
source = "hashicorp/aws"
7+
version = ">= 5.77.0"
8+
}
9+
}
10+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
formatter: markdown table
2+
output:
3+
file: README.md
4+
mode: inject
5+
settings:
6+
anchor: true
7+
color: true
8+
default: true
9+
description: false
10+
escape: true
11+
hide-empty: false
12+
indent: 2
13+
required: true
14+
sensitive: true
15+
type: true

modules/ecs_fargate/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
docs:
2+
terraform-docs . --config .terraform-docs.yml

modules/ecs_fargate/README.md

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# Datadog ECS Fargate Terraform
2+
3+
<!-- BEGIN_TF_DOCS -->
4+
## Requirements
5+
6+
| Name | Version |
7+
|------|---------|
8+
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.5.0 |
9+
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 5.77.0 |
10+
11+
## Providers
12+
13+
| Name | Version |
14+
|------|---------|
15+
| <a name="provider_aws"></a> [aws](#provider\_aws) | 5.90.1 |
16+
17+
## Modules
18+
19+
No modules.
20+
21+
## Resources
22+
23+
| Name | Type |
24+
|------|------|
25+
| [aws_ecs_task_definition.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/ecs_task_definition) | resource |
26+
| [aws_iam_policy.dd_ecs_task_permissions](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
27+
| [aws_iam_policy.dd_secret_access](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
28+
| [aws_iam_role.ecs_task_execution](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
29+
| [aws_iam_role_policy_attachment.ecs_task_execution_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
30+
| [aws_iam_role_policy_attachment.existing_role_dd_secret](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
31+
| [aws_iam_role_policy_attachment.existing_role_ecs_task_permissions](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
32+
| [aws_iam_role_policy_attachment.new_role_dd_secret](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
33+
| [aws_iam_role_policy_attachment.new_role_ecs_task_permissions](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
34+
| [aws_iam_policy_document.dd_ecs_task_permissions](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
35+
| [aws_iam_policy_document.dd_secret_access](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
36+
| [aws_iam_role.ecs_task_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_role) | data source |
37+
38+
## Inputs
39+
40+
| Name | Description | Type | Default | Required |
41+
|------|-------------|------|---------|:--------:|
42+
| <a name="input_container_definitions"></a> [container\_definitions](#input\_container\_definitions) | A map of valid [container definitions](http://docs.aws.amazon.com/AmazonECS/latest/APIReference/API_ContainerDefinition.html). Please note that you should only provide values that are part of the container definition document | `any` | n/a | yes |
43+
| <a name="input_cpu"></a> [cpu](#input\_cpu) | Number of cpu units used by the task. If the `requires_compatibilities` is `FARGATE` this field is required | `number` | `512` | no |
44+
| <a name="input_dd_api_key"></a> [dd\_api\_key](#input\_dd\_api\_key) | Datadog API Key | `string` | `null` | no |
45+
| <a name="input_dd_api_key_secret_arn"></a> [dd\_api\_key\_secret\_arn](#input\_dd\_api\_key\_secret\_arn) | Datadog API Key Secret ARN | `string` | `null` | no |
46+
| <a name="input_dd_environment"></a> [dd\_environment](#input\_dd\_environment) | Datadog Agent container environment variables | `list(map(string))` | <pre>[<br/> {}<br/>]</pre> | no |
47+
| <a name="input_dd_image_version"></a> [dd\_image\_version](#input\_dd\_image\_version) | Datadog Agent image version | `string` | `"latest"` | no |
48+
| <a name="input_dd_registry"></a> [dd\_registry](#input\_dd\_registry) | Datadog Agent image registry | `string` | `"public.ecr.aws/datadog/agent"` | no |
49+
| <a name="input_dd_site"></a> [dd\_site](#input\_dd\_site) | Datadog Site | `string` | `"datadoghq.com"` | no |
50+
| <a name="input_enable_fault_injection"></a> [enable\_fault\_injection](#input\_enable\_fault\_injection) | Enables fault injection and allows for fault injection requests to be accepted from the task's containers | `bool` | `false` | no |
51+
| <a name="input_ephemeral_storage"></a> [ephemeral\_storage](#input\_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 | `any` | `{}` | no |
52+
| <a name="input_execution_role_arn"></a> [execution\_role\_arn](#input\_execution\_role\_arn) | ARN of the task execution role that the Amazon ECS container agent and the Docker daemon can assume | `string` | `null` | no |
53+
| <a name="input_family"></a> [family](#input\_family) | A unique name for your task definition | `string` | n/a | yes |
54+
| <a name="input_inference_accelerator"></a> [inference\_accelerator](#input\_inference\_accelerator) | Configuration block(s) with Inference Accelerators settings | `any` | `[]` | no |
55+
| <a name="input_ipc_mode"></a> [ipc\_mode](#input\_ipc\_mode) | IPC resource namespace to be used for the containers in the task The valid values are `host`, `task`, and `none` | `string` | `null` | no |
56+
| <a name="input_memory"></a> [memory](#input\_memory) | Amount (in MiB) of memory used by the task. If the `requires_compatibilities` is `FARGATE` this field is required | `number` | `1024` | no |
57+
| <a name="input_network_mode"></a> [network\_mode](#input\_network\_mode) | Docker networking mode to use for the containers in the task. Valid values are `none`, `bridge`, `awsvpc`, and `host` | `string` | `"awsvpc"` | no |
58+
| <a name="input_pid_mode"></a> [pid\_mode](#input\_pid\_mode) | Process namespace to use for the containers in the task. The valid values are `host` and `task` | `string` | `"task"` | no |
59+
| <a name="input_placement_constraints"></a> [placement\_constraints](#input\_placement\_constraints) | Configuration block for rules that are taken into consideration during task placement (up to max of 10). This is set at the task definition, see `placement_constraints` for setting at the service | <pre>list(object({<br/> type = string<br/> expression = string<br/> }))</pre> | `[]` | no |
60+
| <a name="input_proxy_configuration"></a> [proxy\_configuration](#input\_proxy\_configuration) | Configuration block for the App Mesh proxy | <pre>object({<br/> container_name = string<br/> properties = map(any)<br/> type = optional(string, "APPMESH")<br/> })</pre> | `null` | no |
61+
| <a name="input_requires_compatibilities"></a> [requires\_compatibilities](#input\_requires\_compatibilities) | Set of launch types required by the task. The valid values are `EC2` and `FARGATE` | `list(string)` | <pre>[<br/> "FARGATE"<br/>]</pre> | no |
62+
| <a name="input_runtime_platform"></a> [runtime\_platform](#input\_runtime\_platform) | Configuration block for `runtime_platform` that containers in your task may use | `any` | <pre>{<br/> "cpu_architecture": "X86_64",<br/> "operating_system_family": "LINUX"<br/>}</pre> | no |
63+
| <a name="input_skip_destroy"></a> [skip\_destroy](#input\_skip\_destroy) | Whether to retain the old revision when the resource is destroyed or replacement is necessary | `bool` | `false` | no |
64+
| <a name="input_tags"></a> [tags](#input\_tags) | A map of additional tags to add to the task definition/set created | `map(string)` | `{}` | no |
65+
| <a name="input_task_role_arn"></a> [task\_role\_arn](#input\_task\_role\_arn) | The ARN of the IAM role that allows your Amazon ECS container task to make calls to other AWS services | `string` | `null` | no |
66+
| <a name="input_track_latest"></a> [track\_latest](#input\_track\_latest) | Whether should track latest ACTIVE task definition on AWS or the one created with the resource stored in state | `bool` | `false` | no |
67+
| <a name="input_volume"></a> [volume](#input\_volume) | Configuration block for volumes that containers in your task may use | `any` | `{}` | no |
68+
69+
## Outputs
70+
71+
| Name | Description |
72+
|------|-------------|
73+
| <a name="output_arn"></a> [arn](#output\_arn) | Full ARN of the Task Definition (including both family and revision). |
74+
| <a name="output_arn_without_revision"></a> [arn\_without\_revision](#output\_arn\_without\_revision) | ARN of the Task Definition with the trailing revision removed. |
75+
| <a name="output_container_definitions"></a> [container\_definitions](#output\_container\_definitions) | A list of valid container definitions provided as a single valid JSON document. |
76+
| <a name="output_cpu"></a> [cpu](#output\_cpu) | Number of cpu units used by the task. |
77+
| <a name="output_enable_fault_injection"></a> [enable\_fault\_injection](#output\_enable\_fault\_injection) | Enables fault injection and allows for fault injection requests to be accepted from the task's containers. |
78+
| <a name="output_ephemeral_storage"></a> [ephemeral\_storage](#output\_ephemeral\_storage) | The amount of ephemeral storage to allocate for the task. |
79+
| <a name="output_execution_role_arn"></a> [execution\_role\_arn](#output\_execution\_role\_arn) | ARN of the task execution role. |
80+
| <a name="output_family"></a> [family](#output\_family) | A unique name for your task definition. |
81+
| <a name="output_inference_accelerator"></a> [inference\_accelerator](#output\_inference\_accelerator) | Inference accelerator settings. |
82+
| <a name="output_ipc_mode"></a> [ipc\_mode](#output\_ipc\_mode) | IPC resource namespace to be used for the containers. |
83+
| <a name="output_memory"></a> [memory](#output\_memory) | Amount (in MiB) of memory used by the task. |
84+
| <a name="output_network_mode"></a> [network\_mode](#output\_network\_mode) | Docker networking mode to use for the containers. |
85+
| <a name="output_pid_mode"></a> [pid\_mode](#output\_pid\_mode) | Process namespace to use for the containers. |
86+
| <a name="output_placement_constraints"></a> [placement\_constraints](#output\_placement\_constraints) | Rules that are taken into consideration during task placement. |
87+
| <a name="output_proxy_configuration"></a> [proxy\_configuration](#output\_proxy\_configuration) | Configuration block for the App Mesh proxy. |
88+
| <a name="output_requires_compatibilities"></a> [requires\_compatibilities](#output\_requires\_compatibilities) | Set of launch types required by the task. |
89+
| <a name="output_revision"></a> [revision](#output\_revision) | Revision of the task in a particular family. |
90+
| <a name="output_runtime_platform"></a> [runtime\_platform](#output\_runtime\_platform) | Runtime platform configuration for the task definition. |
91+
| <a name="output_skip_destroy"></a> [skip\_destroy](#output\_skip\_destroy) | Whether to retain the old revision when the resource is destroyed or replacement is necessary. |
92+
| <a name="output_tags"></a> [tags](#output\_tags) | Key-value map of resource tags. |
93+
| <a name="output_tags_all"></a> [tags\_all](#output\_tags\_all) | Map of tags assigned to the resource, including inherited tags. |
94+
| <a name="output_task_role_arn"></a> [task\_role\_arn](#output\_task\_role\_arn) | ARN of IAM role that allows your Amazon ECS container task to make calls to other AWS services. |
95+
| <a name="output_track_latest"></a> [track\_latest](#output\_track\_latest) | Whether should track latest ACTIVE task definition on AWS or the one created with the resource stored in state. |
96+
| <a name="output_volume"></a> [volume](#output\_volume) | Configuration block for volumes that containers in your task may use. |
97+
<!-- END_TF_DOCS -->

modules/ecs_fargate/datadog.tf

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
locals {
2+
base_env = var.dd_environment
3+
4+
dynamic_env = [
5+
for pair in [
6+
{ key = "DD_API_KEY", value = var.dd_api_key },
7+
{ key = "DD_SITE", value = var.dd_site },
8+
{ key = "DD_SERVICE", value = var.dd_service },
9+
{ key = "DD_ENV", value = var.dd_env },
10+
{ key = "DD_VERSION", value = var.dd_version },
11+
# TODO: clusterName, ddTags, etc.
12+
] : { name = pair.key, value = pair.value } if pair.value != null
13+
]
14+
15+
dd_agent_env = concat(local.base_env, local.dynamic_env)
16+
17+
# Datadog Agent container definition
18+
dd_agent_container = {
19+
name = "datadog-agent"
20+
image = "${var.dd_registry}:${var.dd_image_version}"
21+
environment = local.dd_agent_env
22+
secrets = var.dd_api_key_secret_arn != null ? [
23+
{
24+
name = "DD_API_KEY"
25+
valueFrom = var.dd_api_key_secret_arn
26+
}
27+
] : []
28+
portMappings = [
29+
{
30+
containerPort = 8125
31+
hostPort = 8125
32+
protocol = "udp"
33+
},
34+
{
35+
containerPort = 8126
36+
hostPort = 8126
37+
protocol = "tcp"
38+
}
39+
]
40+
}
41+
}

0 commit comments

Comments
 (0)