Skip to content
Merged
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
25 changes: 25 additions & 0 deletions .github/workflows/terraform-fmt.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Terraform Format Check

permissions:
contents: read

on:
pull_request:
workflow_dispatch:

jobs:
terraform-fmt:
name: Check Terraform Formatting
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Setup Terraform
uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3.1.2
with:
terraform_version: "1.12.0"

- name: Run terraform fmt check
run: terraform fmt -recursive -check -diff .
28 changes: 28 additions & 0 deletions .github/workflows/terraform-test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Terraform Tests

permissions:
contents: read

on:
pull_request:
workflow_dispatch:

jobs:
terraform-test:
name: Run Terraform Tests
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

- name: Setup Terraform
uses: hashicorp/setup-terraform@b9cd54a3c349d3f38e8881555d616ced269862dd # v3.1.2
with:
terraform_version: "1.12.0"

- name: Terraform Init
run: terraform init

- name: Terraform Test
run: terraform test
23 changes: 23 additions & 0 deletions data.tf
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,26 @@ locals {
} : {}
)
}

# Deprecation warnings for conflicting API key configurations.
# These will become hard validation errors in a future major release.
check "dd_api_key_not_used_with_secret_arn" {
assert {
condition = var.dd_api_key == null || var.dd_api_key_secret_arn == null
error_message = "DEPRECATED: dd_api_key and dd_api_key_secret_arn are both set. Only one API key approach should be used. Currently dd_api_key is being ignored in favor of dd_api_key_secret_arn. Remove dd_api_key to silence this warning. This will become an error in a future release."
}
}

check "dd_api_key_not_used_with_ssm_parameter" {
assert {
condition = var.dd_api_key == null || var.dd_api_key_ssm_parameter_name == null
error_message = "DEPRECATED: dd_api_key and dd_api_key_ssm_parameter_name are both set. Only one API key approach should be used. Currently dd_api_key is being ignored in favor of dd_api_key_ssm_parameter_name. Remove dd_api_key to silence this warning. This will become an error in a future release."
}
}

