---
# === IDENTITY ===
id: software/devops/terraform-gcp-basic/2026
canonical_question: "Terraform reference: GCP basic infrastructure"
aliases:
  - "How to set up GCP VPC Compute Engine with Terraform"
  - "Terraform Google Cloud provider configuration"
  - "Basic GCP infrastructure as code with Terraform"
  - "Terraform GCS backend state management"
entity_type: software_reference
domain: software > devops > Terraform GCP Basic
region: global
jurisdiction: global
temporal_scope: 2024-2026

# === VERIFICATION ===
last_verified: 2026-02-28
confidence: 0.92
version: 1.0
first_published: 2026-02-28

# === TEMPORAL VALIDITY ===
temporal_validity:
  status: stable
  last_breaking_change: "Google provider v5.0 (2023) — removed deprecated resources, default labels support"
  next_review: 2026-08-27
  change_sensitivity: medium

# === CONSTRAINTS ===
constraints:
  - "Always use GCS backend for remote state — never commit terraform.tfstate to version control"
  - "Pin provider versions with ~> constraint (e.g., ~> 5.0 or ~> 6.0)"
  - "Enable versioning and encryption on the GCS state bucket"
  - "Use service accounts with least-privilege IAM roles — never use owner role for Terraform"
  - "GCP requires enabling APIs before creating resources (e.g., compute.googleapis.com)"

# === SKIP CONDITIONS ===
skip_this_unit_if:
  - condition: "You need AWS infrastructure instead of GCP"
    use_instead: "software/devops/terraform-aws-basic/2026"
  - condition: "You prefer gcloud CLI or Deployment Manager"
    use_instead: "GCP Deployment Manager or gcloud reference"

# === AGENT HINTS ===
inputs_needed:
  - key: "gcp_services"
    question: "What GCP services do you need?"
    type: choice
    options: ["VPC + Compute Engine", "Cloud Run", "GKE", "Cloud Storage + CDN", "Cloud SQL + VPC"]
  - key: "auth_method"
    question: "How will Terraform authenticate with GCP?"
    type: choice
    options: ["Service account key (JSON)", "Workload Identity Federation", "Application Default Credentials"]

# === DISTRIBUTION ===
canonical_source: "https://knowledgelib.io/software/devops/terraform-gcp-basic/2026"
suggested_citation: "Source: knowledgelib.io — AI Knowledge Library (verified 2026-02-28)"

# === RELATED UNITS ===
related_kos:
  depends_on: []
  related_to: []
  solves: []
  alternative_to:
    - id: software/devops/terraform-aws-basic/2026
      label: "Terraform AWS Basic Infrastructure"
  often_confused_with: []

# === SOURCES ===
sources:
  - id: src1
    title: "Google Cloud Provider Documentation"
    author: HashiCorp
    url: https://registry.terraform.io/providers/hashicorp/google/latest/docs
    type: official_docs
    published: 2025-06-01
    reliability: authoritative
  - id: src2
    title: "Build Infrastructure - GCP Get Started"
    author: HashiCorp
    url: https://developer.hashicorp.com/terraform/tutorials/gcp-get-started/google-cloud-platform-build
    type: official_docs
    published: 2025-01-15
    reliability: authoritative
  - id: src3
    title: "Store Terraform state in Cloud Storage"
    author: Google Cloud
    url: https://docs.cloud.google.com/docs/terraform/resource-management/store-state
    type: official_docs
    published: 2025-03-01
    reliability: authoritative
  - id: src4
    title: "Backend Type: gcs"
    author: HashiCorp
    url: https://developer.hashicorp.com/terraform/language/backend/gcs
    type: official_docs
    published: 2025-01-15
    reliability: authoritative
  - id: src5
    title: "Provision Compute Engine resources with Terraform"
    author: Google Cloud
    url: https://cloud.google.com/compute/docs/terraform
    type: official_docs
    published: 2025-06-01
    reliability: authoritative
  - id: src6
    title: "Terraform on Google Cloud documentation"
    author: Google Cloud
    url: https://docs.cloud.google.com/docs/terraform
    type: official_docs
    published: 2025-06-01
    reliability: authoritative
  - id: src7
    title: "Best practices for security - Terraform on GCP"
    author: Google Cloud
    url: https://cloud.google.com/docs/terraform/best-practices/security
    type: official_docs
    published: 2025-01-01
    reliability: authoritative
