Skip to content

Commit a62f80a

Browse files
authored
Merge pull request #18 from chris-qa-org/conditionally-redirect-to-www
Conditionally redirect to www
2 parents a6d589a + c2a0fc4 commit a62f80a

File tree

7 files changed

+217
-6
lines changed

7 files changed

+217
-6
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,27 +85,38 @@ module "static_site_hosting" {
8585
| [aws_acm_certificate.cloudfront_static_site](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate) | resource |
8686
| [aws_acm_certificate_validation.cloudfront_static_site](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/acm_certificate_validation) | resource |
8787
| [aws_cloudfront_cache_policy.static_site](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_cache_policy) | resource |
88+
| [aws_cloudfront_distribution.site_www_redirect](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_distribution) | resource |
8889
| [aws_cloudfront_distribution.static_site](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_distribution) | resource |
8990
| [aws_cloudfront_origin_access_control.static_site](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_origin_access_control) | resource |
9091
| [aws_cloudfront_origin_request_policy.static_site](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudfront_origin_request_policy) | resource |
9192
| [aws_route53_record.cloudfront_static_site_tls_certificate_dns_validation](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
9293
| [aws_route53_record.static_site](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
94+
| [aws_route53_record.static_site_www_redirect](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/route53_record) | resource |
9395
| [aws_s3_bucket.logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource |
96+
| [aws_s3_bucket.site_redirect_to_www](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource |
9497
| [aws_s3_bucket.static_site](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket) | resource |
9598
| [aws_s3_bucket_acl.cloudfront_logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_acl) | resource |
9699
| [aws_s3_bucket_acl.logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_acl) | resource |
100+
| [aws_s3_bucket_acl.site_redirect_to_www](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_acl) | resource |
97101
| [aws_s3_bucket_acl.static_site](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_acl) | resource |
102+
| [aws_s3_bucket_logging.site_redirect_to_www](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_logging) | resource |
98103
| [aws_s3_bucket_logging.static_site](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_logging) | resource |
99104
| [aws_s3_bucket_ownership_controls.logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_ownership_controls) | resource |
105+
| [aws_s3_bucket_ownership_controls.site_redirect_to_www](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_ownership_controls) | resource |
100106
| [aws_s3_bucket_ownership_controls.static_site](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_ownership_controls) | resource |
101107
| [aws_s3_bucket_policy.logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | resource |
108+
| [aws_s3_bucket_policy.site_redirect_to_www](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | resource |
102109
| [aws_s3_bucket_policy.static_site](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_policy) | resource |
103110
| [aws_s3_bucket_public_access_block.logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource |
111+
| [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 |
104112
| [aws_s3_bucket_public_access_block.static_site](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block) | resource |
105113
| [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 |
114+
| [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 |
106115
| [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 |
107116
| [aws_s3_bucket_versioning.logs](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource |
117+
| [aws_s3_bucket_versioning.site_redirect_to_www](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource |
108118
| [aws_s3_bucket_versioning.static_site](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning) | resource |
119+
| [aws_s3_bucket_website_configuration.site_redirect_to_www](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_website_configuration) | resource |
109120
| [aws_s3_bucket_website_configuration.static_site](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_website_configuration) | resource |
110121
| [aws_s3_object.static_site_index](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_object) | resource |
111122
| [aws_caller_identity.current](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity) | data source |
@@ -133,6 +144,7 @@ module "static_site_hosting" {
133144
| <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 |
134145
| <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 |
135146
| <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 |
147+
| <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 |
136148
| <a name="input_static_site_s3_acl"></a> [static\_site\_s3\_acl](#input\_static\_site\_s3\_acl) | Static Site S3 ACL | `string` | `"private"` | no |
137149
| <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 |
138150

certificates.tf

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ resource "aws_acm_certificate" "cloudfront_static_site" {
44
count = local.cloudfront_static_site_tls_certificate_arn == "" ? 1 : 0
55

66
domain_name = local.site_host_name
7+
subject_alternative_names = local.site_redirect_to_www ? [
8+
"www.${local.site_host_name}"
9+
] : []
710

811
validation_method = "DNS"
912

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
resource "aws_cloudfront_distribution" "site_www_redirect" {
2+
count = local.enable_cloudfront && local.site_redirect_to_www ? 1 : 0
3+
4+
comment = "${local.project_name} Static Site redirect to www"
5+
enabled = true
6+
7+
web_acl_id = local.cloudfront_static_site_web_acl_id
8+
9+
aliases = [local.site_host_name]
10+
11+
viewer_certificate {
12+
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
13+
minimum_protocol_version = "TLSv1.2_2021"
14+
ssl_support_method = "sni-only"
15+
}
16+
17+
origin {
18+
domain_name = aws_s3_bucket_website_configuration.site_redirect_to_www[0].website_endpoint
19+
origin_id = local.cloudfront_site_www_redirect_s3_origin_id
20+
21+
custom_origin_config {
22+
origin_protocol_policy = "http-only"
23+
http_port = "80"
24+
https_port = "443"
25+
origin_ssl_protocols = ["TLSv1.2"]
26+
}
27+
}
28+
29+
default_cache_behavior {
30+
allowed_methods = local.cloudfront_static_site_default_cache_behaviour["allowed_methods"]
31+
cached_methods = local.cloudfront_static_site_default_cache_behaviour["cached_methods"]
32+
target_origin_id = local.cloudfront_site_www_redirect_s3_origin_id
33+
cache_policy_id = local.cloudfront_static_site_s3_cache_policy_id
34+
compress = local.cloudfront_static_site_default_cache_behaviour["compress"]
35+
default_ttl = local.cloudfront_static_site_default_cache_behaviour["default_ttl"]
36+
37+
origin_request_policy_id = local.cloudfront_static_site_s3_origin_request_policy_id
38+
realtime_log_config_arn = local.cloudfront_static_site_default_cache_behaviour["realtime_log_config_arn"]
39+
response_headers_policy_id = local.cloudfront_static_site_default_cache_behaviour["response_headers_policy_id"]
40+
smooth_streaming = local.cloudfront_static_site_default_cache_behaviour["smooth_streaming"]
41+
trusted_signers = local.cloudfront_static_site_default_cache_behaviour["trusted_signers"]
42+
viewer_protocol_policy = local.cloudfront_static_site_default_cache_behaviour["viewer_protocol_policy"]
43+
}
44+
45+
is_ipv6_enabled = local.cloudfront_static_site_is_ipv6_enabled
46+
http_version = local.cloudfront_static_site_http_version
47+
48+
restrictions {
49+
geo_restriction {
50+
restriction_type = local.cloudfront_static_site_restrictions["geo_restriction"]["restriction_type"]
51+
locations = local.cloudfront_static_site_restrictions["geo_restriction"]["locations"]
52+
}
53+
}
54+
55+
price_class = local.cloudfront_static_site_price_class
56+
57+
dynamic "logging_config" {
58+
for_each = local.enable_cloudfront_static_site_logs ? [1] : []
59+
60+
content {
61+
include_cookies = false
62+
bucket = aws_s3_bucket.logs[0].bucket_domain_name
63+
prefix = "cloudfront/static_site_www_redirect"
64+
}
65+
}
66+
67+
depends_on = [
68+
aws_acm_certificate_validation.cloudfront_static_site
69+
]
70+
}

locals.tf

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
locals {
2-
project_name = var.project_name
3-
account_id = data.aws_caller_identity.current.account_id
4-
site_host_name = var.site_host_name
5-
route53_zone_id = var.route53_zone_id
2+
project_name = var.project_name
3+
account_id = data.aws_caller_identity.current.account_id
4+
site_host_name = var.site_host_name
5+
site_redirect_to_www = var.site_redirect_to_www
6+
route53_zone_id = var.route53_zone_id
67

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

41+
site_redirect_to_www_bucket_enforce_tls_statement = local.site_redirect_to_www ? templatefile(
42+
local.s3_bucket_policy_statement_enforce_tls_path,
43+
{
44+
bucket_arn = aws_s3_bucket.site_redirect_to_www[0].arn
45+
}
46+
) : ""
47+
site_redirect_to_www_bucket_cloudfront_read_statement = local.site_redirect_to_www ? templatefile(
48+
local.s3_bucket_policy_statement_cloudfront_read,
49+
{
50+
bucket_arn = aws_s3_bucket.site_redirect_to_www[0].arn,
51+
cloudfront_arn = aws_cloudfront_distribution.site_www_redirect[0].arn
52+
}
53+
) : ""
54+
site_redirect_to_www_bucket_policy = templatefile(
55+
local.s3_bucket_policy_path,
56+
{
57+
statement = <<EOT
58+
[
59+
${local.site_redirect_to_www_bucket_enforce_tls_statement},
60+
${local.site_redirect_to_www_bucket_cloudfront_read_statement}
61+
]
62+
EOT
63+
}
64+
)
65+
4066
enable_cloudfront = var.enable_cloudfront
4167
cloudfront_static_site_s3_origin_id = "${local.project_name}-static-site-s3"
68+
cloudfront_site_www_redirect_s3_origin_id = "${local.project_name}-static-site-s3-redirect-to-www"
4269
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"]
4370
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"]
44-
cloudfront_static_site_aliases = [local.site_host_name]
71+
cloudfront_static_site_aliases = local.site_redirect_to_www ? ["www.${local.site_host_name}"] : [local.site_host_name]
4572
cloudfront_static_site_web_acl_id = var.cloudfront_static_site_web_acl_id
4673
cloudfront_static_site_tls_certificate_arn = var.cloudfront_static_site_tls_certificate_arn
4774
cloudfront_static_site_custom_error_responses = var.cloudfront_static_site_custom_error_responses

route53.tf

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ resource "aws_route53_record" "static_site" {
1818
count = local.route53_zone_id != "" ? 1 : 0
1919

2020
zone_id = data.aws_route53_zone.static_site[0].zone_id
21-
name = local.site_host_name
21+
name = local.site_redirect_to_www ? "www.${local.site_host_name}" : local.site_host_name
2222
type = "A"
2323

2424
alias {
@@ -27,3 +27,17 @@ resource "aws_route53_record" "static_site" {
2727
evaluate_target_health = true
2828
}
2929
}
30+
31+
resource "aws_route53_record" "static_site_www_redirect" {
32+
count = local.route53_zone_id != "" && local.site_redirect_to_www ? 1 : 0
33+
34+
zone_id = data.aws_route53_zone.static_site[0].zone_id
35+
name = local.site_host_name
36+
type = "A"
37+
38+
alias {
39+
name = aws_cloudfront_distribution.site_www_redirect[0].domain_name
40+
zone_id = aws_cloudfront_distribution.site_www_redirect[0].hosted_zone_id
41+
evaluate_target_health = true
42+
}
43+
}

s3-static-site-www-redirect.tf

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
resource "aws_s3_bucket" "site_redirect_to_www" {
2+
count = local.site_redirect_to_www ? 1 : 0
3+
4+
bucket = "${local.project_name}-static-site-www-redirect"
5+
force_destroy = local.s3_static_site_force_destroy
6+
}
7+
8+
resource "aws_s3_bucket_versioning" "site_redirect_to_www" {
9+
count = local.site_redirect_to_www ? 1 : 0
10+
11+
bucket = aws_s3_bucket.site_redirect_to_www[0].id
12+
versioning_configuration {
13+
status = "Enabled"
14+
}
15+
}
16+
17+
resource "aws_s3_bucket_logging" "site_redirect_to_www" {
18+
count = local.enable_s3_access_logs && local.site_redirect_to_www ? 1 : 0
19+
20+
bucket = aws_s3_bucket.site_redirect_to_www[0].id
21+
target_bucket = aws_s3_bucket.logs[0].id
22+
target_prefix = "s3/static_site_www_redirect/"
23+
}
24+
25+
resource "aws_s3_bucket_ownership_controls" "site_redirect_to_www" {
26+
bucket = aws_s3_bucket.site_redirect_to_www[0].id
27+
28+
rule {
29+
object_ownership = "BucketOwnerPreferred"
30+
}
31+
}
32+
33+
resource "aws_s3_bucket_acl" "site_redirect_to_www" {
34+
count = local.site_redirect_to_www ? 1 : 0
35+
36+
bucket = aws_s3_bucket.site_redirect_to_www[0].id
37+
acl = local.static_site_s3_acl
38+
}
39+
40+
resource "aws_s3_bucket_public_access_block" "site_redirect_to_www" {
41+
count = local.site_redirect_to_www ? 1 : 0
42+
43+
bucket = aws_s3_bucket.site_redirect_to_www[0].id
44+
block_public_acls = local.static_site_s3_acl == "public" ? false : true
45+
block_public_policy = local.static_site_s3_acl == "public" ? false : true
46+
ignore_public_acls = local.static_site_s3_acl == "public" ? false : true
47+
restrict_public_buckets = local.static_site_s3_acl == "public" ? false : true
48+
}
49+
50+
resource "aws_s3_bucket_website_configuration" "site_redirect_to_www" {
51+
count = local.site_redirect_to_www ? 1 : 0
52+
53+
bucket = aws_s3_bucket.site_redirect_to_www[0].id
54+
55+
redirect_all_requests_to {
56+
host_name = "www.${local.site_host_name}"
57+
protocol = "https"
58+
}
59+
}
60+
61+
#tfsec:ignore:aws-s3-encryption-customer-key
62+
resource "aws_s3_bucket_server_side_encryption_configuration" "site_redirect_to_www" {
63+
count = local.static_site_s3_enable_encryption && local.site_redirect_to_www ? 1 : 0
64+
65+
bucket = aws_s3_bucket.site_redirect_to_www[0].id
66+
67+
rule {
68+
apply_server_side_encryption_by_default {
69+
sse_algorithm = "AES256"
70+
}
71+
}
72+
}
73+
74+
resource "aws_s3_bucket_policy" "site_redirect_to_www" {
75+
count = local.site_redirect_to_www ? 1 : 0
76+
77+
bucket = aws_s3_bucket.site_redirect_to_www[0].id
78+
policy = local.site_redirect_to_www_bucket_policy
79+
}

variables.tf

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,12 @@ variable "site_host_name" {
99
default = ""
1010
}
1111

12+
variable "site_redirect_to_www" {
13+
description = "Conditionally redirect to www.<site_host_name>"
14+
type = bool
15+
default = false
16+
}
17+
1218
variable "route53_zone_id" {
1319
description = "Route53 zone id. If provided, the certificate validation records and site records will be created in that zone"
1420
type = string

0 commit comments

Comments
 (0)