--- name: terraform-gcp description: Provision GCP infrastructure with Terraform. Configure providers and deploy Google Cloud resources. Use when implementing IaC for GCP. license: MIT metadata: author: devops-skills version: "1.0" --- # Terraform GCP Provision and manage Google Cloud Platform infrastructure using Terraform with the `hashicorp/google` provider. ## When to Use - Defining GCP infrastructure as code for repeatable, auditable deployments - Managing multi-environment setups (dev, staging, production) from a single codebase - Provisioning complex resource graphs (VPC + GKE + Cloud SQL + IAM) in one plan - Integrating infrastructure changes into CI/CD pipelines with plan/apply stages ## Prerequisites - Terraform >= 1.5 installed - Google Cloud SDK or a service account key for CI - A GCP project with billing enabled ```bash gcloud auth application-default login # local dev export GOOGLE_APPLICATION_CREDENTIALS="sa.json" # CI/CD terraform version ``` ## Provider Configuration ```hcl # versions.tf terraform { required_version = ">= 1.5" required_providers { google = { source = "hashicorp/google"; version = "~> 5.0" } google-beta = { source = "hashicorp/google-beta"; version = "~> 5.0" } } backend "gcs" { bucket = "my-project-tf-state"; prefix = "terraform/state" } } provider "google" { project = var.project_id; region = var.region } provider "google-beta" { project = var.project_id; region = var.region } ``` ```hcl # variables.tf variable "project_id" { type = string } variable "region" { type = string; default = "us-central1" } variable "environment" { type = string validation { condition = contains(["dev", "staging", "production"], var.environment) error_message = "Must be dev, staging, or production." } } ``` ## Project Setup and State Bucket ```bash gcloud storage buckets create gs://my-project-tf-state \ --location=us-central1 --uniform-bucket-level-access --public-access-prevention gcloud storage buckets update gs://my-project-tf-state --versioning terraform init terraform plan -var="project_id=my-project" -var="environment=production" -out=tfplan terraform apply tfplan ``` ```hcl resource "google_project_service" "apis" { for_each = toset([ "compute.googleapis.com", "container.googleapis.com", "sqladmin.googleapis.com", "servicenetworking.googleapis.com", "cloudfunctions.googleapis.com", "run.googleapis.com", "secretmanager.googleapis.com", "artifactregistry.googleapis.com", ]) project = var.project_id service = each.value disable_dependent_services = false disable_on_destroy = false } ``` ## Networking Module ```hcl # modules/networking/main.tf resource "google_compute_network" "vpc" { name = "${var.environment}-vpc" auto_create_subnetworks = false routing_mode = "REGIONAL" } resource "google_compute_subnetwork" "main" { name = "${var.environment}-main-subnet" ip_cidr_range = var.subnet_cidr region = var.region network = google_compute_network.vpc.id private_ip_google_access = true log_config { aggregation_interval = "INTERVAL_5_SEC"; flow_sampling = 0.5 } } resource "google_compute_subnetwork" "gke" { name = "${var.environment}-gke-subnet" ip_cidr_range = var.gke_subnet_cidr region = var.region network = google_compute_network.vpc.id private_ip_google_access = true secondary_ip_range { range_name = "pods"; ip_cidr_range = var.pods_cidr } secondary_ip_range { range_name = "services"; ip_cidr_range = var.services_cidr } } resource "google_compute_firewall" "allow_iap" { name = "${var.environment}-allow-iap" network = google_compute_network.vpc.name allow { protocol = "tcp"; ports = ["22", "3389"] } source_ranges = ["35.235.240.0/20"] } resource "google_compute_router" "router" { name = "${var.environment}-router" region = var.region network = google_compute_network.vpc.id } resource "google_compute_router_nat" "nat" { name = "${var.environment}-nat" router = google_compute_router.router.name region = var.region nat_ip_allocate_option = "AUTO_ONLY" source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_IP_RANGES" log_config { enable = true; filter = "ERRORS_ONLY" } } output "vpc_id" { value = google_compute_network.vpc.id } output "gke_subnet_id" { value = google_compute_subnetwork.gke.id } ``` ## GKE Cluster Module ```hcl # modules/gke/main.tf resource "google_container_cluster" "primary" { name = "${var.environment}-cluster" location = var.region release_channel { channel = var.release_channel } workload_identity_config { workload_pool = "${var.project_id}.svc.id.goog" } network = var.vpc_name subnetwork = var.gke_subnet_name ip_allocation_policy { cluster_secondary_range_name = "pods" services_secondary_range_name = "services" } private_cluster_config { enable_private_nodes = true master_ipv4_cidr_block = "172.16.0.0/28" } network_policy { enabled = true } logging_config { enable_components = ["SYSTEM_COMPONENTS", "WORKLOADS"] } monitoring_config { enable_components = ["SYSTEM_COMPONENTS", "WORKLOADS"] managed_prometheus { enabled = true } } remove_default_node_pool = true initial_node_count = 1 } resource "google_container_node_pool" "primary" { name = "primary-pool" cluster = google_container_cluster.primary.name location = var.region initial_node_count = var.initial_node_count autoscaling { min_node_count = var.min_nodes; max_node_count = var.max_nodes } management { auto_repair = true; auto_upgrade = true } node_config { machine_type = var.machine_type disk_size_gb = 100 oauth_scopes = ["https://www.googleapis.com/auth/cloud-platform"] shielded_instance_config { enable_secure_boot = true; enable_integrity_monitoring = true } metadata = { disable-legacy-endpoints = "true" } } } output "cluster_name" { value = google_container_cluster.primary.name } output "cluster_endpoint" { value = google_container_cluster.primary.endpoint; sensitive = true } ``` ## Cloud SQL Module ```hcl # modules/cloud-sql/main.tf resource "google_sql_database_instance" "main" { name = "${var.environment}-db" database_version = var.database_version region = var.region settings { tier = var.tier availability_type = var.environment == "production" ? "REGIONAL" : "ZONAL" disk_type = "PD_SSD" disk_size = var.disk_size disk_autoresize = true backup_configuration { enabled = true start_time = "02:00" point_in_time_recovery_enabled = true backup_retention_settings { retained_backups = var.environment == "production" ? 30 : 7 } } ip_configuration { ipv4_enabled = false private_network = var.vpc_id require_ssl = true } database_flags { name = "max_connections"; value = var.max_connections } } deletion_protection = var.environment == "production" depends_on = [var.private_vpc_connection] } resource "google_sql_database" "app" { name = var.database_name; instance = google_sql_database_instance.main.name } resource "google_sql_user" "app" { name = var.db_user; instance = google_sql_database_instance.main.name; password = random_password.db.result } resource "random_password" "db" { length = 32; special = true } output "connection_name" { value = google_sql_database_instance.main.connection_name } output "private_ip" { value = google_sql_database_instance.main.private_ip_address } ``` ## IAM and Service Accounts ```hcl resource "google_service_account" "gke_nodes" { account_id = "${var.environment}-gke-nodes" display_name = "GKE Node Pool SA" } resource "google_project_iam_member" "gke_nodes" { for_each = toset([ "roles/logging.logWriter", "roles/monitoring.metricWriter", "roles/artifactregistry.reader", ]) project = var.project_id role = each.value member = "serviceAccount:${google_service_account.gke_nodes.email}" } resource "google_service_account" "app" { account_id = "${var.environment}-app" display_name = "Application SA" } resource "google_service_account_iam_member" "workload_identity" { service_account_id = google_service_account.app.name role = "roles/iam.workloadIdentityUser" member = "serviceAccount:${var.project_id}.svc.id.goog[myapp/app-ksa]" } ``` ## Root Module Composition ```hcl module "networking" { source = "./modules/networking" project_id = var.project_id environment = var.environment region = var.region } module "gke" { source = "./modules/gke" project_id = var.project_id environment = var.environment region = var.region vpc_name = module.networking.vpc_id gke_subnet_name = module.networking.gke_subnet_id node_sa_email = google_service_account.gke_nodes.email depends_on = [module.networking] } module "database" { source = "./modules/cloud-sql" project_id = var.project_id environment = var.environment region = var.region vpc_id = module.networking.vpc_id database_version = "POSTGRES_16" tier = "db-custom-4-16384" private_vpc_connection = module.networking.private_vpc_connection depends_on = [module.networking] } ``` ## Environment Configuration ```hcl # environments/production.tfvars project_id = "my-company-prod" environment = "production" region = "us-central1" ``` ```bash terraform plan -var-file=environments/production.tfvars -out=tfplan terraform apply tfplan ``` ## CI/CD Integration ```bash terraform init -input=false terraform validate && terraform fmt -check terraform plan -var-file=environments/${ENV}.tfvars -out=tfplan -input=false terraform apply -input=false tfplan # Import existing resources terraform import google_compute_network.vpc projects/${PROJECT_ID}/global/networks/prod-vpc # State management terraform state list terraform state mv google_compute_instance.old google_compute_instance.new ``` ## Troubleshooting | Symptom | Cause | Fix | |---------|-------|-----| | `Error 403: Access Not Configured` | API not enabled | Add API to `google_project_service` resources | | `Error acquiring the state lock` | Concurrent run or stale lock | Run `terraform force-unlock LOCK_ID` after verification | | `Resource already exists` | Created outside Terraform | Import with `terraform import` | | `Quota exceeded` | Project quota too low | Request increase in Cloud Console > Quotas | | Plan shows destroy/recreate | Changed force-new attribute | Use `moved` blocks or `terraform state mv` | | `Backend initialization required` | Changed backend config | Run `terraform init -migrate-state` | | Cycle in resource graph | Circular references | Refactor with data sources; split applies | ## Related Skills - **gcp-networking** - VPC and firewall resources managed by Terraform - **gcp-gke** - GKE cluster provisioning with Terraform modules - **gcp-cloud-sql** - Cloud SQL instance management via Terraform - **gcp-compute** - Compute Engine resources defined in Terraform - **gcp-cloud-functions** - Serverless function deployment with Terraform