Skip to content

Instantly share code, notes, and snippets.

@samklr
Last active June 2, 2025 14:17
Show Gist options
  • Save samklr/c4de062a4a3838f9ed33f11ce0058c77 to your computer and use it in GitHub Desktop.
Save samklr/c4de062a4a3838f9ed33f11ce0058c77 to your computer and use it in GitHub Desktop.
TF Practices

GCP deployment with Terraform

Basics

Run terraform fmt to format your code consistently

Use terraform validate to check for syntax or semantic issues before apply

Adopt tflint or similar linters to catch anti-patterns or unused code

These tools improve reliability and make collaboration smoother — and they're easy to integrate into a basic workflow or CI/CD pipeline.

Backend ready (even if local state)

Environment separation (dev/staging/prod)

Readable naming conventions

Inline documentation

Module-ready folder structure

Optional: start using terraform-docs or tflint

Best practices for deploying a platform on GCP with Terraform?


  1. Project Structure

    • Organize your Terraform code into logical modules
    • Use a consistent directory structure:
      terraform/
      ├── environments/
      │   ├── dev/
      │   ├── staging/
      │   └── prod/
      ├── modules/
      │   ├── networking/
      │   ├── compute/
      │   └── storage/
      └── shared/
      
  2. State Management

    • Use remote state storage in GCS (Google Cloud Storage)
    • Enable state locking using GCS Or S3 backend
    • Example backend configuration:
      terraform {
        backend "gcs" {
          bucket = "your-terraform-state-bucket"
          prefix = "terraform/state"
        }
      }
  3. Security Best Practices

    • Use service accounts with least privilege
    • Store sensitive data in Secret Manager
    • Enable VPC Service Controls
    • Use private Google Access
    • Implement proper IAM roles and permissions
  4. Code Organization

    • Use variables for configurable values
    • Implement proper tagging/labeling
    • Use data sources for existing resources
    • Implement proper output values
    • Use locals for computed values
  5. Version Control

    • Use semantic versioning for modules
    • Implement proper branching strategy
    • Use .gitignore for sensitive files
    • Document your infrastructure code
  6. CI/CD Integration

    • Implement automated testing
    • Use Terraform Cloud or similar for remote execution
    • Implement proper validation steps
    • Use workspaces for environment separation
  7. Resource Management

    • Use proper resource naming conventions
    • Implement proper resource dependencies
    • Use count/for_each for resource replication
    • Implement proper resource lifecycle rules
  8. Cost Management

    • Use proper resource sizing
    • Implement proper auto-scaling
    • Use committed use discounts where appropriate
    • Implement proper monitoring and alerting
  9. Networking

    • Use proper VPC design
    • Implement proper firewall rules
    • Use proper subnet design
    • Implement proper routing
  10. Monitoring and Logging

    • Enable proper logging
    • Implement proper monitoring
    • Use proper alerting
    • Implement proper tracing
  11. Disaster Recovery

    • Implement proper backup strategies
    • Use proper replication
    • Implement proper failover
    • Use proper recovery procedures
  12. Documentation

    • Document your infrastructure
    • Document your modules
    • Document your variables
    • Document your outputs
  13. Testing

    • Implement proper unit testing
    • Implement proper integration testing
    • Use proper test coverage
    • Implement proper validation
  14. Compliance

    • Implement proper security controls
    • Use proper compliance frameworks
    • Implement proper auditing
    • Use proper logging
  15. Performance

    • Use proper resource sizing
    • Implement proper caching
    • Use proper load balancing
    • Implement proper scaling
     terraform/
     ├── environments/
     │   ├── dev/
     │   ├── staging/
     │   └── prod/
     ├── modules/
     │   ├── networking/
     │   ├── compute/
     │   └── storage/
     └── shared/
     terraform {
       backend "gcs" {
         bucket = "your-terraform-state-bucket"
         prefix = "terraform/state"
       }
     }

Networking

A comprehensive guide:

  1. VPC Design Best Practices
    • Create separate VPCs for different environments (dev, staging, prod)
    • Use proper CIDR ranges that don't overlap
    • Implement proper subnet design
    • Use proper naming conventions

Example

# modules/networking/main.tf
resource "google_compute_network" "vpc" {
  name                    = var.vpc_name
  auto_create_subnetworks = false
  project                 = var.project_id
}

