LAB20_GCP_Egress Networks: Cloud NAT and Cloud Router

Egress Networks: Cloud NAT and Cloud Router

☁ GCP 🟢 Beginner Cloud NAT Cloud Router Egress

Scenario

You have provisioned a Virtual Machine sitting on a custom VPC subnet. For security, you explicitly did not give the VM a public IP address. Now, the VM needs to reach the internet to download a software patch via `apt-get`. You need to configure a Cloud NAT (Network Address Translation) gateway to give private resources outbound internet access.

Your Objectives
  • Create a custom google_compute_network and a private google_compute_subnetwork.
  • Create a google_compute_router attached to the network in your region (Cloud NAT requires a Cloud Router).
  • Create a google_compute_router_nat attached to the Cloud Router.
  • Set the NAT ip allocation to AUTO_ONLY (Google provisions and manages the public IPs dynamically based on load).
  • Set the NAT to apply to ALL_SUBNETWORKS_ALL_IP_RANGES.

Additional Context

Where's the Internet Gateway? Unlike AWS (where you build an IGW, an Elastic IP, a NAT Gateway, and manually edit Route Tables so `0.0.0.0/0` points to the NAT), GCP abstracts all of this magic. Creating a Cloud NAT attached to a Cloud Router automatically programmes the implicit VPC routes for all chosen subnetworks. Egress traffic just "works".

Cloud Router Dependency: Cloud NAT is actually a software-defined feature built on top of Cloud Router. Even though we aren't using BGP (Border Gateway Protocol) routing here, the Router control plane is required.

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. Custom VPC Network
resource "google_compute_network" "private_vpc" {
  name                    = "private-app-vpc"
  auto_create_subnetworks = false
}

# 2. Private Subnet (VMs here will get no public IPs)
resource "google_compute_subnetwork" "private_subnet" {
  name          = "private-subnet-us"
  network       = google_compute_network.private_vpc.id
  region        = var.gcp_region
  cidr_range    = "10.0.1.0/24"
}

# 3. Cloud Router (Prerequisite for Cloud NAT)
resource "google_compute_router" "router" {
  name    = "nat-router"
  network = google_compute_network.private_vpc.name
  region  = var.gcp_region
}

# 4. Cloud NAT Configuration
resource "google_compute_router_nat" "nat" {
  name                               = "nat-gateway"
  router                             = google_compute_router.router.name
  region                             = google_compute_router.router.region
  
  # Tell GCP to allocate and scale the external NAT IPs automatically
  nat_ip_allocate_option             = "AUTO_ONLY"
  
  # Allow all instances in all subnets in this region to use this NAT for egress
  source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_IP_RANGES"

  log_config {
    enable = true
    filter = "ERRORS_ONLY"
  }
}

# 5. A test VM with NO public IP to prove it works
resource "google_compute_instance" "private_vm" {
  name         = "isolated-worker"
  machine_type = "e2-micro"
  zone         = "${var.gcp_region}-a"

  boot_disk {
    initialize_params { image = "debian-cloud/debian-11" }
  }

  network_interface {
    network    = google_compute_network.private_vpc.name
    subnetwork = google_compute_subnetwork.private_subnet.name
    # No access_config block means NO public internet IP assigned
  }

  metadata_startup_script = "curl -s -o /dev/null http://example.com"
}

outputs.tf

output "nat_id" {
  value = google_compute_router_nat.nat.id
}

Workflow Commands

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

# SSH into the private VM using IAP (Identity-Aware Proxy bypasses the need for public IPs)
gcloud compute ssh isolated-worker --zone=us-central1-a --tunnel-through-iap

# Inside the VM, verify outbound internet works:
# curl https://ifconfig.me
# ^ This will return the Google-managed NAT IP address, proving egress works.

terraform destroy -var="project_id=my-gcp-project-123"
✓ Expected Output After Apply
Apply complete! Resources: 5 added, 0 changed, 0 destroyed.

Outputs:

nat_id = "my-gcp-project-123/us-central1/nat-router/nat-gateway"