Automating GCP Firewall Rules with Terraform: Consistent, Scalable Network Security

Automating GCP Firewall Rules with Terraform: Consistent, Scalable Network Security

Introduction

Firewall rules are your first—and often only—line of defense in a VPC. Manual management invites drift, misconfigurations, and audit headaches. In this post, you’ll learn how to codify, test, and deploy GCP firewall rules reliably using Terraform.


Background & Problem Statement

Every GCP firewall rule consists of:

  • Network tags or service accounts
  • Direction (INGRESS or EGRESS)
  • Priority and name conventions
  • Source/destination ranges and protocols

Manual edits often lead to:

  • Inconsistent naming
  • Overly permissive “catch-all” rules
  • Forgotten rules after team handoffs

Why Terraform for Firewall Automation

  1. Idempotence – Repeat applies with zero surprises
  2. Version Control & Code Review – Peer-review your security policy
  3. Drift Detection & Auditing – See exactly what will change with terraform plan

Getting Started: Provider & Setup

  1. Enable the Compute API in your GCP project.

  2. Create a service account with roles/compute.networkAdmin and download its JSON key.

  3. Add this to your Terraform config:

    provider "google" {
      credentials = file(var.credentials_file)
      project     = var.project_id
      region      = var.region
    }
    
    
  4. Initialize and validate:

    terraform init
    terraform validate
    

Defining a Simple Firewall Rule

resource "google_compute_firewall" "allow_ssh" {
  name          = "allow-ssh"
  network       = var.network
  direction     = "INGRESS"
  priority      = 1000
  source_ranges = ["203.0.113.0/24"]

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

  target_tags = ["ssh-able"]
}

Run:

terraform plan
terraform apply

Parameterizing with Variables & Locals

variable "environments" {
  type = map(object({
    cidr = string
    tags = list(string)
  }))
  default = {
    dev  = { cidr = "10.0.0.0/24",  tags = ["dev"] }
    prod = { cidr = "192.168.0.0/24", tags = ["prod"] }
  }
}

locals {
  firewall_rules = [
    for name, env in var.environments : {
      name_prefix   = "allow-${name}-ssh"
      source_ranges = [env.cidr]
      target_tags   = env.tags
    }
  ]
}

Building a Reusable Module

  1. Create modules/firewall-rule/main.tf:

    variable "name_prefix"   { type = string }
    variable "network"       { type = string }
    variable "source_ranges" { type = list(string) }
    variable "allows"        { type = list(object({ protocol = string, ports = list(string) })) }
    variable "target_tags"   { type = list(string) }
    variable "priority"      { type = number }
    
    resource "google_compute_firewall" "this" {
      name          = var.name_prefix
      network       = var.network
      direction     = "INGRESS"
      priority      = var.priority
      source_ranges = var.source_ranges
    
      dynamic "allows" {
        for_each = var.allows
        content {
          protocol = allows.value.protocol
          ports    = allows.value.ports
        }
      }
    
      target_tags = var.target_tags
    }
    
  2. Invoke the module in your root config:

    module "app_firewall" {
      source        = "./modules/firewall-rule"
      name_prefix   = "allow-app"
      network       = var.network
      source_ranges = ["0.0.0.0/0"]
      allows        = [{ protocol = "tcp", ports = ["8080", "443"] }]
      target_tags   = ["app-server"]
      priority      = 1000
    }
    

Testing & Validation

  • Dry Runs: Gate terraform plan in your CI pipelines.
  • Security Scans: Integrate tools like tfsec or checkov.
  • Connectivity Tests: Use GCP’s Network Intelligence Center for end-to-end reachability validation (see Connectivity Tests in GCP).

CI/CD Integration

# .github/workflows/terraform.yml
jobs:
  terraform:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: hashicorp/setup-terraform@v2
        with:
          terraform_version: 1.5.0
      - run: terraform fmt -check
      - run: terraform validate
      - run: terraform plan -out=tfplan
      - name: Apply in Prod
        if: github.ref == 'refs/heads/main'
        run: terraform apply -auto-approve tfplan

Best Practices & Tips

  • Naming Conventions: Prefix by environment—e.g., prod-allow-ssh.
  • Least Privilege: Default deny, then allow only required traffic.
  • Tags vs. Service Accounts: Choose the right selector for your VM groups.
  • Audit Logging: Enable VPC Flow Logs for firewall rule hits.

Common Pitfalls & FAQs

IssueFix
Overlapping priorities cause conflictsAllocate distinct ranges: SSH (1000–1999), APP (2000–2999).
Forgotten instance tagsManage tags via Terraform or startup scripts.
Default “allow-google” interferenceDisable default rules or add higher-priority denies.

Conclusion

Terraform brings consistency, auditability, and repeatability to firewall management—ensuring your VPC scales securely with your team’s velocity.


Call to Action

  • 💬 Drop your Terraform firewall patterns in the comments below!
  • 🔗 Check out Connectivity Tests in GCP for full-network validation.
  • 📥 Subscribe for monthly GCP IaC deep dives.

References & Further Reading