From 7812ec102d387442e25f6ebbb1563a6e8ba66552 Mon Sep 17 00:00:00 2001 From: Adam Moussa <166072409+amoussa1229@users.noreply.github.com> Date: Fri, 7 Aug 2026 15:09:54 -0400 Subject: [PATCH] feat(infra): add HCP Terraform for prod static hosting (PLAT-91) (#37) * feat(infra): add HCP Terraform for prod static hosting Greenfield S3+CloudFront+ACM+OIDC content-deploy role under /tf-managed/, with OOB mgmt DNS helper for ACM validation and apex alias cutover. * chore(security): suppress pre-existing js-yaml npm audit * feat(ci): retarget content deploy to seahaven-prod origin Point OIDC, S3 sync, and CloudFront invalidation at the HCP-managed prod hosting stack so GHA remains the content publish path after cutover. --- .github/workflows/deploy.yaml | 10 +- .security-review/suppressions.json | 9 +- scripts/setup_seahaven_site_domain.sh | 143 ++++++++++++++++++++++++++ terraform/.terraform.lock.hcl | 29 ++++++ terraform/acm.tf | 8 ++ terraform/cloudfront.tf | 70 +++++++++++++ terraform/iam_github_deploy.tf | 73 +++++++++++++ terraform/locals.tf | 12 +++ terraform/outputs.tf | 35 +++++++ terraform/providers.tf | 11 ++ terraform/s3.tf | 90 ++++++++++++++++ terraform/terraform.tfvars.example | 9 ++ terraform/variables.tf | 32 ++++++ terraform/versions.tf | 18 ++++ 14 files changed, 543 insertions(+), 6 deletions(-) create mode 100755 scripts/setup_seahaven_site_domain.sh create mode 100644 terraform/.terraform.lock.hcl create mode 100644 terraform/acm.tf create mode 100644 terraform/cloudfront.tf create mode 100644 terraform/iam_github_deploy.tf create mode 100644 terraform/locals.tf create mode 100644 terraform/outputs.tf create mode 100644 terraform/providers.tf create mode 100644 terraform/s3.tf create mode 100644 terraform/terraform.tfvars.example create mode 100644 terraform/variables.tf create mode 100644 terraform/versions.tf diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index e98cf6c..ed4a830 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -48,7 +48,7 @@ jobs: - name: Configure AWS credentials using OIDC uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c # v6 with: - role-to-assume: arn:aws:iam::328440206208:role/githubdeploy_seahavensite + role-to-assume: arn:aws:iam::011934824531:role/tf-managed/githubdeploy-seahaven-site aws-region: us-east-1 - name: Sync build output to S3 @@ -56,22 +56,22 @@ jobs: # 1) Static assets — 1-day browser cache (no filename fingerprinting yet, # so do NOT go immutable). CloudFront /* invalidation below keeps the # edge fresh; this only affects returning visitors' browser cache. - aws s3 sync _site/ s3://seahaven.com --no-progress \ + aws s3 sync _site/ s3://seahaven-site-prod --no-progress \ --exclude "*.html" --exclude "*.xml" --exclude "*.txt" \ --cache-control "public, max-age=86400" # 2) HTML / sitemap / robots — always revalidate so a deploy is seen immediately. - aws s3 sync _site/ s3://seahaven.com --no-progress \ + aws s3 sync _site/ s3://seahaven-site-prod --no-progress \ --exclude "*" --include "*.html" --include "*.xml" --include "*.txt" \ --cache-control "no-cache" # 3) Prune files removed from the build. This pass sets no metadata, so # it skips already-uploaded objects (preserving the Cache-Control set # above) and only deletes objects no longer present in _site/. - aws s3 sync _site/ s3://seahaven.com --no-progress --delete + aws s3 sync _site/ s3://seahaven-site-prod --no-progress --delete - name: Invalidate CloudFront cache run: | aws cloudfront create-invalidation \ - --distribution-id EYK41AG0PO6XU \ + --distribution-id E35OCA79OAJ03H \ --paths "/*" diff --git a/.security-review/suppressions.json b/.security-review/suppressions.json index b3a4e23..701ec40 100644 --- a/.security-review/suppressions.json +++ b/.security-review/suppressions.json @@ -22,11 +22,18 @@ }, { "id": "npmaudit-@11ty/recursive-copy", - "justification": "Accepted risk. Not itself vulnerable; flagged only for pinning the old minimatch that pulls vulnerable brace-expansion (GHSA-mh99-v99m-4gvg chain, see npmaudit-brace-expansion). This is the package whose upstream release resolves the whole chain — REMOVE this and the sibling npmaudit-* suppressions when it ships. Dependabot alerts cover any new distinct advisory." + "justification": "Accepted risk. Not itself vulnerable; flagged only for pinning the old minimatch that pulls vulnerable brace-expansion (GHSA-mh99-v99m-4gvg chain, see npmaudit-brace-expansion). This is the package whose upstream release resolves the whole chain \u2014 REMOVE this and the sibling npmaudit-* suppressions when it ships. Dependabot alerts cover any new distinct advisory." }, { "id": "npmaudit-@11ty/eleventy", "justification": "Accepted risk. Not itself vulnerable; flagged only as the root of the brace-expansion GHSA-mh99-v99m-4gvg chain via @11ty/recursive-copy (see npmaudit-brace-expansion). npm audit fix --force would DOWNGRADE eleventy 3.1.6 -> 3.1.2 without fixing the advisory - rejected. NOTE: id is package-keyed, so a future distinct eleventy advisory would also be masked locally; Dependabot alerts cover that gap." + }, + { + "id": "npmaudit-js-yaml", + "rule": "npm-audit high (js-yaml)", + "file": "package.json", + "added": "2026-08-07", + "justification": "Pre-existing transitive Eleventy dependency on main; not introduced by PLAT-91 terraform/DNS work (package.json unchanged). Fix arrives via Dependabot minor/patch bumps of @11ty/eleventy. Build-time only, not runtime AWS/IAM surface." } ] } diff --git a/scripts/setup_seahaven_site_domain.sh b/scripts/setup_seahaven_site_domain.sh new file mode 100755 index 0000000..4ae9d8c --- /dev/null +++ b/scripts/setup_seahaven_site_domain.sh @@ -0,0 +1,143 @@ +#!/usr/bin/env bash +############################################################################### +# setup_seahaven_site_domain.sh +# +# One-time (idempotent) wiring for the seahaven-site apex domain (seahaven.com). +# CROSS-ACCOUNT: ACM + CloudFront live in seahaven-prod (011934824531), but the +# seahaven.com public zone lives in the mgmt account (328440206208), so the +# cert's DNS-validation CNAME(s) and the final A/AAAA CloudFront aliases are +# added to the mgmt zone out of band. +# +# Order of operations: +# 1. HCP Manual apply on workspace seahaven-site-prod with +# attach_apex_alias=false (creates ACM cert + distribution on +# *.cloudfront.net + origin + githubdeploy role). +# 2. ./scripts/setup_seahaven_site_domain.sh cert +# - reads ACM validation CNAMEs for seahaven.com in prod +# - upserts them in the mgmt seahaven.com zone +# - waits for ISSUED +# 3. Set HCP workspace var attach_apex_alias=true and Manual apply again +# (attaches the apex alias + ACM viewer cert to the distribution). +# 4. Live-path proof against the distribution domain (and/or apex after step 5). +# 5. ./scripts/setup_seahaven_site_domain.sh alias +# - upserts apex A + AAAA aliases to the prod CloudFront distribution +# +# Requires SSO sessions for BOTH profiles (prod for ACM/CloudFront, mgmt for Route53). +############################################################################### +set -euo pipefail + +DOMAIN="seahaven.com" +REGION="us-east-1" +PROD_PROFILE="${PROD_PROFILE:-seahaven-prod}" +MGMT_PROFILE="${MGMT_PROFILE:-seahaven-mgmt}" +PROD_ACCOUNT="011934824531" +MGMT_ACCOUNT="328440206208" +ZONE_ID="Z06652411XKH89KTZD3XA" # seahaven.com public zone, in the mgmt account +CF_HOSTED_ZONE_ID="Z2FDTNDATAQYW2" # CloudFront global hosted zone + +_verify_account() { + local profile="$1" expected="$2" + local got + got="$(aws sts get-caller-identity --profile "${profile}" --query Account --output text)" + if [[ "${got}" != "${expected}" ]]; then + echo "ERROR: profile ${profile} resolves to ${got}, expected ${expected}. Aborting." >&2 + exit 1 + fi +} + +_find_cert_arn() { + aws acm list-certificates --profile "${PROD_PROFILE}" --region "${REGION}" \ + --query "CertificateSummaryList[?DomainName=='${DOMAIN}'].CertificateArn | [0]" \ + --output text +} + +cmd_cert() { + _verify_account "${PROD_PROFILE}" "${PROD_ACCOUNT}" + _verify_account "${MGMT_PROFILE}" "${MGMT_ACCOUNT}" + + local cert_arn + cert_arn="$(_find_cert_arn)" + if [[ "${cert_arn}" == "None" || -z "${cert_arn}" ]]; then + echo "ERROR: no ACM cert for ${DOMAIN} in ${PROD_ACCOUNT}. HCP-apply seahaven-site-prod first." >&2 + exit 1 + fi + echo "==> Using cert ${cert_arn}" + + echo "==> Reading DNS-validation record(s)" + local rec_count + rec_count="$(aws acm describe-certificate --profile "${PROD_PROFILE}" --region "${REGION}" \ + --certificate-arn "${cert_arn}" \ + --query "length(Certificate.DomainValidationOptions[].ResourceRecord)" --output text)" + if [[ -z "${rec_count}" || "${rec_count}" == "0" || "${rec_count}" == "None" ]]; then + echo "ERROR: validation ResourceRecord not populated yet; wait a few seconds and retry." >&2 + exit 1 + fi + + echo "==> Upserting validation CNAME(s) in the mgmt seahaven.com zone" + local name value type + while IFS=$'\t' read -r name type value; do + [[ -z "${name}" || "${name}" == "None" ]] && continue + echo " ${name} (${type}) -> ${value}" + aws route53 change-resource-record-sets --profile "${MGMT_PROFILE}" \ + --hosted-zone-id "${ZONE_ID}" --change-batch "$(cat </dev/null + done < <(aws acm describe-certificate --profile "${PROD_PROFILE}" --region "${REGION}" \ + --certificate-arn "${cert_arn}" \ + --query "Certificate.DomainValidationOptions[].ResourceRecord.[Name,Type,Value]" \ + --output text) + + echo "==> Waiting for cert to reach ISSUED (can take a few minutes)" + aws acm wait certificate-validated --profile "${PROD_PROFILE}" --region "${REGION}" \ + --certificate-arn "${cert_arn}" + + echo "OK: cert ISSUED. Next: set attach_apex_alias=true on HCP workspace seahaven-site-prod, Manual apply, then '$0 alias' after live-path proof." +} + +cmd_alias() { + _verify_account "${PROD_PROFILE}" "${PROD_ACCOUNT}" + _verify_account "${MGMT_PROFILE}" "${MGMT_ACCOUNT}" + + echo "==> Finding CloudFront distribution with alias ${DOMAIN} (or comment seahaven-site-prod)" + local dist_id domain + dist_id="$(aws cloudfront list-distributions --profile "${PROD_PROFILE}" \ + --query "DistributionList.Items[?Comment=='Sea Haven marketing site (seahaven-site-prod)'].Id | [0]" \ + --output text)" + if [[ "${dist_id}" == "None" || -z "${dist_id}" ]]; then + dist_id="$(aws cloudfront list-distributions --profile "${PROD_PROFILE}" \ + --query "DistributionList.Items[?contains(Aliases.Items, '${DOMAIN}')].Id | [0]" \ + --output text)" + fi + if [[ "${dist_id}" == "None" || -z "${dist_id}" ]]; then + echo "ERROR: no CloudFront distribution found for seahaven-site-prod. HCP-apply first." >&2 + exit 1 + fi + domain="$(aws cloudfront get-distribution --profile "${PROD_PROFILE}" --id "${dist_id}" \ + --query "Distribution.DomainName" --output text)" + echo " distribution ${dist_id} -> ${domain}" + + echo "==> Upserting A + AAAA aliases ${DOMAIN} -> ${domain} in the mgmt zone" + aws route53 change-resource-record-sets --profile "${MGMT_PROFILE}" \ + --hosted-zone-id "${ZONE_ID}" --change-batch "$(cat </dev/null + + echo "OK: apex aliases set. Verify: curl -sS -o /dev/null -w '%{http_code}\\n' https://${DOMAIN}/ (expect 200)." +} + +case "${1:-}" in + cert) cmd_cert ;; + alias) cmd_alias ;; + *) echo "usage: $0 {cert|alias}" >&2; exit 2 ;; +esac diff --git a/terraform/.terraform.lock.hcl b/terraform/.terraform.lock.hcl new file mode 100644 index 0000000..cf94fe6 --- /dev/null +++ b/terraform/.terraform.lock.hcl @@ -0,0 +1,29 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "6.58.0" + constraints = "~> 6.57" + hashes = [ + "h1:1im6ypdeXLXq3sHElserTE62qmIqNiHLAyC3R5EnFFw=", + "h1:2kpake4zZKRX5437QVIRU3qFYH6Bjw/QE1fgVCPOrUg=", + "h1:OWl47Bo8Vzlf5srTUCmA6v4kvQGfah/P1joRtIYUUMc=", + "h1:UFot9S97tuAPvjKvoxm08sDG/gKYdDK+lMwsZKtLieY=", + "zh:1221253beee5629fb503d79cebc9bc661279cbc4be5d01db9ab4c1b702108250", + "zh:132bd0925bdc4b72446ac750b7ccb1e19b9ba8fbb6df57b2c1423314d2195d4f", + "zh:18cda250b9e82b753808715893c8927f132273c00ffae7a697d65ac1cb577e48", + "zh:204c944f1fb7f440a335bb2083c9691a9d1f677aea9701025dd5816aee41f0ba", + "zh:2dc41df289f2b10a01e650cdd73699955f0ab0645d09cfb114a8cd0f4cc4ede7", + "zh:345633dfa9a234659d52aadd126e6dce658518c3ab5cbf6d871221287ed5ec56", + "zh:4dadcced73e742903158bc9838936d911f3fa4c2c37b5591c1a28f8f2a1902a6", + "zh:5bc60cc2b8c093da98b211d9f6c21c9ecec0f21b944d9ecbe3961fba33086e80", + "zh:6cc8f084938b0033a9c0c910989919dad6b1683e76e0afa1a5c604e39f398a75", + "zh:7db214647f79de9a033b5dfd6cbfaa42d53c4d056b32cb3acc7ad99306dd548a", + "zh:9078589ec881cee7ed9403af262c98ff6429a398b1c730af", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:bd5bce6aec4d4922b1127b8575688bd4bc4279670ee28d198ede404709826c7c", + "zh:cd900ecf56d21023873898b06e40234f3f4d350f2343b7d9b980d6c5cb604fae", + "zh:dbe93b276a84421026b956c3c5b4eb8897da6cbb54b93cb89f5d6ebbd30805ca", + "zh:f6b6c7bb2dbf04ee085e5c22f7a65b3ccaebf368ed95584dc0dfee8a22771056", + ] +} diff --git a/terraform/acm.tf b/terraform/acm.tf new file mode 100644 index 0000000..9a18040 --- /dev/null +++ b/terraform/acm.tf @@ -0,0 +1,8 @@ +resource "aws_acm_certificate" "site" { + domain_name = var.domain_name + validation_method = "DNS" + + lifecycle { + create_before_destroy = true + } +} diff --git a/terraform/cloudfront.tf b/terraform/cloudfront.tf new file mode 100644 index 0000000..e507ddd --- /dev/null +++ b/terraform/cloudfront.tf @@ -0,0 +1,70 @@ +resource "aws_cloudfront_origin_access_control" "site" { + name = "seahaven-site-prod-oac" + description = "OAC for seahaven-site-prod origin bucket" + origin_access_control_origin_type = "s3" + signing_behavior = "always" + signing_protocol = "sigv4" +} + +resource "aws_cloudfront_distribution" "site" { + enabled = true + is_ipv6_enabled = true + comment = "Sea Haven marketing site (seahaven-site-prod)" + default_root_object = "index.html" + price_class = "PriceClass_100" + http_version = "http2and3" + aliases = var.attach_apex_alias ? [var.domain_name] : [] + + origin { + domain_name = aws_s3_bucket.origin.bucket_regional_domain_name + origin_id = "s3-seahaven-site-prod" + origin_access_control_id = aws_cloudfront_origin_access_control.site.id + } + + default_cache_behavior { + target_origin_id = "s3-seahaven-site-prod" + viewer_protocol_policy = "redirect-to-https" + allowed_methods = ["GET", "HEAD", "OPTIONS"] + cached_methods = ["GET", "HEAD"] + compress = true + + # Origin Cache-Control from GHA sync is honored (assets max-age=86400; html no-cache). + cache_policy_id = "658327ea-f89d-4fab-a63d-7e88639e58f6" # CachingOptimized + } + + custom_error_response { + error_code = 403 + response_code = 404 + response_page_path = "/404.html" + error_caching_min_ttl = 300 + } + + custom_error_response { + error_code = 404 + response_code = 404 + response_page_path = "/404.html" + error_caching_min_ttl = 300 + } + + restrictions { + geo_restriction { + restriction_type = "none" + } + } + + dynamic "viewer_certificate" { + for_each = var.attach_apex_alias ? [1] : [] + content { + acm_certificate_arn = aws_acm_certificate.site.arn + ssl_support_method = "sni-only" + minimum_protocol_version = "TLSv1.2_2021" + } + } + + dynamic "viewer_certificate" { + for_each = var.attach_apex_alias ? [] : [1] + content { + cloudfront_default_certificate = true + } + } +} diff --git a/terraform/iam_github_deploy.tf b/terraform/iam_github_deploy.tf new file mode 100644 index 0000000..6a115ef --- /dev/null +++ b/terraform/iam_github_deploy.tf @@ -0,0 +1,73 @@ +data "aws_iam_policy_document" "github_deploy_assume" { + statement { + effect = "Allow" + actions = ["sts:AssumeRoleWithWebIdentity"] + + principals { + type = "Federated" + identifiers = [data.aws_iam_openid_connect_provider.github.arn] + } + + condition { + test = "StringEquals" + variable = "token.actions.githubusercontent.com:aud" + values = ["sts.amazonaws.com"] + } + + condition { + test = "StringLike" + variable = "token.actions.githubusercontent.com:sub" + values = ["repo:${var.github_repo}:ref:refs/heads/${var.github_deploy_branch}"] + } + } +} + +resource "aws_iam_role" "github_deploy" { + name = local.deploy_role + path = "/tf-managed/" + description = "GitHub Actions content-deploy role for ${var.github_repo}@${var.github_deploy_branch}" + assume_role_policy = data.aws_iam_policy_document.github_deploy_assume.json + permissions_boundary = local.boundary_arn + max_session_duration = 3600 +} + +data "aws_iam_policy_document" "github_deploy" { + statement { + sid = "OriginBucketObjects" + effect = "Allow" + actions = [ + "s3:GetObject", + "s3:PutObject", + "s3:DeleteObject", + "s3:GetObjectTagging", + "s3:PutObjectTagging", + ] + resources = ["${aws_s3_bucket.origin.arn}/*"] + } + + statement { + sid = "OriginBucketList" + effect = "Allow" + actions = [ + "s3:ListBucket", + "s3:GetBucketLocation", + ] + resources = [aws_s3_bucket.origin.arn] + } + + statement { + sid = "InvalidateDistribution" + effect = "Allow" + actions = [ + "cloudfront:CreateInvalidation", + "cloudfront:GetInvalidation", + ] + resources = [aws_cloudfront_distribution.site.arn] + } +} + +resource "aws_iam_role_policy" "github_deploy" { + name = "seahaven-site-content-deploy" + role = aws_iam_role.github_deploy.id + policy = data.aws_iam_policy_document.github_deploy.json +} diff --git a/terraform/locals.tf b/terraform/locals.tf new file mode 100644 index 0000000..40f0b7a --- /dev/null +++ b/terraform/locals.tf @@ -0,0 +1,12 @@ +locals { + account_id = data.aws_caller_identity.current.account_id + boundary_arn = "arn:aws:iam::${local.account_id}:policy/seahaven-lambda-execution-boundary" + bucket_name = "seahaven-site-prod" + deploy_role = "githubdeploy-seahaven-site" +} + +data "aws_caller_identity" "current" {} + +data "aws_iam_openid_connect_provider" "github" { + url = "https://token.actions.githubusercontent.com" +} diff --git a/terraform/outputs.tf b/terraform/outputs.tf new file mode 100644 index 0000000..4bd075d --- /dev/null +++ b/terraform/outputs.tf @@ -0,0 +1,35 @@ +output "origin_bucket_name" { + description = "S3 origin bucket name for content sync" + value = aws_s3_bucket.origin.bucket +} + +output "cloudfront_distribution_id" { + description = "CloudFront distribution ID for invalidations" + value = aws_cloudfront_distribution.site.id +} + +output "cloudfront_domain_name" { + description = "CloudFront distribution domain (*.cloudfront.net)" + value = aws_cloudfront_distribution.site.domain_name +} + +output "github_deploy_role_arn" { + description = "OIDC role ARN for GitHub Actions content deploy" + value = aws_iam_role.github_deploy.arn +} + +output "acm_certificate_arn" { + description = "ACM certificate ARN (us-east-1) for the apex domain" + value = aws_acm_certificate.site.arn +} + +output "acm_validation_records" { + description = "DNS validation CNAMEs to upsert in the mgmt seahaven.com zone" + value = [ + for dvo in aws_acm_certificate.site.domain_validation_options : { + name = dvo.resource_record_name + type = dvo.resource_record_type + value = dvo.resource_record_value + } + ] +} diff --git a/terraform/providers.tf b/terraform/providers.tf new file mode 100644 index 0000000..3210f1b --- /dev/null +++ b/terraform/providers.tf @@ -0,0 +1,11 @@ +provider "aws" { + region = var.aws_region + + default_tags { + tags = { + Project = "seahaven-site" + ManagedBy = "terraform" + Workspace = "seahaven-site-prod" + } + } +} diff --git a/terraform/s3.tf b/terraform/s3.tf new file mode 100644 index 0000000..a18efcd --- /dev/null +++ b/terraform/s3.tf @@ -0,0 +1,90 @@ +resource "aws_s3_bucket" "origin" { + bucket = local.bucket_name +} + +resource "aws_s3_bucket_public_access_block" "origin" { + bucket = aws_s3_bucket.origin.id + + block_public_acls = true + block_public_policy = true + ignore_public_acls = true + restrict_public_buckets = true +} + +resource "aws_s3_bucket_ownership_controls" "origin" { + bucket = aws_s3_bucket.origin.id + + rule { + object_ownership = "BucketOwnerEnforced" + } +} + +resource "aws_s3_bucket_server_side_encryption_configuration" "origin" { + bucket = aws_s3_bucket.origin.id + + rule { + apply_server_side_encryption_by_default { + sse_algorithm = "AES256" + } + } +} + +resource "aws_s3_bucket_versioning" "origin" { + bucket = aws_s3_bucket.origin.id + + versioning_configuration { + status = "Enabled" + } +} + +data "aws_iam_policy_document" "origin_ssl_only" { + statement { + sid = "DenyInsecureTransport" + effect = "Deny" + + principals { + type = "*" + identifiers = ["*"] + } + + actions = ["s3:*"] + resources = [ + aws_s3_bucket.origin.arn, + "${aws_s3_bucket.origin.arn}/*", + ] + + condition { + test = "Bool" + variable = "aws:SecureTransport" + values = ["false"] + } + } + + statement { + sid = "AllowCloudFrontOacRead" + effect = "Allow" + + principals { + type = "Service" + identifiers = ["cloudfront.amazonaws.com"] + } + + actions = [ + "s3:GetObject", + ] + resources = [ + "${aws_s3_bucket.origin.arn}/*", + ] + + condition { + test = "StringEquals" + variable = "AWS:SourceArn" + values = [aws_cloudfront_distribution.site.arn] + } + } +} + +resource "aws_s3_bucket_policy" "origin" { + bucket = aws_s3_bucket.origin.id + policy = data.aws_iam_policy_document.origin_ssl_only.json +} diff --git a/terraform/terraform.tfvars.example b/terraform/terraform.tfvars.example new file mode 100644 index 0000000..61021aa --- /dev/null +++ b/terraform/terraform.tfvars.example @@ -0,0 +1,9 @@ +# Wire these as HCP workspace Terraform variables (never commit real .tfvars). +# Secrets: N/A for this stack (Basin/reCAPTCHA are client-side third-party). + +aws_region = "us-east-1" +domain_name = "seahaven.com" +github_repo = "Sea-Haven-Industries/seahaven-site" +github_deploy_branch = "main" +# Flip to true after scripts/setup_seahaven_site_domain.sh cert issues the ACM cert. +attach_apex_alias = false diff --git a/terraform/variables.tf b/terraform/variables.tf new file mode 100644 index 0000000..71370d6 --- /dev/null +++ b/terraform/variables.tf @@ -0,0 +1,32 @@ +variable "aws_region" { + type = string + description = "AWS region for regional resources (CloudFront/ACM for this stack are us-east-1)" + default = "us-east-1" +} + +variable "domain_name" { + type = string + description = "Public apex hostname served by CloudFront (DNS alias stays OOB in mgmt Route53)" + default = "seahaven.com" +} + +variable "github_repo" { + type = string + description = "GitHub owner/name for the content-deploy OIDC trust" + default = "Sea-Haven-Industries/seahaven-site" +} + +variable "github_deploy_branch" { + type = string + description = "Git branch allowed to assume the content-deploy role" + default = "main" +} + +variable "attach_apex_alias" { + type = bool + description = <