---

# Terraform Reference: GCP Basic Infrastructure

## TL;DR

- **Bottom line**: Terraform with the Google provider (`hashicorp/google ~> 6.0`) lets you define VPC networks, Compute Engine VMs, Cloud Storage, Cloud SQL, and IAM as code — fully reproducible and version-controlled GCP infrastructure.
- **Key tool/command**: `terraform init && terraform plan && terraform apply`
- **Watch out for**: GCP APIs must be enabled before Terraform can create resources — use `google_project_service` to enable APIs declaratively or you get cryptic "API not enabled" errors.
- **Works with**: Terraform 1.6+, Google provider ~> 6.0, OpenTofu 1.6+.

## Constraints
<!-- Agents: read this section before recommending any action from this unit.
     These are hard constraints that must not be violated. -->

- Always use GCS backend for remote state — never commit `terraform.tfstate` to version control.
- Pin provider versions with `~>` constraint — unversioned providers break on major updates.
- Enable versioning and encryption on the GCS state bucket.
- Use service accounts with least-privilege IAM — never use `roles/owner` for Terraform.
- GCP requires enabling APIs before creating resources — add `google_project_service` resources.
- GCS backend supports native state locking — no separate lock table needed (unlike AWS DynamoDB).

## Quick Reference

| Resource | Terraform Type | Key Arguments | Notes |
|---|---|---|---|
| VPC Network | `google_compute_network` | `name`, `auto_create_subnetworks` | Set `auto_create_subnetworks = false` for custom |
| Subnet | `google_compute_subnetwork` | `network`, `ip_cidr_range`, `region` | One per region |
| Firewall Rule | `google_compute_firewall` | `network`, `allow`, `source_ranges` | Applies to network, not instance |
| Compute Instance | `google_compute_instance` | `machine_type`, `zone`, `boot_disk` | Use `data.google_compute_image` for latest |
| Cloud Storage | `google_storage_bucket` | `name`, `location`, `storage_class` | Globally unique name required |
| Cloud SQL | `google_sql_database_instance` | `database_version`, `settings.tier` | Use private IP with VPC peering |
| IAM Binding | `google_project_iam_binding` | `role`, `members` | Use `google_project_iam_member` for additive |
| Service Account | `google_service_account` | `account_id`, `display_name` | One per service/function |
| Cloud NAT | `google_compute_router_nat` | `router`, `nat_ip_allocate_option` | For private subnet egress |
| Cloud Router | `google_compute_router` | `network`, `region` | Required for Cloud NAT |
| API Enablement | `google_project_service` | `service` | Enable APIs declaratively |
| GCS Backend | `backend "gcs"` | `bucket`, `prefix` | State with native locking |

## Decision Tree

```
START
├── First time with Terraform on GCP?
│   ├── YES → Enable APIs + set up GCS backend (Step 1-2 below)
│   └── NO ↓
├── Need a full VPC with private/public subnets?
│   ├── YES → Custom VPC + Cloud NAT + Cloud Router (Step 3)
│   └── NO ↓
├── Need VMs or containers?
│   ├── VMs → Compute Engine with instance template (Step 4)
│   ├── Containers → Cloud Run or GKE
│   └── NO ↓
├── Need a managed database?
│   ├── YES → Cloud SQL with private IP (Example 2)
│   └── NO ↓
├── Need object storage?
│   ├── YES → Cloud Storage bucket (Step 5)
│   └── NO ↓
└── DEFAULT → VPC + Compute Engine + Cloud Storage
```

## Step-by-Step Guide

### 1. Enable required APIs and configure provider

Set up the Google provider and enable APIs. [src1] [src6]

