Last reviewed: May 2026
Build the AWS services on the PCA 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 five-block PCA reference architecture — a custom VPC with a regional subnet and Cloud NAT for outbound-only egress, a GKE Autopilot cluster as the workload runtime, a Cloud SQL Postgres instance with private IP only (reachable from GKE via VPC peering), and a Cloud Storage app bucket. This is the shape every PCA exam scenario starts from.
Drop the snippets into a single main.tf, run terraform init, then terraform apply step-by-step.
>= 1.5 or OpenTofu >= 1.6.your-project-id in the provider block.terraform apply: gcloud container clusters get-credentials certlabpro-pca-cluster --region us-central1.Three line items bill while idle:
db-f1-micro tier exists but is being deprecated; PCA exam now references db-perf-optimized-N SKUs.~$125/month with everything provisioned. Destroy promptly after the lab session — this is the most expensive lab in the GCP set.
Enable Compute, GKE, Cloud SQL, Service Networking (for Cloud SQL private IP), and Cloud Storage 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-pca"
managed_by = "terraform"
}
}
resource "google_project_service" "compute" {
service = "compute.googleapis.com"
disable_on_destroy = false
}
resource "google_project_service" "container" {
service = "container.googleapis.com"
disable_on_destroy = false
}
resource "google_project_service" "sqladmin" {
service = "sqladmin.googleapis.com"
disable_on_destroy = false
}
resource "google_project_service" "servicenetworking" {
service = "servicenetworking.googleapis.com"
disable_on_destroy = false
}
resource "google_project_service" "storage" {
service = "storage.googleapis.com"
disable_on_destroy = false
}PCA-recommended pattern: private VMs / pods have no external IPs, but still need to reach external dependencies (pip / docker / API calls). The answer is Cloud NAT — a managed regional NAT service that gives private resources outbound-only egress.
We carve a /20 subnet with two secondary ranges for GKE (pods + services), provision a Cloud Router, and attach a Cloud NAT to it.
resource "google_compute_network" "main" {
name = "certlabpro-pca-vpc"
auto_create_subnetworks = false
depends_on = [google_project_service.compute]
}
resource "google_compute_subnetwork" "main" {
name = "certlabpro-pca-subnet"
ip_cidr_range = "10.10.0.0/20"
region = "us-central1"
network = google_compute_network.main.id
private_ip_google_access = true
secondary_ip_range {
range_name = "pods"
ip_cidr_range = "10.20.0.0/14"
}
secondary_ip_range {
range_name = "services"
ip_cidr_range = "10.24.0.0/20"
}
}
resource "google_compute_router" "nat_router" {
name = "certlabpro-pca-router"
region = "us-central1"
network = google_compute_network.main.id
}
resource "google_compute_router_nat" "nat" {
name = "certlabpro-pca-nat"
router = google_compute_router.nat_router.name
region = "us-central1"
nat_ip_allocate_option = "AUTO_ONLY"
source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_IP_RANGES"
log_config {
enable = true
filter = "ERRORS_ONLY"
}
}GKE Autopilot is the PCA-recommended GKE mode — Google manages the node pool, you pay per pod-vCPU / pod-memory consumed. Compare to GKE Standard where you size and pay for the node pool yourself. PCA exam tests this Autopilot-vs-Standard trade-off as the recurring managed-vs-control shape.
We enable VPC-native (alias IPs — the pods get IPs from the secondary range in Step 2, not from NAT), private cluster (node IPs are private; control plane gets a private endpoint too), and Workload Identity (the PCA-recommended pattern for pod → GCP-API auth — no node-level service-account credentials).
resource "google_container_cluster" "main" {
name = "certlabpro-pca-cluster"
location = "us-central1" # regional Autopilot
enable_autopilot = true
network = google_compute_network.main.id
subnetwork = google_compute_subnetwork.main.id
ip_allocation_policy {
cluster_secondary_range_name = "pods"
services_secondary_range_name = "services"
}
private_cluster_config {
enable_private_nodes = true
enable_private_endpoint = false # public control plane for kubectl access
master_ipv4_cidr_block = "172.16.0.0/28"
}
deletion_protection = false # lab-only
depends_on = [google_project_service.container]
}PCA-recommended database connectivity: private IP via VPC peering — Cloud SQL provisions inside a Google-managed VPC that peers with yours, the instance gets a private IP, and pods reach it over private networking. Public-IP Cloud SQL is the anti-pattern the PCA exam tests against.
The shape: (1) allocate a /16 from your VPC for Service Networking to use, (2) peer the VPC with servicenetworking.googleapis.com, (3) provision the Cloud SQL instance with ipv4_enabled = false + the peered network reference.
resource "google_compute_global_address" "private_ip_alloc" {
name = "certlabpro-pca-sql-peer"
purpose = "VPC_PEERING"
address_type = "INTERNAL"
prefix_length = 16
network = google_compute_network.main.id
}
resource "google_service_networking_connection" "sql_peering" {
network = google_compute_network.main.id
service = "servicenetworking.googleapis.com"
reserved_peering_ranges = [google_compute_global_address.private_ip_alloc.name]
depends_on = [google_project_service.servicenetworking]
}
resource "google_sql_database_instance" "main" {
name = "certlabpro-pca-pg"
database_version = "POSTGRES_15"
region = "us-central1"
settings {
tier = "db-perf-optimized-N-2"
availability_type = "ZONAL" # lab-only; production = REGIONAL
ip_configuration {
ipv4_enabled = false
private_network = google_compute_network.main.id
}
backup_configuration {
enabled = true
point_in_time_recovery_enabled = true
}
}
deletion_protection = false # lab-only
depends_on = [
google_service_networking_connection.sql_peering,
google_project_service.sqladmin,
]
}PCA reference architectures always include a Cloud Storage bucket for something — uploads, exports, ML feature data, static web assets. We add one here, uniform-bucket-level-access on, lifecycle moving data to Nearline after 30 days.
With five blocks in place (VPC + NAT, Autopilot GKE, Cloud SQL with private IP, GCS bucket), the PCA reference shape is complete: workloads run on GKE, talk to Postgres over private networking, write artifacts to GCS, egress through Cloud NAT.
resource "random_id" "suffix" {
byte_length = 4
}
resource "google_storage_bucket" "app" {
name = "certlabpro-pca-app-${random_id.suffix.hex}"
location = "US"
uniform_bucket_level_access = true
force_destroy = true # lab-only
lifecycle_rule {
condition {
age = 30
}
action {
type = "SetStorageClass"
storage_class = "NEARLINE"
}
}
labels = local.labels
depends_on = [google_project_service.storage]
}terraform destroy tears down everything. The GKE cluster destroys cleanly (the cluster fee stops immediately, ~$74/month saved). The Cloud SQL instance destroys (lab-only deletion_protection = false, ~$50/month saved). The Cloud NAT + router detach; the VPC peering range frees up. The GCS bucket destroys (force_destroy = true).
PCA covers many architecture-tier surfaces this lab can't fit — Cloud Load Balancing (global HTTP(S) LB with Cloud CDN + Cloud Armor in front of GKE), Cloud Identity-Aware Proxy (IAP) for app-level auth, Cloud Run + Cloud Functions for serverless app tiers, Cloud Pub/Sub for async messaging, Cloud Tasks / Cloud Scheduler, Memorystore (Redis / Memcached) for caching, Cloud Spanner / Bigtable / Firestore for data tier alternatives, Cloud Composer / Workflows for orchestration, BigQuery for analytics, Vertex AI for ML, Cloud DNS / Cloud Domains, Cloud KMS for CMK encryption ([[gcp-pcse]]), Cloud Interconnect / VPN for hybrid, Anthos for multi-cloud, Cloud Asset Inventory + Cloud Asset Service, Resource Manager hierarchy (folders + orgs), Cloud Operations Suite (logging + monitoring + APM + profiling + tracing).
We stick to the VPC + Cloud NAT + GKE + Cloud SQL + GCS primitives because they're the PCA reference architecture spine — every other GCP service plugs into this base. Spanner / Bigtable substitute for Cloud SQL. Cloud Run substitutes for GKE for serverless apps. Pub/Sub adds async messaging between GKE pods. The shape stays.
For service-by-service conceptual coverage, see the Browse, Playbook, and Editorial sections of this cert page.