Terraform GCP Setup

Complete guide to setting up Terraform with Google Cloud Platform (GCP). Learn how to configure authentication, create your first GCP resources, and manage infrastructure.

Prerequisites

  • ✅ GCP Account
  • ✅ gcloud CLI installed and configured
  • ✅ Terraform installed
  • ✅ GCP Project created
  • ✅ Service Account with appropriate permissions

Installation

Install gcloud CLI

# macOS
brew install --cask google-cloud-sdk

# Linux
curl https://sdk.cloud.google.com | bash
exec -l $SHELL

# Windows
# Download from: https://cloud.google.com/sdk/docs/install

Initialize gcloud

gcloud init
gcloud auth login
gcloud config set project YOUR_PROJECT_ID

Authentication

Method 1: Application Default Credentials (ADC)

# Authenticate with ADC
gcloud auth application-default login

# Terraform will automatically use ADC
provider "google" {
  project = var.project_id
  region  = var.region
}

Method 2: Service Account Key

# Create service account
gcloud iam service-accounts create terraform \
  --display-name="Terraform Service Account"

# Grant permissions
gcloud projects add-iam-policy-binding YOUR_PROJECT_ID \
  --member="serviceAccount:terraform@YOUR_PROJECT_ID.iam.gserviceaccount.com" \
  --role="roles/owner"

# Create key
gcloud iam service-accounts keys create credentials.json \
  --iam-account=terraform@YOUR_PROJECT_ID.iam.gserviceaccount.com

# Use in Terraform
provider "google" {
  project     = var.project_id
  region      = var.region
  credentials = file("credentials.json")
}

Method 3: Environment Variable

export GOOGLE_APPLICATION_CREDENTIALS="path/to/credentials.json"

# Or export service account key content
export GOOGLE_CREDENTIALS='{"type":"service_account",...}'

# In Terraform
provider "google" {
  project = var.project_id
  region  = var.region
}

Basic GCP Provider Configuration

versions.tf

terraform {
  required_version = ">= 1.0"
  
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = "~> 5.0"
    }
  }
}

provider.tf

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

IAM Permissions

Required Roles

# Grant necessary roles to service account
gcloud projects add-iam-policy-binding PROJECT_ID \
  --member="serviceAccount:terraform@PROJECT_ID.iam.gserviceaccount.com" \
  --role="roles/compute.admin"

gcloud projects add-iam-policy-binding PROJECT_ID \
  --member="serviceAccount:terraform@PROJECT_ID.iam.gserviceaccount.com" \
  --role="roles/iam.serviceAccountAdmin"

gcloud projects add-iam-policy-binding PROJECT_ID \
  --member="serviceAccount:terraform@PROJECT_ID.iam.gserviceaccount.com" \
  --role="roles/storage.admin"
💡 Best Practice: Use minimum required permissions. Grant only the roles needed for your infrastructure.

Complete Example: VPC with Compute Engine

variables.tf

variable "project_id" {
  description = "GCP Project ID"
  type        = string
}

variable "region" {
  description = "GCP region"
  type        = string
  default     = "us-central1"
}

variable "zone" {
  description = "GCP zone"
  type        = string
  default     = "us-central1-a"
}

variable "environment" {
  description = "Environment name"
  type        = string
}

variable "instance_type" {
  description = "GCE instance type"
  type        = string
  default     = "e2-micro"
}

main.tf

# VPC Network
resource "google_compute_network" "vpc" {
  name                    = "${var.environment}-vpc"
  auto_create_subnetworks = false

  project = var.project_id
}

# Public Subnet
resource "google_compute_subnetwork" "public" {
  name          = "${var.environment}-public-subnet"
  ip_cidr_range = "10.0.1.0/24"
  region        = var.region
  network       = google_compute_network.vpc.id

  project = var.project_id
}

# Firewall Rule - Allow HTTP
resource "google_compute_firewall" "allow_http" {
  name    = "${var.environment}-allow-http"
  network = google_compute_network.vpc.name

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

  source_ranges = ["0.0.0.0/0"]
  target_tags   = ["http-server"]

  project = var.project_id
}

# Firewall Rule - Allow SSH
resource "google_compute_firewall" "allow_ssh" {
  name    = "${var.environment}-allow-ssh"
  network = google_compute_network.vpc.name

  allow {
    protocol = "tcp"
    ports    = ["22"]
  }

  source_ranges = ["0.0.0.0/0"]
  target_tags   = ["ssh-server"]

  project = var.project_id
}

