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

@@ -7,5 +7,7 @@ region: $regions:primary
description: Default subnet for prod gke nodes
ip_cidr_range: 10.68.1.0/24
secondary_ip_ranges:
pods: 100.68.0.0/16
services: 100.71.1.0/24
pods:
ip_cidr_range: 100.68.0.0/16
services:
ip_cidr_range: 100.71.1.0/24

View File

@@ -7,5 +7,7 @@ region: $regions:primary
description: Default subnet for dev Data Platform
ip_cidr_range: 10.68.2.0/24
secondary_ip_ranges:
pods: 100.69.0.0/16
services: 100.71.2.0/24
pods:
ip_cidr_range: 100.69.0.0/16
services:
ip_cidr_range: 100.71.2.0/24

View File

@@ -7,5 +7,7 @@ region: $regions:primary
description: Default subnet for prod gke nodes
ip_cidr_range: 10.68.1.0/24
secondary_ip_ranges:
pods: 100.68.0.0/16
services: 100.71.1.0/24
pods:
ip_cidr_range: 100.68.0.0/16
services:
ip_cidr_range: 100.71.1.0/24

View File

@@ -7,5 +7,7 @@ region: $regions:primary
description: Default subnet for dev Data Platform
ip_cidr_range: 10.68.2.0/24
secondary_ip_ranges:
pods: 100.69.0.0/16
services: 100.71.2.0/24
pods:
ip_cidr_range: 100.69.0.0/16
services:
ip_cidr_range: 100.71.2.0/24

View File

@@ -7,5 +7,7 @@ region: $regions:primary
description: Default subnet for prod gke nodes
ip_cidr_range: 10.68.1.0/24
secondary_ip_ranges:
pods: 100.68.0.0/16
services: 100.71.1.0/24
pods:
ip_cidr_range: 100.68.0.0/16
services:
ip_cidr_range: 100.71.1.0/24

View File