```hcl
# versions.tf
terraform {
  required_version = ">= 1.6.0"

  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "~> 6.0"
    }
  }

  backend "gcs" {
    bucket = "my-project-tf-state"
    prefix = "prod"
  }
}

provider "google" {
  project = var.project_id
  region  = var.region
}

# Enable required APIs
resource "google_project_service" "apis" {
  for_each = toset([
    "compute.googleapis.com",
    "sqladmin.googleapis.com",
    "storage.googleapis.com",
    "iam.googleapis.com",
  ])
  service            = each.value
  disable_on_destroy = false
}
```

**Verify**: `terraform init` → `Initializing the backend... Successfully configured the backend "gcs"!`

### 2. Create the GCS state bucket

Bootstrap the state bucket (run once). [src3] [src4]

```hcl
# bootstrap/main.tf
resource "google_storage_bucket" "terraform_state" {
  name          = "${var.project_id}-tf-state"
  location      = "US"
  storage_class = "STANDARD"

  versioning {
    enabled = true
  }

  uniform_bucket_level_access = true

  lifecycle_rule {
    action {
      type = "Delete"
    }
    condition {
      num_newer_versions = 5
    }
  }
}
```

**Verify**: `gsutil ls gs://${PROJECT_ID}-tf-state/` → bucket exists

### 3. Create a VPC network with subnets

Define a custom-mode VPC with public and private subnets. [src1] [src5]

```hcl
# network.tf
resource "google_compute_network" "main" {
  name                    = "${var.project_name}-vpc"
  auto_create_subnetworks = false

  depends_on = [google_project_service.apis]
}

resource "google_compute_subnetwork" "public" {
  name          = "${var.project_name}-public"
  ip_cidr_range = "10.0.1.0/24"
  region        = var.region
  network       = google_compute_network.main.id
}

resource "google_compute_subnetwork" "private" {
  name                     = "${var.project_name}-private"
  ip_cidr_range            = "10.0.2.0/24"
  region                   = var.region
  network                  = google_compute_network.main.id
  private_ip_google_access = true
}

# Cloud NAT for private subnet egress
resource "google_compute_router" "main" {
  name    = "${var.project_name}-router"
  region  = var.region
  network = google_compute_network.main.id
}

resource "google_compute_router_nat" "main" {
  name                               = "${var.project_name}-nat"
  router                             = google_compute_router.main.name
  region                             = var.region
  nat_ip_allocate_option             = "AUTO_ONLY"
  source_subnetwork_ip_ranges_to_nat = "LIST_OF_SUBNETWORKS"

  subnetwork {
    name                    = google_compute_subnetwork.private.id
    source_ip_ranges_to_nat = ["ALL_IP_RANGES"]
  }
}

# Firewall rules
resource "google_compute_firewall" "allow_http" {
  name    = "${var.project_name}-allow-http"
  network = google_compute_network.main.name

  allow {
    protocol = "tcp"
    ports    = ["80", "443"]
  }

  source_ranges = ["0.0.0.0/0"]
  target_tags   = ["web"]
}

resource "google_compute_firewall" "allow_ssh_iap" {
  name    = "${var.project_name}-allow-ssh-iap"
  network = google_compute_network.main.name

  allow {
    protocol = "tcp"
    ports    = ["22"]
  }

  # IAP's IP range — no public SSH needed
  source_ranges = ["35.235.240.0/20"]
  target_tags   = ["ssh"]
}
```

**Verify**: `terraform plan` → shows network, subnets, NAT, firewall resources

### 4. Launch a Compute Engine instance

Deploy a VM in the public subnet. [src2] [src5]

```hcl
# compute.tf
data "google_compute_image" "debian" {
  family  = "debian-12"
  project = "debian-cloud"
}

resource "google_service_account" "vm" {
  account_id   = "${var.project_name}-vm-sa"
  display_name = "VM Service Account"
}

resource "google_compute_instance" "web" {
  name         = "${var.project_name}-web"
  machine_type = "e2-micro"
  zone         = "${var.region}-a"

  boot_disk {
    initialize_params {
      image = data.google_compute_image.debian.self_link
      size  = 20
      type  = "pd-balanced"
    }
  }

  network_interface {
    subnetwork = google_compute_subnetwork.public.id
    access_config {} # Ephemeral public IP
  }

  service_account {
    email  = google_service_account.vm.email
    scopes = ["cloud-platform"]
  }

  tags = ["web", "ssh"]

  metadata = {
    enable-oslogin = "TRUE"
  }
}
```

