diff --git a/README.md b/README.md index 627545f..e8163c4 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)
}))
}) | `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-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..8cba919 100644
--- a/main.tf
+++ b/main.tf
@@ -529,6 +529,73 @@ 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_type = var.std_logging_destination.delivery_destination_type
+
+ 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.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 = var.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..fb82234 100644
--- a/variables.tf
+++ b/variables.tf
@@ -459,3 +459,49 @@ 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-