resource "google_compute_subnetwork" "subnet" {
  for_each = var.subnets

  name          = each.value.name
  ip_cidr_range = each.value.ip_cidr_range
  region        = each.value.region
  network       = google_compute_network.vpc.id
  
  private_ip_google_access = true

  dynamic "log_config" {
    for_each = each.value.enable_flow_logs ? [1] : []
    content {
      aggregation_interval = "INTERVAL_5_SEC"
      flow_sampling        = 0.5
      metadata            = "INCLUDE_ALL_METADATA"
    }
  }
}
  1. Firewall Rules Best Practices
    • Implement least privilege access
    • Use proper naming conventions
    • Implement proper tagging
    • Use proper source/destination ranges

Example of firewall rules:

# modules/networking/firewall.tf
resource "google_compute_firewall" "allow_internal" {
  name    = "allow-internal"
  network = google_compute_network.vpc.name

  allow {
    protocol = "tcp"
    ports    = ["0-65535"]
  }

  allow {
    protocol = "udp"
    ports    = ["0-65535"]
  }

  allow {
    protocol = "icmp"
  }

  source_ranges = [var.vpc_cidr]
  target_tags   = ["internal"]
}

resource "google_compute_firewall" "allow_ssh" {
  name    = "allow-ssh"
  network = google_compute_network.vpc.name

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

  source_ranges = ["0.0.0.0/0"]
  target_tags   = ["ssh"]
}
  1. Cloud NAT Best Practices
    • Implement proper NAT gateway configuration
    • Use proper IP allocation
    • Implement proper logging
    • Use proper routing

Example of Cloud NAT configuration:

# modules/networking/nat.tf
resource "google_compute_router" "router" {
  name    = "nat-router"
  region  = var.region
  network = google_compute_network.vpc.id
}

resource "google_compute_router_nat" "nat" {
  name                               = "nat-gateway"
  router                            = google_compute_router.router.name
  region                            = google_compute_router.router.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"
  }
}
  1. VPC Peering Best Practices
    • Implement proper peering configuration
    • Use proper routing
    • Implement proper security
    • Use proper naming conventions

Example of VPC peering:

# modules/networking/peering.tf
resource "google_compute_network_peering" "peering" {
  name         = "peering-${var.peer_name}"
  network      = google_compute_network.vpc.id
  peer_network = var.peer_network_id

  export_custom_routes = true
  import_custom_routes = true
}
  1. Cloud DNS Best Practices
    • Implement proper DNS zones
    • Use proper record sets
    • Implement proper security
    • Use proper naming conventions

Example of Cloud DNS configuration:

# modules/networking/dns.tf
resource "google_dns_managed_zone" "private_zone" {
  name        = "private-zone"
  dns_name    = "private.example.com."
  description = "Private DNS zone"

  visibility = "private"

  private_visibility_config {
    networks {
      network_url = google_compute_network.vpc.id
    }
  }
}

resource "google_dns_record_set" "a_record" {
  name         = "app.${google_dns_managed_zone.private_zone.dns_name}"
  managed_zone = google_dns_managed_zone.private_zone.name
  type         = "A"
  ttl          = 300

  rrdatas = ["10.0.0.1"]
}
  1. Load Balancing Best Practices
    • Implement proper health checks
    • Use proper backend services
    • Implement proper SSL/TLS
    • Use proper routing rules

Example of load balancer configuration:

# modules/networking/load_balancer.tf
resource "google_compute_health_check" "health_check" {
  name               = "health-check"
  check_interval_sec = 5
  timeout_sec        = 5

  http_health_check {
    port = 80
  }
}

resource "google_compute_backend_service" "backend" {
  name        = "backend-service"
  protocol    = "HTTP"
  port_name   = "http"
  timeout_sec = 10

  health_checks = [google_compute_health_check.health_check.id]

  backend {
    group = var.instance_group
  }
}
  1. Variables and Outputs
    • Use proper variable definitions
    • Implement proper output values
    • Use proper descriptions
    • Use proper validation

Example of variables and outputs:

# modules/networking/variables.tf
variable "project_id" {
  description = "The project ID to deploy to"
  type        = string
}

variable "vpc_name" {
  description = "The name of the VPC"
  type        = string
}

variable "subnets" {
  description = "The subnets to create"
  type = map(object({
    name            = string
    ip_cidr_range   = string
    region          = string
    enable_flow_logs = bool
  }))
}

# modules/networking/outputs.tf
output "vpc_id" {
  description = "The ID of the VPC"
  value       = google_compute_network.vpc.id
}

output "subnet_ids" {
  description = "The IDs of the subnets"
  value       = { for k, v in google_compute_subnetwork.subnet : k => v.id }
}
  1. Security Best Practices
    • Implement proper IAM roles
    • Use proper service accounts
    • Implement proper VPC Service Controls
    • Use proper private Google Access

