How to Build a Production-Ready GCP Foundation with Terraform
A practical reference architecture for separating environments, controlling access, managing Terraform state, monitoring spend, and giving a growing team a safe path to production on Google Cloud.

A production GCP foundation is not a collection of services selected from the console. It is the operating boundary that determines who can change production, where workloads live, how costs are attributed, what gets logged, and whether the environment can be rebuilt after a bad change.
This guide builds a realistic starting point for a 15-person SaaS company. The company has one product team, a customer-facing API, a background worker, a PostgreSQL database, and GitHub Actions for delivery. It expects to add engineers and customers, but it does not need an enterprise landing zone with dozens of folders and committees.
This is a reference production scenario, not a client case study. The goal is to show the decisions, controls, and validation steps that make the foundation useful.
1. Define the design target before writing Terraform
The foundation should answer concrete operating questions. Before creating a project, write down:
- Which customer journey must remain available?
- Which data is sensitive, and where may it be stored?
- Who can deploy to production?
- Who can approve an infrastructure change?
- How much downtime and data loss can the business tolerate?
- Who receives a billing alert, and what action should they take?
For this scenario, the API has a 99.9% monthly availability objective, production changes require review, the database recovery point objective is 15 minutes, and the business owner receives budget alerts alongside the platform lead. These decisions influence architecture. Without them, “best practice” becomes decoration.
2. Use projects as isolation boundaries
Google Cloud resources sit under an organization, optional folders, and projects. A small company does not need a deep hierarchy, but production should not share a project with development. Separate projects provide clearer IAM, billing, quotas, API enablement, and blast radius.
A practical initial hierarchy is:
organization
├── bootstrap
│ ├── prj-tf-state
│ └── prj-cicd-identity
├── nonproduction
│ └── prj-saas-dev
└── production
└── prj-saas-prod
The bootstrap projects hold shared control-plane resources. Workload projects contain application infrastructure. Folders let you apply policies and access at an environment boundary without granting the same permissions everywhere.
Do not mirror every department in the company. Google recommends designing the hierarchy around cloud operating needs rather than copying the corporate org chart. For a small team, environment and risk are usually more stable boundaries than reporting lines.
A minimal Terraform layout
foundation/
├── bootstrap/
├── environments/
│ ├── dev/
│ └── prod/
├── modules/
│ ├── project/
│ ├── budget/
│ └── workload-identity/
└── policies/
Keep environment roots small. Reusable modules should express a tested pattern, while each root should make environment-specific inputs and state explicit.
3. Bootstrap remote Terraform state deliberately
Terraform cannot use a remote state bucket until that bucket exists. Treat bootstrap as a small, controlled exception: create the state project and bucket once, migrate the bootstrap state into it, then require reviewed automation for future changes.
resource "google_storage_bucket" "terraform_state" {
name = "acme-prod-tf-state"
project = google_project.terraform_state.project_id
location = "US"
uniform_bucket_level_access = true
public_access_prevention = "enforced"
versioning {
enabled = true
}
lifecycle_rule {
condition { num_newer_versions = 20 }
action { type = "Delete" }
}
}
State may contain resource identifiers and sensitive values. Restrict bucket access to the deployment identity and a small break-glass group. Enable versioning so an accidental state overwrite can be recovered. Do not use one state file for every environment; a failed development change should not lock or damage production state.
terraform {
backend "gcs" {
bucket = "acme-prod-tf-state"
prefix = "foundation/prod"
}
}
4. Separate human access from deployment identity
Grant human access to groups, not individual users. A reasonable starting model is:
- gcp-platform-admins: administers the foundation through reviewed changes.
- gcp-prod-viewers: reads production configuration, logs, and metrics.
- gcp-security-reviewers: reviews IAM, audit logs, and policy changes.
- gcp-break-glass: tightly controlled emergency access with monitoring and a documented process.
Avoid broad basic roles such as Owner and Editor for daily work. Prefer predefined roles, then narrow further when the access pattern is understood. Production access should be rarer than non-production access.
Do not store a service-account key in GitHub
Use Workload Identity Federation so GitHub Actions exchanges its OIDC identity for short-lived Google credentials. Google specifically recommends attribute conditions for multi-tenant identity providers such as GitHub. Restrict the provider to your organization and bind the deployment identity to the exact repository or branch required.
resource "google_iam_workload_identity_pool_provider" "github" {
workload_identity_pool_id = google_iam_workload_identity_pool.github.workload_identity_pool_id
workload_identity_pool_provider_id = "github"
oidc { issuer_uri = "https://token.actions.githubusercontent.com" }
attribute_mapping = {
"google.subject" = "assertion.sub"
"attribute.repository" = "assertion.repository"
"attribute.ref" = "assertion.ref"
}
attribute_condition = "assertion.repository_owner == 'acme-example'"
}
Create a dedicated deployment service account per pipeline and environment. The production pipeline should not share an identity with development, and it should receive only the permissions required to deploy its workload.
5. Choose the smallest network model that meets the risk
Not every workload needs Shared VPC on day one. If the team has one product and one production project, a custom VPC in each environment is easier to operate. Shared VPC becomes useful when several service projects need centrally managed subnets, routes, firewalls, or hybrid connectivity.
For the SaaS scenario:
- Create separate custom VPCs for development and production.
- Do not use the default network.
- Allocate non-overlapping address ranges so future connectivity remains possible.
- Keep databases on private addresses.
- Use narrowly scoped firewall rules with logging where the evidence is useful.
- Document every required outbound path; private workloads still need a deliberate egress design.
The objective is not “private everything.” The objective is an explainable traffic path with known ingress, egress, identity, and logging behavior.
6. Add guardrails that produce action
Budgets notify; they do not automatically cap spend
Create budgets before workloads launch. Set thresholds that match the team’s response process, such as 50%, 80%, 100%, and forecasted 100%. Route notifications to both engineering and the budget owner. If the response requires automation, publish budget notifications to Pub/Sub and build the action explicitly.
resource "google_billing_budget" "production" {
billing_account = var.billing_account_id
display_name = "Production monthly budget"
budget_filter {
projects = ["projects/${google_project.production.number}"]
}
amount {
specified_amount {
currency_code = "USD"
units = "2500"
}
}
threshold_rules { threshold_percent = 0.5 }
threshold_rules { threshold_percent = 0.8 }
threshold_rules { threshold_percent = 1.0 }
}
Centralize the evidence you will need
Cloud Audit Logs answer who changed what. Application logs answer what the workload experienced. Route organization-level audit logs to a central project with retention matching business and regulatory needs. Avoid collecting every possible data-access log without estimating volume and cost; enable high-value logs deliberately.
At minimum, production should have:
- Uptime or synthetic checks for the customer-facing path.
- Request success rate and latency measurements.
- Database availability, connections, storage, and backup monitoring.
- Alerts that require action, with an owner and runbook.
- A tested restore procedure, not only an enabled backup setting.
7. Validate the foundation before the first production deploy
A successful Terraform apply does not prove production readiness. Run a short acceptance test:
- Identity: confirm a developer cannot mutate production directly.
- Pipeline: deploy through GitHub without a stored Google service-account key.
- Change control: verify production Terraform requires review and a successful plan.
- Network: test approved ingress and egress, then confirm an unapproved path fails.
- Observability: generate a known application error and find it in logs and metrics.
- Alerting: trigger one non-destructive alert and confirm the correct person receives it.
- Recovery: restore a backup into an isolated environment and measure elapsed time.
- Cost: confirm the project appears in billing reports and budget notifications have recipients.
Capture the result in a one-page readiness record: check, evidence, owner, date, and unresolved risk. This makes the foundation an operating system instead of a Terraform repository nobody revisits.
What this foundation intentionally leaves out
This design does not automatically require Shared VPC, VPC Service Controls, a security command center program, centralized CI runners, or a large folder hierarchy. Those controls may become appropriate as data sensitivity, teams, compliance, or connectivity grow. Adding them without a clear threat or operating requirement creates complexity without proving lower risk.
Start with isolation, identity, reproducible infrastructure, evidence, recovery, and cost ownership. Expand the landing zone when the business produces a reason.
Official references
- Google Cloud landing zone design
- Decide a resource hierarchy
- Terraform style and structure best practices
- Workload Identity Federation best practices
- Service account security best practices
- Cloud Billing budgets and alerts
If you are building your first production GCP environment and want the foundation reviewed before customer traffic arrives, the Cloud Foundation Launch turns these decisions into an implemented, documented handoff.
Work With Us
Want help applying this to your environment?
Every environment is different. A 60-minute Power Hour gets you specific recommendations for your AWS or GCP setup — with a written action plan in your inbox next morning.