Skip to content
Merged
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
17 changes: 9 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,15 +100,15 @@ aws acm request-certificate --domain-name example.com --subject-alternative-name

| Name | Version |
| ---- | ------- |
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.0 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 6.13.0 |
| <a name="requirement_terraform"></a> [terraform](#requirement\_terraform) | >= 1.2 |
| <a name="requirement_aws"></a> [aws](#requirement\_aws) | >= 6.28.0 |
| <a name="requirement_local"></a> [local](#requirement\_local) | >= 1.2 |

## Providers

| Name | Version |
| ---- | ------- |
| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 6.13.0 |
| <a name="provider_aws"></a> [aws](#provider\_aws) | >= 6.28.0 |

## Modules

Expand Down Expand Up @@ -145,7 +145,7 @@ aws acm request-certificate --domain-name example.com --subject-alternative-name
| <a name="input_context"></a> [context](#input\_context) | Single object for setting entire context at once.<br/>See description of individual variables for details.<br/>Leave string and numeric variables as `null` to use default value.<br/>Individual variable settings (non-null) override settings in context object,<br/>except for attributes, tags, and additional\_tag\_map, which are merged. | `any` | <pre>{<br/> "additional_tag_map": {},<br/> "attributes": [],<br/> "delimiter": null,<br/> "descriptor_formats": {},<br/> "enabled": true,<br/> "environment": null,<br/> "id_length_limit": null,<br/> "label_key_case": null,<br/> "label_order": [],<br/> "label_value_case": null,<br/> "labels_as_tags": [<br/> "unset"<br/> ],<br/> "name": null,<br/> "namespace": null,<br/> "regex_replace_chars": null,<br/> "stage": null,<br/> "tags": {},<br/> "tenant": null<br/>}</pre> | no |
| <a name="input_custom_error_response"></a> [custom\_error\_response](#input\_custom\_error\_response) | List of one or more custom error response element maps | <pre>list(object({<br/> error_caching_min_ttl = optional(string, null)<br/> error_code = string<br/> response_code = optional(string, null)<br/> response_page_path = optional(string, null)<br/> }))</pre> | `[]` | no |
| <a name="input_custom_header"></a> [custom\_header](#input\_custom\_header) | List of one or more custom headers passed to the origin | <pre>list(object({<br/> name = string<br/> value = string<br/> }))</pre> | `[]` | no |
| <a name="input_custom_origins"></a> [custom\_origins](#input\_custom\_origins) | One or more custom origins for this distribution (multiples allowed). See documentation for configuration options description https://www.terraform.io/docs/providers/aws/r/cloudfront_distribution.html#origin-arguments | <pre>list(object({<br/> domain_name = string<br/> origin_id = string<br/> origin_path = optional(string, "")<br/> origin_access_control_id = optional(string, null)<br/> response_completion_timeout = optional(number, 0)<br/> custom_headers = optional(list(object({<br/> name = string<br/> value = string<br/> })), [])<br/> custom_origin_config = optional(object({<br/> http_port = optional(number, 80)<br/> https_port = optional(number, 443)<br/> origin_protocol_policy = optional(string, "match-viewer")<br/> origin_ssl_protocols = optional(list(string), ["TLSv1", "TLSv1.1", "TLSv1.2"])<br/> origin_keepalive_timeout = optional(number, 5)<br/> origin_read_timeout = optional(number, 30)<br/> }), null)<br/> s3_origin_config = optional(object({<br/> origin_access_identity = string<br/> }), null)<br/> origin_shield = optional(object({<br/> enabled = optional(bool, false)<br/> region = optional(string, null)<br/> }), null)<br/> }))</pre> | `[]` | no |
| <a name="input_custom_origins"></a> [custom\_origins](#input\_custom\_origins) | One or more custom origins for this distribution (multiples allowed). See documentation for configuration options description https://www.terraform.io/docs/providers/aws/r/cloudfront_distribution.html#origin-arguments | <pre>list(object({<br/> domain_name = string<br/> origin_id = string<br/> origin_path = optional(string, "")<br/> origin_access_control_id = optional(string, null)<br/> response_completion_timeout = optional(number, 0)<br/> custom_headers = optional(list(object({<br/> name = string<br/> value = string<br/> })), [])<br/> custom_origin_config = optional(object({<br/> http_port = optional(number, 80)<br/> https_port = optional(number, 443)<br/> origin_protocol_policy = optional(string, "match-viewer")<br/> origin_ssl_protocols = optional(list(string), ["TLSv1", "TLSv1.1", "TLSv1.2"])<br/> origin_keepalive_timeout = optional(number, 5)<br/> origin_read_timeout = optional(number, 30)<br/> }), null)<br/> s3_origin_config = optional(object({<br/> origin_access_identity = string<br/> }), null)<br/> vpc_origin_config = optional(object({<br/> vpc_origin_id = string<br/> origin_keepalive_timeout = optional(number, 5)<br/> origin_read_timeout = optional(number, 30)<br/> owner_account_id = optional(string, null)<br/> }), null)<br/> origin_shield = optional(object({<br/> enabled = optional(bool, false)<br/> region = optional(string, null)<br/> }), null)<br/> }))</pre> | `[]` | no |
| <a name="input_default_root_object"></a> [default\_root\_object](#input\_default\_root\_object) | Object that CloudFront return when requests the root URL | `string` | `"index.html"` | no |
| <a name="input_default_ttl"></a> [default\_ttl](#input\_default\_ttl) | Default amount of time (in seconds) that an object is in a CloudFront cache | `number` | `60` | no |
| <a name="input_delimiter"></a> [delimiter](#input\_delimiter) | Delimiter to be used between ID elements.<br/>Defaults to `-` (hyphen). Set to `""` to use no delimiter at all. | `string` | `null` | no |
Expand Down Expand Up @@ -195,7 +195,7 @@ aws acm request-certificate --domain-name example.com --subject-alternative-name
| <a name="input_origin_request_policy_id"></a> [origin\_request\_policy\_id](#input\_origin\_request\_policy\_id) | ID of the origin request policy attached to the cache behavior | `string` | `null` | no |
| <a name="input_origin_shield"></a> [origin\_shield](#input\_origin\_shield) | The CloudFront Origin Shield settings | <pre>object({<br/> enabled = optional(bool, false)<br/> region = optional(string, null)<br/> })</pre> | `null` | no |
| <a name="input_origin_ssl_protocols"></a> [origin\_ssl\_protocols](#input\_origin\_ssl\_protocols) | The SSL/TLS protocols that you want CloudFront to use when communicating with your origin over HTTPS | `list(string)` | <pre>[<br/> "TLSv1",<br/> "TLSv1.1",<br/> "TLSv1.2"<br/>]</pre> | no |
| <a name="input_origin_type"></a> [origin\_type](#input\_origin\_type) | The type of origin configuration to use. Valid values are 'custom' or 's3'. | `string` | `"custom"` | no |
| <a name="input_origin_type"></a> [origin\_type](#input\_origin\_type) | The type of origin configuration to use. Valid values are 'custom', 's3', or 'vpc'. | `string` | `"custom"` | no |
| <a name="input_parent_zone_id"></a> [parent\_zone\_id](#input\_parent\_zone\_id) | ID of the hosted zone to contain this record (or specify `parent_zone_name`) | `string` | `""` | no |
| <a name="input_parent_zone_name"></a> [parent\_zone\_name](#input\_parent\_zone\_name) | Name of the hosted zone to contain this record (or specify `parent_zone_id`) | `string` | `""` | no |
| <a name="input_price_class"></a> [price\_class](#input\_price\_class) | Price class for this distribution: `PriceClass_All`, `PriceClass_200`, `PriceClass_100` | `string` | `"PriceClass_100"` | no |
Expand All @@ -211,6 +211,7 @@ aws acm request-certificate --domain-name example.com --subject-alternative-name
| <a name="input_viewer_minimum_protocol_version"></a> [viewer\_minimum\_protocol\_version](#input\_viewer\_minimum\_protocol\_version) | The minimum version of the SSL protocol that you want CloudFront to use for HTTPS connections. This is ignored if the default CloudFront certificate is used. | `string` | `"TLSv1.2_2021"` | no |
| <a name="input_viewer_mtls_config"></a> [viewer\_mtls\_config](#input\_viewer\_mtls\_config) | Configuration for mutual TLS (mTLS) authentication on the CloudFront distribution. The trust store (`aws_cloudfront_trust_store`) should be managed outside this module, as it is commonly shared across multiple distributions. | <pre>object({<br/> mode = string<br/> trust_store_id = string<br/> advertise_trust_store_ca_names = optional(bool, false)<br/> ignore_certificate_expiry = optional(bool, false)<br/> })</pre> | `null` | no |
| <a name="input_viewer_protocol_policy"></a> [viewer\_protocol\_policy](#input\_viewer\_protocol\_policy) | allow-all, redirect-to-https | `string` | `"redirect-to-https"` | no |
| <a name="input_vpc_origin_config"></a> [vpc\_origin\_config](#input\_vpc\_origin\_config) | Configuration for a CloudFront VPC origin, used when `origin_type` is set to `vpc`. The VPC origin (`aws_cloudfront_vpc_origin`) should be managed outside this module. | <pre>object({<br/> vpc_origin_id = string<br/> origin_keepalive_timeout = optional(number, 5)<br/> origin_read_timeout = optional(number, 30)<br/> owner_account_id = optional(string, null)<br/> })</pre> | `null` | no |
| <a name="input_web_acl_id"></a> [web\_acl\_id](#input\_web\_acl\_id) | ID of the AWS WAF web ACL that is associated with the distribution | `string` | `""` | no |

## Outputs
Expand Down Expand Up @@ -326,15 +327,15 @@ Setup dependencies:

To run tests:

- Run all tests:
- Run all tests:
```sh
atmos test run
```
- Clean up test artifacts:
- Clean up test artifacts:
```sh
atmos test clean
```
- Explore additional test options:
- Explore additional test options:
```sh
atmos test --help
```
Expand Down
4 changes: 4 additions & 0 deletions examples/complete/fixtures.us-east-2.tfvars
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,7 @@ origin_domain_name = "example.com"
parent_zone_name = "testing.cloudposse.co"

http_version = "http2and3"

availability_zones = ["us-east-2a", "us-east-2b"]

vpc_cidr_block = "172.16.0.0/16"
122 changes: 122 additions & 0 deletions examples/complete/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,125 @@ module "cdn" {

context = module.this.context
}

# -----------------------------------------------------------------------------
# Private origin: an internal ALB reachable only via a CloudFront VPC origin.
# Uses a dedicated VPC (not the default one).
# -----------------------------------------------------------------------------

module "vpc_origin_label" {
source = "cloudposse/label/null"
version = "0.25.0"

attributes = ["vpc"]

context = module.this.context
}

module "vpc" {
source = "cloudposse/vpc/aws"
version = "3.0.0"

ipv4_primary_cidr_block = var.vpc_cidr_block

context = module.vpc_origin_label.context
}

module "subnets" {
source = "cloudposse/dynamic-subnets/aws"
version = "3.1.1"

availability_zones = var.availability_zones
vpc_id = module.vpc.vpc_id
igw_id = [module.vpc.igw_id]
ipv4_cidr_block = [module.vpc.vpc_cidr_block]
nat_gateway_enabled = false
nat_instance_enabled = false

context = module.vpc_origin_label.context
}

# The ALB only accepts traffic from CloudFront. The module's own security group is
# disabled because it always opens HTTP to `http_ingress_cidr_blocks`.
resource "aws_security_group" "alb" {
name_prefix = "${module.vpc_origin_label.id}-"
description = "Allows HTTP from CloudFront VPC origins only"
vpc_id = module.vpc.vpc_id

tags = module.vpc_origin_label.tags
}

resource "aws_vpc_security_group_egress_rule" "alb_all_outbound" {
security_group_id = aws_security_group.alb.id
ip_protocol = "-1"
cidr_ipv4 = "0.0.0.0/0"

tags = module.vpc_origin_label.tags
}

# CloudFront creates (or reuses) this security group in the VPC the first time a
# VPC origin targets it. The origin's own security group must allow inbound
# traffic from it.
# https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-vpc-origins.html
data "aws_security_group" "cloudfront_vpc_origins" {
vpc_id = module.vpc.vpc_id
name = "CloudFront-VPCOrigins-Service-SG"

depends_on = [aws_cloudfront_vpc_origin.this]
}

resource "aws_vpc_security_group_ingress_rule" "alb_from_cloudfront_vpc_origins" {
security_group_id = aws_security_group.alb.id
from_port = 80
to_port = 80
ip_protocol = "tcp"
referenced_security_group_id = data.aws_security_group.cloudfront_vpc_origins.id

tags = module.vpc_origin_label.tags
}

module "alb" {
source = "cloudposse/alb/aws"
version = "2.5.0"

vpc_id = module.vpc.vpc_id
subnet_ids = module.subnets.private_subnet_ids
internal = true
http_enabled = true
access_logs_enabled = false
security_group_enabled = false
security_group_ids = [aws_security_group.alb.id]

context = module.vpc_origin_label.context
}

resource "aws_cloudfront_vpc_origin" "this" {
vpc_origin_endpoint_config {
name = module.vpc_origin_label.id
arn = module.alb.alb_arn
http_port = 80
https_port = 443
origin_protocol_policy = "http-only"

origin_ssl_protocols {
items = ["TLSv1.2"]
quantity = 1
}
}

tags = module.vpc_origin_label.tags
}

module "cdn_vpc_origin" {
source = "../../"

origin_domain_name = module.alb.alb_dns_name
origin_type = "vpc"
log_force_destroy = true

vpc_origin_config = {
vpc_origin_id = aws_cloudfront_vpc_origin.this.id
}

context = module.vpc_origin_label.context
}
15 changes: 15 additions & 0 deletions examples/complete/outputs.tf
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,18 @@ output "cf_origin_access_identity" {
value = module.cdn.cf_origin_access_identity
description = "A shortcut to the full path for the origin access identity to use in CloudFront"
}

output "vpc_origin_id" {
value = aws_cloudfront_vpc_origin.this.id
description = "ID of the CloudFront VPC origin"
}

output "vpc_origin_cf_id" {
value = module.cdn_vpc_origin.cf_id
description = "ID of the CloudFront distribution backed by the VPC origin"
}

output "vpc_origin_cf_domain_name" {
value = module.cdn_vpc_origin.cf_domain_name
description = "Domain name of the CloudFront distribution backed by the VPC origin"
}
10 changes: 10 additions & 0 deletions examples/complete/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -272,3 +272,13 @@ variable "http_version" {
default = "http2"
description = "The maximum HTTP version to support on the distribution. Allowed values are http1.1, http2, http2and3 and http3."
}

variable "availability_zones" {
type = list(string)
description = "List of availability zones for the VPC subnets"
}

variable "vpc_cidr_block" {
type = string
description = "CIDR block of the dedicated VPC that hosts the internal ALB used as the CloudFront VPC origin"
}
4 changes: 2 additions & 2 deletions examples/complete/versions.tf
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
terraform {
required_version = ">= 1.0"
required_version = ">= 1.3"

required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.0"
version = ">= 6.28.0"
}
local = {
source = "hashicorp/local"
Expand Down
4 changes: 2 additions & 2 deletions examples/mtls/versions.tf
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
terraform {
required_version = ">= 1.0"
required_version = ">= 1.2"

required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 6.13.0"
version = ">= 6.28.0"
}
tls = {
source = "hashicorp/tls"
Expand Down
4 changes: 2 additions & 2 deletions examples/multi-origin/versions.tf
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
terraform {
required_version = ">= 1.0"
required_version = ">= 1.2"

required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.0"
version = ">= 6.28.0"
}
}
}
4 changes: 2 additions & 2 deletions examples/s3/versions.tf
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
terraform {
required_version = ">= 1.0"
required_version = ">= 1.2"

required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.0"
version = ">= 6.28.0"
}
}
}
4 changes: 2 additions & 2 deletions examples/wordpress/versions.tf
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
terraform {
required_version = ">= 1.0"
required_version = ">= 1.2"

required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.0"
version = ">= 6.28.0"
}
}
}
27 changes: 27 additions & 0 deletions main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,16 @@ resource "aws_cloudfront_distribution" "default" {
}
}

dynamic "vpc_origin_config" {
for_each = var.origin_type == "vpc" ? [var.vpc_origin_config] : []
content {
vpc_origin_id = vpc_origin_config.value.vpc_origin_id
origin_keepalive_timeout = vpc_origin_config.value.origin_keepalive_timeout
origin_read_timeout = vpc_origin_config.value.origin_read_timeout
owner_account_id = vpc_origin_config.value.owner_account_id
}
}

dynamic "origin_shield" {
for_each = var.origin_shield != null ? ["true"] : []
content {
Expand Down Expand Up @@ -158,6 +168,16 @@ resource "aws_cloudfront_distribution" "default" {
}
}

dynamic "vpc_origin_config" {
for_each = origin.value.vpc_origin_config == null ? [] : [true]
content {
vpc_origin_id = origin.value.vpc_origin_config.vpc_origin_id
origin_keepalive_timeout = origin.value.vpc_origin_config.origin_keepalive_timeout
origin_read_timeout = origin.value.vpc_origin_config.origin_read_timeout
owner_account_id = origin.value.vpc_origin_config.owner_account_id
}
}

dynamic "origin_shield" {
for_each = origin.value.origin_shield != null ? [origin.value.origin_shield] : []
content {
Expand Down Expand Up @@ -325,6 +345,13 @@ resource "aws_cloudfront_distribution" "default" {
tags = module.this.tags

depends_on = [module.logs]

lifecycle {
precondition {
condition = var.origin_type != "vpc" || var.vpc_origin_config != null
error_message = "`vpc_origin_config` must be set when `origin_type` is \"vpc\"."
}
}
}

resource "aws_cloudfront_monitoring_subscription" "default" {
Expand Down
7 changes: 7 additions & 0 deletions test/src/examples_complete_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,11 @@ func TestExamplesComplete(t *testing.T) {
cfArn := terraform.Output(t, terraformOptions, "cf_arn")
// Verify we're getting back the outputs we expect
assert.Contains(t, cfArn, "arn:aws:cloudfront::")

// Verify the distribution backed by the CloudFront VPC origin
vpcOriginID := terraform.Output(t, terraformOptions, "vpc_origin_id")
assert.NotEmpty(t, vpcOriginID)

vpcOriginCfDomainName := terraform.Output(t, terraformOptions, "vpc_origin_cf_domain_name")
assert.Contains(t, vpcOriginCfDomainName, ".cloudfront.net")
}
Loading
Loading