Example of security configuration:

# modules/networking/security.tf
resource "google_compute_shared_vpc_host_project" "host" {
  project = var.project_id
}

resource "google_compute_shared_vpc_service_project" "service" {
  host_project    = google_compute_shared_vpc_host_project.host.project
  service_project = var.service_project_id
}
# modules/networking/main.tf
resource "google_compute_network" "vpc" {
  name                    = var.vpc_name
  auto_create_subnetworks = false
  project                 = var.project_id
}

resource "google_compute_subnetwork" "subnet" {
  for_each = var.subnets

  name          = each.value.name
  ip_cidr_range = each.value.ip_cidr_range
  region        = each.value.region
  network       = google_compute_network.vpc.id
  
  private_ip_google_access = true

  dynamic "log_config" {
    for_each = each.value.enable_flow_logs ? [1] : []
    content {
      aggregation_interval = "INTERVAL_5_SEC"
      flow_sampling        = 0.5
      metadata            = "INCLUDE_ALL_METADATA"
    }
  }
}
# modules/networking/firewall.tf
resource "google_compute_firewall" "allow_internal" {
  name    = "allow-internal"
  network = google_compute_network.vpc.name

  allow {
    protocol = "tcp"
    ports    = ["0-65535"]
  }

  allow {
    protocol = "udp"
    ports    = ["0-65535"]
  }

  allow {
    protocol = "icmp"
  }

  source_ranges = [var.vpc_cidr]
  target_tags   = ["internal"]
}

resource "google_compute_firewall" "allow_ssh" {
  name    = "allow-ssh"
  network = google_compute_network.vpc.name

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

  source_ranges = ["0.0.0.0/0"]
  target_tags   = ["ssh"]
}
# modules/networking/nat.tf
resource "google_compute_router" "router" {
  name    = "nat-router"
  region  = var.region
  network = google_compute_network.vpc.id
}

resource "google_compute_router_nat" "nat" {
  name                               = "nat-gateway"
  router                            = google_compute_router.router.name
  region                            = google_compute_router.router.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"
  }
}
# modules/networking/peering.tf
resource "google_compute_network_peering" "peering" {
  name         = "peering-${var.peer_name}"
  network      = google_compute_network.vpc.id
  peer_network = var.peer_network_id

  export_custom_routes = true
  import_custom_routes = true
}
# modules/networking/dns.tf
resource "google_dns_managed_zone" "private_zone" {
  name        = "private-zone"
  dns_name    = "private.example.com."
  description = "Private DNS zone"

  visibility = "private"

  private_visibility_config {
    networks {
      network_url = google_compute_network.vpc.id
    }
  }
}

resource "google_dns_record_set" "a_record" {
  name         = "app.${google_dns_managed_zone.private_zone.dns_name}"
  managed_zone = google_dns_managed_zone.private_zone.name
  type         = "A"
  ttl          = 300

  rrdatas = ["10.0.0.1"]
}
# modules/networking/load_balancer.tf
resource "google_compute_health_check" "health_check" {
  name               = "health-check"
  check_interval_sec = 5
  timeout_sec        = 5

  http_health_check {
    port = 80
  }
}

resource "google_compute_backend_service" "backend" {
  name        = "backend-service"
  protocol    = "HTTP"
  port_name   = "http"
  timeout_sec = 10

  health_checks = [google_compute_health_check.health_check.id]

  backend {
    group = var.instance_group
  }
}
# modules/networking/variables.tf
variable "project_id" {
  description = "The project ID to deploy to"
  type        = string
}

variable "vpc_name" {
  description = "The name of the VPC"
  type        = string
}

variable "subnets" {
  description = "The subnets to create"
  type = map(object({
    name            = string
    ip_cidr_range   = string
    region          = string
    enable_flow_logs = bool
  }))
}

# modules/networking/outputs.tf
output "vpc_id" {
  description = "The ID of the VPC"
  value       = google_compute_network.vpc.id
}

output "subnet_ids" {
  description = "The IDs of the subnets"
  value       = { for k, v in google_compute_subnetwork.subnet : k => v.id }
}
# modules/networking/security.tf
resource "google_compute_shared_vpc_host_project" "host" {
  project = var.project_id
}

resource "google_compute_shared_vpc_service_project" "service" {
  host_project    = google_compute_shared_vpc_host_project.host.project
  service_project = var.service_project_id
}

Shared VPC

A Shared VPC allows you to share a VPC network across multiple projects, which is particularly useful for organizations with multiple teams or environments.

