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
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,27 +85,38 @@ module "static_site_hosting" {
| [aws_acm_certificate.cloudfront_static_site](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate) | resource |
| [aws_acm_certificate_validation.cloudfront_static_site](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate_validation) | resource |
| [aws_cloudfront_cache_policy.static_site](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_cache_policy) | resource |
| [aws_cloudfront_distribution.site_www_redirect](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_distribution) | resource |
| [aws_cloudfront_distribution.static_site](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_distribution) | resource |
| [aws_cloudfront_origin_access_control.static_site](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_origin_access_control) | resource |
| [aws_cloudfront_origin_request_policy.static_site](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_origin_request_policy) | resource |
| [aws_route53_record.cloudfront_static_site_tls_certificate_dns_validation](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
| [aws_route53_record.static_site](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
| [aws_route53_record.static_site_www_redirect](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
| [aws_s3_bucket.logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource |
| [aws_s3_bucket.site_redirect_to_www](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource |
| [aws_s3_bucket.static_site](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource |
| [aws_s3_bucket_acl.cloudfront_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_acl) | resource |
| [aws_s3_bucket_acl.logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_acl) | resource |
| [aws_s3_bucket_acl.site_redirect_to_www](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_acl) | resource |
| [aws_s3_bucket_acl.static_site](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_acl) | resource |
| [aws_s3_bucket_logging.site_redirect_to_www](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_logging) | resource |
| [aws_s3_bucket_logging.static_site](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_logging) | resource |
| [aws_s3_bucket_ownership_controls.logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_ownership_controls) | resource |
| [aws_s3_bucket_ownership_controls.site_redirect_to_www](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_ownership_controls) | resource |
| [aws_s3_bucket_ownership_controls.static_site](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_ownership_controls) | resource |
| [aws_s3_bucket_policy.logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | resource |
| [aws_s3_bucket_policy.site_redirect_to_www](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | resource |
| [aws_s3_bucket_policy.static_site](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | resource |
| [aws_s3_bucket_public_access_block.logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource |
| [aws_s3_bucket_public_access_block.site_redirect_to_www](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource |
| [aws_s3_bucket_public_access_block.static_site](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource |
| [aws_s3_bucket_server_side_encryption_configuration.logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource |
| [aws_s3_bucket_server_side_encryption_configuration.site_redirect_to_www](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource |
| [aws_s3_bucket_server_side_encryption_configuration.static_site](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration) | resource |
| [aws_s3_bucket_versioning.logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource |
| [aws_s3_bucket_versioning.site_redirect_to_www](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource |
| [aws_s3_bucket_versioning.static_site](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource |
| [aws_s3_bucket_website_configuration.site_redirect_to_www](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_website_configuration) | resource |
| [aws_s3_bucket_website_configuration.static_site](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_website_configuration) | resource |
| [aws_s3_object.static_site_index](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_object) | resource |
| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
Expand Down Expand Up @@ -133,6 +144,7 @@ module "static_site_hosting" {
| <a name="input_s3_logs_force_destroy"></a> [s3\_logs\_force\_destroy](#input\_s3\_logs\_force\_destroy) | Force destroy Logs S3 bucket | `bool` | `false` | no |
| <a name="input_s3_static_site_force_destroy"></a> [s3\_static\_site\_force\_destroy](#input\_s3\_static\_site\_force\_destroy) | Force destroy Static Site S3 bucket | `bool` | `false` | no |
| <a name="input_site_host_name"></a> [site\_host\_name](#input\_site\_host\_name) | Site Host Name. This will be used for Certificate generation and CloudFront aliases | `string` | `""` | no |
| <a name="input_site_redirect_to_www"></a> [site\_redirect\_to\_www](#input\_site\_redirect\_to\_www) | Conditionally redirect to www.<site\_host\_name> | `bool` | `false` | no |
| <a name="input_static_site_s3_acl"></a> [static\_site\_s3\_acl](#input\_static\_site\_s3\_acl) | Static Site S3 ACL | `string` | `"private"` | no |
| <a name="input_static_site_s3_enable_encryption"></a> [static\_site\_s3\_enable\_encryption](#input\_static\_site\_s3\_enable\_encryption) | Static Site S3 Enable Encyption | `bool` | `true` | no |

Expand Down
3 changes: 3 additions & 0 deletions certificates.tf
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ resource "aws_acm_certificate" "cloudfront_static_site" {
count = local.cloudfront_static_site_tls_certificate_arn == "" ? 1 : 0

domain_name = local.site_host_name
subject_alternative_names = local.site_redirect_to_www ? [
"www.${local.site_host_name}"
] : []

validation_method = "DNS"

Expand Down
70 changes: 70 additions & 0 deletions cloudfront-static-site-www-redirect.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
resource "aws_cloudfront_distribution" "site_www_redirect" {
count = local.enable_cloudfront && local.site_redirect_to_www ? 1 : 0

comment = "${local.project_name} Static Site redirect to www"
enabled = true

web_acl_id = local.cloudfront_static_site_web_acl_id

aliases = [local.site_host_name]

viewer_certificate {
acm_certificate_arn = local.cloudfront_static_site_tls_certificate_arn == "" ? aws_acm_certificate.cloudfront_static_site[0].arn : local.cloudfront_static_site_tls_certificate_arn
minimum_protocol_version = "TLSv1.2_2021"
ssl_support_method = "sni-only"
}

origin {
domain_name = aws_s3_bucket_website_configuration.site_redirect_to_www[0].website_endpoint
origin_id = local.cloudfront_site_www_redirect_s3_origin_id

custom_origin_config {
origin_protocol_policy = "http-only"
http_port = "80"
https_port = "443"
origin_ssl_protocols = ["TLSv1.2"]
}
}

default_cache_behavior {
allowed_methods = local.cloudfront_static_site_default_cache_behaviour["allowed_methods"]
cached_methods = local.cloudfront_static_site_default_cache_behaviour["cached_methods"]
target_origin_id = local.cloudfront_site_www_redirect_s3_origin_id
cache_policy_id = local.cloudfront_static_site_s3_cache_policy_id
compress = local.cloudfront_static_site_default_cache_behaviour["compress"]
default_ttl = local.cloudfront_static_site_default_cache_behaviour["default_ttl"]

origin_request_policy_id = local.cloudfront_static_site_s3_origin_request_policy_id
realtime_log_config_arn = local.cloudfront_static_site_default_cache_behaviour["realtime_log_config_arn"]
response_headers_policy_id = local.cloudfront_static_site_default_cache_behaviour["response_headers_policy_id"]
smooth_streaming = local.cloudfront_static_site_default_cache_behaviour["smooth_streaming"]
trusted_signers = local.cloudfront_static_site_default_cache_behaviour["trusted_signers"]
viewer_protocol_policy = local.cloudfront_static_site_default_cache_behaviour["viewer_protocol_policy"]
}

is_ipv6_enabled = local.cloudfront_static_site_is_ipv6_enabled
http_version = local.cloudfront_static_site_http_version

restrictions {
geo_restriction {
restriction_type = local.cloudfront_static_site_restrictions["geo_restriction"]["restriction_type"]
locations = local.cloudfront_static_site_restrictions["geo_restriction"]["locations"]
}
}

price_class = local.cloudfront_static_site_price_class

dynamic "logging_config" {
for_each = local.enable_cloudfront_static_site_logs ? [1] : []

content {
include_cookies = false
bucket = aws_s3_bucket.logs[0].bucket_domain_name
prefix = "cloudfront/static_site_www_redirect"
}
}

depends_on = [
aws_acm_certificate_validation.cloudfront_static_site
]
}
37 changes: 32 additions & 5 deletions locals.tf
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
locals {
project_name = var.project_name
account_id = data.aws_caller_identity.current.account_id
site_host_name = var.site_host_name
route53_zone_id = var.route53_zone_id
project_name = var.project_name
account_id = data.aws_caller_identity.current.account_id
site_host_name = var.site_host_name
site_redirect_to_www = var.site_redirect_to_www
route53_zone_id = var.route53_zone_id

s3_bucket_policy_statement_enforce_tls_path = "${path.module}/policies/s3-bucket-policy-statements/enforce-tls.json.tpl"
s3_bucket_policy_statement_log_delivery_access = "${path.module}/policies/s3-bucket-policy-statements/log-delivery-access.json.tpl"
Expand Down Expand Up @@ -37,11 +38,37 @@ locals {
)
s3_static_site_force_destroy = var.s3_static_site_force_destroy

site_redirect_to_www_bucket_enforce_tls_statement = local.site_redirect_to_www ? templatefile(
local.s3_bucket_policy_statement_enforce_tls_path,
{
bucket_arn = aws_s3_bucket.site_redirect_to_www[0].arn
}
) : ""
site_redirect_to_www_bucket_cloudfront_read_statement = local.site_redirect_to_www ? templatefile(
local.s3_bucket_policy_statement_cloudfront_read,
{
bucket_arn = aws_s3_bucket.site_redirect_to_www[0].arn,
cloudfront_arn = aws_cloudfront_distribution.site_www_redirect[0].arn
}
) : ""
site_redirect_to_www_bucket_policy = templatefile(
local.s3_bucket_policy_path,
{
statement = <<EOT
[
${local.site_redirect_to_www_bucket_enforce_tls_statement},
${local.site_redirect_to_www_bucket_cloudfront_read_statement}
]
EOT
}
)

enable_cloudfront = var.enable_cloudfront
cloudfront_static_site_s3_origin_id = "${local.project_name}-static-site-s3"
cloudfront_site_www_redirect_s3_origin_id = "${local.project_name}-static-site-s3-redirect-to-www"
cloudfront_static_site_s3_cache_policy_id = local.enable_cloudfront && local.cloudfront_static_site_default_cache_behaviour["cache_policy_id"] == null ? aws_cloudfront_cache_policy.static_site[0].id : local.cloudfront_static_site_default_cache_behaviour["cache_policy_id"]
cloudfront_static_site_s3_origin_request_policy_id = local.enable_cloudfront && local.cloudfront_static_site_default_cache_behaviour["origin_request_policy_id"] == null ? aws_cloudfront_origin_request_policy.static_site[0].id : local.cloudfront_static_site_default_cache_behaviour["origin_request_policy_id"]
cloudfront_static_site_aliases = [local.site_host_name]
cloudfront_static_site_aliases = local.site_redirect_to_www ? ["www.${local.site_host_name}"] : [local.site_host_name]
cloudfront_static_site_web_acl_id = var.cloudfront_static_site_web_acl_id
cloudfront_static_site_tls_certificate_arn = var.cloudfront_static_site_tls_certificate_arn
cloudfront_static_site_custom_error_responses = var.cloudfront_static_site_custom_error_responses
Expand Down
16 changes: 15 additions & 1 deletion route53.tf
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ resource "aws_route53_record" "static_site" {
count = local.route53_zone_id != "" ? 1 : 0

zone_id = data.aws_route53_zone.static_site[0].zone_id
name = local.site_host_name
name = local.site_redirect_to_www ? "www.${local.site_host_name}" : local.site_host_name
type = "A"

alias {
Expand All @@ -27,3 +27,17 @@ resource "aws_route53_record" "static_site" {
evaluate_target_health = true
}
}

resource "aws_route53_record" "static_site_www_redirect" {
count = local.route53_zone_id != "" && local.site_redirect_to_www ? 1 : 0

zone_id = data.aws_route53_zone.static_site[0].zone_id
name = local.site_host_name
type = "A"

alias {
name = aws_cloudfront_distribution.site_www_redirect[0].domain_name
zone_id = aws_cloudfront_distribution.site_www_redirect[0].hosted_zone_id
evaluate_target_health = true
}
}
79 changes: 79 additions & 0 deletions s3-static-site-www-redirect.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
resource "aws_s3_bucket" "site_redirect_to_www" {
count = local.site_redirect_to_www ? 1 : 0

bucket = "${local.project_name}-static-site-www-redirect"
force_destroy = local.s3_static_site_force_destroy
}

resource "aws_s3_bucket_versioning" "site_redirect_to_www" {
count = local.site_redirect_to_www ? 1 : 0

bucket = aws_s3_bucket.site_redirect_to_www[0].id
versioning_configuration {
status = "Enabled"
}
}

resource "aws_s3_bucket_logging" "site_redirect_to_www" {
count = local.enable_s3_access_logs && local.site_redirect_to_www ? 1 : 0

bucket = aws_s3_bucket.site_redirect_to_www[0].id
target_bucket = aws_s3_bucket.logs[0].id
target_prefix = "s3/static_site_www_redirect/"
}

resource "aws_s3_bucket_ownership_controls" "site_redirect_to_www" {
bucket = aws_s3_bucket.site_redirect_to_www[0].id

rule {
object_ownership = "BucketOwnerPreferred"
}
}

resource "aws_s3_bucket_acl" "site_redirect_to_www" {
count = local.site_redirect_to_www ? 1 : 0

bucket = aws_s3_bucket.site_redirect_to_www[0].id
acl = local.static_site_s3_acl
}

resource "aws_s3_bucket_public_access_block" "site_redirect_to_www" {
count = local.site_redirect_to_www ? 1 : 0

bucket = aws_s3_bucket.site_redirect_to_www[0].id
block_public_acls = local.static_site_s3_acl == "public" ? false : true
block_public_policy = local.static_site_s3_acl == "public" ? false : true
ignore_public_acls = local.static_site_s3_acl == "public" ? false : true
restrict_public_buckets = local.static_site_s3_acl == "public" ? false : true
}

resource "aws_s3_bucket_website_configuration" "site_redirect_to_www" {
count = local.site_redirect_to_www ? 1 : 0

bucket = aws_s3_bucket.site_redirect_to_www[0].id

redirect_all_requests_to {
host_name = "www.${local.site_host_name}"
protocol = "https"
}
}

#tfsec:ignore:aws-s3-encryption-customer-key
resource "aws_s3_bucket_server_side_encryption_configuration" "site_redirect_to_www" {
count = local.static_site_s3_enable_encryption && local.site_redirect_to_www ? 1 : 0

bucket = aws_s3_bucket.site_redirect_to_www[0].id

rule {
apply_server_side_encryption_by_default {
sse_algorithm = "AES256"
}
}
}

resource "aws_s3_bucket_policy" "site_redirect_to_www" {
count = local.site_redirect_to_www ? 1 : 0

bucket = aws_s3_bucket.site_redirect_to_www[0].id
policy = local.site_redirect_to_www_bucket_policy
}
6 changes: 6 additions & 0 deletions variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ variable "site_host_name" {
default = ""
}

variable "site_redirect_to_www" {
description = "Conditionally redirect to www.<site_host_name>"
type = bool
default = false
}

variable "route53_zone_id" {
description = "Route53 zone id. If provided, the certificate validation records and site records will be created in that zone"
type = string
Expand Down