Create a GCP project with org/gcp/project-factory¶
-
Purpose: Create (or converge) a GCP project using the Terraform Google Project Factory module via HyOps. Owner: Platform operations
-
Trigger: First-time GCP bootstrap for an environment; new project required.
- Impact: Creates cloud resources and links billing; may incur cost.
- Severity: P2
Pre-reqs:
hyops init gcpcompleted for the environment;gcloudinstalled and authenticated (ADC); Terraform Cloud token available (recommended: runhyops init terraform-cloud --env <env>), unless you run with local state (HYOPS_TERRAFORM_BACKEND_MODE=local).
If you run hyops init gcp --with-cli-login for a brand new project and the active gcloud identity can access the billing account but ADC cannot, HyOps now drives gcloud auth application-default login inline before project-factory rather than letting the mismatch surface later in Terraform.
- Rollback strategy: Use
hyops destroyonly if you intentionally want to delete the project. Note: the upstream module defaultsdeletion_policy=PREVENT, so destroy will fail unless you first apply withdeletion_policy: "DELETE"(see Destroy / purge).
Context¶
This runbook covers the project creation step. It assumes you have already initialised GCP credentials with:
- Runbook:
RB-026-hyops-init-gcp.md
org/gcp/project-factory runs via:
- Driver:
iac/terragrunt - Profile:
gcp@v1.0 - Pack:
gcp/org/00-project-factory@v1.0
After apply, re-run hyops init gcp --force so HyOps can auto-derive the Terraform impersonation target (service account email), validate it, and where needed bootstrap the Token Creator grant for the active operator identity before rechecking impersonation.
If the env-scoped GCP init config already contains GCP_BILLING_ACCOUNT_ID, HyOps can use that as the default billing account for this module when the module inputs omit billing_account / billing_account_id.
In the preferred GCP operating model, this project is the env-scoped
control project by default. That makes it the clean place for:
- GCP Secret Manager
- env-scoped backup bucket modules
- other env-scoped control services
It does not need to be the same project that owns the Shared VPC, Cloud NAT, or runner VM placement.
State backend (cloud vs local)¶
By default, the gcp@v1.0 Terragrunt profile uses HCP Terraform (Terraform Cloud) backend for state (cloud state, local execution).
- Default:
HYOPS_TERRAFORM_BACKEND_MODE=cloud - Local state (for offline/dev):
HYOPS_TERRAFORM_BACKEND_MODE=local(writes tfstate under<root>/state/terraform/)
Example:
# recommended for cloud provider stacks
export HYOPS_TERRAFORM_BACKEND_MODE=cloud
# or: local state backend
export HYOPS_TERRAFORM_BACKEND_MODE=local
Preconditions and safety checks¶
-
Ensure you are targeting the correct environment:
echo "HYOPS_ENV=$HYOPS_ENV"
-
Confirm you have a billing account:
gcloud beta billing accounts list
-
(Optional) Check if you have an Organization:
gcloud organizations list
Notes:
- Personal/trial accounts often show Listed 0 items. This is OK. In that case, omit org_id and folder_id in inputs.
Inputs¶
Create a local inputs file (do not commit it to a public repo if it contains real billing/org identifiers).
Minimal example (trial/personal accounts, no Organization)¶
project_id: hybridops-pf-dev
region: europe-west2
billing_account_id: "01B1FD-FAC417-423408"
activate_apis:
- cloudresourcemanager.googleapis.com
- iam.googleapis.com
- secretmanager.googleapis.com
labels:
owner: hybridops
env: dev
Enterprise example (Organization or Folder)¶
name_prefix: platform
context_id: dev
project_id: hybridops-pf-dev
region: europe-west2
billing_account_id: "01B1FD-FAC417-423408"
org_id: "123456789012" # OR folder_id: "123456789012"
activate_apis:
- cloudresourcemanager.googleapis.com
- iam.googleapis.com
- secretmanager.googleapis.com
labels:
owner: hybridops
env: dev
Notes:
- HyOps accepts billing_account_id (legacy alias) and will map it to the upstream input billing_account.
- Billing account values can be given either as the bare id (01ED84-...) or the Google resource form (billingAccounts/01ED84-...). HyOps normalizes both forms before Terraform runs.
- If module inputs omit billing, HyOps can default it from GCP_BILLING_ACCOUNT_ID in <root>/config/gcp.conf.
- org_id / folder_id are optional (recommended for enterprise governance).
- secretmanager.googleapis.com should be enabled by default if you plan to use GCP Secret Manager as the external secret authority for runner-driven DR.
- context_id is not the same thing as --env. --env selects the runtime lane; context_id is a naming token used by this module for stable naming and labels.
- If you create the durable overlay with hyops module init --env <env> --module org/gcp/project-factory, HyOps prefills project_id, region, billing_account_id, and context_id from the current env GCP init context when available. That generated file is the durable place to edit recovery values.
- If you recover the same env onto a different GCP project, changing project_id in that durable overlay now rolls this module to a new Terraform Cloud workspace automatically. That avoids treating the new project as an in-place replacement of the old project state.
- If the target project_id already exists and is accessible, hyops preflight now skips the billing-association permission check and lets project-factory converge project-scoped settings such as enabled APIs and labels. Billing-association permission remains required only when the lane still needs to create or adopt the project.
Steps¶
1) Preflight
INPUTS=/tmp/hyops-gcp-project-factory.dev.yml
hyops preflight --env dev --strict --module org/gcp/project-factory --inputs "$INPUTS"
2) Apply
hyops apply --env dev --module org/gcp/project-factory --inputs "$INPUTS"
3) Re-run GCP init to validate impersonation
hyops init gcp --env dev --force
4) Refresh GCP init into steady-state impersonation
hyops init gcp --env dev --force
Expected result:
project_bootstrap_pending=falseproject_access_validated=trueauth_mode="impersonation"impersonation_validated=true
5) Destroy / purge (optional)
By default, the upstream Project Factory module sets deletion_policy=PREVENT. That is a safe default, but it means hyops destroy cannot delete the project.
To intentionally delete the project:
# 1) converge deletion_policy into state
# (edit your inputs file and add: deletion_policy: "DELETE")
hyops apply --env dev --module org/gcp/project-factory --inputs "$INPUTS"
# 2) destroy using the same inputs
hyops destroy --env dev --module org/gcp/project-factory --inputs "$INPUTS"
Notes:
- GCP project deletion is asynchronous. A deleted project ID may remain reserved for a period of time, so immediate re-create with the same project_id can fail with 409 alreadyExists.
- For iterative testing, prefer a new project_id per run (for example hybridops-pf-dev2).
Verification¶
-
Confirm module state exists:
ls -la ~/.hybridops/envs/dev/state/modules/org__gcp__project-factory/latest.json
-
Confirm GCP readiness marker includes
impersonation_validated=true:cat ~/.hybridops/envs/dev/meta/gcp.ready.json
Troubleshooting¶
- If apply fails with permission errors:
- Use an existing project and skip project-factory, or
-
Ask an admin to create the project / grant permissions (project create + billing link).
-
If you have no Organization (
gcloud organizations listis empty): - Omit
org_id/folder_idand proceed only if your account can create projects under “No organization”.
License: MIT-0 for code, CC-BY-4.0 for documentation