---
title: "Production Environment"
slug: gcp-env-production
section: GCP Infrastructure
status: stable
description: "Project `production-499116` (display name Production, project number in the `terraform-apply-prod` workflow) holds the live customer-facing platform: the Globus back office and its Cloud SQL, the Applicant Portal (PPPToo, `ppp2-participa…"
last-updated: 2026-10-07
---

# Production Environment

## Purpose and layout

Project `production-499116` (display name Production, project number in the `terraform-apply-prod` workflow) holds the live customer-facing platform: the Globus back office and its Cloud SQL, the Applicant Portal (PPPToo, `ppp2-participant-*`), Keycloak for the `applicant-portal` realm, the Host Portal, and n8n. Terraform for it lives in `environments/production`, with state in the GCS bucket `prod-tfstate-production-499116` (prefix `alliance-gcp-infra`). The older bucket `terraform-state-bucket-produ` holds legacy Terragrunt state and must not be reused (see the comment in `environments/production/backend.tf`).

Runtime split, from the live snapshot:

- Cloud Run in `northamerica-northeast1`: `applicant-keycloak`, `ppp2-participant-client-prod`, `ppp2-participant-server-prod`, `host-company-portal-client-prod`, `host-company-portal-server-prod`.
- Cloud Run in `us-central1`: `db-query-proxy-prod`, `db-query-proxy-prod-broker`, `cloudbuild-teams-notifier`, `sandbox-web-prod`.
- GKE Autopilot cluster `globus-cluster-prod` (`us-central1`). Workloads were not inspected (no kubectl); the project has GKE ingress load balancers for globus, n8n and wildpacks, which suggests those workloads run here. For wildpacks the Terraform backs this up: it declares a dedicated ingress address (`google_compute_global_address.wildpacks_prod_ip`), a Workload Identity binding for the `wildpacks-prod/wildpacks-prod-ksa` Kubernetes service account, and `roles/container.developer` for `wildpacks-prod-runtime` so its CI can deploy to the cluster (PR #92). The earlier wildpacks Cloud Run service and its `apply.allianceabroad.com` domain mapping were removed from Terraform (PR #93); `module.ar_wildpacks_prod` and `module.wildpacks_prod_runtime` were kept. Whether the wildpacks workloads are currently running on the cluster was not checked.
- Cloud SQL (all private IP, `us-central1`): `globus-db-prod`, `n8n-db-prod`, `keycloak-db-prod`; Memorystore Redis `n8n-redis-prod`.

For how this relates to the other projects see `/docs/gcp-overview`; for the promotion path see `/docs/git-branching-release-model` and `/docs/onboarding-cicd-deployment`.

## How production is deployed

Three mechanisms apply:

- **Terraform apply.** `terraform-apply-prod` is `workflow_dispatch` only. Merging to `environments/production/**` applies nothing. A human runs the workflow and types the phrase `apply-to-production` in the `confirm` input; any other value aborts before checkout or GCP authentication. An optional `target` input scopes the plan and apply to one address. The workflow authenticates through the `github-actions-apply-prod` WIF pool as `terraform-tf`, and the pool only accepts `Alliance-Strategies/alliance-gcp-infra` on `refs/heads/main`. A fresh plan runs first; if it errors or proposes more than 1 destroy (`DESTROY_THRESHOLD`, versus 3 for dev and qa) the job stops before apply. Only one run executes at a time (queued, not cancelled). Reviewers use the PR plan comment and the read-only `terraform-plan-notify-prod` workflow. See `/docs/gcp-terraform-workflows`.
- **Application CI.** Images and revisions for the portals are not Terraform-owned. For example `deploy-production-cloudrun.yml` in HostPortalPrototype runs `gcloud run deploy` for the Host Portal, and the Terraform resources ignore `image` and `env` changes so a later apply does not revert them. The GitHub WIF providers declared in `branching-sandbox.tf`, `host-portal-deploy-identity.tf`, `ppp2-deploy-identity.tf` and `wildpacks.tf` have an attribute condition that accepts only the declared repo on `refs/heads/main` and also requires `job_workflow_ref` to be that repo's `.github/workflows/deploy.yml@refs/heads/main`.
- **Manual or console work.** The drift data shows the leftovers (below).

The README of the repo still says production is not under Terraform; the `.tf` files and workflows show otherwise. See also `/docs/dev-only-deployment-policy`.

## Host Portal on Cloud Run and the load balancer cutover

The Host Portal frontend and API moved from GKE to Cloud Run (`host-company-portal-client-prod`, `host-company-portal-server-prod`). The relevant files, all in `environments/production`:

- `host-portal-client-cloudrun-migration.tf` - the client service. It was first a placeholder in `main.tf`, briefly moved onto the shared `cloud-run-service` module, then inlined again because the module would have stripped the `VITE_*` environment variables that the application pipeline sets on every deploy. The file records that the client listens on port 3000, min 2 and max 4 instances, 1000m CPU (the file says this matches the live value; 500m right-sizing is noted as a follow-up), and that `allUsers` holds `roles/run.invoker`.
- `host-portal-server-invoker.tf` - the matching public invoker binding for the server service.
- `host-portal-dual-domain-cloudrun-lb-design.tf` - module `host_portal_cloudrun_lb_prod`: a global external managed HTTPS load balancer with a static IP, two serverless NEGs, two backends, and a URL map for `hosts.allianceabroad.com` and `hosts.wildpacks.com`. Each host sends `/api` and `/api/*` to the server backend and everything else to the client backend. HTTP is redirected. The file header (dated 2026-09-18) states that the DNS A records for both hosts were switched to the new LB IP and the old GKE ingress and its static IP were deleted. The live snapshot shows the forwarding rules `host-portal-cloudrun-lb-prod-http-fwd-rule` and `-https-fwd-rule`, the url map and the two backend services; DNS was not checked live.

The file name still says "design", and its header still opens with design-only text that the later update notes contradict. Treat the update notes and the live snapshot as authoritative.

### Certificate map versus classic certificate

The module block uses classic Google-managed certificates (`host-portal-cloudrun-lb-prod-cert-alliance` and `-wildpacks`, passed through the `certs` input). The live snapshot shows the HTTPS proxy attached to those two certificates (no certificate map) and both ACTIVE. The same file also declares Certificate Manager resources (DNS authorizations, two certificates, a certificate map and two map entries) that no proxy references. In the live snapshot the two Certificate Manager certificates are in state `PROVISIONING`. The comments explain why: an earlier revision switched the proxy to the certificate map, the plan would have destroyed the working classic certs, and the Certificate Manager DNS-authorization CNAME records had never been added to GoDaddy, so the new certs would not have been ACTIVE. That would have been an HTTPS outage on both hosts. The module was reverted to classic certs. Before anyone migrates, add the DNS authorization records, confirm the new certificates are ACTIVE, and only then flip the proxy. Classic certs also only become ACTIVE once DNS already points at the LB, which is why they were stuck provisioning before the cutover.

## Applicant Portal load balancer and certificate

`module.ppp2_applicants_prod_lb` (`main.tf`, `load-balancer-https`, region `northamerica-northeast1`, static IP `ppp2-applicants-prod-ip`, backend `ppp2-participant-client-prod`) serves two brand domains from the same backend: `applicants.allianceabroad.com` and `applicants.wildpacks.com` (PR #289). It uses a classic Google-managed certificate, so the certificate's domain list is immutable; adding the second domain forces a replacement. GCP certificate names are globally unique, so a create-before-destroy replacement under the same name returns HTTP 409. The certificate was therefore renamed from `ppp2-applicants-prod-cert` to `ppp2-applicants-prod-cert-v2` (PR #290) so the new two-domain certificate is created and attached to the HTTPS proxy before the old one is destroyed. The code comment states the `applicants` A record for wildpacks.com at GoDaddy had to point at the load balancer's static IP before Google would issue the certificate. The live snapshot on this page does not enumerate load balancer or certificate resources, so certificate state was not checked live.

## Globus blue-green capability (no cutover)

`environments/production/globus-prod-bluegreen.tf` adds the production blue-green capability for Globus (BackOfficePrototype) on GKE. Globus stays on GKE, so instead of a Cloud Run revision split it uses a GKE Gateway with a weighted HTTPRoute between two colour Deployments (`globus-app-blue` / `globus-app-green`) in the existing `globus-prod` namespace.

> **No cutover**
> The file does not cut production over. `globus.allianceabroad.com` keeps resolving to the legacy GCE Ingress (`globus-prod-ip`) until Phase 3; everything in the file is additive and serves nothing public until that A record moves.

- **Deploy identity.** WIF pool `github-actions-globus`, whose provider accepts only `refs/heads/main` of `Alliance-Strategies/BackOfficePrototype`; `hotfix/*` and `release/*` reach production through a PR into main, never directly. The deployer service account (module `globus_deployer_prod`) is for `deploy-prod-bluegreen.yml` on main only: kubectl on `globus-cluster-prod` (colour Deployments, migration Jobs, HTTPRoute weights, the pool Gateway), push to the Artifact Registry repo `globus`, and read the Vite build secrets.
- **Edge.** One global static IP, `globus-prod-gateway-ip`, is claimed by the pool Gateway (`globus-pool-prod`, applied by the deploy workflow from `BackOfficePrototype/k8s/pool/gateway/prod`) via `spec.addresses` NamedAddress. One Certificate Manager DNS-authorized certificate covers the public host. The idle colour is smoke-tested through an `x-globus-preview` header rule on the public HTTPRoute (the GKE stand-in for a Cloud Run revision tag URL), so there are no colour hostnames, certificates or DNS records. The certificate map `globus-prod-pool-cert-map` is referenced by the Gateway via `networking.gke.io/certmap`.
- **DNS.** DNS is GoDaddy. The ACME CNAME for the public host is created by hand from the `globus_prod_pool_dns_records` output with a REST PATCH call, never with the replace tools (they wipe sibling records). The public A record is left alone until the cutover.
- **Workflow values.** The `globus_prod_pool_identity` output holds the values the BackOfficePrototype `deploy-prod-bluegreen.yml` needs.

## n8n sync folders

`environments/production` has four n8n manifest folders: `n8n-sync` (plus `credential-manifest.json` and a `pre-wipe-archive-2026-09-03`), `n8n-sync-email-template`, `n8n-sync-external-notification` and `n8n-sync-sendgrid-webhook-receiver`. The last three are deliberately isolated manifests, each scoped to workflows that had never been deployed to prod, kept apart from the `n8n-sync` baseline: `n8n-sync-email-template` holds the SendGrid Template Admin provider adapter and the `email_template` domain handler (PR #291, later moved into its own manifest); `n8n-sync-external-notification` holds the `external_notification` domain handler (PR #300, `null_resource.n8n_baseline_deploy_external_notification_prod`); `n8n-sync-sendgrid-webhook-receiver` holds the SendGrid event webhook receiver, pinned to a commit that fixes a hardcoded dev Globus URL so prod SendGrid events are not forwarded to dev (PR #301, `null_resource.n8n_baseline_deploy_sendgrid_webhook_receiver_prod`). Each resource is a `null_resource` that depends on `null_resource.n8n_credentials_bootstrap_prod`. In `main.tf`, `null_resource` resources run `scripts/n8n-sync/import-credentials.cjs` and `deploy-n8n-baseline.cjs` against `globus-cluster-prod` (namespace `n8n`); each re-runs when its manifest hash or its script hash changes. The workflow therefore installs `kubectl` and GKE credentials on the runner, and clones the private `n8n-workflows` repo using secrets it can read cross-project. The manifest comment says the baseline is a curated, pinned-commit subset of the `n8n-workflows` repo and deliberately excludes platform-ops agents. It replaced an older delete-first sync after a 2026-09-03 incident in which prod n8n was found with 81 workflows from an unknown source (archived in `pre-wipe-archive-2026-09-03`). The workflow comments say `null_resource.n8n_workflow_sync_prod` was tainted from earlier failed runs; that resource is no longer in `main.tf`, so the current state of these provisioners is not confirmed. Application changes to workflows follow the n8n repo process, not this folder.

_Terraform inventory generated from `alliance-gcp-infra` at commit `fc9bd1d`. Live snapshots: `production-499116` read 2026-10-01. Snapshots hold names and a few stable attributes only, never secret values._

## Terraform-managed resources

Every module call and resource block under `environments/production` (217 in total), grouped by category.

### Identity and access (service accounts, IAM bindings, Workload Identity)

70 items.

| Terraform address | Module or resource type | Identifier | Defined in |
| --- | --- | --- | --- |
| `google_artifact_registry_repository_iam_member.sandbox_deployer_writer` | `google_artifact_registry_repository_iam_member` | `roles/artifactregistry.writer -> serviceAccount:${module.sandbox_deployer.email}` | `branching-sandbox.tf:172` |
| `google_cloud_run_service_iam_member.host_company_portal_client_prod_invokers` | `google_cloud_run_service_iam_member` | `roles/run.invoker -> each.value` (one per `for_each` key) | `host-portal-client-cloudrun-migration.tf:188` |
| `google_cloud_run_service_iam_member.host_company_portal_server_prod_invokers` | `google_cloud_run_service_iam_member` | `roles/run.invoker -> each.value` (one per `for_each` key) | `host-portal-server-invoker.tf:18` |
| `google_cloud_run_service_iam_member.ppp2_client_run_prod_invokers` | `google_cloud_run_service_iam_member` | `roles/run.invoker -> each.value` (one per `for_each` key) | `main.tf:1484` |
| `google_cloud_run_service_iam_member.ppp2_server_run_prod_invokers` | `google_cloud_run_service_iam_member` | `roles/run.invoker -> each.value` (one per `for_each` key) | `main.tf:1667` |
| `google_cloud_run_v2_service_iam_member.sandbox_deployer` | `google_cloud_run_v2_service_iam_member` | `google_cloud_run_v2_service.sandbox_web.name` | `branching-sandbox.tf:151` |
| `google_cloud_run_v2_service_iam_member.sandbox_public` | `google_cloud_run_v2_service_iam_member` | `google_cloud_run_v2_service.sandbox_web.name` | `branching-sandbox.tf:143` |
| `google_iam_workload_identity_pool_provider.sandbox` | `google_iam_workload_identity_pool_provider` | `GitHub` | `branching-sandbox.tf:69` |
| `google_iam_workload_identity_pool.sandbox` | `google_iam_workload_identity_pool` | `GitHub Actions (sandbox)` | `branching-sandbox.tf:62` |
| `google_project_iam_member.cloudbuild_default_sa_ar_writer` | `google_project_iam_member` | `roles/artifactregistry.writer -> serviceAccount:445487831874-compute@developer.gserviceaccount.com` | `main.tf:1970` |
| `google_project_iam_member.cloudbuild_default_sa_log_writer` | `google_project_iam_member` | `roles/logging.logWriter -> serviceAccount:445487831874-compute@developer.gserviceaccount.com` | `main.tf:1964` |
| `google_project_iam_member.cloudbuild_default_sa_storage_viewer` | `google_project_iam_member` | `roles/storage.objectViewer -> serviceAccount:445487831874-compute@developer.gserviceaccount.com` | `main.tf:1958` |
| `google_project_iam_member.cloudbuild_service_agent_secret_accessor` | `google_project_iam_member` | `roles/secretmanager.secretAccessor -> serviceAccount:service-445487831874@gcp-sa-cloudbuild.iam.gserviceaccount.com` | `main.tf:2143` |
| `google_project_iam_member.env_audit_agent_sa_cloudsql_viewer` | `google_project_iam_member` | `roles/cloudsql.viewer -> serviceAccount:env-audit-agent-sa@alliance-platform-agents.iam.gserviceaccount.com` | `env-audit-agent-sa-viewer-roles.tf:18` |
| `google_project_iam_member.env_audit_agent_sa_compute_viewer` | `google_project_iam_member` | `roles/compute.viewer -> serviceAccount:env-audit-agent-sa@alliance-platform-agents.iam.gserviceaccount.com` | `env-audit-agent-sa-viewer-roles.tf:24` |
| `google_project_iam_member.env_audit_agent_sa_secretmanager_viewer` | `google_project_iam_member` | `roles/secretmanager.viewer -> serviceAccount:env-audit-agent-sa@alliance-platform-agents.iam.gserviceaccount.com` | `env-audit-agent-sa-viewer-roles.tf:12` |
| `google_project_iam_member.manus_agent_sa_secretmanager_admin` | `google_project_iam_member` | `roles/secretmanager.admin -> serviceAccount:manus-agent-sa@${var.project_id}.iam.gserviceaccount.com` | `manus-agent-sa-secretmanager-admin.tf:12` |
| `google_project_iam_member.n8n_runtime_dev_compute_viewer` | `google_project_iam_member` | `roles/compute.viewer -> serviceAccount:n8n-runtime-dev@skilled-script-469819-f2.iam.gserviceaccount.com` | `cert-monitor-n8n-runtime-sa-compute-viewer.tf:12` |
| `google_project_iam_member.platform_hub_sa_cross_project` | `google_project_iam_member` | `each.value -> serviceAccount:platform-hub-sa@alliance-mcp-services.iam.gserviceaccount.com` (one per `for_each` key) | `main.tf:2115` |
| `google_project_iam_member.sandbox_deployer_run_viewer` | `google_project_iam_member` | `roles/run.viewer -> serviceAccount:${module.sandbox_deployer.email}` | `branching-sandbox.tf:160` |
| `google_project_iam_member.security_agent_sa_cloudkms_viewer` | `google_project_iam_member` | `roles/cloudkms.viewer -> serviceAccount:security-agent-sa@alliance-platform-agents.iam.gserviceaccount.com` | `security-agent-sa-cloudkms-viewer.tf:12` |
| `google_project_iam_member.security_agent_sa_cloudsql_viewer` | `google_project_iam_member` | `roles/cloudsql.viewer -> serviceAccount:security-agent-sa@alliance-platform-agents.iam.gserviceaccount.com` | `security-agent-sa-cloudsql-viewer.tf:12` |
| `google_project_iam_member.user_addition_agent_sa_iam_viewer` | `google_project_iam_member` | `roles/iam.securityReviewer -> serviceAccount:user-addition-agent-sa@skilled-script-469819-f2.iam.gserviceaccount.com` | `user-addition-agent-sa-iam-viewer.tf:8` |
| `google_project_iam_member.wildpacks_prod_runtime_gke_deployer` | `google_project_iam_member` | `roles/container.developer -> serviceAccount:wildpacks-prod-runtime@${var.project_id}.iam.gserviceaccount.com` | `main.tf:2167` |
| `google_secret_manager_secret_iam_member.broker_can_read_app_token` | `google_secret_manager_secret_iam_member` | `projects/production-499116/secrets/db-query-proxy-prod-token` | `db-query-proxy-broker.tf:83` |
| `google_service_account_iam_member.broker_can_impersonate_karin_caller` | `google_service_account_iam_member` | `roles/iam.serviceAccountTokenCreator -> serviceAccount:${module.db_query_proxy_prod_broker_runtime.email}` | `db-query-proxy-broker.tf:70` |
| `google_service_account_iam_member.external_secrets_prod_runtime_wi` | `google_service_account_iam_member` | `roles/iam.workloadIdentityUser -> serviceAccount:${var.project_id}.svc.id.goog[external-secrets/external-secrets]` | `main.tf:2000` |
| `google_service_account_iam_member.github_actions_mcp_prod_wif` | `google_service_account_iam_member` | `roles/iam.workloadIdentityUser -> principalSet://iam.googleapis.com/${module.wif_pool_github_actions.name}/*` | `main.tf:2023` |
| `google_service_account_iam_member.globus_prod_runtime_wi` | `google_service_account_iam_member` | `roles/iam.workloadIdentityUser -> serviceAccount:${var.project_id}.svc.id.goog[globus-prod/globus-prod-ksa]` | `main.tf:1984` |
| `google_service_account_iam_member.host_portal_deployer_can_actas_client` | `google_service_account_iam_member` | `roles/iam.serviceAccountUser -> serviceAccount:${module.host_portal_deployer.email}` | `main.tf:2258` |
| `google_service_account_iam_member.host_portal_deployer_can_actas_server` | `google_service_account_iam_member` | `roles/iam.serviceAccountUser -> serviceAccount:${module.host_portal_deployer.email}` | `main.tf:2252` |
| `google_service_account_iam_member.host_portal_deployer_wif` | `google_service_account_iam_member` | `roles/iam.workloadIdentityUser -> principalSet://iam.googleapis.com/${module.wif_pool_github_actions.name}/attribute.repository/Alliance-Strategies/HostPortalPrototype` | `main.tf:2233` |
| `google_service_account_iam_member.n8n_runtime_prod_wi` | `google_service_account_iam_member` | `roles/iam.workloadIdentityUser -> serviceAccount:${var.project_id}.svc.id.goog[n8n/n8n-ksa]` | `main.tf:1994` |
| `google_service_account_iam_member.platform_hub_sa_tc_manus` | `google_service_account_iam_member` | `roles/iam.serviceAccountTokenCreator -> serviceAccount:platform-hub-sa@alliance-mcp-services.iam.gserviceaccount.com` | `main.tf:2130` |
| `google_service_account_iam_member.ppp2_deployer_can_actas_client` | `google_service_account_iam_member` | `roles/iam.serviceAccountUser -> serviceAccount:${module.ppp2_deployer.email}` | `main.tf:514` |
| `google_service_account_iam_member.ppp2_deployer_can_actas_server` | `google_service_account_iam_member` | `roles/iam.serviceAccountUser -> serviceAccount:${module.ppp2_deployer.email}` | `main.tf:508` |
| `google_service_account_iam_member.sandbox_deployer_actas` | `google_service_account_iam_member` | `roles/iam.serviceAccountUser -> serviceAccount:${module.sandbox_deployer.email}` | `branching-sandbox.tf:166` |
| `google_service_account_iam_member.sandbox_deployer_wif` | `google_service_account_iam_member` | `roles/iam.workloadIdentityUser -> principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.sandbox.name}/attribute.repository/${local.sandbox_repo}` | `branching-sandbox.tf:88` |
| `google_service_account_iam_member.terraform_ci_viewer_prod_wif` | `google_service_account_iam_member` | `roles/iam.workloadIdentityUser -> principalSet://iam.googleapis.com/${module.wif_pool_github_actions_iac.name}/attribute.repository/Alliance-Strategies/alliance-gcp-infra` | `main.tf:279` |
| `google_service_account_iam_member.terraform_tf_apply_prod_wif` | `google_service_account_iam_member` | `roles/iam.workloadIdentityUser -> principalSet://iam.googleapis.com/${module.wif_pool_github_actions_apply_prod.name}/attribute.repository/Alliance-Strategies/alliance-gcp-infra` | `ci-terraform-apply.tf:56` |
| `google_service_account_iam_member.wildpacks_prod_runtime_wi` | `google_service_account_iam_member` | `roles/iam.workloadIdentityUser -> serviceAccount:${var.project_id}.svc.id.goog[wildpacks-prod/wildpacks-prod-ksa]` | `main.tf:2155` |
| `google_storage_bucket_iam_member.globus_prod_runtime_documents_creator` | `google_storage_bucket_iam_member` | `roles/storage.objectCreator -> serviceAccount:${module.globus_prod_runtime.email}` | `main.tf:444` |
| `google_storage_bucket_iam_member.globus_prod_runtime_documents_viewer` | `google_storage_bucket_iam_member` | `roles/storage.objectViewer -> serviceAccount:${module.globus_prod_runtime.email}` | `main.tf:450` |
| `google_storage_bucket_iam_member.globus_prod_runtime_photos_creator` | `google_storage_bucket_iam_member` | `roles/storage.objectCreator -> serviceAccount:${module.globus_prod_runtime.email}` | `main.tf:456` |
| `google_storage_bucket_iam_member.globus_prod_runtime_photos_viewer` | `google_storage_bucket_iam_member` | `roles/storage.objectViewer -> serviceAccount:${module.globus_prod_runtime.email}` | `main.tf:462` |
| `google_storage_bucket_iam_member.terraform_ci_viewer_prod_tfstate_lock` | `google_storage_bucket_iam_member` | `roles/storage.objectUser -> serviceAccount:${module.terraform_ci_viewer_prod.email}` | `main.tf:288` |
| `module.cloudbuild_prod` | `service-account` | `cloudbuild-prod` | `main.tf:404` |
| `module.cloudbuild_teams_notifier_sa` | `service-account` | `cloudbuild-teams-notifier` | `main.tf:393` |
| `module.db_query_proxy_prod_broker_runtime` | `service-account` | `db-query-proxy-prod-broker` | `db-query-proxy-broker.tf:51` |
| `module.db_query_proxy_prod_runtime` | `service-account` | `db-query-proxy-prod-runtime` | `db-query-proxy.tf:55` |
| `module.external_secrets_prod_runtime` | `service-account` | `external-secrets-prod-runtime` | `main.tf:353` |
| `module.github_actions_mcp_prod` | `service-account` | `github-actions-mcp-prod` | `main.tf:552` |
| `module.globus_prod_runtime` | `service-account` | `globus-prod-runtime` | `main.tf:424` |
| `module.host_portal_client_prod_run` | `service-account` | `host-portal-client-run-prod` | `main.tf:2197` |
| `module.host_portal_deployer` | `service-account` | `host-portal-deployer` | `main.tf:2210` |
| `module.host_portal_server_prod_run` | `service-account` | `host-portal-server-run-prod` | `main.tf:2187` |
| `module.karin_proxy_caller_prod` | `service-account` | `karin-proxy-caller-prod` | `db-query-proxy.tf:72` |
| `module.keycloak_runtime_prod` | `service-account` | `keycloak-runtime-prod` | `main.tf:539` |
| `module.n8n_runtime_prod` | `service-account` | `n8n-runtime-prod` | `main.tf:362` |
| `module.ppp2_client_prod` | `service-account` | `ppp2-client-prod` | `main.tf:530` |
| `module.ppp2_deployer` | `service-account` | `ppp2-deployer` | `main.tf:468` |
| `module.ppp2_server_prod` | `service-account` | `ppp2-server-prod` | `main.tf:520` |
| `module.sandbox_deployer` | `service-account` | `sandbox-deployer-prod` | `branching-sandbox.tf:53` |
| `module.sandbox_runtime` | `service-account` | `sandbox-runtime-prod` | `branching-sandbox.tf:44` |
| `module.terraform_ci_viewer_prod` | `service-account` | `terraform-ci-viewer-prod` | `main.tf:266` |
| `module.terraform_tf_prod` | `service-account` | `terraform-tf` | `main.tf:595` |
| `module.wif_pool_github_actions` | `workload-identity-pool` | `github-actions-pool` | `main.tf:294` |
| `module.wif_pool_github_actions_apply_prod` | `workload-identity-pool` | `github-actions-apply-prod` | `ci-terraform-apply.tf:33` |
| `module.wif_pool_github_actions_iac` | `workload-identity-pool` | `github-actions-iac` | `main.tf:227` |
| `module.wildpacks_prod_runtime` | `service-account` | `wildpacks-prod-runtime` | `main.tf:384` |

### Secrets (Secret Manager containers)

96 items.

| Terraform address | Module or resource type | Identifier | Defined in |
| --- | --- | --- | --- |
| `google_secret_manager_secret.ppp2_globus_api_key_noenv` | `google_secret_manager_secret` | `ppp2-globus-api-key` | `main.tf:494` |
| `google_secret_manager_secret.ppp2_keycloak_admin_client_secret_noenv` | `google_secret_manager_secret` | `ppp2-keycloak-admin-client-secret` | `main.tf:486` |
| `module.cloudbuild_teams_notifier_prod_github_token` | `secret-manager-secret` | `cloudbuild-teams-notifier-prod-github-token` | `main.tf:1046` |
| `module.cloudbuild_teams_notifier_prod_webhook_url` | `secret-manager-secret` | `cloudbuild-teams-notifier-prod-webhook-url` | `main.tf:1054` |
| `module.db_query_proxy_prod_db_password` | `secret-manager-secret` | `db-query-proxy-prod-db-password` | `db-query-proxy.tf:127` |
| `module.db_query_proxy_prod_token` | `secret-manager-secret` | `db-query-proxy-prod-token` | `db-query-proxy.tf:109` |
| `module.github_packages_read_pat` | `secret-manager-secret` | `github-packages-read-pat` | `globus-short-secret-names-migration.tf:390` |
| `module.github_prod_oauth_token` | `secret-manager-secret` | `github-prod-oauth-token` | `main.tf:1892` |
| `module.globus_internal_ocr_api_key` | `secret-manager-secret` | `globus-internal-ocr-api-key` | `globus-short-secret-names-migration.tf:397` |
| `module.globus_openai_api_key` | `secret-manager-secret` | `globus-openai-api-key` | `globus-short-secret-names-migration.tf:404` |
| `module.globus_partner_pipeline_api_key` | `secret-manager-secret` | `globus-partner-pipeline-api-key` | `globus-short-secret-names-migration.tf:348` |
| `module.globus_partner_pipeline_api_url` | `secret-manager-secret` | `globus-partner-pipeline-api-url` | `globus-short-secret-names-migration.tf:355` |
| `module.globus_partner_pipeline_user_id` | `secret-manager-secret` | `globus-partner-pipeline-user-id` | `globus-short-secret-names-migration.tf:362` |
| `module.globus_prod_applicant_portal_posthog_key` | `secret-manager-secret` | `globus-prod-applicant-portal-posthog-key` | `main.tf:745` |
| `module.globus_prod_docai_processor_id` | `secret-manager-secret` | `globus-prod-docai-processor-id` | `main.tf:753` |
| `module.globus_prod_environment` | `secret-manager-secret` | `globus-prod-environment` | `main.tf:764` |
| `module.globus_prod_hubspot_api_key` | `secret-manager-secret` | `globus-prod-hubspot-api-key` | `main.tf:772` |
| `module.globus_prod_internal_ocr_api_key` | `secret-manager-secret` | `globus-prod-internal-ocr-api-key` | `main.tf:780` |
| `module.globus_prod_mailjet_api_key` | `secret-manager-secret` | `globus-prod-mailjet-api-key` | `main.tf:791` |
| `module.globus_prod_mailjet_from_email` | `secret-manager-secret` | `globus-prod-mailjet-from-email` | `main.tf:804` |
| `module.globus_prod_n8n_globus_api_key` | `secret-manager-secret` | `globus-prod-n8n-globus-api-key` | `main.tf:812` |
| `module.globus_prod_openai_api_key` | `secret-manager-secret` | `globus-prod-openai-api-key` | `main.tf:820` |
| `module.globus_prod_postgres_admin_password` | `secret-manager-secret` | `globus-prod-postgres-admin-password` | `globus-short-secret-names-migration.tf:369` |
| `module.globus_prod_posthog_host` | `secret-manager-secret` | `globus-prod-posthog-host` | `main.tf:828` |
| `module.globus_prod_posthog_key` | `secret-manager-secret` | `globus-prod-posthog-key` | `main.tf:836` |
| `module.globus_prod_sandbox_mode` | `secret-manager-secret` | `globus-prod-sandbox-mode` | `main.tf:844` |
| `module.globus_prod_sendgrid_api_key` | `secret-manager-secret` | `globus-prod-sendgrid-api-key` | `main.tf:860` |
| `module.globus_prod_translation_glossary_id` | `secret-manager-secret` | `globus-prod-translation-glossary-id` | `globus-short-secret-names-migration.tf:411` |
| `module.globus_sendgrid_api_key` | `secret-manager-secret` | `globus-sendgrid-api-key` | `globus-short-secret-names-migration.tf:418` |
| `module.globus_short_applicant_portal_webhook_url` | `secret-manager-secret` | `globus-applicant-portal-webhook-url` | `globus-short-secret-names-migration.tf:224` |
| `module.globus_short_asana_access_token` | `secret-manager-secret` | `globus-asana-access-token` | `globus-short-secret-names-migration.tf:56` |
| `module.globus_short_asana_client_id` | `secret-manager-secret` | `globus-asana-client-id` | `globus-short-secret-names-migration.tf:42` |
| `module.globus_short_asana_client_secret` | `secret-manager-secret` | `globus-asana-client-secret` | `globus-short-secret-names-migration.tf:49` |
| `module.globus_short_backoffice_api_key` | `secret-manager-secret` | `globus-backoffice-api-key` | `globus-short-secret-names-migration.tf:63` |
| `module.globus_short_brave_api_key` | `secret-manager-secret` | `globus-brave-api-key` | `globus-short-secret-names-migration.tf:119` |
| `module.globus_short_database_url` | `secret-manager-secret` | `globus-database-url` | `globus-short-secret-names-migration.tf:21` |
| `module.globus_short_document_bucket` | `secret-manager-secret` | `globus-document-bucket` | `globus-short-secret-names-migration.tf:133` |
| `module.globus_short_entra_client_id` | `secret-manager-secret` | `globus-entra-client-id` | `globus-short-secret-names-migration.tf:154` |
| `module.globus_short_entra_client_secret` | `secret-manager-secret` | `globus-entra-client-secret` | `globus-short-secret-names-migration.tf:161` |
| `module.globus_short_entra_tenant_id` | `secret-manager-secret` | `globus-entra-tenant-id` | `globus-short-secret-names-migration.tf:147` |
| `module.globus_short_github_config_repo_token` | `secret-manager-secret` | `globus-github-config-repo-token` | `globus-short-secret-names-migration.tf:245` |
| `module.globus_short_github_config_repo_write_token` | `secret-manager-secret` | `globus-github-config-repo-write-token` | `globus-short-secret-names-migration.tf:252` |
| `module.globus_short_google_maps_api_key` | `secret-manager-secret` | `globus-google-maps-api-key` | `globus-short-secret-names-migration.tf:306` |
| `module.globus_short_google_workspace_domain` | `secret-manager-secret` | `globus-google-workspace-domain` | `globus-short-secret-names-migration.tf:299` |
| `module.globus_short_host_portal_webhook_url` | `secret-manager-secret` | `globus-host-portal-webhook-url` | `globus-short-secret-names-migration.tf:231` |
| `module.globus_short_internal_translation_api_key` | `secret-manager-secret` | `globus-internal-translation-api-key` | `globus-short-secret-names-migration.tf:238` |
| `module.globus_short_mcp_api_key` | `secret-manager-secret` | `globus-mcp-api-key` | `globus-short-secret-names-migration.tf:140` |
| `module.globus_short_n8n_api_key` | `secret-manager-secret` | `globus-n8n-api-key` | `globus-short-secret-names-migration.tf:196` |
| `module.globus_short_n8n_api_url` | `secret-manager-secret` | `globus-n8n-api-url` | `globus-short-secret-names-migration.tf:189` |
| `module.globus_short_n8n_job_order_webhook_url` | `secret-manager-secret` | `globus-n8n-job-order-webhook-url` | `globus-short-secret-names-migration.tf:203` |
| `module.globus_short_n8n_webhook_url_application` | `secret-manager-secret` | `globus-n8n-webhook-url-application` | `globus-short-secret-names-migration.tf:210` |
| `module.globus_short_n8n_webhook_url_document` | `secret-manager-secret` | `globus-n8n-webhook-url-document` | `globus-short-secret-names-migration.tf:217` |
| `module.globus_short_oidc_client_id` | `secret-manager-secret` | `globus-oidc-client-id` | `globus-short-secret-names-migration.tf:175` |
| `module.globus_short_oidc_client_secret` | `secret-manager-secret` | `globus-oidc-client-secret` | `globus-short-secret-names-migration.tf:182` |
| `module.globus_short_oidc_discovery_url` | `secret-manager-secret` | `globus-oidc-discovery-url` | `globus-short-secret-names-migration.tf:168` |
| `module.globus_short_photo_bucket` | `secret-manager-secret` | `globus-photo-bucket` | `globus-short-secret-names-migration.tf:126` |
| `module.globus_short_pii_encryption_key` | `secret-manager-secret` | `globus-pii-encryption-key` | `globus-short-secret-names-migration.tf:70` |
| `module.globus_short_portal_session_secret` | `secret-manager-secret` | `globus-portal-session-secret` | `globus-short-secret-names-migration.tf:35` |
| `module.globus_short_seed_admin_email` | `secret-manager-secret` | `globus-seed-admin-email` | `globus-short-secret-names-migration.tf:98` |
| `module.globus_short_seed_admin_password` | `secret-manager-secret` | `globus-seed-admin-password` | `globus-short-secret-names-migration.tf:105` |
| `module.globus_short_sendgrid_from_email` | `secret-manager-secret` | `globus-sendgrid-from-email` | `globus-short-secret-names-migration.tf:91` |
| `module.globus_short_session_secret` | `secret-manager-secret` | `globus-session-secret` | `globus-short-secret-names-migration.tf:28` |
| `module.globus_short_sevis_batch_user_id` | `secret-manager-secret` | `globus-sevis-batch-user-id` | `globus-short-secret-names-migration.tf:273` |
| `module.globus_short_sevis_cert_encryption_key` | `secret-manager-secret` | `globus-sevis-cert-encryption-key` | `globus-short-secret-names-migration.tf:84` |
| `module.globus_short_sevis_cert_file` | `secret-manager-secret` | `globus-sevis-cert-file` | `globus-short-secret-names-migration.tf:259` |
| `module.globus_short_sevis_cert_password` | `secret-manager-secret` | `globus-sevis-cert-password` | `globus-short-secret-names-migration.tf:77` |
| `module.globus_short_sevis_program_sponsor_id` | `secret-manager-secret` | `globus-sevis-program-sponsor-id` | `globus-short-secret-names-migration.tf:266` |
| `module.globus_short_vite_google_maps_api_key` | `secret-manager-secret` | `globus-vite-google-maps-api-key` | `globus-short-secret-names-migration.tf:112` |
| `module.globus_short_webhook_secret` | `secret-manager-secret` | `globus-webhook-secret` | `globus-short-secret-names-migration.tf:292` |
| `module.host_portal_prod_anthropic_api_key` | `secret-manager-secret` | `host-portal-prod-anthropic-api-key` | `main.tf:872` |
| `module.host_portal_prod_auth0_audience` | `secret-manager-secret` | `host-portal-prod-auth0-audience` | `main.tf:881` |
| `module.host_portal_prod_auth0_client_id` | `secret-manager-secret` | `host-portal-prod-auth0-client-id` | `main.tf:890` |
| `module.host_portal_prod_auth0_domain` | `secret-manager-secret` | `host-portal-prod-auth0-domain` | `main.tf:899` |
| `module.host_portal_prod_auth0_mgmt_audience` | `secret-manager-secret` | `host-portal-prod-auth0-mgmt-audience` | `main.tf:908` |
| `module.host_portal_prod_auth0_mgmt_client_id` | `secret-manager-secret` | `host-portal-prod-auth0-mgmt-client-id` | `main.tf:917` |
| `module.host_portal_prod_auth0_mgmt_client_secret` | `secret-manager-secret` | `host-portal-prod-auth0-mgmt-client-secret` | `main.tf:926` |
| `module.host_portal_prod_auth0_namespace` | `secret-manager-secret` | `host-portal-prod-auth0-namespace` | `main.tf:935` |
| `module.host_portal_prod_globus_apikey` | `secret-manager-secret` | `host-portal-prod-globus-apikey` | `main.tf:944` |
| `module.host_portal_prod_globus_webhook_secret` | `secret-manager-secret` | `host-portal-prod-globus-webhook-secret` | `main.tf:953` |
| `module.host_portal_prod_openai_api_key` | `secret-manager-secret` | `host-portal-prod-openai-api-key` | `main.tf:962` |
| `module.host_portal_prod_postgres_admin_password` | `secret-manager-secret` | `host-portal-prod-postgres-admin-password` | `globus-short-secret-names-migration.tf:376` |
| `module.host_portal_prod_posthog_key` | `secret-manager-secret` | `host-portal-prod-posthog-key` | `main.tf:971` |
| `module.host_portal_prod_sendgrid_api_key` | `secret-manager-secret` | `host-portal-prod-sendgrid-api-key` | `main.tf:980` |
| `module.host_portal_prod_session_secret` | `secret-manager-secret` | `host-portal-prod-session-secret` | `main.tf:989` |
| `module.keycloak_prod_admin_password` | `secret-manager-secret` | `keycloak-prod-admin-password` | `main.tf:1064` |
| `module.keycloak_prod_db_password` | `secret-manager-secret` | `keycloak-prod-db-password` | `main.tf:1198` |
| `module.n8n_prod_api_key` | `secret-manager-secret` | `n8n-prod-api-key` | `globus-short-secret-names-migration.tf:425` |
| `module.n8n_prod_db_password` | `secret-manager-secret` | `n8n-prod-db-password` | `main.tf:1211` |
| `module.n8n_prod_encryption_key` | `secret-manager-secret` | `n8n-prod-encryption-key` | `main.tf:1219` |
| `module.n8n_prod_kc_admin_user` | `secret-manager-secret` | `n8n-prod-kc-admin-user` | `globus-short-secret-names-migration.tf:432` |
| `module.n8n_prod_postgres_admin_password` | `secret-manager-secret` | `n8n-prod-postgres-admin-password` | `globus-short-secret-names-migration.tf:383` |
| `module.n8n_prod_self_api_key` | `secret-manager-secret` | `n8n-prod-self-api-key` | `globus-short-secret-names-migration.tf:439` |
| `module.ppp2_prod_globus_api_key` | `secret-manager-secret` | `ppp2-prod-globus-api-key` | `main.tf:999` |
| `module.ppp2_prod_keycloak_admin_client_secret` | `secret-manager-secret` | `ppp2-prod-keycloak-admin-client-secret` | `main.tf:1007` |
| `module.ppp2_prod_passport_name_sync_secret` | `secret-manager-secret` | `ppp2-prod-passport-name-sync-secret` | `main.tf:1023` |
| `module.wildpacks_prod_hubspot_token` | `secret-manager-secret` | `wildpacks-prod-hubspot-token` | `main.tf:1033` |

### Compute (Cloud Run, GKE, jobs, schedulers)

15 items.

| Terraform address | Module or resource type | Identifier | Defined in |
| --- | --- | --- | --- |
| `google_cloud_run_service.host_company_portal_client_prod_cloudrun` | `google_cloud_run_service` | `host-company-portal-client-prod` | `host-portal-client-cloudrun-migration.tf:115` |
| `google_cloud_run_service.host_company_portal_server_prod` | `google_cloud_run_service` | `host-company-portal-server-prod` | `main.tf:2270` |
| `google_cloud_run_service.ppp2_client_run_prod` | `google_cloud_run_service` | `ppp2-participant-client-prod` | `main.tf:1404` |
| `google_cloud_run_service.ppp2_server_run_prod` | `google_cloud_run_service` | `ppp2-participant-server-prod` | `main.tf:1498` |
| `google_cloud_run_v2_service.sandbox_web` | `google_cloud_run_v2_service` | `sandbox-web-${local.sandbox_env}` | `branching-sandbox.tf:94` |
| `module.cloudbuild_teams_notifier` | `cloud-run-service` | `cloudbuild-teams-notifier` | `main.tf:1386` |
| `module.db_query_proxy_prod` | `cloud-run-service` | `db-query-proxy-prod` | `db-query-proxy.tf:150` |
| `module.db_query_proxy_prod_broker` | `cloud-run-service` | `db-query-proxy-prod-broker` | `db-query-proxy-broker.tf:104` |
| `module.globus_cluster_prod` | `gke-autopilot-cluster` | `globus-cluster-prod` | `main.tf:1161` |
| `module.keycloak_prod` | `cloud-run-service` | `applicant-keycloak` | `main.tf:1698` |
| `null_resource.n8n_baseline_deploy_email_template_prod` | `null_resource` |  | `main.tf:1297` |
| `null_resource.n8n_baseline_deploy_external_notification_prod` | `null_resource` |  | `main.tf:1321` |
| `null_resource.n8n_baseline_deploy_prod` | `null_resource` |  | `main.tf:1266` |
| `null_resource.n8n_baseline_deploy_sendgrid_webhook_receiver_prod` | `null_resource` |  | `main.tf:1346` |
| `null_resource.n8n_credentials_bootstrap_prod` | `null_resource` |  | `main.tf:1236` |

### Data stores (Cloud SQL, Redis, Firestore, Cloud Storage)

8 items.

| Terraform address | Module or resource type | Identifier | Defined in |
| --- | --- | --- | --- |
| `module.bucket_globus_prod_documents` | `storage-bucket` | `globus-prod-documents` | `main.tf:2069` |
| `module.bucket_globus_prod_photos` | `storage-bucket` | `globus-prod-photos` | `main.tf:2076` |
| `module.bucket_prod_globus_branding_assets` | `storage-bucket` | `prod-globus-branding-assets` | `main.tf:2083` |
| `module.bucket_prod_tfstate_own_backend` | `storage-bucket` | `prod-tfstate-production-499116` | `main.tf:2090` |
| `module.globus_db_prod` | `cloud-sql-postgres` | `globus-db-prod` | `main.tf:641` |
| `module.keycloak_db_prod` | `cloud-sql-postgres` | `keycloak-db-prod` | `main.tf:680` |
| `module.n8n_db_prod` | `cloud-sql-postgres` | `n8n-db-prod` | `main.tf:667` |
| `module.n8n_redis_prod` | `redis-instance` | `n8n-redis-prod` | `main.tf:1177` |

### Networking and load balancing

16 items.

| Terraform address | Module or resource type | Identifier | Defined in |
| --- | --- | --- | --- |
| `google_certificate_manager_certificate_map_entry.host_portal_alliance` | `google_certificate_manager_certificate_map_entry` | `host-portal-cloudrun-lb-prod-cert-map-entry-alliance` | `host-portal-dual-domain-cloudrun-lb-design.tf:353` |
| `google_certificate_manager_certificate_map_entry.host_portal_wildpacks` | `google_certificate_manager_certificate_map_entry` | `host-portal-cloudrun-lb-prod-cert-map-entry-wildpacks` | `host-portal-dual-domain-cloudrun-lb-design.tf:361` |
| `google_certificate_manager_certificate_map.host_portal_cloudrun_lb_prod` | `google_certificate_manager_certificate_map` | `host-portal-cloudrun-lb-prod-cert-map` | `host-portal-dual-domain-cloudrun-lb-design.tf:348` |
| `google_certificate_manager_certificate.host_portal_alliance` | `google_certificate_manager_certificate` | `host-portal-cloudrun-lb-prod-cm-cert-alliance` | `host-portal-dual-domain-cloudrun-lb-design.tf:328` |
| `google_certificate_manager_certificate.host_portal_wildpacks` | `google_certificate_manager_certificate` | `host-portal-cloudrun-lb-prod-cm-cert-wildpacks` | `host-portal-dual-domain-cloudrun-lb-design.tf:338` |
| `google_certificate_manager_dns_authorization.host_portal_alliance` | `google_certificate_manager_dns_authorization` | `host-portal-cloudrun-lb-prod-dnsauth-alliance` | `host-portal-dual-domain-cloudrun-lb-design.tf:316` |
| `google_certificate_manager_dns_authorization.host_portal_wildpacks` | `google_certificate_manager_dns_authorization` | `host-portal-cloudrun-lb-prod-dnsauth-wildpacks` | `host-portal-dual-domain-cloudrun-lb-design.tf:322` |
| `google_compute_global_address.globus_prod_ip` | `google_compute_global_address` | `globus-prod-ip` | `main.tf:1811` |
| `google_compute_global_address.n8n_prod_ip` | `google_compute_global_address` | `n8n-prod-ip` | `main.tf:1816` |
| `google_compute_global_address.wildpacks_prod_ip` | `google_compute_global_address` | `wildpacks-prod-ip` | `main.tf:1821` |
| `module.fw_default_allow_internal_prod` | `firewall-rule` | `default-allow-internal` | `main.tf:194` |
| `module.fw_prod_allow_internal` | `firewall-rule` | `fw-prod-allow-internal` | `main.tf:209` |
| `module.host_portal_cloudrun_lb_prod` | `load-balancer-https-multihost` |  | `host-portal-dual-domain-cloudrun-lb-design.tf:203` |
| `module.network_default_prod` | `network` | `default` | `main.tf:89` |
| `module.network_skilled_script_vpc_prod` | `network` | `skilled-script-vpc-prod` | `main.tf:146` |
| `module.ppp2_applicants_prod_lb` | `load-balancer-https` |  | `main.tf:1826` |

### Build, registry and delivery

11 items.

| Terraform address | Module or resource type | Identifier | Defined in |
| --- | --- | --- | --- |
| `module.ar_applicants_portal_prod` | `artifact-registry` | `applicants-portal` | `main.tf:1123` |
| `module.ar_globus_prod` | `artifact-registry` | `globus` | `main.tf:1084` |
| `module.ar_host_company_portal_prod` | `artifact-registry` | `host-company-portal` | `main.tf:2225` |
| `module.ar_keycloak_upstream_prod` | `artifact-registry` | `keycloak-upstream` | `main.tf:1135` |
| `module.ar_portal_prod` | `artifact-registry` | `portal` | `main.tf:1092` |
| `module.ar_ppp2_prod` | `artifact-registry` | `ppp2` | `main.tf:1108` |
| `module.ar_wildpacks_prod` | `artifact-registry` | `wildpacks` | `main.tf:1100` |
| `module.cloudbuild_github_connection_prod` | `cloudbuild-github-connection` |  | `main.tf:1911` |
| `module.db_query_proxy_prod_ar` | `artifact-registry` | `db-query-proxy` | `db-query-proxy.tf:89` |
| `module.sandbox_artifact_registry` | `artifact-registry` | `sandbox` | `branching-sandbox.tf:36` |
| `module.trigger_wildpacks_prod` | `cloudbuild-trigger` | `wildpacks-prod` | `main.tf:2038` |

### Project services, messaging and other

1 item.

| Terraform address | Module or resource type | Identifier | Defined in |
| --- | --- | --- | --- |
| `module.project_services_prod` | `project-services` | `53 items: analyticshub, artifactregistry, autoscaling, ...` | `main.tf:27` |

## Live state

### Terraform compared with live state

| Resource type | Live | In Terraform | Matched | Live only (unmanaged) | Platform-created | Terraform only | Name not resolvable |
| --- | --- | --- | --- | --- | --- | --- | --- |
| `api` | 63 | 53 | 53 | 10 | 0 | 0 | 0 |
| `cloud_run_service` | 9 | 9 | 9 | 0 | 0 | 0 | 0 |
| `cloud_run_job` | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| `cloud_sql` | 3 | 3 | 3 | 0 | 0 | 0 | 0 |
| `redis` | 1 | 1 | 1 | 0 | 0 | 0 | 0 |
| `bucket` | 8 | 4 | 4 | 3 | 1 | 0 | 0 |
| `secret` | 97 | 96 | 96 | 1 | 0 | 0 | 0 |
| `service_account` | 25 | 21 | 21 | 3 | 1 | 0 | 0 |
| `wif_pool` | 4 | 4 | 3 | 1 | 0 | 0 | 1 |
| `artifact_registry` | 9 | 9 | 9 | 0 | 0 | 0 | 0 |
| `cloudbuild_trigger` | 0 | 1 | 0 | 0 | 0 | 1 | 0 |
| `pubsub_topic` | 1 | 0 | 0 | 1 | 0 | 0 | 0 |
| `gke_cluster` | 1 | 1 | 1 | 0 | 0 | 0 | 0 |
| `domain_mapping` | 1 | 0 | 0 | 1 | 0 | 0 | 0 |
| `firewall_rule` | 5 | 2 | 2 | 0 | 3 | 0 | 0 |
| `vpc_network` | 2 | 2 | 2 | 0 | 0 | 0 | 0 |

### Live but not in Terraform

20 resources of the compared types exist in the project without a matching Terraform declaration.

| Resource type | Name |
| --- | --- |
| `api` | `anthos` |
| `api` | `apikeys` |
| `api` | `certificatemanager` |
| `api` | `cloudasset` |
| `api` | `cloudkms` |
| `api` | `containersecurity` |
| `api` | `distance-matrix-backend` |
| `api` | `iap` |
| `api` | `memorystore` |
| `api` | `orgpolicy` |
| `bucket` | `host-portal-db-backups-prod` |
| `bucket` | `production-499116-db-migration` |
| `bucket` | `terraform-state-bucket-produ` |
| `secret` | `n8n-prod-hubspot-webhook-secret` |
| `service_account` | `data-lake-bigquery` |
| `service_account` | `manus-agent-sa` |
| `service_account` | `ocr-service-prod-deployer` |
| `wif_pool` | `github-actions-sandbox` |
| `pubsub_topic` | `secret-rotation-notifications` |
| `domain_mapping` | `sandbox.allianceabroad.com` |

### In Terraform but not found live

| Resource type | Name | Terraform address | Defined in |
| --- | --- | --- | --- |
| `cloudbuild_trigger` | `wildpacks-prod` | `module.trigger_wildpacks_prod` | `main.tf:2038` |

### Created by GCP or GKE

These are created by the platform itself (default network rules, GKE firewall rules, default compute service accounts, Cloud Build buckets, container analysis topics) and are not drift.

| Resource type | Name |
| --- | --- |
| `bucket` | `production-499116_cloudbuild` |
| `service_account` | `445487831874-compute` |
| `firewall_rule` | `gke-globus-cluster-prod-c4b72319-all` |
| `firewall_rule` | `gke-globus-cluster-prod-c4b72319-vms` |
| `firewall_rule` | `k8s-fw-l7--ccab3f00c36e3a86` |

### Terraform items whose name is computed at plan time

These could not be matched by name, so they are not counted as matched or missing.

| Resource type | Terraform address | Defined in |
| --- | --- | --- |
| `wif_pool` | `google_iam_workload_identity_pool.sandbox` | `branching-sandbox.tf:62` |

### Resource types not compared

| Resource type | Reason | Declared in Terraform |
| --- | --- | --- |
| `firestore` | snapshot section unreadable (denied, API disabled or not collected) | 0 |
| `scheduler_job` | snapshot section unreadable (denied, API disabled or not collected) | 0 |

### Live reads that were denied or unavailable

| Read attempted | Result |
| --- | --- |
| gcloud firestore databases list | firestore.googleapis.com not enabled on project (SERVICE_DISABLED) - treated as no Firestore |
| gcloud scheduler jobs list | cloudscheduler.googleapis.com not enabled (SERVICE_DISABLED) - treated as none |
| gcloud deploy delivery-pipelines list | clouddeploy.googleapis.com not enabled - treated as none |
| gcloud billing projects describe | call returned 'backend request failed' (grouped with project describe); billing state not captured |

### Collection notes

Caveats recorded when the live snapshot was read.

- Run via gateway gcp_shell_exec env=prod as manus-agent-sa; several multi-command calls returned 'backend request failed' and were retried split into smaller calls.
- apis list is names only, all *.googleapis.com.
- Cloud Build triggers checked in global scope only (regional triggers not enumerated); GitHub connection found in us-central1 only.
- Cloud Run list covered all regions via --platform=managed; domain mappings checked only in northamerica-northeast1 and us-central1.
- Cloud Run jobs: none (empty list).
- IAP settings readable only at project level (no per-resource settings enumerated).
- GKE workloads not inspected (no kubectl). GKE ingress LBs for globus, n8n, wildpacks exist in this project.
- No secret values read.

## Live inventory

Selected stable attributes read from the running project.

### Cloud Run services (live)

| Service | Region | Ingress | Scaling | Runs as |
| --- | --- | --- | --- | --- |
| `applicant-keycloak` | `northamerica-northeast1` | `all` | `1-3` | `keycloak-runtime-prod@production-499116.iam.gserviceaccount.com` |
| `cloudbuild-teams-notifier` | `us-central1` | `internal-and-cloud-load-balancing` | `0-3` | `cloudbuild-teams-notifier@production-499116.iam.gserviceaccount.com` |
| `db-query-proxy-prod` | `us-central1` | `all` | `0-2` | `db-query-proxy-prod-runtime@production-499116.iam.gserviceaccount.com` |
| `db-query-proxy-prod-broker` | `us-central1` | `all` | `0-2` | `db-query-proxy-prod-broker@production-499116.iam.gserviceaccount.com` |
| `host-company-portal-client-prod` | `northamerica-northeast1` | `all` | `2-4` | `host-portal-client-run-prod@production-499116.iam.gserviceaccount.com` |
| `host-company-portal-server-prod` | `northamerica-northeast1` | `all` | `1-1` | `host-portal-server-run-prod@production-499116.iam.gserviceaccount.com` |
| `ppp2-participant-client-prod` | `northamerica-northeast1` | `all` | `1-3` | `ppp2-client-prod@production-499116.iam.gserviceaccount.com` |
| `ppp2-participant-server-prod` | `northamerica-northeast1` | `all` | `1-5` | `ppp2-server-prod@production-499116.iam.gserviceaccount.com` |
| `sandbox-web-prod` | `us-central1` | `all` | `0-2` | `sandbox-runtime-prod@production-499116.iam.gserviceaccount.com` |

### Cloud SQL instances (live)

| Instance | Region | Version | Tier | Edition | Private IP | Backups |
| --- | --- | --- | --- | --- | --- | --- |
| `n8n-db-prod` | `us-central1` | `POSTGRES_15` | `db-g1-small` | `ENTERPRISE` | `true` | `true` |
| `globus-db-prod` | `us-central1` | `POSTGRES_16` | `db-custom-2-7680` | `ENTERPRISE` | `true` | `true` |
| `keycloak-db-prod` | `us-central1` | `POSTGRES_15` | `db-g1-small` | `ENTERPRISE` | `true` | `true` |

### Redis instances (live)

| Instance | Region | Tier | Memory GB | Version |
| --- | --- | --- | --- | --- |
| `n8n-redis-prod` | `us-central1` | `BASIC` | `1` | `REDIS_7_2` |

### Cloud Storage buckets (live)

| Bucket | Location | Class | Versioning | Public access prevention |
| --- | --- | --- | --- | --- |
| `globus-prod-documents` | `US-CENTRAL1` | `STANDARD` | `false` | `inherited` |
| `globus-prod-photos` | `US-CENTRAL1` | `STANDARD` | `false` | `inherited` |
| `host-portal-db-backups-prod` | `US-CENTRAL1` | `STANDARD` |  | `enforced` |
| `prod-globus-branding-assets` | `US-CENTRAL1` | `STANDARD` | `false` | `inherited` |
| `prod-tfstate-production-499116` | `US-CENTRAL1` | `STANDARD` | `true` | `inherited` |
| `production-499116-db-migration` | `US-CENTRAL1` | `STANDARD` |  | `enforced` |
| `production-499116_cloudbuild` | `US-CENTRAL1` | `STANDARD` |  | `enforced` |
| `terraform-state-bucket-produ` | `NORTHAMERICA-NORTHEAST1` | `STANDARD` | `true` | `enforced` |

### Workload Identity pools (live)

| Pool | State | Providers |
| --- | --- | --- |
| `github-actions-apply-prod` | `ACTIVE` | `github-provider` |
| `github-actions-sandbox` | `ACTIVE` | `github-provider` |
| `github-actions-iac` | `ACTIVE` | `github-provider` |
| `github-actions-pool` | `ACTIVE` | `github-provider` |
| `production-499116.svc.id.goog` | `ACTIVE` |  |

## Drift between Terraform and reality

Drift comparison reports every compared type as matched except those below.

- Cloud Run, Cloud SQL, Redis, GKE, Artifact Registry and VPC networks: all live items are declared. There is no pattern of agent services deployed outside Terraform in this project (unlike the agents project).
- Buckets, service accounts, one secret, one Pub/Sub topic (`secret-rotation-notifications`) and several enabled APIs exist live but are not declared. Several are legacy or manually made (a data lake service account, an OCR deployer, `manus-agent-sa`, the legacy state bucket). Per PR #98, `host-portal-db-backups-prod` is the bucket that received a final Cloud SQL export of the Host Portal database (`host-portal-db-prod`) before that instance and its two secrets were removed from Terraform; the live-only bucket is not declared.
- `sandbox.allianceabroad.com` is a live Cloud Run domain mapping to `sandbox-web-prod` with no Terraform declaration. This is intentional: `branching-sandbox.tf` states that creating a domain mapping needs a verified owner of allianceabroad.com in Google's domain verification, which the Terraform service account is not, so a verified human creates it (`gcloud beta run domain-mappings create --region=us-central1 ... --service=sandbox-web-prod`), with a GoDaddy CNAME `sandbox` to `ghs.googlehosted.com`. The sandbox service runs in `us-central1` (domain mappings are not allowed in `northamerica-northeast1`); its Artifact Registry repo stays in `northamerica-northeast1`. The `github-actions-sandbox` WIF pool is live and `google_iam_workload_identity_pool.sandbox` in `branching-sandbox.tf` could not be resolved to it.
- The Cloud Build trigger `wildpacks-prod` is declared but not live (triggers were checked in the global scope only; regional triggers were not enumerated).
- Firewall rules created by GKE are platform-created and expected.
- Firestore and Cloud Scheduler APIs are disabled, so they were not compared.

## Security observations

These are facts from the snapshot and the code and should be reviewed:

- `allUsers` holds `roles/run.invoker` on both Host Portal Cloud Run services, by design for the load balancer path. The other Cloud Run services have ingress `all`; only `cloudbuild-teams-notifier` is restricted to internal and load balancer traffic. The Host Portal client service still has ingress `all`, which the code comments note can be tightened.
- Redis `n8n-redis-prod` has transit encryption disabled.
- No Cloud SQL instance has a public IPv4 address. `keycloak-db-prod` uses `ENCRYPTED_ONLY` while the other two require trusted client certificates. No instance has point-in-time recovery enabled and all are zonal (`/docs/disaster-recovery-plan` should be read against that).
- Four buckets (`globus-prod-documents`, `globus-prod-photos`, `prod-globus-branding-assets`, `prod-tfstate-production-499116`) show public access prevention as `inherited` rather than `enforced`; `versioning` is off on the three Globus buckets (`globus-prod-documents`, `globus-prod-photos`, `prod-globus-branding-assets`).
- Project IAM has no user, group or domain members; the only non-service-account member is the n8n Kubernetes service account bound for Workload Identity. One Google Maps API key is restricted to the Distance Matrix API.
- `environment-index-sa` (project `alliance-mcp-services`) holds `roles/run.viewer` on this project through `google_project_iam_member.environment_index_sa_run_viewer` in `main.tf`. The code comment says it lists Cloud Run services in this project to show which branch holds each pooled slot and what production serves, and describes the grant as read-only.
- `main.tf` also declares `google_project_iam_member.environment_index_pool_runtimes_run_viewer`. Its code comment says the environment-index dev/QA pool runtimes (sandbox branching model) list this project's services for the production rows of the board, and describes the grant as read-only.
- `main.tf` also declares `google_project_iam_member.environment_index_platform_sa_run_viewer`, which grants `roles/run.viewer`. Its code comment says the board moved to `alliance-platform-services` and that the new identity needs the same read-only view.
- `globus-prod-runtime` no longer holds project-wide `roles/storage.objectCreator` and `roles/storage.objectViewer`; since PR #283 it has those roles bound separately on `globus-prod-documents` and `globus-prod-photos` only, so it cannot read or write the state, Cloud Build, backup and branding buckets in this project.
- The production photo bucket name read by the Globus app (secret `globus-photo-bucket`) resolves to `globus-profile-photos-prod`, a bucket that lives in the development project, not in `production-499116`. `globus-prod-runtime` is granted `roles/storage.objectViewer` and `roles/storage.objectCreator` on it from `environments/development` (PR #281 and a follow-up import), because the project-level grants do not cross projects. Whether production should use a production-hosted bucket instead is recorded in the code as an unresolved data-residency question.

## Known gaps

- `alliance-platform-services` could not be read, and `alliance-mcp-services` was read through a development token with sections denied, so cross-project grants into production are only seen from the production side. Known examples are declared in `environments/development`, not in `environments/production` (for example grants on `globus-profile-photos-prod`).
- Billing state was not captured (call failed). Regional Cloud Build triggers, Cloud Run domain mappings outside `northamerica-northeast1` and `us-central1`, per-resource IAP settings and GKE workloads were not enumerated.
- The comment in `backend.tf` says the state bucket does not exist yet; the live snapshot shows it exists, so the comment is stale.
- DNS and the Cloud Run traffic split were not checked live; the cutover description comes from code comments dated 2026-09-18.
