Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ For complete usage examples demonstrating different configuration scenarios, see
| timeout | Timeout in seconds | `number` | `120` |
| reserved_concurrency | Reserved concurrency | `string` | `null` |
| log_retention_in_days | CloudWatch log retention | `number` | `90` |
| layer_version | Version of the Datadog Forwarder Lambda layer | `string` | `"latest"` |
| layer_arn | Custom layer ARN (optional) | `string` | `null` |
| layer_version | Version of the Datadog Forwarder Lambda layer. Required when `layer_arn` is not provided. `"latest"` is not supported. | `string` | `null` |
| layer_arn | Custom layer ARN. Required when `layer_version` is not provided. | `string` | `null` |
| existing_iam_role_arn | ARN of existing IAM role to use for the Lambda function. When using an existing role, you must provide either `dd_api_key_secret_arn` or `dd_api_key_ssm_parameter_name`, and you are responsible for ensuring the role has the necessary permissions for any resources the module creates. See [Using an Existing IAM Role](#using-an-existing-iam-role) for details. | `string` | `null` |
| tags | Resource tags | `map(string)` | `{}` |

Expand Down
27 changes: 3 additions & 24 deletions data.tf
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,10 @@ data "aws_caller_identity" "current" {}
data "aws_region" "current" {}
data "aws_partition" "current" {}

# Fetch version mapping from public S3 bucket
data "http" "forwarder_versions" {
url = "https://datadog-opensource-asset-versions.s3.us-east-1.amazonaws.com/forwarder/versions.json"
}

# Local values
locals {
# Parse version mapping from S3
version_data = jsondecode(data.http.forwarder_versions.response_body)

# Determine layer version: use latest or specified version
layer_version = var.layer_version == "latest" ? local.version_data.latest.layer_version : var.layer_version

# Determine forwarder version: use latest or lookup in mappings
forwarder_version = (
var.layer_version == "latest"
? local.version_data.latest.forwarder_version
: lookup(local.version_data.mappings, var.layer_version, null)
)
# Determine layer version from explicit input or layer ARN
layer_version = var.layer_version != null ? var.layer_version : regex("[0-9]+$", var.layer_arn)

# Determine if we need to create an S3 bucket for caching and failed events storage
create_s3_bucket = (coalesce(var.dd_fetch_log_group_tags, false) || coalesce(var.dd_fetch_lambda_tags, false) || coalesce(var.dd_fetch_s3_tags, false) || coalesce(var.dd_store_failed_events, false)) && var.dd_forwarder_existing_bucket_name == null
Expand Down Expand Up @@ -54,13 +39,7 @@ locals {
var.dd_api_key_secret_arn
) : null

# Merge dd_forwarder_version tag with user-provided tags (only when version is known)
tags_with_version = merge(
var.tags,
local.forwarder_version != null ? {
dd_forwarder_version = local.forwarder_version
} : {}
)
tags_with_version = var.tags
}

# Deprecation warnings for conflicting API key configurations.
Expand Down
2 changes: 1 addition & 1 deletion main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ resource "aws_lambda_function" "forwarder" {
description = "Pushes logs, metrics and traces from AWS to Datadog."
role = local.iam_role_arn
handler = "lambda_function.lambda_handler"
runtime = var.layer_version == "latest" ? "python3.14" : (can(tonumber(var.layer_version)) && tonumber(var.layer_version) >= 94 ? "python3.14" : "python3.13")
runtime = can(tonumber(local.layer_version)) && tonumber(local.layer_version) >= 94 ? "python3.14" : "python3.13"
architectures = ["arm64"]
memory_size = var.memory_size
timeout = var.timeout
Expand Down
36 changes: 1 addition & 35 deletions tests/create_api_key_secret_flag.tftest.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ mock_provider "aws" {
variables {
dd_site = "datadoghq.com"
region = "us-east-1"
layer_version = "92"
}

# ─────────────────────────────────────────────────────────────────────────────
Expand All @@ -41,13 +42,6 @@ run "explicit_false_with_secret_arn" {
create_dd_api_key_secret = false
}

override_data {
target = data.http.forwarder_versions
values = {
response_body = "{\"latest\":{\"layer_version\":\"92\",\"forwarder_version\":\"5.1.0\"},\"mappings\":{}}"
}
}

assert {
condition = length(aws_secretsmanager_secret.dd_api_key_secret) == 0
error_message = "No secret should be created when create_dd_api_key_secret=false"
Expand All @@ -70,13 +64,6 @@ run "explicit_false_with_ssm_parameter" {
create_dd_api_key_secret = false
}

override_data {
target = data.http.forwarder_versions
values = {
response_body = "{\"latest\":{\"layer_version\":\"92\",\"forwarder_version\":\"5.1.0\"},\"mappings\":{}}"
}
}

assert {
condition = length(aws_secretsmanager_secret.dd_api_key_secret) == 0
error_message = "No secret should be created when using SSM parameter with flag=false"
Expand All @@ -94,13 +81,6 @@ run "explicit_true_creates_secret" {
create_dd_api_key_secret = true
}

override_data {
target = data.http.forwarder_versions
values = {
response_body = "{\"latest\":{\"layer_version\":\"92\",\"forwarder_version\":\"5.1.0\"},\"mappings\":{}}"
}
}

assert {
condition = length(aws_secretsmanager_secret.dd_api_key_secret) == 1
error_message = "Secret should be created when create_dd_api_key_secret=true and dd_api_key is set"
Expand All @@ -118,13 +98,6 @@ run "null_flag_auto_creates_secret_from_api_key" {
# create_dd_api_key_secret not set (null/default)
}

override_data {
target = data.http.forwarder_versions
values = {
response_body = "{\"latest\":{\"layer_version\":\"92\",\"forwarder_version\":\"5.1.0\"},\"mappings\":{}}"
}
}

assert {
condition = length(aws_secretsmanager_secret.dd_api_key_secret) == 1
error_message = "Secret should be auto-created when dd_api_key is provided and flag is null"
Expand All @@ -139,13 +112,6 @@ run "null_flag_auto_skips_secret_with_external_arn" {
# create_dd_api_key_secret not set (null/default)
}

override_data {
target = data.http.forwarder_versions
values = {
response_body = "{\"latest\":{\"layer_version\":\"92\",\"forwarder_version\":\"5.1.0\"},\"mappings\":{}}"
}
}

assert {
condition = length(aws_secretsmanager_secret.dd_api_key_secret) == 0
error_message = "Secret should NOT be auto-created when dd_api_key_secret_arn is provided"
Expand Down
61 changes: 13 additions & 48 deletions tests/forwarder_version_tag.tftest.hcl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Test the dd_forwarder_version tag functionality
# Test explicit layer version configuration
mock_provider "aws" {
mock_data "aws_caller_identity" {
defaults = {
Expand All @@ -20,62 +20,35 @@ mock_provider "aws" {
}

variables {
dd_api_key = "test-api-key-value"
dd_site = "datadoghq.com"
dd_api_key = "test-api-key-value"
dd_site = "datadoghq.com"
layer_version = "92"
}

# Test with layer_version = "latest" (default)
run "version_tag_with_latest" {
run "explicit_layer_version" {
command = plan

# Lambda should have dd_forwarder_version tag
assert {
condition = contains(keys(aws_lambda_function.forwarder.tags), "dd_forwarder_version")
error_message = "Lambda function should have dd_forwarder_version tag"
}

# Tag should be a semantic version (e.g., "5.1.0")
assert {
condition = can(regex("^[0-9]+\\.[0-9]+\\.[0-9]+", aws_lambda_function.forwarder.tags["dd_forwarder_version"]))
error_message = "dd_forwarder_version tag should be a semantic version (e.g., 5.1.0)"
}

# Verify local.forwarder_version is set
assert {
condition = local.forwarder_version != null
error_message = "local.forwarder_version should be set when using latest"
condition = can(regex(":92$", aws_lambda_function.forwarder.layers[0]))
error_message = "Lambda layer ARN should end with :92"
}
}

# Test with specific layer_version
run "version_tag_with_specific_layer" {
run "custom_layer_arn" {
command = plan

variables {
layer_version = "92"
}

# Lambda should have dd_forwarder_version tag
assert {
condition = contains(keys(aws_lambda_function.forwarder.tags), "dd_forwarder_version")
error_message = "Lambda function should have dd_forwarder_version tag when using specific layer version"
layer_version = null
layer_arn = "arn:aws:lambda:us-east-1:464622532012:layer:Datadog-Forwarder:94"
}

# Tag should be a semantic version
assert {
condition = can(regex("^[0-9]+\\.[0-9]+\\.[0-9]+", aws_lambda_function.forwarder.tags["dd_forwarder_version"]))
error_message = "dd_forwarder_version tag should be a semantic version"
}

# Layer ARN should use the specified version
assert {
condition = can(regex(":92$", aws_lambda_function.forwarder.layers[0]))
error_message = "Lambda layer ARN should end with :92"
condition = aws_lambda_function.forwarder.runtime == "python3.14"
error_message = "Lambda runtime should be python3.14 for layer version 94"
}
}

# Test that user tags are preserved alongside dd_forwarder_version
run "version_tag_with_user_tags" {
run "user_tags_are_preserved" {
command = plan

variables {
Expand All @@ -85,7 +58,6 @@ run "version_tag_with_user_tags" {
}
}

# User tags should be present
assert {
condition = aws_lambda_function.forwarder.tags["Environment"] == "test"
error_message = "User-provided Environment tag should be preserved"
Expand All @@ -95,11 +67,4 @@ run "version_tag_with_user_tags" {
condition = aws_lambda_function.forwarder.tags["Team"] == "platform"
error_message = "User-provided Team tag should be preserved"
}

# dd_forwarder_version should also be present
assert {
condition = contains(keys(aws_lambda_function.forwarder.tags), "dd_forwarder_version")
error_message = "dd_forwarder_version tag should be added alongside user tags"
}
}

24 changes: 22 additions & 2 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -177,14 +177,34 @@ variable "log_retention_in_days" {

variable "layer_version" {
type = string
default = "latest"
description = "Version of the Datadog Forwarder Lambda layer. Use 'latest' to automatically fetch the latest version from GitHub releases or specify a version like '89'."
default = null
description = "Version of the Datadog Forwarder Lambda layer. Specify an explicit version like '89' when layer_arn is not provided."

validation {
condition = var.layer_version == null || var.layer_version != "latest"
error_message = "layer_version no longer supports 'latest'. Specify an explicit layer version or set layer_arn."
}

validation {
condition = var.layer_version == null || can(regex("^[0-9]+$", var.layer_version))
error_message = "layer_version must be a numeric string like '89'."
}

validation {
condition = var.layer_version != null || var.layer_arn != null
error_message = "You must specify either layer_version or layer_arn."
}
}

variable "layer_arn" {
type = string
default = null
description = "ARN for the layer containing the forwarder code. If empty, the script will use the version of the layer the forwarder was published with."

validation {
condition = var.layer_arn == null || can(regex("^arn:.*:lambda:.*:layer:.*:[0-9]+$", var.layer_arn))
error_message = "layer_arn must be a valid Lambda layer ARN ending in a numeric version."
}
}

# Datadog configuration
Expand Down