Use Case — Create the US prod VPC in GCP Oregon (us-west1)

Subsidiary US (10.0.0.0/12)
Provider / region GCP · us-west1 (Oregon)
Environment prod
VPC Global, custom-mode (us-gcp-prod-vpc)
Region block 10.4.0.0/16 (us-west1)
Subnets 4 regional subnets — one per tier, each spanning all 3 zones

1. How GCP differs from AWS

The same four-tier design maps onto GCP very differently, because of two platform facts:

  • A VPC is global. One VPC spans every region; you do not create a VPC per region. Regional subnets are added to the single global VPC.
  • A subnet is regional, not zonal. A regional subnet automatically covers every zone in the region (us-west1-a, -b, -c). You distribute workloads across zones using regional managed instance groups or GKE node pools — all drawing from the same subnet.

The practical result: the design that needed 12 subnets in AWS (4 tiers × 3 AZs) needs only 4 subnets in GCP (4 tiers × 1 regional subnet). The zone spread is automatic and free.

Aspect AWS (us-west-2) GCP (us-west1)
VPC scope Regional Global
Subnet scope One per AZ One per region (spans all zones)
Subnets for this design 12 4
Internet ingress/egress Internet Gateway + NAT gateway per AZ Default internet route + Cloud NAT (regional) + external IPs
Pod/container IPs Subnet IPs (or 100.64/10 for EKS) Alias secondary ranges on the subnet
L3/L4 filtering Security groups + NACLs VPC firewall rules (by network tag / service account)
Reserved IPs per subnet 5 4

2. Where this fits in the plan

The addressing inherits top-down exactly as in the reference architecture; only the per-VPC /20 tier collapses, because one global VPC serves the region rather than a discrete regional VPC block:

US subsidiary      10.0.0.0/12
  GCP              10.4.0.0/14
    us-west1       10.4.0.0/16
      prod env     10.4.0.0/18
        tiers      4 × /22 regional subnets   <-- this use case

3. Subnet plan

Four regional subnets, one per tier, keeping the /22-per-tier scheme. GKE pod and service ranges are allocated as secondary alias ranges from the enterprise CGNAT block (100.64.0.0/10), per the reference architecture, so they never consume routable RFC 1918 space.

Tier Subnet name Primary CIDR Secondary ranges (GKE) Egress
Public us-gcp-usw1-prod-public 10.4.0.0/22 external IP / external LB
App us-gcp-usw1-prod-app 10.4.4.0/22 pods 100.64.0.0/16, services 100.65.0.0/20 Cloud NAT
Data us-gcp-usw1-prod-data 10.4.8.0/22 none (isolated; PGA only)
Mgmt us-gcp-usw1-prod-mgmt 10.4.12.0/22 Cloud NAT
Global VPC · us-gcp-prod-vpc
│
└─ us-west1 (Oregon)   — every subnet below spans zones a · b · c
    ├─ public  10.4.0.0/22
    ├─ app     10.4.4.0/22    (pods 100.64.0.0/16 · services 100.65.0.0/20)
    ├─ data    10.4.8.0/22
    └─ mgmt    10.4.12.0/22

The spare /22 space in the region (10.4.16.0/20 onward) holds future tiers or a second VPC environment; the next GCP region (e.g. us-east4) takes 10.5.0.0/16.


4. Routing and access design

GCP has no per-subnet route table and no internet-gateway resource. The default route to the internet exists at the VPC level, and what actually controls reachability is whether instances have external IPs, whether Cloud NAT is attached, and the firewall rules.

  • Public — instances reach the internet directly via external IPs or sit behind an external load balancer. No Cloud NAT needed.
  • App and Mgmt — private (no external IPs). Outbound internet goes through Cloud NAT, attached to a regional Cloud Router. App NAT covers the pod alias range too, so GKE pods can pull images.
  • Data — excluded from Cloud NAT, so it has no usable internet egress despite the implied default route. Private Google Access is enabled so it can still reach Google APIs (e.g. Cloud Storage) privately. Add an egress-deny firewall rule if you want hard isolation.
  • Firewall — default-deny ingress. Representative rules: allow intra-enterprise traffic from 10.0.0.0/8, allow IAP (35.235.240.0/20) to the mgmt tag for SSH/RDP, and allow GCP load-balancer health-check ranges to the public/app tiers.

