Terraform’s Google provider doesn’t just create infrastructure; it enforces your desired state, constantly reconciling the real world with your code.

Let’s see it in action. Imagine we want a simple GKE cluster. Here’s the Terraform code:

provider "google" {
  project = "your-gcp-project-id"
  region  = "us-central1"
}

resource "google_container_cluster" "primary" {
  name     = "my-gke-cluster"
  location = "us-central1"

  initial_node_count = 1
  remove_default_node_pool = true

  node_pool {
    name = "default-pool"
    node_count = 1
    node_config {
      machine_type = "e2-medium"
    }
  }
}

When you run terraform apply, Terraform communicates with the Google Cloud API. It checks if a cluster named my-gke-cluster already exists in us-central1 within your your-gcp-project-id. If not, it makes API calls to create it. If it does exist but has different settings (e.g., a different machine_type or node_count), Terraform will update it to match your code. If you delete the resource block from your .tf file and run terraform apply again, Terraform will call the API to destroy the cluster. This is the core of "declarative infrastructure."

The problem this solves is the drift between what you think your infrastructure looks like and what it actually looks like. Manual changes, accidental deletions, or even automated scripts can lead to inconsistencies that break applications or incur unexpected costs. Terraform, through its provider model, acts as a single source of truth and a continuous auditor.

Internally, the Google provider is a Go program that implements the schema.Provider interface. It uses the official Google Cloud client libraries (like google-cloud-go) to interact with GCP services. When you define a resource like google_container_cluster, Terraform maps those attributes to the corresponding API fields. The provider’s Create, Read, and Update functions are called by the Terraform core. The Read function is crucial – it fetches the current state of the resource from GCP, which Terraform then compares against the desired state defined in your code to determine what actions (create, update, delete) are necessary.

You control this system through the attributes within each resource block. For google_container_cluster, you can specify name, location, initial_node_count, remove_default_node_pool, and define node_pool blocks with their own name, node_count, and node_config (including machine_type, disk_size_gb, oauth_scopes, etc.). Beyond GKE, the provider exposes hundreds of other GCP resources: networks, subnets, firewalls, storage buckets, SQL instances, IAM policies, and more. Each resource has its own set of configurable attributes that mirror the GCP API.

The terraform plan command is your best friend here. It performs a "dry run" by executing the Read functions for all resources in your configuration and comparing their current state against your desired state. It then shows you exactly what changes Terraform intends to make, allowing you to review and approve before any actual modifications occur in GCP.

One aspect often overlooked is how the provider handles dependencies. When you define a google_compute_instance and reference a google_compute_network in its network_interface block, Terraform automatically infers that the network must exist before the instance can be created. It uses the resource’s ID or other unique identifiers to establish these relationships, ensuring operations happen in the correct order. This dependency graph is what allows Terraform to manage complex, interconnected infrastructure reliably.

The next concept you’ll likely grapple with is managing sensitive data, like database passwords or API keys, securely within your Terraform configurations.

Want structured learning?

Take the full Gcp course →