diff --git a/terraform/addons/byo-s3-logging-destination-firehose/.header.md b/terraform/addons/byo-s3-logging-destination-firehose/.header.md
new file mode 100644
index 0000000000..cd409454e3
--- /dev/null
+++ b/terraform/addons/byo-s3-logging-destination-firehose/.header.md
@@ -0,0 +1,6 @@
+# Logging Destination: Firehose
+This addon provides a Kinesis Firehose logging destination for Fleet.
+
+First apply the `target-account` module which will provision the necessary bucket, KMS key, and policies.
+
+Then apply the `firehose` module with the required variables.
diff --git a/terraform/addons/byo-s3-logging-destination-firehose/firehose/.terraform-docs.yml b/terraform/addons/byo-s3-logging-destination-firehose/firehose/.terraform-docs.yml
new file mode 100644
index 0000000000..1d139ddb40
--- /dev/null
+++ b/terraform/addons/byo-s3-logging-destination-firehose/firehose/.terraform-docs.yml
@@ -0,0 +1 @@
+header-from: .header.md
diff --git a/terraform/addons/byo-s3-logging-destination-firehose/firehose/README.md b/terraform/addons/byo-s3-logging-destination-firehose/firehose/README.md
new file mode 100644
index 0000000000..8270139f8a
--- /dev/null
+++ b/terraform/addons/byo-s3-logging-destination-firehose/firehose/README.md
@@ -0,0 +1,51 @@
+# Logging Destination: Firehose
+This addon provides a Kinesis Firehose logging destination for Fleet with support for cross account S3 delivery.
+
+## Requirements
+
+Apply module `target-account` to provision destination bucket, kms key, and IAM policies.
+
+## Providers
+
+| Name | Version |
+|---------------------------------------------------|---------|
+| [aws](#provider\_aws) | 4.49.0 |
+
+## Modules
+
+No modules.
+
+## Resources
+
+| Name | Type |
+|----------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------|
+| [aws_iam_policy.firehose-results](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
+| [aws_iam_policy.firehose-status](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
+| [aws_iam_role.firehose-results](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
+| [aws_iam_role.firehose-status](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
+| [aws_iam_role_policy_attachment.firehose-results](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
+| [aws_iam_role_policy_attachment.firehose-status](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role_policy_attachment) | resource |
+| [aws_kinesis_firehose_delivery_stream.osquery_results](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kinesis_firehose_delivery_stream) | resource |
+| [aws_kinesis_firehose_delivery_stream.osquery_status](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kinesis_firehose_delivery_stream) | resource |
+| [aws_iam_policy_document.osquery_firehose_assume_role](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
+| [aws_iam_policy_document.osquery_results_policy_doc](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
+| [aws_iam_policy_document.osquery_status_policy_doc](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
+| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|-------------------------------|----------------------------------------|----------|---------------------|:--------:|
+| firehose_results_name | n/a | `string` | no default provided | yes |
+| firehose_status_name | n/a | `string` | no default provided | yes |
+| customer_prefix | used for resource tagging | `string` | no default provided | yes |
+| kms_key_arn | key arn used to encrypt target buckets | `string` | no default provided | yes |
+| results_destination_s3_bucket | bucket name to send osquery results | `string` | no default provided | yes |
+| status_destination_s3_bucket | bucket name to send osquery status | `string` | no default provided | yes |
+
+
+## Outputs
+
+| Name | Description |
+|-----------------------------------------------------------------------------------------------------------------|-------------|
+| [fleet-extra-env-variables](#output\_fleet-extra-env-variables) | n/a |
diff --git a/terraform/addons/byo-s3-logging-destination-firehose/firehose/main.tf b/terraform/addons/byo-s3-logging-destination-firehose/firehose/main.tf
new file mode 100644
index 0000000000..59986d18ac
--- /dev/null
+++ b/terraform/addons/byo-s3-logging-destination-firehose/firehose/main.tf
@@ -0,0 +1,104 @@
+data "aws_region" "current" {}
+data "aws_caller_identity" "current" {}
+
+data "aws_iam_policy_document" "osquery_firehose_assume_role" {
+ statement {
+ effect = "Allow"
+ actions = ["sts:AssumeRole"]
+ principals {
+ identifiers = ["firehose.amazonaws.com"]
+ type = "Service"
+ }
+ }
+}
+
+data "aws_iam_policy_document" "firehose_policy" {
+ statement {
+ effect = "Allow"
+ actions = [
+ "s3:AbortMultipartUpload",
+ "s3:GetBucketLocation",
+ "s3:GetObject",
+ "s3:ListBucket",
+ "s3:ListBucketMultipartUploads",
+ "s3:PutObject",
+ "s3:PutObjectAcl" // required according to https://docs.aws.amazon.com/firehose/latest/dev/controlling-access.html#using-iam-s3
+ ]
+ resources = [
+ "arn:aws:s3:::${var.results_destination_s3_bucket}",
+ "arn:aws:s3:::${var.results_destination_s3_bucket}/*",
+ "arn:aws:s3:::${var.status_destination_s3_bucket}",
+ "arn:aws:s3:::${var.status_destination_s3_bucket}/*"
+ ]
+ }
+
+ statement {
+ effect = "Allow"
+ actions = ["kms:GenerateDataKey*"]
+ resources = [var.kms_key_arn]
+ }
+
+ statement {
+ effect = "Allow"
+ actions = ["logs:PutLogEvents"]
+ resources = [
+ "arn:aws:logs:${data.aws_region.current.id}:${data.aws_caller_identity.current.account_id}:log-group:/aws/kinesisfirehose/${var.firehose_results_name}:*",
+ "arn:aws:logs:${data.aws_region.current.id}:${data.aws_caller_identity.current.account_id}:log-group:/aws/kinesisfirehose/${var.firehose_status_name}:*"
+ ]
+ }
+}
+
+resource "aws_iam_role" "firehose" {
+ name = "${var.customer_prefix}-firehose-cross-account-role"
+ assume_role_policy = data.aws_iam_policy_document.osquery_firehose_assume_role.json
+}
+
+resource "aws_iam_policy" "firehose" {
+ policy = data.aws_iam_policy_document.firehose_policy.json
+}
+
+resource "aws_iam_role_policy_attachment" "firehose" {
+ policy_arn = aws_iam_policy.firehose.arn
+ role = aws_iam_role.firehose.name
+}
+
+resource "aws_kinesis_firehose_delivery_stream" "osquery_results" {
+ name = var.firehose_results_name
+ destination = "s3"
+
+ s3_configuration {
+ prefix = var.results_object_prefix
+ role_arn = aws_iam_role.firehose.arn
+ bucket_arn = "arn:aws:s3:::${var.results_destination_s3_bucket}"
+ kms_key_arn = var.kms_key_arn
+ }
+}
+
+resource "aws_kinesis_firehose_delivery_stream" "osquery_status" {
+ name = var.firehose_status_name
+ destination = "s3"
+
+ s3_configuration {
+ prefix = var.status_object_prefix
+ role_arn = aws_iam_role.firehose
+ bucket_arn = "arn:aws:s3:::${var.status_destination_s3_bucket}"
+ kms_key_arn = var.kms_key_arn
+ }
+}
+
+data "aws_iam_policy_document" "firehose-logging" {
+ statement {
+ actions = [
+ "firehose:DescribeDeliveryStream",
+ "firehose:PutRecord",
+ "firehose:PutRecordBatch",
+ ]
+ resources = [aws_kinesis_firehose_delivery_stream.osquery_results.arn, aws_kinesis_firehose_delivery_stream.osquery_status.arn]
+ }
+}
+
+resource "aws_iam_policy" "firehose-logging" {
+ name = "fleet-firehose-logging"
+ description = "An IAM policy for fleet to log to Firehose destinations"
+ policy = data.aws_iam_policy_document.firehose-logging.json
+}
diff --git a/terraform/addons/byo-s3-logging-destination-firehose/firehose/outputs.tf b/terraform/addons/byo-s3-logging-destination-firehose/firehose/outputs.tf
new file mode 100644
index 0000000000..0aea5c0816
--- /dev/null
+++ b/terraform/addons/byo-s3-logging-destination-firehose/firehose/outputs.tf
@@ -0,0 +1,19 @@
+output "fleet_extra_environment_variables" {
+ value = {
+ FLEET_FIREHOSE_STATUS_STREAM = aws_kinesis_firehose_delivery_stream.osquery_status.name
+ FLEET_FIREHOSE_RESULT_STREAM = aws_kinesis_firehose_delivery_stream.osquery_results.name
+ FLEET_FIREHOSE_REGION = data.aws_region.current.name
+ FLEET_OSQUERY_STATUS_LOG_PLUGIN = "firehose"
+ FLEET_OSQUERY_RESULT_LOG_PLUGIN = "firehose"
+ }
+}
+
+output "fleet_extra_iam_policies" {
+ value = [
+ aws_iam_policy.firehose-logging.arn
+ ]
+}
+
+output "firehose_role_arn" {
+ value = aws_iam_role.firehose.arn
+}
diff --git a/terraform/addons/byo-s3-logging-destination-firehose/firehose/variables.tf b/terraform/addons/byo-s3-logging-destination-firehose/firehose/variables.tf
new file mode 100644
index 0000000000..eafe6daff0
--- /dev/null
+++ b/terraform/addons/byo-s3-logging-destination-firehose/firehose/variables.tf
@@ -0,0 +1,42 @@
+variable "results_destination_s3_bucket" {
+ type = string
+ description = "s3 bucket name for osquery results"
+}
+
+variable "status_destination_s3_bucket" {
+ type = string
+ description = "s3 bucket name for osquery status"
+}
+
+variable "kms_key_arn" {
+ type = string
+ description = "kms key arn used to encrypt destination buckets"
+ default = "arn:aws:kms:us-east-2:123456789123:key/fix-me"
+}
+
+variable "firehose_results_name" {
+ type = string
+ description = "name of the firehose delivery stream for osquery results logs"
+}
+
+variable "firehose_status_name" {
+ type = string
+ description = "name of the firehose delivery stream for osquery status logs"
+}
+
+variable "customer_prefix" {
+ type = string
+ description = "customer prefix to use to namespace all resources"
+}
+
+variable "results_object_prefix" {
+ type = string
+ description = "object prefix for results logs e.g. 'results/'"
+ default = "results/"
+}
+
+variable "status_object_prefix" {
+ type = string
+ description = "object prefix for results logs e.g. 'status/'"
+ default = "status/"
+}
\ No newline at end of file
diff --git a/terraform/addons/byo-s3-logging-destination-firehose/firehose/version.tf b/terraform/addons/byo-s3-logging-destination-firehose/firehose/version.tf
new file mode 100644
index 0000000000..00143c571d
--- /dev/null
+++ b/terraform/addons/byo-s3-logging-destination-firehose/firehose/version.tf
@@ -0,0 +1,10 @@
+terraform {
+ required_version = ">= 1.3.7"
+
+ required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ version = ">= 4.52.0"
+ }
+ }
+}
\ No newline at end of file
diff --git a/terraform/addons/byo-s3-logging-destination-firehose/target-account/README.md b/terraform/addons/byo-s3-logging-destination-firehose/target-account/README.md
new file mode 100644
index 0000000000..2ff22be984
--- /dev/null
+++ b/terraform/addons/byo-s3-logging-destination-firehose/target-account/README.md
@@ -0,0 +1,46 @@
+# Logging Destination: S3
+This module will provision necessary resources to feed osquery results/status logs into S3.
+
+## Requirements
+
+None
+
+## Providers
+
+| Name | Version |
+|---------------------------------------------------|---------|
+| [aws](#provider\_aws) | 4.52.0 |
+
+## Modules
+
+No modules.
+
+## Resources
+
+| Name | Type |
+|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-------------|
+| [aws_s3_bucket.osquery-results](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource |
+| [aws_s3_bucket.osquery-status](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource |
+| [aws_s3_bucket_acl.osquery-results](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_acl) | resource |
+| [aws_s3_bucket_acl.osquery-status](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_acl) | resource |
+| [aws_s3_bucket_public_access_block.osquery-results](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource |
+| [aws_s3_bucket_public_access_block.osquery-status](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource |
+| [aws_s3_bucket_server_side_encryption_configuration.osquery-results](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource |
+| [aws_s3_bucket_server_side_encryption_configuration.osquery-status](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource |
+| [aws_region.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/region) | data source |
+
+## Inputs
+
+| Name | Description | Type | Default | Required |
+|------------------------|----------------------------------------|----------|---------------------|:--------:|
+| osquery_results_bucket | name of the bucket for results logging | `string` | no default provided | yes |
+| osquery_status_bucket | name of the bucket for status logging | `string` | no default provided | yes |
+| fleet_iam_role_arn | the role ARN from Fleet Cloud | `string` | no default provided | yes |
+
+## Outputs
+
+| Name | Description |
+|---------------------|-------------|
+| kms_key_arn | n/a |
+| results_bucket_name | n/a |
+| status_bucket_name | n/a |
diff --git a/terraform/addons/byo-s3-logging-destination-firehose/target-account/main.tf b/terraform/addons/byo-s3-logging-destination-firehose/target-account/main.tf
new file mode 100644
index 0000000000..4ee98db0bf
--- /dev/null
+++ b/terraform/addons/byo-s3-logging-destination-firehose/target-account/main.tf
@@ -0,0 +1,179 @@
+data "aws_caller_identity" "current" {}
+
+data "aws_iam_policy_document" "results" {
+ statement {
+ principals {
+ identifiers = [var.fleet_iam_role_arn]
+ type = "AWS"
+ }
+ effect = "Allow"
+ actions = [
+ "s3:AbortMultipartUpload",
+ "s3:GetBucketLocation",
+ "s3:GetObject",
+ "s3:ListBucket",
+ "s3:ListBucketMultipartUploads",
+ "s3:PutObject",
+ "s3:PutObjectAcl" // required according to https://docs.aws.amazon.com/firehose/latest/dev/controlling-access.html#using-iam-s3
+ ]
+ resources = [
+ aws_s3_bucket.osquery-results.arn,
+ "${aws_s3_bucket.osquery-results.arn}/*"
+ ]
+ }
+
+ statement {
+ principals {
+ identifiers = [var.fleet_iam_role_arn]
+ type = "AWS"
+ }
+ effect = "Allow"
+ actions = ["s3:PutObject"]
+ resources = ["${aws_s3_bucket.osquery-results.arn}/*"]
+ condition {
+ test = "StringEquals"
+ values = ["bucket-owner-full-control"]
+ variable = "s3:x-amz-acl"
+ }
+ }
+}
+
+data "aws_iam_policy_document" "status" {
+ statement {
+ principals {
+ identifiers = [var.fleet_iam_role_arn]
+ type = "AWS"
+ }
+ effect = "Allow"
+ actions = [
+ "s3:AbortMultipartUpload",
+ "s3:GetBucketLocation",
+ "s3:GetObject",
+ "s3:ListBucket",
+ "s3:ListBucketMultipartUploads",
+ "s3:PutObject",
+ "s3:PutObjectAcl" // required according to https://docs.aws.amazon.com/firehose/latest/dev/controlling-access.html#using-iam-s3
+ ]
+ resources = [
+ aws_s3_bucket.osquery-status.arn,
+ "${aws_s3_bucket.osquery-status.arn}/*"
+ ]
+ }
+
+ statement {
+ principals {
+ identifiers = [var.fleet_iam_role_arn]
+ type = "AWS"
+ }
+ effect = "Allow"
+ actions = ["s3:PutObject"]
+ resources = ["${aws_s3_bucket.osquery-status.arn}/*"]
+ condition {
+ test = "StringEquals"
+ values = ["bucket-owner-full-control"]
+ variable = "s3:x-amz-acl"
+ }
+ }
+}
+
+resource "aws_s3_bucket" "osquery-results" {
+ bucket = var.osquery_results_bucket
+}
+
+resource "aws_s3_bucket" "osquery-status" {
+ bucket = var.osquery_status_bucket
+}
+
+resource "aws_s3_bucket_policy" "results" {
+ bucket = aws_s3_bucket.osquery-results.id
+ policy = data.aws_iam_policy_document.results.json
+}
+
+resource "aws_s3_bucket_policy" "status" {
+ bucket = aws_s3_bucket.osquery-status.id
+ policy = data.aws_iam_policy_document.status.json
+}
+
+resource "aws_s3_bucket_public_access_block" "results" {
+ bucket = aws_s3_bucket.osquery-results.id
+ block_public_acls = true
+ block_public_policy = true
+ ignore_public_acls = true
+ restrict_public_buckets = true
+}
+
+resource "aws_s3_bucket_public_access_block" "status" {
+ bucket = aws_s3_bucket.osquery-status.id
+ block_public_acls = true
+ block_public_policy = true
+ ignore_public_acls = true
+ restrict_public_buckets = true
+}
+
+
+resource "aws_s3_bucket_acl" "results" {
+ bucket = aws_s3_bucket.osquery-results.id
+ acl = "private"
+}
+
+resource "aws_s3_bucket_acl" "status" {
+ bucket = aws_s3_bucket.osquery-status.id
+ acl = "private"
+}
+
+data "aws_iam_policy_document" "key_policy" {
+ // self account has access to key
+ statement {
+ principals {
+ identifiers = [
+ "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"
+ ]
+ type = "AWS"
+ }
+ effect = "Allow"
+ actions = ["*"]
+ resources = ["*"]
+ }
+
+ // only allow the IAM role from fleet aws account
+ statement {
+ principals {
+ identifiers = [var.fleet_iam_role_arn]
+ type = "AWS"
+ }
+ effect = "Allow"
+ actions = ["kms:GenerateDataKey*"]
+ resources = ["*"] // this is basically "self" aka this particular key
+ }
+}
+
+// customer managed key to allow other aws account access
+resource "aws_kms_key" "key" {
+ enable_key_rotation = true
+ policy = data.aws_iam_policy_document.key_policy.json
+ description = "key used for osquery results and status bucket encryption"
+}
+
+// enable server side encryption with KMS key
+resource "aws_s3_bucket_server_side_encryption_configuration" "results" {
+ bucket = aws_s3_bucket.osquery-results.id
+ rule {
+ bucket_key_enabled = true
+ apply_server_side_encryption_by_default {
+ kms_master_key_id = aws_kms_key.key.id
+ sse_algorithm = "aws:kms"
+ }
+ }
+}
+
+resource "aws_s3_bucket_server_side_encryption_configuration" "status" {
+ bucket = aws_s3_bucket.osquery-status.id
+ rule {
+ bucket_key_enabled = true
+ apply_server_side_encryption_by_default {
+ kms_master_key_id = aws_kms_key.key.id
+ sse_algorithm = "aws:kms"
+ }
+ }
+}
+
diff --git a/terraform/addons/byo-s3-logging-destination-firehose/target-account/outputs.tf b/terraform/addons/byo-s3-logging-destination-firehose/target-account/outputs.tf
new file mode 100644
index 0000000000..69f21d29fd
--- /dev/null
+++ b/terraform/addons/byo-s3-logging-destination-firehose/target-account/outputs.tf
@@ -0,0 +1,11 @@
+output "kms_key_arn" {
+ value = aws_kms_key.key.arn
+}
+
+output "results_bucket_name" {
+ value = aws_s3_bucket.osquery-results.id
+}
+
+output "status_bucket_name" {
+ value = aws_s3_bucket.osquery-status.id
+}
\ No newline at end of file
diff --git a/terraform/addons/byo-s3-logging-destination-firehose/target-account/variables.tf b/terraform/addons/byo-s3-logging-destination-firehose/target-account/variables.tf
new file mode 100644
index 0000000000..ac68078ad1
--- /dev/null
+++ b/terraform/addons/byo-s3-logging-destination-firehose/target-account/variables.tf
@@ -0,0 +1,14 @@
+variable "osquery_results_bucket" {
+ type = string
+ description = "name of the bucket to store osquery results logs"
+}
+
+variable "osquery_status_bucket" {
+ type = string
+ description = "name of the bucket to store osquery status logs"
+}
+
+variable "fleet_iam_role_arn" {
+ type = string
+ description = "the arn of the fleet role that firehose will assume to write data to your bucket"
+}
\ No newline at end of file
diff --git a/terraform/addons/byo-s3-logging-destination-firehose/target-account/version.tf b/terraform/addons/byo-s3-logging-destination-firehose/target-account/version.tf
new file mode 100644
index 0000000000..00143c571d
--- /dev/null
+++ b/terraform/addons/byo-s3-logging-destination-firehose/target-account/version.tf
@@ -0,0 +1,10 @@
+terraform {
+ required_version = ">= 1.3.7"
+
+ required_providers {
+ aws = {
+ source = "hashicorp/aws"
+ version = ">= 4.52.0"
+ }
+ }
+}
\ No newline at end of file