check "dd_secret_arn_not_used_with_ssm_parameter" {
assert {
condition = var.dd_api_key_secret_arn == null || var.dd_api_key_ssm_parameter_name == null
error_message = "DEPRECATED: dd_api_key_secret_arn and dd_api_key_ssm_parameter_name are both set. Only one API key approach should be used. Currently dd_api_key_secret_arn is being ignored in favor of dd_api_key_ssm_parameter_name. Remove dd_api_key_secret_arn to silence this warning. This will become an error in a future release."
}
}
1 change: 1 addition & 0 deletions outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ output "datadog_forwarder_role_name" {
output "dd_api_key_secret_arn" {
description = "ARN of SecretsManager Secret with Datadog API Key (only set if created by this module)"
value = local.should_create_secret ? aws_secretsmanager_secret.dd_api_key_secret[0].arn : null
sensitive = true
}

output "forwarder_bucket_name" {
Expand Down
20 changes: 18 additions & 2 deletions tests/character_limit.tftest.hcl
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
# Test that character limits are respected
provider "aws" {
region = "us-east-1"
mock_provider "aws" {
mock_data "aws_caller_identity" {
defaults = {
account_id = "123456789012"
}
}

mock_data "aws_region" {
defaults = {
region = "us-east-1"
}
}

mock_data "aws_partition" {
defaults = {
partition = "aws"
}
}
}

variables {
Expand Down
48 changes: 48 additions & 0 deletions tests/create_api_key_secret_flag.tftest.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -211,3 +211,51 @@ run "invalid_ssm_parameter_name_fails_validation" {
var.dd_api_key_ssm_parameter_name
]
}

# ─────────────────────────────────────────────────────────────────────────────
# Scenario 6: mutual exclusivity — conflicting API key configurations warn
# These are deprecation warnings (check blocks) that will become hard errors
# in a future major release.
# ─────────────────────────────────────────────────────────────────────────────

# dd_api_key + dd_api_key_secret_arn should warn
run "api_key_and_secret_arn_conflict_warns" {
command = plan

variables {
dd_api_key = "test-api-key-value"
dd_api_key_secret_arn = "arn:aws:secretsmanager:us-east-1:123456789012:secret:my-key-AbCdEf"
}

expect_failures = [
check.dd_api_key_not_used_with_secret_arn
]
}

# dd_api_key + dd_api_key_ssm_parameter_name should warn
run "api_key_and_ssm_parameter_conflict_warns" {
command = plan

variables {
dd_api_key = "test-api-key-value"
dd_api_key_ssm_parameter_name = "/datadog/api-key"
}

expect_failures = [
check.dd_api_key_not_used_with_ssm_parameter
]
}

# dd_api_key_secret_arn + dd_api_key_ssm_parameter_name should warn
run "secret_arn_and_ssm_parameter_conflict_warns" {
command = plan

variables {
dd_api_key_secret_arn = "arn:aws:secretsmanager:us-east-1:123456789012:secret:my-key-AbCdEf"
dd_api_key_ssm_parameter_name = "/datadog/api-key"
}

expect_failures = [
check.dd_secret_arn_not_used_with_ssm_parameter
]
}
81 changes: 46 additions & 35 deletions tests/default_config.tftest.hcl
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
# Test the default configuration of the Datadog Forwarder module
provider "aws" {
region = "us-east-1"
mock_provider "aws" {
mock_data "aws_caller_identity" {
defaults = {
account_id = "123456789012"
}
}

mock_data "aws_region" {
defaults = {
region = "us-east-1"
}
}

mock_data "aws_partition" {
defaults = {
partition = "aws"
}
}
}

variables {
Expand Down Expand Up @@ -65,9 +81,10 @@ run "default_config_test" {
}

# === Secrets Management ===
# Default path: dd_api_key provided, no external secret reference → module creates secret
assert {
condition = length(aws_secretsmanager_secret.dd_api_key_secret) == 1
error_message = "Secrets Manager secret should be created by default"
error_message = "Secrets Manager secret should be created by default when only dd_api_key is provided"
}

# === Storage Configuration ===
Expand Down Expand Up @@ -102,11 +119,18 @@ run "default_config_test" {
condition = aws_lambda_permission.eventbridge_invoke.principal == "events.amazonaws.com"
error_message = "EventBridge permission should be created"
}

# === Output Validation ===
assert {
condition = output.datadog_forwarder_function_name == "DatadogForwarder"
error_message = "datadog_forwarder_function_name should match expected value"
}
}

# Test environment variable values
run "environment_variables_test" {
command = apply
command = plan

# Test actual environment variable values (only available after apply)
assert {
condition = aws_lambda_function.forwarder.environment[0].variables.DD_SITE == "datadoghq.com"
error_message = "DD_SITE environment variable should be set correctly"
Expand All @@ -127,54 +151,41 @@ run "environment_variables_test" {
error_message = "DD_TRACE_ENABLED should be true by default"
}

# Test that optional environment variables are NOT set when null
# Test that optional environment variables are null when not provided
assert {
condition = !contains(keys(aws_lambda_function.forwarder.environment[0].variables), "DD_TAGS")
error_message = "DD_TAGS should not be present when null"
condition = aws_lambda_function.forwarder.environment[0].variables.DD_TAGS == null
error_message = "DD_TAGS should be null when not provided"
}

assert {
condition = !contains(keys(aws_lambda_function.forwarder.environment[0].variables), "DD_FETCH_LAMBDA_TAGS")
error_message = "DD_FETCH_LAMBDA_TAGS should not be present when null"
}
assert {
condition = !contains(keys(aws_lambda_function.forwarder.environment[0].variables), "DD_FETCH_S3_TAGS")
error_message = "DD_FETCH_S3_TAGS should not be present when null"
condition = aws_lambda_function.forwarder.environment[0].variables.DD_FETCH_LAMBDA_TAGS == null
error_message = "DD_FETCH_LAMBDA_TAGS should be null when not provided"
}

assert {
condition = !contains(keys(aws_lambda_function.forwarder.environment[0].variables), "DD_FORWARD_LOG")
error_message = "DD_FORWARD_LOG should not be present when null"
condition = aws_lambda_function.forwarder.environment[0].variables.DD_FETCH_S3_TAGS == null
error_message = "DD_FETCH_S3_TAGS should be null when not provided"
}

assert {
condition = !contains(keys(aws_lambda_function.forwarder.environment[0].variables), "DD_LOG_LEVEL")
error_message = "DD_LOG_LEVEL should not be present when null"
condition = aws_lambda_function.forwarder.environment[0].variables.DD_FORWARD_LOG == null
error_message = "DD_FORWARD_LOG should be null when not provided"
}

# Test that key outputs have values after apply
assert {
condition = output.datadog_forwarder_arn != null && output.datadog_forwarder_arn != ""
error_message = "datadog_forwarder_arn output should have a value"
condition = aws_lambda_function.forwarder.environment[0].variables.DD_LOG_LEVEL == null
error_message = "DD_LOG_LEVEL should be null when not provided"
}

# Verify secret ARN path is used (not SSM)
assert {
condition = output.datadog_forwarder_function_name == "DatadogForwarder"
error_message = "datadog_forwarder_function_name should match expected value"
condition = contains(keys(aws_lambda_function.forwarder.environment[0].variables), "DD_API_KEY_SECRET_ARN")
error_message = "DD_API_KEY_SECRET_ARN should be present when module auto-creates the secret"
}

assert {
condition = output.datadog_forwarder_role_arn != null && output.datadog_forwarder_role_arn != ""
error_message = "datadog_forwarder_role_arn should have a value"
}

assert {
condition = output.forwarder_log_group_name != null && output.forwarder_log_group_name != ""
error_message = "forwarder_log_group_name should have a value"
}

assert {
condition = output.forwarder_log_group_arn != null && output.forwarder_log_group_arn != ""
error_message = "forwarder_log_group_arn should have a value"
condition = !contains(keys(aws_lambda_function.forwarder.environment[0].variables), "DD_API_KEY_SSM_NAME")
error_message = "DD_API_KEY_SSM_NAME should not be present when using Secrets Manager"
}
}

28 changes: 22 additions & 6 deletions tests/enhanced_features.tftest.hcl
Original file line number Diff line number Diff line change
@@ -1,11 +1,27 @@
# Test enhanced features configuration
provider "aws" {
region = "us-east-1"
mock_provider "aws" {
mock_data "aws_caller_identity" {
defaults = {
account_id = "123456789012"
}
}

mock_data "aws_region" {
defaults = {
region = "us-east-1"
}
}

mock_data "aws_partition" {
defaults = {
partition = "aws"
}
}
}

variables {
dd_api_key = "test-api-key-value"
dd_site = "datadoghq.com"
dd_api_key_secret_arn = "arn:aws:secretsmanager:us-east-1:123456789012:secret:DatadogAPIKey-mock"
dd_fetch_lambda_tags = true
dd_fetch_log_group_tags = true
dd_fetch_step_functions_tags = true
Expand Down Expand Up @@ -39,7 +55,7 @@ run "enhanced_features_test" {
}
}
run "enhanced_features_env_vars_test" {
command = apply
command = plan

assert {
condition = aws_lambda_function.forwarder.layers[0] == "arn:aws:lambda:us-east-1:464622532012:layer:Datadog-Forwarder:80"
Expand Down Expand Up @@ -79,9 +95,9 @@ run "enhanced_features_env_vars_test" {
error_message = "DD_STORE_FAILED_EVENTS should be true when enabled"
}

# Test S3 bucket name is set
# Test S3 bucket name key is present in env vars
assert {
condition = length(aws_lambda_function.forwarder.environment[0].variables.DD_S3_BUCKET_NAME) > 0
condition = contains(keys(aws_lambda_function.forwarder.environment[0].variables), "DD_S3_BUCKET_NAME")
error_message = "DD_S3_BUCKET_NAME should be set when S3 bucket is created"
}
}
8 changes: 0 additions & 8 deletions tests/existing_iam_role_without_bucket.tftest.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ run "existing_role_with_ssm_no_bucket" {
command = plan

variables {
dd_api_key = "test-api-key-value"
dd_site = "datadoghq.com"
existing_iam_role_arn = "arn:aws:iam::123456789012:role/existing-datadog-role"
dd_api_key_ssm_parameter_name = "/datadog/api-key"
}
Expand All @@ -51,8 +49,6 @@ run "existing_role_with_secret_arn_no_bucket" {
command = plan

variables {
dd_api_key = "test-api-key-value"
dd_site = "datadoghq.com"
existing_iam_role_arn = "arn:aws:iam::123456789012:role/existing-datadog-role"
dd_api_key_secret_arn = "arn:aws:secretsmanager:us-east-1:123456789012:secret:datadog-api-key-AbCdEf"
}
Expand All @@ -75,8 +71,6 @@ run "existing_role_with_tag_fetching" {
command = plan

variables {
dd_api_key = "test-api-key-value"
dd_site = "datadoghq.com"
existing_iam_role_arn = "arn:aws:iam::123456789012:role/existing-datadog-role"
dd_api_key_ssm_parameter_name = "/datadog/api-key"
dd_fetch_lambda_tags = true
Expand All @@ -100,8 +94,6 @@ run "existing_role_with_failed_events" {
command = plan

variables {
dd_api_key = "test-api-key-value"
dd_site = "datadoghq.com"
existing_iam_role_arn = "arn:aws:iam::123456789012:role/existing-datadog-role"
dd_api_key_ssm_parameter_name = "/datadog/api-key"
dd_store_failed_events = true
Expand Down
1 change: 0 additions & 1 deletion tests/existing_resources.tftest.hcl
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ mock_provider "aws" {
}

variables {
dd_api_key = "test-api-key-value"
dd_site = "datadoghq.com"
existing_iam_role_arn = "arn:aws:iam::123456789012:role/existing-datadog-role"
dd_forwarder_existing_bucket_name = "existing-datadog-bucket"
Expand Down
Loading
Loading