From 35ad999822c7bc5a043702e69f321bcbdb5cb3f2 Mon Sep 17 00:00:00 2001 From: Robert Fairburn <8029478+rfairburn@users.noreply.github.com> Date: Wed, 15 Jul 2026 07:45:23 -0500 Subject: [PATCH] S3 bucket tagging (#268) --- addons/logging-alb/README.md | 15 +++++++------ addons/logging-alb/main.tf | 4 ++++ addons/logging-alb/variables.tf | 6 +++++ addons/logging-destination-datadog/README.md | 22 ++++++------------- addons/logging-destination-datadog/main.tf | 1 + .../logging-destination-datadog/variables.tf | 6 +++++ addons/logging-destination-splunk/README.md | 11 +++++----- addons/logging-destination-splunk/main.tf | 1 + .../logging-destination-splunk/variables.tf | 6 +++++ addons/osquery-carve/README.md | 14 ++++++------ addons/osquery-carve/main.tf | 1 + addons/osquery-carve/variables.tf | 2 ++ 12 files changed, 55 insertions(+), 34 deletions(-) diff --git a/addons/logging-alb/README.md b/addons/logging-alb/README.md index 397a8b0..c383a54 100644 --- a/addons/logging-alb/README.md +++ b/addons/logging-alb/README.md @@ -89,22 +89,22 @@ No requirements. ## Providers | Name | Version | -|------|---------| -| [archive](#provider\_archive) | 2.7.1 | -| [aws](#provider\_aws) | 6.40.0 | +| ---- | ------- | +| [archive](#provider\_archive) | 2.8.0 | +| [aws](#provider\_aws) | 6.54.0 | | [terraform](#provider\_terraform) | n/a | ## Modules | Name | Source | Version | -|------|--------|---------| +| ---- | ------ | ------- | | [athena-s3-bucket](#module\_athena-s3-bucket) | terraform-aws-modules/s3-bucket/aws | 5.12.0 | | [s3\_bucket\_for\_logs](#module\_s3\_bucket\_for\_logs) | terraform-aws-modules/s3-bucket/aws | 5.12.0 | ## Resources | Name | Type | -|------|------| +| ---- | ---- | | [aws_athena_database.logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/athena_database) | resource | | [aws_athena_workgroup.logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/athena_workgroup) | resource | | [aws_cloudwatch_event_rule.sweep_reencrypt](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_rule) | resource | @@ -144,7 +144,7 @@ No requirements. ## Inputs | Name | Description | Type | Default | Required | -|------|-------------|------|---------|:--------:| +| ---- | ----------- | ---- | ------- | :------: | | [alt\_path\_prefix](#input\_alt\_path\_prefix) | Used if the prefix inside of the s3 bucket doesn't match the name of the bucket prefix | `string` | `null` | no | | [enable\_athena](#input\_enable\_athena) | n/a | `bool` | `true` | no | | [enable\_reencrypt\_sweep](#input\_enable\_reencrypt\_sweep) | Enable the sweep and re-encrypt Lambda functions, EventBridge schedule, S3 bucket notification, and associated IAM roles. | `bool` | `false` | no | @@ -155,6 +155,7 @@ No requirements. | [kms\_base\_policy](#input\_kms\_base\_policy) | Optional base KMS key-policy statements to apply to module-created CMKs before module-required service access statements are merged in. If null, the module defaults to the historical root `kms:*` statement. |
list(object({
sid = string
effect = string
principals = object({
type = string
identifiers = list(string)
})
actions = list(string)
resources = list(string)
conditions = optional(list(object({
test = string
variable = string
values = list(string)
})), [])
})) | `null` | no |
| [lambda\_log\_retention\_in\_days](#input\_lambda\_log\_retention\_in\_days) | CloudWatch log retention in days for the re-encrypt and sweep Lambda functions | `number` | `365` | no |
| [prefix](#input\_prefix) | n/a | `string` | `"fleet"` | no |
+| [s3\_bucket\_tags](#input\_s3\_bucket\_tags) | Additional tags to apply to all S3 buckets created by this module. | `map(string)` | `{}` | no |
| [s3\_expiration\_days](#input\_s3\_expiration\_days) | n/a | `number` | `90` | no |
| [s3\_newer\_noncurrent\_versions](#input\_s3\_newer\_noncurrent\_versions) | n/a | `number` | `5` | no |
| [s3\_noncurrent\_version\_expiration\_days](#input\_s3\_noncurrent\_version\_expiration\_days) | n/a | `number` | `30` | no |
@@ -163,5 +164,5 @@ No requirements.
## Outputs
| Name | Description |
-|------|-------------|
+| ---- | ----------- |
| [log\_s3\_bucket\_id](#output\_log\_s3\_bucket\_id) | S3 bucket used by ALB access logging (SSE-S3 on write, re-encrypted to SSE-KMS by Lambda) |
diff --git a/addons/logging-alb/main.tf b/addons/logging-alb/main.tf
index 308d202..70686d9 100644
--- a/addons/logging-alb/main.tf
+++ b/addons/logging-alb/main.tf
@@ -260,6 +260,8 @@ module "s3_bucket_for_logs" {
bucket = local.landing_bucket_name
+ tags = var.s3_bucket_tags
+
# Allow deletion of non-empty bucket
force_destroy = true
@@ -646,6 +648,8 @@ module "athena-s3-bucket" {
bucket = "${var.prefix}-alb-logs-athena"
+ tags = var.s3_bucket_tags
+
# Allow deletion of non-empty bucket
force_destroy = true
diff --git a/addons/logging-alb/variables.tf b/addons/logging-alb/variables.tf
index c5983c0..8749b4b 100644
--- a/addons/logging-alb/variables.tf
+++ b/addons/logging-alb/variables.tf
@@ -86,3 +86,9 @@ variable "extra_s3_athena_policies" {
type = list(any)
default = []
}
+
+variable "s3_bucket_tags" {
+ description = "Additional tags to apply to all S3 buckets created by this module."
+ type = map(string)
+ default = {}
+}
diff --git a/addons/logging-destination-datadog/README.md b/addons/logging-destination-datadog/README.md
index 5d130f4..785824e 100644
--- a/addons/logging-destination-datadog/README.md
+++ b/addons/logging-destination-datadog/README.md
@@ -9,15 +9,7 @@ This addon configures AWS Kinesis Firehose to send Fleet's osquery logs to Datad
## S3 Bucket Policy: Deny Non-HTTPS
-Set `attach_deny_insecure_transport_policy = true` to attach a bucket policy to the failure S3 bucket that denies any requests made over plain HTTP:
-
-```hcl
-module "datadog-logging" {
- source = "github.com/fleetdm/fleet-terraform//addons/logging-destination-datadog?ref=map(object({
name = string
buffering_size = number
buffering_interval = number
s3_buffering_size = number
s3_buffering_interval = number
content_encoding = string
common_attributes = optional(list(object({
name = string
value = string
})), [])
})) | {
"audit": {
"buffering_interval": 60,
"buffering_size": 2,
"common_attributes": [],
"content_encoding": "NONE",
"name": "fleet-audit-datadog",
"s3_buffering_interval": 400,
"s3_buffering_size": 10
},
"results": {
"buffering_interval": 60,
"buffering_size": 2,
"common_attributes": [],
"content_encoding": "NONE",
"name": "fleet-osquery-results-datadog",
"s3_buffering_interval": 400,
"s3_buffering_size": 10
},
"status": {
"buffering_interval": 60,
"buffering_size": 2,
"common_attributes": [],
"content_encoding": "NONE",
"name": "fleet-osquery-status-datadog",
"s3_buffering_interval": 400,
"s3_buffering_size": 10
}
} | no |
| [s3\_bucket\_config](#input\_s3\_bucket\_config) | Configuration for the S3 bucket used to store failed Datadog delivery attempts | object({
name_prefix = optional(string, "fleet-datadog-failure")
expires_days = optional(number, 1)
}) | {
"expires_days": 1,
"name_prefix": "fleet-datadog-failure"
} | no |
+| [s3\_bucket\_tags](#input\_s3\_bucket\_tags) | Additional tags to apply to the S3 failure bucket. | `map(string)` | `{}` | no |
## Outputs
| Name | Description |
-|------|-------------|
+| ---- | ----------- |
| [fleet\_extra\_environment\_variables](#output\_fleet\_extra\_environment\_variables) | Environment variables to configure Fleet to use Datadog logging via Firehose |
| [fleet\_extra\_iam\_policies](#output\_fleet\_extra\_iam\_policies) | IAM policies required for Fleet to log to Datadog via Firehose |
| [fleet\_s3\_datadog\_failure\_config](#output\_fleet\_s3\_datadog\_failure\_config) | S3 bucket details - datadog-failure |
diff --git a/addons/logging-destination-datadog/main.tf b/addons/logging-destination-datadog/main.tf
index 0202541..05fc2e4 100644
--- a/addons/logging-destination-datadog/main.tf
+++ b/addons/logging-destination-datadog/main.tf
@@ -2,6 +2,7 @@ data "aws_region" "current" {}
resource "aws_s3_bucket" "datadog-failure" { #tfsec:ignore:aws-s3-encryption-customer-key:exp:2022-07-01 #tfsec:ignore:aws-s3-enable-versioning #tfsec:ignore:aws-s3-enable-bucket-logging:exp:2022-06-15
bucket_prefix = var.s3_bucket_config.name_prefix
+ tags = var.s3_bucket_tags
}
resource "aws_s3_bucket_lifecycle_configuration" "datadog-failure" {
diff --git a/addons/logging-destination-datadog/variables.tf b/addons/logging-destination-datadog/variables.tf
index 5ba41d6..6db709c 100644
--- a/addons/logging-destination-datadog/variables.tf
+++ b/addons/logging-destination-datadog/variables.tf
@@ -70,3 +70,9 @@ variable "datadog_api_key" {
description = "Datadog API key for authentication"
sensitive = true
}
+
+variable "s3_bucket_tags" {
+ description = "Additional tags to apply to the S3 failure bucket."
+ type = map(string)
+ default = {}
+}
diff --git a/addons/logging-destination-splunk/README.md b/addons/logging-destination-splunk/README.md
index 88f2b8c..852b66b 100644
--- a/addons/logging-destination-splunk/README.md
+++ b/addons/logging-destination-splunk/README.md
@@ -100,8 +100,8 @@ No requirements.
## Providers
| Name | Version |
-|------|---------|
-| [aws](#provider\_aws) | 6.41.0 |
+| ---- | ------- |
+| [aws](#provider\_aws) | n/a |
## Modules
@@ -110,7 +110,7 @@ No modules.
## Resources
| Name | Type |
-|------|------|
+| ---- | ---- |
| [aws_iam_policy.firehose](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
| [aws_iam_policy.firehose-logging](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
| [aws_iam_role.firehose](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
@@ -130,16 +130,17 @@ No modules.
## Inputs
| Name | Description | Type | Default | Required |
-|------|-------------|------|---------|:--------:|
+| ---- | ----------- | ---- | ------- | :------: |
| [compression\_format](#input\_compression\_format) | Compression format for the Firehose delivery stream | `string` | `"UNCOMPRESSED"` | no |
| [iam\_policy\_name](#input\_iam\_policy\_name) | n/a | `string` | `"splunk-firehose-policy"` | no |
| [log\_destinations](#input\_log\_destinations) | A map of configurations for Splunk Firehose delivery streams. | map(object({
# hec endpoint/token are logically optional but validated to enforce
name = string
hec_endpoint = optional(string)
hec_token = optional(string)
hec_acknowledgement_timeout = optional(number, 600)
hec_endpoint_type = optional(string, "Raw")
s3_buffering_size = optional(number, 2)
s3_buffering_interval = optional(number, 400)
s3_error_output_prefix = optional(string, null)
})) | {
"audit": {
"hec_acknowledgement_timeout": 600,
"hec_endpoint_type": "Raw",
"name": "fleet-audit-splunk",
"s3_buffering_interval": 400,
"s3_buffering_size": 10,
"s3_error_output_prefix": "audit/"
},
"results": {
"hec_acknowledgement_timeout": 600,
"hec_endpoint_type": "Raw",
"name": "fleet-osquery-results-splunk",
"s3_buffering_interval": 400,
"s3_buffering_size": 10,
"s3_error_output_prefix": "results/"
},
"status": {
"hec_acknowledgement_timeout": 600,
"hec_endpoint_type": "Raw",
"name": "fleet-osquery-status-splunk",
"s3_buffering_interval": 400,
"s3_buffering_size": 10,
"s3_error_output_prefix": "status/"
}
} | no |
| [s3\_bucket\_config](#input\_s3\_bucket\_config) | Configuration for the S3 bucket used to store failed Splunk delivery attempts | object({
name_prefix = optional(string, "fleet-splunk-failure")
expires_days = optional(number, 1)
}) | {
"expires_days": 1,
"name_prefix": "fleet-splunk-failure"
} | no |
+| [s3\_bucket\_tags](#input\_s3\_bucket\_tags) | Additional tags to apply to the S3 failure bucket. | `map(string)` | `{}` | no |
## Outputs
| Name | Description |
-|------|-------------|
+| ---- | ----------- |
| [fleet\_extra\_environment\_variables](#output\_fleet\_extra\_environment\_variables) | Environment variables to configure Fleet to use Splunk logging via Firehose |
| [fleet\_extra\_iam\_policies](#output\_fleet\_extra\_iam\_policies) | IAM policies required for Fleet to log to Splunk via Firehose |
| [fleet\_s3\_splunk\_failure\_config](#output\_fleet\_s3\_splunk\_failure\_config) | S3 bucket details - splunk-failure |
diff --git a/addons/logging-destination-splunk/main.tf b/addons/logging-destination-splunk/main.tf
index 9e8fa97..8a355de 100644
--- a/addons/logging-destination-splunk/main.tf
+++ b/addons/logging-destination-splunk/main.tf
@@ -3,6 +3,7 @@ data "aws_region" "current" {}
resource "aws_s3_bucket" "splunk-failure" { #tfsec:ignore:aws-s3-encryption-customer-key:exp:2022-07-01 #tfsec:ignore:aws-s3-enable-versioning #tfsec:ignore:aws-s3-enable-bucket-logging:exp:2022-06-15
bucket_prefix = var.s3_bucket_config.name_prefix
force_destroy = true
+ tags = var.s3_bucket_tags
}
resource "aws_s3_bucket_lifecycle_configuration" "splunk-failure" {
diff --git a/addons/logging-destination-splunk/variables.tf b/addons/logging-destination-splunk/variables.tf
index 3ca3c3b..eeec99b 100644
--- a/addons/logging-destination-splunk/variables.tf
+++ b/addons/logging-destination-splunk/variables.tf
@@ -71,3 +71,9 @@ variable "compression_format" {
default = "UNCOMPRESSED"
description = "Compression format for the Firehose delivery stream"
}
+
+variable "s3_bucket_tags" {
+ description = "Additional tags to apply to the S3 failure bucket."
+ type = map(string)
+ default = {}
+}
diff --git a/addons/osquery-carve/README.md b/addons/osquery-carve/README.md
index 466c944..2ae6ca0 100644
--- a/addons/osquery-carve/README.md
+++ b/addons/osquery-carve/README.md
@@ -21,15 +21,15 @@ This module automatically attaches a bucket policy that denies any requests made
## Requirements
| Name | Version |
-|------|---------|
+| ---- | ------- |
| [terraform](#requirement\_terraform) | >= 1.12.0 |
| [aws](#requirement\_aws) | >= 6.37.0 |
## Providers
| Name | Version |
-|------|---------|
-| [aws](#provider\_aws) | 6.41.0 |
+| ---- | ------- |
+| [aws](#provider\_aws) | 6.39.0 |
## Modules
@@ -38,7 +38,7 @@ No modules.
## Resources
| Name | Type |
-|------|------|
+| ---- | ---- |
| [aws_iam_policy.main](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
| [aws_kms_alias.osquery_carve](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_alias) | resource |
| [aws_kms_key.osquery_carve](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource |
@@ -59,12 +59,12 @@ No modules.
## Inputs
| Name | Description | Type | Default | Required |
-|------|-------------|------|---------|:--------:|
-| [osquery\_carve\_s3\_bucket](#input\_osquery\_carve\_s3\_bucket) | Configuration for the osquery carve S3 bucket, including optional customer-managed KMS settings. | object({
name = optional(string, "fleet-osquery-results-archive")
expires_days = optional(number, 1)
kms = optional(object({
kms_key_arn = optional(string, null)
create_kms_key = optional(bool, false)
kms_alias = optional(string, "osquery-carve")
kms_base_policy = optional(list(object({
sid = string
effect = string
principals = object({
type = string
identifiers = list(string)
})
actions = list(string)
resources = list(string)
conditions = optional(list(object({
test = string
variable = string
values = list(string)
})), [])
})), null)
extra_kms_policies = optional(list(any), [])
fleet_role_name = optional(string, null)
}), {
kms_key_arn = null
create_kms_key = false
kms_alias = "osquery-carve"
kms_base_policy = null
extra_kms_policies = []
fleet_role_name = null
})
}) | {
"expires_days": 1,
"kms": {
"create_kms_key": false,
"extra_kms_policies": [],
"fleet_role_name": null,
"kms_alias": "osquery-carve",
"kms_base_policy": null,
"kms_key_arn": null
},
"name": "fleet-osquery-results-archive"
} | no |
+| ---- | ----------- | ---- | ------- | :------: |
+| [osquery\_carve\_s3\_bucket](#input\_osquery\_carve\_s3\_bucket) | Configuration for the osquery carve S3 bucket, including optional customer-managed KMS settings. | object({
name = optional(string, "fleet-osquery-results-archive")
expires_days = optional(number, 1)
tags = optional(map(string), {})
kms = optional(object({
kms_key_arn = optional(string, null)
create_kms_key = optional(bool, false)
kms_alias = optional(string, "osquery-carve")
kms_base_policy = optional(list(object({
sid = string
effect = string
principals = object({
type = string
identifiers = list(string)
})
actions = list(string)
resources = list(string)
conditions = optional(list(object({
test = string
variable = string
values = list(string)
})), [])
})), null)
extra_kms_policies = optional(list(any), [])
fleet_role_name = optional(string, null)
}), {
kms_key_arn = null
create_kms_key = false
kms_alias = "osquery-carve"
kms_base_policy = null
extra_kms_policies = []
fleet_role_name = null
})
}) | {
"expires_days": 1,
"kms": {
"create_kms_key": false,
"extra_kms_policies": [],
"fleet_role_name": null,
"kms_alias": "osquery-carve",
"kms_base_policy": null,
"kms_key_arn": null
},
"name": "fleet-osquery-results-archive",
"tags": {}
} | no |
## Outputs
| Name | Description |
-|------|-------------|
+| ---- | ----------- |
| [fleet\_extra\_environment\_variables](#output\_fleet\_extra\_environment\_variables) | n/a |
| [fleet\_extra\_iam\_policies](#output\_fleet\_extra\_iam\_policies) | IAM policies required for Fleet to access the osquery carve S3 bucket. |
diff --git a/addons/osquery-carve/main.tf b/addons/osquery-carve/main.tf
index 0e143cf..66e3e88 100644
--- a/addons/osquery-carve/main.tf
+++ b/addons/osquery-carve/main.tf
@@ -82,6 +82,7 @@ data "aws_iam_role" "osquery_carve_fleet" {
resource "aws_s3_bucket" "main" { #tfsec:ignore:aws-s3-encryption-customer-key:exp:2028-07-01 #tfsec:ignore:aws-s3-enable-versioning #tfsec:ignore:aws-s3-enable-bucket-logging:exp:2028-07-01
bucket = var.osquery_carve_s3_bucket.name
+ tags = var.osquery_carve_s3_bucket.tags
}
resource "aws_s3_bucket_lifecycle_configuration" "main" {
diff --git a/addons/osquery-carve/variables.tf b/addons/osquery-carve/variables.tf
index 5e17bf6..52e9cf1 100644
--- a/addons/osquery-carve/variables.tf
+++ b/addons/osquery-carve/variables.tf
@@ -2,6 +2,7 @@ variable "osquery_carve_s3_bucket" {
type = object({
name = optional(string, "fleet-osquery-results-archive")
expires_days = optional(number, 1)
+ tags = optional(map(string), {})
kms = optional(object({
kms_key_arn = optional(string, null)
create_kms_key = optional(bool, false)
@@ -36,6 +37,7 @@ variable "osquery_carve_s3_bucket" {
default = {
name = "fleet-osquery-results-archive"
expires_days = 1
+ tags = {}
kms = {
kms_key_arn = null
create_kms_key = false