5. Terraform implementation

###############################################################################
# US office — GCP Oregon (us-west1) — prod
#
# Position in the enterprise IPAM hierarchy:
#   US subsidiary      10.0.0.0/12
#     GCP              10.0.0.0/14 -> 10.4.0.0/14
#       us-west1       10.4.0.0/16
#         prod env     10.4.0.0/18
#           tiers      4 x /22 regional subnets   <-- this file
#
# A GCP VPC is GLOBAL and subnets are REGIONAL (they span all zones in the
# region automatically), so this is 4 subnets, not 12.
###############################################################################

terraform {
  required_version = ">= 1.5"
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "~> 5.0"
    }
  }
}

variable "project_id" {
  type        = string
  description = "GCP project ID for the US prod environment"
}

provider "google" {
  project = var.project_id
  region  = local.region
}

###############################################################################
# Locals — the addressing plan
###############################################################################

locals {
  region = "us-west1" # Oregon

  labels = {
    subsidiary  = "us"
    provider    = "gcp"
    region      = "us-west1"
    environment = "prod"
    managed_by  = "terraform"
  }

  # Four regional subnets, one per tier.
  #   pga           = enable Private Google Access (private tiers reach Google APIs)
  #   nat           = route outbound internet through Cloud NAT
  #   nat_all_ranges = NAT the secondary (pod) ranges too, not just the primary
  #   secondary     = GKE alias ranges, carved from the enterprise 100.64.0.0/10 block
  subnets = {
    public = {
      cidr           = "10.4.0.0/22"
      pga            = false
      nat            = false
      nat_all_ranges = false
      secondary      = []
    }
    app = {
      cidr           = "10.4.4.0/22"
      pga            = true
      nat            = true
      nat_all_ranges = true
      secondary = [
        { name = "us-gcp-usw1-prod-app-pods", cidr = "100.64.0.0/16" },
        { name = "us-gcp-usw1-prod-app-svcs", cidr = "100.65.0.0/20" },
      ]
    }
    data = {
      cidr           = "10.4.8.0/22"
      pga            = true
      nat            = false
      nat_all_ranges = false
      secondary      = []
    }
    mgmt = {
      cidr           = "10.4.12.0/22"
      pga            = true
      nat            = true
      nat_all_ranges = false
      secondary      = []
    }
  }
}

###############################################################################
# Global VPC (custom subnet mode)
###############################################################################

resource "google_compute_network" "vpc" {
  name                    = "us-gcp-prod-vpc"
  auto_create_subnetworks = false
  routing_mode            = "GLOBAL" # dynamic routes propagate across regions
  description             = "US subsidiary, prod environment, global VPC"
}

###############################################################################
# Regional subnets (one per tier)
###############################################################################

resource "google_compute_subnetwork" "this" {
  for_each = local.subnets

  name                     = "us-gcp-usw1-prod-${each.key}"
  ip_cidr_range            = each.value.cidr
  region                   = local.region
  network                  = google_compute_network.vpc.id
  private_ip_google_access = each.value.pga

  dynamic "secondary_ip_range" {
    for_each = each.value.secondary
    content {
      range_name    = secondary_ip_range.value.name
      ip_cidr_range = secondary_ip_range.value.cidr
    }
  }
}

###############################################################################
# Cloud Router + Cloud NAT — outbound for the private tiers (app, mgmt)
###############################################################################

resource "google_compute_router" "router" {
  name    = "us-gcp-usw1-prod-router"
  region  = local.region
  network = google_compute_network.vpc.id
}

resource "google_compute_router_nat" "nat" {
  name                               = "us-gcp-usw1-prod-nat"
  router                             = google_compute_router.router.name
  region                             = local.region
  nat_ip_allocate_option             = "AUTO_ONLY"
  source_subnetwork_ip_ranges_to_nat = "LIST_OF_SUBNETWORKS"

  dynamic "subnetwork" {
    for_each = { for k, v in local.subnets : k => v if v.nat }
    content {
      name = google_compute_subnetwork.this[subnetwork.key].id
      source_ip_ranges_to_nat = (
        subnetwork.value.nat_all_ranges
        ? ["ALL_IP_RANGES"]          # include GKE pod/service ranges
        : ["PRIMARY_IP_RANGE"]
      )
    }
  }
}

