From 60c62ac4961aed8f314700cbbcc6a0bea808b089 Mon Sep 17 00:00:00 2001 From: Felix Haus Date: Wed, 17 Mar 2021 20:02:16 +0100 Subject: [PATCH] Adds request origin and cache policies --- docs/upgrading-v0.8.0.md | 55 +++++++++++++++++++++++++++++ main.tf | 67 +++++++++++++++++++++++++++--------- modules/proxy-config/main.tf | 27 ++++++++------- modules/proxy/main.tf | 55 ++++++++++++----------------- modules/proxy/variables.tf | 44 +++++++++++++++-------- variables.tf | 12 +++---- versions.tf | 2 +- 7 files changed, 179 insertions(+), 83 deletions(-) create mode 100644 docs/upgrading-v0.8.0.md diff --git a/docs/upgrading-v0.8.0.md b/docs/upgrading-v0.8.0.md new file mode 100644 index 00000000..1f0daece --- /dev/null +++ b/docs/upgrading-v0.8.0.md @@ -0,0 +1,55 @@ +# Upgrading to 0.8.0 + +This is a guide for upgrading from a previous version of this module to `v0.8.0`. + +With version `v0.8.0` the CloudFront distributions were updated to use [cache and origin request policies](https://aws.amazon.com/blogs/networking-and-content-delivery/amazon-cloudfront-announces-cache-and-origin-request-policies/) ([#82](https://github.com/dealmore/terraform-aws-next-js/pull/82)) instead of the (now) legacy cache settings. + +Unfortunately the Terraform AWS provider currently does not support ([terraform-provider-aws#17626](https://github.com/hashicorp/terraform-provider-aws/issues/17626)) upgrading existing CloudFront distributions from the legacy settings to policies, so the update requires a manual step in the console. + +If you are not sure whether you need to apply this steps, you can run `terraform apply`. +When the following error message appears you need to do the manual update: + +``` +Error: error updating CloudFront Distribution (E27ZJ807F87DQA): InvalidArgument: The parameter ForwardedValues cannot be used when a cache policy is associated to the cache behavior. + status code: 400, request id: 012b60c1-adc8-4175-a5ff-111d819e2b18 +``` + +## Manual upgrade from the AWS Console + +Terraform Next.js module for AWS uses two CloudFront distributions, that booth need to be upgraded. + +### Upgrading Proxy-Config CloudFront distribution + +1. Login into the [AWS Console](https://console.aws.amazon.com/). +2. Go to the [CloudFront service page](https://console.aws.amazon.com/cloudfront/home). +3. Click in the sidebar left on "Distributions". +4. Select the distribution with the suffix **"- Proxy-Config"** (From the "Comment" column) in the list. +5. With the distribution click on "Distribution Settings" button above. +6. In the next screen go to the "Behaviors" tab. +7. It should show you a single behavior in the list. Select it and click on the "Edit" button above. +8. For the setting **"Cache and origin request settings"** switch to **"Use a cache policy and origin request policy"** +9. For **"Cache Policy"** now select **"Managed-CachingOptimizedForUncompressedObjects"** from the dropdown +10. For **"Origin Request Policy"** now select **"Managed-CORS-S3Origin"** from the dropdown +11. Click the button **"Yes, edit"** in the bottom right corner + +### Upgrading the main CloudFront distribution + +1. Login into the [AWS Console](https://console.aws.amazon.com/). +2. Go to the [CloudFront service page](https://console.aws.amazon.com/cloudfront/home). +3. Click in the sidebar left on "Distributions". +4. Select the distribution with the suffix **"- Main"** (From the "Comment" column) in the list. +5. With the distribution click on "Distribution Settings" button above. +6. In the next screen go to the "Behaviors" tab. +7. Based on your configuration you should see 2 or more entries, the following steps should be completed for each entry: + +8. Select a behavior from the list. Click on the "Edit" button above. +9. For the setting **"Cache and origin request settings"** switch to **"Use a cache policy and origin request policy"**. +10. For **"Cache Policy"** now select **"Managed-CachingOptimized"** from the dropdown. +11. For **"Origin Request Policy"** now select **"Managed-CORS-S3Origin"** from the dropdown. +12. Click the button **"Yes, edit"** in the bottom right corner. + +13. Repeat the steps 8-12 for all behaviors from this distribution. + +After the manual upgrade through the console, run `terraform apply` again. +This time it should apply the changes without errors. +If you still experience issues, please [create an issue](https://github.com/dealmore/terraform-aws-next-js/issues). diff --git a/main.tf b/main.tf index 774de09f..7b62c03d 100644 --- a/main.tf +++ b/main.tf @@ -141,10 +141,9 @@ module "next_image" { count = var.create_image_optimization ? 1 : 0 source = "dealmore/next-js-image-optimization/aws" - version = "2.0.1" + version = "~> 10.0.5" cloudfront_create_distribution = false - next_image_version = var.image_optimization_version # tf-next does not distinct between image and device sizes, because they # are eventually merged together on the image optimizer. @@ -181,25 +180,13 @@ locals { path_pattern = "/_next/image*" allowed_methods = ["GET", "HEAD"] cached_methods = ["GET", "HEAD"] - target_origin_id = module.next_image[0].cloudfront_origin_image_optimizer.origin_id + target_origin_id = module.next_image[0].cloudfront_origin_id compress = true viewer_protocol_policy = "redirect-to-https" - min_ttl = 0 - default_ttl = 86400 - max_ttl = 31536000 - - forwarded_values = { - cookies = { - forward = "none" - } - - headers = module.next_image[0].cloudfront_allowed_headers - - query_string = true - query_string_cache_keys = module.next_image[0].cloudfront_allowed_query_string_keys - } + origin_request_policy_id = module.next_image[0].cloudfront_origin_request_policy_id + cache_policy_id = module.next_image[0].cloudfront_cache_policy_id }] : [] cloudfront_custom_behaviors = var.cloudfront_custom_behaviors != null ? merge( @@ -208,6 +195,50 @@ locals { ) : local.next_image_custom_behavior } +resource "random_id" "policy_name" { + prefix = "${var.deployment_name}-" + byte_length = 4 +} + +# Managed origin request policy +data "aws_cloudfront_origin_request_policy" "managed_all_viewer" { + name = "Managed-AllViewer" +} + +resource "aws_cloudfront_cache_policy" "this" { + name = "${random_id.policy_name.hex}-cache" + comment = "Managed by Terraform Next.js" + + # Default values (Should be provided by origin) + min_ttl = 0 + default_ttl = 86400 + max_ttl = 31536000 + + parameters_in_cache_key_and_forwarded_to_origin { + cookies_config { + cookie_behavior = "all" + } + + headers_config { + header_behavior = length(var.cloudfront_cache_key_headers) == 0 ? "none" : "whitelist" + + dynamic "headers" { + for_each = length(var.cloudfront_cache_key_headers) == 0 ? [] : [true] + content { + items = var.cloudfront_cache_key_headers + } + } + } + + query_strings_config { + query_string_behavior = "all" + } + + enable_accept_encoding_gzip = true + enable_accept_encoding_brotli = true + } +} + module "proxy" { source = "./modules/proxy" @@ -225,6 +256,8 @@ module "proxy" { cloudfront_alias_domains = var.domain_names cloudfront_viewer_certificate_arn = var.cloudfront_viewer_certificate_arn cloudfront_minimum_protocol_version = var.cloudfront_minimum_protocol_version + cloudfront_origin_request_policy_id = data.aws_cloudfront_origin_request_policy.managed_all_viewer.id + cloudfront_cache_policy_id = aws_cloudfront_cache_policy.this.id debug_use_local_packages = var.debug_use_local_packages tags = var.tags lambda_role_permissions_boundary = var.lambda_role_permissions_boundary diff --git a/modules/proxy-config/main.tf b/modules/proxy-config/main.tf index 2044a834..7cd6e20a 100644 --- a/modules/proxy-config/main.tf +++ b/modules/proxy-config/main.tf @@ -51,6 +51,16 @@ resource "aws_s3_bucket_object" "proxy_config" { # CloudFront ############ +# Managed origin request policy +data "aws_cloudfront_origin_request_policy" "managed_cors_s3_origin" { + name = "Managed-CORS-S3Origin" +} + +# Managed cache policy +data "aws_cloudfront_cache_policy" "managed_caching_optimized_for_uncompressed_objects" { + name = "Managed-CachingOptimizedForUncompressedObjects" +} + resource "aws_cloudfront_origin_access_identity" "this" { comment = "S3 CloudFront access ${aws_s3_bucket.proxy_config.id}" } @@ -60,7 +70,6 @@ resource "aws_cloudfront_distribution" "distribution" { is_ipv6_enabled = true comment = "${var.deployment_name} - Proxy-Config" price_class = var.cloudfront_price_class - tags = var.tags origin { domain_name = aws_s3_bucket.proxy_config.bucket_regional_domain_name @@ -76,19 +85,11 @@ resource "aws_cloudfront_distribution" "distribution" { cached_methods = ["GET", "HEAD"] target_origin_id = local.s3_origin_id - forwarded_values { - query_string = false - - cookies { - forward = "none" - } - } - # Allow connections via HTTP to improve speed viewer_protocol_policy = "allow-all" - min_ttl = 0 - default_ttl = 86400 - max_ttl = 31536000 + + origin_request_policy_id = data.aws_cloudfront_origin_request_policy.managed_cors_s3_origin.id + cache_policy_id = data.aws_cloudfront_cache_policy.managed_caching_optimized_for_uncompressed_objects.id } viewer_certificate { @@ -100,4 +101,6 @@ resource "aws_cloudfront_distribution" "distribution" { restriction_type = "none" } } + + tags = var.tags } diff --git a/modules/proxy/main.tf b/modules/proxy/main.tf index 5763c518..19398fb7 100644 --- a/modules/proxy/main.tf +++ b/modules/proxy/main.tf @@ -41,7 +41,7 @@ module "edge_proxy" { lambda_at_edge = true function_name = random_id.function_name.hex - description = "Managed by Terraform-next.js" + description = "Managed by Terraform Next.js" handler = "handler.handler" runtime = var.lambda_default_runtime role_permissions_boundary = var.lambda_role_permissions_boundary @@ -58,6 +58,16 @@ module "edge_proxy" { # CloudFront ############ +# Managed origin request policy +data "aws_cloudfront_origin_request_policy" "managed_cors_s3_origin" { + name = "Managed-CORS-S3Origin" +} + +# Managed cache policy +data "aws_cloudfront_cache_policy" "managed_caching_optimized" { + name = "Managed-CachingOptimized" +} + resource "aws_cloudfront_distribution" "distribution" { enabled = true is_ipv6_enabled = true @@ -65,7 +75,6 @@ resource "aws_cloudfront_distribution" "distribution" { price_class = var.cloudfront_price_class aliases = var.cloudfront_alias_domains default_root_object = "index" - tags = var.tags # Static deployment S3 bucket origin { @@ -103,6 +112,7 @@ resource "aws_cloudfront_distribution" "distribution" { dynamic "custom_origin_config" { for_each = lookup(origin.value, "custom_origin_config", null) != null ? [true] : [] + content { http_port = lookup(origin.value["custom_origin_config"], "http_port", null) https_port = lookup(origin.value["custom_origin_config"], "https_port", null) @@ -121,17 +131,12 @@ resource "aws_cloudfront_distribution" "distribution" { cached_methods = ["GET", "HEAD"] target_origin_id = local.origin_id_static_deployment - forwarded_values { - query_string = true - - cookies { - forward = "all" - } - } - viewer_protocol_policy = "redirect-to-https" compress = true + origin_request_policy_id = var.cloudfront_origin_request_policy_id + cache_policy_id = var.cloudfront_cache_policy_id + lambda_function_association { event_type = "origin-request" lambda_arn = module.edge_proxy.this_lambda_function_qualified_arn @@ -148,21 +153,11 @@ resource "aws_cloudfront_distribution" "distribution" { cached_methods = ordered_cache_behavior.value["cached_methods"] target_origin_id = ordered_cache_behavior.value["target_origin_id"] - min_ttl = ordered_cache_behavior.value["min_ttl"] - default_ttl = ordered_cache_behavior.value["default_ttl"] - max_ttl = ordered_cache_behavior.value["max_ttl"] compress = ordered_cache_behavior.value["compress"] viewer_protocol_policy = ordered_cache_behavior.value["viewer_protocol_policy"] - dynamic "forwarded_values" { - for_each = lookup(ordered_cache_behavior.value, "forwarded_values", null) != null ? [true] : [] - content { - query_string = lookup(ordered_cache_behavior.value["forwarded_values"], "query_string", null) - cookies { - forward = lookup(lookup(ordered_cache_behavior.value["forwarded_values"], "cookies", null), "forward", null) - } - } - } + origin_request_policy_id = ordered_cache_behavior.value["origin_request_policy_id"] + cache_policy_id = ordered_cache_behavior.value["cache_policy_id"] } } @@ -173,19 +168,11 @@ resource "aws_cloudfront_distribution" "distribution" { cached_methods = ["GET", "HEAD"] target_origin_id = local.origin_id_static_deployment - forwarded_values { - query_string = false - - cookies { - forward = "none" - } - } - compress = true viewer_protocol_policy = "redirect-to-https" - min_ttl = 0 - default_ttl = 86400 - max_ttl = 31536000 + + origin_request_policy_id = data.aws_cloudfront_origin_request_policy.managed_cors_s3_origin.id + cache_policy_id = data.aws_cloudfront_cache_policy.managed_caching_optimized.id } # Custom error response when a doc is not found in S3 (returns 403) @@ -209,4 +196,6 @@ resource "aws_cloudfront_distribution" "distribution" { restriction_type = "none" } } + + tags = var.tags } diff --git a/modules/proxy/variables.tf b/modules/proxy/variables.tf index 90beed5d..e4d5fb5a 100644 --- a/modules/proxy/variables.tf +++ b/modules/proxy/variables.tf @@ -10,10 +10,6 @@ variable "static_bucket_endpoint" { type = string } -variable "cloudfront_price_class" { - type = string -} - variable "proxy_config_json" { type = string } @@ -32,20 +28,22 @@ variable "proxy_module_version" { default = "0.5.0" } -variable "debug_use_local_packages" { - type = bool - default = false -} - variable "lambda_default_runtime" { type = string default = "nodejs12.x" } -variable "deployment_name" { - type = string +variable "lambda_role_permissions_boundary" { + type = string + default = null } +############ +# CloudFront +############ +variable "cloudfront_price_class" { + type = string +} variable "cloudfront_origins" { type = list(any) default = null @@ -70,12 +68,30 @@ variable "cloudfront_minimum_protocol_version" { type = string } +variable "cloudfront_origin_request_policy_id" { + type = string +} + +variable "cloudfront_cache_policy_id" { + type = string +} + +########## +# Labeling +########## +variable "deployment_name" { + type = string +} + variable "tags" { type = map(string) default = {} } -variable "lambda_role_permissions_boundary" { - type = string - default = null +####### +# Debug +####### +variable "debug_use_local_packages" { + type = bool + default = false } diff --git a/variables.tf b/variables.tf index 651be141..4ef8efe9 100644 --- a/variables.tf +++ b/variables.tf @@ -13,12 +13,6 @@ variable "create_image_optimization" { default = true } -variable "image_optimization_version" { - description = "Next.js version from where you want to use the image optimizer from. Supports semver ranges." - type = string - default = "10.0.5-beta2" -} - variable "domain_names" { description = "Alternative domain names for the CloudFront distribution." type = list(string) @@ -118,6 +112,12 @@ variable "cloudfront_custom_behaviors" { default = null } +variable "cloudfront_cache_key_headers" { + description = "Header keys that should be used to calculate the cache key in CloudFront." + type = list(string) + default = ["Authorization"] +} + ########## # Labeling ########## diff --git a/versions.tf b/versions.tf index 92d5f158..6168f04e 100644 --- a/versions.tf +++ b/versions.tf @@ -4,7 +4,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = ">= 3.27.0" + version = ">= 3.28.0" } random = { source = "hashicorp/random"