Add support for VPC internal ranges to modules/net-vpc (#3318)

* Add support for VPC internal ranges to modules/net-vpc

* Fix linting

* Fix variable order

* Fix README

* Sort outputs.

* Fix validation for terraform < 1.13
This commit is contained in:
Julio Castillo
2025-09-11 19:42:54 +02:00
committed by GitHub
parent 1dee8c8682
commit ea445fa7e4
17 changed files with 815 additions and 90 deletions

View File

@@ -1,5 +1,5 @@
/**
* Copyright 2024 Google LLC
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -70,7 +70,8 @@ variable "dns_policy" {
variable "factories_config" {
description = "Paths to data files and folders that enable factory functionality."
type = object({
subnets_folder = optional(string)
subnets_folder = optional(string)
internal_ranges_folder = optional(string)
})
default = {}
}
@@ -87,6 +88,70 @@ variable "firewall_policy_enforcement_order" {
}
}
variable "internal_ranges" {
description = "Internal range configuration for IPAM operations within the VPC network."
type = list(object({
name = string
description = optional(string)
ip_cidr_range = optional(string)
labels = optional(map(string), {})
usage = string
peering = string
prefix_length = optional(number)
target_cidr_range = optional(list(string))
exclude_cidr_ranges = optional(list(string))
overlaps = optional(list(string))
immutable = optional(bool)
allocation_options = optional(object({
allocation_strategy = optional(string)
first_available_ranges_lookup_size = optional(number)
}))
migration = optional(object({
source = string
target = string
}))
}))
default = []
nullable = false
validation {
condition = alltrue([
for r in var.internal_ranges :
contains(["FOR_VPC", "EXTERNAL_TO_VPC", "FOR_MIGRATION"], r.usage)
])
error_message = "Usage must be one of: FOR_VPC, EXTERNAL_TO_VPC, FOR_MIGRATION."
}
validation {
condition = alltrue([
for r in var.internal_ranges :
contains(["FOR_SELF", "FOR_PEER", "NOT_SHARED"], r.peering)
])
error_message = "Peering must be one of: FOR_SELF, FOR_PEER, NOT_SHARED."
}
validation {
condition = alltrue([
for r in var.internal_ranges : (
r.allocation_options == null ||
try(r.allocation_options.allocation_strategy, null) == null ||
contains(
["RANDOM", "FIRST_AVAILABLE", "RANDOM_FIRST_N_AVAILABLE", "FIRST_SMALLEST_FITTING"],
try(r.allocation_options.allocation_strategy, "")
)
)
])
error_message = "Allocation strategy must be one of: RANDOM, FIRST_AVAILABLE, RANDOM_FIRST_N_AVAILABLE, FIRST_SMALLEST_FITTING."
}
validation {
condition = alltrue([
for r in var.internal_ranges :
r.overlaps == null || alltrue([
for overlap in coalesce(r.overlaps, []) :
contains(["OVERLAP_ROUTE_RANGE", "OVERLAP_EXISTING_SUBNET_RANGE"], overlap)
])
])
error_message = "Overlaps must contain only: OVERLAP_ROUTE_RANGE, OVERLAP_EXISTING_SUBNET_RANGE."
}
}
variable "ipv6_config" {
description = "Optional IPv6 configuration for this network."
type = object({
@@ -263,11 +328,12 @@ variable "subnets" {
description = "Subnet configuration."
type = list(object({
name = string
ip_cidr_range = string
ip_cidr_range = optional(string)
region = string
description = optional(string)
enable_private_access = optional(bool, true)
allow_subnet_cidr_routes_overlap = optional(bool, null)
reserved_internal_range = optional(string)
flow_logs_config = optional(object({
aggregation_interval = optional(string)
filter_expression = optional(string)
@@ -282,9 +348,12 @@ variable "subnets" {
# enable_private_access = optional(string)
ipv6_only = optional(bool, false)
}))
ip_collection = optional(string, null)
secondary_ip_ranges = optional(map(string))
iam = optional(map(list(string)), {})
ip_collection = optional(string, null)
secondary_ip_ranges = optional(map(object({
ip_cidr_range = optional(string)
reserved_internal_range = optional(string)
})))
iam = optional(map(list(string)), {})
iam_bindings = optional(map(object({
role = string
members = list(string)
@@ -306,6 +375,33 @@ variable "subnets" {
}))
default = []
nullable = false
validation {
condition = alltrue([
for s in var.subnets :
(
length([
for field in [s.ip_cidr_range, s.reserved_internal_range, s.ip_collection] :
field if field != null
]) == 1
) || (
length([
for field in [s.ip_cidr_range, s.reserved_internal_range, s.ip_collection] :
field if field != null
]) == 0 && try(s.ipv6.ipv6_only, false) == true
)
])
error_message = "Each subnet must specify exactly one of ip_cidr_range, reserved_internal_range, or ip_collection, or all three can be null for IPv6-only subnets (ipv6.ipv6_only = true)."
}
validation {
condition = alltrue([
for s in var.subnets :
s.secondary_ip_ranges == null || alltrue([
for range_name, range_config in coalesce(s.secondary_ip_ranges, {}) :
(range_config.ip_cidr_range != null) != (range_config.reserved_internal_range != null)
])
])
error_message = "Each secondary IP range must specify either ip_cidr_range or reserved_internal_range, but not both."
}
}
variable "subnets_private_nat" {