Last reviewed: May 2026
Build the AWS services on the PCDE 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 PCDE-shape CI/CD + observability substrate — an Artifact Registry repo for built images, a Cloud Build trigger watching a stub GitHub source, a Cloud Deploy delivery pipeline with two Cloud Run targets (staging + prod), and a Cloud Monitoring SLO + alert policy on the prod target. Five blocks; the commit → build → deploy → observe loop PCDE tests.
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 (and optionally github-owner / github-repo in Step 3) in the snippets.Free or near-free at lab scope:
min_instances = 0: $0 idle.~$0/month at lab volume.
Enable Cloud Build, Cloud Deploy, Cloud Run, Artifact Registry, and Cloud Monitoring 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-pcde"
managed_by = "terraform"
}
}
resource "google_project_service" "cloudbuild" {
service = "cloudbuild.googleapis.com"
disable_on_destroy = false
}
resource "google_project_service" "clouddeploy" {
service = "clouddeploy.googleapis.com"
disable_on_destroy = false
}
resource "google_project_service" "run" {
service = "run.googleapis.com"
disable_on_destroy = false
}
resource "google_project_service" "artifactregistry" {
service = "artifactregistry.googleapis.com"
disable_on_destroy = false
}
resource "google_project_service" "monitoring" {
service = "monitoring.googleapis.com"
disable_on_destroy = false
}PCDE-canonical CI/CD shape: Cloud Build outputs images into Artifact Registry; Cloud Deploy promotes the same image SHA through environments. The repo is the single source-of-truth across all environments — immutable images, mutable tags.
resource "google_artifact_registry_repository" "images" {
repository_id = "certlabpro-pcde-images"
location = "us-central1"
format = "DOCKER"
labels = local.labels
depends_on = [google_project_service.artifactregistry]
}Cloud Build triggers are the PCDE commit-to-build primitive. We define a trigger on a GitHub repo's main branch — every push runs the cloudbuild.yaml at repo root, which would docker build + docker push into the Artifact Registry from Step 2.
Replace github-owner and github-repo with your actual repo. The trigger will provision but won't fire until the GitHub Cloud Build app is installed on the repo (manual one-time console step).
resource "google_cloudbuild_trigger" "main_push" {
name = "certlabpro-pcde-main-push"
description = "Build on push to main"
filename = "cloudbuild.yaml"
github {
owner = "github-owner" # REPLACE
name = "github-repo" # REPLACE
push {
branch = "^main$"
}
}
depends_on = [google_project_service.cloudbuild]
}Cloud Deploy is GCP's managed CD service — promotes a release through ordered stages (typically dev → staging → prod), each backed by a target (Cloud Run service, GKE cluster, or Anthos cluster). PCDE exam tests this release-once, promote-many shape extensively.
We define two Cloud Run target services (one for staging, one for prod, both scale-to-zero) + one delivery pipeline chaining them. A real release would be triggered via gcloud deploy releases create after a successful Cloud Build run; the lab provisions the substrate without firing a release.
resource "google_cloud_run_v2_service" "staging" {
name = "certlabpro-pcde-staging"
location = "us-central1"
template {
scaling { max_instance_count = 5 }
containers {
image = "us-docker.pkg.dev/cloudrun/container/hello"
}
}
labels = local.labels
depends_on = [google_project_service.run]
}
resource "google_cloud_run_v2_service" "prod" {
name = "certlabpro-pcde-prod"
location = "us-central1"
template {
scaling { max_instance_count = 10 }
containers {
image = "us-docker.pkg.dev/cloudrun/container/hello"
}
}
labels = local.labels
depends_on = [google_project_service.run]
}
resource "google_clouddeploy_target" "staging" {
name = "staging"
location = "us-central1"
run {
location = "projects/${data.google_project.current.project_id}/locations/us-central1"
}
depends_on = [google_project_service.clouddeploy]
}
resource "google_clouddeploy_target" "prod" {
name = "prod"
location = "us-central1"
run {
location = "projects/${data.google_project.current.project_id}/locations/us-central1"
}
require_approval = true # promotion to prod needs manual approval
depends_on = [google_project_service.clouddeploy]
}
data "google_project" "current" {}
resource "google_clouddeploy_delivery_pipeline" "main" {
name = "certlabpro-pcde-pipeline"
location = "us-central1"
serial_pipeline {
stages {
target_id = google_clouddeploy_target.staging.name
}
stages {
target_id = google_clouddeploy_target.prod.name
}
}
depends_on = [google_project_service.clouddeploy]
}PCDE's SLO / SLI / error budget vocabulary is the load-bearing observability shape — every PCDE exam question tagged Site Reliability tests this. We define a service-level objective: "99% of HTTP requests to the prod service should return 2xx over a rolling 28-day window." Then a Cloud Monitoring alert fires when the burn rate indicates the budget is being consumed too fast.
With the five blocks in place (Artifact Registry, Cloud Build trigger, two Cloud Run targets, Cloud Deploy pipeline, prod SLO + alert), the PCDE commit → build → deploy → observe loop is end-to-end provisioned.
resource "google_monitoring_service" "prod" {
service_id = "certlabpro-pcde-prod-svc"
display_name = "PCDE prod Cloud Run service"
basic_service {
service_type = "CLOUD_RUN"
service_labels = {
service_name = google_cloud_run_v2_service.prod.name
location = google_cloud_run_v2_service.prod.location
}
}
depends_on = [google_project_service.monitoring]
}
resource "google_monitoring_slo" "prod_availability" {
service = google_monitoring_service.prod.service_id
slo_id = "certlabpro-pcde-prod-availability"
display_name = "99% requests return 2xx (28-day rolling)"
goal = 0.99
rolling_period_days = 28
basic_sli {
availability {
enabled = true
}
}
}
resource "google_monitoring_alert_policy" "prod_budget_burn" {
display_name = "PCDE prod — fast budget burn"
combiner = "OR"
conditions {
display_name = "1-hour burn rate > 14.4 (fast burn)"
condition_threshold {
filter = "select_slo_burn_rate(\"${google_monitoring_slo.prod_availability.name}\", \"3600s\")"
duration = "0s"
comparison = "COMPARISON_GT"
threshold_value = 14.4 # consumes 2% of monthly budget in 1 hour
}
}
}terraform destroy tears down everything. The Cloud Deploy pipeline + targets destroy cleanly (no in-flight releases to block). The two Cloud Run services destroy (min_instances = 0 → no idle billing during the lab anyway). The Cloud Build trigger detaches. SLO + alert + monitoring service destroy cleanly.
PCDE covers many surfaces this lab can't fit — Cloud Logging deep dive (log routing, log sinks, log buckets, log analytics ↔ BigQuery integration), Cloud Trace + Cloud Profiler + Cloud Debugger (app perf), Error Reporting, Cloud Monitoring dashboards + custom metrics + uptime checks, Cloud Build private worker pools, Cloud Build approval rules, Cloud Deploy canary + blue-green strategies, Cloud Deploy custom render / verify steps, the Skaffold-based Cloud Deploy render pipeline, Binary Authorization for image-attestation enforcement, Container Threat Detection, Web App and API Protection (WAAP) / Cloud Armor, Anthos Config Management + Policy Controller, GKE Backup, the entire Google SRE Book / SRE Workbook practices that PCDE exam frequently references.
We stick to the Artifact Registry + Cloud Build + Cloud Deploy + Cloud Run + SLO/Alert primitives because they're the PCDE-canonical CI/CD/Operate loop. Logging / Trace / Profiler / Debugger / Error Reporting all plug into the same prod target service. Binary Authorization gates the Cloud Deploy promotion step. Cloud Monitoring SLOs are the load-bearing reliability primitive — get the shape right; layer more telemetry surfaces on as the architecture matures.
For service-by-service conceptual coverage, see the Browse, Playbook, and Editorial sections of this cert page.