Skip to content

Commit f8f97a8

Browse files
authored
feat: add log forwarder cf (#6)
* feat: add log forwarder cf * fix: update version refs and readme
1 parent 4dfd090 commit f8f97a8

File tree

14 files changed

+330
-10
lines changed

14 files changed

+330
-10
lines changed

.github/.templatesyncignore

+4
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,8 @@ README.md
22
.github/workflows/*
33
.terraform-docs.yml
44
docs/20-badges.md
5+
docs/assets/logo.svg
56
*.tf
7+
test/*
8+
go.mod
9+
go.sum

README.md

+18-3
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@ for creating the AWS Integration role and the following submodules:
5050
### Resource collection
5151
* Cloud Security Posture Management (can be enabled via the integration role)
5252

53+
### ECS Fargate Agent
54+
* Scrape DB metrics for DBM
55+
56+
### Log Forwarder Lambda
57+
* Forward any S3 or CloudWatch logs to Datadog
58+
5359
## Inputs
5460

5561
| Name | Description | Type | Default | Required |
@@ -81,9 +87,9 @@ for creating the AWS Integration role and the following submodules:
8187

8288
## Resources
8389

84-
- resource.aws_iam_role_policy_attachment.csp (main.tf#151)
85-
- resource.datadog_integration_aws.main (main.tf#22)
86-
- data source.aws_caller_identity.current (main.tf#14)
90+
- resource.aws_iam_role_policy_attachment.csp (main.tf#157)
91+
- resource.datadog_integration_aws.main (main.tf#28)
92+
- data source.aws_caller_identity.current (main.tf#20)
8793

8894
# Examples
8995
### Full
@@ -108,5 +114,14 @@ module "metric_stream" {
108114
prefix = "datadog-pro"
109115
datadog_api_key = var.datadog_api_key
110116
}
117+
118+
# Deploy the log forwarder Lambda via CloudFormation:
119+
# https://docs.datadoghq.com/logs/guide/forwarder/?tab=terraform
120+
module "log_forwarder" {
121+
source = "../../modules/log_forwarder"
122+
123+
prefix = "datadog-pro"
124+
datadog_api_key = var.datadog_api_key
125+
}
111126
```
112127
<!-- END_TF_DOCS -->

examples/complete/main.tf

+9
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,12 @@ module "metric_stream" {
1818
prefix = "datadog-pro"
1919
datadog_api_key = var.datadog_api_key
2020
}
21+
22+
# Deploy the log forwarder Lambda via CloudFormation:
23+
# https://docs.datadoghq.com/logs/guide/forwarder/?tab=terraform
24+
module "log_forwarder" {
25+
source = "../../modules/log_forwarder"
26+
27+
prefix = "datadog-pro"
28+
datadog_api_key = var.datadog_api_key
29+
}

main.tf

+6
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@
1010
*
1111
* ### Resource collection
1212
* * Cloud Security Posture Management (can be enabled via the integration role)
13+
*
14+
* ### ECS Fargate Agent
15+
* * Scrape DB metrics for DBM
16+
*
17+
* ### Log Forwarder Lambda
18+
* * Forward any S3 or CloudWatch logs to Datadog
1319
*/
1420
data "aws_caller_identity" "current" {
1521
count = var.aws_account_id == null ? 1 : 0

modules/fargate_agent/main.tf

+3-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* having to setup an EC2 instance. For more information, see: https://docs.datadoghq.com/database_monitoring/
77
*/
88
module "ecs_agent_container" {
9-
source = "github.com/geekcell/terraform-aws-ecs-container-definition?ref=main"
9+
source = "github.com/geekcell/terraform-aws-ecs-container-definition?ref=v1"
1010

1111
name = "datadog-agent"
1212
image = var.agent_container
@@ -26,7 +26,7 @@ module "ecs_agent_container" {
2626
}
2727

2828
module "ecs_task_definition" {
29-
source = "github.com/geekcell/terraform-aws-ecs-task-definition.git?ref=main"
29+
source = "github.com/geekcell/terraform-aws-ecs-task-definition.git?ref=v1"
3030

3131
name = var.name
3232
container_definitions = [module.ecs_agent_container.hcl]
@@ -78,7 +78,7 @@ resource "aws_secretsmanager_secret" "main" {
7878
module "ecs_exec_ssm_policy" {
7979
count = length(var.secretsmanager_secret_keys) > 0 ? 1 : 0
8080

81-
source = "github.com/geekcell/terraform-aws-iam-policy?ref=main"
81+
source = "github.com/geekcell/terraform-aws-iam-policy?ref=v1"
8282

8383
name = "${var.name}-ssm-env"
8484
statements = [
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
content: |-
2+
{{ .Header }}
3+
4+
{{ .Inputs }}
5+
6+
{{ .Outputs }}
7+
8+
{{ .Providers }}
9+
10+
## Resources
11+
{{ range .Module.Resources }}
12+
- {{ .GetMode }}.{{ .Spec }} ({{ .Position.Filename }}#{{ .Position.Line }})
13+
{{- end }}
14+
15+
# Examples
16+
### Full
17+
```hcl
18+
{{ include "examples/full/main.tf" }}
19+
```

modules/log_forwarder/README.md

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
<!-- BEGIN_TF_DOCS -->
2+
# Terraform AWS Datadog Log Forwarder Lambda Module
3+
4+
This Terraform module deploys the Datadog Log Forwarder Lambda function as a Cloudformation stack. This is this
5+
recommended way by Datadog.
6+
7+
## Inputs
8+
9+
| Name | Description | Type | Default | Required |
10+
|------|-------------|------|---------|:--------:|
11+
| <a name="input_datadog_api_key"></a> [datadog\_api\_key](#input\_datadog\_api\_key) | Pass an existing Datadog API key to write to the SecretsManager Secret. | `string` | `null` | no |
12+
| <a name="input_datadog_site"></a> [datadog\_site](#input\_datadog\_site) | Datadog site to use. | `string` | `"datadoghq.eu"` | no |
13+
| <a name="input_lambda_function_name"></a> [lambda\_function\_name](#input\_lambda\_function\_name) | The name of the Lambda function. | `string` | `null` | no |
14+
| <a name="input_lambda_log_retention_days"></a> [lambda\_log\_retention\_days](#input\_lambda\_log\_retention\_days) | The number of days to retain log events for the Lambda in Cloudwatch. | `number` | `90` | no |
15+
| <a name="input_lambda_memory_size"></a> [lambda\_memory\_size](#input\_lambda\_memory\_size) | Amount of memory to allocate to the Lambda function. | `number` | `1024` | no |
16+
| <a name="input_lambda_reserved_concurrency"></a> [lambda\_reserved\_concurrency](#input\_lambda\_reserved\_concurrency) | The number of concurrent executions reserved for the Lambda. | `number` | `null` | no |
17+
| <a name="input_lambda_timeout"></a> [lambda\_timeout](#input\_lambda\_timeout) | The amount of time the Lambda function has to run in seconds. | `number` | `120` | no |
18+
| <a name="input_prefix"></a> [prefix](#input\_prefix) | Prefix to use for resources in this module. | `string` | n/a | yes |
19+
| <a name="input_s3_bucket_name"></a> [s3\_bucket\_name](#input\_s3\_bucket\_name) | The name of the S3 forwarder bucket to create. | `string` | `null` | no |
20+
| <a name="input_s3_mfa"></a> [s3\_mfa](#input\_s3\_mfa) | MFA device ARN including a TOTP token to enable MFA delete for the forwarder bucket. | `string` | `null` | no |
21+
| <a name="input_s3_mfa_delete"></a> [s3\_mfa\_delete](#input\_s3\_mfa\_delete) | Enable MFA Delete for the forwarder bucket. | `string` | `"Disabled"` | no |
22+
| <a name="input_s3_noncurrent_version_expiration"></a> [s3\_noncurrent\_version\_expiration](#input\_s3\_noncurrent\_version\_expiration) | Specifies when non-current object versions expire in the forwarder bucket. | `number` | `30` | no |
23+
| <a name="input_s3_versioning"></a> [s3\_versioning](#input\_s3\_versioning) | Enable S3 Versioning for the forwarder bucket. | `string` | `"Enabled"` | no |
24+
| <a name="input_stack_additional_parameters"></a> [stack\_additional\_parameters](#input\_stack\_additional\_parameters) | Additional parameters to pass to the CloudFormation template. | `map(string)` | `{}` | no |
25+
| <a name="input_stack_capabilities"></a> [stack\_capabilities](#input\_stack\_capabilities) | CloudFormation capabilities required to create the stack. | `list(string)` | <pre>[<br> "CAPABILITY_IAM",<br> "CAPABILITY_NAMED_IAM",<br> "CAPABILITY_AUTO_EXPAND"<br>]</pre> | no |
26+
| <a name="input_stack_template_url"></a> [stack\_template\_url](#input\_stack\_template\_url) | URL of the CloudFormation template to use to create the stack. | `string` | `"https://datadog-cloudformation-template.s3.amazonaws.com/aws/forwarder/latest.yaml"` | no |
27+
| <a name="input_tags"></a> [tags](#input\_tags) | n/a | `map(string)` | `{}` | no |
28+
29+
## Outputs
30+
31+
| Name | Description |
32+
|------|-------------|
33+
| <a name="output_secretsmanager_secret_arn"></a> [secretsmanager\_secret\_arn](#output\_secretsmanager\_secret\_arn) | n/a |
34+
| <a name="output_stack_outputs"></a> [stack\_outputs](#output\_stack\_outputs) | n/a |
35+
36+
## Providers
37+
38+
| Name | Version |
39+
|------|---------|
40+
| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 4.50 |
41+
42+
## Resources
43+
44+
- resource.aws_cloudformation_stack.main (modules/log_forwarder/main.tf#21)
45+
- resource.aws_s3_bucket_lifecycle_configuration.main (modules/log_forwarder/main.tf#57)
46+
- resource.aws_s3_bucket_versioning.main (modules/log_forwarder/main.tf#45)
47+
- resource.aws_secretsmanager_secret.main (modules/log_forwarder/main.tf#7)
48+
- resource.aws_secretsmanager_secret_version.main (modules/log_forwarder/main.tf#14)
49+
50+
# Examples
51+
### Full
52+
```hcl
53+
module "datadog_log_forwarder" {
54+
source = "../../"
55+
56+
prefix = "production-core"
57+
datadog_api_key = "1234567890abcdef"
58+
}
59+
```
60+
<!-- END_TF_DOCS -->
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
module "datadog_log_forwarder" {
2+
source = "../../"
3+
4+
prefix = "production-core"
5+
datadog_api_key = "1234567890abcdef"
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
terraform {
2+
required_version = ">= 1.3"
3+
4+
required_providers {
5+
aws = {
6+
source = "hashicorp/aws"
7+
version = ">= 4.50"
8+
}
9+
}
10+
}

modules/log_forwarder/main.tf

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/**
2+
* # Terraform AWS Datadog Log Forwarder Lambda Module
3+
*
4+
* This Terraform module deploys the Datadog Log Forwarder Lambda function as a Cloudformation stack. This is this
5+
* recommended way by Datadog.
6+
*/
7+
resource "aws_secretsmanager_secret" "main" {
8+
name = "${var.prefix}-datadog-log-forwarder/api-key"
9+
description = "Encrypted Datadog API Key."
10+
11+
tags = var.tags
12+
}
13+
14+
resource "aws_secretsmanager_secret_version" "main" {
15+
count = var.datadog_api_key != null ? 1 : 0
16+
17+
secret_id = aws_secretsmanager_secret.main.id
18+
secret_string = var.datadog_api_key
19+
}
20+
21+
resource "aws_cloudformation_stack" "main" {
22+
name = "${var.prefix}-datadog-log-forwarder"
23+
capabilities = var.stack_capabilities
24+
template_url = var.stack_template_url
25+
26+
parameters = merge({
27+
# Datadog
28+
DdApiKeySecretArn = aws_secretsmanager_secret.main.arn
29+
DdSite = var.datadog_site
30+
31+
# Lambda
32+
FunctionName = coalesce(var.lambda_function_name, "${var.prefix}-datadog-log-forwarder")
33+
MemorySize = var.lambda_memory_size
34+
Timeout = var.lambda_timeout
35+
LogRetentionInDays = var.lambda_log_retention_days
36+
ReservedConcurrency = var.lambda_reserved_concurrency
37+
38+
# Bucket
39+
DdForwarderBucketName = coalesce(var.s3_bucket_name, "${var.prefix}-datadog-log-forwarder")
40+
}, var.stack_additional_parameters)
41+
42+
tags = var.tags
43+
}
44+
45+
resource "aws_s3_bucket_versioning" "main" {
46+
bucket = coalesce(var.s3_bucket_name, "${var.prefix}-datadog-log-forwarder")
47+
mfa = var.s3_mfa
48+
49+
versioning_configuration {
50+
status = var.s3_versioning
51+
mfa_delete = var.s3_mfa_delete
52+
}
53+
54+
depends_on = [aws_cloudformation_stack.main]
55+
}
56+
57+
resource "aws_s3_bucket_lifecycle_configuration" "main" {
58+
bucket = coalesce(var.s3_bucket_name, "${var.prefix}-datadog-log-forwarder")
59+
60+
rule {
61+
id = "expire-non-current-versions"
62+
status = "Enabled"
63+
64+
noncurrent_version_expiration {
65+
noncurrent_days = var.s3_noncurrent_version_expiration
66+
}
67+
}
68+
69+
depends_on = [aws_cloudformation_stack.main]
70+
}

modules/log_forwarder/outputs.tf

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
output "secretsmanager_secret_arn" {
2+
value = aws_secretsmanager_secret.main.arn
3+
}
4+
5+
output "stack_outputs" {
6+
value = aws_cloudformation_stack.main.outputs
7+
}

modules/log_forwarder/variables.tf

+104
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
## NAMING
2+
variable "prefix" {
3+
description = "Prefix to use for resources in this module."
4+
type = string
5+
}
6+
7+
variable "tags" {
8+
default = {}
9+
type = map(string)
10+
}
11+
12+
## DATADOG
13+
variable "datadog_site" {
14+
description = "Datadog site to use."
15+
default = "datadoghq.eu"
16+
type = string
17+
}
18+
19+
variable "datadog_api_key" {
20+
description = "Pass an existing Datadog API key to write to the SecretsManager Secret."
21+
default = null
22+
type = string
23+
}
24+
25+
## STACK
26+
variable "stack_capabilities" {
27+
description = "CloudFormation capabilities required to create the stack."
28+
default = ["CAPABILITY_IAM", "CAPABILITY_NAMED_IAM", "CAPABILITY_AUTO_EXPAND"]
29+
type = list(string)
30+
}
31+
32+
variable "stack_template_url" {
33+
description = "URL of the CloudFormation template to use to create the stack."
34+
default = "https://datadog-cloudformation-template.s3.amazonaws.com/aws/forwarder/latest.yaml"
35+
type = string
36+
}
37+
38+
variable "stack_additional_parameters" {
39+
description = "Additional parameters to pass to the CloudFormation template."
40+
default = {}
41+
type = map(string)
42+
}
43+
44+
## LAMBDA
45+
variable "lambda_function_name" {
46+
description = "The name of the Lambda function."
47+
default = null
48+
type = string
49+
}
50+
51+
variable "lambda_reserved_concurrency" {
52+
description = "The number of concurrent executions reserved for the Lambda."
53+
default = null
54+
type = number
55+
}
56+
57+
variable "lambda_log_retention_days" {
58+
description = "The number of days to retain log events for the Lambda in Cloudwatch."
59+
default = 90
60+
type = number
61+
}
62+
63+
variable "lambda_timeout" {
64+
description = "The amount of time the Lambda function has to run in seconds."
65+
default = 120
66+
type = number
67+
}
68+
69+
variable "lambda_memory_size" {
70+
description = "Amount of memory to allocate to the Lambda function."
71+
default = 1024
72+
type = number
73+
}
74+
75+
## S3
76+
variable "s3_bucket_name" {
77+
description = "The name of the S3 forwarder bucket to create."
78+
default = null
79+
type = string
80+
}
81+
82+
variable "s3_versioning" {
83+
description = "Enable S3 Versioning for the forwarder bucket."
84+
default = "Enabled"
85+
type = string
86+
}
87+
88+
variable "s3_noncurrent_version_expiration" {
89+
description = "Specifies when non-current object versions expire in the forwarder bucket."
90+
default = 30
91+
type = number
92+
}
93+
94+
variable "s3_mfa" {
95+
description = "MFA device ARN including a TOTP token to enable MFA delete for the forwarder bucket."
96+
default = null
97+
type = string
98+
}
99+
100+
variable "s3_mfa_delete" {
101+
description = "Enable MFA Delete for the forwarder bucket."
102+
default = "Disabled"
103+
type = string
104+
}

modules/log_forwarder/versions.tf

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
terraform {
2+
required_version = ">= 1.3"
3+
4+
required_providers {
5+
aws = {
6+
source = "hashicorp/aws"
7+
version = ">= 4.50"
8+
}
9+
}
10+
}

0 commit comments

Comments
 (0)