Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down Expand Up @@ -210,6 +213,11 @@ No modules.
| <a name="input_restrictions"></a> [restrictions](#input\_restrictions) | The restrictions configuration for this distribution | <pre>object({<br/> geo_restriction = object({<br/> locations = optional(list(string))<br/> restriction_type = optional(string, "none")<br/> })<br/> })</pre> | <pre>{<br/> "geo_restriction": {<br/> "restriction_type": "none"<br/> }<br/>}</pre> | no |
| <a name="input_retain_on_delete"></a> [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 |
| <a name="input_staging"></a> [staging](#input\_staging) | Whether the distribution is a staging distribution | `bool` | `null` | no |
| <a name="input_std_logging_delivery"></a> [std\_logging\_delivery](#input\_std\_logging\_delivery) | Configuration for the standard logging delivery | <pre>object({<br/> field_delimiter = optional(string)<br/> record_fields = optional(list(string))<br/> s3_delivery_configuration = optional(object({<br/> enable_hive_compatible_path = optional(bool)<br/> suffix_path = optional(string)<br/> }))<br/> })</pre> | `null` | no |
| <a name="input_std_logging_destination"></a> [std\_logging\_destination](#input\_std\_logging\_destination) | Configuration for creating a new standard logging destination. Ignored if std\_logging\_destination\_arn is set | <pre>object({<br/> name = string<br/> output_format = optional(string, "json") # json, plain, w3c, raw, parquet<br/> destination_arn = optional(string) # Required for S3, CloudWatch Logs, Firehose. Not required for X-Ray<br/> delivery_destination_type = optional(string) # S3, CWL, FH, XRAY. Auto-detected from destination_arn if not set<br/> })</pre> | `null` | no |
| <a name="input_std_logging_destination_arn"></a> [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 |
| <a name="input_std_logging_region"></a> [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 |
| <a name="input_std_logging_source_name"></a> [std\_logging\_source\_name](#input\_std\_logging\_source\_name) | Name for the CloudWatch Log Delivery Source. Defaults to 'cloudfront-<distribution\_id>' | `string` | `null` | no |
| <a name="input_tags"></a> [tags](#input\_tags) | A map of tags to add to all resources | `map(string)` | `{}` | no |
| <a name="input_viewer_certificate"></a> [viewer\_certificate](#input\_viewer\_certificate) | The SSL configuration for this distribution | <pre>object({<br/> acm_certificate_arn = optional(string)<br/> cloudfront_default_certificate = optional(bool)<br/> iam_certificate_id = optional(string)<br/> minimum_protocol_version = optional(string, "TLSv1.2_2025")<br/> ssl_support_method = optional(string)<br/> })</pre> | `{}` | no |
| <a name="input_vpc_origin"></a> [vpc\_origin](#input\_vpc\_origin) | Map of CloudFront VPC origins | <pre>map(object({<br/> arn = string<br/> http_port = number<br/> https_port = number<br/> name = optional(string)<br/> origin_protocol_policy = string<br/> origin_ssl_protocols = object({<br/> items = optional(list(string), ["TLSv1.2"])<br/> quantity = optional(number, 1)<br/> })<br/> timeouts = optional(object({<br/> create = optional(string)<br/> update = optional(string)<br/> delete = optional(string)<br/> }))<br/> tags = optional(map(string), {})<br/> }))</pre> | `null` | no |
Expand All @@ -234,6 +242,11 @@ No modules.
| <a name="output_cloudfront_monitoring_subscription_id"></a> [cloudfront\_monitoring\_subscription\_id](#output\_cloudfront\_monitoring\_subscription\_id) | The ID of the CloudFront monitoring subscription, which corresponds to the `distribution_id`. |
| <a name="output_cloudfront_origin_access_controls"></a> [cloudfront\_origin\_access\_controls](#output\_cloudfront\_origin\_access\_controls) | The origin access controls created |
| <a name="output_cloudfront_response_headers_policies"></a> [cloudfront\_response\_headers\_policies](#output\_cloudfront\_response\_headers\_policies) | The response headers policies created |
| <a name="output_cloudfront_std_logging_delivery_arn"></a> [cloudfront\_std\_logging\_delivery\_arn](#output\_cloudfront\_std\_logging\_delivery\_arn) | The ARN of the CloudWatch Log Delivery for standard logging |
| <a name="output_cloudfront_std_logging_delivery_id"></a> [cloudfront\_std\_logging\_delivery\_id](#output\_cloudfront\_std\_logging\_delivery\_id) | The ID of the CloudWatch Log Delivery for standard logging |
| <a name="output_cloudfront_std_logging_destination_arn"></a> [cloudfront\_std\_logging\_destination\_arn](#output\_cloudfront\_std\_logging\_destination\_arn) | The ARN of the CloudWatch Log Delivery Destination for standard logging |
| <a name="output_cloudfront_std_logging_source_arn"></a> [cloudfront\_std\_logging\_source\_arn](#output\_cloudfront\_std\_logging\_source\_arn) | The ARN of the CloudWatch Log Delivery Source for standard logging |
| <a name="output_cloudfront_std_logging_source_name"></a> [cloudfront\_std\_logging\_source\_name](#output\_cloudfront\_std\_logging\_source\_name) | The name of the CloudWatch Log Delivery Source for standard logging |
| <a name="output_cloudfront_vpc_origins"></a> [cloudfront\_vpc\_origins](#output\_cloudfront\_vpc\_origins) | The IDS of the VPC origin created |
<!-- END_TF_DOCS -->

Expand Down
4 changes: 2 additions & 2 deletions examples/complete/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |

Expand Down
84 changes: 63 additions & 21 deletions examples/complete/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -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"
Expand All @@ -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]
}
}
}

################################################################################
Expand Down
67 changes: 67 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
29 changes: 29 additions & 0 deletions outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
46 changes: 46 additions & 0 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -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-<distribution_id>'"
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") # 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
}

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)
}))
})
default = null
}
Loading