SMS Blog

Google Cloud Landing Zone 101: What It Is, Why You Need It, and How to Build One

What It Is

A Google Cloud landing zone is a secure and scalable foundation for running workloads built as infrastructure as code. It standardizes the resource hierarchy, guardrails, networking, identity, observability, and security controls so teams can ship without reinventing the basics each time. Google’s landing zone guidance defines these domains and shows how they fit together.


Why You Need It

  • Consistency at scale so teams reuse proven patterns
  • Least privilege by design through organization policies and hierarchical firewalls
  • Separation of duties through host projects for networking and service projects for apps
  • Auditability through centralized logging and controllable egress
  • A path to advanced protection through Private Service Connect, VPC Service Controls, Interconnect, and Network Connectivity Center

AWS → Google Cloud Mental Map

If you’re coming from AWS, like me, think of accounts as projects in Google Cloud—smaller, easier containers for apps and billing. What AWS calls Service Control Policies become Google Cloud organization policies with a top-down firewall, so you set guardrails once at the org or folder and inherit them everywhere. The “central networking account” idea maps to a shared network per environment (Google Cloud’s Shared VPC) that app projects plug into. Security groups/NACLs (Network Access Control List) translate to firewall rules plus those top-level policies, so most rules live centrally. For identity, use your company groups for people and service accounts with short-lived access for automation. And when you need private, internal-only access to services, AWS PrivateLink maps to Google Cloud’s Private Service Connect.

How to Build One with Terraform

Assumptions

You have a Google Cloud Organization and Billing Account. Terraform will authenticate with Workload Identity Federation and service account impersonation so no long-lived keys are required. The snippets below are minimal and version pinning is expected. Variables are not shown for the sake of brevity.


Bootstrap and Authentication

Create a small bootstrap project and a versioned GCS bucket for Terraform state. Use impersonation in the provider block so CI exchanges an OIDC token for short-lived credentials.

terraform {
  required_version = ">= 1.6"
  required_providers {
    google = { source = "hashicorp/google", version = "~> 6.0" }
  }
  backend "gcs" {
    bucket = var.state_bucket
    prefix = "tf/landing-zone/bootstrap"
  }
}

provider "google" {
  project                     = var.bootstrap_project_id
  region                      = var.default_region
  impersonate_service_account = var.deploy_sa_email
}

This aligns with Google guidance to automate foundational setup and govern deployment through org-level identity.


Resource Hierarchy

Start with one Organization. Under it, create four peer folders: Bootstrap, Shared infrastructure, Development environment, and Production environment. Put your Terraform seed/admin projects in Bootstrap. Keep org-wide services (e.g., observability) in Shared infrastructure. In each environment folder, create a Shared VPC host project (for the hub) and attach service projects for workloads. If you need Testing, add another environment folder the same way. This follows Google’s environment-based design and keeps policies, IAM, and billing cleanly separated by environment.

Organization
├── Bootstrap                    # IaC seed/admin projects
├── Shared infrastructure        # org-wide logging/monitoring, etc.
├── Development environment
│   ├── env-shared               # e.g., KMS, secrets
│   ├── net-host-development     # Shared VPC host project
│   └── app-*                    # service projects attached to the host
├── Nonproduction environment    # optional, if you use a separate “nonprod”
│   ├── env-shared
│   ├── net-host-nonproduction
│   └── app-*
└── Production environment
    ├── env-shared
    ├── net-host-production
    └── app-*

Guardrails First with Organization Policies

Enforce constraints at the Organization and allow exceptions lower in the tree only when necessary. Use the managed constraint for service account keys and require OS Login. Restrict external VM IPs to drive private access patterns.

resource "google_org_policy_policy" "no_sa_keys" {
  name   = "organizations/${var.org_id}/policies/constraints/iam.managed.disableServiceAccountKeyCreation"
  parent = "organizations/${var.org_id}"
  spec { rules { enforce = true } }
}

resource "google_org_policy_policy" "require_oslogin" {
  name   = "organizations/${var.org_id}/policies/constraints/compute.requireOsLogin"
  parent = "organizations/${var.org_id}"
  spec { rules { enforce = true } }
}

resource "google_org_policy_policy" "no_external_ips" {
  name   = "organizations/${var.org_id}/policies/constraints/compute.vmExternalIpAccess"
  parent = "organizations/${var.org_id}"
  spec { rules { deny_all = true } }
}

These constraints are called out in Google’s landing zone and security foundations guidance.


