diff --git a/modules/cloudfront/distribution.tf b/modules/cloudfront/distribution.tf index d95cfad..4296f26 100644 --- a/modules/cloudfront/distribution.tf +++ b/modules/cloudfront/distribution.tf @@ -61,9 +61,9 @@ resource "aws_cloudfront_distribution" "wordpress_distribution" { } } - lambda_function_association { - event_type = "origin-request" - lambda_arn = "${aws_lambda_function.object_redirect.arn}:${aws_lambda_function.object_redirect.version}" + function_association { + event_type = "viewer-request" + function_arn = aws_cloudfront_function.object_rewrite.arn } viewer_protocol_policy = "redirect-to-https" diff --git a/modules/cloudfront/function_rewrite/index.js.tftpl b/modules/cloudfront/function_rewrite/index.js.tftpl new file mode 100644 index 0000000..c466b1e --- /dev/null +++ b/modules/cloudfront/function_rewrite/index.js.tftpl @@ -0,0 +1,34 @@ +function handler(event) { + var request = event.request; + var uri = request.uri; + + try { + %{ for match, target in REDIRECTS } + if (/${match}/.test(uri)) { + return permanentRedirect(/${match}/, '${target}'); + } + %{ endfor ~} + + // Check whether the URI is missing a file name. + if (uri.endsWith('/')) { + request.uri += 'index.html'; + return request; + } + } + catch (e) { + // console.error is not supported + console.log(e); + } + + return request; +} + +function permanentRedirect(match, target) { + return { + statusCode: 301, + statusDescription: 'Moved Permanently', + headers: { + 'location': { value: uri.replace(match, target) } + } + }; +} diff --git a/modules/cloudfront/lambda_redirect/index_html/index.js b/modules/cloudfront/lambda_redirect/index_html/index.js deleted file mode 100644 index 44b6d6a..0000000 --- a/modules/cloudfront/lambda_redirect/index_html/index.js +++ /dev/null @@ -1,30 +0,0 @@ -// Add index.html to any request URLs that end in / -// This allows "friendly" URLs in static websites, e.g. -// /about-us/ is converted to /about-us/index.html - -// https://aws.amazon.com/blogs/compute/implementing-default-directory-indexes-in-amazon-s3-backed-amazon-cloudfront-origins-using-lambdaedge/ - -'use strict'; -exports.handler = (event, context, callback) => { - // Extract the request from the CloudFront event that is sent to Lambda@Edge - var request = event.Records[0].cf.request; - - // Extract the URI from the request - var olduri = request.uri; - - // Match any '/' that occurs at the end of a URI. Replace it with a default index - // Match also any calls to 'index.php' which Wordpress would ordinarily look for - - var newuri = olduri.replace(/\/$/, '\/index.html'); - newuri = newuri.replace(/\/index.php$/, '\/index.html'); - - // For debugging: Log the URI as received by CloudFront and the new URI to be used to fetch from origin - // console.log("Old URI: " + olduri); - // console.log("New URI: " + newuri); - - // Replace the received URI with the URI that includes the index page - request.uri = newuri; - - // Return to CloudFront - return callback(null, request); -}; diff --git a/modules/cloudfront/main.tf b/modules/cloudfront/main.tf index 7659151..5465752 100644 --- a/modules/cloudfront/main.tf +++ b/modules/cloudfront/main.tf @@ -1,76 +1,24 @@ -data "archive_file" "index_html" { - type = "zip" - source_dir = "${path.module}/lambda_redirect/index_html" - output_path = "${path.module}/lambda_redirect/dst/index_html.zip" -} - -#tfsec:ignore:AWS089 -resource "aws_cloudwatch_log_group" "object_redirect" { - name = "/aws/lambda/${var.site_name}_redirect_index_html" - retention_in_days = 7 -} - -#tfsec:ignore:AWS089 -resource "aws_cloudwatch_log_group" "object_redirect_ue1_local" { - name = "/aws/lambda/us-east-1.${var.site_name}_redirect_index_html" - retention_in_days = 7 -} - -# TODO: A solution to create/manage default log groups in all Edge Cache Regions #tfsec:ignore:AWS089 -resource "aws_cloudwatch_log_group" "object_redirect_ue1" { - name = "/aws/lambda/us-east-1.${var.site_name}_redirect_index_html" +resource "aws_cloudwatch_log_group" "object_rewrite" { + name = "/aws/cloudfront/function/${var.site_name}_rewrite" retention_in_days = 7 + # CloudFront Functions always creates log streams in us-east-1, no matter which edge location ran the function. + # The purpose of this resource is to set the retention days. provider = aws.ue1 } -resource "aws_lambda_function" "object_redirect" { - provider = aws.ue1 - filename = data.archive_file.index_html.output_path - function_name = "${var.site_name}_redirect_index_html" - role = aws_iam_role.lambda-edge.arn - handler = "index.handler" - source_code_hash = data.archive_file.index_html.output_base64sha256 - runtime = "nodejs12.x" - publish = true - memory_size = 128 - timeout = 3 +resource "aws_cloudfront_function" "object_rewrite" { depends_on = [ - aws_cloudwatch_log_group.object_redirect, - aws_cloudwatch_log_group.object_redirect_ue1, - aws_cloudwatch_log_group.object_redirect_ue1_local + aws_cloudwatch_log_group.object_rewrite ] -} - -data "aws_iam_policy_document" "lambda-edge-service-role" { - statement { - actions = ["sts:AssumeRole"] - principals { - type = "Service" - identifiers = ["edgelambda.amazonaws.com", "lambda.amazonaws.com"] + + name = "${var.site_name}_rewrite" + runtime = "cloudfront-js-1.0" + publish = true + code = templatefile( + "${path.module}/function_rewrite/index.js.tftpl", + { + REDIRECTS = var.cloudfront_function_301_redirects } - } -} - -resource "aws_iam_role" "lambda-edge" { - name = "${var.site_name}-lambda-edge-service-role" - assume_role_policy = data.aws_iam_policy_document.lambda-edge-service-role.json -} - -resource "aws_iam_role_policy_attachment" "basic" { - role = aws_iam_role.lambda-edge.name - policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole" -} - -data "aws_iam_policy_document" "lambda-edge-cloudwatch-logs" { - statement { - actions = ["logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents"] - resources = ["arn:aws:logs:*:*:*"] - } -} - -resource "aws_iam_role_policy" "lambda-edge-cloudwatch-logs" { - name = "${var.site_name}-lambda-edge-cloudwatch-logs" - role = aws_iam_role.lambda-edge.name - policy = data.aws_iam_policy_document.lambda-edge-cloudwatch-logs.json + ) } diff --git a/modules/cloudfront/variables.tf b/modules/cloudfront/variables.tf index ad773fa..0366e22 100644 --- a/modules/cloudfront/variables.tf +++ b/modules/cloudfront/variables.tf @@ -36,3 +36,11 @@ variable "waf_acl_arn" { default = null description = "The ARN of the WAF ACL applied to the CloudFront distribution." } + +variable "cloudfront_function_301_redirects" { + type = map + default = { + "^(.*)index\\.php$": "$1" + } + description = "A list of key value pairs of Regex match and destination for 301 redirects at CloudFront." +} diff --git a/variables.tf b/variables.tf index 0c6acc6..e42e1fd 100644 --- a/variables.tf +++ b/variables.tf @@ -104,6 +104,14 @@ variable "cloudfront_class" { default = "PriceClass_All" } +variable "cloudfront_function_301_redirects" { + type = map + default = { + "^(.*)index\\.php$": "$1" + } + description = "A list of key value pairs of Regex match and destination for 301 redirects at CloudFront." +} + variable "hosted_zone_id" { type = string description = "The Route53 HostedZone ID to use to create records in."