Here's a comprehensive guide for implementing a Shared VPC:

  1. Project Structure First, you'll need to organize your projects into:
  • Host Project: Contains the Shared VPC
  • Service Projects: Projects that will use the Shared VPC

Here's an example of how to structure your Terraform code:

# modules/shared-vpc/main.tf
# Host Project Configuration
resource "google_compute_shared_vpc_host_project" "host" {
  project = var.host_project_id
}

# VPC Network in Host Project
resource "google_compute_network" "shared_vpc" {
  name                    = "shared-vpc"
  auto_create_subnetworks = false
  project                 = var.host_project_id
}

# Subnets in Host Project
resource "google_compute_subnetwork" "shared_subnet" {
  for_each = var.subnets

  name          = each.value.name
  ip_cidr_range = each.value.ip_cidr_range
  region        = each.value.region
  network       = google_compute_network.shared_vpc.id
  project       = var.host_project_id

  private_ip_google_access = true
}

# Service Project Attachment
resource "google_compute_shared_vpc_service_project" "service" {
  for_each = var.service_projects

  host_project    = var.host_project_id
  service_project = each.value
}
  1. IAM Configuration Set up proper IAM roles for the Shared VPC:
# modules/shared-vpc/iam.tf
# Network Admin role for service projects
resource "google_project_iam_member" "network_admin" {
  for_each = var.service_projects

  project = each.value
  role    = "roles/compute.networkUser"
  member  = "serviceAccount:${var.service_account_email}"
}

# Security Admin role for service projects
resource "google_project_iam_member" "security_admin" {
  for_each = var.service_projects

  project = each.value
  role    = "roles/compute.securityAdmin"
  member  = "serviceAccount:${var.service_account_email}"
}
  1. Firewall Rules Configure firewall rules in the host project:
# modules/shared-vpc/firewall.tf
resource "google_compute_firewall" "allow_internal" {
  name    = "allow-internal"
  network = google_compute_network.shared_vpc.name
  project = var.host_project_id

  allow {
    protocol = "tcp"
    ports    = ["0-65535"]
  }

  allow {
    protocol = "udp"
    ports    = ["0-65535"]
  }

  allow {
    protocol = "icmp"
  }

  source_ranges = [var.vpc_cidr]
  target_tags   = ["internal"]
}

# Allow specific service project access
resource "google_compute_firewall" "allow_service_project" {
  name    = "allow-service-project"
  network = google_compute_network.shared_vpc.name
  project = var.host_project_id

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

  source_ranges = var.service_project_cidrs
  target_tags   = ["service-project"]
}
  1. Variables Configuration Define the necessary variables:
# modules/shared-vpc/variables.tf
variable "host_project_id" {
  description = "The ID of the host project"
  type        = string
}

variable "service_projects" {
  description = "Map of service project IDs"
  type        = map(string)
}

variable "subnets" {
  description = "Map of subnet configurations"
  type = map(object({
    name            = string
    ip_cidr_range   = string
    region          = string
    enable_flow_logs = bool
  }))
}

variable "service_account_email" {
  description = "Service account email for IAM bindings"
  type        = string
}

variable "vpc_cidr" {
  description = "CIDR range for the VPC"
  type        = string
}

variable "service_project_cidrs" {
  description = "List of CIDR ranges for service projects"
  type        = list(string)
}
  1. Outputs Define useful outputs:
# modules/shared-vpc/outputs.tf
output "vpc_id" {
  description = "The ID of the shared VPC"
  value       = google_compute_network.shared_vpc.id
}

output "subnet_ids" {
  description = "Map of subnet IDs"
  value       = { for k, v in google_compute_subnetwork.shared_subnet : k => v.id }
}
  1. Usage Example Here's how to use the Shared VPC module:
# environments/prod/main.tf
module "shared_vpc" {
  source = "../../modules/shared-vpc"

  host_project_id = "host-project-id"
  service_projects = {
    "service-project-1" = "service-project-1-id"
    "service-project-2" = "service-project-2-id"
  }
  
  subnets = {
    "subnet-1" = {
      name            = "subnet-1"
      ip_cidr_range   = "10.0.0.0/24"
      region          = "us-central1"
      enable_flow_logs = true
    }
    "subnet-2" = {
      name            = "subnet-2"
      ip_cidr_range   = "10.0.1.0/24"
      region          = "us-east1"
      enable_flow_logs = true
    }
  }