**Verify**: `terraform apply` → `gcloud compute instances describe ${var.project_name}-web --zone=${var.region}-a`

### 5. Create a Cloud Storage bucket

Set up object storage with proper defaults. [src1]

```hcl
# storage.tf
resource "google_storage_bucket" "assets" {
  name          = "${var.project_id}-assets"
  location      = var.region
  storage_class = "STANDARD"

  uniform_bucket_level_access = true

  versioning {
    enabled = true
  }

  lifecycle_rule {
    action {
      type          = "SetStorageClass"
      storage_class = "NEARLINE"
    }
    condition {
      age = 30
    }
  }
}
```

**Verify**: `terraform apply` → `gsutil ls` shows the bucket

### 6. Define variables and outputs

Create reusable variables. [src2]

```hcl
# variables.tf
variable "project_id" {
  description = "GCP project ID"
  type        = string
}

variable "region" {
  description = "GCP region"
  type        = string
  default     = "us-central1"
}

variable "project_name" {
  description = "Project name for resource naming"
  type        = string
}

# outputs.tf
output "vpc_id" {
  value = google_compute_network.main.id
}

output "instance_ip" {
  value = google_compute_instance.web.network_interface[0].access_config[0].nat_ip
}

output "bucket_url" {
  value = google_storage_bucket.assets.url
}
```

**Verify**: `terraform output` → shows all values

## Code Examples

### HCL: VPC + Compute Engine + Cloud SQL

> Full script: [hcl-vpc-compute-engine-cloud-sql.txt](scripts/hcl-vpc-compute-engine-cloud-sql.txt) (42 lines)

```hcl
# Input:  Terraform configuration
# Output: VPC with private Compute Engine and Cloud SQL PostgreSQL
resource "google_compute_global_address" "private_ip" {
  name          = "private-ip-range"
  purpose       = "VPC_PEERING"
# ... (see full script)
```

### HCL: Cloud Storage static website with Load Balancer

> Full script: [hcl-cloud-storage-static-website-with-load-balance.txt](scripts/hcl-cloud-storage-static-website-with-load-balance.txt) (40 lines)

```hcl
# Input:  Domain name, SSL certificate
# Output: Cloud Storage + HTTP(S) Load Balancer
resource "google_storage_bucket" "website" {
  name          = var.domain_name
  location      = "US"
# ... (see full script)
```

## Anti-Patterns

### Wrong: Using default network

```hcl
# BAD — default network has permissive firewall rules
resource "google_compute_instance" "web" {
  name         = "web"
  machine_type = "e2-micro"
  zone         = "us-central1-a"

  network_interface {
    network = "default"  # Insecure, shared with everything
  }
}
```

### Correct: Custom VPC with explicit firewall rules

```hcl
# GOOD — isolated network, minimal firewall rules
resource "google_compute_network" "main" {
  name                    = "my-vpc"
  auto_create_subnetworks = false
}

resource "google_compute_instance" "web" {
  name         = "web"
  machine_type = "e2-micro"
  zone         = "us-central1-a"

  network_interface {
    subnetwork = google_compute_subnetwork.public.id
  }
}
```

### Wrong: Forgetting to enable APIs

```hcl
# BAD — will fail with "API not enabled" error
resource "google_compute_instance" "web" {
  name         = "web"
  machine_type = "e2-micro"
  zone         = "us-central1-a"
  # compute.googleapis.com not enabled!
}
```

### Correct: Declare API enablement as resources

```hcl
# GOOD — APIs enabled declaratively with dependency
resource "google_project_service" "compute" {
  service            = "compute.googleapis.com"
  disable_on_destroy = false
}

resource "google_compute_instance" "web" {
  name         = "web"
  machine_type = "e2-micro"
  zone         = "us-central1-a"
  depends_on   = [google_project_service.compute]
}
```

### Wrong: Using owner role for Terraform service account

