diff --git a/terragrunt/accounts/legacy/rustc-ci-prod/.terraform.lock.hcl b/terragrunt/accounts/legacy/rustc-ci-prod/.terraform.lock.hcl new file mode 100644 index 000000000..ad4000a1b --- /dev/null +++ b/terragrunt/accounts/legacy/rustc-ci-prod/.terraform.lock.hcl @@ -0,0 +1,48 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "5.94.1" + constraints = ">= 4.20.0, >= 5.64.0, ~> 5.86, < 6.0.0" + hashes = [ + "h1:dYdnGlaCJONFyGk/t3Y4iJzQ8EiJr2DaDdZ/2JV5PZU=", + "zh:14fb41e50219660d5f02b977e6f786d8ce78766cce8c2f6b8131411b087ae945", + "zh:3bc5d12acd5e1a5f1cf78a7f05d0d63f988b57485e7d20c47e80a0b723a99d26", + "zh:4835e49377f80a37c6191a092f636e227a9f086e3cc3f0c9e1b554da8793cfe8", + "zh:605971275adae25096dca30a94e29931039133c667c1d9b38778a09594312964", + "zh:8ae46b4a9a67815facf59da0c56d74ef71bcc77ae79e8bfbac504fa43f267f8e", + "zh:913f3f371c3e6d1f040d6284406204b049977c13cb75aae71edb0ef8361da7dd", + "zh:91f85ae8c73932547ad7139ce0b047a6a7c7be2fd944e51db13231cc80ce6d8e", + "zh:96352ae4323ce137903b9fe879941f894a3ce9ef30df1018a0f29f285a448793", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:9b51922c9201b1dc3d05b39f9972715db5f67297deee088793d02dea1832564b", + "zh:a689e82112aa71e15647b06502d5b585980cd9002c3cc8458f092e8c8a667696", + "zh:c3723fa3e6aff3c1cc0088bdcb1edee168fe60020f2f77161d135bf473f45ab2", + "zh:d6a2052b864dd394b01ad1bae32d0a7d257940ee47908d02df7fa7873981d619", + "zh:dda4c9c0406cc54ad8ee4f19173a32de7c6e73abb5a948ea0f342d567df26a1d", + "zh:f42e0fe592b97cbdf70612f0fbe2bab851835e2d1aaf8cbb87c3ab0f2c96bb27", + ] +} + +provider "registry.terraform.io/integrations/github" { + version = "6.6.0" + constraints = "~> 6.2" + hashes = [ + "h1:P4SRG4605PvPKASeDu1lW49TTz1cCGsjQ7qbOBgNd6I=", + "zh:0b1b5342db6a17de7c71386704e101be7d6761569e03fb3ff1f3d4c02c32d998", + "zh:2fb663467fff76852126b58315d9a1a457e3b04bec51f04bf1c0ddc9dfbb3517", + "zh:4183e557a1dfd413dae90ca4bac37dbbe499eae5e923567371f768053f977800", + "zh:48b2979f88fb55cdb14b7e4c37c44e0dfbc21b7a19686ce75e339efda773c5c2", + "zh:5d803fb06625e0bcf83abb590d4235c117fa7f4aa2168fa3d5f686c41bc529ec", + "zh:6f1dd094cbab36363583cda837d7ca470bef5f8abf9b19f23e9cd8b927153498", + "zh:772edb5890d72b32868f9fdc0a9a1d4f4701d8e7f8acb37a7ac530d053c776e3", + "zh:798f443dbba6610431dcef832047f6917fb5a4e184a3a776c44e6213fb429cc6", + "zh:cc08dfcc387e2603f6dbaff8c236c1254185450d6cadd6bad92879fe7e7dbce9", + "zh:d5e2c8d7f50f91d6847ddce27b10b721bdfce99c1bbab42a68fa271337d73d63", + "zh:e69a0045440c706f50f84a84ff8b1df520ec9bf757de4b8f9959f2ed20c3f440", + "zh:efc5358573a6403cbea3a08a2fcd2407258ac083d9134c641bdcb578966d8bdf", + "zh:f627a255e5809ec2375f79949c79417847fa56b9e9222ea7c45a463eb663f137", + "zh:f7c02f762e4cf1de7f58bde520798491ccdd54a5bd52278d579c146d1d07d4f0", + "zh:fbd1fee2c9df3aa19cf8851ce134dea6e45ea01cb85695c1726670c285797e25", + ] +} diff --git a/terragrunt/accounts/legacy/rustc-ci-prod/terragrunt.hcl b/terragrunt/accounts/legacy/rustc-ci-prod/terragrunt.hcl new file mode 100644 index 000000000..39e54c5d7 --- /dev/null +++ b/terragrunt/accounts/legacy/rustc-ci-prod/terragrunt.hcl @@ -0,0 +1,17 @@ +terraform { + source = "../../../modules//rustc-ci" +} + +include { + path = find_in_parent_folders() + merge_strategy = "deep" +} + +inputs = { + repo = "rust" + caches_bucket = "rust-lang-ci-sccache2" + artifacts_bucket = "rust-lang-ci2" + artifacts_domain = "ci-artifacts.rust-lang.org" + caches_domain = "ci-caches.rust-lang.org" + inventories_bucket = "rust-inventories" +} diff --git a/terragrunt/accounts/legacy/rustc-ci-staging/.terraform.lock.hcl b/terragrunt/accounts/legacy/rustc-ci-staging/.terraform.lock.hcl new file mode 100644 index 000000000..b075c0b3d --- /dev/null +++ b/terragrunt/accounts/legacy/rustc-ci-staging/.terraform.lock.hcl @@ -0,0 +1,48 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "5.98.0" + constraints = ">= 4.20.0, >= 5.64.0, ~> 5.86, < 6.0.0" + hashes = [ + "h1:neMFK/kP1KT6cTGID+Tkkt8L7PsN9XqwrPDGXVw3WVY=", + "zh:23377bd90204b6203b904f48f53edcae3294eb072d8fc18a4531c0cde531a3a1", + "zh:2e55a6ea14cc43b08cf82d43063e96c5c2f58ee953c2628523d0ee918fe3b609", + "zh:4885a817c16fdaaeddc5031edc9594c1f300db0e5b23be7cd76a473e7dcc7b4f", + "zh:6ca7177ad4e5c9d93dee4be1ac0792b37107df04657fddfe0c976f36abdd18b5", + "zh:78bf8eb0a67bae5dede09666676c7a38c9fb8d1b80a90ba06cf36ae268257d6f", + "zh:874b5a99457a3f88e2915df8773120846b63d820868a8f43082193f3dc84adcb", + "zh:95e1e4cf587cde4537ac9dfee9e94270652c812ab31fce3a431778c053abf354", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:a75145b58b241d64570803e6565c72467cd664633df32678755b51871f553e50", + "zh:aa31b13d0b0e8432940d6892a48b6268721fa54a02ed62ee42745186ee32f58d", + "zh:ae4565770f76672ce8e96528cbb66afdade1f91383123c079c7fdeafcb3d2877", + "zh:b99f042c45bf6aa69dd73f3f6d9cbe0b495b30442c526e0b3810089c059ba724", + "zh:bbb38e86d926ef101cefafe8fe090c57f2b1356eac9fc5ec81af310c50375897", + "zh:d03c89988ba4a0bd3cfc8659f951183ae7027aa8018a7ca1e53a300944af59cb", + "zh:d179ef28843fe663fc63169291a211898199009f0d3f63f0a6f65349e77727ec", + ] +} + +provider "registry.terraform.io/integrations/github" { + version = "6.6.0" + constraints = "~> 6.2" + hashes = [ + "h1:P4SRG4605PvPKASeDu1lW49TTz1cCGsjQ7qbOBgNd6I=", + "zh:0b1b5342db6a17de7c71386704e101be7d6761569e03fb3ff1f3d4c02c32d998", + "zh:2fb663467fff76852126b58315d9a1a457e3b04bec51f04bf1c0ddc9dfbb3517", + "zh:4183e557a1dfd413dae90ca4bac37dbbe499eae5e923567371f768053f977800", + "zh:48b2979f88fb55cdb14b7e4c37c44e0dfbc21b7a19686ce75e339efda773c5c2", + "zh:5d803fb06625e0bcf83abb590d4235c117fa7f4aa2168fa3d5f686c41bc529ec", + "zh:6f1dd094cbab36363583cda837d7ca470bef5f8abf9b19f23e9cd8b927153498", + "zh:772edb5890d72b32868f9fdc0a9a1d4f4701d8e7f8acb37a7ac530d053c776e3", + "zh:798f443dbba6610431dcef832047f6917fb5a4e184a3a776c44e6213fb429cc6", + "zh:cc08dfcc387e2603f6dbaff8c236c1254185450d6cadd6bad92879fe7e7dbce9", + "zh:d5e2c8d7f50f91d6847ddce27b10b721bdfce99c1bbab42a68fa271337d73d63", + "zh:e69a0045440c706f50f84a84ff8b1df520ec9bf757de4b8f9959f2ed20c3f440", + "zh:efc5358573a6403cbea3a08a2fcd2407258ac083d9134c641bdcb578966d8bdf", + "zh:f627a255e5809ec2375f79949c79417847fa56b9e9222ea7c45a463eb663f137", + "zh:f7c02f762e4cf1de7f58bde520798491ccdd54a5bd52278d579c146d1d07d4f0", + "zh:fbd1fee2c9df3aa19cf8851ce134dea6e45ea01cb85695c1726670c285797e25", + ] +} diff --git a/terragrunt/accounts/legacy/rustc-ci-staging/terragrunt.hcl b/terragrunt/accounts/legacy/rustc-ci-staging/terragrunt.hcl new file mode 100644 index 000000000..35f14fad6 --- /dev/null +++ b/terragrunt/accounts/legacy/rustc-ci-staging/terragrunt.hcl @@ -0,0 +1,12 @@ +terraform { + source = "../../../modules//rustc-ci" +} + +include { + path = find_in_parent_folders() + merge_strategy = "deep" +} + +inputs = { + repo = "bors-kindergarten" +} diff --git a/terragrunt/modules/gha-iam-user/main.tf b/terragrunt/modules/gha-iam-user/main.tf index df28d9519..9041df773 100644 --- a/terragrunt/modules/gha-iam-user/main.tf +++ b/terragrunt/modules/gha-iam-user/main.tf @@ -2,7 +2,7 @@ terraform { required_providers { github = { source = "integrations/github" - version = "~> 6.2.3" + version = "~> 6.2" } } } @@ -19,14 +19,34 @@ resource "aws_iam_access_key" "ci" { user = aws_iam_user.ci.name } +# Create repository secrets if the variable `environment` is not specified resource "github_actions_secret" "aws_access_key_id" { + count = var.environment == null ? 1 : 0 repository = var.repo secret_name = "${var.env_prefix != null ? "${var.env_prefix}_" : ""}AWS_ACCESS_KEY_ID" plaintext_value = aws_iam_access_key.ci.id } resource "github_actions_secret" "aws_secret_access_key" { + count = var.environment == null ? 1 : 0 repository = var.repo secret_name = "${var.env_prefix != null ? "${var.env_prefix}_" : ""}AWS_SECRET_ACCESS_KEY" plaintext_value = aws_iam_access_key.ci.secret } + +# Create environment secrets if `environment` is specified +resource "github_actions_environment_secret" "aws_access_key_id" { + count = var.environment != null ? 1 : 0 + repository = var.repo + environment = var.environment + secret_name = "${var.env_prefix != null ? "${var.env_prefix}_" : ""}AWS_ACCESS_KEY_ID" + plaintext_value = aws_iam_access_key.ci.id +} + +resource "github_actions_environment_secret" "aws_secret_access_key" { + count = var.environment != null ? 1 : 0 + repository = var.repo + environment = var.environment + secret_name = "${var.env_prefix != null ? "${var.env_prefix}_" : ""}AWS_SECRET_ACCESS_KEY" + plaintext_value = aws_iam_access_key.ci.secret +} diff --git a/terragrunt/modules/gha-iam-user/variables.tf b/terragrunt/modules/gha-iam-user/variables.tf index 617251ca9..5c4028c78 100644 --- a/terragrunt/modules/gha-iam-user/variables.tf +++ b/terragrunt/modules/gha-iam-user/variables.tf @@ -19,3 +19,9 @@ variable "env_prefix" { default = null description = "Prefix the environment variables in GitHub Actions should have" } + +variable "environment" { + type = string + default = null + description = "The name of the GitHub environment where GitHub Actions environment secrets are stored. If omitted, GitHub Actions repository secrets are created instead." +} diff --git a/terragrunt/modules/rustc-ci/_terraform.tf b/terragrunt/modules/rustc-ci/_terraform.tf new file mode 100644 index 000000000..992bde320 --- /dev/null +++ b/terragrunt/modules/rustc-ci/_terraform.tf @@ -0,0 +1,91 @@ +terraform { + required_version = "~> 1.0" + + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 5.86" + } + + github = { + source = "integrations/github" + version = "~> 6.2" + } + } +} + +provider "github" { + owner = "rust-lang" +} + +data "terraform_remote_state" "shared" { + backend = "s3" + config = { + bucket = "rust-terraform" + key = "simpleinfra/shared.tfstate" + region = "us-west-1" + } +} + +variable "repo" { + description = "GitHub repository to authorize. E.g. `rust`. GitHub org is hardcoded to `rust-lang`." + type = string + validation { + condition = !can(regex("/", var.repo)) + error_message = "The repo variable must not contain `/`. Only provide the repository name." + } + validation { + condition = length(var.repo) > 0 + error_message = "The repo variable must not be empty." + } +} + +variable "artifacts_bucket" { + description = "ID of the S3 bucket to store build artifacts. If unspecified, it is automatically generated, based on the repo name." + type = string + default = null + validation { + condition = var.artifacts_bucket == null ? true : length(var.artifacts_bucket) > 0 + error_message = "The artifacts_bucket variable must not be empty when specified." + } +} + +variable "inventories_bucket" { + description = "ID of the S3 bucket to store rust inventories. If unspecified, it is automatically generated, based on the repo name." + type = string + default = null + validation { + condition = var.inventories_bucket == null ? true : length(var.inventories_bucket) > 0 + error_message = "The inventories_bucket variable must not be empty when specified." + } +} + +variable "caches_bucket" { + description = "ID of the S3 bucket to store caches. If unspecified, it is automatically generated, based on the repo name." + type = string + default = null + validation { + condition = var.caches_bucket == null ? true : length(var.caches_bucket) > 0 + error_message = "The caches_bucket variable must not be empty when specified." + } +} + +variable "artifacts_domain" { + description = "Domain name for the CloudFront distribution in front of the artifacts bucket. If unspecified, it is automatically generated, based on the repo name." + type = string + default = null + validation { + condition = var.artifacts_domain == null ? true : length(var.artifacts_domain) > 0 + error_message = "The artifacts_domain variable must not be empty when specified." + } +} + +variable "caches_domain" { + description = "Domain name for the CloudFront distribution in front of the caches bucket. If unspecified, it is automatically generated, based on the repo name." + type = string + default = null + validation { + condition = var.caches_domain == null ? true : length(var.caches_domain) > 0 + error_message = "The caches_domain variable must not be empty when specified." + } +} diff --git a/terragrunt/modules/rustc-ci/artifacts.tf b/terragrunt/modules/rustc-ci/artifacts.tf new file mode 100644 index 000000000..f1a57d58c --- /dev/null +++ b/terragrunt/modules/rustc-ci/artifacts.tf @@ -0,0 +1,186 @@ +locals { + rustc_builds = "rustc-builds" + rustc_builds_alt = "rustc-builds-alt" + iam_prefix = "rustc-ci--rust-lang--${var.repo}" + + s3_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Sid = "ArtifactsBucketWrite" + Effect = "Allow" + Resource = [ + "${aws_s3_bucket.artifacts.arn}/${local.rustc_builds}", + "${aws_s3_bucket.artifacts.arn}/${local.rustc_builds}/*", + "${aws_s3_bucket.artifacts.arn}/${local.rustc_builds_alt}", + "${aws_s3_bucket.artifacts.arn}/${local.rustc_builds_alt}/*", + ] + Action = [ + "s3:GetObject", + "s3:DeleteObject", + "s3:PutObject", + "s3:PutObjectAcl", + ] + }, + { + Sid = "ArtifactsBucketList" + Effect = "Allow" + Resource = "${aws_s3_bucket.artifacts.arn}" + Action = [ + "s3:ListBucket", + ], + }, + { + Sid = "HeadBuckets", + Effect = "Allow", + Resource = "*" + Action = [ + "s3:HeadBucket", + "s3:GetBucketLocation", + ], + }, + ] + }) +} + +# For the rust-lang repo (prod environment) this was imported. +resource "aws_s3_bucket" "artifacts" { + bucket = var.artifacts_bucket != null ? var.artifacts_bucket : "${var.repo}-ci-artifacts" +} + +resource "aws_s3_bucket_lifecycle_configuration" "artifacts_lifecycle" { + bucket = aws_s3_bucket.artifacts.id + + rule { + status = "Enabled" + id = "cleanup-${local.rustc_builds}" + + // Note that this applies equally to rustc-builds and rustc-builds-alt, as + // it is a prefix. + filter { + prefix = local.rustc_builds + } + + expiration { + days = 168 + } + + noncurrent_version_expiration { + // This is *in addition* to the delete_artifacts_after_days above; we + // don't really need to keep CI artifacts around in an inaccessible state. + noncurrent_days = 1 + } + + abort_incomplete_multipart_upload { + days_after_initiation = 1 + } + } +} + +# For the rust-lang repo (prod environment) this was imported. +resource "aws_s3_bucket_policy" "artifacts" { + # Only create this policy for the rust-lang repo + # because for new repositories we want to keep the artifacts + # bucket private. + count = var.repo == "rust" ? 1 : 0 + bucket = aws_s3_bucket.artifacts.id + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Principal = { + AWS = "*" + } + Sid = "PublicReadGetObject" + Effect = "Allow" + Action = "s3:GetObject" + Resource = "${aws_s3_bucket.artifacts.arn}/*" + }, + ] + }) +} + +module "artifacts_user" { + source = "../gha-iam-user" + + org = "rust-lang" + repo = var.repo + + environment = github_repository_environment.bors.environment + user_name = "${local.iam_prefix}--artifacts" + env_prefix = "ARTIFACTS" +} + +resource "aws_iam_user_policy" "artifacts_write" { + name = "artifacts-write" + user = module.artifacts_user.user_name + + policy = local.s3_policy +} + +# TODO: probably this should be imported +module "artifacts_cdn" { + source = "../static-website" + providers = { + aws = aws.us-east-1 + } + + domain_name = var.artifacts_domain != null ? var.artifacts_domain : "${var.repo}-ci-artifacts.rust-lang.org" + origin_domain_name = aws_s3_bucket.artifacts.bucket_regional_domain_name + response_policy_id = data.terraform_remote_state.shared.outputs.mdbook_response_policy +} + +# In the prod environment, this was imported +resource "aws_s3_bucket" "inventories" { + bucket = var.inventories_bucket != null ? var.inventories_bucket : "${var.repo}-rust-inventories" +} + +# In the prod environment, this was imported +resource "aws_s3_bucket_inventory" "artifacts" { + name = "all-objects-csv" + bucket = aws_s3_bucket.artifacts.id + enabled = true + + included_object_versions = "Current" + optional_fields = ["ETag", "Size", "StorageClass", "IntelligentTieringAccessTier"] + + schedule { + frequency = "Weekly" + } + destination { + bucket { + bucket_arn = aws_s3_bucket.inventories.arn + prefix = aws_s3_bucket.artifacts.id + format = "CSV" + } + } +} + +resource "aws_iam_role" "try_builds" { + name = "${local.iam_prefix}--try-role" + + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Effect = "Allow" + Action = "sts:AssumeRoleWithWebIdentity" + Principal = { + Federated = "arn:aws:iam::890664054962:oidc-provider/token.actions.githubusercontent.com" + } + Condition = { + StringEquals = { + "token.actions.githubusercontent.com:sub" = "repo:rust-lang/${var.repo}:ref:refs/heads/automation/bors/try" + } + } + } + ] + }) +} + +resource "aws_iam_role_policy" "try_builds" { + name = "put-objects" + role = aws_iam_role.try_builds.id + policy = local.s3_policy +} diff --git a/terragrunt/modules/rustc-ci/caches.tf b/terragrunt/modules/rustc-ci/caches.tf new file mode 100644 index 000000000..1aa6e05eb --- /dev/null +++ b/terragrunt/modules/rustc-ci/caches.tf @@ -0,0 +1,122 @@ +# For the rust-lang repo (prod environment) this was imported. +resource "aws_s3_bucket" "caches" { + bucket = var.caches_bucket != null ? var.caches_bucket : "${var.repo}-ci-sccache" +} + +resource "aws_s3_bucket_lifecycle_configuration" "caches_lifecycle" { + bucket = aws_s3_bucket.caches.id + + rule { + status = "Enabled" + id = "delete-bucket-after-90-days" + + filter { + // Empty prefix means apply to all objects. + // It is equivalent to having an empty filter block. + prefix = "" + } + + expiration { + days = 90 + } + + noncurrent_version_expiration { + // This is *in addition* to the delete_caches_after_days above; we + // don't really need to keep CI caches around in an inaccessible state. + noncurrent_days = 1 + } + + abort_incomplete_multipart_upload { + days_after_initiation = 1 + } + } +} + +# For the rust-lang repo (prod environment) this was imported. +resource "aws_s3_bucket_policy" "caches" { + # Only create this policy for the rust-lang repo + # because for new repositories we want to keep the artifacts + # bucket private. + count = var.repo == "rust" ? 1 : 0 + bucket = aws_s3_bucket.caches.id + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Principal = { + AWS = "*" + } + Sid = "PublicReadGetObject" + Effect = "Allow" + Action = "s3:GetObject" + Resource = "${aws_s3_bucket.caches.arn}/*" + }, + ] + }) +} + +module "caches_user" { + source = "../gha-iam-user" + + org = "rust-lang" + repo = var.repo + + environment = github_repository_environment.bors.environment + user_name = "rustc-ci--${var.repo}--caches" + env_prefix = "CACHES" +} + +resource "aws_iam_user_policy" "caches_write" { + name = "caches-write" + user = module.caches_user.user_name + + policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Sid = "CachesBucketWrite" + Effect = "Allow" + Resource = [ + "${aws_s3_bucket.caches.arn}", + "${aws_s3_bucket.caches.arn}/*", + ] + Action = [ + "s3:GetObject", + "s3:DeleteObject", + "s3:PutObject", + "s3:PutObjectAcl", + ] + }, + { + Sid = "CachesBucketList" + Effect = "Allow" + Resource = "${aws_s3_bucket.caches.arn}" + Action = [ + "s3:ListBucket", + ], + }, + { + Sid = "HeadBuckets", + Effect = "Allow", + Resource = "*" + Action = [ + "s3:HeadBucket", + "s3:GetBucketLocation", + ], + }, + ] + }) +} + +# For the rust-lang repo (prod environment) this was imported. +module "caches_cdn" { + source = "../static-website" + providers = { + aws = aws.us-east-1 + } + + domain_name = var.caches_domain != null ? var.caches_domain : "${var.repo}-ci-caches.rust-lang.org" + origin_domain_name = aws_s3_bucket.caches.bucket_regional_domain_name + response_policy_id = data.terraform_remote_state.shared.outputs.mdbook_response_policy +} diff --git a/terragrunt/modules/rustc-ci/github_env.tf b/terragrunt/modules/rustc-ci/github_env.tf new file mode 100644 index 000000000..8bb2d94ca --- /dev/null +++ b/terragrunt/modules/rustc-ci/github_env.tf @@ -0,0 +1,8 @@ +resource "github_repository_environment" "bors" { + environment = "bors" + repository = var.repo + deployment_branch_policy { + custom_branch_policies = true + protected_branches = false + } +} diff --git a/terragrunt/modules/static-website/main.tf b/terragrunt/modules/static-website/main.tf new file mode 100644 index 000000000..948f682f9 --- /dev/null +++ b/terragrunt/modules/static-website/main.tf @@ -0,0 +1,99 @@ +terraform { + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 5.64" + } + } +} + +module "certificate" { + source = "../acm-certificate" + legacy = true + + domains = [ + var.domain_name, + ] +} + +data "aws_cloudfront_cache_policy" "cache" { + name = "Managed-CachingOptimized" +} + +resource "aws_cloudfront_distribution" "website" { + comment = "static website ${var.domain_name}" + + enabled = true + wait_for_deployment = true + is_ipv6_enabled = true + default_root_object = "index.html" + price_class = "PriceClass_All" + http_version = "http2and3" + + aliases = [var.domain_name] + viewer_certificate { + acm_certificate_arn = module.certificate.arn + minimum_protocol_version = "TLSv1.2_2021" + ssl_support_method = "sni-only" + } + + default_cache_behavior { + target_origin_id = "main" + allowed_methods = ["GET", "HEAD"] + cached_methods = ["GET", "HEAD"] + compress = true + viewer_protocol_policy = "redirect-to-https" + + cache_policy_id = data.aws_cloudfront_cache_policy.cache.id + response_headers_policy_id = var.response_policy_id + } + + dynamic "origin" { + for_each = var.origin_access_identity == null ? toset([true]) : toset([]) + content { + origin_id = "main" + domain_name = var.origin_domain_name + origin_path = var.origin_path + custom_origin_config { + http_port = 80 + https_port = 443 + origin_ssl_protocols = ["TLSv1.2"] + origin_protocol_policy = "https-only" + } + } + } + dynamic "origin" { + for_each = var.origin_access_identity != null ? toset([true]) : toset([]) + content { + origin_id = "main" + domain_name = var.origin_domain_name + + s3_origin_config { + origin_access_identity = var.origin_access_identity + } + } + } + + restrictions { + geo_restriction { + restriction_type = "none" + } + } +} + +data "aws_route53_zone" "zone" { + // Convert foo.bar.baz into bar.baz + name = join(".", reverse(slice(reverse(split(".", var.domain_name)), 0, 2))) +} + +resource "aws_route53_record" "website" { + zone_id = data.aws_route53_zone.zone.id + name = var.domain_name + type = "CNAME" + ttl = 300 + records = [aws_cloudfront_distribution.website.domain_name] +} + +output "distribution_arn" { + value = aws_cloudfront_distribution.website.arn +} diff --git a/terragrunt/modules/static-website/outputs.tf b/terragrunt/modules/static-website/outputs.tf new file mode 100644 index 000000000..e69de29bb diff --git a/terragrunt/modules/static-website/variables.tf b/terragrunt/modules/static-website/variables.tf new file mode 100644 index 000000000..f24bdf1a2 --- /dev/null +++ b/terragrunt/modules/static-website/variables.tf @@ -0,0 +1,26 @@ +variable "domain_name" { + type = string + description = "Domain name of the CloudFront distribution" +} + +variable "origin_domain_name" { + type = string + description = "Domain name of the origin" +} + +variable "origin_path" { + type = string + default = null + description = "Root path in the origin" +} + +variable "origin_access_identity" { + type = string + default = null + description = "Origin Access Identity to use to fetch contents from S3" +} + +variable "response_policy_id" { + type = string + description = "CloudFront response headers policy ID" +}