LAB31_GCP_Multi-Project Networking: Shared VPC

Multi-Project Networking: Shared VPC

☁ GCP 🔥 Intermediate Shared VPC Host Project Service Project IAM

Scenario

In enterprise environments, networking resources (VPCs, VPNs, Interconnects) are centralized in a single "Host Project" managed by the Network Administrators. Developers build applications in isolated "Service Projects". Shared VPC allows you to share subnetworks from the Host Project into the Service Projects. You need to enable a Host Project, attach a Service Project, and deploy a VM in the Service Project using the Host's subnet.

Your Objectives
  • You will need two project IDs for variables: host_project_id and service_project_id.
  • Enable Shared VPC using google_compute_shared_vpc_host_project.
  • Attach the service project using google_compute_shared_vpc_service_project.
  • Create a Subnet in the Host Project.
  • Grant the Service Project's default Compute Service Account the roles/compute.networkUser role on the Host Project subnet.
  • Deploy a Compute Engine instance in the Service Project that seamlessly uses the Host Project's subnet.

Additional Context

Subnet-Level vs Project-Level Sharing: You can either grant `networkUser` on the entire Host Project (giving developers access to all subnets globally), or strictly at the subnet level (giving the `web-dev` team access only to the `us-central1-web` subnet). Subnet-level sharing is the recommended security posture.

Billing: In Shared VPC, the Service Project pays for the Compute Engine VMs and egress data transfer, even though the network infrastructure lives in the Host Project.

variables.tf

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

main.tf

provider "google" {
  # No default project block here, passing project variables explicitly
}

# 1. Enable the Host Project
resource "google_compute_shared_vpc_host_project" "host" {
  project = var.host_project_id
}

# 2. Attach the Service Project to the Host
resource "google_compute_shared_vpc_service_project" "service1" {
  host_project    = google_compute_shared_vpc_host_project.host.project
  service_project = var.service_project_id
}

# 3. Create a Network + Subnet in the HOST project
resource "google_compute_network" "shared_vpc" {
  name                    = "shared-network"
  project                 = google_compute_shared_vpc_host_project.host.project
  auto_create_subnetworks = false
}

resource "google_compute_subnetwork" "shared_subnet" {
  name          = "app-subnet-usea1"
  project       = google_compute_shared_vpc_host_project.host.project
  region        = var.gcp_region
  network       = google_compute_network.shared_vpc.id
  cidr_range    = "10.100.0.0/24"
}

# 4. Fetch the Service Project's default Compute Service Account
data "google_project" "service" {
  project_id = var.service_project_id
}

# 5. Grant Subnet-level NetworkUser IAM role to the Service Project SA
# This allows VMs in the service project to connect to this specific subnet
resource "google_compute_subnetwork_iam_member" "network_user" {
  project    = google_compute_shared_vpc_host_project.host.project
  region     = google_compute_subnetwork.shared_subnet.region
  subnetwork = google_compute_subnetwork.shared_subnet.name
  
  # The required role for Shared VPC usage
  role       = "roles/compute.networkUser"
  
  # Using the default Compute Engine SA string formatting
  member     = "serviceAccount:${data.google_project.service.number}[email protected]"
}

# 6. Deploy the VM in the SERVICE project, but attach it to the HOST subnet
resource "google_compute_instance" "app_vm" {
  name         = "shared-vpc-worker"
  project      = var.service_project_id
  zone         = "${var.gcp_region}-a"
  machine_type = "e2-micro"

  depends_on = [
    google_compute_shared_vpc_service_project.service1,
    google_compute_subnetwork_iam_member.network_user
  ]

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

  network_interface {
    # Notice how we must specify the fully qualified self_link for cross-project
    subnetwork = google_compute_subnetwork.shared_subnet.self_link
  }
}

Workflow Commands

terraform init

# IMPORTANT: Ensure the APIs are enabled in both projects first!
# gcloud services enable compute.googleapis.com --project=my-host-project
# gcloud services enable compute.googleapis.com --project=my-service-project

terraform apply \
  -var="host_project_id=my-host-project-111" \
  -var="service_project_id=my-service-project-222"

terraform destroy \
  -var="host_project_id=my-host-project-111" \
  -var="service_project_id=my-service-project-222"