```hcl
# BAD — excessive permissions
resource "google_project_iam_member" "terraform" {
  project = var.project_id
  role    = "roles/owner"
  member  = "serviceAccount:terraform@${var.project_id}.iam.gserviceaccount.com"
}
```

### Correct: Least-privilege roles

```hcl
# GOOD — specific roles for what Terraform needs
locals {
  terraform_roles = [
    "roles/compute.admin",
    "roles/storage.admin",
    "roles/cloudsql.admin",
    "roles/iam.serviceAccountAdmin",
  ]
}

resource "google_project_iam_member" "terraform" {
  for_each = toset(local.terraform_roles)
  project  = var.project_id
  role     = each.value
  member   = "serviceAccount:terraform@${var.project_id}.iam.gserviceaccount.com"
}
```

## Common Pitfalls

- **API not enabled errors**: GCP requires explicit API enablement. Fix: add `google_project_service` resources with `disable_on_destroy = false`. [src6]
- **Cloud SQL deletion protection**: Destroying a Cloud SQL instance with `deletion_protection = true` fails. Fix: set to `false` in dev, `true` in prod using a variable. [src1]
- **Globally unique bucket names**: GCS bucket names are globally unique across all GCP. Fix: include project ID in bucket name: `"${var.project_id}-assets"`. [src1]
- **Forgetting `auto_create_subnetworks = false`**: Default VPC creates a subnet in every region. Fix: always set to `false` for custom networks. [src2]
- **SSH via public IP**: Insecure and requires firewall rules for 0.0.0.0/0. Fix: use IAP tunneling with source range `35.235.240.0/20`. [src7]
- **Using `google_project_iam_binding` instead of `google_project_iam_member`**: Bindings are authoritative and can remove other members. Fix: use `google_project_iam_member` for additive bindings. [src7]

## Diagnostic Commands

```bash
# Initialize and download providers
terraform init

# Preview changes
terraform plan -out=tfplan

# Apply saved plan
terraform apply tfplan

# List resources in state
terraform state list

# Show resource details
terraform state show google_compute_instance.web

# Validate configuration
terraform validate

# Format files
terraform fmt -recursive

# List enabled APIs
gcloud services list --enabled --project=PROJECT_ID

# Check VM status
gcloud compute instances list --project=PROJECT_ID

# View Cloud SQL instances
gcloud sql instances list --project=PROJECT_ID
```

## Version History & Compatibility

| Version | Status | Breaking Changes | Migration Notes |
|---|---|---|---|
| Terraform 1.9+ | Current | None | HCL improvements |
| Terraform 1.6-1.8 | Supported | `terraform test` GA | — |
| Google Provider 6.x | Current | Removed deprecated resources | Check migration guide |
| Google Provider 5.x | Supported | Default labels, resource cleanup | — |
| Google Provider 4.x | Deprecated | — | Significant resource changes |
| OpenTofu 1.6+ | Active | Fork of Terraform 1.6 | Drop-in compatible |

## When to Use / When Not to Use

| Use When | Don't Use When | Use Instead |
|---|---|---|
| Reproducible GCP infrastructure as code | One-off gcloud commands | gcloud CLI |
| Team collaboration with review workflows | Rapid prototyping | GCP Console |
| Multi-cloud (GCP + AWS) deployments | GCP-only with Deployment Manager expertise | Deployment Manager |
| Long-lived infrastructure (VPCs, Cloud SQL) | Ephemeral dev environments | Cloud Workstations |
| CI/CD infrastructure pipelines | Kubernetes resource management | Helm, Config Connector |

## Important Caveats

- The `google` provider and `google-beta` provider are separate — beta features require the beta provider declaration.
- GCS backend locks state natively (no DynamoDB equivalent needed), but the lock is advisory — a crashed apply can leave a stale lock. Use `terraform force-unlock` if needed.
- Cloud SQL instances take 5-10 minutes to create — plan for longer `terraform apply` times.
- `google_project_iam_binding` is authoritative (replaces all members for that role) — use `google_project_iam_member` for additive, non-destructive bindings.

## Related Units

- [Terraform AWS Basic Infrastructure](/software/devops/terraform-aws-basic/2026)