  service_account_email = "[email protected]"
  vpc_cidr             = "10.0.0.0/16"
  service_project_cidrs = ["10.0.0.0/24", "10.0.1.0/24"]
}
  1. Best Practices for Shared VPC
  • Use proper naming conventions for all resources
  • Implement proper IAM roles and permissions
  • Use proper subnet design
  • Implement proper firewall rules
  • Use proper logging and monitoring
  • Implement proper security controls
  • Use proper documentation
  • Implement proper testing
  • Use proper version control
  • Implement proper CI/CD
  1. Security Considerations
  • Implement proper IAM roles
  • Use proper service accounts
  • Implement proper VPC Service Controls
  • Use proper private Google Access
  • Implement proper firewall rules
  • Use proper logging and monitoring
  • Implement proper security controls
  • Use proper documentation
# modules/shared-vpc/main.tf
# Host Project Configuration
resource "google_compute_shared_vpc_host_project" "host" {
  project = var.host_project_id
}

# VPC Network in Host Project
resource "google_compute_network" "shared_vpc" {
  name                    = "shared-vpc"
  auto_create_subnetworks = false
  project                 = var.host_project_id
}

# Subnets in Host Project
resource "google_compute_subnetwork" "shared_subnet" {
  for_each = var.subnets

  name          = each.value.name
  ip_cidr_range = each.value.ip_cidr_range
  region        = each.value.region
  network       = google_compute_network.shared_vpc.id
  project       = var.host_project_id

  private_ip_google_access = true
}

# Service Project Attachment
resource "google_compute_shared_vpc_service_project" "service" {
  for_each = var.service_projects

  host_project    = var.host_project_id
  service_project = each.value
}
# modules/shared-vpc/iam.tf
# Network Admin role for service projects
resource "google_project_iam_member" "network_admin" {
  for_each = var.service_projects

  project = each.value
  role    = "roles/compute.networkUser"
  member  = "serviceAccount:${var.service_account_email}"
}

# Security Admin role for service projects
resource "google_project_iam_member" "security_admin" {
  for_each = var.service_projects

  project = each.value
  role    = "roles/compute.securityAdmin"
  member  = "serviceAccount:${var.service_account_email}"
}
# modules/shared-vpc/firewall.tf
resource "google_compute_firewall" "allow_internal" {
  name    = "allow-internal"
  network = google_compute_network.shared_vpc.name
  project = var.host_project_id

  allow {
    protocol = "tcp"
    ports    = ["0-65535"]
  }

  allow {
    protocol = "udp"
    ports    = ["0-65535"]
  }

  allow {
    protocol = "icmp"
  }

  source_ranges = [var.vpc_cidr]
  target_tags   = ["internal"]
}

# Allow specific service project access
resource "google_compute_firewall" "allow_service_project" {
  name    = "allow-service-project"
  network = google_compute_network.shared_vpc.name
  project = var.host_project_id

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

  source_ranges = var.service_project_cidrs
  target_tags   = ["service-project"]
}
# modules/shared-vpc/variables.tf
variable "host_project_id" {
  description = "The ID of the host project"
  type        = string
}

variable "service_projects" {
  description = "Map of service project IDs"
  type        = map(string)
}

variable "subnets" {
  description = "Map of subnet configurations"
  type = map(object({
    name            = string
    ip_cidr_range   = string
    region          = string
    enable_flow_logs = bool
  }))
}

variable "service_account_email" {
  description = "Service account email for IAM bindings"
  type        = string
}

variable "vpc_cidr" {
  description = "CIDR range for the VPC"
  type        = string
}

variable "service_project_cidrs" {
  description = "List of CIDR ranges for service projects"
  type        = list(string)
}
# modules/shared-vpc/outputs.tf
output "vpc_id" {
  description = "The ID of the shared VPC"
  value       = google_compute_network.shared_vpc.id
}

output "subnet_ids" {
  description = "Map of subnet IDs"
  value       = { for k, v in google_compute_subnetwork.shared_subnet : k => v.id }
}
# environments/prod/main.tf
module "shared_vpc" {
  source = "../../modules/shared-vpc"

  host_project_id = "host-project-id"
  service_projects = {
    "service-project-1" = "service-project-1-id"
    "service-project-2" = "service-project-2-id"
  }
  
  subnets = {
    "subnet-1" = {
      name            = "subnet-1"
      ip_cidr_range   = "10.0.0.0/24"
      region          = "us-central1"
      enable_flow_logs = true
    }
    "subnet-2" = {
      name            = "subnet-2"
      ip_cidr_range   = "10.0.1.0/24"
      region          = "us-east1"
      enable_flow_logs = true
    }
  }

  service_account_email = "[email protected]"
  vpc_cidr             = "10.0.0.0/16"
  service_project_cidrs = ["10.0.0.0/24", "10.0.1.0/24"]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment