Skip to content

Terraform on GCP -- Complete Guide to Provisioning Google Cloud Infrastructure

DodaTech 6 min read

Terraform on Google Cloud enables you to define and provision the full GCP infrastructure stack -- compute, networking, storage, databases, Kubernetes, and serverless -- using declarative HCL configuration files.

What You'll Learn

In this tutorial, you will learn how to configure the GCP provider, provision GCE instances with custom images, create VPC networks with firewall rules, manage Cloud Storage buckets with IAM, deploy Cloud SQL databases, set up GKE clusters, and implement GCP IAM with Terraform.

Why It Matters

GCP's console and gcloud CLI do not scale for multi-team, multi-project organizations. Terraform provides a unified workflow across all GCP services, with plan preview, state tracking, and automation through CI/CD pipelines.

Real-World Use

DodaTech uses Terraform on GCP for data analytics pipelines and machine learning workloads. Durga Antivirus Pro's malware detection engine runs on GKE, with Cloud Storage for sample storage and Cloud SQL for threat intelligence, all provisioned through Terraform.

GCP Provider Configuration

graph TD
    A[Terraform GCP Config] --> B[Google Provider]
    B --> C[Compute Engine]
    B --> D[VPC Network]
    B --> E[Cloud Storage]
    B --> F[Cloud SQL]
    B --> G[GKE]
    B --> H[IAM]
    C --> I[Instances]
    C --> J[Instance Groups]
    C --> K[Disks]
    D --> L[Subnets]
    D --> M[Firewall Rules]
    D --> N[Cloud NAT]
    E --> O[Buckets]
    E --> P[IAM Policies]
    style A fill:#4285f4,color:#fff
    style B fill:#4285f4,color:#fff

Provider Setup with Workload Identity

# provider.tf
terraform {
  required_version = ">= 1.6"
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "~> 6.0"
    }
    google-beta = {
      source  = "hashicorp/google-beta"
      version = "~> 6.0"
    }
  }
}

provider "google" {
  project = var.project_id
  region  = var.region

  # Workload Identity Federation for CI/CD
  access_token = data.google_service_account_access_token.default.access_token
}

data "google_service_account_access_token" "default" {
  target_service_account = var.terraform_sa_email
  scopes                = ["https://www.googleapis.com/auth/cloud-platform"]
  lifetime              = "600s"
}
gcloud auth application-default login
terraform init

Expected output:

Initializing the backend...
Initializing provider plugins...
- Finding hashicorp/google versions matching "~> 6.0"...
- Installing hashicorp/google v6.10.0...
- Installed hashicorp/google v6.10.0 (signed by HashiCorp)

GCE Instance with Custom VPC

# compute.tf
resource "google_compute_network" "main" {
  name                    = "vpc-${var.environment}"
  auto_create_subnetworks = false
}

resource "google_compute_subnetwork" "main" {
  name          = "subnet-${var.environment}-${var.region}"
  network       = google_compute_network.main.id
  region        = var.region
  ip_cidr_range = var.subnet_cidr

  secondary_ip_range {
    range_name    = "pods"
    ip_cidr_range = "10.1.0.0/16"
  }

  secondary_ip_range {
    range_name    = "services"
    ip_cidr_range = "10.2.0.0/20"
  }

  private_ip_google_access = true
}

resource "google_compute_firewall" "allow_http" {
  name    = "fw-${var.environment}-allow-http"
  network = google_compute_network.main.name

  allow {
    protocol = "tcp"
    ports    = ["80", "443"]
  }

  source_ranges = ["0.0.0.0/0"]
  target_tags   = ["web-server"]
}

resource "google_compute_instance" "web" {
  name         = "web-${var.environment}-001"
  machine_type = var.machine_type
  zone         = "${var.region}-a"

  boot_disk {
    initialize_params {
      image = "ubuntu-os-cloud/ubuntu-2204-lts"
      size  = 50
      type  = "pd-standard"
    }
  }

  network_interface {
    subnetwork = google_compute_subnetwork.main.self_link

    access_config {
      # Ephemeral public IP
    }
  }

  tags = ["web-server"]

  metadata = {
    environment = var.environment
    managed-by  = "terraform"
  }
}
terraform plan

Expected output:

Terraform will perform the following actions:

  # google_compute_instance.web will be created
  + resource "google_compute_instance" "web" {
      + name          = "web-production-001"
      + machine_type  = "e2-standard-2"
      + zone          = "us-central1-a"
      + tags          = ["web-server"]
      + boot_disk {
          + initialize_params {
              + image = "ubuntu-os-cloud/ubuntu-2204-lts"
              + size  = 50
            }
        }
    }

Plan: 3 to add, 0 to change, 0 to destroy.

Cloud Storage with IAM Conditions

# storage.tf
resource "google_storage_bucket" "data" {
  name          = "${var.project_id}-${var.environment}-data"
  location      = var.region
  storage_class = var.environment == "production" ? "STANDARD" : "NEARLINE"

  versioning {
    enabled = true
  }

  lifecycle_rule {
    condition {
      age = 90
    }
    action {
      type = "SetStorageClass"
      storage_class = "ARCHIVE"
    }
  }

  lifecycle_rule {
    condition {
      age = 365
    }
    action {
      type = "Delete"
    }
  }

  encryption {
    default_kms_key_name = google_kms_crypto_key.bucket_key.id
  }

  uniform_bucket_level_access = true
}

resource "google_storage_bucket_iam_binding" "data_readers" {
  bucket = google_storage_bucket.data.name
  role   = "roles/storage.objectViewer"
  members = [
    "serviceAccount:${google_service_account.data_processor.email}",
    "group:data-engineers"@dodatech".com",
  ]

  condition {
    title       = "business_hours"
    description = "Only allow access during business hours"
    expression  = "request.time.getHours(\"America/New_York\") >= 9 && request.time.getHours(\"America/New_York\") <= 17"
  }
}

GKE Cluster with Autopilot

# gke.tf
resource "google_container_cluster" "main" {
  name     = "gke-${var.environment}"
  location = var.region

  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"
  }

  workload_identity_config {
    workload_pool = "${var.project_id}.svc.id.goog"
  }

  release_channel {
    channel = var.environment == "production" ? "STABLE" : "REGULAR"
  }

  master_authorized_networks_config {
    cidr_blocks {
      cidr_block   = var.admin_cidr
      display_name = "Admin network"
    }
  }

  private_cluster_config {
    enable_private_nodes    = true
    enable_private_endpoint = false
    master_ipv4_cidr_block  = "172.16.0.0/28"
  }

  resource_labels = {
    environment = var.environment
    managed-by  = "terraform"
  }
}
gcloud container clusters get-credentials $(terraform output -raw cluster_name) --region us-central1
kubectl get nodes

Expected output:

NAME                                                 STATUS   ROLES    AGE   VERSION
gk3-gke-production-default-pool-8a2f1c5c-2x3d       Ready    <none>   5m    v1.30.3-gke.1234000
gk3-gke-production-default-pool-9b3e2d6d-1y4e       Ready    <none>   5m    v1.30.3-gke.1234000

Cloud SQL for PostgreSQL

# database.tf
resource "google_sql_database_instance" "main" {
  name             = "sql-${var.environment}"
  database_version = "POSTGRES_16"
  region           = var.region

  settings {
    tier              = var.environment == "production" ? "db-custom-8-32768" : "db-custom-2-7680"
    activation_policy = "ALWAYS"
    disk_size         = var.environment == "production" ? 500 : 100
    disk_type         = "PD_SSD"
    disk_autoresize   = true

    backup_configuration {
      enabled                        = true
      start_time                     = "03:00"
      point_in_time_recovery_enabled = true
      transaction_log_retention_days = 7
      retained_backups               = var.environment == "production" ? 30 : 7
    }

    ip_configuration {
      ipv4_enabled    = false
      private_network = google_compute_network.main.id
      require_ssl     = true
    }

    insights_config {
      query_insights_enabled  = true
      record_application_tags = true
      record_client_address   = true
      query_string_length     = 4096
    }
  }

  deletion_protection = var.environment == "production"
}

Common Mistakes

1. Forgetting to Enable GCP APIs

GCP APIs must be enabled per project. Use the google_project_service resource to enable required APIs in Terraform.

2. Not Using Service Accounts

Running Terraform with user credentials breaks in CI. Use dedicated service accounts with workload identity federation.

3. Missing VPC Subnet Mode

GCP VPCs default to auto-mode which creates subnets in every region. Use custom-mode VPCs for production control.

4. GKE Autopilot Resource Limits

Autopilot clusters impose resource limits on pods. Check the resource quota before deploying workloads.

5. Cloud SQL Public IP Exposure

Cloud SQL instances with public IPs are security risks. Always configure private_network and disable ipv4_enabled.

Practice Questions

1. How do you enable GCP APIs using Terraform? Use the google_project_service resource with the API name (e.g., container.googleapis.com) and disable_on_destroy = false.

2. What is the difference between Autopilot and Standard GKE clusters? Autopilot manages node infrastructure automatically. Standard gives full control over node pools but requires more management.

3. How do you conditionally set Cloud SQL backup retention based on environment? Use a conditional expression: var.environment == "production" ? 30 : 7 in the retained_backups argument.

4. Challenge: Write a Terraform configuration for a GKE cluster with workload identity, a Cloud Storage bucket with object lifecycle policies, and a Cloud SQL PostgreSQL database connected through a private VPC.

Mini Project: GCP Data Analytics Platform

Provision a VPC with private subnet and Cloud NAT, a GKE cluster with workload identity, a Cloud Storage bucket with versioning and lifecycle rules, a Cloud SQL PostgreSQL instance with private IP, and a service account with least-privilege IAM roles. Enable VPC Service Controls for data perimeter security.

Terraform on Azure
Remote State with S3 & DynamoDB

What's Next

Deploy GCP infrastructure with Terraform for your workloads, then implement Remote State for team collaboration. Study DevOps best practices for multi-cloud infrastructure management.

Built by the developers of Doda Browser, DodaZIP, and Durga Antivirus Pro.

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro