From 3c9c3d640da4b0bb7093a4614d2eb7e310db871c Mon Sep 17 00:00:00 2001 From: iammukeshm Date: Fri, 5 Jun 2026 23:36:01 +0530 Subject: [PATCH 1/2] fix(deploy): isolate Terraform data dir per env/region (TF_DATA_DIR) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit app_stack/ kept Terraform's working data in one shared .terraform, and each env/region was selected via `init -reconfigure -backend-config=...`. So two runs against different backends — e.g. an ap-south-1 deploy while us-east-1 was being destroyed — clobbered each other's backend pointer mid-run: the deploy applied fine but its closing `terraform output` then read the wrong region's state ("Output not found") and silently skipped the SPA publish. Fix: deploy.sh/deploy.ps1/destroy.ps1 now set TF_DATA_DIR to an absolute app_stack/.terraform/- before any terraform call, giving each env/region its own backend pointer, providers, and modules. Concurrent runs across regions can no longer collide — no "run them one at a time" caveat. Co-Authored-By: Claude Opus 4.8 (1M context) --- deploy/terraform/apps/starter/deploy.ps1 | 7 +++++++ deploy/terraform/apps/starter/deploy.sh | 7 +++++++ deploy/terraform/apps/starter/destroy.ps1 | 4 ++++ 3 files changed, 18 insertions(+) diff --git a/deploy/terraform/apps/starter/deploy.ps1 b/deploy/terraform/apps/starter/deploy.ps1 index a832151e64..fdfc2168e6 100644 --- a/deploy/terraform/apps/starter/deploy.ps1 +++ b/deploy/terraform/apps/starter/deploy.ps1 @@ -41,6 +41,13 @@ $RepoRoot = (Resolve-Path "$ScriptDir/../../../..").Path $AppStackDir = Join-Path $ScriptDir 'app_stack' $EnvDir = Join-Path $ScriptDir "envs/$Environment/$Region" +# Each env/region gets its OWN Terraform data dir (backend pointer, providers, +# modules) instead of the shared app_stack/.terraform. Without this, two runs +# against different backends — e.g. a deploy in one region while another region +# is being destroyed — clobber each other's backend pointer mid-run, so the +# later `terraform output` reads the wrong state ("Output not found"). +$env:TF_DATA_DIR = Join-Path $AppStackDir ".terraform/$Environment-$Region" + function Die($msg) { Write-Error $msg; exit 1 } if (-not (Test-Path "$EnvDir/backend.hcl")) { Die "no backend.hcl for $Environment/$Region at $EnvDir" } diff --git a/deploy/terraform/apps/starter/deploy.sh b/deploy/terraform/apps/starter/deploy.sh index 33040b109e..9376b2977b 100755 --- a/deploy/terraform/apps/starter/deploy.sh +++ b/deploy/terraform/apps/starter/deploy.sh @@ -80,6 +80,13 @@ ENV_DIR="$SCRIPT_DIR/envs/$ENVIRONMENT/$REGION" [[ -f "$ENV_DIR/backend.hcl" ]] || die "no backend.hcl for $ENVIRONMENT/$REGION at $ENV_DIR" [[ -f "$ENV_DIR/terraform.tfvars" ]] || die "no terraform.tfvars for $ENVIRONMENT/$REGION at $ENV_DIR" +# Each env/region gets its OWN Terraform data dir (backend pointer, providers, +# modules) instead of the shared app_stack/.terraform — otherwise two runs against +# different backends (e.g. a deploy in one region while another is destroyed) +# clobber each other's backend pointer mid-run and `terraform output` then reads +# the wrong state. Absolute path so it is unaffected by `terraform -chdir`. +export TF_DATA_DIR="$APP_STACK_DIR/.terraform/$ENVIRONMENT-$REGION" + # ---- tooling preflight ------------------------------------------------------ for tool in terraform aws jq; do command -v "$tool" >/dev/null || die "$tool is required but not installed"; done diff --git a/deploy/terraform/apps/starter/destroy.ps1 b/deploy/terraform/apps/starter/destroy.ps1 index 3cf9985e81..47666d2ded 100644 --- a/deploy/terraform/apps/starter/destroy.ps1 +++ b/deploy/terraform/apps/starter/destroy.ps1 @@ -34,6 +34,10 @@ $ScriptDir = $PSScriptRoot $AppStackDir = Join-Path $ScriptDir 'app_stack' $EnvDir = Join-Path $ScriptDir "envs/$Environment/$Region" +# Per-env/region Terraform data dir so a destroy never clobbers (or is clobbered +# by) a concurrent deploy/destroy in another region sharing app_stack/.terraform. +$env:TF_DATA_DIR = Join-Path $AppStackDir ".terraform/$Environment-$Region" + function Die($msg) { Write-Error $msg; exit 1 } if (-not (Test-Path "$EnvDir/backend.hcl")) { Die "no backend.hcl for $Environment/$Region at $EnvDir" } From 03c88b5afee8a47220b92f19f5d8d7899edae0c5 Mon Sep 17 00:00:00 2001 From: iammukeshm Date: Fri, 5 Jun 2026 23:43:24 +0530 Subject: [PATCH 2/2] feat(deploy): prompt to seed demo tenants when not specified deploy.sh/deploy.ps1 now ask "Seed demo tenants (acme/globex)?" before the apply. Skipped when already chosen (--seed-demo/-SeedDemo), not migrating (--skip-migrate), or unattended (--auto-approve / no TTY) so CI never blocks. Co-Authored-By: Claude Opus 4.8 (1M context) --- deploy/terraform/apps/starter/deploy.ps1 | 8 ++++++++ deploy/terraform/apps/starter/deploy.sh | 8 ++++++++ 2 files changed, 16 insertions(+) diff --git a/deploy/terraform/apps/starter/deploy.ps1 b/deploy/terraform/apps/starter/deploy.ps1 index fdfc2168e6..dd79df2627 100644 --- a/deploy/terraform/apps/starter/deploy.ps1 +++ b/deploy/terraform/apps/starter/deploy.ps1 @@ -68,6 +68,14 @@ if ($tfVersion -lt $MinTfVersion) { Die "Terraform >= $MinTfVersion required (found $tfVersion). Upgrade with e.g. 'choco upgrade terraform'." } +# Ask up front (before the long apply) whether to seed the acme/globex demo +# tenants. Skipped when already chosen (-SeedDemo), not migrating (-SkipMigrate), +# or running unattended (-AutoApprove / non-interactive) so CI never blocks. +if (-not $SeedDemo -and -not $SkipMigrate -and -not $AutoApprove -and [Environment]::UserInteractive) { + $ans = Read-Host 'Seed demo tenants (acme/globex) after migrating? [y/N]' + if ($ans -match '^[Yy]') { $SeedDemo = $true } +} + Write-Host "==> Deploying '$Environment' in $Region" # ---- 1. optional API image build/push -------------------------------------- diff --git a/deploy/terraform/apps/starter/deploy.sh b/deploy/terraform/apps/starter/deploy.sh index 9376b2977b..3ab3a29aac 100755 --- a/deploy/terraform/apps/starter/deploy.sh +++ b/deploy/terraform/apps/starter/deploy.sh @@ -95,6 +95,14 @@ if [[ "$(printf '%s\n%s\n' "$MIN_TF_VERSION" "$TF_VERSION" | sort -V | head -1)" die "Terraform >= $MIN_TF_VERSION required (found $TF_VERSION). Upgrade with e.g. 'choco upgrade terraform'." fi +# Ask up front (before the long apply) whether to seed the acme/globex demo +# tenants. Skipped when already chosen (--seed-demo), not migrating +# (--skip-migrate), or running unattended (--auto-approve / no TTY) so CI never blocks. +if [[ "$SEED_DEMO" != true && "$SKIP_MIGRATE" != true && "$AUTO_APPROVE" != true && -t 0 ]]; then + read -rp "Seed demo tenants (acme/globex) after migrating? [y/N]: " ans + [[ "$ans" =~ ^[Yy] ]] && SEED_DEMO=true +fi + echo "==> Deploying '$ENVIRONMENT' in $REGION" # ---- 1. optional API image build/push --------------------------------------