# Compute Engine Instance
resource "google_compute_instance" "web" {
  name         = "${var.environment}-web-server"
  machine_type = var.instance_type
  zone         = var.zone

  boot_disk {
    initialize_params {
      image = "debian-cloud/debian-11"
      size  = 10
    }
  }

  network_interface {
    network    = google_compute_network.vpc.name
    subnetwork = google_compute_subnetwork.public.name

    access_config {
      // Ephemeral public IP
    }
  }

  metadata_startup_script = <<-EOF
    #!/bin/bash
    apt-get update
    apt-get install -y nginx
    systemctl start nginx
    systemctl enable nginx
    echo "<h1>Hello from Terraform on GCP!</h1>" > /var/www/html/index.html
  EOF

  tags = ["http-server", "ssh-server"]

  project = var.project_id
}

outputs.tf

output "vpc_id" {
  description = "ID of the VPC network"
  value       = google_compute_network.vpc.id
}

output "instance_id" {
  description = "ID of the compute instance"
  value       = google_compute_instance.web.id
}

output "instance_public_ip" {
  description = "Public IP of the compute instance"
  value       = google_compute_instance.web.network_interface[0].access_config[0].nat_ip
}

output "instance_name" {
  description = "Name of the compute instance"
  value       = google_compute_instance.web.name
}

terraform.tfvars

project_id   = "my-gcp-project-id"
region       = "us-central1"
zone         = "us-central1-a"
environment  = "production"
instance_type = "e2-micro"

Remote State with GCS

GCS Backend Configuration

terraform {
  backend "gcs" {
    bucket  = "terraform-state-bucket"
    prefix  = "terraform/state"
    project = "my-gcp-project"
  }
}

Create GCS Bucket for State

# Create GCS bucket
gsutil mb -p PROJECT_ID -l us-central1 gs://terraform-state-bucket

# Enable versioning
gsutil versioning set on gs://terraform-state-bucket

# Enable encryption
gsutil encryption set -k projects/PROJECT_ID/locations/global/keyRings/KEY_RING/cryptoKeys/KEY_NAME \
  gs://terraform-state-bucket

Common GCP Resources

Cloud Storage Bucket

resource "google_storage_bucket" "website" {
  name          = "${var.environment}-website-bucket"
  location      = var.region
  force_destroy = false

  versioning {
    enabled = true
  }

  uniform_bucket_level_access {
    enabled = true
  }

  project = var.project_id
}

Cloud SQL Database

resource "google_sql_database_instance" "main" {
  name             = "${var.environment}-db"
  database_version = "POSTGRES_15"
  region           = var.region

  settings {
    tier = "db-f1-micro"
    
    backup_configuration {
      enabled    = true
      start_time = "03:00"
    }
    
    ip_configuration {
      ipv4_enabled = true
      authorized_networks {
        value = "0.0.0.0/0"
      }
    }
  }

  project = var.project_id
}

resource "google_sql_database" "main" {
  name     = "mydb"
  instance = google_sql_database_instance.main.name
  project  = var.project_id
}

resource "google_sql_user" "main" {
  name     = "admin"
  instance = google_sql_database_instance.main.name
  password = var.db_password
  project  = var.project_id
}

Load Balancer

resource "google_compute_backend_service" "web" {
  name      = "${var.environment}-backend-service"
  protocol  = "HTTP"
  port_name = "http"
  timeout_sec = 10

  backend {
    group = google_compute_instance_group.web.id
  }

  health_checks = [google_compute_health_check.web.id]

  project = var.project_id
}

resource "google_compute_health_check" "web" {
  name = "${var.environment}-health-check"

  http_health_check {
    port = 80
    path = "/"
  }

  project = var.project_id
}

resource "google_compute_url_map" "web" {
  name            = "${var.environment}-url-map"
  default_service = google_compute_backend_service.web.id

  project = var.project_id
}

resource "google_compute_target_http_proxy" "web" {
  name    = "${var.environment}-http-proxy"
  url_map = google_compute_url_map.web.id

  project = var.project_id
}

resource "google_compute_global_forwarding_rule" "web" {
  name       = "${var.environment}-forwarding-rule"
  target     = google_compute_target_http_proxy.web.id
  port_range = "80"

  project = var.project_id
}

Deployment Steps

1. Initialize Terraform

terraform init

2. Validate Configuration

terraform validate

3. Format Files

terraform fmt

4. Plan Changes

terraform plan

5. Apply Changes

terraform apply

Troubleshooting

Common Issues

⚠️ Issue: Authentication Error

Error: Error: google: could not find default credentials

Solution: Run gcloud auth application-default login or set credentials.

⚠️ Issue: Permission Denied

Error: Error: Error 403: Permission denied

Solution: Ensure service account has required IAM roles.

💡 Tip: Enable required APIs for your project:
gcloud services enable compute.googleapis.com
gcloud services enable sqladmin.googleapis.com
gcloud services enable storage-api.googleapis.com

Next Steps