diff --git a/README.md b/README.md index b644ea3..34bfd55 100644 --- a/README.md +++ b/README.md @@ -127,6 +127,7 @@ No modules. | [aws_cloudfront_monitoring_subscription.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_monitoring_subscription) | resource | | [aws_cloudfront_origin_access_control.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_origin_access_control) | resource | | [aws_cloudfront_origin_access_identity.this](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_origin_access_identity) | 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_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 | @@ -143,6 +144,7 @@ No modules. | [create\_monitoring\_subscription](#input\_create\_monitoring\_subscription) | If enabled, the resource for monitoring subscription will created. | `bool` | `false` | no | | [create\_origin\_access\_control](#input\_create\_origin\_access\_control) | Controls if CloudFront origin access control should be created | `bool` | `false` | no | | [create\_origin\_access\_identity](#input\_create\_origin\_access\_identity) | Controls if CloudFront origin access identity should be created | `bool` | `false` | no | +| [create\_response\_headers\_policy](#input\_create\_response\_headers\_policy) | Controls if CloudFront response headers policies should be created | `bool` | `false` | no | | [create\_vpc\_origin](#input\_create\_vpc\_origin) | If enabled, the resource for VPC origin will be created. | `bool` | `false` | no | | [custom\_error\_response](#input\_custom\_error\_response) | One or more custom error response elements | `any` | `{}` | no | | [default\_cache\_behavior](#input\_default\_cache\_behavior) | The default cache behavior for this distribution | `any` | `null` | no | @@ -159,6 +161,7 @@ No modules. | [origin\_group](#input\_origin\_group) | One or more origin\_group for this distribution (multiples allowed). | `any` | `{}` | no | | [price\_class](#input\_price\_class) | The price class for this distribution. One of PriceClass\_All, PriceClass\_200, PriceClass\_100 | `string` | `null` | no | | [realtime\_metrics\_subscription\_status](#input\_realtime\_metrics\_subscription\_status) | A flag that indicates whether additional CloudWatch metrics are enabled for a given CloudFront distribution. Valid values are `Enabled` and `Disabled`. | `string` | `"Enabled"` | no | +| [response\_headers\_policy](#input\_response\_headers\_policy) | Map of CloudFront response headers policies with their configurations |
map(object({
name = optional(string)
comment = optional(string)
cors_config = optional(object({
access_control_allow_credentials = bool
origin_override = bool
access_control_allow_headers = object({
items = list(string)
})
access_control_allow_methods = object({
items = list(string)
})
access_control_allow_origins = object({
items = list(string)
})
access_control_expose_headers = optional(object({
items = list(string)
}))
access_control_max_age_sec = optional(number)
}))
custom_headers_config = optional(object({
items = list(object({
header = string
override = bool
value = string
}))
}))
remove_headers_config = optional(object({
items = list(object({
header = string
}))
}))
security_headers_config = optional(object({
content_security_policy = optional(object({
content_security_policy = string
override = bool
}))
content_type_options = optional(object({
override = bool
}))
frame_options = optional(object({
frame_option = string
override = bool
}))
referrer_policy = optional(object({
referrer_policy = string
override = bool
}))
strict_transport_security = optional(object({
access_control_max_age_sec = number
override = bool
include_subdomains = optional(bool)
preload = optional(bool)
}))
xss_protection = optional(object({
mode_block = bool
override = bool
protection = bool
report_uri = optional(string)
}))
}))
server_timing_headers_config = optional(object({
enabled = bool
sampling_rate = number
}))
})) | `{}` | 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` | `false` | no |
| [staging](#input\_staging) | Whether the distribution is a staging distribution. | `bool` | `false` | no |
| [tags](#input\_tags) | A map of tags to assign to the resource. | `map(string)` | `null` | no |
@@ -189,6 +192,9 @@ No modules.
| [cloudfront\_origin\_access\_identities](#output\_cloudfront\_origin\_access\_identities) | The origin access identities created |
| [cloudfront\_origin\_access\_identity\_iam\_arns](#output\_cloudfront\_origin\_access\_identity\_iam\_arns) | The IAM arns of the origin access identities created |
| [cloudfront\_origin\_access\_identity\_ids](#output\_cloudfront\_origin\_access\_identity\_ids) | The IDS of the origin access identities created |
+| [cloudfront\_response\_headers\_policies](#output\_cloudfront\_response\_headers\_policies) | The response headers policies created |
+| [cloudfront\_response\_headers\_policy\_etags](#output\_cloudfront\_response\_headers\_policy\_etags) | The ETags of the response headers policies created |
+| [cloudfront\_response\_headers\_policy\_ids](#output\_cloudfront\_response\_headers\_policy\_ids) | The IDs of the response headers policies created |
| [cloudfront\_vpc\_origin\_ids](#output\_cloudfront\_vpc\_origin\_ids) | The IDS of the VPC origin created |
diff --git a/examples/complete/main.tf b/examples/complete/main.tf
index 76bc588..2a24ced 100644
--- a/examples/complete/main.tf
+++ b/examples/complete/main.tf
@@ -176,6 +176,8 @@ module "cloudfront" {
cache_policy_name = "Managed-CachingOptimized"
origin_request_policy_name = "Managed-UserAgentRefererHeaders"
response_headers_policy_name = "Managed-SimpleCORS"
+ # using a response header policy you're dynamically creating below
+ # response_header_policy: "cors_policy"
function_association = {
# Valid keys: viewer-request, viewer-response
@@ -231,6 +233,79 @@ module "cloudfront" {
locations = ["NO", "UA", "US", "GB"]
}
+ create_response_headers_policy = true
+ response_headers_policy = {
+ cors_policy = {
+ name = "CORSPolicy"
+ comment = "CORS configuration for API"
+
+ cors_config = {
+ access_control_allow_credentials = true
+ origin_override = true
+
+ access_control_allow_headers = {
+ items = ["*"]
+ }
+
+ access_control_allow_methods = {
+ items = ["GET", "POST", "PUT", "DELETE", "OPTIONS"]
+ }
+
+ access_control_allow_origins = {
+ items = ["https://example.com", "https://app.example.com"]
+ }
+
+ access_control_expose_headers = {
+ items = ["X-Custom-Header", "X-Request-Id"]
+ }
+
+ access_control_max_age_sec = 3600
+ }
+ }
+ custom_headers = {
+ name = "CustomHeadersPolicy"
+ comment = "Add custom response headers"
+
+ custom_headers_config = {
+ items = [
+ {
+ header = "X-Powered-By"
+ override = true
+ value = "MyApp/1.0"
+ },
+ {
+ header = "X-API-Version"
+ override = false
+ value = "v2"
+ },
+ {
+ header = "Cache-Control"
+ override = true
+ value = "public, max-age=3600"
+ }
+ ]
+ }
+ }
+ remove_headers = {
+ name = "RemoveHeadersPolicy"
+ comment = "Remove unwanted headers from origin"
+
+ remove_headers_config = {
+ items = [
+ {
+ header = "x-robots-tag"
+ },
+ {
+ header = "server"
+ },
+ {
+ header = "x-powered-by"
+ }
+ ]
+ }
+ }
+ }
+
}
######
diff --git a/main.tf b/main.tf
index f773d7e..9482281 100644
--- a/main.tf
+++ b/main.tf
@@ -1,9 +1,150 @@
locals {
- create_origin_access_identity = var.create_origin_access_identity && length(keys(var.origin_access_identities)) > 0
- create_origin_access_control = var.create_origin_access_control && length(keys(var.origin_access_control)) > 0
- create_vpc_origin = var.create_vpc_origin && length(keys(var.vpc_origin)) > 0
+ create_origin_access_identity = var.create_origin_access_identity && length(keys(var.origin_access_identities)) > 0
+ create_origin_access_control = var.create_origin_access_control && length(keys(var.origin_access_control)) > 0
+ create_vpc_origin = var.create_vpc_origin && length(keys(var.vpc_origin)) > 0
+ create_response_headers_policy = var.create_response_headers_policy && length(keys(var.response_headers_policy)) > 0
}
+resource "aws_cloudfront_response_headers_policy" "this" {
+ for_each = local.create_response_headers_policy ? var.response_headers_policy : {}
+
+ name = each.value.name != null ? each.value.name : each.key
+ comment = each.value.comment
+
+ dynamic "cors_config" {
+ for_each = each.value.cors_config != null ? [each.value.cors_config] : []
+
+ content {
+ access_control_allow_credentials = cors_config.value.access_control_allow_credentials
+ origin_override = cors_config.value.origin_override
+ access_control_max_age_sec = cors_config.value.access_control_max_age_sec != null ? cors_config.value.access_control_max_age_sec : null
+
+ access_control_allow_headers {
+ items = cors_config.value.access_control_allow_headers.items
+ }
+
+ access_control_allow_methods {
+ items = cors_config.value.access_control_allow_methods.items
+ }
+
+ access_control_allow_origins {
+ items = cors_config.value.access_control_allow_origins.items
+ }
+
+ dynamic "access_control_expose_headers" {
+ for_each = cors_config.value.access_control_expose_headers != null ? [cors_config.value.access_control_expose_headers] : []
+
+ content {
+ items = access_control_expose_headers.value.items
+ }
+ }
+ }
+ }
+
+ dynamic "custom_headers_config" {
+ for_each = each.value.custom_headers_config != null ? [each.value.custom_headers_config] : []
+
+ content {
+ dynamic "items" {
+ for_each = custom_headers_config.value.items
+
+ content {
+ header = items.value.header
+ override = items.value.override
+ value = items.value.value
+ }
+ }
+ }
+ }
+
+ dynamic "remove_headers_config" {
+ for_each = each.value.remove_headers_config != null ? [each.value.remove_headers_config] : []
+
+ content {
+ dynamic "items" {
+ for_each = remove_headers_config.value.items
+
+ content {
+ header = items.value.header
+ }
+ }
+ }
+ }
+
+ dynamic "security_headers_config" {
+ for_each = each.value.security_headers_config != null ? [each.value.security_headers_config] : []
+
+ content {
+ dynamic "content_security_policy" {
+ for_each = security_headers_config.value.content_security_policy != null ? [security_headers_config.value.content_security_policy] : []
+
+ content {
+ content_security_policy = content_security_policy.value.content_security_policy
+ override = content_security_policy.value.override
+ }
+ }
+
+ dynamic "content_type_options" {
+ for_each = security_headers_config.value.content_type_options != null ? [security_headers_config.value.content_type_options] : []
+
+ content {
+ override = content_type_options.value.override
+ }
+ }
+
+ dynamic "frame_options" {
+ for_each = security_headers_config.value.frame_options != null ? [security_headers_config.value.frame_options] : []
+
+ content {
+ frame_option = frame_options.value.frame_option
+ override = frame_options.value.override
+ }
+ }
+
+ dynamic "referrer_policy" {
+ for_each = security_headers_config.value.referrer_policy != null ? [security_headers_config.value.referrer_policy] : []
+
+ content {
+ referrer_policy = referrer_policy.value.referrer_policy
+ override = referrer_policy.value.override
+ }
+ }
+
+ dynamic "strict_transport_security" {
+ for_each = security_headers_config.value.strict_transport_security != null ? [security_headers_config.value.strict_transport_security] : []
+
+ content {
+ access_control_max_age_sec = strict_transport_security.value.access_control_max_age_sec
+ override = strict_transport_security.value.override
+ include_subdomains = strict_transport_security.value.include_subdomains
+ preload = strict_transport_security.value.preload
+ }
+ }
+
+ dynamic "xss_protection" {
+ for_each = security_headers_config.value.xss_protection != null ? [security_headers_config.value.xss_protection] : []
+
+ content {
+ mode_block = xss_protection.value.mode_block
+ override = xss_protection.value.override
+ protection = xss_protection.value.protection
+ report_uri = xss_protection.value.report_uri
+ }
+ }
+ }
+ }
+
+ dynamic "server_timing_headers_config" {
+ for_each = each.value.server_timing_headers_config != null ? [each.value.server_timing_headers_config] : []
+
+ content {
+ enabled = server_timing_headers_config.value.enabled
+ sampling_rate = server_timing_headers_config.value.sampling_rate
+ }
+ }
+}
+
+
resource "aws_cloudfront_origin_access_identity" "this" {
for_each = local.create_origin_access_identity ? var.origin_access_identities : {}
diff --git a/outputs.tf b/outputs.tf
index 29e7642..c9efa9b 100644
--- a/outputs.tf
+++ b/outputs.tf
@@ -87,3 +87,18 @@ output "cloudfront_vpc_origin_ids" {
description = "The IDS of the VPC origin created"
value = local.create_vpc_origin ? [for v in aws_cloudfront_vpc_origin.this : v.id] : []
}
+
+output "cloudfront_response_headers_policies" {
+ description = "The response headers policies created"
+ value = local.create_response_headers_policy ? { for k, v in aws_cloudfront_response_headers_policy.this : k => v } : {}
+}
+
+output "cloudfront_response_headers_policy_ids" {
+ description = "The IDs of the response headers policies created"
+ value = local.create_response_headers_policy ? { for k, v in aws_cloudfront_response_headers_policy.this : k => v.id } : {}
+}
+
+output "cloudfront_response_headers_policy_etags" {
+ description = "The ETags of the response headers policies created"
+ value = local.create_response_headers_policy ? { for k, v in aws_cloudfront_response_headers_policy.this : k => v.etag } : {}
+}
diff --git a/variables.tf b/variables.tf
index afeec33..83e987a 100644
--- a/variables.tf
+++ b/variables.tf
@@ -210,3 +210,85 @@ variable "vpc_origin_timeouts" {
type = map(string)
default = {}
}
+
+variable "create_response_headers_policy" {
+ description = "Controls if CloudFront response headers policies should be created"
+ type = bool
+ default = false
+}
+
+variable "response_headers_policy" {
+ description = "Map of CloudFront response headers policies with their configurations"
+ type = map(object({
+ name = optional(string)
+ comment = optional(string)
+
+ cors_config = optional(object({
+ access_control_allow_credentials = bool
+ origin_override = bool
+ access_control_allow_headers = object({
+ items = list(string)
+ })
+ access_control_allow_methods = object({
+ items = list(string)
+ })
+ access_control_allow_origins = object({
+ items = list(string)
+ })
+ access_control_expose_headers = optional(object({
+ items = list(string)
+ }))
+ access_control_max_age_sec = optional(number)
+ }))
+
+ custom_headers_config = optional(object({
+ items = list(object({
+ header = string
+ override = bool
+ value = string
+ }))
+ }))
+
+ remove_headers_config = optional(object({
+ items = list(object({
+ header = string
+ }))
+ }))
+
+ security_headers_config = optional(object({
+ content_security_policy = optional(object({
+ content_security_policy = string
+ override = bool
+ }))
+ content_type_options = optional(object({
+ override = bool
+ }))
+ frame_options = optional(object({
+ frame_option = string
+ override = bool
+ }))
+ referrer_policy = optional(object({
+ referrer_policy = string
+ override = bool
+ }))
+ strict_transport_security = optional(object({
+ access_control_max_age_sec = number
+ override = bool
+ include_subdomains = optional(bool)
+ preload = optional(bool)
+ }))
+ xss_protection = optional(object({
+ mode_block = bool
+ override = bool
+ protection = bool
+ report_uri = optional(string)
+ }))
+ }))
+
+ server_timing_headers_config = optional(object({
+ enabled = bool
+ sampling_rate = number
+ }))
+ }))
+ default = {}
+}
diff --git a/wrappers/main.tf b/wrappers/main.tf
index 750d27e..1bdb676 100644
--- a/wrappers/main.tf
+++ b/wrappers/main.tf
@@ -10,6 +10,7 @@ module "wrapper" {
create_monitoring_subscription = try(each.value.create_monitoring_subscription, var.defaults.create_monitoring_subscription, false)
create_origin_access_control = try(each.value.create_origin_access_control, var.defaults.create_origin_access_control, false)
create_origin_access_identity = try(each.value.create_origin_access_identity, var.defaults.create_origin_access_identity, false)
+ create_response_headers_policy = try(each.value.create_response_headers_policy, var.defaults.create_response_headers_policy, false)
create_vpc_origin = try(each.value.create_vpc_origin, var.defaults.create_vpc_origin, false)
custom_error_response = try(each.value.custom_error_response, var.defaults.custom_error_response, {})
default_cache_behavior = try(each.value.default_cache_behavior, var.defaults.default_cache_behavior, null)
@@ -33,6 +34,7 @@ module "wrapper" {
origin_group = try(each.value.origin_group, var.defaults.origin_group, {})
price_class = try(each.value.price_class, var.defaults.price_class, null)
realtime_metrics_subscription_status = try(each.value.realtime_metrics_subscription_status, var.defaults.realtime_metrics_subscription_status, "Enabled")
+ response_headers_policy = try(each.value.response_headers_policy, var.defaults.response_headers_policy, {})
retain_on_delete = try(each.value.retain_on_delete, var.defaults.retain_on_delete, false)
staging = try(each.value.staging, var.defaults.staging, false)
tags = try(each.value.tags, var.defaults.tags, null)