Skip to content

Multi-Cloud Networking & CIDR Strategy Guide

This document outlines the standardized approach for managing IP address space across AWS, Azure, and GCP to ensure zero overlaps in a hybrid-cloud environment.

1. Global CIDR Allocation (The Root Registry)

We use a central registry (live/network_registry.hcl) as the single source of truth. The 10.0.0.0/8 private IP space is divided into large blocks for each provider.

Core Allocation Map

Cloud Provider Purpose CIDR Range
AWS Development 10.10.0.0/16
AWS Staging 10.11.0.0/16
AWS Production 10.12.0.0/16
Azure Development 10.20.0.0/16
GCP Development 10.30.0.0/16

Environment-Level Mapping (Example)

Each environment is allocated a full /16 range to provide maximum scalability.

2. Avoidance Zones (Cloud Defaults)

To prevent routing conflicts with "Default VPCs" or "Auto-Mode" networks, we explicitly AVOID the following ranges:

  • AWS Default VPCs: 172.31.0.0/16
  • Azure Portal Defaults: 10.0.0.0/16
  • GCP Auto-Mode VPCs: 10.128.0.0/9 (Matches anything from 10.128.x.x to 10.255.x.x)

3. Hierarchical Subnetting (Internal VPC Design)

Within a single /16 environment (65,536 IPs), we use a hierarchical structure to accommodate different resource types.

Subnet Capacity per /16

Subnet Size Total IPs Max Subnets per /16 Ideal Use Case
/18 16,384 4 Large Clusters (Secondary Ranges)
/24 256 256 Standard Nodes, Management, LBs

4. Automation with cidrsubnet

We avoid hardcoding subnet ranges. Instead, we use the Terraform cidrsubnet function for mathematical precision and portability.

Usage Pattern

cidrsubnet(base_cidr, newbits, index)

  • To get a /18 (from a /16): newbits = 2
  • To get a /24 (from a /16): newbits = 8

Implementation Example

locals {
  vpc_cidr = "10.30.0.0/16"
}

# The First /24 for Nodes
node_range = cidrsubnet(local.vpc_cidr, 8, 0) # "10.30.0.0/24"

# A /24 for Pods
pod_range = cidrsubnet(local.vpc_cidr, 8, 1) # "10.30.1.0/24"

5. Expansion Workflow: Adding New Ranges and Subnets

To maintain a conflict-free network as the platform scales, you MUST follow this "Registry-First" workflow. This process mathematically guarantees that new environments or subnets will not overlap with existing infrastructure.

Step 1: Claim the Block in the Global Registry

Before touching any Terraform code, you must register the new environment in live/network_registry.hcl.

Example: Adding a new gcp-prod environment. 1. Check the registry for the next available /16 block. 2. Add the entry:

    gcp = {
      dev    = "10.30.0.0/16"
      stg    = "10.31.0.0/16"
      prod   = "10.32.0.0/16" # <-- NEW ENTRY
      shared = "10.33.0.0/16"
    }

Step 2: Ingest the CIDR via Terragrunt

Create the terragrunt.hcl for the new environment and configure it to pull the CIDR dynamically. Do not hardcode the IP.

# live/gcp/prod/vpc/terragrunt.hcl
locals {
  network_registry = read_terragrunt_config(find_in_parent_folders("network_registry.hcl"))
  cloud            = "gcp"
  env              = "prod"
}

inputs = {
  # Dynamically pulls "10.32.0.0/16"
  vpc_cidr = local.network_registry.locals.cidr_map[local.cloud][local.env]
}

Step 3: Carve Subnets using cidrsubnet

Inside your Terraform module, use cidrsubnet to slice the vpc_cidr into smaller, usable blocks (e.g., /24 for Nodes).

# modules/gcp/vpc/main.tf
variable "vpc_cidr" {} # Inherits "10.32.0.0/16"

# GKE Pod Range (/24)
resource "google_compute_subnetwork" "prod_pods" {
  name          = "prod-pods-subnet"
  ip_cidr_range = cidrsubnet(var.vpc_cidr, 8, 0) # "10.32.0.0/24"
}

# GKE Node Range (/24)
resource "google_compute_subnetwork" "prod_nodes" {
  name          = "prod-nodes-subnet"
  ip_cidr_range = cidrsubnet(var.vpc_cidr, 8, 1) # "10.32.1.0/24"
}

How this prevents overlap: The cidrsubnet function guarantees uniqueness within the VPC. Because the parent vpc_cidr (e.g., 10.30.0.0/16) is globally unique (secured by Step 1), any subnet carved out of it using cidrsubnet is mathematically guaranteed to be unique across your entire Multi-Cloud platform.

6. Automated Validation

To ensure that no overlapping CIDR ranges are ever introduced, we automate the validation process. Every plan or apply command will automatically trigger our validation script before Terraform proceeds.

Implementation

Add the following configuration to your root.hcl:

# root.hcl
terraform {
  before_hook "validate_cidrs" {
    commands     = ["plan", "apply"]
    execute      = ["bash", "${get_repo_root()}/scripts/validate-cidrs.sh"]
  }
}

How it works:

  • Automatic Execution: Whenever you run terragrunt plan or terragrunt apply, Terragrunt executes the scripts/validate-cidrs.sh script first.
  • Failure on Overlap: If the script detects an overlapping range, it exits with a non-zero status, and Terragrunt will immediately stop execution.
  • CI/CD Safety: This acts as a final safety gate in your CI/CD pipeline, preventing any PR that introduces an overlapping CIDR from ever being applied to infrastructure.

7. Summary of Steps Taken

  1. Registry Creation: Initialized live/network_registry.hcl.
  2. Conflict Research: Identified and avoided cloud-provider default ranges.
  3. Registry Integration: Linked environment terragrunt.hcl files to the registry.
  4. Capacity Planning: Verified /16 environment blocks and /24 subnets.
  5. Documentation: Created this guide and updated the root README.md.
  6. Automated Validation: Implemented a pre-execution hook in root.hcl.