diff --git a/org-logging/README.md b/org-logging/README.md
index bedbe74..bb60bb0 100644
--- a/org-logging/README.md
+++ b/org-logging/README.md
@@ -141,6 +141,8 @@ No modules.
|------|------|
| [aws_iam_policy.logging_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_policy) | resource |
| [aws_iam_role.logging](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/iam_role) | resource |
+| [aws_kms_alias.key](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_alias) | resource |
+| [aws_kms_key.key](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key) | resource |
| [aws_s3_bucket.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource |
| [aws_s3_bucket_acl.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_acl) | resource |
| [aws_s3_bucket_logging.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_logging) | resource |
@@ -167,6 +169,11 @@ No modules.
| [aws_iam_policy_document.additional_logging_deadletter](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.additional_logging_sqs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.bucket_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
+| [aws_iam_policy_document.empty](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
+| [aws_iam_policy_document.key](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
+| [aws_iam_policy_document.key_admin](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
+| [aws_iam_policy_document.key_orig](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
+| [aws_iam_policy_document.key_policy_combined](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.logging_assume](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.logging_cloudwatch](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
| [aws_iam_policy_document.logging_deadletter](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source |
@@ -192,8 +199,10 @@ No modules.
| [enable\_organization](#input\_enable\_organization) | Enable Logging as an organization trail. This will only work in the organization master account | `bool` | `false` | no |
| [enable\_sns](#input\_enable\_sns) | Flag to enable or disable the creation of SNS for Cloudtrail (TBD) | `bool` | `false` | no |
| [enable\_sqs](#input\_enable\_sqs) | Flag to enable or disable the creation of SQS attached to SNS for Cloudtrail, used for Splunk ingestion (TBD) | `bool` | `false` | no |
+| [kms\_admin\_roles](#input\_kms\_admin\_roles) | AWS KMS Key administrative role(s) which have full access to the key. The root user is included by default. | `list(string)` | `[]` | no |
| [kms\_key\_arn](#input\_kms\_key\_arn) | AWS Logging KMS ARN to be used for encrypting the ClouldTrail, S3 Bucket, and SQS | `string` | n/a | yes |
| [kms\_key\_management\_identifiers](#input\_kms\_key\_management\_identifiers) | AWS IAM ARNs (roles, groups, users) for full access to the created KMS Key for this bucket | `list(string)` | `[]` | no |
+| [kms\_policy\_document](#input\_kms\_policy\_document) | AWS KMS Key Policy Document JSON, merged with admin policy document | `string` | `null` | no |
| [name](#input\_name) | Name to apply to Cloudtrail, S3, SNS and SQS | `string` | `null` | no |
| [organization\_id](#input\_organization\_id) | AWS Organization ID | `string` | `""` | no |
| [override\_prefixes](#input\_override\_prefixes) | Override built-in prefixes by component (efs, s3, ebs, kms, role, policy, security-group). This should be used primarily for common infrastructure things | `map(string)` | `{}` | no |
@@ -204,6 +213,9 @@ No modules.
| Name | Description |
|------|-------------|
| [additional\_sqs\_info](#output\_additional\_sqs\_info) | Additional SQS ARNs and IDs (main, deadletter) |
+| [kms\_alias\_name](#output\_kms\_alias\_name) | Org Logging Key Alias name |
+| [kms\_key\_arn](#output\_kms\_key\_arn) | Org Logging Key ARN |
+| [kms\_key\_id](#output\_kms\_key\_id) | Org Logging Key ID |
| [s3\_bucket\_arn](#output\_s3\_bucket\_arn) | Created S3 Bucket ARN |
| [s3\_bucket\_id](#output\_s3\_bucket\_id) | Created S3 Bucket ID |
| [sns\_arn](#output\_sns\_arn) | SNS ARN |
diff --git a/org-logging/kms.tf b/org-logging/kms.tf
new file mode 100644
index 0000000..99450bf
--- /dev/null
+++ b/org-logging/kms.tf
@@ -0,0 +1,265 @@
+locals {
+ name = var.name == null ? format("%v-%v", lookup(local._defaults["org_logging"], "name"), local.region) : var.name
+ kms_key_name = format("%v%v", local._prefixes["kms"], local.name)
+ kms_admin_root = format("arn:%v:iam::%v:root", local.partition, local.account_id)
+ # kms_admin_roles = compact(concat([local.kms_admin_root], var.kms_admin_roles))
+ kms_admin_roles = var.kms_admin_roles
+ kms_policy_document = var.kms_policy_document != null ? var.kms_policy_document : data.aws_iam_policy_document.empty.json
+
+}
+
+resource "aws_kms_key" "key" {
+ description = "KMS CMK for Organization Logging"
+ enable_key_rotation = true
+ policy = data.aws_iam_policy_document.key_policy_combined.json
+
+ tags = merge(
+ local.base_tags,
+ var.tags,
+ {
+ "boc:aws:region" = local.region
+ Name = local.name
+ },
+ )
+}
+
+resource "aws_kms_alias" "key" {
+ name = "alias/${local.kms_key_name}"
+ target_key_id = aws_kms_key.key.key_id
+}
+
+data "aws_iam_policy_document" "key_policy_combined" {
+ source_policy_documents = [
+ data.aws_iam_policy_document.key.json,
+ data.aws_iam_policy_document.key_admin.json,
+ local.kms_policy_document
+ ]
+}
+
+data "aws_iam_policy_document" "key_orig" {
+ policy_id = "Logging KMS Access"
+ statement {
+ sid = "EnableIAMUserPermissions"
+ effect = "Allow"
+ actions = ["kms:*"]
+ resources = ["*"]
+ principals {
+ type = "AWS"
+ identifiers = [local.kms_admin_root]
+ }
+ }
+ statement {
+ sid = "AllowOrgLoggingEncryptLogs"
+ effect = "Allow"
+ actions = ["kms:GenerateDataKey*"]
+ resources = ["*"]
+ principals {
+ type = "Service"
+ identifiers = ["cloudtrail.amazonaws.com", "logs.amazonaws.com", "logs.${local.region}.amazonaws.com"]
+ }
+ condition {
+ test = "StringLike"
+ variable = "kms:EncryptionContext:aws:cloudtrail:arn"
+ # values = [format("arn:%v:cloudtrail:%v:%v:trail/*",local.partition,local.region,local.account_id]
+ values = [format("arn:%v:cloudtrail:*:%v:trail/*", local.partition, local.account_id)]
+ }
+ }
+ statement {
+ sid = "AllowOrgLoggingKeyActivities"
+ effect = "Allow"
+ actions = [
+ "kms:Describe*",
+ "log:AssociateKmsKey",
+ "log:DisassociateKmsKey"
+ ]
+ resources = ["*"]
+ principals {
+ type = "Service"
+ identifiers = ["cloudtrail.amazonaws.com", "logs.amazonaws.com", "logs.${local.region}.amazonaws.com"]
+ }
+ }
+ statement {
+ sid = "AllowPrincipalsDecryptLogFiles"
+ effect = "Allow"
+ principals {
+ type = "AWS"
+ identifiers = ["*"]
+ }
+ actions = [
+ "kms:Encrypt",
+ "kms:Decrypt",
+ "kms:ReEncryptFrom"
+ ]
+ resources = ["*"]
+ condition {
+ test = "StringEquals"
+ variable = "kms:CallerAccount"
+ values = [var.account_id]
+ }
+ condition {
+ test = "StringLike"
+ variable = "kms:EncryptionContext:aws:cloudtrail:arn"
+ # values = [format("arn:%v:cloudtrail:%v:%v:trail/*",local.partition,local.region,local.account_id]
+ values = [format("arn:%v:cloudtrail:*:%v:trail/*", local.partition, local.account_id)]
+ }
+ }
+ statement {
+ sid = "EnableCrossAccountDecryptLogFiles"
+ effect = "Allow"
+ principals {
+ type = "AWS"
+ identifiers = ["*"]
+ }
+ actions = [
+ "kms:Encrypt",
+ "kms:Decrypt",
+ "kms:ReEncryptFrom"
+ ]
+ resources = ["*"]
+ condition {
+ test = "StringEquals"
+ variable = "kms:CallerAccount"
+ values = [var.account_id]
+ }
+ condition {
+ test = "StringLike"
+ variable = "kms:EncryptionContext:aws:cloudtrail:arn"
+ # values = [format("arn:%v:cloudtrail:%v:%v:trail/*",local.partition,local.region,local.account_id]
+ values = [format("arn:%v:cloudtrail:*:%v:trail/*", local.partition, local.account_id)]
+ }
+ }
+ statement {
+ sid = "AllowAliasCreationDuringSetup"
+ effect = "Allow"
+ actions = ["kms:CreateAlias"]
+ resources = ["*"]
+ principals {
+ type = "AWS"
+ identifiers = ["*"]
+ }
+ condition {
+ test = "StringEquals"
+ variable = "kms:CallerAccount"
+ values = [var.account_id]
+ }
+ condition {
+ test = "StringEquals"
+ variable = "kms:ViaService"
+ values = [format("ec2.%v.amazonaws.com", local.region)]
+ }
+ }
+}
+
+data "aws_iam_policy_document" "key_admin" {
+ dynamic "statement" {
+ for_each = length(local.kms_admin_roles) > 0 ? [1] : []
+ content {
+ sid = "BuiltinKMSAdminRoles"
+ effect = "Allow"
+ actions = ["kms:*"]
+ resources = ["*"]
+ principals {
+ type = "AWS"
+ identifiers = local.kms_admin_roles
+ }
+ }
+ }
+}
+
+data "aws_iam_policy_document" "empty" {}
+
+
+#---
+# key policy for clodutrail
+# https://docs.aws.amazon.com/awscloudtrail/latest/userguide/create-kms-key-policy-for-cloudtrail.html
+# can't use aws_cloudtrail.this.arn as it makes for a circular reference
+#
+# from aws-setup-s3-object-logging
+#---
+data "aws_iam_policy_document" "key" {
+ policy_id = "object-logging-cloud-trail"
+ # manage key by root and other principals
+ statement {
+ sid = "IAMPermissionsAccessKMSManagement"
+ effect = "Allow"
+ actions = ["kms:*"]
+ resources = ["*"]
+ principals {
+ type = "AWS"
+ identifiers = [local.kms_admin_root]
+ }
+ }
+ # let cloudtrial, logs, sns, and sqs find key
+ statement {
+ sid = "KMSDescribeKeyFromServices"
+ effect = "Allow"
+ actions = ["kms:DescribeKey"]
+ resources = ["*"]
+ principals {
+ type = "Service"
+ identifiers = ["cloudtrail.amazonaws.com", "sns.amazonaws.com", "sqs.amazonaws.com"]
+ }
+ }
+ statement {
+ sid = "OrgLoggingKMSEncryptAccess"
+ effect = "Allow"
+ actions = [
+ "kms:Decrypt*",
+ "kms:Encrypt*",
+ "kms:ReEncrypt*",
+ "kms:GenerateDataKey",
+ ]
+ resources = ["*"]
+ principals {
+ type = "Service"
+ identifiers = ["cloudtrail.amazonaws.com"]
+ }
+ # condition {
+ # test = "StringLike"
+ # variable = "kms:EncryptionContext:aws:cloudtrail:arn"
+ # values = [format("arn:%v:cloudtrail:*:%v:trail/*", local.partition, local.account_id)]
+ # }
+ }
+ # https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/encrypt-log-data-kms.html
+ statement {
+ sid = "Cloudwatch"
+ effect = "Allow"
+ actions = [
+ "kms:Decrypt*",
+ "kms:Encrypt*",
+ "kms:ReEncrypt*",
+ "kms:GenerateDataKey*",
+ "kms:Describe*"
+ ]
+ resources = ["*"]
+ principals {
+ type = "Service"
+ identifiers = ["logs.amazonaws.com", "logs.${local.region}.amazonaws.com"]
+ }
+ condition {
+ test = "StringLike"
+ variable = "kms:EncryptionContext:aws:logs:arn"
+ values = [format("arn:%v:logs:%v:%v:log-group:*", local.partition, local.region, local.account_id)]
+ }
+ }
+ # https://aws.amazon.com/blogs/compute/encrypting-messages-published-to-amazon-sns-with-aws-kms/
+ # https://docs.aws.amazon.com/sns/latest/dg/sns-key-management.html#sns-what-permissions-for-sse
+ # https://docs.aws.amazon.com/sns/latest/dg/sns-enable-encryption-for-topic-sqs-queue-subscriptions.html
+ statement {
+ sid = "ServiceMSAccess"
+ effect = "Allow"
+ actions = [
+ "kms:Decrypt*",
+ "kms:GenerateDataKey*",
+ "kms:Describe*"
+ ]
+ resources = ["*"]
+ principals {
+ type = "Service"
+ identifiers = ["sns.amazonaws.com", "sqs.amazonaws.com"]
+ }
+ }
+}
+
+# producers and consumers
+# https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/sqs-key-management.html#compatibility-with-aws-services
diff --git a/org-logging/outputs.kms.tf b/org-logging/outputs.kms.tf
new file mode 100644
index 0000000..f5e2fb6
--- /dev/null
+++ b/org-logging/outputs.kms.tf
@@ -0,0 +1,14 @@
+output "kms_key_id" {
+ description = "Org Logging Key ID"
+ value = aws_kms_key.key.id
+}
+
+output "kms_key_arn" {
+ description = "Org Logging Key ARN"
+ value = aws_kms_key.key.arn
+}
+
+output "kms_alias_name" {
+ description = "Org Logging Key Alias name"
+ value = aws_kms_alias.key.arn
+}
diff --git a/org-logging/variables.kms.tf b/org-logging/variables.kms.tf
new file mode 100644
index 0000000..7a5dbab
--- /dev/null
+++ b/org-logging/variables.kms.tf
@@ -0,0 +1,23 @@
+variable "name" {
+ description = "Name to apply to Org Logging KMS Key (default: k-inf-org-logging)"
+ type = string
+ default = null
+}
+
+variable "kms_policy_document" {
+ description = "AWS KMS Key Policy Document JSON, merged with admin policy document"
+ type = string
+ default = null
+}
+
+variable "kms_admin_roles" {
+ description = "AWS KMS Key administrative role(s) which have full access to the key. The root user is included by default."
+ type = list(string)
+ default = []
+}
+
+variable "component_tags" {
+ description = "Additional tags for Components (s3, kms, ddb)"
+ type = map(map(string))
+ default = { "s3" = {}, "kms" = {}, "ddb" = {} }
+}