Copy provider "google" {
project = var.project_id
region = var.region
}
# Variables
variable "project_id" {
description = "Project ID for deployment"
type = string
}
variable "region" {
description = "Primary region for resources"
type = string
default = "us-central1"
}
variable "environment_prefixes" {
description = "Environment names for spoke networks"
type = list(string)
default = ["dev", "test", "prod"]
}
variable "on_prem_cidr" {
description = "On-premises CIDR range"
type = string
default = "192.168.0.0/16"
}
# Create a hub VPC
resource "google_compute_network" "hub_vpc" {
name = "hub-vpc"
auto_create_subnetworks = false
routing_mode = "GLOBAL"
description = "Hub VPC for centralized connectivity"
}
# Create subnets in the hub VPC
resource "google_compute_subnetwork" "hub_subnet" {
name = "hub-subnet"
ip_cidr_range = "10.0.0.0/24"
region = var.region
network = google_compute_network.hub_vpc.id
private_ip_google_access = true
log_config {
aggregation_interval = "INTERVAL_5_SEC"
flow_sampling = 0.5
metadata = "INCLUDE_ALL_METADATA"
}
}
resource "google_compute_subnetwork" "hub_subnet_2" {
name = "hub-subnet-2"
ip_cidr_range = "10.0.1.0/24"
region = "europe-west1"
network = google_compute_network.hub_vpc.id
private_ip_google_access = true
}
# Create spoke VPCs
resource "google_compute_network" "spoke_vpcs" {
for_each = toset(var.environment_prefixes)
name = "${each.value}-vpc"
auto_create_subnetworks = false
routing_mode = "GLOBAL"
description = "Spoke VPC for ${each.value} environment"
}
# Create subnets in the spoke VPCs
resource "google_compute_subnetwork" "spoke_subnets_primary" {
for_each = toset(var.environment_prefixes)
name = "${each.value}-primary-subnet"
ip_cidr_range = "10.${index(var.environment_prefixes, each.value) + 1}.0.0/24"
region = var.region
network = google_compute_network.spoke_vpcs[each.value].id
private_ip_google_access = true
}
resource "google_compute_subnetwork" "spoke_subnets_secondary" {
for_each = toset(var.environment_prefixes)
name = "${each.value}-secondary-subnet"
ip_cidr_range = "10.${index(var.environment_prefixes, each.value) + 1}.1.0/24"
region = "europe-west1"
network = google_compute_network.spoke_vpcs[each.value].id
private_ip_google_access = true
}
# Create VPC peering from spoke to hub
resource "google_compute_network_peering" "spoke_to_hub_peering" {
for_each = toset(var.environment_prefixes)
name = "${each.value}-to-hub-peering"
network = google_compute_network.spoke_vpcs[each.value].id
peer_network = google_compute_network.hub_vpc.id
export_custom_routes = true
import_custom_routes = true
}
# Create VPC peering from hub to spoke
resource "google_compute_network_peering" "hub_to_spoke_peering" {
for_each = toset(var.environment_prefixes)
name = "hub-to-${each.value}-peering"
network = google_compute_network.hub_vpc.id
peer_network = google_compute_network.spoke_vpcs[each.value].id
export_custom_routes = true
import_custom_routes = true
depends_on = [google_compute_network_peering.spoke_to_hub_peering]
}
# Create Cloud Router for VPN and NAT in hub VPC
resource "google_compute_router" "hub_router" {
name = "hub-router"
region = var.region
network = google_compute_network.hub_vpc.id
bgp {
asn = 64514
}
}
# Configure Cloud NAT for the hub VPC
resource "google_compute_router_nat" "hub_nat" {
name = "hub-nat"
router = google_compute_router.hub_router.name
region = google_compute_router.hub_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"
}
}
# Create Cloud Routers for NAT in spoke VPCs
resource "google_compute_router" "spoke_routers" {
for_each = toset(var.environment_prefixes)
name = "${each.value}-router"
region = var.region
network = google_compute_network.spoke_vpcs[each.value].id
bgp {
asn = 64515 + index(var.environment_prefixes, each.value)
}
}
# Configure Cloud NAT for spoke VPCs
resource "google_compute_router_nat" "spoke_nats" {
for_each = toset(var.environment_prefixes)
name = "${each.value}-nat"
router = google_compute_router.spoke_routers[each.value].name
region = google_compute_router.spoke_routers[each.value].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"
}
}
# Create VPN gateway in hub VPC for on-premises connectivity
resource "google_compute_vpn_gateway" "hub_vpn_gateway" {
name = "hub-vpn-gateway"
network = google_compute_network.hub_vpc.id
region = var.region
}
# Create external IP for VPN
resource "google_compute_address" "vpn_ip" {
name = "vpn-ip"
region = var.region
}
# Create a VPN tunnel to on-premises (Note: on-prem side configuration not shown)
resource "google_compute_vpn_tunnel" "hub_vpn_tunnel" {
name = "hub-to-onprem-tunnel"
region = var.region
vpn_gateway = google_compute_vpn_gateway.hub_vpn_gateway.id
peer_ip = "203.0.113.1" # Replace with actual on-prem VPN endpoint
shared_secret = "a-very-secure-secret"
target_vpn_gateway = google_compute_vpn_gateway.hub_vpn_gateway.id
depends_on = [
google_compute_address.vpn_ip
]
}
# Create routes for on-premises traffic
resource "google_compute_route" "route_to_onprem" {
name = "route-to-onprem"
dest_range = var.on_prem_cidr
network = google_compute_network.hub_vpc.id
next_hop_vpn_tunnel = google_compute_vpn_tunnel.hub_vpn_tunnel.id
priority = 1000
}
# Create firewall rules for hub VPC
resource "google_compute_firewall" "hub_allow_internal" {
name = "hub-allow-internal"
network = google_compute_network.hub_vpc.id
allow {
protocol = "icmp"
}
allow {
protocol = "tcp"
}
allow {
protocol = "udp"
}
source_ranges = ["10.0.0.0/16"]
}
resource "google_compute_firewall" "hub_allow_iap_ssh" {
name = "hub-allow-iap-ssh"
network = google_compute_network.hub_vpc.id
allow {
protocol = "tcp"
ports = ["22"]
}
# IAP IP ranges
source_ranges = ["35.235.240.0/20"]
}
resource "google_compute_firewall" "hub_allow_onprem" {
name = "hub-allow-onprem"
network = google_compute_network.hub_vpc.id
allow {
protocol = "icmp"
}
allow {
protocol = "tcp"
}
allow {
protocol = "udp"
}
source_ranges = [var.on_prem_cidr]
}
# Create firewall rules for spoke VPCs
resource "google_compute_firewall" "spoke_allow_internal" {
for_each = toset(var.environment_prefixes)
name = "${each.value}-allow-internal"
network = google_compute_network.spoke_vpcs[each.value].id
allow {
protocol = "icmp"
}
allow {
protocol = "tcp"
}
allow {
protocol = "udp"
}
source_ranges = [
"10.${index(var.environment_prefixes, each.value) + 1}.0.0/16",
"10.0.0.0/16" # Allow from hub
]
}
# Create Proxy-only subnet for internal load balancers in the hub
resource "google_compute_subnetwork" "proxy_only_subnet" {
name = "proxy-only-subnet"
ip_cidr_range = "10.0.10.0/24"
region = var.region
network = google_compute_network.hub_vpc.id
purpose = "REGIONAL_MANAGED_PROXY"
role = "ACTIVE"
}
# Create a bastion host in the hub VPC
resource "google_compute_instance" "hub_bastion" {
name = "hub-bastion"
machine_type = "e2-micro"
zone = "${var.region}-a"
boot_disk {
initialize_params {
image = "debian-cloud/debian-11"
size = 10
}
}
network_interface {
network = google_compute_network.hub_vpc.id
subnetwork = google_compute_subnetwork.hub_subnet.id
# No external IP - will use IAP for SSH
}
tags = ["bastion", "ssh"]
metadata = {
enable-oslogin = "true"
}
service_account {
scopes = ["cloud-platform"]
}
}
# Create sample workload VMs in each spoke network
resource "google_compute_instance" "spoke_vms" {
for_each = toset(var.environment_prefixes)
name = "${each.value}-vm"
machine_type = "e2-micro"
zone = "${var.region}-a"
boot_disk {
initialize_params {
image = "debian-cloud/debian-11"
size = 10
}
}
network_interface {
network = google_compute_network.spoke_vpcs[each.value].id
subnetwork = google_compute_subnetwork.spoke_subnets_primary[each.value].id
# No external IP
}
tags = ["${each.value}-vm"]
metadata = {
enable-oslogin = "true"
}
service_account {
scopes = ["cloud-platform"]
}
}
# Outputs
output "hub_vpc_name" {
value = google_compute_network.hub_vpc.name
}
output "spoke_vpc_names" {
value = { for prefix in var.environment_prefixes : prefix => google_compute_network.spoke_vpcs[prefix].name }
}
output "hub_subnet_ranges" {
value = {
primary = google_compute_subnetwork.hub_subnet.ip_cidr_range
secondary = google_compute_subnetwork.hub_subnet_2.ip_cidr_range
}
}
output "spoke_subnet_ranges" {
value = { for prefix in var.environment_prefixes : prefix => {
primary = google_compute_subnetwork.spoke_subnets_primary[prefix].ip_cidr_range
secondary = google_compute_subnetwork.spoke_subnets_secondary[prefix].ip_cidr_range
}}
}
output "vpn_gateway_ip" {
value = google_compute_address.vpn_ip.address
}
output "bastion_name" {
value = google_compute_instance.hub_bastion.name
}
output "iap_ssh_command" {
value = "gcloud compute ssh ${google_compute_instance.hub_bastion.name} --zone=${google_compute_instance.hub_bastion.zone} --tunnel-through-iap"
}