diff --git a/aws/dev/backend/locals.tf b/aws/dev/backend/locals.tf index f495906..2ff5400 100644 --- a/aws/dev/backend/locals.tf +++ b/aws/dev/backend/locals.tf @@ -13,5 +13,6 @@ locals { routes = toset([ "/search", "/proxy", + "/healthz" ]) } diff --git a/aws/dev/backend/main.tf b/aws/dev/backend/main.tf index 13c315a..777c049 100644 --- a/aws/dev/backend/main.tf +++ b/aws/dev/backend/main.tf @@ -29,7 +29,10 @@ module "salt" { ### IAM for lambda execution and logging module "lambda_iam" { - source = "../../modules/backend/lambda_iam" + source = "../../modules/universal/lambda_iam" + + role_name = "aws-iam-role-exec-hearchco-api" + policy_name = "hearchco_api_logging" } ## Cloudfront @@ -42,7 +45,7 @@ provider "aws" { ### Certificate for the Cloudfront distribution module "hearchco_cdn_certificate" { - source = "../../modules/backend/acm" + source = "../../modules/universal/acm" domain_name = local.api_domain_name hosted_zone_id = data.aws_route53_zone.hearchco_route53.zone_id @@ -62,14 +65,19 @@ module "hearchco_cloudfront" { paths_cache = { "/search" = { - min_ttl = 0 - default_ttl = 0 - max_ttl = 1 + min_ttl = 60 // 1 minute + default_ttl = 600 // 10 minutes + max_ttl = 3600 // 1 hour }, "/proxy" = { - min_ttl = 5 - default_ttl = 30 - max_ttl = 60 + min_ttl = 3600 // 1 hour + default_ttl = 86400 // 1 day + max_ttl = 259200 // 3 days + }, + "/healthz" = { + min_ttl = 0 + default_ttl = 0 + max_ttl = 5 }, } diff --git a/aws/dev/backend/region_eu_central_1.tf b/aws/dev/backend/region_eu_central_1.tf index ecfa9ad..7b8bd56 100644 --- a/aws/dev/backend/region_eu_central_1.tf +++ b/aws/dev/backend/region_eu_central_1.tf @@ -9,7 +9,9 @@ provider "aws" { } module "hearchco_s3_eu_central_1" { - source = "../../modules/backend/s3" + source = "../../modules/universal/s3_source_code" + filename = "bootstrap" + bucket_name = "hearchco-api-binary" providers = { aws = aws.eu-central-1 @@ -31,7 +33,7 @@ module "hearchco_lambda_eu_central_1" { } module "hearchco_certificate_eu_central_1" { - source = "../../modules/backend/acm" + source = "../../modules/universal/acm" domain_name = local.api_gateway_domain_name hosted_zone_id = data.aws_route53_zone.hearchco_route53.zone_id diff --git a/aws/dev/frontend/cloudfront.tf b/aws/dev/frontend/cloudfront.tf new file mode 100644 index 0000000..53ac171 --- /dev/null +++ b/aws/dev/frontend/cloudfront.tf @@ -0,0 +1,39 @@ +module "hearchco_cdn_certificate" { + source = "../../modules/universal/acm" + domain_name = var.domain_name + hosted_zone_id = data.aws_route53_zone.hearchco_route53.zone_id + + providers = { + aws = aws.us-east-1-cdn + } +} + +module "hearchco_cloudfront" { + source = "../../modules/frontend/cloudfront" + domain_name = var.domain_name + hosted_zone_id = data.aws_route53_zone.hearchco_route53.zone_id + target_domain_name = module.hearchco_s3_assets.bucket_domain_name + oai_id = module.hearchco_s3_assets.oai + lambda_edge_arn = module.hearchco_lambda_edge.invoke_arn + acm_certificate_arn = module.hearchco_cdn_certificate.cert_arn + price_class = "PriceClass_100" + + top_level_assets = module.hearchco_s3_assets.top_level_assets + + paths_cache = { + "/search" = { + min_ttl = 60 // 1 minute + default_ttl = 600 // 10 minutes + max_ttl = 3600 // 1 hour + }, + "/healthz" = { + min_ttl = 0 + default_ttl = 0 + max_ttl = 5 + }, + } + + providers = { + aws = aws.us-east-1-cdn + } +} diff --git a/aws/dev/frontend/lambda.tf b/aws/dev/frontend/lambda.tf new file mode 100644 index 0000000..2bafcf2 --- /dev/null +++ b/aws/dev/frontend/lambda.tf @@ -0,0 +1,39 @@ +module "hearchco_env_injection" { + source = "../../modules/frontend/env_injection" + environment = local.environment +} + +module "hearchco_s3_source_code" { + source = "../../modules/universal/s3_source_code" + bucket_name = "hearchco-ssr-function" + filename = module.hearchco_env_injection.filename + path = module.hearchco_env_injection.path + + depends_on = [module.hearchco_env_injection] + + providers = { + aws = aws.us-east-1-cdn + } +} + +module "lambda_iam" { + source = "../../modules/universal/lambda_iam" + + role_name = "aws-iam-role-exec-hearchco-ssr" + policy_name = "hearchco_ssr_logging" + edge = true +} + +### Lambda@Edge for SSR (us-east-1 for Cloudfront) +module "hearchco_lambda_edge" { + source = "../../modules/frontend/lambda_edge" + role = module.lambda_iam.role_arn + s3_bucket = module.hearchco_s3_source_code.bucket_id + s3_key = module.hearchco_s3_source_code.s3_key + source_code_hash = module.hearchco_s3_source_code.source_code_hash + # environment = local.environment # Not supported by Lambda@Edge + + providers = { + aws = aws.us-east-1-cdn + } +} diff --git a/aws/dev/frontend/locals.tf b/aws/dev/frontend/locals.tf new file mode 100644 index 0000000..34492ee --- /dev/null +++ b/aws/dev/frontend/locals.tf @@ -0,0 +1,9 @@ +locals { + api_domain_name = "api.${var.domain_name}" + + # Lambda + environment = tomap({ + API_URI = "https://${local.api_domain_name}" + PUBLIC_API_URI = "https://${local.api_domain_name}" + }) +} diff --git a/aws/dev/frontend/main.tf b/aws/dev/frontend/main.tf new file mode 100644 index 0000000..0bff9ab --- /dev/null +++ b/aws/dev/frontend/main.tf @@ -0,0 +1,27 @@ +terraform { + backend "s3" { + profile = "992382822186_TFStateLock" + region = "eu-central-1" + dynamodb_table = "hearchco-shared-tf-state" + bucket = "hearchco-shared-tf-state" + key = "aws/dev/frontend/terraform.tfstate" + encrypt = "true" + } +} + +provider "aws" { + profile = var.aws_profile + region = "eu-central-1" +} + +# us-east-1 region required for Cloudfront's certificate and Lambda@Edge +provider "aws" { + profile = var.aws_profile + region = "us-east-1" + alias = "us-east-1-cdn" +} + +# Route53 DNS +data "aws_route53_zone" "hearchco_route53" { + name = var.domain_name +} diff --git a/aws/dev/frontend/s3.tf b/aws/dev/frontend/s3.tf new file mode 100644 index 0000000..6a679ab --- /dev/null +++ b/aws/dev/frontend/s3.tf @@ -0,0 +1,8 @@ +module "hearchco_s3_assets" { + source = "../../modules/frontend/s3_assets" + bucket_name = "hearchco-assets" + + providers = { + aws = aws.us-east-1-cdn + } +} diff --git a/aws/dev/frontend/variables.tf b/aws/dev/frontend/variables.tf new file mode 100644 index 0000000..038b99a --- /dev/null +++ b/aws/dev/frontend/variables.tf @@ -0,0 +1,9 @@ +variable "aws_profile" { + type = string + default = "891377085136_Admin" +} + +variable "domain_name" { + type = string + default = "dev.hearch.co" +} diff --git a/aws/modules/backend/acm/versions.tf b/aws/dev/frontend/versions.tf similarity index 100% rename from aws/modules/backend/acm/versions.tf rename to aws/dev/frontend/versions.tf diff --git a/aws/modules/backend/cloudfront/cache.tf b/aws/modules/backend/cloudfront/cache.tf index a7a0dbe..7d7d1aa 100644 --- a/aws/modules/backend/cloudfront/cache.tf +++ b/aws/modules/backend/cloudfront/cache.tf @@ -1,5 +1,5 @@ resource "aws_cloudfront_cache_policy" "default_cache_policy" { - name = "default-cache-policy" + name = "api-default-cache-policy" min_ttl = var.default_cache.min_ttl default_ttl = var.default_cache.default_ttl max_ttl = var.default_cache.max_ttl @@ -34,7 +34,7 @@ resource "aws_cloudfront_cache_policy" "default_cache_policy" { resource "aws_cloudfront_cache_policy" "cache_policy" { for_each = var.paths_cache - name = "cache-policy${replace(each.key, "/", "-")}" + name = "api-cache-policy${replace(each.key, "/", "-")}" min_ttl = each.value.min_ttl default_ttl = each.value.default_ttl max_ttl = each.value.max_ttl diff --git a/aws/modules/backend/lambda/variables.tf b/aws/modules/backend/lambda/variables.tf index 62be3be..ef06e0e 100644 --- a/aws/modules/backend/lambda/variables.tf +++ b/aws/modules/backend/lambda/variables.tf @@ -62,5 +62,5 @@ variable "memory_size" { variable "timeout" { type = number - default = 5 + default = 4 } diff --git a/aws/modules/backend/s3/locals.tf b/aws/modules/backend/s3/locals.tf deleted file mode 100644 index fbda9e6..0000000 --- a/aws/modules/backend/s3/locals.tf +++ /dev/null @@ -1,10 +0,0 @@ -data "aws_region" "current" {} -data "aws_caller_identity" "current" {} - -locals { - bucket_name = "${var.bucket_name}-${data.aws_region.current.name}-${data.aws_caller_identity.current.account_id}" - binary_name = "bootstrap" - binary_key = "${local.binary_name}.zip" - source_file = "${var.path}/${local.binary_name}" - output_path = "${local.source_file}.zip" -} diff --git a/aws/modules/backend/s3/main.tf b/aws/modules/backend/s3/main.tf deleted file mode 100644 index e162485..0000000 --- a/aws/modules/backend/s3/main.tf +++ /dev/null @@ -1,29 +0,0 @@ -resource "aws_s3_bucket" "binary" { - bucket = local.bucket_name -} - -# Ownership -resource "aws_s3_bucket_ownership_controls" "binary" { - bucket = aws_s3_bucket.binary.id - - rule { - object_ownership = "BucketOwnerPreferred" - } -} - -# Visibility -resource "aws_s3_bucket_acl" "binary" { - bucket = aws_s3_bucket.binary.id - acl = "private" - - depends_on = [aws_s3_bucket_ownership_controls.binary] -} - -# Versioning -resource "aws_s3_bucket_versioning" "binary" { - bucket = aws_s3_bucket.binary.id - - versioning_configuration { - status = "Enabled" - } -} diff --git a/aws/modules/backend/s3/outputs.tf b/aws/modules/backend/s3/outputs.tf deleted file mode 100644 index 9e0939e..0000000 --- a/aws/modules/backend/s3/outputs.tf +++ /dev/null @@ -1,15 +0,0 @@ -output "bucket_id" { - value = aws_s3_bucket.binary.id -} - -output "bucket_arn" { - value = aws_s3_bucket.binary.arn -} - -output "s3_key" { - value = aws_s3_object.binary.key -} - -output "source_code_hash" { - value = aws_s3_object.binary.source_hash -} diff --git a/aws/modules/backend/s3/upload.tf b/aws/modules/backend/s3/upload.tf deleted file mode 100644 index c556602..0000000 --- a/aws/modules/backend/s3/upload.tf +++ /dev/null @@ -1,13 +0,0 @@ -// Upload new file -data "archive_file" "binary" { - type = "zip" - source_file = local.source_file - output_path = local.output_path -} - -resource "aws_s3_object" "binary" { - key = local.binary_key - bucket = aws_s3_bucket.binary.id - source = data.archive_file.binary.output_path - source_hash = data.archive_file.binary.output_base64sha256 -} diff --git a/aws/modules/backend/s3/variables.tf b/aws/modules/backend/s3/variables.tf deleted file mode 100644 index 7ced39d..0000000 --- a/aws/modules/backend/s3/variables.tf +++ /dev/null @@ -1,10 +0,0 @@ -// this will get appended to region name -variable "bucket_name" { - type = string - default = "hearchco-api-binary" -} - -variable "path" { - type = string - default = "tmp" -} diff --git a/aws/modules/frontend/cloudfront/cache.tf b/aws/modules/frontend/cloudfront/cache.tf new file mode 100644 index 0000000..b2eba07 --- /dev/null +++ b/aws/modules/frontend/cloudfront/cache.tf @@ -0,0 +1,100 @@ +resource "aws_cloudfront_cache_policy" "default_cache_policy" { + name = "ssr-default-cache-policy" + min_ttl = var.default_cache.min_ttl + default_ttl = var.default_cache.default_ttl + max_ttl = var.default_cache.max_ttl + + parameters_in_cache_key_and_forwarded_to_origin { + enable_accept_encoding_brotli = true + enable_accept_encoding_gzip = true + + cookies_config { + cookie_behavior = "none" + } + + headers_config { + header_behavior = "whitelist" + headers { + items = [ + "Accept", + "Accept-Language", + "Access-Control-Request-Headers", + "Access-Control-Request-Method", + "Origin", + ] + } + } + + query_strings_config { + query_string_behavior = "all" + } + } +} + +resource "aws_cloudfront_cache_policy" "cache_policy_s3" { + name = "ssr-cache-policy-assets" + min_ttl = var.default_asset_cache.min_ttl + default_ttl = var.default_asset_cache.default_ttl + max_ttl = var.default_asset_cache.max_ttl + + parameters_in_cache_key_and_forwarded_to_origin { + enable_accept_encoding_brotli = true + enable_accept_encoding_gzip = true + + cookies_config { + cookie_behavior = "none" + } + + headers_config { + header_behavior = "whitelist" + headers { + items = [ + "Accept", + "Accept-Language", + "Access-Control-Request-Headers", + "Access-Control-Request-Method", + "Origin", + ] + } + } + + query_strings_config { + query_string_behavior = "none" + } + } +} + +resource "aws_cloudfront_cache_policy" "cache_policy" { + for_each = var.paths_cache + + name = "ssr-cache-policy${replace(each.key, "/", "-")}" + min_ttl = each.value.min_ttl + default_ttl = each.value.default_ttl + max_ttl = each.value.max_ttl + + parameters_in_cache_key_and_forwarded_to_origin { + enable_accept_encoding_brotli = true + enable_accept_encoding_gzip = true + + cookies_config { + cookie_behavior = "none" + } + + headers_config { + header_behavior = "whitelist" + headers { + items = [ + "Accept", + "Accept-Language", + "Access-Control-Request-Headers", + "Access-Control-Request-Method", + "Origin", + ] + } + } + + query_strings_config { + query_string_behavior = "all" + } + } +} diff --git a/aws/modules/frontend/cloudfront/function.tf b/aws/modules/frontend/cloudfront/function.tf new file mode 100644 index 0000000..7e9afb0 --- /dev/null +++ b/aws/modules/frontend/cloudfront/function.tf @@ -0,0 +1,6 @@ +resource "aws_cloudfront_function" "sveltekit-rewriter" { + name = "sveltekit-rewriter" + runtime = "cloudfront-js-2.0" + publish = true + code = file(var.function_path) +} diff --git a/aws/modules/frontend/cloudfront/main.tf b/aws/modules/frontend/cloudfront/main.tf new file mode 100644 index 0000000..f594dc9 --- /dev/null +++ b/aws/modules/frontend/cloudfront/main.tf @@ -0,0 +1,88 @@ +locals { + origin_id = "s3-origin" +} + +resource "aws_cloudfront_distribution" "sveltekit_distribution" { + enabled = true + is_ipv6_enabled = true + wait_for_deployment = true + price_class = var.price_class + aliases = [var.domain_name] + + origin { + origin_id = local.origin_id + domain_name = var.target_domain_name + + s3_origin_config { + origin_access_identity = "origin-access-identity/cloudfront/${var.oai_id}" + } + } + + default_cache_behavior { + allowed_methods = var.allowed_methods + cached_methods = var.cached_methods + target_origin_id = local.origin_id + viewer_protocol_policy = "redirect-to-https" + compress = true + cache_policy_id = aws_cloudfront_cache_policy.default_cache_policy.id + + function_association { + event_type = "viewer-request" + function_arn = aws_cloudfront_function.sveltekit-rewriter.arn + } + + lambda_function_association { + event_type = "origin-request" + lambda_arn = var.lambda_edge_arn + include_body = true + } + } + + dynamic "ordered_cache_behavior" { + for_each = var.top_level_assets + content { + path_pattern = ordered_cache_behavior.key + allowed_methods = var.allowed_methods_s3 + cached_methods = var.cached_methods_s3 + target_origin_id = local.origin_id + viewer_protocol_policy = "redirect-to-https" + compress = true + cache_policy_id = aws_cloudfront_cache_policy.cache_policy_s3.id + } + } + + dynamic "ordered_cache_behavior" { + for_each = var.paths_cache + content { + path_pattern = ordered_cache_behavior.key + allowed_methods = var.allowed_methods + cached_methods = var.cached_methods + target_origin_id = local.origin_id + viewer_protocol_policy = "redirect-to-https" + compress = true + cache_policy_id = aws_cloudfront_cache_policy.cache_policy[ordered_cache_behavior.key].id + + function_association { + event_type = "viewer-request" + function_arn = aws_cloudfront_function.sveltekit-rewriter.arn + } + + lambda_function_association { + event_type = "origin-request" + lambda_arn = var.lambda_edge_arn + include_body = true + } + } + } + + viewer_certificate { + acm_certificate_arn = var.acm_certificate_arn + ssl_support_method = "sni-only" + } + + restrictions { + geo_restriction { + restriction_type = "none" + } + } +} diff --git a/aws/modules/frontend/cloudfront/route53_records.tf b/aws/modules/frontend/cloudfront/route53_records.tf new file mode 100644 index 0000000..56c2ede --- /dev/null +++ b/aws/modules/frontend/cloudfront/route53_records.tf @@ -0,0 +1,23 @@ +resource "aws_route53_record" "record" { + name = var.domain_name + type = "A" + zone_id = var.hosted_zone_id + + alias { + name = aws_cloudfront_distribution.sveltekit_distribution.domain_name + zone_id = aws_cloudfront_distribution.sveltekit_distribution.hosted_zone_id + evaluate_target_health = true + } +} + +resource "aws_route53_record" "record_v6" { + name = var.domain_name + type = "AAAA" + zone_id = var.hosted_zone_id + + alias { + name = aws_cloudfront_distribution.sveltekit_distribution.domain_name + zone_id = aws_cloudfront_distribution.sveltekit_distribution.hosted_zone_id + evaluate_target_health = true + } +} diff --git a/aws/modules/frontend/cloudfront/variables.tf b/aws/modules/frontend/cloudfront/variables.tf new file mode 100644 index 0000000..4d9fcd5 --- /dev/null +++ b/aws/modules/frontend/cloudfront/variables.tf @@ -0,0 +1,93 @@ +variable "domain_name" { + type = string +} + +variable "target_domain_name" { + type = string +} + +variable "oai_id" { + type = string +} + +variable "function_path" { + type = string + default = "tmp/cloudfront/index.js" +} + +variable "lambda_edge_arn" { + type = string +} + +variable "price_class" { + type = string + default = "PriceClass_All" +} + +variable "hosted_zone_id" { + type = string +} + +variable "acm_certificate_arn" { + type = string +} + +variable "allowed_methods" { + type = set(string) + default = ["HEAD", "DELETE", "POST", "GET", "OPTIONS", "PUT", "PATCH"] +} + +variable "cached_methods" { + type = set(string) + default = ["HEAD", "GET"] +} + +variable "allowed_methods_s3" { + type = set(string) + default = ["HEAD", "GET", "OPTIONS"] +} + +variable "cached_methods_s3" { + type = set(string) + default = ["HEAD", "GET"] +} + +variable "default_cache" { + type = object({ + min_ttl = number + default_ttl = number + max_ttl = number + }) + default = { + min_ttl = 60 // 1 minute + default_ttl = 21600 // 6 hours + max_ttl = 86400 // 1 day + } +} + +variable "default_asset_cache" { + type = object({ + min_ttl = number + default_ttl = number + max_ttl = number + }) + default = { + min_ttl = 60 // 1 minute + default_ttl = 21600 // 6 hours + max_ttl = 86400 // 1 day + } +} + +variable "top_level_assets" { + type = set(string) + default = [] +} + +variable "paths_cache" { + type = map(object({ + min_ttl = number + default_ttl = number + max_ttl = number + })) + default = {} +} diff --git a/aws/modules/backend/lambda_iam/versions.tf b/aws/modules/frontend/cloudfront/versions.tf similarity index 100% rename from aws/modules/backend/lambda_iam/versions.tf rename to aws/modules/frontend/cloudfront/versions.tf diff --git a/aws/modules/frontend/env_injection/main.tf b/aws/modules/frontend/env_injection/main.tf new file mode 100644 index 0000000..f3ee06e --- /dev/null +++ b/aws/modules/frontend/env_injection/main.tf @@ -0,0 +1,14 @@ +data "local_file" "source_file" { + filename = "${var.path}/${var.source_file}" +} + +locals { + environment_string = jsonencode(var.environment) + source_content = data.local_file.source_file.content + substitude_source_content = replace(local.source_content, var.placeholder, local.environment_string) +} + +resource "local_file" "output_file" { + content = local.substitude_source_content + filename = "${var.path}/injected/${var.source_file}" +} diff --git a/aws/modules/frontend/env_injection/outputs.tf b/aws/modules/frontend/env_injection/outputs.tf new file mode 100644 index 0000000..d3eeabd --- /dev/null +++ b/aws/modules/frontend/env_injection/outputs.tf @@ -0,0 +1,9 @@ +// last part of the path (divided by /) +output "filename" { + value = split("/", local_file.output_file.filename)[length(split("/", local_file.output_file.filename)) - 1] +} + +// everything except the last part of the path (divided by /) +output "path" { + value = join("/", slice(split("/", local_file.output_file.filename), 0, length(split("/", local_file.output_file.filename)) - 1)) +} diff --git a/aws/modules/frontend/env_injection/variables.tf b/aws/modules/frontend/env_injection/variables.tf new file mode 100644 index 0000000..72eca1d --- /dev/null +++ b/aws/modules/frontend/env_injection/variables.tf @@ -0,0 +1,19 @@ +variable "path" { + type = string + default = "tmp/lambda" +} + +variable "source_file" { + type = string + default = "index.mjs" +} + +variable "placeholder" { + type = string + default = "'{{_EDGE_FUNCTION_ENVIRONMENT_}}'" +} + +variable "environment" { + type = map(string) + default = {} +} diff --git a/aws/modules/frontend/env_injection/versions.tf b/aws/modules/frontend/env_injection/versions.tf new file mode 100644 index 0000000..146cb2c --- /dev/null +++ b/aws/modules/frontend/env_injection/versions.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 1.5" + required_providers { + local = { + source = "hashicorp/local" + version = "~> 2.0" + } + } +} diff --git a/aws/modules/frontend/lambda_edge/main.tf b/aws/modules/frontend/lambda_edge/main.tf new file mode 100644 index 0000000..84bde82 --- /dev/null +++ b/aws/modules/frontend/lambda_edge/main.tf @@ -0,0 +1,17 @@ +resource "aws_lambda_function" "ssr_lambda" { + function_name = var.function_name + handler = var.handler + runtime = var.runtime + role = var.role + + s3_bucket = var.s3_bucket + s3_key = var.s3_key + source_code_hash = var.source_code_hash + + architectures = var.architectures + memory_size = var.memory_size + timeout = var.timeout + + // needed for Lambda@Edge + publish = true +} diff --git a/aws/modules/frontend/lambda_edge/outputs.tf b/aws/modules/frontend/lambda_edge/outputs.tf new file mode 100644 index 0000000..b72ef17 --- /dev/null +++ b/aws/modules/frontend/lambda_edge/outputs.tf @@ -0,0 +1,7 @@ +output "function_name" { + value = aws_lambda_function.ssr_lambda.function_name +} + +output "invoke_arn" { + value = aws_lambda_function.ssr_lambda.qualified_arn // needed for Lambda@Edge +} diff --git a/aws/modules/frontend/lambda_edge/variables.tf b/aws/modules/frontend/lambda_edge/variables.tf new file mode 100644 index 0000000..69ab53c --- /dev/null +++ b/aws/modules/frontend/lambda_edge/variables.tf @@ -0,0 +1,53 @@ +variable "function_name" { + type = string + default = "hearchco-ssr-lambda" +} + +# has to correspond to file.function format in the source code +variable "handler" { + type = string + default = "index.handler" +} + +variable "runtime" { + type = string + default = "nodejs20.x" +} + +variable "role" { + type = string +} + +variable "s3_bucket" { + type = string +} + +variable "s3_key" { + type = string +} + +variable "source_code_hash" { + type = string +} + +// since Lambda@Edge doesn't support enviroment variables, it's required to pass them as a part of the function +# variable "environment" { +# type = map(string) +# default = {} +# } + +variable "architectures" { + type = list(string) + default = ["x86_64"] +} + +# SvelteKit used around 114MB and crashed, so it's better to have it at least 160MB +variable "memory_size" { + type = number + default = 160 +} + +variable "timeout" { + type = number + default = 5 +} diff --git a/aws/modules/frontend/lambda_edge/versions.tf b/aws/modules/frontend/lambda_edge/versions.tf new file mode 100644 index 0000000..847f2ab --- /dev/null +++ b/aws/modules/frontend/lambda_edge/versions.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 1.5" + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } +} diff --git a/aws/modules/frontend/s3_assets/locals.tf b/aws/modules/frontend/s3_assets/locals.tf new file mode 100644 index 0000000..d6872a1 --- /dev/null +++ b/aws/modules/frontend/s3_assets/locals.tf @@ -0,0 +1,21 @@ +data "aws_region" "current" {} +data "aws_caller_identity" "current" {} + +locals { + bucket_name = "${var.bucket_name}-${data.aws_region.current.name}-${data.aws_caller_identity.current.account_id}" + + assets = fileset(var.path, "**/*") + + assets_with_content_type = { + for asset in local.assets : + asset => lookup(var.content_types, ".${split(".", asset)[length(split(".", asset)) - 1]}", "application/octet-stream") + } + + // Top level files and folders (e.g. index.html, images/*) + top_level_assets = toset([ + for asset in local.assets : + length(split("/", asset)) == 1 ? + "/${split("/", asset)[0]}" : + "/${split("/", asset)[0]}/*" + ]) +} diff --git a/aws/modules/frontend/s3_assets/main.tf b/aws/modules/frontend/s3_assets/main.tf new file mode 100644 index 0000000..8f0ca81 --- /dev/null +++ b/aws/modules/frontend/s3_assets/main.tf @@ -0,0 +1,29 @@ +resource "aws_s3_bucket" "assets" { + bucket = local.bucket_name +} + +# Ownership +resource "aws_s3_bucket_ownership_controls" "assets" { + bucket = aws_s3_bucket.assets.id + + rule { + object_ownership = "BucketOwnerPreferred" + } +} + +# Visibility +resource "aws_s3_bucket_acl" "assets" { + bucket = aws_s3_bucket.assets.id + acl = "private" + + depends_on = [aws_s3_bucket_ownership_controls.assets] +} + +# Versioning +resource "aws_s3_bucket_versioning" "assets" { + bucket = aws_s3_bucket.assets.id + + versioning_configuration { + status = "Enabled" + } +} diff --git a/aws/modules/frontend/s3_assets/oai.tf b/aws/modules/frontend/s3_assets/oai.tf new file mode 100644 index 0000000..72366fb --- /dev/null +++ b/aws/modules/frontend/s3_assets/oai.tf @@ -0,0 +1,21 @@ +resource "aws_cloudfront_origin_access_identity" "oai" { + comment = "OAI for accessing S3 bucket" +} + +resource "aws_s3_bucket_policy" "bucket_policy" { + bucket = aws_s3_bucket.assets.id + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = "s3:GetObject" + Effect = "Allow" + Resource = "${aws_s3_bucket.assets.arn}/*" + Principal = { + AWS = "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity ${aws_cloudfront_origin_access_identity.oai.id}" + } + }, + ] + }) +} diff --git a/aws/modules/frontend/s3_assets/outputs.tf b/aws/modules/frontend/s3_assets/outputs.tf new file mode 100644 index 0000000..16955ae --- /dev/null +++ b/aws/modules/frontend/s3_assets/outputs.tf @@ -0,0 +1,15 @@ +output "bucket_domain_name" { + value = aws_s3_bucket.assets.bucket_domain_name +} + +output "assets" { + value = local.assets +} + +output "top_level_assets" { + value = local.top_level_assets +} + +output "oai" { + value = aws_cloudfront_origin_access_identity.oai.id +} diff --git a/aws/modules/frontend/s3_assets/upload.tf b/aws/modules/frontend/s3_assets/upload.tf new file mode 100644 index 0000000..f4a2a63 --- /dev/null +++ b/aws/modules/frontend/s3_assets/upload.tf @@ -0,0 +1,10 @@ +// Upload new file +resource "aws_s3_object" "assets" { + for_each = local.assets_with_content_type + + key = each.key + bucket = aws_s3_bucket.assets.id + source = "${var.path}/${each.key}" + source_hash = filebase64sha256("${var.path}/${each.key}") + content_type = each.value +} diff --git a/aws/modules/frontend/s3_assets/variables.tf b/aws/modules/frontend/s3_assets/variables.tf new file mode 100644 index 0000000..017917f --- /dev/null +++ b/aws/modules/frontend/s3_assets/variables.tf @@ -0,0 +1,33 @@ +// this will get appended to region name +variable "bucket_name" { + type = string +} + +variable "path" { + type = string + default = "tmp/s3" +} + +variable "content_types" { + type = map(string) + default = { + ".html" = "text/html" + ".css" = "text/css" + ".js" = "application/javascript" + ".json" = "application/json" + ".png" = "image/png" + ".jpg" = "image/jpeg" + ".jpeg" = "image/jpeg" + ".gif" = "image/gif" + ".svg" = "image/svg+xml" + ".ico" = "image/x-icon" + ".woff" = "font/woff" + ".woff2" = "font/woff2" + ".ttf" = "font/ttf" + ".otf" = "font/otf" + ".eot" = "application/vnd.ms-fontobject" + ".xml" = "application/xml" + ".txt" = "text/plain" + ".webmanifest" = "application/manifest+json" + } +} diff --git a/aws/modules/frontend/s3_assets/versions.tf b/aws/modules/frontend/s3_assets/versions.tf new file mode 100644 index 0000000..847f2ab --- /dev/null +++ b/aws/modules/frontend/s3_assets/versions.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 1.5" + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } +} diff --git a/aws/modules/backend/acm/main.tf b/aws/modules/universal/acm/main.tf similarity index 100% rename from aws/modules/backend/acm/main.tf rename to aws/modules/universal/acm/main.tf diff --git a/aws/modules/backend/acm/outputs.tf b/aws/modules/universal/acm/outputs.tf similarity index 100% rename from aws/modules/backend/acm/outputs.tf rename to aws/modules/universal/acm/outputs.tf diff --git a/aws/modules/backend/acm/variables.tf b/aws/modules/universal/acm/variables.tf similarity index 100% rename from aws/modules/backend/acm/variables.tf rename to aws/modules/universal/acm/variables.tf diff --git a/aws/modules/universal/acm/versions.tf b/aws/modules/universal/acm/versions.tf new file mode 100644 index 0000000..847f2ab --- /dev/null +++ b/aws/modules/universal/acm/versions.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 1.5" + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } +} diff --git a/aws/modules/universal/lambda_iam/locals.tf b/aws/modules/universal/lambda_iam/locals.tf new file mode 100644 index 0000000..a30d9b8 --- /dev/null +++ b/aws/modules/universal/lambda_iam/locals.tf @@ -0,0 +1,4 @@ +locals { + default_identifiers = toset(var.edge ? ["lambda.amazonaws.com", "edgelambda.amazonaws.com"] : ["lambda.amazonaws.com"]) + identifiers = toset(setunion(var.identifiers, local.default_identifiers)) +} diff --git a/aws/modules/backend/lambda_iam/main.tf b/aws/modules/universal/lambda_iam/main.tf similarity index 83% rename from aws/modules/backend/lambda_iam/main.tf rename to aws/modules/universal/lambda_iam/main.tf index 9a07a25..f6215ab 100644 --- a/aws/modules/backend/lambda_iam/main.tf +++ b/aws/modules/universal/lambda_iam/main.tf @@ -5,7 +5,7 @@ data "aws_iam_policy_document" "assume_role" { principals { type = "Service" - identifiers = ["lambda.amazonaws.com"] + identifiers = local.identifiers } actions = ["sts:AssumeRole"] @@ -13,7 +13,7 @@ data "aws_iam_policy_document" "assume_role" { } resource "aws_iam_role" "aws_iam_role_exec_lambda" { - name = "aws-iam-role-exec-lambda" + name = var.role_name assume_role_policy = data.aws_iam_policy_document.assume_role.json } @@ -33,9 +33,9 @@ data "aws_iam_policy_document" "lambda_logging" { } resource "aws_iam_policy" "lambda_logging" { - name = "lambda_logging" + name = var.policy_name path = "/" - description = "IAM policy for logging from a lambda" + description = "IAM policy for logging from a lambda@edge" policy = data.aws_iam_policy_document.lambda_logging.json } diff --git a/aws/modules/backend/lambda_iam/outputs.tf b/aws/modules/universal/lambda_iam/outputs.tf similarity index 100% rename from aws/modules/backend/lambda_iam/outputs.tf rename to aws/modules/universal/lambda_iam/outputs.tf diff --git a/aws/modules/universal/lambda_iam/variables.tf b/aws/modules/universal/lambda_iam/variables.tf new file mode 100644 index 0000000..5322c1f --- /dev/null +++ b/aws/modules/universal/lambda_iam/variables.tf @@ -0,0 +1,17 @@ +variable "role_name" { + type = string +} + +variable "policy_name" { + type = string +} + +variable "identifiers" { + type = set(string) + default = [] +} + +variable "edge" { + type = bool + default = false +} diff --git a/aws/modules/universal/lambda_iam/versions.tf b/aws/modules/universal/lambda_iam/versions.tf new file mode 100644 index 0000000..847f2ab --- /dev/null +++ b/aws/modules/universal/lambda_iam/versions.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 1.5" + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } +} diff --git a/aws/modules/universal/s3_source_code/main.tf b/aws/modules/universal/s3_source_code/main.tf new file mode 100644 index 0000000..d2c1c6c --- /dev/null +++ b/aws/modules/universal/s3_source_code/main.tf @@ -0,0 +1,32 @@ +data "aws_region" "current" {} +data "aws_caller_identity" "current" {} + +resource "aws_s3_bucket" "source_code" { + bucket = "${var.bucket_name}-${data.aws_region.current.name}-${data.aws_caller_identity.current.account_id}" +} + +# Ownership +resource "aws_s3_bucket_ownership_controls" "source_code" { + bucket = aws_s3_bucket.source_code.id + + rule { + object_ownership = "BucketOwnerPreferred" + } +} + +# Visibility +resource "aws_s3_bucket_acl" "source_code" { + bucket = aws_s3_bucket.source_code.id + acl = "private" + + depends_on = [aws_s3_bucket_ownership_controls.source_code] +} + +# Versioning +resource "aws_s3_bucket_versioning" "source_code" { + bucket = aws_s3_bucket.source_code.id + + versioning_configuration { + status = "Enabled" + } +} diff --git a/aws/modules/universal/s3_source_code/outputs.tf b/aws/modules/universal/s3_source_code/outputs.tf new file mode 100644 index 0000000..e7ad673 --- /dev/null +++ b/aws/modules/universal/s3_source_code/outputs.tf @@ -0,0 +1,15 @@ +output "bucket_id" { + value = aws_s3_bucket.source_code.id +} + +output "bucket_arn" { + value = aws_s3_bucket.source_code.arn +} + +output "s3_key" { + value = aws_s3_object.source_code.key +} + +output "source_code_hash" { + value = aws_s3_object.source_code.source_hash +} diff --git a/aws/modules/universal/s3_source_code/upload.tf b/aws/modules/universal/s3_source_code/upload.tf new file mode 100644 index 0000000..546f7c9 --- /dev/null +++ b/aws/modules/universal/s3_source_code/upload.tf @@ -0,0 +1,17 @@ +// Upload new file +data "archive_file" "source_code" { + type = "zip" + source_file = "${var.path}/${var.filename}" + output_path = "${var.path}/${var.filename}.zip" +} + +resource "aws_s3_object" "source_code" { + key = "${var.filename}.zip" + bucket = aws_s3_bucket.source_code.id + source = data.archive_file.source_code.output_path + source_hash = data.archive_file.source_code.output_base64sha256 + + lifecycle { + create_before_destroy = true + } +} diff --git a/aws/modules/universal/s3_source_code/variables.tf b/aws/modules/universal/s3_source_code/variables.tf new file mode 100644 index 0000000..db55d86 --- /dev/null +++ b/aws/modules/universal/s3_source_code/variables.tf @@ -0,0 +1,18 @@ +// this will get appended to region name +variable "bucket_name" { + type = string +} + +variable "path" { + type = string + default = "tmp" +} + +variable "filename" { + type = string + + validation { + condition = var.filename == "bootstrap" || var.filename == "index.mjs" + error_message = "Only 'bootstrap' or 'index.mjs' are allowed" + } +} diff --git a/aws/modules/backend/s3/versions.tf b/aws/modules/universal/s3_source_code/versions.tf similarity index 100% rename from aws/modules/backend/s3/versions.tf rename to aws/modules/universal/s3_source_code/versions.tf diff --git a/aws/prod/backend/locals.tf b/aws/prod/backend/locals.tf index f495906..2ff5400 100644 --- a/aws/prod/backend/locals.tf +++ b/aws/prod/backend/locals.tf @@ -13,5 +13,6 @@ locals { routes = toset([ "/search", "/proxy", + "/healthz" ]) } diff --git a/aws/prod/backend/main.tf b/aws/prod/backend/main.tf index e259513..701453a 100644 --- a/aws/prod/backend/main.tf +++ b/aws/prod/backend/main.tf @@ -29,7 +29,10 @@ module "salt" { ### IAM for lambda execution and logging module "lambda_iam" { - source = "../../modules/backend/lambda_iam" + source = "../../modules/universal/lambda_iam" + + role_name = "aws-iam-role-exec-hearchco-api" + policy_name = "hearchco_api_logging" } ## Cloudfront @@ -42,7 +45,7 @@ provider "aws" { ### Certificate for the Cloudfront distribution module "hearchco_cdn_certificate" { - source = "../../modules/backend/acm" + source = "../../modules/universal/acm" domain_name = local.api_domain_name hosted_zone_id = data.aws_route53_zone.hearchco_route53.zone_id @@ -62,7 +65,7 @@ module "hearchco_cloudfront" { paths_cache = { "/search" = { - min_ttl = 600 // 10 minutes + min_ttl = 3600 // 1 hour default_ttl = 86400 // 1 day max_ttl = 259200 // 3 days }, @@ -71,6 +74,11 @@ module "hearchco_cloudfront" { default_ttl = 1296000 // 15 days max_ttl = 2592000 // 30 days }, + "/healthz" = { + min_ttl = 0 + default_ttl = 0 + max_ttl = 5 + }, } providers = { diff --git a/aws/prod/backend/region_af_south_1.tf b/aws/prod/backend/region_af_south_1.tf index d31cd69..197e7e8 100644 --- a/aws/prod/backend/region_af_south_1.tf +++ b/aws/prod/backend/region_af_south_1.tf @@ -9,7 +9,9 @@ provider "aws" { } module "hearchco_s3_af_south_1" { - source = "../../modules/backend/s3" + source = "../../modules/universal/s3_source_code" + filename = "bootstrap" + bucket_name = "hearchco-api-binary" providers = { aws = aws.af-south-1 @@ -31,7 +33,7 @@ module "hearchco_lambda_af_south_1" { } module "hearchco_certificate_af_south_1" { - source = "../../modules/backend/acm" + source = "../../modules/universal/acm" domain_name = local.api_gateway_domain_name hosted_zone_id = data.aws_route53_zone.hearchco_route53.zone_id diff --git a/aws/prod/backend/region_ap_east_1.tf b/aws/prod/backend/region_ap_east_1.tf index 9985366..d41e4ad 100644 --- a/aws/prod/backend/region_ap_east_1.tf +++ b/aws/prod/backend/region_ap_east_1.tf @@ -9,7 +9,9 @@ provider "aws" { } module "hearchco_s3_ap_east_1" { - source = "../../modules/backend/s3" + source = "../../modules/universal/s3_source_code" + filename = "bootstrap" + bucket_name = "hearchco-api-binary" providers = { aws = aws.ap-east-1 @@ -31,7 +33,7 @@ module "hearchco_lambda_ap_east_1" { } module "hearchco_certificate_ap_east_1" { - source = "../../modules/backend/acm" + source = "../../modules/universal/acm" domain_name = local.api_gateway_domain_name hosted_zone_id = data.aws_route53_zone.hearchco_route53.zone_id diff --git a/aws/prod/backend/region_ap_northeast_1.tf b/aws/prod/backend/region_ap_northeast_1.tf index 6e8ee3f..94b9a56 100644 --- a/aws/prod/backend/region_ap_northeast_1.tf +++ b/aws/prod/backend/region_ap_northeast_1.tf @@ -9,7 +9,9 @@ provider "aws" { } module "hearchco_s3_ap_northeast_1" { - source = "../../modules/backend/s3" + source = "../../modules/universal/s3_source_code" + filename = "bootstrap" + bucket_name = "hearchco-api-binary" providers = { aws = aws.ap-northeast-1 @@ -31,7 +33,7 @@ module "hearchco_lambda_ap_northeast_1" { } module "hearchco_certificate_ap_northeast_1" { - source = "../../modules/backend/acm" + source = "../../modules/universal/acm" domain_name = local.api_gateway_domain_name hosted_zone_id = data.aws_route53_zone.hearchco_route53.zone_id diff --git a/aws/prod/backend/region_ap_northeast_2.tf b/aws/prod/backend/region_ap_northeast_2.tf index 1fed28a..c544502 100644 --- a/aws/prod/backend/region_ap_northeast_2.tf +++ b/aws/prod/backend/region_ap_northeast_2.tf @@ -9,7 +9,9 @@ provider "aws" { } module "hearchco_s3_ap_northeast_2" { - source = "../../modules/backend/s3" + source = "../../modules/universal/s3_source_code" + filename = "bootstrap" + bucket_name = "hearchco-api-binary" providers = { aws = aws.ap-northeast-2 @@ -31,7 +33,7 @@ module "hearchco_lambda_ap_northeast_2" { } module "hearchco_certificate_ap_northeast_2" { - source = "../../modules/backend/acm" + source = "../../modules/universal/acm" domain_name = local.api_gateway_domain_name hosted_zone_id = data.aws_route53_zone.hearchco_route53.zone_id diff --git a/aws/prod/backend/region_ap_south_1.tf b/aws/prod/backend/region_ap_south_1.tf index a992743..caa5398 100644 --- a/aws/prod/backend/region_ap_south_1.tf +++ b/aws/prod/backend/region_ap_south_1.tf @@ -9,7 +9,9 @@ provider "aws" { } module "hearchco_s3_ap_south_1" { - source = "../../modules/backend/s3" + source = "../../modules/universal/s3_source_code" + filename = "bootstrap" + bucket_name = "hearchco-api-binary" providers = { aws = aws.ap-south-1 @@ -31,7 +33,7 @@ module "hearchco_lambda_ap_south_1" { } module "hearchco_certificate_ap_south_1" { - source = "../../modules/backend/acm" + source = "../../modules/universal/acm" domain_name = local.api_gateway_domain_name hosted_zone_id = data.aws_route53_zone.hearchco_route53.zone_id diff --git a/aws/prod/backend/region_ap_southeast_1.tf b/aws/prod/backend/region_ap_southeast_1.tf index 6ac665f..54bbdb8 100644 --- a/aws/prod/backend/region_ap_southeast_1.tf +++ b/aws/prod/backend/region_ap_southeast_1.tf @@ -9,7 +9,9 @@ provider "aws" { } module "hearchco_s3_ap_southeast_1" { - source = "../../modules/backend/s3" + source = "../../modules/universal/s3_source_code" + filename = "bootstrap" + bucket_name = "hearchco-api-binary" providers = { aws = aws.ap-southeast-1 @@ -31,7 +33,7 @@ module "hearchco_lambda_ap_southeast_1" { } module "hearchco_certificate_ap_southeast_1" { - source = "../../modules/backend/acm" + source = "../../modules/universal/acm" domain_name = local.api_gateway_domain_name hosted_zone_id = data.aws_route53_zone.hearchco_route53.zone_id diff --git a/aws/prod/backend/region_ap_southeast_2.tf b/aws/prod/backend/region_ap_southeast_2.tf index 7e8356d..02ab14a 100644 --- a/aws/prod/backend/region_ap_southeast_2.tf +++ b/aws/prod/backend/region_ap_southeast_2.tf @@ -9,7 +9,9 @@ provider "aws" { } module "hearchco_s3_ap_southeast_2" { - source = "../../modules/backend/s3" + source = "../../modules/universal/s3_source_code" + filename = "bootstrap" + bucket_name = "hearchco-api-binary" providers = { aws = aws.ap-southeast-2 @@ -31,7 +33,7 @@ module "hearchco_lambda_ap_southeast_2" { } module "hearchco_certificate_ap_southeast_2" { - source = "../../modules/backend/acm" + source = "../../modules/universal/acm" domain_name = local.api_gateway_domain_name hosted_zone_id = data.aws_route53_zone.hearchco_route53.zone_id diff --git a/aws/prod/backend/region_ca_central_1.tf b/aws/prod/backend/region_ca_central_1.tf index bff261d..b9cc511 100644 --- a/aws/prod/backend/region_ca_central_1.tf +++ b/aws/prod/backend/region_ca_central_1.tf @@ -9,7 +9,9 @@ provider "aws" { } module "hearchco_s3_ca_central_1" { - source = "../../modules/backend/s3" + source = "../../modules/universal/s3_source_code" + filename = "bootstrap" + bucket_name = "hearchco-api-binary" providers = { aws = aws.ca-central-1 @@ -31,7 +33,7 @@ module "hearchco_lambda_ca_central_1" { } module "hearchco_certificate_ca_central_1" { - source = "../../modules/backend/acm" + source = "../../modules/universal/acm" domain_name = local.api_gateway_domain_name hosted_zone_id = data.aws_route53_zone.hearchco_route53.zone_id diff --git a/aws/prod/backend/region_eu_central_1.tf b/aws/prod/backend/region_eu_central_1.tf index ecfa9ad..7b8bd56 100644 --- a/aws/prod/backend/region_eu_central_1.tf +++ b/aws/prod/backend/region_eu_central_1.tf @@ -9,7 +9,9 @@ provider "aws" { } module "hearchco_s3_eu_central_1" { - source = "../../modules/backend/s3" + source = "../../modules/universal/s3_source_code" + filename = "bootstrap" + bucket_name = "hearchco-api-binary" providers = { aws = aws.eu-central-1 @@ -31,7 +33,7 @@ module "hearchco_lambda_eu_central_1" { } module "hearchco_certificate_eu_central_1" { - source = "../../modules/backend/acm" + source = "../../modules/universal/acm" domain_name = local.api_gateway_domain_name hosted_zone_id = data.aws_route53_zone.hearchco_route53.zone_id diff --git a/aws/prod/backend/region_eu_central_2.tf b/aws/prod/backend/region_eu_central_2.tf index b351aa9..e32f4aa 100644 --- a/aws/prod/backend/region_eu_central_2.tf +++ b/aws/prod/backend/region_eu_central_2.tf @@ -11,7 +11,9 @@ provider "aws" { # CURRENTLY THIS REGION DOESN'T SUPPORT HTTP API GATEWAY # module "hearchco_s3_eu_central_2" { -# source = "../../modules/backend/s3" +# source = "../../modules/universal/s3_source_code" +# filename = "bootstrap" +# bucket_name = "hearchco-api-binary" # providers = { # aws = aws.eu-central-2 @@ -33,7 +35,7 @@ provider "aws" { # } # module "hearchco_certificate_eu_central_2" { -# source = "../../modules/backend/acm" +# source = "../../modules/universal/acm" # domain_name = local.api_gateway_domain_name # hosted_zone_id = data.aws_route53_zone.hearchco_route53.zone_id diff --git a/aws/prod/backend/region_eu_north_1.tf b/aws/prod/backend/region_eu_north_1.tf index 014370e..98c5ff4 100644 --- a/aws/prod/backend/region_eu_north_1.tf +++ b/aws/prod/backend/region_eu_north_1.tf @@ -9,7 +9,9 @@ provider "aws" { } module "hearchco_s3_eu_north_1" { - source = "../../modules/backend/s3" + source = "../../modules/universal/s3_source_code" + filename = "bootstrap" + bucket_name = "hearchco-api-binary" providers = { aws = aws.eu-north-1 @@ -31,7 +33,7 @@ module "hearchco_lambda_eu_north_1" { } module "hearchco_certificate_eu_north_1" { - source = "../../modules/backend/acm" + source = "../../modules/universal/acm" domain_name = local.api_gateway_domain_name hosted_zone_id = data.aws_route53_zone.hearchco_route53.zone_id diff --git a/aws/prod/backend/region_eu_south_1.tf b/aws/prod/backend/region_eu_south_1.tf index beae646..6e16a59 100644 --- a/aws/prod/backend/region_eu_south_1.tf +++ b/aws/prod/backend/region_eu_south_1.tf @@ -9,7 +9,9 @@ provider "aws" { } module "hearchco_s3_eu_south_1" { - source = "../../modules/backend/s3" + source = "../../modules/universal/s3_source_code" + filename = "bootstrap" + bucket_name = "hearchco-api-binary" providers = { aws = aws.eu-south-1 @@ -31,7 +33,7 @@ module "hearchco_lambda_eu_south_1" { } module "hearchco_certificate_eu_south_1" { - source = "../../modules/backend/acm" + source = "../../modules/universal/acm" domain_name = local.api_gateway_domain_name hosted_zone_id = data.aws_route53_zone.hearchco_route53.zone_id diff --git a/aws/prod/backend/region_eu_south_2.tf b/aws/prod/backend/region_eu_south_2.tf index 136213e..5c62b8a 100644 --- a/aws/prod/backend/region_eu_south_2.tf +++ b/aws/prod/backend/region_eu_south_2.tf @@ -11,7 +11,9 @@ provider "aws" { # CURRENTLY THIS REGION DOESN'T SUPPORT HTTP API GATEWAY # module "hearchco_s3_eu_south_2" { -# source = "../../modules/backend/s3" +# source = "../../modules/universal/s3_source_code" +# filename = "bootstrap" +# bucket_name = "hearchco-api-binary" # providers = { # aws = aws.eu-south-2 @@ -33,7 +35,7 @@ provider "aws" { # } # module "hearchco_certificate_eu_south_2" { -# source = "../../modules/backend/acm" +# source = "../../modules/universal/acm" # domain_name = local.api_gateway_domain_name # hosted_zone_id = data.aws_route53_zone.hearchco_route53.zone_id diff --git a/aws/prod/backend/region_eu_west_1.tf b/aws/prod/backend/region_eu_west_1.tf index 3b1b96b..8129e63 100644 --- a/aws/prod/backend/region_eu_west_1.tf +++ b/aws/prod/backend/region_eu_west_1.tf @@ -9,7 +9,9 @@ provider "aws" { } module "hearchco_s3_eu_west_1" { - source = "../../modules/backend/s3" + source = "../../modules/universal/s3_source_code" + filename = "bootstrap" + bucket_name = "hearchco-api-binary" providers = { aws = aws.eu-west-1 @@ -31,7 +33,7 @@ module "hearchco_lambda_eu_west_1" { } module "hearchco_certificate_eu_west_1" { - source = "../../modules/backend/acm" + source = "../../modules/universal/acm" domain_name = local.api_gateway_domain_name hosted_zone_id = data.aws_route53_zone.hearchco_route53.zone_id diff --git a/aws/prod/backend/region_eu_west_2.tf b/aws/prod/backend/region_eu_west_2.tf index 8943093..f04bb46 100644 --- a/aws/prod/backend/region_eu_west_2.tf +++ b/aws/prod/backend/region_eu_west_2.tf @@ -9,7 +9,9 @@ provider "aws" { } module "hearchco_s3_eu_west_2" { - source = "../../modules/backend/s3" + source = "../../modules/universal/s3_source_code" + filename = "bootstrap" + bucket_name = "hearchco-api-binary" providers = { aws = aws.eu-west-2 @@ -31,7 +33,7 @@ module "hearchco_lambda_eu_west_2" { } module "hearchco_certificate_eu_west_2" { - source = "../../modules/backend/acm" + source = "../../modules/universal/acm" domain_name = local.api_gateway_domain_name hosted_zone_id = data.aws_route53_zone.hearchco_route53.zone_id diff --git a/aws/prod/backend/region_eu_west_3.tf b/aws/prod/backend/region_eu_west_3.tf index 0d0b742..a2c992d 100644 --- a/aws/prod/backend/region_eu_west_3.tf +++ b/aws/prod/backend/region_eu_west_3.tf @@ -9,7 +9,9 @@ provider "aws" { } module "hearchco_s3_eu_west_3" { - source = "../../modules/backend/s3" + source = "../../modules/universal/s3_source_code" + filename = "bootstrap" + bucket_name = "hearchco-api-binary" providers = { aws = aws.eu-west-3 @@ -31,7 +33,7 @@ module "hearchco_lambda_eu_west_3" { } module "hearchco_certificate_eu_west_3" { - source = "../../modules/backend/acm" + source = "../../modules/universal/acm" domain_name = local.api_gateway_domain_name hosted_zone_id = data.aws_route53_zone.hearchco_route53.zone_id diff --git a/aws/prod/backend/region_me_south_1.tf b/aws/prod/backend/region_me_south_1.tf index 84e0cb2..46d771c 100644 --- a/aws/prod/backend/region_me_south_1.tf +++ b/aws/prod/backend/region_me_south_1.tf @@ -9,7 +9,9 @@ provider "aws" { } module "hearchco_s3_me_south_1" { - source = "../../modules/backend/s3" + source = "../../modules/universal/s3_source_code" + filename = "bootstrap" + bucket_name = "hearchco-api-binary" providers = { aws = aws.me-south-1 @@ -31,7 +33,7 @@ module "hearchco_lambda_me_south_1" { } module "hearchco_certificate_me_south_1" { - source = "../../modules/backend/acm" + source = "../../modules/universal/acm" domain_name = local.api_gateway_domain_name hosted_zone_id = data.aws_route53_zone.hearchco_route53.zone_id diff --git a/aws/prod/backend/region_sa_east_1.tf b/aws/prod/backend/region_sa_east_1.tf index 9208928..eb558ca 100644 --- a/aws/prod/backend/region_sa_east_1.tf +++ b/aws/prod/backend/region_sa_east_1.tf @@ -9,7 +9,9 @@ provider "aws" { } module "hearchco_s3_sa_east_1" { - source = "../../modules/backend/s3" + source = "../../modules/universal/s3_source_code" + filename = "bootstrap" + bucket_name = "hearchco-api-binary" providers = { aws = aws.sa-east-1 @@ -31,7 +33,7 @@ module "hearchco_lambda_sa_east_1" { } module "hearchco_certificate_sa_east_1" { - source = "../../modules/backend/acm" + source = "../../modules/universal/acm" domain_name = local.api_gateway_domain_name hosted_zone_id = data.aws_route53_zone.hearchco_route53.zone_id diff --git a/aws/prod/backend/region_us_east_1.tf b/aws/prod/backend/region_us_east_1.tf index 3398fe6..3421f7f 100644 --- a/aws/prod/backend/region_us_east_1.tf +++ b/aws/prod/backend/region_us_east_1.tf @@ -9,7 +9,9 @@ provider "aws" { } module "hearchco_s3_us_east_1" { - source = "../../modules/backend/s3" + source = "../../modules/universal/s3_source_code" + filename = "bootstrap" + bucket_name = "hearchco-api-binary" providers = { aws = aws.us-east-1 @@ -31,7 +33,7 @@ module "hearchco_lambda_us_east_1" { } module "hearchco_certificate_us_east_1" { - source = "../../modules/backend/acm" + source = "../../modules/universal/acm" domain_name = local.api_gateway_domain_name hosted_zone_id = data.aws_route53_zone.hearchco_route53.zone_id diff --git a/aws/prod/backend/region_us_east_2.tf b/aws/prod/backend/region_us_east_2.tf index d1cd650..fa87ae6 100644 --- a/aws/prod/backend/region_us_east_2.tf +++ b/aws/prod/backend/region_us_east_2.tf @@ -9,7 +9,9 @@ provider "aws" { } module "hearchco_s3_us_east_2" { - source = "../../modules/backend/s3" + source = "../../modules/universal/s3_source_code" + filename = "bootstrap" + bucket_name = "hearchco-api-binary" providers = { aws = aws.us-east-2 @@ -31,7 +33,7 @@ module "hearchco_lambda_us_east_2" { } module "hearchco_certificate_us_east_2" { - source = "../../modules/backend/acm" + source = "../../modules/universal/acm" domain_name = local.api_gateway_domain_name hosted_zone_id = data.aws_route53_zone.hearchco_route53.zone_id diff --git a/aws/prod/backend/region_us_west_1.tf b/aws/prod/backend/region_us_west_1.tf index df1f192..55cbd8f 100644 --- a/aws/prod/backend/region_us_west_1.tf +++ b/aws/prod/backend/region_us_west_1.tf @@ -9,7 +9,9 @@ provider "aws" { } module "hearchco_s3_us_west_1" { - source = "../../modules/backend/s3" + source = "../../modules/universal/s3_source_code" + filename = "bootstrap" + bucket_name = "hearchco-api-binary" providers = { aws = aws.us-west-1 @@ -31,7 +33,7 @@ module "hearchco_lambda_us_west_1" { } module "hearchco_certificate_us_west_1" { - source = "../../modules/backend/acm" + source = "../../modules/universal/acm" domain_name = local.api_gateway_domain_name hosted_zone_id = data.aws_route53_zone.hearchco_route53.zone_id diff --git a/aws/prod/backend/region_us_west_2.tf b/aws/prod/backend/region_us_west_2.tf index 8bce27e..b958fd6 100644 --- a/aws/prod/backend/region_us_west_2.tf +++ b/aws/prod/backend/region_us_west_2.tf @@ -9,7 +9,9 @@ provider "aws" { } module "hearchco_s3_us_west_2" { - source = "../../modules/backend/s3" + source = "../../modules/universal/s3_source_code" + filename = "bootstrap" + bucket_name = "hearchco-api-binary" providers = { aws = aws.us-west-2 @@ -31,7 +33,7 @@ module "hearchco_lambda_us_west_2" { } module "hearchco_certificate_us_west_2" { - source = "../../modules/backend/acm" + source = "../../modules/universal/acm" domain_name = local.api_gateway_domain_name hosted_zone_id = data.aws_route53_zone.hearchco_route53.zone_id diff --git a/aws/prod/dns/main.tf b/aws/prod/dns/main.tf index 6ff8925..1174578 100644 --- a/aws/prod/dns/main.tf +++ b/aws/prod/dns/main.tf @@ -19,13 +19,6 @@ module "hearchco_route53" { domain_name = var.domain_name additional_records = [ - // vercel - { - name = "", - type = "A", - ttl = 3600, - records = ["76.76.21.21"] - }, // email { name = "", diff --git a/aws/prod/frontend/cloudfront.tf b/aws/prod/frontend/cloudfront.tf new file mode 100644 index 0000000..cc37a5f --- /dev/null +++ b/aws/prod/frontend/cloudfront.tf @@ -0,0 +1,39 @@ +module "hearchco_cdn_certificate" { + source = "../../modules/universal/acm" + domain_name = var.domain_name + hosted_zone_id = data.aws_route53_zone.hearchco_route53.zone_id + + providers = { + aws = aws.us-east-1-cdn + } +} + +module "hearchco_cloudfront" { + source = "../../modules/frontend/cloudfront" + domain_name = var.domain_name + hosted_zone_id = data.aws_route53_zone.hearchco_route53.zone_id + target_domain_name = module.hearchco_s3_assets.bucket_domain_name + oai_id = module.hearchco_s3_assets.oai + lambda_edge_arn = module.hearchco_lambda_edge.invoke_arn + acm_certificate_arn = module.hearchco_cdn_certificate.cert_arn + price_class = "PriceClass_All" + + top_level_assets = module.hearchco_s3_assets.top_level_assets + + paths_cache = { + "/search" = { + min_ttl = 3600 // 1 hour + default_ttl = 86400 // 1 day + max_ttl = 259200 // 3 days + }, + "/healthz" = { + min_ttl = 0 + default_ttl = 0 + max_ttl = 5 + }, + } + + providers = { + aws = aws.us-east-1-cdn + } +} diff --git a/aws/prod/frontend/lambda.tf b/aws/prod/frontend/lambda.tf new file mode 100644 index 0000000..3b2110e --- /dev/null +++ b/aws/prod/frontend/lambda.tf @@ -0,0 +1,41 @@ +module "hearchco_env_injection" { + source = "../../modules/frontend/env_injection" + environment = local.environment +} + +module "hearchco_s3_source_code" { + source = "../../modules/universal/s3_source_code" + bucket_name = "hearchco-ssr-function" + filename = module.hearchco_env_injection.filename + path = module.hearchco_env_injection.path + + depends_on = [module.hearchco_env_injection] + + providers = { + aws = aws.us-east-1-cdn + } +} + +module "lambda_iam" { + source = "../../modules/universal/lambda_iam" + + role_name = "aws-iam-role-exec-hearchco-ssr" + policy_name = "hearchco_ssr_logging" + edge = true +} + +### Lambda@Edge for SSR (us-east-1 for Cloudfront) +module "hearchco_lambda_edge" { + source = "../../modules/frontend/lambda_edge" + role = module.lambda_iam.role_arn + s3_bucket = module.hearchco_s3_source_code.bucket_id + s3_key = module.hearchco_s3_source_code.s3_key + source_code_hash = module.hearchco_s3_source_code.source_code_hash + # environment = local.environment # Not supported by Lambda@Edge + + depends_on = [module.hearchco_s3_source_code] + + providers = { + aws = aws.us-east-1-cdn + } +} diff --git a/aws/prod/frontend/locals.tf b/aws/prod/frontend/locals.tf new file mode 100644 index 0000000..34492ee --- /dev/null +++ b/aws/prod/frontend/locals.tf @@ -0,0 +1,9 @@ +locals { + api_domain_name = "api.${var.domain_name}" + + # Lambda + environment = tomap({ + API_URI = "https://${local.api_domain_name}" + PUBLIC_API_URI = "https://${local.api_domain_name}" + }) +} diff --git a/aws/prod/frontend/main.tf b/aws/prod/frontend/main.tf new file mode 100644 index 0000000..0cab6dc --- /dev/null +++ b/aws/prod/frontend/main.tf @@ -0,0 +1,27 @@ +terraform { + backend "s3" { + profile = "992382822186_TFStateLock" + region = "eu-central-1" + dynamodb_table = "hearchco-shared-tf-state" + bucket = "hearchco-shared-tf-state" + key = "aws/prod/frontend/terraform.tfstate" + encrypt = "true" + } +} + +provider "aws" { + profile = var.aws_profile + region = "eu-central-1" +} + +# us-east-1 region required for Cloudfront's certificate and Lambda@Edge +provider "aws" { + profile = var.aws_profile + region = "us-east-1" + alias = "us-east-1-cdn" +} + +# Route53 DNS +data "aws_route53_zone" "hearchco_route53" { + name = var.domain_name +} diff --git a/aws/prod/frontend/s3.tf b/aws/prod/frontend/s3.tf new file mode 100644 index 0000000..6a679ab --- /dev/null +++ b/aws/prod/frontend/s3.tf @@ -0,0 +1,8 @@ +module "hearchco_s3_assets" { + source = "../../modules/frontend/s3_assets" + bucket_name = "hearchco-assets" + + providers = { + aws = aws.us-east-1-cdn + } +} diff --git a/aws/prod/frontend/variables.tf b/aws/prod/frontend/variables.tf new file mode 100644 index 0000000..d6cef3a --- /dev/null +++ b/aws/prod/frontend/variables.tf @@ -0,0 +1,9 @@ +variable "aws_profile" { + type = string + default = "730335356331_Admin" +} + +variable "domain_name" { + type = string + default = "hearch.co" +} diff --git a/aws/prod/frontend/versions.tf b/aws/prod/frontend/versions.tf new file mode 100644 index 0000000..847f2ab --- /dev/null +++ b/aws/prod/frontend/versions.tf @@ -0,0 +1,9 @@ +terraform { + required_version = ">= 1.5" + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.0" + } + } +}