LAB26_GCP_Architecture: Production VPC Topology

Architecture: Production VPC Topology

☁ GCP 🔥 Intermediate VPC Cloud NAT Firewalls IAP

Scenario

In a production environment, you never use the `default` VPC network. You need a custom VPC with strict boundaries. You will build a foundational networking stack for a 3-tier application. This includes a custom VPC network, two regional subnets (web and database), a Cloud Router + Cloud NAT for secure outbound internet access, and firewall rules that only allow SSH via Identity-Aware Proxy (IAP) rather than the public internet.

Your Objectives
  • Provision a custom google_compute_network.
  • Provision google_compute_subnetwork resources for "web" and "db", enforcing private_ip_google_access = true.
  • Deploy a google_compute_router and google_compute_router_nat mapping to all subnets in the region.
  • Create a google_compute_firewall allowing ingress on TCP 22 only from Google's IAP IP range (35.235.240.0/20).
  • Deploy a test Compute Engine instance in the web subnet with no public IP to verify the NAT and IAP configurations.

Additional Context

Identity-Aware Proxy (IAP) TCP Forwarding: Bastion hosts are outdated. GCP allows you to SSH into completely private VMs by tunneling the TCP connection through Google's IAM-secured proxy. To make this work, your VPC firewall must allow ingress on port 22 from `35.235.240.0/20` (the IP blocks used by IAP).

Private Google Access: If a VM has no external IP address, it normally cannot reach Google APIs (like Cloud Storage or the Artifact Registry). Turning on `private_ip_google_access` on the subnet routes requests for Google services internally through the VPC backbone.

variables.tf

variable "project_id" { type = string }
variable "gcp_region" { type = string; default = "us-central1" }

main.tf

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

# 1. Base Network
resource "google_compute_network" "prod_vpc" {
  name                    = "prod-network"
  auto_create_subnetworks = false
}

# 2. Subnets
resource "google_compute_subnetwork" "web_tier" {
  name                     = "web-subnet"
  network                  = google_compute_network.prod_vpc.id
  region                   = var.gcp_region
  cidr_range               = "10.10.10.0/24"
  private_ip_google_access = true
}

resource "google_compute_subnetwork" "db_tier" {
  name                     = "db-subnet"
  network                  = google_compute_network.prod_vpc.id
  region                   = var.gcp_region
  cidr_range               = "10.10.20.0/24"
  private_ip_google_access = true
}

# 3. Secure Egress (NAT)
resource "google_compute_router" "prod_router" {
  name    = "prod-router"
  network = google_compute_network.prod_vpc.id
}

resource "google_compute_router_nat" "prod_nat" {
  name                               = "prod-nat"
  router                             = google_compute_router.prod_router.name
  region                             = google_compute_router.prod_router.region
  nat_ip_allocate_option             = "AUTO_ONLY"
  source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_IP_RANGES"
}

# 4. Ingress Security (IAP Firewall)
resource "google_compute_firewall" "allow_iap_ssh" {
  name    = "allow-ssh-from-iap"
  network = google_compute_network.prod_vpc.id

  # GCP IAP IP range
  source_ranges = ["35.235.240.0/20"]

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

# 5. Test Workload (Private VM)
resource "google_compute_instance" "test_vm" {
  name         = "web-test-01"
  machine_type = "e2-micro"
  zone         = "${var.gcp_region}-a"
  
  boot_disk {
    initialize_params { image = "debian-cloud/debian-11" }
  }

  network_interface {
    network    = google_compute_network.prod_vpc.id
    subnetwork = google_compute_subnetwork.web_tier.id
    # No access_config = no public IP!
  }
}

Workflow Commands

terraform init
terraform apply -var="project_id=my-gcp-project-123"

# Connect to the private VM securely using IAP
gcloud compute ssh web-test-01 \
  --zone=us-central1-a \
  --tunnel-through-iap

# Inside the VM, confirm outbound internet via NAT works
# curl https://ifconfig.me

terraform destroy -var="project_id=my-gcp-project-123"