From 75518ec0977f601ddd89a6b866e8c69f7f33fbd3 Mon Sep 17 00:00:00 2001 From: Jean 'clark' EYMERT Date: Fri, 5 Dec 2025 11:17:22 +0100 Subject: [PATCH 1/2] feat: add support for CloudFront Standard Logging Add support for CloudFront Standard Logging using CloudWatch Log Delivery resources. This replaces the legacy logging_config and allows logging to S3, CloudWatch Logs, or Firehose as destinations. Standard logging is automatically enabled when std_logging_destination or std_logging_destination_arn is provided (no separate enable flag needed). New variables: - std_logging_region: region for logging resources (default: us-east-1) - std_logging_source_name: optional custom name for delivery source - std_logging_destination_arn: use existing destination (skip creation) - std_logging_destination: destination configuration (name, output_format, destination_arn) - std_logging_delivery: delivery configuration (field_delimiter, record_fields, s3_delivery_configuration) New outputs: - cloudfront_std_logging_source_arn - cloudfront_std_logging_source_name - cloudfront_std_logging_destination_arn - cloudfront_std_logging_delivery_id - cloudfront_std_logging_delivery_arn --- README.md | 13 ++++++ examples/complete/README.md | 4 +- examples/complete/main.tf | 84 +++++++++++++++++++++++++++---------- main.tf | 62 +++++++++++++++++++++++++++ outputs.tf | 29 +++++++++++++ variables.tf | 47 +++++++++++++++++++++ wrappers/main.tf | 19 +++++---- 7 files changed, 228 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 627545f..5625e36 100644 --- a/README.md +++ b/README.md @@ -178,6 +178,9 @@ No modules. | [aws_cloudfront_origin_access_control.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_origin_access_control) | resource | | [aws_cloudfront_response_headers_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_response_headers_policy) | resource | | [aws_cloudfront_vpc_origin.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_vpc_origin) | resource | +| [aws_cloudwatch_log_delivery.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_delivery) | resource | +| [aws_cloudwatch_log_delivery_destination.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_delivery_destination) | resource | +| [aws_cloudwatch_log_delivery_source.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_delivery_source) | resource | | [aws_cloudfront_cache_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/cloudfront_cache_policy) | data source | | [aws_cloudfront_origin_request_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/cloudfront_origin_request_policy) | data source | | [aws_cloudfront_response_headers_policy.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/cloudfront_response_headers_policy) | data source | @@ -210,6 +213,11 @@ No modules. | [restrictions](#input\_restrictions) | The restrictions configuration for this distribution |
object({
geo_restriction = object({
locations = optional(list(string))
restriction_type = optional(string, "none")
})
})
|
{
"geo_restriction": {
"restriction_type": "none"
}
}
| no | | [retain\_on\_delete](#input\_retain\_on\_delete) | Disables the distribution instead of deleting it when destroying the resource through Terraform. If this is set, the distribution needs to be deleted manually afterwards | `bool` | `null` | no | | [staging](#input\_staging) | Whether the distribution is a staging distribution | `bool` | `null` | no | +| [std\_logging\_delivery](#input\_std\_logging\_delivery) | Configuration for the standard logging delivery |
object({
field_delimiter = optional(string)
record_fields = optional(list(string))
s3_delivery_configuration = optional(object({
enable_hive_compatible_path = optional(bool)
suffix_path = optional(string)
}))
tags = optional(map(string), {})
})
| `null` | no | +| [std\_logging\_destination](#input\_std\_logging\_destination) | Configuration for creating a new standard logging destination. Ignored if std\_logging\_destination\_arn is set |
object({
name = string
output_format = optional(string, "json")
destination_arn = string
tags = optional(map(string), {})
})
| `null` | no | +| [std\_logging\_destination\_arn](#input\_std\_logging\_destination\_arn) | ARN of an existing CloudWatch Log Delivery Destination to use. If set, std\_logging\_destination is ignored | `string` | `null` | no | +| [std\_logging\_region](#input\_std\_logging\_region) | Region for standard logging resources. Required for CloudFront (must be us-east-1 for the source) | `string` | `"us-east-1"` | no | +| [std\_logging\_source\_name](#input\_std\_logging\_source\_name) | Name for the CloudWatch Log Delivery Source. Defaults to 'cloudfront-' | `string` | `null` | no | | [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no | | [viewer\_certificate](#input\_viewer\_certificate) | The SSL configuration for this distribution |
object({
acm_certificate_arn = optional(string)
cloudfront_default_certificate = optional(bool)
iam_certificate_id = optional(string)
minimum_protocol_version = optional(string, "TLSv1.2_2025")
ssl_support_method = optional(string)
})
| `{}` | no | | [vpc\_origin](#input\_vpc\_origin) | Map of CloudFront VPC origins |
map(object({
arn = string
http_port = number
https_port = number
name = optional(string)
origin_protocol_policy = string
origin_ssl_protocols = object({
items = optional(list(string), ["TLSv1.2"])
quantity = optional(number, 1)
})
timeouts = optional(object({
create = optional(string)
update = optional(string)
delete = optional(string)
}))
tags = optional(map(string), {})
}))
| `null` | no | @@ -234,6 +242,11 @@ No modules. | [cloudfront\_monitoring\_subscription\_id](#output\_cloudfront\_monitoring\_subscription\_id) | The ID of the CloudFront monitoring subscription, which corresponds to the `distribution_id`. | | [cloudfront\_origin\_access\_controls](#output\_cloudfront\_origin\_access\_controls) | The origin access controls created | | [cloudfront\_response\_headers\_policies](#output\_cloudfront\_response\_headers\_policies) | The response headers policies created | +| [cloudfront\_std\_logging\_delivery\_arn](#output\_cloudfront\_std\_logging\_delivery\_arn) | The ARN of the CloudWatch Log Delivery for standard logging | +| [cloudfront\_std\_logging\_delivery\_id](#output\_cloudfront\_std\_logging\_delivery\_id) | The ID of the CloudWatch Log Delivery for standard logging | +| [cloudfront\_std\_logging\_destination\_arn](#output\_cloudfront\_std\_logging\_destination\_arn) | The ARN of the CloudWatch Log Delivery Destination for standard logging | +| [cloudfront\_std\_logging\_source\_arn](#output\_cloudfront\_std\_logging\_source\_arn) | The ARN of the CloudWatch Log Delivery Source for standard logging | +| [cloudfront\_std\_logging\_source\_name](#output\_cloudfront\_std\_logging\_source\_name) | The name of the CloudWatch Log Delivery Source for standard logging | | [cloudfront\_vpc\_origins](#output\_cloudfront\_vpc\_origins) | The IDS of the VPC origin created | diff --git a/examples/complete/README.md b/examples/complete/README.md index 89805d9..6140c84 100644 --- a/examples/complete/README.md +++ b/examples/complete/README.md @@ -50,8 +50,8 @@ Note that this example may create resources which cost money. Run `terraform des | [aws_cloudfront_function.example](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_function) | resource | | [null_resource.download_package](https://registry.terraform.io/providers/hashicorp/null/latest/docs/resources/resource) | resource | | [aws_availability_zones.available](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/availability_zones) | data source | -| [aws_canonical_user_id.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/canonical_user_id) | data source | -| [aws_cloudfront_log_delivery_canonical_user_id.cloudfront](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/cloudfront_log_delivery_canonical_user_id) | data source | +| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source | +| [aws_iam_policy_document.log_bucket_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_iam_policy_document.s3_policy](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document) | data source | | [aws_route53_zone.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/route53_zone) | data source | diff --git a/examples/complete/main.tf b/examples/complete/main.tf index 171c004..59d4abc 100644 --- a/examples/complete/main.tf +++ b/examples/complete/main.tf @@ -42,9 +42,18 @@ module "cloudfront" { create_monitoring_subscription = true - logging_config = { - bucket = module.log_bucket.s3_bucket_bucket_domain_name - prefix = "cloudfront" + # Standard Logging - logs to S3 with CloudWatch Log Delivery + # Note: This replaces the legacy logging_config which used S3 ACLs + std_logging_destination = { + name = "cloudfront-logs-std" + output_format = "parquet" + destination_arn = "${module.log_bucket.s3_bucket_arn}/cloudfront" + } + std_logging_delivery = { + s3_delivery_configuration = { + enable_hive_compatible_path = true + suffix_path = "{DistributionId}/{yyyy}/{MM}/{dd}/{HH}" + } } origin_access_control = { @@ -435,9 +444,10 @@ data "aws_iam_policy_document" "s3_policy" { } } -data "aws_canonical_user_id" "current" {} -data "aws_cloudfront_log_delivery_canonical_user_id" "cloudfront" {} +data "aws_caller_identity" "current" {} +# S3 bucket for CloudFront Standard Logging +# Standard logging uses CloudWatch Log Delivery service and requires a bucket policy module "log_bucket" { source = "terraform-aws-modules/s3-bucket/aws" version = "~> 5.0" @@ -447,25 +457,57 @@ module "log_bucket" { # For example only force_destroy = true - control_object_ownership = true - object_ownership = "ObjectWriter" + # Standard logging requires a bucket policy for the log delivery service + attach_policy = true + policy = data.aws_iam_policy_document.log_bucket_policy.json - grant = [ - { - type = "CanonicalUser" - permission = "FULL_CONTROL" - id = data.aws_canonical_user_id.current.id - }, - { - type = "CanonicalUser" - permission = "FULL_CONTROL" - id = data.aws_cloudfront_log_delivery_canonical_user_id.cloudfront.id - # Ref. https://github.com/terraform-providers/terraform-provider-aws/issues/12512 - # Ref. https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/AccessLogs.html + tags = local.tags +} + +data "aws_iam_policy_document" "log_bucket_policy" { + statement { + sid = "AWSLogDeliveryWrite" + actions = ["s3:PutObject"] + resources = [ + "${module.log_bucket.s3_bucket_arn}/*" + ] + + principals { + type = "Service" + identifiers = ["delivery.logs.amazonaws.com"] } - ] - tags = local.tags + condition { + test = "StringEquals" + variable = "s3:x-amz-acl" + values = ["bucket-owner-full-control"] + } + + condition { + test = "StringEquals" + variable = "aws:SourceAccount" + values = [data.aws_caller_identity.current.account_id] + } + } + + statement { + sid = "AWSLogDeliveryAclCheck" + actions = ["s3:GetBucketAcl"] + resources = [ + module.log_bucket.s3_bucket_arn + ] + + principals { + type = "Service" + identifiers = ["delivery.logs.amazonaws.com"] + } + + condition { + test = "StringEquals" + variable = "aws:SourceAccount" + values = [data.aws_caller_identity.current.account_id] + } + } } ################################################################################ diff --git a/main.tf b/main.tf index 2e44c66..5355731 100644 --- a/main.tf +++ b/main.tf @@ -529,6 +529,68 @@ resource "aws_cloudfront_monitoring_subscription" "this" { } } +################################################################################ +# Standard Logging (replaces legacy logging_config) +################################################################################ + +locals { + # Standard logging is enabled when either destination_arn or destination config is provided + create_std_logging = var.create && (var.std_logging_destination_arn != null || var.std_logging_destination != null) +} + +resource "aws_cloudwatch_log_delivery_source" "this" { + count = local.create_std_logging ? 1 : 0 + + region = var.std_logging_region + + name = coalesce(var.std_logging_source_name, "cloudfront-${aws_cloudfront_distribution.this[0].id}") + log_type = "ACCESS_LOGS" + resource_arn = aws_cloudfront_distribution.this[0].arn +} + +resource "aws_cloudwatch_log_delivery_destination" "this" { + count = local.create_std_logging && var.std_logging_destination_arn == null ? 1 : 0 + + region = var.std_logging_region + + name = var.std_logging_destination.name + output_format = var.std_logging_destination.output_format + + delivery_destination_configuration { + destination_resource_arn = var.std_logging_destination.destination_arn + } + + tags = var.std_logging_destination.tags +} + +locals { + # Use computed ARN for the actual resource reference + std_logging_destination_arn = var.std_logging_destination_arn != null ? var.std_logging_destination_arn : try(aws_cloudwatch_log_delivery_destination.this[0].arn, null) +} + +resource "aws_cloudwatch_log_delivery" "this" { + count = local.create_std_logging ? 1 : 0 + + region = var.std_logging_region + + delivery_source_name = aws_cloudwatch_log_delivery_source.this[0].name + delivery_destination_arn = local.std_logging_destination_arn + + field_delimiter = try(var.std_logging_delivery.field_delimiter, null) + record_fields = try(var.std_logging_delivery.record_fields, null) + + dynamic "s3_delivery_configuration" { + for_each = try(var.std_logging_delivery.s3_delivery_configuration, null) != null ? [var.std_logging_delivery.s3_delivery_configuration] : [] + + content { + enable_hive_compatible_path = s3_delivery_configuration.value.enable_hive_compatible_path + suffix_path = s3_delivery_configuration.value.suffix_path + } + } + + tags = try(var.std_logging_delivery.tags, {}) +} + ################################################################################ # Data source reverse lookup by name # These are used to refer to resources by name instead of ID diff --git a/outputs.tf b/outputs.tf index 0fedfd6..4733910 100644 --- a/outputs.tf +++ b/outputs.tf @@ -96,3 +96,32 @@ output "cloudfront_monitoring_subscription_id" { description = " The ID of the CloudFront monitoring subscription, which corresponds to the `distribution_id`." value = try(aws_cloudfront_monitoring_subscription.this[0].id, null) } + +################################################################################ +# Standard Logging (replaces legacy logging_config) +################################################################################ + +output "cloudfront_std_logging_source_arn" { + description = "The ARN of the CloudWatch Log Delivery Source for standard logging" + value = try(aws_cloudwatch_log_delivery_source.this[0].arn, null) +} + +output "cloudfront_std_logging_source_name" { + description = "The name of the CloudWatch Log Delivery Source for standard logging" + value = try(aws_cloudwatch_log_delivery_source.this[0].name, null) +} + +output "cloudfront_std_logging_destination_arn" { + description = "The ARN of the CloudWatch Log Delivery Destination for standard logging" + value = try(aws_cloudwatch_log_delivery_destination.this[0].arn, null) +} + +output "cloudfront_std_logging_delivery_id" { + description = "The ID of the CloudWatch Log Delivery for standard logging" + value = try(aws_cloudwatch_log_delivery.this[0].id, null) +} + +output "cloudfront_std_logging_delivery_arn" { + description = "The ARN of the CloudWatch Log Delivery for standard logging" + value = try(aws_cloudwatch_log_delivery.this[0].arn, null) +} diff --git a/variables.tf b/variables.tf index e116580..5634fb7 100644 --- a/variables.tf +++ b/variables.tf @@ -459,3 +459,50 @@ variable "realtime_metrics_subscription_status" { type = string default = "Enabled" } + +################################################################################ +# Standard Logging (replaces legacy logging_config) +################################################################################ + +variable "std_logging_region" { + description = "Region for standard logging resources. Required for CloudFront (must be us-east-1 for the source)" + type = string + default = "us-east-1" +} + +variable "std_logging_source_name" { + description = "Name for the CloudWatch Log Delivery Source. Defaults to 'cloudfront-'" + type = string + default = null +} + +variable "std_logging_destination_arn" { + description = "ARN of an existing CloudWatch Log Delivery Destination to use. If set, std_logging_destination is ignored" + type = string + default = null +} + +variable "std_logging_destination" { + description = "Configuration for creating a new standard logging destination. Ignored if std_logging_destination_arn is set" + type = object({ + name = string + output_format = optional(string, "json") + destination_arn = string + tags = optional(map(string), {}) + }) + default = null +} + +variable "std_logging_delivery" { + description = "Configuration for the standard logging delivery" + type = object({ + field_delimiter = optional(string) + record_fields = optional(list(string)) + s3_delivery_configuration = optional(object({ + enable_hive_compatible_path = optional(bool) + suffix_path = optional(string) + })) + tags = optional(map(string), {}) + }) + default = null +} diff --git a/wrappers/main.tf b/wrappers/main.tf index 8a0f7c0..e2e1f17 100644 --- a/wrappers/main.tf +++ b/wrappers/main.tf @@ -35,11 +35,16 @@ module "wrapper" { restriction_type = "none" } }) - retain_on_delete = try(each.value.retain_on_delete, var.defaults.retain_on_delete, null) - staging = try(each.value.staging, var.defaults.staging, null) - tags = try(each.value.tags, var.defaults.tags, {}) - viewer_certificate = try(each.value.viewer_certificate, var.defaults.viewer_certificate, {}) - vpc_origin = try(each.value.vpc_origin, var.defaults.vpc_origin, null) - wait_for_deployment = try(each.value.wait_for_deployment, var.defaults.wait_for_deployment, null) - web_acl_id = try(each.value.web_acl_id, var.defaults.web_acl_id, null) + retain_on_delete = try(each.value.retain_on_delete, var.defaults.retain_on_delete, null) + staging = try(each.value.staging, var.defaults.staging, null) + std_logging_delivery = try(each.value.std_logging_delivery, var.defaults.std_logging_delivery, null) + std_logging_destination = try(each.value.std_logging_destination, var.defaults.std_logging_destination, null) + std_logging_destination_arn = try(each.value.std_logging_destination_arn, var.defaults.std_logging_destination_arn, null) + std_logging_region = try(each.value.std_logging_region, var.defaults.std_logging_region, "us-east-1") + std_logging_source_name = try(each.value.std_logging_source_name, var.defaults.std_logging_source_name, null) + tags = try(each.value.tags, var.defaults.tags, {}) + viewer_certificate = try(each.value.viewer_certificate, var.defaults.viewer_certificate, {}) + vpc_origin = try(each.value.vpc_origin, var.defaults.vpc_origin, null) + wait_for_deployment = try(each.value.wait_for_deployment, var.defaults.wait_for_deployment, null) + web_acl_id = try(each.value.web_acl_id, var.defaults.web_acl_id, null) } From 0ac9ed35a29cf56f8738bbfc44dc3b4685ab8c97 Mon Sep 17 00:00:00 2001 From: Jean 'clark' EYMERT Date: Mon, 8 Dec 2025 10:27:08 +0100 Subject: [PATCH 2/2] refactor(logging): use distribution tags and support all destination types - Remove dedicated tags from logging variables, use var.tags instead - Add delivery_destination_type parameter for X-Ray trace delivery support - Make destination_arn optional (not required for X-Ray) - Make delivery_destination_configuration a dynamic block --- README.md | 4 ++-- main.tf | 17 +++++++++++------ variables.tf | 9 ++++----- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 5625e36..e8163c4 100644 --- a/README.md +++ b/README.md @@ -213,8 +213,8 @@ No modules. | [restrictions](#input\_restrictions) | The restrictions configuration for this distribution |
object({
geo_restriction = object({
locations = optional(list(string))
restriction_type = optional(string, "none")
})
})
|
{
"geo_restriction": {
"restriction_type": "none"
}
}
| no | | [retain\_on\_delete](#input\_retain\_on\_delete) | Disables the distribution instead of deleting it when destroying the resource through Terraform. If this is set, the distribution needs to be deleted manually afterwards | `bool` | `null` | no | | [staging](#input\_staging) | Whether the distribution is a staging distribution | `bool` | `null` | no | -| [std\_logging\_delivery](#input\_std\_logging\_delivery) | Configuration for the standard logging delivery |
object({
field_delimiter = optional(string)
record_fields = optional(list(string))
s3_delivery_configuration = optional(object({
enable_hive_compatible_path = optional(bool)
suffix_path = optional(string)
}))
tags = optional(map(string), {})
})
| `null` | no | -| [std\_logging\_destination](#input\_std\_logging\_destination) | Configuration for creating a new standard logging destination. Ignored if std\_logging\_destination\_arn is set |
object({
name = string
output_format = optional(string, "json")
destination_arn = string
tags = optional(map(string), {})
})
| `null` | no | +| [std\_logging\_delivery](#input\_std\_logging\_delivery) | Configuration for the standard logging delivery |
object({
field_delimiter = optional(string)
record_fields = optional(list(string))
s3_delivery_configuration = optional(object({
enable_hive_compatible_path = optional(bool)
suffix_path = optional(string)
}))
})
| `null` | no | +| [std\_logging\_destination](#input\_std\_logging\_destination) | Configuration for creating a new standard logging destination. Ignored if std\_logging\_destination\_arn is set |
object({
name = string
output_format = optional(string, "json") # json, plain, w3c, raw, parquet
destination_arn = optional(string) # Required for S3, CloudWatch Logs, Firehose. Not required for X-Ray
delivery_destination_type = optional(string) # S3, CWL, FH, XRAY. Auto-detected from destination_arn if not set
})
| `null` | no | | [std\_logging\_destination\_arn](#input\_std\_logging\_destination\_arn) | ARN of an existing CloudWatch Log Delivery Destination to use. If set, std\_logging\_destination is ignored | `string` | `null` | no | | [std\_logging\_region](#input\_std\_logging\_region) | Region for standard logging resources. Required for CloudFront (must be us-east-1 for the source) | `string` | `"us-east-1"` | no | | [std\_logging\_source\_name](#input\_std\_logging\_source\_name) | Name for the CloudWatch Log Delivery Source. Defaults to 'cloudfront-' | `string` | `null` | no | diff --git a/main.tf b/main.tf index 5355731..8cba919 100644 --- a/main.tf +++ b/main.tf @@ -553,14 +553,19 @@ resource "aws_cloudwatch_log_delivery_destination" "this" { region = var.std_logging_region - name = var.std_logging_destination.name - output_format = var.std_logging_destination.output_format + name = var.std_logging_destination.name + output_format = var.std_logging_destination.output_format + delivery_destination_type = var.std_logging_destination.delivery_destination_type - delivery_destination_configuration { - destination_resource_arn = var.std_logging_destination.destination_arn + dynamic "delivery_destination_configuration" { + for_each = var.std_logging_destination.destination_arn != null ? [1] : [] + + content { + destination_resource_arn = var.std_logging_destination.destination_arn + } } - tags = var.std_logging_destination.tags + tags = var.tags } locals { @@ -588,7 +593,7 @@ resource "aws_cloudwatch_log_delivery" "this" { } } - tags = try(var.std_logging_delivery.tags, {}) + tags = var.tags } ################################################################################ diff --git a/variables.tf b/variables.tf index 5634fb7..fb82234 100644 --- a/variables.tf +++ b/variables.tf @@ -485,10 +485,10 @@ variable "std_logging_destination_arn" { variable "std_logging_destination" { description = "Configuration for creating a new standard logging destination. Ignored if std_logging_destination_arn is set" type = object({ - name = string - output_format = optional(string, "json") - destination_arn = string - tags = optional(map(string), {}) + name = string + output_format = optional(string, "json") # json, plain, w3c, raw, parquet + destination_arn = optional(string) # Required for S3, CloudWatch Logs, Firehose. Not required for X-Ray + delivery_destination_type = optional(string) # S3, CWL, FH, XRAY. Auto-detected from destination_arn if not set }) default = null } @@ -502,7 +502,6 @@ variable "std_logging_delivery" { enable_hive_compatible_path = optional(bool) suffix_path = optional(string) })) - tags = optional(map(string), {}) }) default = null }