Last reviewed: May 2026
Build the AWS services on the PCSE 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, the smallest realistic PCSE security baseline — an Org Policy constraint preventing default-network creation, a Cloud KMS keyring + customer-managed encryption key with automatic rotation, a CMK-encrypted Cloud Storage bucket, and a Cloud Audit Logs sink routing IAM events into a dedicated logging bucket. Five blocks; the PCSE prevent → encrypt → audit loop.
Drop the snippets into a single main.tf, run terraform init, then terraform apply step-by-step.
>= 1.5 or OpenTofu >= 1.6.roles/orgpolicy.policyAdmin at project scope. The org-level version of the same constraint exists but needs Org Admin.your-project-id in the provider block.Near-free at lab scope:
~$0–$1/month. Cheap to leave running if you want to study the dashboards.
Enable Cloud KMS, Cloud Storage, Cloud Logging, and Org Policy 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-pcse"
managed_by = "terraform"
}
}
data "google_project" "current" {}
resource "google_project_service" "cloudkms" {
service = "cloudkms.googleapis.com"
disable_on_destroy = false
}
resource "google_project_service" "storage" {
service = "storage.googleapis.com"
disable_on_destroy = false
}
resource "google_project_service" "logging" {
service = "logging.googleapis.com"
disable_on_destroy = false
}
resource "google_project_service" "orgpolicy" {
service = "orgpolicy.googleapis.com"
disable_on_destroy = false
}Org Policy Service is PCSE's guardrail primitive — set constraints once at org / folder / project scope, and resources violating them can never be created. PCSE exam tests constraints-vs-IAM distinction: IAM gates who can act; Org Policy gates what can exist.
We apply compute.skipDefaultNetworkCreation at project scope — when this project is newly initialized, GCP normally creates a default VPC with overly-permissive firewall rules. The constraint prevents that. The PCSE-canonical prevent by default shape.
resource "google_project_organization_policy" "skip_default_network" {
project = data.google_project.current.project_id
constraint = "compute.skipDefaultNetworkCreation"
boolean_policy {
enforced = true
}
depends_on = [google_project_service.orgpolicy]
}Cloud KMS is PCSE's CMK primitive — customer-managed encryption keys for every encryption-at-rest scenario across GCS, BigQuery, Compute Engine disks, Cloud SQL, Spanner, Secret Manager. PCSE exam tests CMK rotation (always-on by default in CMEK; default 90-day rotation is the recommended setting) and the separation of keyring + key (the keyring is the IAM boundary; keys inherit it).
We create a regional keyring + a SYMMETRIC_ENCRYPTION key with 90-day rotation. Granting the Cloud Storage service account roles/cloudkms.cryptoKeyEncrypterDecrypter is required before the bucket in Step 4 can use the key — Step 4 includes that binding.
resource "google_kms_key_ring" "main" {
name = "certlabpro-pcse-keyring"
location = "us-central1"
depends_on = [google_project_service.cloudkms]
}
resource "google_kms_crypto_key" "storage_cmk" {
name = "storage-cmk"
key_ring = google_kms_key_ring.main.id
purpose = "ENCRYPT_DECRYPT"
rotation_period = "7776000s" # 90 days
lifecycle {
prevent_destroy = false # lab-only
}
}PCSE-recommended encryption-at-rest pattern: never use default Google-managed encryption for sensitive data; always wire a CMK. We grant the Cloud Storage service agent (a project-specific service account service-PROJECT_NUMBER@gs-project-accounts.iam.gserviceaccount.com) roles/cloudkms.cryptoKeyEncrypterDecrypter on the CMK, then provision a bucket that references the key.
Any object written to this bucket is wrapped with a data encryption key (DEK), which is itself wrapped by the CMK. Rotating the CMK rotates the next DEK; existing objects re-encrypt on next write. PCSE exam tests this envelope encryption shape.
resource "random_id" "suffix" {
byte_length = 4
}
resource "google_storage_project_service_account" "gcs_account" {}
resource "google_kms_crypto_key_iam_member" "gcs_kms" {
crypto_key_id = google_kms_crypto_key.storage_cmk.id
role = "roles/cloudkms.cryptoKeyEncrypterDecrypter"
member = "serviceAccount:${google_storage_project_service_account.gcs_account.email_address}"
}
resource "google_storage_bucket" "secure" {
name = "certlabpro-pcse-secure-${random_id.suffix.hex}"
location = "us-central1"
uniform_bucket_level_access = true
force_destroy = true # lab-only
encryption {
default_kms_key_name = google_kms_crypto_key.storage_cmk.id
}
labels = local.labels
depends_on = [google_kms_crypto_key_iam_member.gcs_kms]
}PCSE-recommended audit-log retention: the default _Required and _Default log buckets have 30-day retention. Sensitive audit events (IAM grants / revokes / policy edits) need much longer — often 7 years for SOX / HIPAA / FedRAMP compliance.
We create a dedicated logging bucket with 400-day retention + a log sink routing IAM-policy-related audit logs into it. The unique_writer_identity = true flag generates a per-sink service identity (preserves the audit chain — you can grant it write-only access without exposing your user identity).
With five blocks in place (provider+APIs, Org Policy guardrail, KMS keyring+CMK, CMK bucket, audit-log sink), the PCSE prevent → encrypt → audit baseline is shaped. Real PCSE deployments layer Security Command Center (SCC) Premium, VPC Service Controls perimeters, Binary Authorization, Cloud Armor WAF, Identity-Aware Proxy, Workforce Identity Federation, and Sensitive Data Protection (DLP) on this base.
resource "google_logging_project_bucket_config" "audit" {
project = data.google_project.current.project_id
location = "global"
retention_days = 400
bucket_id = "audit-events"
depends_on = [google_project_service.logging]
}
resource "google_logging_project_sink" "audit_sink" {
name = "certlabpro-pcse-audit-sink"
destination = "logging.googleapis.com/${google_logging_project_bucket_config.audit.id}"
filter = "logName:\"cloudaudit.googleapis.com\" AND (protoPayload.serviceName=\"iam.googleapis.com\" OR protoPayload.serviceName=\"cloudkms.googleapis.com\")"
unique_writer_identity = true
}
# Required so the sink can write to the bucket.
resource "google_project_iam_member" "audit_sink_writer" {
project = data.google_project.current.project_id
role = "roles/logging.bucketWriter"
member = google_logging_project_sink.audit_sink.writer_identity
}terraform destroy tears down everything. The KMS key is scheduled for destruction (24-hour grace period — Cloud KMS keys never delete immediately). The CMK-encrypted bucket destroys (force_destroy = true); the existing objects re-encrypt on the way out — Cloud Storage handles this transparently. The Org Policy constraint removes; default-network creation is permitted again on the project. Audit-log sink + bucket destroy cleanly.
PCSE covers many security surfaces this lab can't fit — Security Command Center (SCC Premium / Enterprise — the unified threat detection + posture management surface), VPC Service Controls (data exfil perimeters around BigQuery / Storage / etc.), Cloud Armor (WAF + DDoS + bot management), Identity-Aware Proxy (IAP — zero-trust app-level auth), Cloud HSM (FIPS-140-2 Level 3 hardware-backed KMS), Cloud External Key Manager (EKM — keys held outside Google), Confidential VMs / Confidential GKE Nodes (memory encryption), Binary Authorization (image-attestation gating), Container Threat Detection / Web App and API Protection (WAAP), Sensitive Data Protection (DLP — PII discovery + masking), Workforce Identity Federation, Workload Identity Federation, Access Context Manager (Context-Aware Access policies), and the entire Mandiant-acquired surface (threat-intel, attack-surface management, breach analytics).
We stick to the Org Policy + KMS + CMK Storage + Audit Logs primitives because they're the PCSE foundation every more-advanced control composes on top of. SCC reads from the same audit logs. VPC-SC perimeters wrap the same buckets. Cloud HSM swaps out KMS as the key backend. IAP overlays on the same IAM identities. Master the substrate; layer specialty controls.
For service-by-service conceptual coverage, see the Browse, Playbook, and Editorial sections of this cert page.