@@ -39,8 +39,8 @@ module "vpc" {
name = "cluster-1"
region = "europe-west1"
secondary_ip_range = {
pods = "10.1.0.0/16"
services = "10.2.0.0/24"
pods = { ip_cidr_range = "10.1.0.0/16" }
services = { ip_cidr_range = "10.2.0.0/24" }
}
}]
}
@@ -142,8 +142,8 @@ module "vpc" {
name = "subnet-cluster-1"
region = "europe-west1"
secondary_ip_ranges = {
pods = "10.1.0.0/16"
services = "10.2.0.0/24"
pods = { ip_cidr_range = "10.1.0.0/16" }
services = { ip_cidr_range = "10.2.0.0/24" }
}
},
{
@@ -151,8 +151,8 @@ module "vpc" {
name = "subnet-cluster-2"
region = "europe-west4"
secondary_ip_ranges = {
pods = "10.3.0.0/16"
services = "10.4.0.0/24"
pods = { ip_cidr_range = "10.3.0.0/16" }
services = { ip_cidr_range = "10.4.0.0/24" }
}
},
{

View File

@@ -24,6 +24,10 @@ This module allows creation and management of VPC networks including subnetworks
- [Allow Firewall Policy to be evaluated before Firewall Rules](#allow-firewall-policy-to-be-evaluated-before-firewall-rules)
- [IPv6](#ipv6)
- [IPv6-Only and IP Collections](#ipv6-only-and-ip-collections)
- [Internal Ranges](#internal-ranges)
- [Basic Internal Range Configuration](#basic-internal-range-configuration)
- [Subnets with Internal Ranges](#subnets-with-internal-ranges)
- [Internal Range Factory](#internal-range-factory)
- [Variables](#variables)
- [Outputs](#outputs)
<!-- END TOC -->
@@ -41,8 +45,8 @@ module "vpc" {
name = "production"
region = "europe-west1"
secondary_ip_ranges = {
pods = "172.16.0.0/20"
services = "192.168.0.0/24"
pods = { ip_cidr_range = "172.16.0.0/20" }
services = { ip_cidr_range = "192.168.0.0/24" }
}
},
{
@@ -83,8 +87,8 @@ module "vpc" {
region = "europe-west1"
ip_cidr_range = "10.0.2.0/24"
secondary_ip_ranges = {
a = "192.168.0.0/24"
b = "192.168.1.0/24"
a = { ip_cidr_range = "192.168.0.0/24" }
b = { ip_cidr_range = "192.168.1.0/24" }
}
},
# enable flow logs
@@ -221,8 +225,8 @@ module "vpc-host" {
name = "subnet-1"
region = "europe-west1"
secondary_ip_ranges = {
pods = "172.16.0.0/20"
services = "192.168.0.0/24"
pods = { ip_cidr_range = "172.16.0.0/20" }
services = { ip_cidr_range = "192.168.0.0/24" }
}
iam = {
"roles/compute.networkUser" = [
@@ -522,7 +526,8 @@ iam:
- serviceAccount:fbz@prj.iam.gserviceaccount.com
- user:foobar@example.com
secondary_ip_ranges: # map of secondary ip ranges
secondary-range-a: 192.168.0.0/24
secondary-range-a:
ip_cidr_range: 192.168.0.0/24
flow_logs_config: # enable, set to empty map to use defaults
aggregation_interval: "INTERVAL_5_SEC"
flow_sampling: 0.5
@@ -666,8 +671,8 @@ module "vpc" {
name = "production"
region = "europe-west1"
secondary_ip_ranges = {
pods = "172.16.0.0/20"
services = "192.168.0.0/24"
pods = { ip_cidr_range = "172.16.0.0/20" }
services = { ip_cidr_range = "192.168.0.0/24" }
}
},
{
@@ -716,12 +721,7 @@ module "vpc" {
### IPv6-Only and IP Collections
An IPv6-only subnetwork can be specified by setting `ipv6_only` to `true` and
setting `ip_cidr_range` to `null`. An IP Collection may be specified with
`ip_collection` and a
[reference](https://cloud.google.com/compute/docs/reference/rest/v1/subnetworks/insert)
to a collection source, like a PublicDelegatedPrefix (PDP) for BYOIPv6. The PDP
must be a sub-PDP in `EXTERNAL_IPV6_SUBNETWORK_CREATION` mode.
An IPv6-only subnetwork can be specified by setting `ipv6_only` to `true` and setting `ip_cidr_range` to `null`. An IP Collection may be specified with `ip_collection` and a [reference](https://cloud.google.com/compute/docs/reference/rest/v1/subnetworks/insert) to a collection source, like a PublicDelegatedPrefix (PDP) for BYOIPv6. The PDP must be a sub-PDP in `EXTERNAL_IPV6_SUBNETWORK_CREATION` mode.
```hcl
module "vpc" {
@@ -754,36 +754,177 @@ module "vpc" {
}
# tftest modules=1 resources=6 inventory=ipv6_only.yaml
```
### Internal Ranges
Google Cloud [Internal Ranges](https://cloud.google.com/vpc/docs/create-use-internal-ranges) provide advanced IPAM (IP Address Management) capabilities for VPC networks. Internal ranges represent private address ranges with specific behavioral characteristics such as usage and peering behavior. The module supports creating internal ranges directly or through factory configurations, and integrating them with subnet creation.
#### Basic Internal Range Configuration
```hcl
module "vpc" {
source = "./fabric/modules/net-vpc"
project_id = var.project_id
name = "my-network"
internal_ranges = [
{
name = "range1"
usage = "FOR_VPC"
peering = "FOR_SELF"
ip_cidr_range = "10.0.0.0/16"
},
{
name = "range2"
usage = "FOR_VPC"
peering = "FOR_SELF"
prefix_length = 24
target_cidr_range = ["10.1.0.0/16"]
description = "Auto-allocated secondary range"
}
]
}
# tftest inventory=internal-ranges.yaml
```
#### Subnets with Internal Ranges
Subnets can reference internal ranges instead of specifying explicit CIDR ranges, enabling centralized IP management:
```hcl
module "vpc" {
source = "./fabric/modules/net-vpc"
project_id = var.project_id
name = "my-network"
internal_ranges = [
{
name = "subnet-range"
usage = "FOR_VPC"
peering = "FOR_SELF"
ip_cidr_range = "10.0.1.0/24"
},
{
name = "pods-range"
usage = "FOR_VPC"
peering = "FOR_SELF"
ip_cidr_range = "10.1.0.0/16"
},
{
name = "services-range"
usage = "FOR_VPC"
peering = "FOR_SELF"
prefix_length = 20
target_cidr_range = ["10.2.0.0/16"]
}
]
subnets = [
{
name = "production"
region = "europe-west1"
reserved_internal_range = "subnet-range"
secondary_ip_ranges = {
pods = {
reserved_internal_range = "pods-range"
}
services = {
reserved_internal_range = "services-range"
}
# Mixed configuration: some ranges use internal ranges, others use CIDR
traditional = {
ip_cidr_range = "192.168.0.0/24"
}
}
}
]
}
# tftest inventory=subnets-internal-ranges.yaml
```
#### Internal Range Factory
Internal ranges can be defined using YAML factory files, similar to the subnet factory:
```hcl
module "vpc" {
source = "./fabric/modules/net-vpc"
project_id = var.project_id
name = "my-network"
factories_config = {
internal_ranges_folder = "config/internal-ranges"
subnets_folder = "config/subnets"
}
}
# tftest files=subnet,subnet-range,pods-range,services-range inventory=subnets-internal-ranges.yaml
```
```yaml
usage: FOR_VPC
peering: FOR_SELF
ip_cidr_range: "10.0.1.0/24"
# tftest-file id=subnet-range path=config/internal-ranges/subnet-range.yaml schema=internal-range.schema.json
```
```yaml
usage: FOR_VPC
peering: FOR_SELF
ip_cidr_range: "10.1.0.0/16"
# tftest-file id=pods-range path=config/internal-ranges/pods-range.yaml schema=internal-range.schema.json
```
```yaml
usage: FOR_VPC
peering: FOR_SELF
prefix_length: 20
target_cidr_range:
- "10.2.0.0/16"
# tftest-file id=services-range path=config/internal-ranges/services-range.yaml schema=internal-range.schema.json
```
```yaml
region: europe-west1
reserved_internal_range: subnet-range
secondary_ip_ranges:
pods:
reserved_internal_range: pods-range
services:
reserved_internal_range: services-range
traditional:
ip_cidr_range: "192.168.0.0/24"
# tftest-file id=subnet path=config/subnets/production.yaml schema=subnet.schema.json
```
<!-- BEGIN TFDOC -->
## Variables
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [name](variables.tf#L106) | The name of the network being created. | <code>string</code> | ✓ | |
| [project_id](variables.tf#L183) | The ID of the project where this VPC will be created. | <code>string</code> | ✓ | |
| [name](variables.tf#L171) | The name of the network being created. | <code>string</code> | ✓ | |
| [project_id](variables.tf#L248) | The ID of the project where this VPC will be created. | <code>string</code> | ✓ | |
| [auto_create_subnetworks](variables.tf#L17) | Set to true to create an auto mode subnet, defaults to custom mode. | <code>bool</code> | | <code>false</code> |
| [context](variables.tf#L23) | Context-specific interpolations. | <code title="object&#40;&#123;&#10; regions &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [create_googleapis_routes](variables.tf#L32) | Toggle creation of googleapis private/restricted routes. Disabled when vpc creation is turned off, or when set to null. | <code title="object&#40;&#123;&#10; directpath &#61; optional&#40;bool, true&#41;&#10; directpath-6 &#61; optional&#40;bool, false&#41;&#10; private &#61; optional&#40;bool, true&#41;&#10; private-6 &#61; optional&#40;bool, false&#41;&#10; restricted &#61; optional&#40;bool, true&#41;&#10; restricted-6 &#61; optional&#40;bool, false&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [delete_default_routes_on_create](variables.tf#L45) | Set to true to delete the default routes at creation time. | <code>bool</code> | | <code>false</code> |
| [description](variables.tf#L51) | An optional description of this resource (triggers recreation on change). | <code>string</code> | | <code>&#34;Terraform-managed.&#34;</code> |
| [dns_policy](variables.tf#L57) | DNS policy setup for the VPC. | <code title="object&#40;&#123;&#10; inbound &#61; optional&#40;bool&#41;&#10; logging &#61; optional&#40;bool&#41;&#10; outbound &#61; optional&#40;object&#40;&#123;&#10; private_ns &#61; list&#40;string&#41;&#10; public_ns &#61; list&#40;string&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [factories_config](variables.tf#L70) | Paths to data files and folders that enable factory functionality. | <code title="object&#40;&#123;&#10; subnets_folder &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [firewall_policy_enforcement_order](variables.tf#L78) | Order that Firewall Rules and Firewall Policies are evaluated. Can be either 'BEFORE_CLASSIC_FIREWALL' or 'AFTER_CLASSIC_FIREWALL'. | <code>string</code> | | <code>&#34;AFTER_CLASSIC_FIREWALL&#34;</code> |
| [ipv6_config](variables.tf#L90) | Optional IPv6 configuration for this network. | <code title="object&#40;&#123;&#10; enable_ula_internal &#61; optional&#40;bool&#41;&#10; internal_range &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [mtu](variables.tf#L100) | Maximum Transmission Unit in bytes. The minimum value for this field is 1460 (the default) and the maximum value is 1500 bytes. | <code>number</code> | | <code>null</code> |
| [network_attachments](variables.tf#L111) | PSC network attachments, names as keys. | <code title="map&#40;object&#40;&#123;&#10; subnet &#61; string&#10; automatic_connection &#61; optional&#40;bool, false&#41;&#10; description &#61; optional&#40;string, &#34;Terraform-managed.&#34;&#41;&#10; producer_accept_lists &#61; optional&#40;list&#40;string&#41;&#41;&#10; producer_reject_lists &#61; optional&#40;list&#40;string&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [peering_config](variables.tf#L124) | VPC peering configuration. | <code title="object&#40;&#123;&#10; peer_vpc_self_link &#61; string&#10; create_remote_peer &#61; optional&#40;bool, true&#41;&#10; export_routes &#61; optional&#40;bool&#41;&#10; import_routes &#61; optional&#40;bool&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [policy_based_routes](variables.tf#L135) | Policy based routes, keyed by name. | <code title="map&#40;object&#40;&#123;&#10; description &#61; optional&#40;string, &#34;Terraform-managed.&#34;&#41;&#10; labels &#61; optional&#40;map&#40;string&#41;&#41;&#10; priority &#61; optional&#40;number&#41;&#10; next_hop_ilb_ip &#61; optional&#40;string&#41;&#10; use_default_routing &#61; optional&#40;bool, false&#41;&#10; filter &#61; optional&#40;object&#40;&#123;&#10; ip_protocol &#61; optional&#40;string&#41;&#10; dest_range &#61; optional&#40;string&#41;&#10; src_range &#61; optional&#40;string&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; target &#61; optional&#40;object&#40;&#123;&#10; interconnect_attachment &#61; optional&#40;string&#41;&#10; tags &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [psa_configs](variables.tf#L188) | The Private Service Access configuration. | <code title="list&#40;object&#40;&#123;&#10; deletion_policy &#61; optional&#40;string, null&#41;&#10; ranges &#61; map&#40;string&#41;&#10; export_routes &#61; optional&#40;bool, false&#41;&#10; import_routes &#61; optional&#40;bool, false&#41;&#10; peered_domains &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; range_prefix &#61; optional&#40;string&#41;&#10; service_producer &#61; optional&#40;string, &#34;servicenetworking.googleapis.com&#34;&#41;&#10;&#125;&#41;&#41;">list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#91;&#93;</code> |
| [routes](variables.tf#L219) | Network routes, keyed by name. | <code title="map&#40;object&#40;&#123;&#10; description &#61; optional&#40;string, &#34;Terraform-managed.&#34;&#41;&#10; dest_range &#61; string&#10; next_hop_type &#61; string &#35; gateway, instance, ip, vpn_tunnel, ilb&#10; next_hop &#61; string&#10; priority &#61; optional&#40;number&#41;&#10; tags &#61; optional&#40;list&#40;string&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [routing_mode](variables.tf#L240) | The network routing mode (default 'GLOBAL'). | <code>string</code> | | <code>&#34;GLOBAL&#34;</code> |
| [shared_vpc_host](variables.tf#L250) | Enable shared VPC for this project. | <code>bool</code> | | <code>false</code> |
| [shared_vpc_service_projects](variables.tf#L256) | Shared VPC service projects to register with this host. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
| [subnets](variables.tf#L262) | Subnet configuration. | <code title="list&#40;object&#40;&#123;&#10; name &#61; string&#10; ip_cidr_range &#61; string&#10; region &#61; string&#10; description &#61; optional&#40;string&#41;&#10; enable_private_access &#61; optional&#40;bool, true&#41;&#10; allow_subnet_cidr_routes_overlap &#61; optional&#40;bool, null&#41;&#10; flow_logs_config &#61; optional&#40;object&#40;&#123;&#10; aggregation_interval &#61; optional&#40;string&#41;&#10; filter_expression &#61; optional&#40;string&#41;&#10; flow_sampling &#61; optional&#40;number&#41;&#10; metadata &#61; optional&#40;string&#41;&#10; metadata_fields &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; ipv6 &#61; optional&#40;object&#40;&#123;&#10; access_type &#61; optional&#40;string, &#34;INTERNAL&#34;&#41;&#10; ipv6_only &#61; optional&#40;bool, false&#41;&#10; &#125;&#41;&#41;&#10; ip_collection &#61; optional&#40;string, null&#41;&#10; secondary_ip_ranges &#61; optional&#40;map&#40;string&#41;&#41;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; iam_bindings &#61; optional&#40;map&#40;object&#40;&#123;&#10; role &#61; string&#10; members &#61; list&#40;string&#41;&#10; condition &#61; optional&#40;object&#40;&#123;&#10; expression &#61; string&#10; title &#61; string&#10; description &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10; iam_bindings_additive &#61; optional&#40;map&#40;object&#40;&#123;&#10; member &#61; string&#10; role &#61; string&#10; condition &#61; optional&#40;object&#40;&#123;&#10; expression &#61; string&#10; title &#61; string&#10; description &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;&#41;">list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#91;&#93;</code> |
| [subnets_private_nat](variables.tf#L311) | List of private NAT subnets. | <code title="list&#40;object&#40;&#123;&#10; name &#61; string&#10; ip_cidr_range &#61; string&#10; region &#61; string&#10; description &#61; optional&#40;string&#41;&#10;&#125;&#41;&#41;">list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#91;&#93;</code> |
| [subnets_proxy_only](variables.tf#L323) | 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. | <code title="list&#40;object&#40;&#123;&#10; name &#61; string&#10; ip_cidr_range &#61; string&#10; region &#61; string&#10; description &#61; optional&#40;string&#41;&#10; active &#61; optional&#40;bool, true&#41;&#10; global &#61; optional&#40;bool, false&#41;&#10;&#10;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; iam_bindings &#61; optional&#40;map&#40;object&#40;&#123;&#10; role &#61; string&#10; members &#61; list&#40;string&#41;&#10; condition &#61; optional&#40;object&#40;&#123;&#10; expression &#61; string&#10; title &#61; string&#10; description &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10; iam_bindings_additive &#61; optional&#40;map&#40;object&#40;&#123;&#10; member &#61; string&#10; role &#61; string&#10; condition &#61; optional&#40;object&#40;&#123;&#10; expression &#61; string&#10; title &#61; string&#10; description &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;&#41;">list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#91;&#93;</code> |
| [subnets_psc](variables.tf#L357) | List of subnets for Private Service Connect service producers. | <code title="list&#40;object&#40;&#123;&#10; name &#61; string&#10; ip_cidr_range &#61; string&#10; region &#61; string&#10; description &#61; optional&#40;string&#41;&#10;&#10;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; iam_bindings &#61; optional&#40;map&#40;object&#40;&#123;&#10; role &#61; string&#10; members &#61; list&#40;string&#41;&#10; condition &#61; optional&#40;object&#40;&#123;&#10; expression &#61; string&#10; title &#61; string&#10; description &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10; iam_bindings_additive &#61; optional&#40;map&#40;object&#40;&#123;&#10; member &#61; string&#10; role &#61; string&#10; condition &#61; optional&#40;object&#40;&#123;&#10; expression &#61; string&#10; title &#61; string&#10; description &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;&#41;">list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#91;&#93;</code> |
| [vpc_reuse](variables.tf#L389) | Reuse existing VPC if not null. If the network_id number is not passed in, a data source is used. | <code title="object&#40;&#123;&#10; use_data_source &#61; optional&#40;bool, true&#41;&#10; attributes &#61; optional&#40;object&#40;&#123;&#10; network_id &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [factories_config](variables.tf#L70) | Paths to data files and folders that enable factory functionality. | <code title="object&#40;&#123;&#10; subnets_folder &#61; optional&#40;string&#41;&#10; internal_ranges_folder &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [firewall_policy_enforcement_order](variables.tf#L79) | Order that Firewall Rules and Firewall Policies are evaluated. Can be either 'BEFORE_CLASSIC_FIREWALL' or 'AFTER_CLASSIC_FIREWALL'. | <code>string</code> | | <code>&#34;AFTER_CLASSIC_FIREWALL&#34;</code> |
| [internal_ranges](variables.tf#L91) | Internal range configuration for IPAM operations within the VPC network. | <code title="list&#40;object&#40;&#123;&#10; name &#61; string&#10; description &#61; optional&#40;string&#41;&#10; ip_cidr_range &#61; optional&#40;string&#41;&#10; labels &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; usage &#61; string&#10; peering &#61; string&#10; prefix_length &#61; optional&#40;number&#41;&#10; target_cidr_range &#61; optional&#40;list&#40;string&#41;&#41;&#10; exclude_cidr_ranges &#61; optional&#40;list&#40;string&#41;&#41;&#10; overlaps &#61; optional&#40;list&#40;string&#41;&#41;&#10; immutable &#61; optional&#40;bool&#41;&#10; allocation_options &#61; optional&#40;object&#40;&#123;&#10; allocation_strategy &#61; optional&#40;string&#41;&#10; first_available_ranges_lookup_size &#61; optional&#40;number&#41;&#10; &#125;&#41;&#41;&#10; migration &#61; optional&#40;object&#40;&#123;&#10; source &#61; string&#10; target &#61; string&#10; &#125;&#41;&#41;&#10;&#125;&#41;&#41;">list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#91;&#93;</code> |
| [ipv6_config](variables.tf#L155) | Optional IPv6 configuration for this network. | <code title="object&#40;&#123;&#10; enable_ula_internal &#61; optional&#40;bool&#41;&#10; internal_range &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [mtu](variables.tf#L165) | Maximum Transmission Unit in bytes. The minimum value for this field is 1460 (the default) and the maximum value is 1500 bytes. | <code>number</code> | | <code>null</code> |
| [network_attachments](variables.tf#L176) | PSC network attachments, names as keys. | <code title="map&#40;object&#40;&#123;&#10; subnet &#61; string&#10; automatic_connection &#61; optional&#40;bool, false&#41;&#10; description &#61; optional&#40;string, &#34;Terraform-managed.&#34;&#41;&#10; producer_accept_lists &#61; optional&#40;list&#40;string&#41;&#41;&#10; producer_reject_lists &#61; optional&#40;list&#40;string&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [peering_config](variables.tf#L189) | VPC peering configuration. | <code title="object&#40;&#123;&#10; peer_vpc_self_link &#61; string&#10; create_remote_peer &#61; optional&#40;bool, true&#41;&#10; export_routes &#61; optional&#40;bool&#41;&#10; import_routes &#61; optional&#40;bool&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [policy_based_routes](variables.tf#L200) | Policy based routes, keyed by name. | <code title="map&#40;object&#40;&#123;&#10; description &#61; optional&#40;string, &#34;Terraform-managed.&#34;&#41;&#10; labels &#61; optional&#40;map&#40;string&#41;&#41;&#10; priority &#61; optional&#40;number&#41;&#10; next_hop_ilb_ip &#61; optional&#40;string&#41;&#10; use_default_routing &#61; optional&#40;bool, false&#41;&#10; filter &#61; optional&#40;object&#40;&#123;&#10; ip_protocol &#61; optional&#40;string&#41;&#10; dest_range &#61; optional&#40;string&#41;&#10; src_range &#61; optional&#40;string&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; target &#61; optional&#40;object&#40;&#123;&#10; interconnect_attachment &#61; optional&#40;string&#41;&#10; tags &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [psa_configs](variables.tf#L253) | The Private Service Access configuration. | <code title="list&#40;object&#40;&#123;&#10; deletion_policy &#61; optional&#40;string, null&#41;&#10; ranges &#61; map&#40;string&#41;&#10; export_routes &#61; optional&#40;bool, false&#41;&#10; import_routes &#61; optional&#40;bool, false&#41;&#10; peered_domains &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10; range_prefix &#61; optional&#40;string&#41;&#10; service_producer &#61; optional&#40;string, &#34;servicenetworking.googleapis.com&#34;&#41;&#10;&#125;&#41;&#41;">list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#91;&#93;</code> |
| [routes](variables.tf#L284) | Network routes, keyed by name. | <code title="map&#40;object&#40;&#123;&#10; description &#61; optional&#40;string, &#34;Terraform-managed.&#34;&#41;&#10; dest_range &#61; string&#10; next_hop_type &#61; string &#35; gateway, instance, ip, vpn_tunnel, ilb&#10; next_hop &#61; string&#10; priority &#61; optional&#40;number&#41;&#10; tags &#61; optional&#40;list&#40;string&#41;&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [routing_mode](variables.tf#L305) | The network routing mode (default 'GLOBAL'). | <code>string</code> | | <code>&#34;GLOBAL&#34;</code> |
| [shared_vpc_host](variables.tf#L315) | Enable shared VPC for this project. | <code>bool</code> | | <code>false</code> |
| [shared_vpc_service_projects](variables.tf#L321) | Shared VPC service projects to register with this host. | <code>list&#40;string&#41;</code> | | <code>&#91;&#93;</code> |
| [subnets](variables.tf#L327) | Subnet configuration. | <code title="list&#40;object&#40;&#123;&#10; name &#61; string&#10; ip_cidr_range &#61; optional&#40;string&#41;&#10; region &#61; string&#10; description &#61; optional&#40;string&#41;&#10; enable_private_access &#61; optional&#40;bool, true&#41;&#10; allow_subnet_cidr_routes_overlap &#61; optional&#40;bool, null&#41;&#10; reserved_internal_range &#61; optional&#40;string&#41;&#10; flow_logs_config &#61; optional&#40;object&#40;&#123;&#10; aggregation_interval &#61; optional&#40;string&#41;&#10; filter_expression &#61; optional&#40;string&#41;&#10; flow_sampling &#61; optional&#40;number&#41;&#10; metadata &#61; optional&#40;string&#41;&#10; metadata_fields &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; ipv6 &#61; optional&#40;object&#40;&#123;&#10; access_type &#61; optional&#40;string, &#34;INTERNAL&#34;&#41;&#10; ipv6_only &#61; optional&#40;bool, false&#41;&#10; &#125;&#41;&#41;&#10; ip_collection &#61; optional&#40;string, null&#41;&#10; secondary_ip_ranges &#61; optional&#40;map&#40;object&#40;&#123;&#10; ip_cidr_range &#61; optional&#40;string&#41;&#10; reserved_internal_range &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#41;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; iam_bindings &#61; optional&#40;map&#40;object&#40;&#123;&#10; role &#61; string&#10; members &#61; list&#40;string&#41;&#10; condition &#61; optional&#40;object&#40;&#123;&#10; expression &#61; string&#10; title &#61; string&#10; description &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10; iam_bindings_additive &#61; optional&#40;map&#40;object&#40;&#123;&#10; member &#61; string&#10; role &#61; string&#10; condition &#61; optional&#40;object&#40;&#123;&#10; expression &#61; string&#10; title &#61; string&#10; description &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;&#41;">list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#91;&#93;</code> |
| [subnets_private_nat](variables.tf#L407) | List of private NAT subnets. | <code title="list&#40;object&#40;&#123;&#10; name &#61; string&#10; ip_cidr_range &#61; string&#10; region &#61; string&#10; description &#61; optional&#40;string&#41;&#10;&#125;&#41;&#41;">list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#91;&#93;</code> |
| [subnets_proxy_only](variables.tf#L419) | 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. | <code title="list&#40;object&#40;&#123;&#10; name &#61; string&#10; ip_cidr_range &#61; string&#10; region &#61; string&#10; description &#61; optional&#40;string&#41;&#10; active &#61; optional&#40;bool, true&#41;&#10; global &#61; optional&#40;bool, false&#41;&#10;&#10;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; iam_bindings &#61; optional&#40;map&#40;object&#40;&#123;&#10; role &#61; string&#10; members &#61; list&#40;string&#41;&#10; condition &#61; optional&#40;object&#40;&#123;&#10; expression &#61; string&#10; title &#61; string&#10; description &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10; iam_bindings_additive &#61; optional&#40;map&#40;object&#40;&#123;&#10; member &#61; string&#10; role &#61; string&#10; condition &#61; optional&#40;object&#40;&#123;&#10; expression &#61; string&#10; title &#61; string&#10; description &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;&#41;">list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#91;&#93;</code> |
| [subnets_psc](variables.tf#L453) | List of subnets for Private Service Connect service producers. | <code title="list&#40;object&#40;&#123;&#10; name &#61; string&#10; ip_cidr_range &#61; string&#10; region &#61; string&#10; description &#61; optional&#40;string&#41;&#10;&#10;&#10; iam &#61; optional&#40;map&#40;list&#40;string&#41;&#41;, &#123;&#125;&#41;&#10; iam_bindings &#61; optional&#40;map&#40;object&#40;&#123;&#10; role &#61; string&#10; members &#61; list&#40;string&#41;&#10; condition &#61; optional&#40;object&#40;&#123;&#10; expression &#61; string&#10; title &#61; string&#10; description &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10; iam_bindings_additive &#61; optional&#40;map&#40;object&#40;&#123;&#10; member &#61; string&#10; role &#61; string&#10; condition &#61; optional&#40;object&#40;&#123;&#10; expression &#61; string&#10; title &#61; string&#10; description &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;&#41;">list&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code>&#91;&#93;</code> |
| [vpc_reuse](variables.tf#L485) | Reuse existing VPC if not null. If the network_id number is not passed in, a data source is used. | <code title="object&#40;&#123;&#10; use_data_source &#61; optional&#40;bool, true&#41;&#10; attributes &#61; optional&#40;object&#40;&#123;&#10; network_id &#61; number&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
## Outputs
@@ -791,19 +932,22 @@ module "vpc" {
|---|---|:---:|
| [id](outputs.tf#L17) | Fully qualified network id. | |
| [internal_ipv6_range](outputs.tf#L29) | ULA range. | |
| [name](outputs.tf#L34) | Network name. | |
| [network](outputs.tf#L46) | Network resource. | |
| [network_attachment_ids](outputs.tf#L58) | IDs of network attachments. | |
| [project_id](outputs.tf#L66) | Project ID containing the network. Use this when you need to create resources *after* the VPC is fully set up (e.g. subnets created, shared VPC service projects attached, Private Service Networking configured). | |
| [self_link](outputs.tf#L79) | Network self link. | |
| [subnet_ids](outputs.tf#L91) | Map of subnet IDs keyed by name. | |
| [subnet_ips](outputs.tf#L100) | Map of subnet address ranges keyed by name. | |
| [subnet_ipv6_external_prefixes](outputs.tf#L107) | Map of subnet external IPv6 prefixes keyed by name. | |
| [subnet_regions](outputs.tf#L115) | Map of subnet regions keyed by name. | |
| [subnet_secondary_ranges](outputs.tf#L122) | Map of subnet secondary ranges keyed by name. | |
| [subnet_self_links](outputs.tf#L133) | Map of subnet self links keyed by name. | |
| [subnets](outputs.tf#L142) | Subnet resources. | |
| [subnets_private_nat](outputs.tf#L151) | Private NAT subnet resources. | |
| [subnets_proxy_only](outputs.tf#L156) | L7 ILB or L7 Regional LB subnet resources. | |
| [subnets_psc](outputs.tf#L161) | Private Service Connect subnet resources. | |
| [internal_range_ids](outputs.tf#L34) | Map of internal range IDs keyed by name. | |
| [internal_range_ip_cidr_ranges](outputs.tf#L39) | Map of internal range IP CIDR ranges keyed by name. | |
| [internal_ranges](outputs.tf#L46) | Internal range resources. | |
| [name](outputs.tf#L51) | Network name. | |
| [network](outputs.tf#L63) | Network resource. | |
| [network_attachment_ids](outputs.tf#L75) | IDs of network attachments. | |
| [project_id](outputs.tf#L83) | Project ID containing the network. Use this when you need to create resources *after* the VPC is fully set up (e.g. subnets created, shared VPC service projects attached, Private Service Networking configured). | |
| [self_link](outputs.tf#L96) | Network self link. | |
| [subnet_ids](outputs.tf#L108) | Map of subnet IDs keyed by name. | |
| [subnet_ips](outputs.tf#L117) | Map of subnet address ranges keyed by name. | |
| [subnet_ipv6_external_prefixes](outputs.tf#L124) | Map of subnet external IPv6 prefixes keyed by name. | |
| [subnet_regions](outputs.tf#L132) | Map of subnet regions keyed by name. | |
| [subnet_secondary_ranges](outputs.tf#L139) | Map of subnet secondary ranges keyed by name. | |
| [subnet_self_links](outputs.tf#L150) | Map of subnet self links keyed by name. | |
| [subnets](outputs.tf#L159) | Subnet resources. | |
| [subnets_private_nat](outputs.tf#L168) | Private NAT subnet resources. | |
| [subnets_proxy_only](outputs.tf#L173) | L7 ILB or L7 Regional LB subnet resources. | |
| [subnets_psc](outputs.tf#L178) | Private Service Connect subnet resources. | |
<!-- END TFDOC -->

View File

@@ -0,0 +1,100 @@
/**
* Copyright 2024 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.
*/
# tfdoc:file:description Internal range resources.
locals {
_internal_ranges_factory_data_raw = {
for f in try(fileset(local._internal_ranges_factory_path, "**/*.yaml"), []) :
trimsuffix(basename(f), ".yaml") => yamldecode(file("${local._internal_ranges_factory_path}/${f}"))
}
_internal_ranges_factory_data = {
for k, v in local._internal_ranges_factory_data_raw : k => merge(v, {
name = try(v.name, k)
})
}
_internal_ranges_factory_path = try(pathexpand(var.factories_config.internal_ranges_folder), null)
_factory_internal_ranges = {
for k, v in local._internal_ranges_factory_data :
try(v.name, k) => {
description = try(v.description, null)
ip_cidr_range = try(v.ip_cidr_range, null)
labels = try(v.labels, {})
name = try(v.name, k)
network = try(v.network, local.network.id)
usage = v.usage
peering = v.peering
prefix_length = try(v.prefix_length, null)
target_cidr_range = try(v.target_cidr_range, null)
exclude_cidr_ranges = try(v.exclude_cidr_ranges, null)
overlaps = try(v.overlaps, null)
immutable = try(v.immutable, null)
allocation_options = !can(v.allocation_options) ? null : {
allocation_strategy = try(v.allocation_options.allocation_strategy, null)
first_available_ranges_lookup_size = try(v.allocation_options.first_available_ranges_lookup_size, null)
}
migration = !can(v.migration) ? null : {
source = v.migration.source
target = v.migration.target
}
}
}
internal_ranges = merge(
{ for r in var.internal_ranges : r.name => r },
local._factory_internal_ranges
)
internal_ranges_ids = {
for k, v in google_network_connectivity_internal_range.internal_range :
k => v.id
}
}
resource "google_network_connectivity_internal_range" "internal_range" {
provider = google-beta
for_each = local.internal_ranges
project = var.project_id
name = each.value.name
network = local.network.id
description = each.value.description
ip_cidr_range = each.value.ip_cidr_range
labels = each.value.labels
usage = each.value.usage
peering = each.value.peering
prefix_length = each.value.prefix_length
target_cidr_range = each.value.target_cidr_range
exclude_cidr_ranges = each.value.exclude_cidr_ranges
overlaps = each.value.overlaps
immutable = each.value.immutable
dynamic "allocation_options" {
for_each = each.value.allocation_options != null ? [""] : []
content {
allocation_strategy = each.value.allocation_options.allocation_strategy
first_available_ranges_lookup_size = each.value.allocation_options.first_available_ranges_lookup_size
}
}
dynamic "migration" {
for_each = each.value.migration != null ? [""] : []
content {
source = each.value.migration.source
target = each.value.migration.target
}
}
}

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.
@@ -31,6 +31,23 @@ output "internal_ipv6_range" {
value = try(google_compute_network.network[0].internal_ipv6_range, null)
}
output "internal_range_ids" {
description = "Map of internal range IDs keyed by name."
value = { for k, v in google_network_connectivity_internal_range.internal_range : k => v.id }
}
output "internal_range_ip_cidr_ranges" {
description = "Map of internal range IP CIDR ranges keyed by name."
value = {
for k, v in google_network_connectivity_internal_range.internal_range : k => v.ip_cidr_range
}
}
output "internal_ranges" {
description = "Internal range resources."
value = { for k, v in google_network_connectivity_internal_range.internal_range : k => v }
}
output "name" {
description = "Network name."
value = local.network.name

View File

@@ -0,0 +1,110 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "InternalRange",
"type": "object",
"additionalProperties": false,
"required": [
"usage",
"peering"
],
"properties": {
"name": {
"type": "string",
"description": "The name of the internal range. If not provided, the filename will be used."
},
"description": {
"type": "string",
"description": "An optional description of this internal range."
},
"labels": {
"type": "object",
"description": "User-defined labels.",
"additionalProperties": {
"type": "string"
}
},
"ip_cidr_range": {
"type": "string",
"description": "The IP range that this internal range defines. Note: IPv6 ranges are limited to usage=EXTERNAL_TO_VPC and peering=FOR_SELF. For IPv6 ranges this field is compulsory."
},
"usage": {
"type": "string",
"enum": ["FOR_VPC", "EXTERNAL_TO_VPC", "FOR_MIGRATION"],
"description": "The type of usage set for this InternalRange."
},
"peering": {
"type": "string",
"enum": ["FOR_SELF", "FOR_PEER", "NOT_SHARED"],
"description": "The type of peering set for this internal range."
},
"prefix_length": {
"type": "integer",
"minimum": 1,
"maximum": 32,
"description": "An alternate to ip_cidr_range. Can be set when trying to create a reservation that automatically finds a free range of the given size."
},
"target_cidr_range": {
"type": "array",
"description": "Optional. Can be set to narrow down or pick a different address space while searching for a free range.",
"items": {
"type": "string"
}
},
"exclude_cidr_ranges": {
"type": "array",
"description": "Optional. List of IP CIDR ranges to be excluded. Only IPv4 CIDR ranges are supported.",
"items": {
"type": "string"
}
},
"allocation_options": {
"type": "object",
"description": "Options for automatically allocating a free range with a size given by prefixLength.",
"additionalProperties": false,
"properties": {
"allocation_strategy": {
"type": "string",
"enum": ["RANDOM", "FIRST_AVAILABLE", "RANDOM_FIRST_N_AVAILABLE", "FIRST_SMALLEST_FITTING"],
"description": "Sets the strategy used to automatically find a free range of a size given by prefixLength."
},
"first_available_ranges_lookup_size": {
"type": "integer",
"minimum": 1,
"description": "Must be set when allocation_strategy is RANDOM_FIRST_N_AVAILABLE, otherwise must remain unset."
}
}
},
"overlaps": {
"type": "array",
"description": "Optional. Types of resources that are allowed to overlap with the current internal range.",
"items": {
"type": "string",
"enum": ["OVERLAP_ROUTE_RANGE", "OVERLAP_EXISTING_SUBNET_RANGE"]
}
},
"migration": {
"type": "object",
"description": "Specification for migration with source and target resource names.",
"additionalProperties": false,
"required": ["source", "target"],
"properties": {
"source": {
"type": "string",
"description": "Resource path as an URI of the source resource, for example a subnet."
},
"target": {
"type": "string",
"description": "Resource path of the target resource. The target project can be different."
}
}
},
"immutable": {
"type": "boolean",
"description": "Immutable ranges cannot have their fields modified, except for labels and description."
}
},
"anyOf": [
{"required": ["ip_cidr_range"]},
{"required": ["prefix_length"]}
]
}

View File

@@ -4,9 +4,21 @@
"type": "object",
"additionalProperties": false,
"required": [
"ip_cidr_range",
"region"
],
"anyOf": [
{"required": ["ip_cidr_range"]},
{"required": ["reserved_internal_range"]},
{"required": ["ip_collection"]},
{
"allOf": [
{"not": {"required": ["ip_cidr_range"]}},
{"not": {"required": ["reserved_internal_range"]}},
{"not": {"required": ["ip_collection"]}},
{"properties": {"ipv6": {"properties": {"ipv6_only": {"const": true}}}}, "required": ["ipv6"]}
]
}
],
"properties": {
"active": {
"type": "boolean"
@@ -50,6 +62,10 @@
"ip_cidr_range": {
"type": "string"
},
"reserved_internal_range": {
"type": "string",
"description": "Name of the internal range to use for this subnet. Mutually exclusive with ip_cidr_range and ip_collection."
},
"ipv6": {
"type": "object",
"additionalProperties": false,
@@ -80,7 +96,30 @@
"secondary_ip_ranges": {
"type": "object",
"additionalProperties": {
"type": "string"
"oneOf": [
{
"type": "string",
"description": "IP CIDR range for backward compatibility"
},
{
"type": "object",
"additionalProperties": false,
"anyOf": [
{"required": ["ip_cidr_range"]},
{"required": ["reserved_internal_range"]}
],
"properties": {
"ip_cidr_range": {
"type": "string",
"description": "IP CIDR range for this secondary range"
},
"reserved_internal_range": {
"type": "string",
"description": "Name of the internal range to use for this secondary range"
}
}
}
]
}
},
"iam": {
@@ -189,4 +228,4 @@
}
}
}
}
}

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.
@@ -34,6 +34,7 @@ locals {
description = try(v.description, null)
enable_private_access = try(v.enable_private_access, true)
allow_subnet_cidr_routes_overlap = try(v.allow_subnet_cidr_routes_overlap, null)
reserved_internal_range = try(v.reserved_internal_range, null)
flow_logs_config = can(v.flow_logs_config) ? {
aggregation_interval = try(v.flow_logs_config.aggregation_interval, null)
filter_expression = try(v.flow_logs_config.filter_expression, null)
@@ -42,16 +43,22 @@ locals {
metadata_fields = try(v.flow_logs_config.metadata_fields, null)
} : null
global = try(v.global, false)
ip_cidr_range = v.ip_cidr_range
ip_cidr_range = try(v.ip_cidr_range, null)
ipv6 = !can(v.ipv6) ? null : {
access_type = try(v.ipv6.access_type, "INTERNAL")
ipv6_only = try(v.ipv6.ipv6_only, false)
}
ip_collection = try(v.ip_collection, null)
name = try(v.name, k)
region = v.region_computed
secondary_ip_ranges = try(v.secondary_ip_ranges, null)
iam = try(v.iam, {})
ip_collection = try(v.ip_collection, null)
name = try(v.name, k)
region = v.region_computed
secondary_ip_ranges = !can(v.secondary_ip_ranges) ? null : {
for k2, v2 in v.secondary_ip_ranges :
k2 => {
ip_cidr_range = try(v2.ip_cidr_range, null)
reserved_internal_range = try(v2.reserved_internal_range, null)
}
}
iam = try(v.iam, {})
iam_bindings = !can(v.iam_bindings) ? {} : {
for k2, v2 in v.iam_bindings :
k2 => {
@@ -151,6 +158,11 @@ resource "google_compute_subnetwork" "subnetwork" {
region = each.value.region
ip_cidr_range = try(each.value.ipv6.ipv6_only, false) ? null : each.value.ip_cidr_range
allow_subnet_cidr_routes_overlap = each.value.allow_subnet_cidr_routes_overlap
reserved_internal_range = (
each.value.reserved_internal_range != null
? "networkconnectivity.googleapis.com/${try(local.internal_ranges_ids[each.value.reserved_internal_range], each.value.reserved_internal_range)}"
: null
)
description = (
# Set description to an empty string (eg "") to create subnet without a description.
each.value.description == null
@@ -178,7 +190,12 @@ resource "google_compute_subnetwork" "subnetwork" {
for_each = each.value.secondary_ip_ranges == null ? {} : each.value.secondary_ip_ranges
content {
range_name = secondary_ip_range.key
ip_cidr_range = secondary_ip_range.value
ip_cidr_range = try(secondary_ip_range.value.ip_cidr_range, secondary_ip_range.value)
reserved_internal_range = (
try(secondary_ip_range.value.reserved_internal_range, null) != null
? "networkconnectivity.googleapis.com/${try(local.internal_ranges_ids[secondary_ip_range.value.reserved_internal_range], secondary_ip_range.value.reserved_internal_range)}"
: null
)
}
}
dynamic "log_config" {

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" {

View File

@@ -1,5 +1,5 @@
/**
* Copyright 2023 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,8 +70,8 @@ module "net-vpc-host" {
name = "fixture-subnet-24"
region = var.region
secondary_ip_ranges = {
pods = "172.16.0.0/20"
services = "192.168.0.0/24"
pods = { ip_cidr_range = "172.16.0.0/20" }
services = { ip_cidr_range = "192.168.0.0/24" }
}
},
{
@@ -79,8 +79,8 @@ module "net-vpc-host" {
name = "fixture-subnet-28"
region = var.region
secondary_ip_ranges = {
pods = "172.16.16.0/20"
services = "192.168.1.0/24"
pods = { ip_cidr_range = "172.16.16.0/20" }
services = { ip_cidr_range = "192.168.1.0/24" }
}
}

View File

@@ -1,4 +1,4 @@
# Copyright 2023 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.
@@ -62,7 +62,6 @@ values:
url_mask: null
description: Terraform managed.
name: glb-test-0-neg-0
network: null
network_endpoint_type: SERVERLESS
project: project-id
psc_target_service: null

View File

@@ -0,0 +1,76 @@
# 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.
values:
module.vpc.google_compute_network.network[0]:
auto_create_subnetworks: false
delete_default_routes_on_create: false
description: Terraform-managed.
enable_ula_internal_ipv6: null
name: my-network
network_firewall_policy_enforcement_order: AFTER_CLASSIC_FIREWALL
network_profile: null
params: []
project: project-id
routing_mode: GLOBAL
timeouts: null
module.vpc.google_network_connectivity_internal_range.internal_range["range1"]:
allocation_options: []
description: null
effective_labels:
goog-terraform-provisioned: 'true'
exclude_cidr_ranges: null
immutable: null
ip_cidr_range: 10.0.0.0/16
labels: null
migration: []
name: range1
overlaps: null
peering: FOR_SELF
prefix_length: null
project: project-id
target_cidr_range: null
terraform_labels:
goog-terraform-provisioned: 'true'
timeouts: null
usage: FOR_VPC
module.vpc.google_network_connectivity_internal_range.internal_range["range2"]:
allocation_options: []
description: Auto-allocated secondary range
effective_labels:
goog-terraform-provisioned: 'true'
exclude_cidr_ranges: null
immutable: null
labels: null
migration: []
name: range2
overlaps: null
peering: FOR_SELF
prefix_length: 24
project: project-id
target_cidr_range:
- 10.1.0.0/16
terraform_labels:
goog-terraform-provisioned: 'true'
timeouts: null
usage: FOR_VPC
counts:
google_compute_network: 1
google_compute_route: 3
google_network_connectivity_internal_range: 2
modules: 1
resources: 6
outputs: {}

View File

@@ -0,0 +1,117 @@
# 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.
values:
module.vpc.google_compute_network.network[0]:
auto_create_subnetworks: false
delete_default_routes_on_create: false
description: Terraform-managed.
enable_ula_internal_ipv6: null
name: my-network
network_firewall_policy_enforcement_order: AFTER_CLASSIC_FIREWALL
network_profile: null
params: []
project: project-id
routing_mode: GLOBAL
timeouts: null
module.vpc.google_compute_subnetwork.subnetwork["europe-west1/production"]:
description: Terraform-managed.
ip_collection: null
ipv6_access_type: null
log_config: []
name: production
network: my-network
params: []
private_ip_google_access: true
project: project-id
region: europe-west1
role: null
secondary_ip_range:
- range_name: pods
- range_name: services
- ip_cidr_range: 192.168.0.0/24
range_name: traditional
reserved_internal_range: null
send_secondary_ip_range_if_empty: true
timeouts: null
module.vpc.google_network_connectivity_internal_range.internal_range["pods-range"]:
allocation_options: []
description: null
effective_labels:
goog-terraform-provisioned: 'true'
exclude_cidr_ranges: null
immutable: null
ip_cidr_range: 10.1.0.0/16
labels: null
migration: []
name: pods-range
overlaps: null
peering: FOR_SELF
prefix_length: null
project: project-id
target_cidr_range: null
terraform_labels:
goog-terraform-provisioned: 'true'
timeouts: null
usage: FOR_VPC
module.vpc.google_network_connectivity_internal_range.internal_range["services-range"]:
allocation_options: []
description: null
effective_labels:
goog-terraform-provisioned: 'true'
exclude_cidr_ranges: null
immutable: null
labels: null
migration: []
name: services-range
overlaps: null
peering: FOR_SELF
prefix_length: 20
project: project-id
target_cidr_range:
- 10.2.0.0/16
terraform_labels:
goog-terraform-provisioned: 'true'
timeouts: null
usage: FOR_VPC
module.vpc.google_network_connectivity_internal_range.internal_range["subnet-range"]:
allocation_options: []
description: null
effective_labels:
goog-terraform-provisioned: 'true'
exclude_cidr_ranges: null
immutable: null
ip_cidr_range: 10.0.1.0/24
labels: null
migration: []
name: subnet-range
overlaps: null
peering: FOR_SELF
prefix_length: null
project: project-id
target_cidr_range: null
terraform_labels:
goog-terraform-provisioned: 'true'
timeouts: null
usage: FOR_VPC
counts:
google_compute_network: 1
google_compute_route: 3
google_compute_subnetwork: 1
google_network_connectivity_internal_range: 3
modules: 1
resources: 8
outputs: {}