Last reviewed: May 2026
Build the AWS services on the PCNE exam with plain Terraform — one block at a time, each tied back to an exam domain. The same code works on OpenTofu.
By the end of this lab you'll have provisioned, with plain Terraform, a PCNE-shape network substrate — Shared VPC host-project enablement, a custom VPC with one regional subnet using Private Google Access, a Cloud Router + Cloud NAT for outbound-only egress, two firewall rules with VPC Flow Logs on the subnet, and a Cloud DNS private zone for internal name resolution. Five blocks; the PCNE hub-VPC + egress + DNS reference shape.
Drop the snippets into a single main.tf, run terraform init, then terraform apply step-by-step.
Note: this single-project lab demonstrates the Shared VPC host-project enablement but doesn't actually share the VPC with another service project (that requires Org Admin + two projects, out of scope for a quick lab).
>= 1.5 or OpenTofu >= 1.6.roles/compute.xpnAdmin at org scope — without org-level rights, the google_compute_shared_vpc_host_project resource will fail. Skip Step 2 and remove it from main.tf if you only have project-level rights.your-project-id in the provider block.Near-free:
~$0/month. Cheap to leave running.
Enable Compute, DNS, and Logging APIs.
terraform {
required_version = ">= 1.5"
required_providers {
google = { source = "hashicorp/google", version = "~> 6.0" }
}
}
provider "google" {
project = "your-project-id" # REPLACE
region = "us-central1"
}
locals {
labels = {
project = "certlabpro-pcne"
managed_by = "terraform"
}
}
resource "google_project_service" "compute" {
service = "compute.googleapis.com"
disable_on_destroy = false
}
resource "google_project_service" "dns" {
service = "dns.googleapis.com"
disable_on_destroy = false
}
resource "google_project_service" "logging" {
service = "logging.googleapis.com"
disable_on_destroy = false
}Shared VPC is the PCNE-canonical multi-project networking pattern — one host project owns the VPC, multiple service projects consume it via attachments. Workloads in service projects get IPs from the host VPC's subnets without the host project owning the workload-tier resources. PCNE exam tests this host vs service separation as the load-bearing scale pattern.
We enable host-project mode on the current project (google_compute_shared_vpc_host_project), then carve a /20 subnet with Private Google Access enabled (lets VMs without external IPs reach *.googleapis.com) and VPC Flow Logs turned on.
data "google_project" "current" {}
resource "google_compute_shared_vpc_host_project" "host" {
project = data.google_project.current.project_id
depends_on = [google_project_service.compute]
}
resource "google_compute_network" "main" {
name = "certlabpro-pcne-vpc"
auto_create_subnetworks = false
routing_mode = "REGIONAL"
depends_on = [google_project_service.compute]
}
resource "google_compute_subnetwork" "main" {
name = "certlabpro-pcne-subnet"
ip_cidr_range = "10.10.0.0/20"
region = "us-central1"
network = google_compute_network.main.id
private_ip_google_access = true
log_config {
aggregation_interval = "INTERVAL_5_SEC"
flow_sampling = 0.5
metadata = "INCLUDE_ALL_METADATA"
}
}Cloud NAT gives subnets outbound internet access without per-VM external IPs — the PCNE-canonical private-by-default egress pattern. The shape: a Cloud Router is the BGP control-plane primitive (used by NAT, VPN, Interconnect); a Cloud NAT attaches to it and provides the data plane.
We enable NAT with AUTO_ONLY NAT IP allocation (Google picks the addresses; production deployments pin to specific reserved IPs for upstream firewall allowlists). Logging on errors only — full-log mode generates substantial Cloud Logging volume.
resource "google_compute_router" "nat_router" {
name = "certlabpro-pcne-router"
region = "us-central1"
network = google_compute_network.main.id
}
resource "google_compute_router_nat" "nat" {
name = "certlabpro-pcne-nat"
router = google_compute_router.nat_router.name
region = "us-central1"
nat_ip_allocate_option = "AUTO_ONLY"
source_subnetwork_ip_ranges_to_nat = "LIST_OF_SUBNETWORKS"
subnetwork {
name = google_compute_subnetwork.main.id
source_ip_ranges_to_nat = ["ALL_IP_RANGES"]
}
log_config {
enable = true
filter = "ERRORS_ONLY"
}
}PCNE-recommended firewall posture: default-deny ingress (GCP's default), allow only what's necessary. We add internal allow-all (any TCP / UDP / ICMP between VMs in the subnet) + IAP SSH allow (TCP/22 from Google's IAP gateway range, so VMs without public IPs are still SSH-reachable via gcloud compute ssh --tunnel-through-iap).
This is the same two-rule pattern as the ACE lab — PCNE deepens it with firewall policies at org / folder scope (hierarchical firewalls that compose with project-level rules; out of scope here).
resource "google_compute_firewall" "allow_internal" {
name = "certlabpro-pcne-allow-internal"
network = google_compute_network.main.name
direction = "INGRESS"
source_ranges = ["10.10.0.0/20"]
allow {
protocol = "tcp"
}
allow {
protocol = "udp"
}
allow {
protocol = "icmp"
}
}
resource "google_compute_firewall" "allow_iap_ssh" {
name = "certlabpro-pcne-allow-iap-ssh"
network = google_compute_network.main.name
direction = "INGRESS"
source_ranges = ["35.235.240.0/20"]
allow {
protocol = "tcp"
ports = ["22"]
}
}PCNE-canonical pattern: internal hostnames resolve via a Cloud DNS private zone attached to the VPC. VMs in the VPC see this zone's records; VMs outside don't. Use case: stable, human-readable hostnames for services (api.internal.acme.com, db.internal.acme.com) that resolve to the VM's private IP.
We create the zone internal.acme.com + one example A record. Production deployments often pair this with Cloud DNS DNS forwarding for hybrid setups (on-prem DNS queries forward to the cloud zone, and vice versa via inbound server policies). With five blocks in place (provider+APIs, Shared VPC + subnet + flow logs, Cloud NAT, firewalls, Cloud DNS private zone), the PCNE substrate is complete.
resource "google_dns_managed_zone" "internal" {
name = "certlabpro-pcne-internal"
dns_name = "internal.acme.com."
description = "PCNE lab private DNS zone"
visibility = "private"
private_visibility_config {
networks {
network_url = google_compute_network.main.id
}
}
labels = local.labels
depends_on = [google_project_service.dns]
}
resource "google_dns_record_set" "api_example" {
managed_zone = google_dns_managed_zone.internal.name
name = "api.${google_dns_managed_zone.internal.dns_name}"
type = "A"
ttl = 300
rrdatas = ["10.10.0.10"]
}terraform destroy tears down everything. Shared VPC host-project disables (must remove any service-project attachments first — none in this lab). Cloud NAT + router detach cleanly. VPC + subnet + firewalls destroy. Cloud DNS private zone destroys (the example record goes with it). VPC Flow Logs stop ingesting.
PCNE covers many networking surfaces this lab can't fit — Cloud VPN (HA / Classic), Cloud Interconnect (Dedicated / Partner), Network Connectivity Center (NCC — the hub-and-spoke control plane), Cross-Cloud Interconnect, Private Service Connect (PSC for service / endpoint / publisher patterns), VPC Peering between VPCs (separate from Shared VPC), Cloud Load Balancing (the entire LB family — global HTTP(S), regional internal, network LB, proxy network LB), Cloud CDN, Cloud Armor (WAF / DDoS), Identity-Aware Proxy (IAP), Cloud DNS DNS forwarding / inbound server policies (the hybrid-DNS shape), Cloud DNS DNSSEC, the legacy Default Firewall Rules posture, hierarchical firewall policies at org / folder scope, Network Intelligence Center (Connectivity Tests / Performance Dashboard / Firewall Insights / Network Topology), and VPC Service Controls perimeters (covered in [[gcp-pcse]] from the data-exfil angle).
We stick to the Shared VPC + subnet + NAT + firewall + private DNS primitives because they're the PCNE foundation every more-advanced network pattern composes on. Cloud VPN / Interconnect attach to the same Cloud Router. Load Balancers front the same firewall-gated VMs. PSC publishes services into the same VPC. Hierarchical firewalls layer above the project-level rules. Master the substrate.
For service-by-service conceptual coverage, see the Browse, Playbook, and Editorial sections of this cert page.