# ============================================================================= # 02-setup-project.ps1 (Windows) # One-time GCP project setup: # - Links a billing account to the project # - Enables required APIs (Cloud Run, Artifact Registry, Secret Manager) # - Creates an Artifact Registry Docker repository # - Grants the current user the minimum required IAM roles # # Safe to re-run — most operations are idempotent. # Linux users: run GCR/scripts/02-setup-project.sh instead. # ============================================================================= #Requires -Version 5.1 Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' $ScriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path $EnvFile = Join-Path $ScriptDir "..\\.env" # ── Load .env ───────────────────────────────────────────────────────────────── if (-not (Test-Path $EnvFile)) { Write-Error "ERROR: $EnvFile not found.`nCopy GCR\.env.example to GCR\.env and fill in your values first." exit 1 } $config = @{} foreach ($line in Get-Content $EnvFile) { if ($line -match '^\s*$' -or $line -match '^\s*#') { continue } if ($line -match '^([^=]+)=(.*)$') { $config[$Matches[1].Trim()] = $Matches[2].Trim() } } $GCP_PROJECT_ID = $config['GCP_PROJECT_ID'] ?? '' $GCP_REGION = $config['GCP_REGION'] ?? '' $GCP_REPOSITORY = $config['GCP_REPOSITORY'] ?? '' if (-not $GCP_PROJECT_ID) { Write-Error "GCP_PROJECT_ID is not set in .env"; exit 1 } if (-not $GCP_REGION) { Write-Error "GCP_REGION is not set in .env"; exit 1 } if (-not $GCP_REPOSITORY) { Write-Error "GCP_REPOSITORY is not set in .env"; exit 1 } Write-Host ">>> Active project: $GCP_PROJECT_ID" Write-Host ">>> Region: $GCP_REGION" Write-Host ">>> AR repository: $GCP_REPOSITORY" Write-Host "" # ── Step 1: Link billing account ────────────────────────────────────────────── Write-Host ">>> Checking billing status..." $billingOutput = gcloud billing projects describe $GCP_PROJECT_ID --format="value(billingEnabled)" 2>$null $billingEnabled = ($billingOutput -eq "True") if ($billingEnabled) { Write-Host " Billing is already enabled — skipping." } else { Write-Host "" Write-Host " Billing is NOT enabled on this project." Write-Host " Listing available billing accounts..." Write-Host "" gcloud billing accounts list --format="table(name,displayName,open)" Write-Host "" $BILLING_ACCOUNT_ID = Read-Host " Enter the BILLING_ACCOUNT_ID from the list above (format: XXXXXX-XXXXXX-XXXXXX)" gcloud billing projects link $GCP_PROJECT_ID --billing-account=$BILLING_ACCOUNT_ID Write-Host " Billing linked." } # ── Step 2: Enable required APIs ───────────────────────────────────────────── Write-Host "" Write-Host ">>> Enabling required Google Cloud APIs (this may take a minute)..." gcloud services enable ` run.googleapis.com ` artifactregistry.googleapis.com ` secretmanager.googleapis.com ` cloudresourcemanager.googleapis.com ` --project=$GCP_PROJECT_ID Write-Host " APIs enabled." # ── Step 3: Create Artifact Registry Docker repository ─────────────────────── Write-Host "" Write-Host ">>> Creating Artifact Registry repository: $GCP_REPOSITORY ..." $repoExists = $false try { gcloud artifacts repositories describe $GCP_REPOSITORY ` --location=$GCP_REGION ` --project=$GCP_PROJECT_ID 2>$null | Out-Null $repoExists = $true } catch { } if ($repoExists) { Write-Host " Repository already exists — skipping." } else { gcloud artifacts repositories create $GCP_REPOSITORY ` --repository-format=docker ` --location=$GCP_REGION ` --description="Container images for Htmx app" ` --project=$GCP_PROJECT_ID Write-Host " Repository created." } # ── Step 4: Grant current user the minimum required IAM roles ───────────────── $CURRENT_USER = (gcloud config get-value account).Trim() Write-Host "" Write-Host ">>> Granting IAM roles to $CURRENT_USER ..." foreach ($role in @( "roles/run.developer", "roles/artifactregistry.writer", "roles/iam.serviceAccountUser", "roles/secretmanager.admin", "roles/secretmanager.secretAccessor", "roles/secretmanager.secretVersionAdder" )) { Write-Host " Adding role: $role" gcloud projects add-iam-policy-binding $GCP_PROJECT_ID ` --member="user:$CURRENT_USER" ` --role=$role ` --quiet } Write-Host "" Write-Host ">>> Project setup complete." Write-Host "" Write-Host ">>> Summary:" Write-Host " Project ID: $GCP_PROJECT_ID" Write-Host " Region: $GCP_REGION" Write-Host " Artifact Registry: $GCP_REGION-docker.pkg.dev/$GCP_PROJECT_ID/$GCP_REPOSITORY" Write-Host "" Write-Host ">>> Next step: run GCR\scripts\03-create-secrets.ps1"