Networking with Shared VPC per Environment

In Google Cloud a VPC network is global and subnets are regional. Create a Shared VPC in the host project for each environment then attach service projects and delegate minimal roles.

module "vpc_prod" {
  source       = "terraform-google-modules/network/google"
  version      = "~> 9.0"
  project_id   = var.prod_host_project_id
  network_name = "vpc-prod"
  subnets = [
    { subnet_name = "prod-use1", subnet_ip = "10.10.0.0/20", subnet_region = "us-east1" },
    { subnet_name = "prod-usc1", subnet_ip = "10.10.16.0/20", subnet_region = "us-central1" }
  ]
}

resource "google_compute_shared_vpc_service_project" "app1_attach" {
  host_project    = var.prod_host_project_id
  service_project = var.app1_project_id
}

resource "google_project_iam_member" "app1_netuser" {
  project = var.prod_host_project_id
  role    = "roles/compute.networkUser"
  member  = "serviceAccount:${var.app1_ci_sa}"
}

This implements the Shared VPC host and service project model described in the product documentation.


Private Access and Egress Control

Enable Private Google Access on subnets so instances without public IPs can reach Google APIs through private addresses. Provide outbound internet through Cloud NAT with logging.

resource "google_compute_router" "nat_router" {
  name    = "nat-router-use1"
  region  = "us-east1"
  project = var.prod_host_project_id
  network = module.vpc_prod.network_self_link
}

resource "google_compute_router_nat" "nat_use1" {
  name                               = "nat-use1"
  project                            = var.prod_host_project_id
  region                             = google_compute_router.nat_router.region
  router                             = google_compute_router.nat_router.name
  nat_ip_allocate_option             = "AUTO_ONLY"
  source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_IP_RANGES"
  log_config { enable = true, filter = "ERRORS_ONLY" }
}

Org and Folder Level Firewall Guardrails

Use hierarchical firewall policies for top-down allow and deny rules. Associate the policy at the Organization or at an environment folder and keep VPC local rules minimal.

resource "google_compute_firewall_policy" "org_fw" {
  name        = "org-default-guardrails"
  description = "Deny by default and allow only what is required"
}

resource "google_compute_firewall_policy_rule" "deny_ingress_all" {
  firewall_policy = google_compute_firewall_policy.org_fw.name
  priority        = 1000
  direction       = "INGRESS"
  enable_logging  = true
  action          = "deny"
  match { layer4_configs { ip_protocol = "all" } }
}

resource "google_compute_firewall_policy_association" "org_attach" {
  name              = "org-assoc"
  attachment_target = "organizations/${var.org_id}"
  firewall_policy   = google_compute_firewall_policy.org_fw.name
}

Security Posture to Wire in Early

Enable Security Command Center at the Organization so you have findings across projects. Wrap sensitive Google APIs in VPC Service Controls and define service perimeters with Access Context Manager as your context engine. These services complement IAM to reduce data exfiltration paths.

resource "google_access_context_manager_access_policy" "org" {
  parent = "organizations/${var.org_id}"
  title  = "org-access-policy"
}

resource "google_access_context_manager_access_level" "corp" {
  parent = google_access_context_manager_access_policy.org.name
  name   = "${google_access_context_manager_access_policy.org.name}/accessLevels/corp"
  basic {
    conditions { ip_subnetworks = var.corp_cidrs }
  }
}

resource "google_access_context_manager_service_perimeter" "data" {
  parent = google_access_context_manager_access_policy.org.name
  name   = "${google_access_context_manager_access_policy.org.name}/servicePerimeters/data"
  title  = "data"
  status {
    resources = [
      "projects/${var.prod_host_project_number}",
      "projects/${var.prod_app_project_number}"
    ]
    restricted_services = [
      "bigquery.googleapis.com",
      "storage.googleapis.com"
    ]
    access_levels = [google_access_context_manager_access_level.corp.name]
    vpc_accessible_services {
      enable_restriction = true
      allowed_services   = ["bigquery.googleapis.com","storage.googleapis.com"]
    }
  }
}

resource "google_pubsub_topic" "scc" {
  project = var.siem_project_id
  name    = "scc-findings"
}

resource "google_scc_v2_organization_notification_config" "scc_pubsub" {
  organization = var.org_id
  location     = var.scc_location
  config_id    = "pubsub-findings"
  pubsub_topic = "projects/${var.siem_project_id}/topics/${google_pubsub_topic.scc.name}"
  streaming_config { filter = "state = \\"ACTIVE\\"" }
}

