Files
hunfabric/modules/net-vpc/variables.tf
Ludovico Magnocavallo 85b0871085 Add net-vpc-factory and starter-gcd dataset to 0-org-setup, fix context in net-vpc-factory (#3860)
* feat(fast/0-org-setup): integrate net-vpc-factory into stage 0

* initial starter dataset

* starter pack

* fix(fast/0-org-setup): correct factory vpcs paths definition and update test inventory

* feat(fast/0-org-setup): add VPC and subnet outputs and update stage tfvars

* test(fast/0-org-setup): update gcd starter defaults and regenerate inventory

* fix(modules/net-vpc-factory): remove internal defaults logic and pass context correctly

* fix typo in dataset fw rule, add missing boilerplate, set sane defaults for net vpc factory

* terraform fmt

* tfdoc, copyright year

* schema docs

* yamllint
2026-04-13 09:42:03 +02:00

565 lines
18 KiB
HCL

/**
* 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
variable "auto_create_subnetworks" {
description = "Set to true to create an auto mode subnet, defaults to custom mode."
type = bool
default = false
}
variable "context" {
description = "Context-specific interpolations."
type = object({
addresses = optional(map(string), {})
cidr_ranges = optional(map(string), {})
condition_vars = optional(map(map(string)), {})
custom_roles = optional(map(string), {})
iam_principals = optional(map(string), {})
locations = optional(map(string), {})
networks = optional(map(string), {})
# legacy context
regions = optional(map(string), {})
project_ids = optional(map(string), {})
})
default = {}
nullable = false
validation {
condition = length(var.context.regions) == 0 || length(var.context.locations) == 0
error_message = "Only one of locations, regions can be used."
}
}
variable "create_googleapis_routes" {
description = "Toggle creation of googleapis private/restricted routes. Disabled when vpc creation is turned off, or when set to null."
type = object({
directpath = optional(bool, true)
directpath-6 = optional(bool, false)
private = optional(bool, true)
private-6 = optional(bool, false)
restricted = optional(bool, true)
restricted-6 = optional(bool, false)
})
default = {}
}
variable "delete_default_routes_on_create" {
description = "Set to true to delete the default routes at creation time."
type = bool
default = false
}
variable "description" {
description = "An optional description of this resource (triggers recreation on change)."
type = string
default = "Terraform-managed."
}
variable "dns_policy" {
description = "DNS policy setup for the VPC."
type = object({
inbound = optional(bool)
logging = optional(bool)
outbound = optional(object({
private_ns = list(string)
public_ns = list(string)
}))
})
default = null
}
variable "factories_config" {
description = "Paths to data files and folders that enable factory functionality."
type = object({
subnets_folder = optional(string)
internal_ranges_folder = optional(string)
})
default = {}
}
variable "firewall_policy_enforcement_order" {
description = "Order that Firewall Rules and Firewall Policies are evaluated. Can be either 'BEFORE_CLASSIC_FIREWALL' or 'AFTER_CLASSIC_FIREWALL'."
type = string
nullable = false
default = "AFTER_CLASSIC_FIREWALL"
validation {
condition = var.firewall_policy_enforcement_order == "BEFORE_CLASSIC_FIREWALL" || var.firewall_policy_enforcement_order == "AFTER_CLASSIC_FIREWALL"
error_message = "Enforcement order must be BEFORE_CLASSIC_FIREWALL or AFTER_CLASSIC_FIREWALL."
}
}
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({
enable_ula_internal = optional(bool)
internal_range = optional(string)
})
nullable = false
default = {}
}
variable "mtu" {
description = "Maximum Transmission Unit in bytes. The minimum value for this field is 1460 (the default) and the maximum value is 1500 bytes."
type = number
default = null
}
variable "name" {
description = "The name of the network being created."
type = string
}
variable "network_attachments" {
description = "PSC network attachments, names as keys."
type = map(object({
subnet = string
automatic_connection = optional(bool, false)
description = optional(string, "Terraform-managed.")
producer_accept_lists = optional(list(string))
producer_reject_lists = optional(list(string))
}))
nullable = false
default = {}
}
variable "peering_config" {
description = "VPC peering configuration."
type = object({
peer_vpc_self_link = string
create_remote_peer = optional(bool, true)
export_routes = optional(bool)
import_routes = optional(bool)
})
default = null
}
variable "policy_based_routes" {
description = "Policy based routes, keyed by name."
type = map(object({
description = optional(string, "Terraform-managed.")
labels = optional(map(string))
priority = optional(number)
next_hop_ilb_ip = optional(string)
use_default_routing = optional(bool, false)
filter = optional(object({
ip_protocol = optional(string)
dest_range = optional(string)
src_range = optional(string)
}), {})
target = optional(object({
interconnect_attachment = optional(string)
tags = optional(list(string))
}), {})
}))
default = {}
nullable = false
validation {
condition = alltrue([
for r in var.policy_based_routes :
contains(["TCP", "UDP", "ALL", null], r.filter.ip_protocol)
if r.filter.ip_protocol != null
])
error_message = "Unsupported protocol for route."
}
validation {
condition = alltrue([
for r in var.policy_based_routes :
(
(r.use_default_routing == true ? 1 : 0)
+
(r.next_hop_ilb_ip != null ? 1 : 0)
) == 1
])
error_message = "Either set `use_default_routing = true` or specify an internal passthrough LB IP."
}
validation {
condition = alltrue([
for r in var.policy_based_routes :
r.target.tags == null || r.target.interconnect_attachment == null
])
error_message = "Either use virtual machine tags or a vlan attachment region as a target."
}
}
variable "project_id" {
description = "The ID of the project where this VPC will be created."
type = string
}
variable "psa_configs" {
description = "The Private Service Access configuration."
type = list(object({
deletion_policy = optional(string, null)
ranges = map(string)
labels = optional(map(string), {})
export_routes = optional(bool, false)
import_routes = optional(bool, false)
peered_domains = optional(list(string), [])
range_prefix = optional(string)
service_producer = optional(string, "servicenetworking.googleapis.com")
}))
nullable = false
default = []
validation {
condition = (
length(var.psa_configs) == length(toset([
for v in var.psa_configs : v.service_producer
]))
)
error_message = "At most one configuration is possible for each service producer."
}
validation {
condition = alltrue([
for v in var.psa_configs : (
v.deletion_policy == null || v.deletion_policy == "ABANDON"
)
])
error_message = "Deletion policy supports only ABANDON."
}
}
variable "routes" {
description = "Network routes, keyed by name."
type = map(object({
description = optional(string, "Terraform-managed.")
dest_range = string
next_hop_type = string # gateway, instance, ip, vpn_tunnel, ilb
next_hop = string
priority = optional(number)
tags = optional(list(string))
}))
default = {}
nullable = false
validation {
condition = alltrue([
for r in var.routes :
contains(["gateway", "instance", "ip", "vpn_tunnel", "ilb"], r.next_hop_type)
])
error_message = "Unsupported next hop type for route."
}
}
variable "routing_mode" {
description = "The network routing mode (default 'GLOBAL')."
type = string
default = "GLOBAL"
validation {
condition = var.routing_mode == "GLOBAL" || var.routing_mode == "REGIONAL"
error_message = "Routing type must be GLOBAL or REGIONAL."
}
}
variable "service_connection_policies" {
description = "Service connection policies, keyed by name."
type = map(object({
location = string
service_class = string
description = optional(string)
labels = optional(map(string))
psc_config = object({
subnetworks = list(string)
limit = optional(number)
producer_instance_location = optional(string)
# maps to allowed_google_producers_resource_hierarchy_level
nodes = optional(list(string))
})
}))
nullable = false
default = {}
validation {
condition = alltrue([
for k, v in var.service_connection_policies :
v.psc_config.producer_instance_location == null || v.psc_config.producer_instance_location == "CUSTOM_RESOURCE_HIERARCHY_LEVELS"
])
error_message = "Producer instance location must be null or CUSTOM_RESOURCE_HIERARCHY_LEVELS."
}
validation {
condition = alltrue([
for k, v in var.service_connection_policies :
v.psc_config.nodes == null || v.psc_config.producer_instance_location == "CUSTOM_RESOURCE_HIERARCHY_LEVELS"
])
error_message = "Nodes can only be set if producer instance location is CUSTOM_RESOURCE_HIERARCHY_LEVELS."
}
validation {
condition = alltrue(flatten([
for k, v in var.service_connection_policies : [
for n in coalesce(v.psc_config.nodes, []) :
can(regex("^(projects|folders|organizations)/[a-zA-Z0-9-]+$", n))
]
]))
error_message = "Nodes must be in the format 'projects/id', 'folders/id', or 'organizations/id'."
}
}
variable "shared_vpc_host" {
description = "Enable shared VPC for this project."
type = bool
default = false
}
variable "shared_vpc_service_projects" {
description = "Shared VPC service projects to register with this host."
type = list(string)
default = []
}
variable "subnets" {
description = "Subnet configuration."
type = list(object({
name = 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)
flow_sampling = optional(number)
metadata = optional(string)
# only if metadata == "CUSTOM_METADATA"
metadata_fields = optional(list(string))
}))
ipv6 = optional(object({
access_type = optional(string, "INTERNAL")
# this field is marked for internal use in the API documentation
# enable_private_access = optional(string)
ipv6_only = optional(bool, false)
}))
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)
condition = optional(object({
expression = string
title = string
description = optional(string)
}))
})), {})
iam_bindings_additive = optional(map(object({
member = string
role = string
condition = optional(object({
expression = string
title = string
description = optional(string)
}))
})), {})
}))
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" {
description = "List of private NAT subnets."
type = list(object({
name = string
ip_cidr_range = string
region = string
description = optional(string)
}))
default = []
nullable = false
}
variable "subnets_proxy_only" {
description = "List of proxy-only subnets for Regional HTTPS or Internal HTTPS load balancers. Note: Only one proxy-only subnet for each VPC network in each region can be active."
type = list(object({
name = string
ip_cidr_range = string
region = string
description = optional(string)
active = optional(bool, true)
global = optional(bool, false)
iam = optional(map(list(string)), {})
iam_bindings = optional(map(object({
role = string
members = list(string)
condition = optional(object({
expression = string
title = string
description = optional(string)
}))
})), {})
iam_bindings_additive = optional(map(object({
member = string
role = string
condition = optional(object({
expression = string
title = string
description = optional(string)
}))
})), {})
}))
default = []
nullable = false
}
variable "subnets_psc" {
description = "List of subnets for Private Service Connect service producers."
type = list(object({
name = string
ip_cidr_range = string
region = string
description = optional(string)
flow_logs_config = optional(object({
aggregation_interval = optional(string)
filter_expression = optional(string)
flow_sampling = optional(number)
metadata = optional(string)
# only if metadata == "CUSTOM_METADATA"
metadata_fields = optional(list(string))
}))
iam = optional(map(list(string)), {})
iam_bindings = optional(map(object({
role = string
members = list(string)
condition = optional(object({
expression = string
title = string
description = optional(string)
}))
})), {})
iam_bindings_additive = optional(map(object({
member = string
role = string
condition = optional(object({
expression = string
title = string
description = optional(string)
}))
})), {})
}))
default = []
nullable = false
}
variable "vpc_reuse" {
description = "Reuse existing VPC if not null. If the network_id number is not passed in, a data source is used."
type = object({
use_data_source = optional(bool, true)
attributes = optional(object({
network_id = number
}))
})
default = null
validation {
condition = (
try(var.vpc_reuse.use_data_source, null) != false ||
try(var.vpc_reuse.attributes, null) != null
)
error_message = "Reuse datasource can be disabled only if attributes are set."
}
}