From 53c1acb19f953edd42fc9ebfd3efb88bfe0ba2bd Mon Sep 17 00:00:00 2001 From: Adam Moussa Date: Thu, 17 Sep 2026 17:13:32 -0400 Subject: [PATCH] refactor(terraform): keep live/dev and live/staging as HCP roots Leave the adopted working directories in place so this CD PR does not retarget two live HCP workspaces. Flattening stays a later change. --- .github/workflows/ci-terraform.yaml | 14 +-- QUALITY_GATES.md | 36 +++---- README.md | 10 +- scripts/terraform-validate.mjs | 24 ++--- scripts/terraform_import_plan_resources.py | 2 +- scripts/test-terraform-import-plan-check.py | 42 ++++---- scripts/test_check_app_terraform_isolation.py | 14 +-- terraform/README.md | 42 ++++---- terraform/live/README.md | 8 ++ terraform/{ => live/dev}/.terraform.lock.hcl | 0 terraform/{ => live/dev}/imports.tf | 26 ++--- terraform/live/dev/main.tf | 95 +++++++++++++++++++ terraform/{ => live/dev}/outputs.tf | 0 terraform/{ => live/dev}/providers.tf | 0 terraform/{ => live/dev}/versions.tf | 17 ++-- .../modules/environment-inventory/main.tf | 0 .../modules/environment-inventory/outputs.tf | 0 .../environment-inventory/variables.tf | 0 .../modules/environment-owned/main.tf | 0 .../modules/environment-owned/outputs.tf | 0 .../modules/environment-owned/variables.tf | 0 terraform/live/staging/.terraform.lock.hcl | 30 ++++++ terraform/live/staging/imports.tf | 64 +++++++++++++ terraform/live/staging/main.tf | 95 +++++++++++++++++++ terraform/live/staging/outputs.tf | 19 ++++ terraform/live/staging/providers.tf | 3 + terraform/live/staging/versions.tf | 19 ++++ terraform/locals.tf | 74 --------------- terraform/main.tf | 45 --------- terraform/variables.tf | 9 -- 30 files changed, 447 insertions(+), 241 deletions(-) create mode 100644 terraform/live/README.md rename terraform/{ => live/dev}/.terraform.lock.hcl (100%) rename terraform/{ => live/dev}/imports.tf (65%) create mode 100644 terraform/live/dev/main.tf rename terraform/{ => live/dev}/outputs.tf (100%) rename terraform/{ => live/dev}/providers.tf (100%) rename terraform/{ => live/dev}/versions.tf (74%) rename terraform/{ => live}/modules/environment-inventory/main.tf (100%) rename terraform/{ => live}/modules/environment-inventory/outputs.tf (100%) rename terraform/{ => live}/modules/environment-inventory/variables.tf (100%) rename terraform/{ => live}/modules/environment-owned/main.tf (100%) rename terraform/{ => live}/modules/environment-owned/outputs.tf (100%) rename terraform/{ => live}/modules/environment-owned/variables.tf (100%) create mode 100644 terraform/live/staging/.terraform.lock.hcl create mode 100644 terraform/live/staging/imports.tf create mode 100644 terraform/live/staging/main.tf create mode 100644 terraform/live/staging/outputs.tf create mode 100644 terraform/live/staging/providers.tf create mode 100644 terraform/live/staging/versions.tf delete mode 100644 terraform/locals.tf delete mode 100644 terraform/main.tf delete mode 100644 terraform/variables.tf diff --git a/.github/workflows/ci-terraform.yaml b/.github/workflows/ci-terraform.yaml index 5a155d70..a4902d15 100644 --- a/.github/workflows/ci-terraform.yaml +++ b/.github/workflows/ci-terraform.yaml @@ -39,13 +39,15 @@ jobs: - name: Terraform fmt run: terraform fmt -check -recursive terraform - - name: Terraform init - run: terraform -chdir=terraform init -backend=false -input=false -lockfile=readonly -no-color + - name: Validate live/dev + run: | + terraform -chdir=terraform/live/dev init -backend=false -input=false -lockfile=readonly -no-color + terraform -chdir=terraform/live/dev validate -no-color - - name: Terraform validate - run: terraform -chdir=terraform validate -no-color - env: - TF_VAR_environment: dev + - name: Validate live/staging + run: | + terraform -chdir=terraform/live/staging init -backend=false -input=false -lockfile=readonly -no-color + terraform -chdir=terraform/live/staging validate -no-color - name: Import plan guard tests run: python3 scripts/test-terraform-import-plan-check.py diff --git a/QUALITY_GATES.md b/QUALITY_GATES.md index 28a2acdf..62ae26c5 100644 --- a/QUALITY_GATES.md +++ b/QUALITY_GATES.md @@ -14,24 +14,24 @@ isolation). A task is not done until this is green. ## Gate matrix -| Gate | Command / rule source | Enforced by | Scope | -| ----------------------------------- | ----------------------------------------------------------------------------------------------------------- | ---------------------- | -------------------------------------- | -| Formatting | `npm run format:check` (Prettier) | `verify` + lint-staged | Whole repo | -| Lint, zero warnings | `npm run lint` → `eslint . --max-warnings=0` | `verify` + CI | Governed TS/TSX (`eslint.config.js`) | -| Type-check + production build | `npm run build` → `tsc -b && vite build` | `verify` + CI | Whole app | -| Unit tests | `npm test` → `vitest run` | `verify` + CI | `src/test/**`, `config/**/*.test.ts` | -| Conditional rendering (no `: null`) | `no-restricted-syntax` in `eslint.config.js` | lint | Governed TSX | -| Boolean-only JSX `&&` | `seahaven/no-non-boolean-jsx-and` (type-aware) in `eslint-rules/` | lint | Governed TSX | -| Shared `Text` typography | `no-restricted-syntax` (raw `p`/`h1`–`h6`) + `seahaven/no-vp-error-outside-text` | lint | Governed TSX | -| Hooks correctness | `eslint-plugin-react-hooks` recommended (incl. `exhaustive-deps`) under zero-warnings | lint | Governed TS/TSX | -| Godfile ratchet (file length) | `scripts/governance-check.mjs` + `scripts/governance-baseline.json` | `governance` | `src/**`, `config/**` (non-test) | -| Changed-file maintainability | `scripts/governance-check.mjs` → ESLint (`complexity`, `max-lines-per-function`, `max-params`, `max-depth`) | `governance` | Changed TS/TSX vs base ref | -| Terraform import-plan contract | `npm run test:terraform-import-plan` → `scripts/test-terraform-import-plan-check.py` | `governance` + CI | Synthetic plan JSON + canonical maps | -| Terraform formatting/validation | `npm run test:terraform` → `scripts/terraform-validate.mjs` | `governance` + CI | `terraform/` | -| HCP run guard | `npm run test:hcp-run-guard` → `scripts/test-hcp-run-guard.py` | `governance` + CI | Workspace invariants + apply reconcile | -| CloudFront release verify | `npm run test:cloudfront-release-verify` → `scripts/test-verify-cloudfront-release.sh` | `governance` + CI | Stubbed aws/curl | -| GitHub workflow shell | `npm run test:github-workflows` → `scripts/check-github-workflows.sh` | `governance` + CI | `bash -n` + actionlint | -| G13 App/Terraform isolation | `python3 scripts/check_app_terraform_isolation.py` vs `GOVERNANCE_BASE` | `governance` + CI | Deployable app files vs `terraform/` | +| Gate | Command / rule source | Enforced by | Scope | +| ----------------------------------- | ----------------------------------------------------------------------------------------------------------- | ---------------------- | ---------------------------------------------- | +| Formatting | `npm run format:check` (Prettier) | `verify` + lint-staged | Whole repo | +| Lint, zero warnings | `npm run lint` → `eslint . --max-warnings=0` | `verify` + CI | Governed TS/TSX (`eslint.config.js`) | +| Type-check + production build | `npm run build` → `tsc -b && vite build` | `verify` + CI | Whole app | +| Unit tests | `npm test` → `vitest run` | `verify` + CI | `src/test/**`, `config/**/*.test.ts` | +| Conditional rendering (no `: null`) | `no-restricted-syntax` in `eslint.config.js` | lint | Governed TSX | +| Boolean-only JSX `&&` | `seahaven/no-non-boolean-jsx-and` (type-aware) in `eslint-rules/` | lint | Governed TSX | +| Shared `Text` typography | `no-restricted-syntax` (raw `p`/`h1`–`h6`) + `seahaven/no-vp-error-outside-text` | lint | Governed TSX | +| Hooks correctness | `eslint-plugin-react-hooks` recommended (incl. `exhaustive-deps`) under zero-warnings | lint | Governed TS/TSX | +| Godfile ratchet (file length) | `scripts/governance-check.mjs` + `scripts/governance-baseline.json` | `governance` | `src/**`, `config/**` (non-test) | +| Changed-file maintainability | `scripts/governance-check.mjs` → ESLint (`complexity`, `max-lines-per-function`, `max-params`, `max-depth`) | `governance` | Changed TS/TSX vs base ref | +| Terraform import-plan contract | `npm run test:terraform-import-plan` → `scripts/test-terraform-import-plan-check.py` | `governance` + CI | Synthetic plan JSON + canonical maps | +| Terraform formatting/validation | `npm run test:terraform` → `scripts/terraform-validate.mjs` | `governance` + CI | `terraform/live/dev`, `terraform/live/staging` | +| HCP run guard | `npm run test:hcp-run-guard` → `scripts/test-hcp-run-guard.py` | `governance` + CI | Workspace invariants + apply reconcile | +| CloudFront release verify | `npm run test:cloudfront-release-verify` → `scripts/test-verify-cloudfront-release.sh` | `governance` + CI | Stubbed aws/curl | +| GitHub workflow shell | `npm run test:github-workflows` → `scripts/check-github-workflows.sh` | `governance` + CI | `bash -n` + actionlint | +| G13 App/Terraform isolation | `python3 scripts/check_app_terraform_isolation.py` vs `GOVERNANCE_BASE` | `governance` + CI | Deployable app files vs `terraform/` | ## No-false-pass guarantees diff --git a/README.md b/README.md index 402dc97d..2aa29ace 100644 --- a/README.md +++ b/README.md @@ -34,9 +34,9 @@ graph LR TF[HCP Terraform] -->|bucket CloudFront IAM SSM| CF ``` -Dev and staging hosting live in one `terraform/` tree. GitHub -`.github/workflows/deploy-web.yaml` syncs content. The older pointer CD -(`deploy.yml`) stays in the tree until cutover. +Dev and staging hosting live in `terraform/live/dev` and +`terraform/live/staging`. GitHub `.github/workflows/deploy-web.yaml` syncs +content. The older pointer CD (`deploy.yml`) stays in the tree until cutover. Frontend stack: React 19, TypeScript, Vite, Tailwind CSS 4 + MUI, TanStack Query, React Router (via `@generouted/react-router`), React Hook Form + Zod, @@ -46,7 +46,7 @@ architecture plan for the keep/discard migration matrix). ## AWS Resources HCP workspace **`shoc-frontend-new-dev`** — account `396287094661`, region -`us-east-1`. Defined in [`terraform/`](terraform/). +`us-east-1`. Defined in [`terraform/live/dev`](terraform/live/dev). | Resource | Name | Purpose | | ----------------------- | ---------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | @@ -84,7 +84,7 @@ build otherwise. See [`.env.example`](.env.example), [`.env.development`](.env.development), and [`.env.production`](.env.production). Pinned hosting constants (domain, certificate ARN, hosted zone) live in -[`terraform/locals.tf`](terraform/locals.tf). +[`terraform/live/dev/main.tf`](terraform/live/dev/main.tf). ## Local Development diff --git a/scripts/terraform-validate.mjs b/scripts/terraform-validate.mjs index 335eafbf..70d6bf59 100644 --- a/scripts/terraform-validate.mjs +++ b/scripts/terraform-validate.mjs @@ -5,9 +5,9 @@ import { fileURLToPath } from "node:url"; const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."); const TERRAFORM = process.env.TERRAFORM_BIN || "terraform"; -const TERRAFORM_ROOT = path.join(ROOT, "terraform"); +const LIVE_ROOTS = ["terraform/live/dev", "terraform/live/staging"]; -function run(args, cwd = TERRAFORM_ROOT, env = process.env) { +function run(args, cwd = ROOT, env = process.env) { const result = spawnSync(TERRAFORM, args, { cwd, encoding: "utf8", @@ -24,17 +24,11 @@ function run(args, cwd = TERRAFORM_ROOT, env = process.env) { } } -run(["fmt", "-check", "-recursive", TERRAFORM_ROOT], ROOT); -// -backend=false never touches HCP state; -lockfile=readonly refuses to -// silently rewrite the committed provider lock. -run(["init", "-backend=false", "-input=false", "-lockfile=readonly", "-no-color"]); -run(["validate", "-no-color"], TERRAFORM_ROOT, { - ...process.env, - TF_VAR_environment: "dev", -}); -run(["validate", "-no-color"], TERRAFORM_ROOT, { - ...process.env, - TF_VAR_environment: "staging", -}); +run(["fmt", "-check", "-recursive", "terraform"]); +for (const liveRoot of LIVE_ROOTS) { + const abs = path.join(ROOT, liveRoot); + run(["init", "-backend=false", "-input=false", "-lockfile=readonly", "-no-color"], abs); + run(["validate", "-no-color"], abs); +} -console.log("Terraform formatting and validation passed for terraform/."); +console.log("Terraform formatting and validation passed for terraform/live/dev and terraform/live/staging."); diff --git a/scripts/terraform_import_plan_resources.py b/scripts/terraform_import_plan_resources.py index 2ee92442..66247570 100755 --- a/scripts/terraform_import_plan_resources.py +++ b/scripts/terraform_import_plan_resources.py @@ -1,7 +1,7 @@ """Canonical frontend Terraform ownership and import-ID maps. Dev is already in HCP state. Staging constants authorize the first import of -the live CDK stack onto the same terraform/ root. +the live CDK stack onto terraform/live/staging. """ COMMON_RESOURCES = { diff --git a/scripts/test-terraform-import-plan-check.py b/scripts/test-terraform-import-plan-check.py index ea87c86d..7ba943c3 100755 --- a/scripts/test-terraform-import-plan-check.py +++ b/scripts/test-terraform-import-plan-check.py @@ -279,7 +279,7 @@ class ImportPlanCheckerTests(unittest.TestCase): def test_cloudfront_function_source_matches_exact_nine_line_join(self) -> None: source = ( - REPOSITORY / "terraform/modules/environment-owned/main.tf" + REPOSITORY / "terraform/live/modules/environment-owned/main.tf" ).read_text(encoding="utf-8") expected = """ spa_rewrite_code = join("\\n", [ "function handler(event) {", @@ -294,31 +294,31 @@ class ImportPlanCheckerTests(unittest.TestCase): ])""" self.assertIn(expected, source) - def test_single_terraform_root(self) -> None: - terraform_root = REPOSITORY / "terraform" - self.assertTrue((terraform_root / "versions.tf").is_file()) - self.assertTrue((terraform_root / "main.tf").is_file()) - self.assertFalse((terraform_root / "live").exists()) + def test_live_roots_are_not_flattened(self) -> None: + live = REPOSITORY / "terraform" / "live" + self.assertTrue((live / "dev" / "versions.tf").is_file()) + self.assertTrue((live / "dev" / "main.tf").is_file()) + self.assertTrue((live / "staging" / "versions.tf").is_file()) + self.assertTrue((live / "staging" / "main.tf").is_file()) + self.assertTrue((live / "modules" / "environment-owned" / "main.tf").is_file()) + self.assertFalse((REPOSITORY / "terraform" / "versions.tf").exists()) + self.assertFalse((REPOSITORY / "terraform" / "main.tf").exists()) def test_dev_adoption_complete_is_pinned_in_locals(self) -> None: - source = (REPOSITORY / "terraform/locals.tf").read_text(encoding="utf-8") - self.assertRegex( - source, - r"dev = \{[\s\S]*?adoption_complete\s+= true", + dev = (REPOSITORY / "terraform/live/dev/main.tf").read_text(encoding="utf-8") + staging = (REPOSITORY / "terraform/live/staging/main.tf").read_text( + encoding="utf-8" ) - self.assertRegex( - source, - r"staging = \{[\s\S]*?adoption_complete\s+= false", - ) - variables = (REPOSITORY / "terraform/variables.tf").read_text(encoding="utf-8") - self.assertIn('variable "environment"', variables) - self.assertNotIn('variable "adoption_complete"', variables) - self.assertNotIn('variable "release_version_label"', variables) + self.assertRegex(dev, r"adoption_complete\s+= true") + self.assertRegex(staging, r"adoption_complete\s+= false") + self.assertNotIn('variable "adoption_complete"', dev) + self.assertNotIn('variable "environment"', dev) + self.assertNotIn('variable "release_version_label"', dev) def test_managed_modules_use_direct_pinned_inputs(self) -> None: - source = (REPOSITORY / "terraform/main.tf").read_text(encoding="utf-8") + source = (REPOSITORY / "terraform/live/dev/main.tf").read_text(encoding="utf-8") expected = { - "hosted_zone_id": "local.stack.hosted_zone_id", + "hosted_zone_id": "local.hosted_zone_id", "certificate_arn": "local.certificate_arn", "github_oidc_provider_arn": "local.github_oidc_arn", "cache_policy_id": "local.cache_policy_id", @@ -459,7 +459,7 @@ class ImportPlanCheckerTests(unittest.TestCase): def test_github_deploy_policy_is_bucket_root_sync(self) -> None: source = ( - REPOSITORY / "terraform/modules/environment-owned/main.tf" + REPOSITORY / "terraform/live/modules/environment-owned/main.tf" ).read_text(encoding="utf-8") document = source.split('data "aws_iam_policy_document" "github_deploy" {', 1)[1] document = document.split("resource ", 1)[0] diff --git a/scripts/test_check_app_terraform_isolation.py b/scripts/test_check_app_terraform_isolation.py index b5672d54..22ba06ac 100644 --- a/scripts/test_check_app_terraform_isolation.py +++ b/scripts/test_check_app_terraform_isolation.py @@ -13,8 +13,8 @@ class IsolationTests(unittest.TestCase): self.assertIsNone( isolation_violation( [ - "terraform/main.tf", - "terraform/README.md", + "terraform/live/dev/main.tf", + "terraform/live/README.md", ] ) ) @@ -35,7 +35,7 @@ class IsolationTests(unittest.TestCase): self.assertIsNone( isolation_violation( [ - "terraform/modules/environment-owned/main.tf", + "terraform/live/modules/environment-owned/main.tf", ".github/workflows/deploy-web.yaml", "QUALITY_GATES.md", "scripts/governance-check.mjs", @@ -48,21 +48,21 @@ class IsolationTests(unittest.TestCase): def test_mixed_src_and_terraform_fails(self) -> None: violation = isolation_violation( [ - "terraform/main.tf", + "terraform/live/dev/main.tf", "src/app/routes.tsx", ] ) self.assertIsNotNone(violation) terraform_files, app_files = violation or ([], []) - self.assertEqual(terraform_files, ["terraform/main.tf"]) + self.assertEqual(terraform_files, ["terraform/live/dev/main.tf"]) self.assertEqual(app_files, ["src/app/routes.tsx"]) def test_mixed_vite_config_and_terraform_fails(self) -> None: - violation = isolation_violation(["terraform/variables.tf", "vite.config.ts"]) + violation = isolation_violation(["terraform/live/dev/versions.tf", "vite.config.ts"]) self.assertIsNotNone(violation) def test_mixed_env_and_terraform_fails(self) -> None: - violation = isolation_violation(["terraform/locals.tf", ".env.production"]) + violation = isolation_violation(["terraform/live/dev/main.tf", ".env.production"]) self.assertIsNotNone(violation) diff --git a/terraform/README.md b/terraform/README.md index dd0731b5..d44815c4 100644 --- a/terraform/README.md +++ b/terraform/README.md @@ -1,6 +1,6 @@ # Frontend Terraform (SPA CD) -One `terraform/` root for `dev` and `staging` in AWS account `396287094661`. +`terraform/live/dev` and `terraform/live/staging` in AWS account `396287094661`. HCP Terraform owns the bucket, CloudFront, DNS, and the GitHub deploy role. GitHub Actions owns content: `.github/workflows/deploy-web.yaml` syncs `dist/` to the bucket root and invalidates `/*`. @@ -9,16 +9,19 @@ Creating, formatting, initializing with `-backend=false`, and validating these files does not authorize an AWS, HCP Terraform, GitHub, or deployment mutation. Live cutover waits for an explicit greenlight. +Do not collapse these roots into one `terraform/` tree in this PR. Flattening +retargets two live HCP working directories and is its own change. + ## Fixed targets -| | dev | staging | -| ----------------------- | ------------------------------------ | ---------------------------------------- | -| Site | `dev.seahaven.com` | `staging.seahaven.com` | -| Bucket | `seahaven-shoc-frontend-dev` | `seahaven-shoc-frontend-staging` | -| Distribution | `E2CWLM1AFB964P` | `E2JDVEZ6EGD49J` | -| Deploy role | `githubdeploy-shoc-frontend-new-dev` | `githubdeploy-shoc-frontend-new-staging` | -| HCP workspace | `shoc-frontend-new-dev` | `shoc-frontend-new-staging` | -| Workspace `environment` | `dev` | `staging` | +| | dev | staging | +| ------------- | ------------------------------------ | ---------------------------------------- | +| Site | `dev.seahaven.com` | `staging.seahaven.com` | +| Bucket | `seahaven-shoc-frontend-dev` | `seahaven-shoc-frontend-staging` | +| Distribution | `E2CWLM1AFB964P` | `E2JDVEZ6EGD49J` | +| Deploy role | `githubdeploy-shoc-frontend-new-dev` | `githubdeploy-shoc-frontend-new-staging` | +| HCP workspace | `shoc-frontend-new-dev` | `shoc-frontend-new-staging` | +| Working dir | `terraform/live/dev` | `terraform/live/staging` | There is no prod CloudFront in this round. Do not create `shoc-frontend-new-prod`. @@ -35,15 +38,17 @@ GetParameter on those two names. OIDC trust is `environment:` plus `job_workflow_ref` for `.github/workflows/deploy-web.yaml` at `refs/heads/main` and `refs/tags/v*`. -`adoption_complete` is pinned per stack in `locals.tf`. It is not a workspace +`adoption_complete` is pinned in each live root. It is not a workspace variable. ## Local checks (no apply) ```bash -terraform -chdir=terraform fmt -check -recursive -terraform -chdir=terraform init -backend=false -lockfile=readonly -TF_VAR_environment=dev terraform -chdir=terraform validate +terraform fmt -check -recursive terraform +terraform -chdir=terraform/live/dev init -backend=false -lockfile=readonly +terraform -chdir=terraform/live/dev validate +terraform -chdir=terraform/live/staging init -backend=false -lockfile=readonly +terraform -chdir=terraform/live/staging validate python3 scripts/test-terraform-import-plan-check.py python3 scripts/test_check_app_terraform_isolation.py bash scripts/test-verify-cloudfront-release.sh @@ -58,13 +63,12 @@ create an HCP run or touch AWS. ## Cutover (greenlight only) -1. Point both HCP workspaces at working directory `terraform/` with tag - `app:shoc-frontend-new`. Dev VCS branch `main`. Staging tag regex +1. Keep HCP working directories `terraform/live/dev` and + `terraform/live/staging`. Dev VCS branch `main`. Staging tag regex `^v[0-9]+\.[0-9]+\.[0-9]+-staging$`. -2. Set workspace variable `environment` to `dev` or `staging`. -3. Auto-apply off. Apply the origin-path move for **dev** before any +2. Auto-apply off. Apply the origin-path move for **dev** before any `--delete` root sync. -4. Create GitHub Environment `dev` (staging already exists). Set +3. Create GitHub Environment `dev` (staging already exists). Set `DEPLOY_ROLE_ARN` on each. -5. Enable `deploy-web.yaml`. Then retire `deploy.yml`, `TF_API_TOKEN`, and +4. Enable `deploy-web.yaml`. Then retire `deploy.yml`, `TF_API_TOKEN`, and `TERRAFORM_CONTENT_CD_ENABLED`. diff --git a/terraform/live/README.md b/terraform/live/README.md new file mode 100644 index 00000000..aa85eec0 --- /dev/null +++ b/terraform/live/README.md @@ -0,0 +1,8 @@ +# Live Terraform roots + +`live/dev/` is the adopted HCP workspace `shoc-frontend-new-dev`. +`live/staging/` is `shoc-frontend-new-staging` (`adoption_complete = false`). + +Do not collapse these into one `terraform/` root in the same PR as application +CD. Flattening retargets two live HCP working directories and belongs in its +own change. diff --git a/terraform/.terraform.lock.hcl b/terraform/live/dev/.terraform.lock.hcl similarity index 100% rename from terraform/.terraform.lock.hcl rename to terraform/live/dev/.terraform.lock.hcl diff --git a/terraform/imports.tf b/terraform/live/dev/imports.tf similarity index 65% rename from terraform/imports.tf rename to terraform/live/dev/imports.tf index 554b6761..8f5e3e3f 100644 --- a/terraform/imports.tf +++ b/terraform/live/dev/imports.tf @@ -1,64 +1,64 @@ import { to = module.environment_owned.aws_s3_bucket.site - id = local.stack.bucket_name + id = local.bucket_name } import { to = module.environment_owned.aws_s3_bucket_public_access_block.site - id = local.stack.bucket_name + id = local.bucket_name } import { to = module.environment_owned.aws_s3_bucket_ownership_controls.site - id = local.stack.bucket_name + id = local.bucket_name } import { to = module.environment_owned.aws_s3_bucket_server_side_encryption_configuration.site - id = local.stack.bucket_name + id = local.bucket_name } import { to = module.environment_owned.aws_s3_bucket_versioning.site - id = local.stack.bucket_name + id = local.bucket_name } import { to = module.environment_owned.aws_s3_bucket_policy.site - id = local.stack.bucket_name + id = local.bucket_name } import { to = module.environment_owned.aws_cloudfront_distribution.site - id = local.stack.distribution_id + id = local.distribution_id } import { to = module.environment_owned.aws_cloudfront_origin_access_control.site - id = local.stack.oac_id + id = local.oac_id } import { to = module.environment_owned.aws_cloudfront_function.spa_rewrite - id = local.stack.function_name + id = local.function_name } import { to = module.environment_owned.aws_route53_record.site_a - id = "${local.stack.hosted_zone_id}_${local.stack.domain_name}_A" + id = "${local.hosted_zone_id}_${local.domain_name}_A" } import { to = module.environment_owned.aws_route53_record.site_aaaa - id = "${local.stack.hosted_zone_id}_${local.stack.domain_name}_AAAA" + id = "${local.hosted_zone_id}_${local.domain_name}_AAAA" } import { to = module.environment_owned.aws_iam_role.github_deploy - id = local.stack.deploy_role_name + id = local.deploy_role_name } import { to = module.environment_owned.aws_iam_role_policy.github_deploy - id = "${local.stack.deploy_role_name}:${local.stack.inline_policy}" + id = "${local.deploy_role_name}:${local.inline_policy}" } diff --git a/terraform/live/dev/main.tf b/terraform/live/dev/main.tf new file mode 100644 index 00000000..36ab5002 --- /dev/null +++ b/terraform/live/dev/main.tf @@ -0,0 +1,95 @@ +locals { + # Controlled ownership transfer. Pinned in code, never a workspace variable. + adoption_complete = true + + environment = "dev" + workspace_name = "shoc-frontend-new-dev" + github_repo = "Sea-Haven-Industries/shoc-frontend-new" + aws_account_id = "396287094661" + aws_region = "us-east-1" + bucket_name = "seahaven-shoc-frontend-dev" + distribution_id = "E2CWLM1AFB964P" + oac_id = "E30VSIK87N8H64" + oac_name = "shocfrontenddevDistributionOrigin1S3OriginAccessControlDFC82620" + origin_id = "shocfrontenddevDistributionOrigin10CCD0EE1" + function_name = "us-east-1shocfrontenddevSpaRewrite58674DB8" + domain_name = "dev.seahaven.com" + hosted_zone_id = "Z07671212N75U4YLPWZR8" + certificate_arn = ( + "arn:aws:acm:us-east-1:396287094661:certificate/2b78e74f-7b65-4b82-a413-7a498b102f00" + ) + github_oidc_arn = ( + "arn:aws:iam::396287094661:oidc-provider/token.actions.githubusercontent.com" + ) + deploy_role_name = "githubdeploy-shoc-frontend-new-dev" + inline_policy = "GithubDeployRoleDefaultPolicyE8F540D1" + stack_name = "shoc-frontend-dev" + cache_policy_id = "658327ea-f89d-4fab-a63d-7e88639e58f6" + permissions_boundary_arn = ( + "arn:aws:iam::396287094661:policy/shoc-frontend-new-dev-deploy-boundary" + ) + bucket_auto_delete_helper_role_arn = ( + "arn:aws:iam::396287094661:role/shoc-frontend-dev-CustomS3AutoDeleteObjectsCustomRe-dmSDIY8EH7KV" + ) + legacy_tags = { + Environment = "dev" + ManagedBy = "cdk" + Project = "shoc-frontend" + } + legacy_bucket_tags = merge(local.legacy_tags, { + "aws-cdk:auto-delete-objects" = "true" + }) + terraform_tags = { + Environment = "dev" + ManagedBy = "terraform" + Ownership = "terraform" + Project = "shoc-frontend" + } + manager_tag = { + HcpTerraformWorkspace = local.workspace_name + } +} + +module "inventory" { + source = "../modules/environment-inventory" + + aws_account_id = local.aws_account_id + aws_region = local.aws_region + hosted_zone_name = local.domain_name + expected_hosted_zone_id = local.hosted_zone_id + certificate_domain = "*.seahaven.com" + expected_certificate_arn = local.certificate_arn + expected_github_oidc_provider_arn = local.github_oidc_arn + expected_cache_policy_id = local.cache_policy_id +} + +module "environment_owned" { + source = "../modules/environment-owned" + + environment = local.environment + adoption_complete = local.adoption_complete + aws_account_id = local.aws_account_id + aws_region = local.aws_region + github_repo = local.github_repo + bucket_name = local.bucket_name + distribution_id = local.distribution_id + origin_access_control_name = local.oac_name + origin_access_control_description = "" + origin_id = local.origin_id + function_name = local.function_name + domain_name = local.domain_name + hosted_zone_id = local.hosted_zone_id + certificate_arn = local.certificate_arn + cache_policy_id = local.cache_policy_id + github_oidc_provider_arn = local.github_oidc_arn + deploy_role_name = local.deploy_role_name + deploy_inline_policy_name = local.inline_policy + deploy_permissions_boundary_arn = local.permissions_boundary_arn + cloudformation_stack_name = local.stack_name + bucket_auto_delete_helper_role_arn = local.bucket_auto_delete_helper_role_arn + pre_adoption_tags = local.legacy_tags + pre_adoption_bucket_tags = local.legacy_bucket_tags + ownership_tags = local.terraform_tags + pre_adoption_deploy_role_tags = merge(local.legacy_tags, local.manager_tag) + post_adoption_deploy_role_tags = merge(local.terraform_tags, local.manager_tag) +} diff --git a/terraform/outputs.tf b/terraform/live/dev/outputs.tf similarity index 100% rename from terraform/outputs.tf rename to terraform/live/dev/outputs.tf diff --git a/terraform/providers.tf b/terraform/live/dev/providers.tf similarity index 100% rename from terraform/providers.tf rename to terraform/live/dev/providers.tf diff --git a/terraform/versions.tf b/terraform/live/dev/versions.tf similarity index 74% rename from terraform/versions.tf rename to terraform/live/dev/versions.tf index 73acea4d..b8a94b0c 100644 --- a/terraform/versions.tf +++ b/terraform/live/dev/versions.tf @@ -1,18 +1,19 @@ terraform { required_version = ">= 1.14.0, < 2.0.0" + cloud { + organization = "seahaven" + + workspaces { + project = "seahaven-external-dev" + name = "shoc-frontend-new-dev" + } + } + required_providers { aws = { source = "hashicorp/aws" version = "~> 6.57" } } - - cloud { - organization = "seahaven" - - workspaces { - tags = ["app:shoc-frontend-new"] - } - } } diff --git a/terraform/modules/environment-inventory/main.tf b/terraform/live/modules/environment-inventory/main.tf similarity index 100% rename from terraform/modules/environment-inventory/main.tf rename to terraform/live/modules/environment-inventory/main.tf diff --git a/terraform/modules/environment-inventory/outputs.tf b/terraform/live/modules/environment-inventory/outputs.tf similarity index 100% rename from terraform/modules/environment-inventory/outputs.tf rename to terraform/live/modules/environment-inventory/outputs.tf diff --git a/terraform/modules/environment-inventory/variables.tf b/terraform/live/modules/environment-inventory/variables.tf similarity index 100% rename from terraform/modules/environment-inventory/variables.tf rename to terraform/live/modules/environment-inventory/variables.tf diff --git a/terraform/modules/environment-owned/main.tf b/terraform/live/modules/environment-owned/main.tf similarity index 100% rename from terraform/modules/environment-owned/main.tf rename to terraform/live/modules/environment-owned/main.tf diff --git a/terraform/modules/environment-owned/outputs.tf b/terraform/live/modules/environment-owned/outputs.tf similarity index 100% rename from terraform/modules/environment-owned/outputs.tf rename to terraform/live/modules/environment-owned/outputs.tf diff --git a/terraform/modules/environment-owned/variables.tf b/terraform/live/modules/environment-owned/variables.tf similarity index 100% rename from terraform/modules/environment-owned/variables.tf rename to terraform/live/modules/environment-owned/variables.tf diff --git a/terraform/live/staging/.terraform.lock.hcl b/terraform/live/staging/.terraform.lock.hcl new file mode 100644 index 00000000..7f171232 --- /dev/null +++ b/terraform/live/staging/.terraform.lock.hcl @@ -0,0 +1,30 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "6.62.0" + constraints = "~> 6.57" + hashes = [ + "h1:4qcuRkosNKYxV2y69uJ6zAfTEO1Op04L4KUuWBrUvBo=", + "h1:OthB9UeoBgmy348EpDjs5GDGk6p6UxAMQD5cXn7u9Ho=", + "h1:lTKd2c1EunGxt2XROLgEeSXA2Jk+WiiG9BTcp+L/0xY=", + "h1:nWSI/kgPk9aieiY01TEKOGXRX3+L889GSkEq0SMCL6E=", + "h1:yOSEz5G8b/n5uhFCZ0gbEsKkAQATtVuhXJEXR3OM5qs=", + "zh:35a9e4bc6fd622c5a99561b882025f2745f1256bbf1a8da8d6b39319b75ae0b5", + "zh:405927d470ff16201e40aa0fa2d0ab1de477360a0926d20719cd029179682ecd", + "zh:4ab7866593a90bcf18f066b0092a209b9f42852acd783b504031ae74cb6f7010", + "zh:5b477f313fc511648a4eed9f9085d0778414835896256ab14296d2345b7070e3", + "zh:87de70bc99751f94262cec2260d972555a98f588aa3a613e417438f88a1182df", + "zh:88f02a8ff07f00da4ffb3bee9e8ae25588e3a0a92633c625c1c2a63bac00a844", + "zh:8d8596257453357c9f3fccaa7d2f04299e8d35b16f364adb8a2c829143a9c090", + "zh:953c8e15fa9c12c081f17d66cf45246d032fa23bee33f264dc82242afdb98bc2", + "zh:9a7dd903e5e9b2b0cc1317ad2d2692e0ddf05ae2a5aaee20ec5dd1db456711b7", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:a859154c75c1088d098a481f1ebb259720c5a2ad87781364abf556a741e5adb7", + "zh:b8d1e72ad39d5864118f64dd3273424ab637d34b3ff8dd3dfeb4aef9d458587f", + "zh:c3666fcfc7b131f5282d4e7249fa68c3a21665757888aa02bbaf6be1cd036bba", + "zh:c6b8ff94b3f49bf85fc087381cfe1b271c5b01cf74cf140d58aa500be7138913", + "zh:d8143d790e9dd77b8e2f9168e4a33ad6d064dc4b082a0196636b182105aaed14", + "zh:fa41eca042f377eb2741e95b36609c1de5b0cd675cd4e3e30c709497cb94db02", + ] +} diff --git a/terraform/live/staging/imports.tf b/terraform/live/staging/imports.tf new file mode 100644 index 00000000..8f5e3e3f --- /dev/null +++ b/terraform/live/staging/imports.tf @@ -0,0 +1,64 @@ +import { + to = module.environment_owned.aws_s3_bucket.site + id = local.bucket_name +} + +import { + to = module.environment_owned.aws_s3_bucket_public_access_block.site + id = local.bucket_name +} + +import { + to = module.environment_owned.aws_s3_bucket_ownership_controls.site + id = local.bucket_name +} + +import { + to = module.environment_owned.aws_s3_bucket_server_side_encryption_configuration.site + id = local.bucket_name +} + +import { + to = module.environment_owned.aws_s3_bucket_versioning.site + id = local.bucket_name +} + +import { + to = module.environment_owned.aws_s3_bucket_policy.site + id = local.bucket_name +} + +import { + to = module.environment_owned.aws_cloudfront_distribution.site + id = local.distribution_id +} + +import { + to = module.environment_owned.aws_cloudfront_origin_access_control.site + id = local.oac_id +} + +import { + to = module.environment_owned.aws_cloudfront_function.spa_rewrite + id = local.function_name +} + +import { + to = module.environment_owned.aws_route53_record.site_a + id = "${local.hosted_zone_id}_${local.domain_name}_A" +} + +import { + to = module.environment_owned.aws_route53_record.site_aaaa + id = "${local.hosted_zone_id}_${local.domain_name}_AAAA" +} + +import { + to = module.environment_owned.aws_iam_role.github_deploy + id = local.deploy_role_name +} + +import { + to = module.environment_owned.aws_iam_role_policy.github_deploy + id = "${local.deploy_role_name}:${local.inline_policy}" +} diff --git a/terraform/live/staging/main.tf b/terraform/live/staging/main.tf new file mode 100644 index 00000000..6d26831c --- /dev/null +++ b/terraform/live/staging/main.tf @@ -0,0 +1,95 @@ +locals { + # Staging is not fully adopted. Pinned in code, never a workspace variable. + adoption_complete = false + + environment = "staging" + workspace_name = "shoc-frontend-new-staging" + github_repo = "Sea-Haven-Industries/shoc-frontend-new" + aws_account_id = "396287094661" + aws_region = "us-east-1" + bucket_name = "seahaven-shoc-frontend-staging" + distribution_id = "E2JDVEZ6EGD49J" + oac_id = "E1PF5R6QQNBZAI" + oac_name = "shocfrontendstagingDistributOrigin1S3OriginAccessControl82B1C17D" + origin_id = "shocfrontendstagingDistributionOrigin16E4628FC" + function_name = "us-east-1shocfrontendstagingSpaRewriteE9C0CBDA" + domain_name = "staging.seahaven.com" + hosted_zone_id = "Z02602739VQWBWCAGXP4" + certificate_arn = ( + "arn:aws:acm:us-east-1:396287094661:certificate/2b78e74f-7b65-4b82-a413-7a498b102f00" + ) + github_oidc_arn = ( + "arn:aws:iam::396287094661:oidc-provider/token.actions.githubusercontent.com" + ) + deploy_role_name = "githubdeploy-shoc-frontend-new-staging" + inline_policy = "GithubDeployRoleDefaultPolicyE8F540D1" + stack_name = "shoc-frontend-staging" + cache_policy_id = "658327ea-f89d-4fab-a63d-7e88639e58f6" + permissions_boundary_arn = ( + "arn:aws:iam::396287094661:policy/shoc-frontend-new-staging-deploy-boundary" + ) + bucket_auto_delete_helper_role_arn = ( + "arn:aws:iam::396287094661:role/shoc-frontend-staging-CustomS3AutoDeleteObjectsCust-QbMDqZbl7YQ3" + ) + legacy_tags = { + Environment = "staging" + ManagedBy = "cdk" + Project = "shoc-frontend" + } + legacy_bucket_tags = merge(local.legacy_tags, { + "aws-cdk:auto-delete-objects" = "true" + }) + terraform_tags = { + Environment = "staging" + ManagedBy = "terraform" + Ownership = "terraform" + Project = "shoc-frontend" + } + manager_tag = { + HcpTerraformWorkspace = local.workspace_name + } +} + +module "inventory" { + source = "../modules/environment-inventory" + + aws_account_id = local.aws_account_id + aws_region = local.aws_region + hosted_zone_name = local.domain_name + expected_hosted_zone_id = local.hosted_zone_id + certificate_domain = "*.seahaven.com" + expected_certificate_arn = local.certificate_arn + expected_github_oidc_provider_arn = local.github_oidc_arn + expected_cache_policy_id = local.cache_policy_id +} + +module "environment_owned" { + source = "../modules/environment-owned" + + environment = local.environment + adoption_complete = local.adoption_complete + aws_account_id = local.aws_account_id + aws_region = local.aws_region + github_repo = local.github_repo + bucket_name = local.bucket_name + distribution_id = local.distribution_id + origin_access_control_name = local.oac_name + origin_access_control_description = "" + origin_id = local.origin_id + function_name = local.function_name + domain_name = local.domain_name + hosted_zone_id = local.hosted_zone_id + certificate_arn = local.certificate_arn + cache_policy_id = local.cache_policy_id + github_oidc_provider_arn = local.github_oidc_arn + deploy_role_name = local.deploy_role_name + deploy_inline_policy_name = local.inline_policy + deploy_permissions_boundary_arn = local.permissions_boundary_arn + cloudformation_stack_name = local.stack_name + bucket_auto_delete_helper_role_arn = local.bucket_auto_delete_helper_role_arn + pre_adoption_tags = local.legacy_tags + pre_adoption_bucket_tags = local.legacy_bucket_tags + ownership_tags = local.terraform_tags + pre_adoption_deploy_role_tags = merge(local.legacy_tags, local.manager_tag) + post_adoption_deploy_role_tags = merge(local.terraform_tags, local.manager_tag) +} diff --git a/terraform/live/staging/outputs.tf b/terraform/live/staging/outputs.tf new file mode 100644 index 00000000..54bbb7c4 --- /dev/null +++ b/terraform/live/staging/outputs.tf @@ -0,0 +1,19 @@ +output "bucket_name" { + value = module.environment_owned.bucket_name + description = "SPA origin bucket name." +} + +output "distribution_id" { + value = module.environment_owned.distribution_id + description = "CloudFront distribution ID." +} + +output "deploy_role_arn" { + value = module.environment_owned.deploy_role_arn + description = "GitHub Actions deploy role ARN." +} + +output "origin_id" { + value = module.environment_owned.origin_id + description = "CloudFront origin ID for the bucket-root SPA." +} diff --git a/terraform/live/staging/providers.tf b/terraform/live/staging/providers.tf new file mode 100644 index 00000000..b6c81d54 --- /dev/null +++ b/terraform/live/staging/providers.tf @@ -0,0 +1,3 @@ +provider "aws" { + region = local.aws_region +} diff --git a/terraform/live/staging/versions.tf b/terraform/live/staging/versions.tf new file mode 100644 index 00000000..8ddf51a2 --- /dev/null +++ b/terraform/live/staging/versions.tf @@ -0,0 +1,19 @@ +terraform { + required_version = ">= 1.14.0, < 2.0.0" + + cloud { + organization = "seahaven" + + workspaces { + project = "seahaven-external-dev" + name = "shoc-frontend-new-staging" + } + } + + required_providers { + aws = { + source = "hashicorp/aws" + version = "~> 6.57" + } + } +} diff --git a/terraform/locals.tf b/terraform/locals.tf deleted file mode 100644 index 7786ce24..00000000 --- a/terraform/locals.tf +++ /dev/null @@ -1,74 +0,0 @@ -locals { - aws_account_id = "396287094661" - aws_region = "us-east-1" - github_repo = "Sea-Haven-Industries/shoc-frontend-new" - cache_policy_id = "658327ea-f89d-4fab-a63d-7e88639e58f6" - github_oidc_arn = ( - "arn:aws:iam::396287094661:oidc-provider/token.actions.githubusercontent.com" - ) - certificate_arn = ( - "arn:aws:acm:us-east-1:396287094661:certificate/2b78e74f-7b65-4b82-a413-7a498b102f00" - ) - - stacks = { - dev = { - # Pinned in code, never a workspace variable. - adoption_complete = true - bucket_name = "seahaven-shoc-frontend-dev" - distribution_id = "E2CWLM1AFB964P" - oac_id = "E30VSIK87N8H64" - oac_name = "shocfrontenddevDistributionOrigin1S3OriginAccessControlDFC82620" - origin_id = "shocfrontenddevDistributionOrigin10CCD0EE1" - function_name = "us-east-1shocfrontenddevSpaRewrite58674DB8" - domain_name = "dev.seahaven.com" - hosted_zone_id = "Z07671212N75U4YLPWZR8" - deploy_role_name = "githubdeploy-shoc-frontend-new-dev" - inline_policy = "GithubDeployRoleDefaultPolicyE8F540D1" - workspace_name = "shoc-frontend-new-dev" - stack_name = "shoc-frontend-dev" - permissions_boundary_arn = ( - "arn:aws:iam::396287094661:policy/shoc-frontend-new-dev-deploy-boundary" - ) - bucket_auto_delete_helper_role_arn = ( - "arn:aws:iam::396287094661:role/shoc-frontend-dev-CustomS3AutoDeleteObjectsCustomRe-dmSDIY8EH7KV" - ) - } - staging = { - adoption_complete = false - bucket_name = "seahaven-shoc-frontend-staging" - distribution_id = "E2JDVEZ6EGD49J" - oac_id = "E1PF5R6QQNBZAI" - oac_name = "shocfrontendstagingDistributOrigin1S3OriginAccessControl82B1C17D" - origin_id = "shocfrontendstagingDistributionOrigin16E4628FC" - function_name = "us-east-1shocfrontendstagingSpaRewriteE9C0CBDA" - domain_name = "staging.seahaven.com" - hosted_zone_id = "Z02602739VQWBWCAGXP4" - deploy_role_name = "githubdeploy-shoc-frontend-new-staging" - inline_policy = "GithubDeployRoleDefaultPolicyE8F540D1" - workspace_name = "shoc-frontend-new-staging" - stack_name = "shoc-frontend-staging" - permissions_boundary_arn = ( - "arn:aws:iam::396287094661:policy/shoc-frontend-new-staging-deploy-boundary" - ) - bucket_auto_delete_helper_role_arn = ( - "arn:aws:iam::396287094661:role/shoc-frontend-staging-CustomS3AutoDeleteObjectsCust-QbMDqZbl7YQ3" - ) - } - } - - stack = local.stacks[var.environment] - legacy_tags = { - Environment = var.environment - ManagedBy = "cdk" - Project = "shoc-frontend" - } - terraform_tags = { - Environment = var.environment - ManagedBy = "terraform" - Ownership = "terraform" - Project = "shoc-frontend" - } - manager_tag = { - HcpTerraformWorkspace = local.stack.workspace_name - } -} diff --git a/terraform/main.tf b/terraform/main.tf deleted file mode 100644 index bbc6bca3..00000000 --- a/terraform/main.tf +++ /dev/null @@ -1,45 +0,0 @@ -module "inventory" { - source = "./modules/environment-inventory" - - aws_account_id = local.aws_account_id - aws_region = local.aws_region - hosted_zone_name = local.stack.domain_name - expected_hosted_zone_id = local.stack.hosted_zone_id - certificate_domain = "*.seahaven.com" - expected_certificate_arn = local.certificate_arn - expected_github_oidc_provider_arn = local.github_oidc_arn - expected_cache_policy_id = local.cache_policy_id -} - -module "environment_owned" { - source = "./modules/environment-owned" - - environment = var.environment - adoption_complete = local.stack.adoption_complete - aws_account_id = local.aws_account_id - aws_region = local.aws_region - github_repo = local.github_repo - bucket_name = local.stack.bucket_name - distribution_id = local.stack.distribution_id - origin_access_control_name = local.stack.oac_name - origin_access_control_description = "" - origin_id = local.stack.origin_id - function_name = local.stack.function_name - domain_name = local.stack.domain_name - hosted_zone_id = local.stack.hosted_zone_id - certificate_arn = local.certificate_arn - cache_policy_id = local.cache_policy_id - github_oidc_provider_arn = local.github_oidc_arn - deploy_role_name = local.stack.deploy_role_name - deploy_inline_policy_name = local.stack.inline_policy - deploy_permissions_boundary_arn = local.stack.permissions_boundary_arn - cloudformation_stack_name = local.stack.stack_name - bucket_auto_delete_helper_role_arn = local.stack.bucket_auto_delete_helper_role_arn - pre_adoption_tags = local.legacy_tags - pre_adoption_bucket_tags = merge(local.legacy_tags, { - "aws-cdk:auto-delete-objects" = "true" - }) - ownership_tags = local.terraform_tags - pre_adoption_deploy_role_tags = merge(local.legacy_tags, local.manager_tag) - post_adoption_deploy_role_tags = merge(local.terraform_tags, local.manager_tag) -} diff --git a/terraform/variables.tf b/terraform/variables.tf deleted file mode 100644 index 9bc517d7..00000000 --- a/terraform/variables.tf +++ /dev/null @@ -1,9 +0,0 @@ -variable "environment" { - description = "Workspace environment. HCP Terraform sets this per workspace." - type = string - - validation { - condition = contains(["dev", "staging"], var.environment) - error_message = "environment must be one of: dev, staging." - } -}