###############################################################################
# Firewall rules (default-deny ingress is implicit)
###############################################################################

resource "google_compute_firewall" "allow_internal" {
  name      = "us-gcp-usw1-prod-allow-internal"
  network   = google_compute_network.vpc.id
  direction = "INGRESS"
  priority  = 1000

  # Allow traffic from anywhere in the enterprise supernet.
  source_ranges = ["10.0.0.0/8"]

  allow { protocol = "tcp" }
  allow { protocol = "udp" }
  allow { protocol = "icmp" }
}

resource "google_compute_firewall" "allow_iap" {
  name      = "us-gcp-usw1-prod-allow-iap-ssh-rdp"
  network   = google_compute_network.vpc.id
  direction = "INGRESS"
  priority  = 1000

  # Identity-Aware Proxy source range — admin access without public IPs.
  source_ranges = ["35.235.240.0/20"]
  target_tags   = ["mgmt"]

  allow {
    protocol = "tcp"
    ports    = ["22", "3389"]
  }
}

resource "google_compute_firewall" "allow_health_checks" {
  name      = "us-gcp-usw1-prod-allow-health-checks"
  network   = google_compute_network.vpc.id
  direction = "INGRESS"
  priority  = 1000

  # GCP load-balancer / health-check source ranges.
  source_ranges = ["130.211.0.0/22", "35.191.0.0/16"]
  target_tags   = ["public", "app"]

  allow { protocol = "tcp" }
}

###############################################################################
# Outputs
###############################################################################

output "network_id" {
  value = google_compute_network.vpc.id
}

output "subnet_self_links" {
  description = "Map of tier => subnet self link"
  value       = { for k, s in google_compute_subnetwork.this : k => s.self_link }
}

output "gke_secondary_ranges" {
  description = "Named alias ranges for GKE on the app subnet"
  value = {
    pods     = "us-gcp-usw1-prod-app-pods"
    services = "us-gcp-usw1-prod-app-svcs"
  }
}

6. Notes

  • One subnet, all zones. Because subnets are regional, high availability across us-west1-a/b/c comes from how you place workloads (regional MIGs, GKE regional clusters), not from the subnet layout.
  • GKE alias ranges. The pods and services secondary ranges are referenced by name when you create the GKE cluster (ip_allocation_policy). Sizing the pod range generously up front avoids a painful migration later — /16 supports large clusters.
  • IPAM-driven allocation. As with AWS, the CIDRs are explicit here for clarity. In production, allocate them from your IPAM source of truth (NetBox or GCP-side tooling) so the global plan stays authoritative.
  • Hard data isolation. The data tier has no Cloud NAT, but the VPC's implied default internet route still exists. For strict isolation add a low-priority egress-deny firewall rule targeting the data tag, leaving Private Google Access as the only outbound path.
  • Next layers. Hierarchical firewall policies (org/folder level), the GKE cluster itself, and internal load balancers are the natural follow-ons. The same module, re-pointed at 10.5.0.0/16, builds a second GCP region under the same global VPC.

Read more

SOP — Create and Manage Microsoft Azure for Enterprise

Version 1.0 Date 2026-06-22 Domain mptwork.com Model Identity rooted in the existing Microsoft Entra tenant; hierarchy of Management Groups → Subscriptions → Resource Groups → Resources Consoles Azure portal portal.azure.com · Entra admin center entra.microsoft.com Companion SOPs M365/Entra — sop-subscribe-manage-m365-mptwork.md, sop-entra-id-authentication-sso-mptwork.md · Network/IPAM — enterprise-ipam-reference-architecture.md · AWS

By admin

SOP — Create and Manage a Google Cloud (GCP) Account

Version 1.0 Date 2026-06-22 Domain mptwork.com Model GCP Organization rooted in Cloud Identity / Google Workspace for mptwork.com; hierarchy of Folders → Projects; workforce access via Google Groups, optionally federated to Microsoft Entra Companion SOPs Google Workspace — sop-subscribe-manage-google-workspace-mptwork.md · Entra auth & SSO — sop-entra-id-authentication-sso-mptwork.md · Network/IPAM — enterprise-ipam-reference-architecture.md

By admin