Architecture overview
The GCP Baseline is one Google Cloud organization with two folders and 14 projects, one Shared VPC host project that runs a hub VPC and two isolation-domain VPCs, guardrails and audit set once at the organization, and a landing-zone contract that every workload project publishes for the blueprints. Everything is OpenTofu and Terragrunt code in one repository, planned on a pull request and applied by CI on merge.
The projects
Each project has one job, and its project ID is built from the namespace, the folder and its name: acme-core-network, acme-plat-prod. The seed project acme-core-root exists before anything else and holds the state bucket.
| Project group | Projects | What they hold |
|---|---|---|
| Seed | core-root | The OpenTofu state bucket and the organization-level units; the quota project of the organization-level APIs |
| Evidence and detection | core-audit, core-security | The central log bucket, the archive bucket and the CIS log-based metrics; the metrics scope, the alert channel, Security Command Center wiring and the SOPS keys |
| Identity | core-identity | The IAM bindings of the 14 groups and the Privileged Access Manager entitlements |
| Network and DNS | core-network, core-dns | The Shared VPC host with its three VPCs, firewall policies, private DNS and IAP access; the public DNS zones |
| Delivery | core-artifacts, core-auto | Artifact Registry; Workload Identity Federation, the CI service accounts and the optional self-hosted runners |
| Reserved | core-corp, core-public | Baselined and ready; core-public is the one project allowed to serve public objects |
| Workloads | plat-sandbox, plat-dev, plat-staging, plat-prod | One project per stage, identical in shape |
Every project runs the same project baseline: a fixed list of enabled APIs, the default compute service account stripped of Editor, a deployer service account sa-acme-terraform-deployer that is Owner of its own project only, _Default log bucket retention with Log Analytics, OS Login, Essential Contacts and an optional budget alert. The four workload projects do not list their units one by one: each instantiates the same template.
unit "project-baseline" {
source = find_in_parent_folders("units/project-baseline")
path = "project-baseline"
values = {
is_seed_project = false
log_retention_days = local.log_retention_days
budget_amount = try(values.budget_amount, 0)
}
}
# Shared VPC attachment and subnet IAM of the stage (after the baseline and the host project's VPCs).
unit "shared-vpc-service" {
source = find_in_parent_folders("units/shared-vpc-service")
path = "shared-vpc-service"
}
# The landing-zone contract of the stage (Parameter Manager; after the VPC, DNS and identity units, and after
# the platform services units unless run with --feature platform_services=false, bootstrap/README.md step 16).
unit "pm-publish" {
source = find_in_parent_folders("units/pm-publish")
path = "pm-publish"
}
# Telemetry APIs and read access of the stage (auditors, the stage's engineer groups).
unit "observability" {
source = find_in_parent_folders("units/observability-link")
path = "observability"
}
The multi-project topology page lists every project and the units it runs.
The network
acme-core-network is the only Shared VPC host the organization policy allows. It runs three VPCs: the hub, prod for the production stage and nonprod for sandbox, dev and staging. The workload projects attach as service projects and may use only their own stage's subnets. The hub peers with each domain VPC, and the domains never peer with each other; peering is not transitive, so production and non-production have no route between them.
Every VPC has one Cloud NAT per region, and no virtual machine (VM) may have an external IP address. An organization firewall policy admits Google Front End health checks and Identity-Aware Proxy (IAP) TCP forwarding; a network firewall policy per VPC lets each domain reach only itself and the hub, and logs every denied attempt. The private zone internal.company.com is attached to all three VPCs. Engineers reach private resources through IAP TCP forwarding with OS Login; there is no VPN to run. Every range comes from one address plan, network_map.yaml. Shared VPC network and address plan explain both.
Identity
People get access through 14 Google groups, such as acme-platform-leads@company.com, bound to roles at the organization, on the two folders and on each stage project. Owner access is never standing: the platform and DevOps leads request it through Privileged Access Manager (PAM), for 12 hours at most, and production is read-only for everyone else. Auditors read configuration and metrics everywhere and logs only in the central audit log bucket in acme-core-audit; two IAM deny policies keep them away from data and from the workload projects' logs.
Pipelines hold no keys, and an organization policy forbids creating any. GitHub Actions signs in through Workload Identity Federation to the pool acme-github in acme-core-auto, becomes its repository's CI service account, such as sa-acme-baseline-ci, and impersonates the deployer of the target project.
Guardrails, audit and detection
| Control | Default | What it does |
|---|---|---|
| Organization policies | On | 19 constraints at the organization: no service account keys, no external IPs, OS Login, Shielded VM, uniform bucket access, allowed locations and member domains |
| Label constraints | Dry run | namespace, stage and managed_by labels required on VMs, GKE clusters and buckets; violations are logged until the constraints are enforced |
| IAM deny policy | On | Only the baseline CI account, the core-root and core-audit deployers and the platform leads may delete or alter log sinks and the audit buckets |
| Aggregated audit sinks | On | Every audit log, Data Access included, plus flow, firewall, DNS and NAT logs, into a 365-day log bucket and an archive bucket, both encrypted with Cloud KMS keys |
| Security Command Center | Standard | Active high and critical findings published to a Pub/Sub topic; the sandbox project muted; Premium when the organization subscribes |
| CIS log-based alerts | On | Log-based metrics over the central audit bucket and alert policies that notify the security mailbox |
| Artifact Analysis | On | Vulnerability scanning of every image pushed to Artifact Registry |
| Cloud Armor edge policy | Off | A hierarchical security policy at the plat folder with threat-intelligence deny lists, $3,000/month for Cloud Armor Enterprise |
| VPC Service Controls | Off | A perimeter around the workload projects, written as a dry run first |
Each switch sits in one of four switch files with its price beside it; variable inheritance shows which file owns which choice.
The landing-zone contract
Every workload project runs a pm-publish unit that writes the landing zone's values into Parameter Manager in that project: the domain VPC and the stage's subnets, the GKE control-plane range, the public and private DNS zones, the project ID and number, the Artifact Registry path, the stage's SOPS key and the Cloud Armor rule template. The Web App, Data and ETL, and Secrets blueprints read these parameters at plan time and never read the Baseline's state. Start with the Web App Blueprint, the Data and ETL Blueprint or the Secrets Blueprint.
Delivery
State for every unit lives in one bucket, acme-usw1-root-tfstate, in the seed project, with native locking. Three workflows move changes: a plan on every pull request after the guard scripts, tflint and Checkov; an apply on merge to main in the GitHub Environment prod, which requires a lead's approval; and drift detection that plans the critical folders every weekday night and every project on Mondays, and opens an issue when a project no longer matches its code. See GitOps and drift for the concepts, and landing zones for why the project is the unit of isolation.