9.1 Infrastructure as Code (IaC) with Terraform on Google Cloud
Key Takeaways
- Terraform is Google Cloud's premier declarative Infrastructure as Code (IaC) framework, managing cloud resources across the official google and google-beta providers while maintaining state in a centralized Cloud Storage backend with native object locking and Object Versioning.
- Enterprise infrastructure architectures structure code into modular hierarchies (root and child modules) using the Cloud Foundation Toolkit (CFT), enforcing separation of environments via distinct directory trees and isolated state backends rather than shared Terraform workspaces.
- Automated IaC delivery pipelines execute through Cloud Build using short-lived Service Account Impersonation and enforce automated Policy-as-Code guardrails via Terraform Validator and Policy Controller before provisioning.
- State management best practices require automated drift detection via refresh-only plans, controlled state imports for unmanaged legacy assets, and strategic use of lifecycle meta-arguments (prevent_destroy, create_before_destroy, and ignore_changes).
- Google Cloud Config Connector (KCC) and Anthos Config Sync provide a Kubernetes-native declarative alternative, managing GCP infrastructure as Kubernetes Custom Resources continuously reconciled by the GKE control plane.
Infrastructure as Code (IaC) with Terraform on Google Cloud
Architectural Objective: Infrastructure as Code (IaC) is the cornerstone of repeatable, auditable, and resilient enterprise cloud operations. A Google Professional Cloud Architect must design robust Terraform architectures using Google Cloud Storage remote backends, structure enterprise-grade modular blueprints, isolate multi-environment state boundaries, automate CI/CD pipeline execution with least-privilege service account impersonation, manage infrastructure drift, and evaluate Kubernetes-native declarative tools like Google Cloud Config Connector.
Declarative Infrastructure Principles & The Google Cloud Providers
In modern cloud engineering, infrastructure automation follows two paradigms:
+---------------------------------------------------------------------------------------------------+
| IMPERATIVE VS. DECLARATIVE INFRASTRUCTURE |
+---------------------------------------------------------------------------------------------------+
| PARADIGM | MECHANISM | EXECUTION CHARACTERISTICS | FAILURE PROFILE |
+-------------+-----------------------------+-------------------------------+-----------------------+
| Imperative | Bash scripts, gcloud CLI, | Defines step-by-step procedural| Partial failures leave|
| | Client SDKs, REST APIs | instructions on *how* to build| orphaned resources and|
| | | resources sequentially. | unrecoverable state. |
+-------------+-----------------------------+-------------------------------+-----------------------+
| Declarative | HashiCorp Terraform (HCL), | Defines the *desired end state*| Engine calculates dependency|
| | Config Connector (CRDs), | of infrastructure; engine | graph and applies only|
| | KRM (Kubernetes Resource) | reconciles current vs target. | necessary deltas. |
+---------------------------------------------------------------------------------------------------+
The google and google-beta Providers
HashiCorp Terraform interacts with Google Cloud APIs through two distinct upstream providers:
googleProvider: Contains generally available (GA) resources and features supported by Google Cloud SLAs. Used for production-critical infrastructure components.google-betaProvider: Exposes preview, alpha, and beta capabilities alongside GA resources. In enterprise architectures,google-betais referenced when bleeding-edge features (such as newer GKE Autopilot settings or private service connect attributes) are required before general availability.
# Provider configuration pattern for enterprise blueprints
terraform {
required_version = ">= 1.6.0"
required_providers {
google = {
source = "hashicorp/google"
version = "~> 5.30.0"
}
google-beta = {
source = "hashicorp/google-beta"
version = "~> 5.30.0"
}
}
}
Terraform State Architecture: Google Cloud Storage Remote Backend
Terraform records metadata and mappings between declared HCL configurations and real-world GCP infrastructure in a terraform.tfstate file. Storing state locally on developer laptops introduces severe concurrency risks, lost state, and security leaks (as state files contain cleartext metadata and potential secrets).
+-----------------------------------------------------------------------------------+
| GCS REMOTE STATE BACKEND ARCHITECTURE |
+-----------------------------------------------------------------------------------+
| [ Developer / CI/CD ] ──> terraform apply ──> [ GCS Backend: gs://tf-state-prod ] |
| │ |
| ┌─────────────────┴─────────────────┐ |
| │ 1. Object Generation Locking │ |
| │ 2. Object Versioning (Backups) │ |
| │ 3. Uniform Bucket-Level Access │ |
| │ 4. CMEK Encryption (Cloud KMS) │ |
| └───────────────────────────────────┘ |
+-----------------------------------------------------------------------------------+
Essential Architectural Guardrails for GCS State Backends
- Native Object Generation State Locking: Unlike AWS (which requires a separate DynamoDB table for distributed state locks), Google Cloud Storage natively supports object generation preconditions (
x-goog-if-generation-match). When Terraform initiates an execution, it creates adefault.tflockobject in the GCS bucket. If another process attempts a concurrent apply, GCS rejects the write with a 412 Precondition Failed error, guaranteeing lock safety without additional database infrastructure. - Object Versioning Enabled: State corruption can occur during unexpected network interruptions or faulty manual state manipulations. Enabling Object Versioning on the GCS state bucket ensures every state mutation preserves prior historical versions, allowing immediate restoration of previous state snapshots.
- Uniform Bucket-Level Access & IAM Isolation: All access to the state bucket must be governed strictly through IAM roles (
roles/storage.objectAdminorroles/storage.objectViewer) assigned to specific automation service accounts. ACLs must be disabled by enforcing Uniform Bucket-Level Access. - Customer-Managed Encryption Keys (CMEK): Because Terraform state files may contain sensitive connection strings, database passwords, or private IP schemas, encrypting the GCS bucket with a Cloud KMS key ensures strict organizational cryptographic control and auditability.
# GCS Backend Configuration
terraform {
backend "gcs" {
bucket = "cft-tfstate-prod-uscentral1-8934"
prefix = "infrastructure/vpc-networking"
impersonate_service_account = "sa-tf-deployer@cft-seed-prod.iam.gserviceaccount.com"
}
}
Modular Architecture & The Cloud Foundation Toolkit (CFT)
Enterprise Terraform code must be modular, reusable, and composable to prevent massive, unmaintainable monolithic state files ("blast radius concentration").
+-----------------------------------------------------------------------------------+
| ENTERPRISE TERRAFORM MODULE TAXONOMY |
+-----------------------------------------------------------------------------------+
| 1. FOUNDATIONAL MODULES | Highly opinionated, reusable building blocks (VPC, |
| (Child Modules) | GKE, Cloud SQL, IAM Bindings) curated by Platform SRE. |
+-------------------------+---------------------------------------------------------+
| 2. ENTERPRISE BLUEPRINTS| Composed stacks assembling multiple child modules into |
| (Landing Zones) | full organizational environments (Core Shared VPC + IAM)|
+-------------------------+---------------------------------------------------------+
| 3. ROOT MODULES | The executable entry points defining environment- |
| (Environment Layer) | specific variables (dev/main.tf, prod/main.tf). |
+-----------------------------------------------------------------------------------+
Google Cloud Foundation Toolkit (CFT)
The Cloud Foundation Toolkit (CFT) provides open-source, production-ready, CIS-benchmark-compliant Terraform modules developed and maintained by Google Cloud engineers. Using CFT modules dramatically reduces boilerplate and guarantees compliance with Google Cloud Well-Architected Framework best practices.
Module Design Principles
- Single Responsibility: A child module should manage a single logical domain (e.g.,
terraform-google-networkfor subnets and Cloud NAT, orterraform-google-kubernetes-enginefor GKE clusters). - Explicit Inputs and Outputs: Expose strictly typed variable inputs with sensible defaults and output resource IDs, self-links, and service account emails to enable composition.
- Avoid Hardcoded Secrets: Pass sensitive values as variables sourced from Secret Manager or Cloud KMS rather than hardcoding credentials inside modules.
Environment Isolation: Workspaces vs. Multi-Project Directory Structures
A critical decision on the Cloud Architect exam involves how to isolate environments (Development, Staging, Production).
TERRAFORM WORKSPACES (ANTI-PATTERN FOR PROD) DIRECTORY-SEPARATED HIERARCHY (BEST PRACTICE)
┌────────────────────────────────────────┐ ┌────────────────────────────────────────────────────────┐
│ Root Module │ │ terraform-live/ │
│ ├── main.tf │ │ ├── environments/ │
│ └── (Workspaces: dev, stage, prod) │ │ │ ├── dev/ │
│ [!] Shares single backend bucket │ │ │ │ ├── main.tf (Backend: dev-state-bucket) │
│ [!] Shares single IAM Service Account │ │ │ │ └── terraform.tfvars │
│ [!] High Blast Radius: apply in prod │ │ │ ├── staging/ │
│ can corrupt dev state │ │ │ └── prod/ │
└────────────────────────────────────────┘ │ ├── main.tf (Backend: prod-state-bucket) │
│ └── (Isolated Service Account & IAM) │
│ [OK] Absolute Blast Radius & Security Isolation │
└────────────────────────────────────────────────────────┘
Architectural Comparison: Workspaces vs. Directory-Based Isolation
| Architectural Attribute | Terraform Workspaces | Directory-Separated Multi-Project Structure |
|---|---|---|
| State File Storage | Single GCS bucket with state paths separated under workspace_name/. | Separate, dedicated GCS buckets per environment (e.g., dev-state-bkt, prod-state-bkt). |
| IAM Access Boundaries | Single IAM identity must have read/write access to the entire bucket across all workspaces. | Granular least-privilege IAM: Developers access dev, only production CI/CD accesses prod. |
| Blast Radius | High; an errant terraform destroy in the wrong workspace context can devastate production. | Minimal; environments exist in completely separate folder/project trees with independent state locks. |
| Configuration Drift | Must rely entirely on ternary logic and variable files for environment differences. | Code differences between environments are explicitly version-controlled and visible in Git pull requests. |
| Recommended Use Case | Ephemeral developer feature testing branches within a single sandbox project. | Enterprise multi-environment production architectures. |
Automated CI/CD Execution via Cloud Build & Service Account Impersonation
Executing Terraform from developer workstations creates compliance risks and security vulnerabilities. Enterprise deployments must utilize automated pipelines.
+-----------------------------------------------------------------------------------+
| SECURE TERRAFORM CI/CD EXECUTION FLOW |
+-----------------------------------------------------------------------------------+
| 1. Developer pushes Git PR ──> Cloud Build Trigger activates |
| 2. Cloud Build Runner (Short-Lived Worker) |
| └── Impersonates: sa-terraform-runner@project.iam.gserviceaccount.com |
| 3. Execution Phase: |
| ├── terraform init & terraform validate |
| ├── Policy-as-Code Check: terraform-validator / Conftest / Google PolicyEngine |
| └── terraform plan -out=tfplan.binary |
| 4. PR Approved & Merged to main ──> Manual Approval Gate ──> terraform apply |
+-----------------------------------------------------------------------------------+
Service Account Impersonation vs. Static Keys
- Static Service Account Keys (
.jsonkeys): Present severe security risks due to accidental Git commits, credential theft, and lack of automatic expiration. Google Cloud strongly discourages static JSON keys. - Service Account Impersonation: The Cloud Build service account is granted the
roles/iam.serviceAccountTokenCreatorrole on the target Terraform Deployment Service Account. During pipeline execution, Cloud Build requests a short-lived OAuth 2.0 access token (valid for 1 hour) to execute infrastructure provisioning operations, eliminating stored credentials entirely.
Policy-as-Code: Terraform Validator & Policy Intelligence
Before applying infrastructure changes, pipelines enforce organizational compliance guardrails using Terraform Validator (or Google Cloud Policy Controller / OPA Conftest):
- Converts the binary
tfplaninto Google Cloud Resource Manager JSON. - Evaluates proposed resources against Organization Policy Constraints (e.g., forbidding public IP addresses on Cloud SQL or enforcing CMEK encryption on GCS buckets).
- Fails the build before any API call is made if a policy violation is detected.
Managing Drift, State Import & Resource Lifecycle
Infrastructure Drift Detection & Remediation
- Drift Origin: Infrastructure drift occurs when resources managed by Terraform are modified out-of-band via the Google Cloud Console,
gcloudCLI, or automated cloud agents. - Detection: Running
terraform plan -refresh-onlyqueries Google Cloud APIs, updates the local state representation without proposing resource modifications, and highlights differences between code, state, and actual cloud assets. - Remediation: Running
terraform applyoverwrites the out-of-band changes, forcing the real-world GCP infrastructure back into compliance with the declared HCL code.
Importing Existing Resources (terraform import & import {} blocks)
When adopting Terraform for pre-existing cloud environments, resources must be imported into the state file to prevent duplicate creation errors:
# Modern declarative import block (Terraform 1.5+)
import {
to = google_compute_network.custom_vpc
id = "projects/prod-networking-host/global/networks/vpc-prod-core"
}
Essential Resource Lifecycle Meta-Arguments
| Lifecycle Meta-Argument | Syntax & Mechanism | Architectural Intent |
|---|---|---|
prevent_destroy | lifecycle { prevent_destroy = true } | Causes Terraform to reject any plan or apply that would result in the resource being deleted. Essential for production databases (Cloud SQL, Spanner) and core state buckets. |
create_before_destroy | lifecycle { create_before_destroy = true } | Inverts default replacement behavior: provisions the new replacement resource first, verifies availability, and only then tears down the old resource. Prevents downtime during SSL certificate or VM updates. |
ignore_changes | lifecycle { ignore_changes = [labels, replicas] } | Instructs Terraform to ignore out-of-band modifications to specific attributes. Vital when external systems (like Kubernetes HPA or Cloud Autoscaler) dynamically modify instance counts. |
Kubernetes-Native Declarative IaC: Google Cloud Config Connector (KCC)
For organizations operating heavily on Google Kubernetes Engine (GKE), Google Cloud Config Connector (KCC) offers a Kubernetes-native alternative to Terraform.
+-----------------------------------------------------------------------------------+
| CONFIG CONNECTOR CONTINUOUS RECONCILIATION |
+-----------------------------------------------------------------------------------+
| Developer writes K8s YAML ──> kubectl apply ──> GKE Control Plane |
| │ |
| [ Config Connector Operator ] |
| │ (Workload Identity) |
| v |
| Google Cloud REST APIs |
| (Creates Cloud SQL / PubSub) |
| │ |
| [ Continuous Reconciliation Loop ] |
| (Automatically heals out-of-band drift) |
+-----------------------------------------------------------------------------------+
Terraform vs. Config Connector Architectural Decision Matrix
| Feature | HashiCorp Terraform | Google Cloud Config Connector (KCC) |
|---|---|---|
| Execution Engine | Client-driven CLI execution / CI/CD pipeline. | In-cluster Kubernetes Custom Resource Controller (KRM). |
| Configuration Format | HashiCorp Configuration Language (HCL). | Standard Kubernetes YAML (Custom Resource Definitions - CRDs). |
| Drift Management | Periodic execution of terraform plan via cron or CI/CD. | Continuous real-time reconciliation loop; automatically undoes manual console changes within minutes. |
| Resource Lifecycle | Tied to terraform apply lifecycle and state file. | Bound to Kubernetes object lifecycle (kubectl delete tears down GCP asset). |
| Optimal Use Case | Core foundation landing zones, Shared VPCs, IAM, organization-level resources. | Application-specific GCP dependencies (Cloud SQL, Pub/Sub, Storage) managed directly alongside Kubernetes Pods via GitOps (Anthos Config Sync). |
Concrete Architectural Scenario: Enterprise Multi-Tier Landing Zone
Scenario Profile
- Organization: Global financial services enterprise migrating retail banking microservices to Google Cloud.
- Compliance & Availability SLA: Zero state loss, 100% auditability of infrastructure changes, no static credentials, strict prevention of accidental production database destruction.
Solution Architecture Blueprint
- State Storage: Separate GCS buckets provisioned per environment (
tf-state-dev,tf-state-stage,tf-state-prod) with Object Versioning, CMEK encryption via Cloud KMS, and Uniform Bucket-Level Access. State locking is handled natively by GCS object generation preconditions. - Directory Structure: Directory-separated environment layout using Cloud Foundation Toolkit (CFT) modules for VPC, GKE, and IAM. Workspaces are strictly prohibited for production environments.
- CI/CD Execution: Cloud Build pipeline triggered on GitHub pull requests. The build runner uses Service Account Impersonation (
roles/iam.serviceAccountTokenCreator) to generate short-lived tokens. The pipeline executesterraform-validatoragainst organization policies before generating a deterministic binary execution plan. - Database Protection: Production Cloud Spanner and Cloud SQL instances configured with
lifecycle { prevent_destroy = true }to eliminate risk of human error in CI/CD automation.
[!IMPORTANT] Exam Watch: For the PCA exam, remember that Google Cloud Storage remote backends for Terraform provide automatic native state locking without requiring an external database. If an exam question asks how to organize multi-environment Terraform infrastructure securely with minimal blast radius and independent access controls, choose directory-separated project structures with distinct backend buckets and service accounts, never Terraform workspaces.
An enterprise is designing a multi-environment Infrastructure as Code (IaC) pipeline on Google Cloud using Terraform. The cloud architecture team must guarantee that concurrent deployments cannot corrupt state files, historical state changes are recoverable in the event of an accidental state overwrite, and no separate third-party database is required for distributed locking. Which remote state backend configuration best satisfies these requirements?
A lead cloud architect is establishing the repository structure and access model for an enterprise with development, staging, and production environments on Google Cloud. The security team mandates strict blast radius containment, distinct IAM permissions per environment, and prevention of cross-environment state corruption. How should the architect structure the Terraform deployment?
A DevOps team manages a critical production Cloud Spanner database and a set of compute instances using Terraform. During a routine infrastructure refactor, an engineer accidentally renames a database resource in HCL, which would trigger a destructive replacement during the next deployment. Additionally, compute instances must be replaced with zero downtime during machine type upgrades. Which Terraform lifecycle configurations must the architect implement?
An enterprise running hundreds of microservices on Google Kubernetes Engine (GKE) wants to declare cloud infrastructure dependencies (such as Pub/Sub topics, Cloud SQL databases, and Cloud Storage buckets) directly alongside their Kubernetes deployment manifests. The platform team requires that any manual changes made to these cloud resources via the Cloud Console be automatically detected and reverted back to the declared state without running a manual CI/CD pipeline. Which solution should the architect select?