resource "google_bigquery_dataset" "scc" {
  project    = var.siem_project_id
  dataset_id = "scc_findings"
  location   = "US"
}

resource "google_scc_v2_organization_scc_big_query_export" "scc_bq" {
  organization        = var.org_id
  location            = var.scc_location
  big_query_export_id = "scc-to-bq"
  dataset             = google_bigquery_dataset.scc.id
  filter              = "state = \\"ACTIVE\\""
}

Observability and Logging

Create sinks at Organization or folder scope to export Admin Activity, Data Access, VPC Flow Logs, and Cloud NAT logs to BigQuery or a SIEM. Cloud NAT logging captures translations and errors for analysis in Cloud Logging.

resource "google_bigquery_dataset" "logs" {
  project    = var.log_project_id
  dataset_id = "org_logs"
  location   = "US"
}

resource "google_logging_organization_sink" "org_to_bq" {
  org_id           = var.org_id
  name             = "org-logs-to-bq"
  destination      = "bigquery.googleapis.com/projects/${var.log_project_id}/datasets/${google_bigquery_dataset.logs.dataset_id}"
  include_children = true
  filter           = "logName:\\"cloudaudit.googleapis.com/activity\\" OR logName:\\"cloudaudit.googleapis.com/data_access\\" OR resource.type=(\\"gce_subnetwork\\" OR \\"gce_router\\")"
}

resource "google_bigquery_dataset_iam_member" "sink_writer" {
  project    = var.log_project_id
  dataset_id = google_bigquery_dataset.logs.dataset_id
  role       = "roles/bigquery.dataEditor"
  member     = google_logging_organization_sink.org_to_bq.writer_identity
}

Repository Layout that Scales

landing-zone/
  00-bootstrap/
  10-org-policies/
  20-network-core/
    prod-host/
      vpc/
      firewall-hier/
    nonprod-host/
  30-project-factory/
  40-security/

This layout matches the enterprise foundations blueprints and deployment methodology for repeatable rollouts.


Common Pitfalls

New builders often stumble on IP planning and peering. Pick your IP ranges early and avoid overlaps—Google Cloud’s network is global while subnets are regional. Don’t chain VPC peering to mimic a transit hub; instead, plug projects into the shared network for each environment. Also remember that “no public IP” doesn’t mean “no internet”—instances can still make outbound calls through your managed egress gateway, so turn on logs and control it. Finally, Google APIs are off by default: enable the ones you need in Terraform before creating resources to prevent confusing errors.

Next Steps

Start by sending logs to one place, set budget alerts and quotas, and use temporary credentials for Terraform instead of downloaded keys. Automate project creation and attachment to the shared network so every team starts the same way, every time. When you’re ready, add simple platform pieces like Cloud Run or GKE from blueprints, and only bolt on a VPN to your data center if you actually need it.

Conclusion

A landing zone in Google Cloud is just a safe, ready-made starting point you reuse for every team. Think “starter home” with the basics already wired: a clean folder structure, shared networks, sensible security rules, and logging. You want it because it removes guesswork, speeds up new projects, keeps costs tidy, and bakes in good security from day one—so you don’t rebuild the foundation every time.

To build it, keep it simple. Create four top-level folders: one for your setup tools, one for shared company services like logging and monitoring, and one each for development and production. In both development and production, stand up a single shared network and plug each app project into the right one. Keep servers off the public internet; when they need to call out, send their traffic through a small managed gateway and make sure it’s logged. Set organization-wide rules—no long-lived keys, use company logins for servers, and block public addresses by default—then only carve out exceptions when you truly need them. Run your infrastructure as code with temporary credentials so there are no secrets sitting around. Start with that foundation; when it’s steady, add the extras you actually need, like central security findings, tighter data boundaries, or private connections back to your data center. That’s your landing zone: clear, secure, and easy to grow.

Picture of Steve Youngblood

Steve Youngblood

Steve Youngblood has expertise in cloud automation, Terraform deployment, and AWS architecture, ensuring scalable and secure multi-account environments. His skills include designing secure infrastructures, implementing security best practices, and optimizing network performance. With a strong foundation in network engineering, he has worked with a wide range of technologies, providing expertise in infrastructure design, security, automation, and team mentorship to enhance operational efficiency. Steve likes camping as a break from screens, giving him time to unplug and enjoy the outdoors.

Leave a Reply

Your email address will not be published. Required fields are marked *