diff --git a/.github/workflows/plan.yaml b/.github/workflows/plan.yaml index cf145b0..aba2b0e 100644 --- a/.github/workflows/plan.yaml +++ b/.github/workflows/plan.yaml @@ -21,7 +21,7 @@ jobs: aws-region: ${{ env.AWS_DEFAULT_REGION }} - uses: hashicorp/setup-terraform@v2 with: - terraform_version: 1.3.5 + terraform_version: ~1.10.0 - name: Terraform fmt id: fmt diff --git a/.terraform.lock.hcl b/.terraform.lock.hcl index 863fbe2..8f51ba5 100644 --- a/.terraform.lock.hcl +++ b/.terraform.lock.hcl @@ -5,6 +5,7 @@ provider "registry.terraform.io/hashicorp/archive" { version = "2.4.2" hashes = [ "h1:G4v6F6Lhqlo3EKGBKEK/kJRhNcQiRrhEdUiVpBHKHOA=", + "h1:qGT7b3uJm78Rb2YY7fFrY42a3bsi0Xbr2ghshaGqTms=", "zh:08faed7c9f42d82bc3d406d0d9d4971e2d1c2d34eae268ad211b8aca57b7f758", "zh:3564112ed2d097d7e0672378044a69b06642c326f6f1584d81c7cdd32ebf3a08", "zh:53cd9afd223c15828c1916e68cb728d2be1cbccb9545568d6c2b122d0bac5102", @@ -23,6 +24,7 @@ provider "registry.terraform.io/hashicorp/archive" { provider "registry.terraform.io/hashicorp/aws" { version = "5.34.0" hashes = [ + "h1:1UEoNI8LGCKvrl0+60qYm0wY8uOoKmF0W+HnuAI1U4k=", "h1:Tbq6dKE+XyXmkup6+7eQj2vH+eCJipk8R3VXhebVYi4=", "zh:01bb20ae12b8c66f0cacec4f417a5d6741f018009f3a66077008e67cce127aa4", "zh:3b0c9bdbbf846beef2c9573fc27898ceb71b69cf9d2f4b1dd2d0c2b539eab114", diff --git a/beta.tf b/beta.tf index 3e84111..9772733 100644 --- a/beta.tf +++ b/beta.tf @@ -20,11 +20,30 @@ module "beta_site" { lambda_arn = aws_lambda_function.image_submission_handler.qualified_arn } ] + }, + { + path_pattern = "/images/*" + allowed_methods = ["GET","HEAD"] + cached_methods = ["GET","HEAD"] + cache_policy_id = data.aws_cloudfront_cache_policy.caching_optimized.id + target_origin_id = local.images_origin_id + + function_association = [ + { + event_type = "viewer-request" + function_arn = aws_cloudfront_function.image-path-cleanup.arn + } + ] } ] common_domain = var.common_domain + origin_access_control_id_images = aws_cloudfront_origin_access_control.images.id + + phlask_images_bucket_name = aws_s3_bucket.images.id + phlask_logs_bucket_name = aws_s3_bucket.logs.id + providers = { aws.us-east-1 = aws.us-east-1 } diff --git a/common.tf b/common.tf index 4297e5b..c8e0f57 100644 --- a/common.tf +++ b/common.tf @@ -8,8 +8,16 @@ data "aws_cloudfront_origin_request_policy" "s3_origin" { name = "Managed-CORS-S3Origin" } -data "aws_cloudfront_origin_access_identity" "images" { - id = "EYLKT3B3LMJM1" +resource "aws_cloudfront_origin_access_control" "images" { + name = aws_s3_bucket.images.bucket_regional_domain_name + description = "Managed by Terraform" + origin_access_control_origin_type = "s3" + signing_behavior = "always" + signing_protocol = "sigv4" +} + +resource "aws_route53_zone" "phlask" { + name = "phlask.me" } resource "aws_s3_bucket" "images" { @@ -27,14 +35,16 @@ resource "aws_s3_bucket_policy" "images" { } data "aws_iam_policy_document" "images" { + policy_id = "PolicyForCloudFrontPrivateContent" + statement { - sid = "PolicyForCloudFrontPrivateContent" + sid = "AllowCloudFrontServicePrincipal" effect = "Allow" principals { - type = "AWS" - identifiers = [data.aws_cloudfront_origin_access_identity.images.iam_arn] + type = "Service" + identifiers = ["cloudfront.amazonaws.com"] } actions = [ @@ -44,6 +54,16 @@ data "aws_iam_policy_document" "images" { resources = [ "${aws_s3_bucket.images.arn}/*", ] + + condition { + test = "StringEquals" + variable = "AWS:SourceArn" + + values = [ + module.prod_site.cf_distribution_arn, + module.beta_site.cf_distribution_arn + ] + } } } @@ -117,3 +137,11 @@ resource "aws_s3_bucket_ownership_controls" "logs" { object_ownership = "BucketOwnerPreferred" } } + +resource "aws_cloudfront_function" "image-path-cleanup" { + name = "image-path-cleanup" + runtime = "cloudfront-js-2.0" + comment = "Managed by Terraform" + publish = true + code = file("${path.module}/src_code/image-path-cleanup/function.js") +} diff --git a/docker-compose.yml b/docker-compose.yml index 520348d..af36b93 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '3' services: shell: - image: hashicorp/terraform:1.3.5 + image: hashicorp/terraform:1.10 stdin_open: true working_dir: /usr/src/app entrypoint: /bin/sh diff --git a/modules/phlask-baseline-resources/dns.tf b/modules/phlask-baseline-resources/dns.tf index 159a8f8..23298a9 100644 --- a/modules/phlask-baseline-resources/dns.tf +++ b/modules/phlask-baseline-resources/dns.tf @@ -1,3 +1,7 @@ +data "aws_route53_zone" "phlask" { + name = "${var.common_domain}." +} + resource "aws_acm_certificate" "phlask_site" { provider = aws.us-east-1 @@ -9,8 +13,22 @@ resource "aws_acm_certificate" "phlask_site" { } } -data "aws_cloudfront_origin_access_identity" "images" { - id = "EYLKT3B3LMJM1" +resource "aws_route53_record" "phlask_site" { + for_each = { + for dvo in aws_acm_certificate.phlask_site.domain_validation_options : dvo.domain_name => { + name = dvo.resource_record_name + record = dvo.resource_record_value + type = dvo.resource_record_type + zone_id = data.aws_route53_zone.phlask.zone_id + } + } + + allow_overwrite = true + name = each.value.name + records = [each.value.record] + ttl = 60 + type = each.value.type + zone_id = each.value.zone_id } resource "aws_cloudfront_distribution" "phlask_site" { @@ -29,13 +47,10 @@ resource "aws_cloudfront_distribution" "phlask_site" { } origin { - domain_name = data.aws_s3_bucket.phlask_images.bucket_domain_name - origin_id = local.images_origin_id - origin_path = "/${var.env_name}" - - s3_origin_config { - origin_access_identity = data.aws_cloudfront_origin_access_identity.images.cloudfront_access_identity_path - } + domain_name = data.aws_s3_bucket.phlask_images.bucket_regional_domain_name + origin_access_control_id = var.origin_access_control_id_images + origin_id = local.images_origin_id + origin_path = "/${var.env_name}/tap-images" } dynamic "custom_error_response" { @@ -55,7 +70,7 @@ resource "aws_cloudfront_distribution" "phlask_site" { logging_config { include_cookies = true - bucket = data.aws_s3_bucket.phlask_logs.bucket_domain_name # change to data source for a logging bucket + bucket = data.aws_s3_bucket.phlask_logs.bucket_domain_name prefix = "${var.env_name}/cloudfront/" } @@ -138,6 +153,15 @@ resource "aws_cloudfront_distribution" "phlask_site" { lambda_arn = lambda_function_association.value["lambda_arn"] } } + + dynamic "function_association" { + for_each = try(ordered_cache_behavior.value["function_association"], []) + + content { + event_type = function_association.value["event_type"] + function_arn = function_association.value["function_arn"] + } + } } } @@ -157,4 +181,18 @@ resource "aws_cloudfront_distribution" "phlask_site" { minimum_protocol_version = "TLSv1.2_2021" ssl_support_method = "sni-only" } +} + +resource "aws_route53_record" "phlask_site_cloudfront" { + for_each = toset(concat(["${var.env_name == "prod" ? "phlask.me" : "${var.env_name}.${var.common_domain}"}"], var.additional_aliases)) + + zone_id = data.aws_route53_zone.phlask.zone_id + name = each.value + type = "A" + + alias { + name = aws_cloudfront_distribution.phlask_site.domain_name + zone_id = aws_cloudfront_distribution.phlask_site.hosted_zone_id + evaluate_target_health = false + } } \ No newline at end of file diff --git a/modules/phlask-baseline-resources/outputs.tf b/modules/phlask-baseline-resources/outputs.tf new file mode 100644 index 0000000..d7c5cf0 --- /dev/null +++ b/modules/phlask-baseline-resources/outputs.tf @@ -0,0 +1,3 @@ +output "cf_distribution_arn" { + value = aws_cloudfront_distribution.phlask_site.arn +} \ No newline at end of file diff --git a/modules/phlask-baseline-resources/storage.tf b/modules/phlask-baseline-resources/storage.tf index 1ca2832..0347d53 100644 --- a/modules/phlask-baseline-resources/storage.tf +++ b/modules/phlask-baseline-resources/storage.tf @@ -1,13 +1,9 @@ -# locals { -# beta_origin_id = "S3-Website-test.${var.common_domain}.s3-website.us-east-2.amazonaws.com" -# beta_images_origin_id = "S3-phlask-tap-images/test" -# } data "aws_s3_bucket" "phlask_images" { - bucket = "phlask-tap-images" + bucket = var.phlask_images_bucket_name } data "aws_s3_bucket" "phlask_logs" { - bucket = "phlask-logs" + bucket = var.phlask_logs_bucket_name } resource "aws_s3_bucket" "phlask_site" { diff --git a/modules/phlask-baseline-resources/variables.tf b/modules/phlask-baseline-resources/variables.tf index f4162ee..b778e2c 100644 --- a/modules/phlask-baseline-resources/variables.tf +++ b/modules/phlask-baseline-resources/variables.tf @@ -2,6 +2,14 @@ variable "env_name" { type = string } +variable "phlask_images_bucket_name" { + type = string +} + +variable "phlask_logs_bucket_name" { + type = string +} + variable "default_cache_behavior" { type = any default = {} @@ -24,4 +32,8 @@ variable "additional_aliases" { variable "custom_error_response" { type = any default = [] +} + +variable "origin_access_control_id_images" { + type = string } \ No newline at end of file diff --git a/prod.tf b/prod.tf index cd6aa18..63d0836 100644 --- a/prod.tf +++ b/prod.tf @@ -27,12 +27,31 @@ module "prod_site" { lambda_arn = aws_lambda_function.image_submission_handler.qualified_arn } ] - } + }, + { + path_pattern = "/images/*" + allowed_methods = ["GET","HEAD"] + cached_methods = ["GET","HEAD"] + cache_policy_id = data.aws_cloudfront_cache_policy.caching_optimized.id + target_origin_id = local.images_origin_id + + function_association = [ + { + event_type = "viewer-request" + function_arn = aws_cloudfront_function.image-path-cleanup.arn + } + ] + } ] common_domain = var.common_domain additional_aliases = ["www.${var.common_domain}"] + origin_access_control_id_images = aws_cloudfront_origin_access_control.images.id + + phlask_images_bucket_name = aws_s3_bucket.images.id + phlask_logs_bucket_name = aws_s3_bucket.logs.id + providers = { aws.us-east-1 = aws.us-east-1 } @@ -40,7 +59,8 @@ module "prod_site" { resource "aws_cloudfront_function" "www_redirect" { name = "www-redirect" - runtime = "cloudfront-js-1.0" + comment = "Managed by Terraform" + runtime = "cloudfront-js-2.0" publish = true code = file("${path.module}/src_code/www-redirect/function.js") } diff --git a/src_code/image-path-cleanup/function.js b/src_code/image-path-cleanup/function.js new file mode 100644 index 0000000..9432294 --- /dev/null +++ b/src_code/image-path-cleanup/function.js @@ -0,0 +1,5 @@ +function handler(event) { + var request = event.request; + request.uri = request.uri.replace(/^\/[^/]*\//, "/"); + return request; +} \ No newline at end of file diff --git a/src_code/test-page-list/lambda_function.py b/src_code/test-page-list/lambda_function.py index 061177c..c36129d 100644 --- a/src_code/test-page-list/lambda_function.py +++ b/src_code/test-page-list/lambda_function.py @@ -75,7 +75,7 @@ def lambda_handler(event, context): continue timestamp = time.strftime("%a, %d %b %Y %H:%M:%S", time.localtime(int(item['timeCreated']))) - testResult = getTestResult(item['lighthouseTestAvailable']) + testResult = getTestResult(item.get('lighthouseTestAvailable', False)) if not item.get('gistID'): lighthouseResult = "N/A" else: diff --git a/src_code/www-redirect/function.js b/src_code/www-redirect/function.js index fdd8773..3371f53 100644 --- a/src_code/www-redirect/function.js +++ b/src_code/www-redirect/function.js @@ -1,6 +1,5 @@ function handler(event) { var request = event.request; - var headers = request.headers; var host = request.headers.host.value; if (host == "phlask.me") { diff --git a/test.tf b/test.tf index 9a68e2b..fa87743 100644 --- a/test.tf +++ b/test.tf @@ -1,5 +1,3 @@ -# TODO: We could switch to use a aws_cloudfront_cache_policy resource to switch away from the existing legacy caching configuration - module "test_site" { source = "./modules/phlask-baseline-resources" @@ -96,12 +94,13 @@ module "test_site" { ] common_domain = var.common_domain + + origin_access_control_id_images = aws_cloudfront_origin_access_control.images.id + + phlask_images_bucket_name = aws_s3_bucket.images.id + phlask_logs_bucket_name = aws_s3_bucket.logs.id providers = { aws.us-east-1 = aws.us-east-1 } } - -# resource "aws_lambda_function" "test_page_redirect" { - -# }