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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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" }
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -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({ regions = optional(map(string), {}) })">object({…})</code> | | <code>{}</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({ 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) })">object({…})</code> | | <code>{}</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>"Terraform-managed."</code> |
|
||||
| [dns_policy](variables.tf#L57) | DNS policy setup for the VPC. | <code title="object({ inbound = optional(bool) logging = optional(bool) outbound = optional(object({ private_ns = list(string) public_ns = list(string) })) })">object({…})</code> | | <code>null</code> |
|
||||
| [factories_config](variables.tf#L70) | Paths to data files and folders that enable factory functionality. | <code title="object({ subnets_folder = optional(string) })">object({…})</code> | | <code>{}</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>"AFTER_CLASSIC_FIREWALL"</code> |
|
||||
| [ipv6_config](variables.tf#L90) | Optional IPv6 configuration for this network. | <code title="object({ enable_ula_internal = optional(bool) internal_range = optional(string) })">object({…})</code> | | <code>{}</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(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)) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [peering_config](variables.tf#L124) | VPC peering configuration. | <code title="object({ peer_vpc_self_link = string create_remote_peer = optional(bool, true) export_routes = optional(bool) import_routes = optional(bool) })">object({…})</code> | | <code>null</code> |
|
||||
| [policy_based_routes](variables.tf#L135) | Policy based routes, keyed by name. | <code title="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)) }), {}) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [psa_configs](variables.tf#L188) | The Private Service Access configuration. | <code title="list(object({ deletion_policy = optional(string, null) ranges = 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") }))">list(object({…}))</code> | | <code>[]</code> |
|
||||
| [routes](variables.tf#L219) | Network routes, keyed by name. | <code title="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)) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [routing_mode](variables.tf#L240) | The network routing mode (default 'GLOBAL'). | <code>string</code> | | <code>"GLOBAL"</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(string)</code> | | <code>[]</code> |
|
||||
| [subnets](variables.tf#L262) | Subnet configuration. | <code title="list(object({ name = string ip_cidr_range = string region = string description = optional(string) enable_private_access = optional(bool, true) allow_subnet_cidr_routes_overlap = optional(bool, null) flow_logs_config = optional(object({ aggregation_interval = optional(string) filter_expression = optional(string) flow_sampling = optional(number) metadata = optional(string) metadata_fields = optional(list(string)) })) ipv6 = optional(object({ access_type = optional(string, "INTERNAL") ipv6_only = optional(bool, false) })) ip_collection = optional(string, null) secondary_ip_ranges = optional(map(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) })) })), {}) }))">list(object({…}))</code> | | <code>[]</code> |
|
||||
| [subnets_private_nat](variables.tf#L311) | List of private NAT subnets. | <code title="list(object({ name = string ip_cidr_range = string region = string description = optional(string) }))">list(object({…}))</code> | | <code>[]</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(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) })) })), {}) }))">list(object({…}))</code> | | <code>[]</code> |
|
||||
| [subnets_psc](variables.tf#L357) | List of subnets for Private Service Connect service producers. | <code title="list(object({ name = string ip_cidr_range = string region = string description = 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) })) })), {}) }))">list(object({…}))</code> | | <code>[]</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({ use_data_source = optional(bool, true) attributes = optional(object({ network_id = number })) })">object({…})</code> | | <code>null</code> |
|
||||
| [factories_config](variables.tf#L70) | Paths to data files and folders that enable factory functionality. | <code title="object({ subnets_folder = optional(string) internal_ranges_folder = optional(string) })">object({…})</code> | | <code>{}</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>"AFTER_CLASSIC_FIREWALL"</code> |
|
||||
| [internal_ranges](variables.tf#L91) | Internal range configuration for IPAM operations within the VPC network. | <code title="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 })) }))">list(object({…}))</code> | | <code>[]</code> |
|
||||
| [ipv6_config](variables.tf#L155) | Optional IPv6 configuration for this network. | <code title="object({ enable_ula_internal = optional(bool) internal_range = optional(string) })">object({…})</code> | | <code>{}</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(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)) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [peering_config](variables.tf#L189) | VPC peering configuration. | <code title="object({ peer_vpc_self_link = string create_remote_peer = optional(bool, true) export_routes = optional(bool) import_routes = optional(bool) })">object({…})</code> | | <code>null</code> |
|
||||
| [policy_based_routes](variables.tf#L200) | Policy based routes, keyed by name. | <code title="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)) }), {}) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [psa_configs](variables.tf#L253) | The Private Service Access configuration. | <code title="list(object({ deletion_policy = optional(string, null) ranges = 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") }))">list(object({…}))</code> | | <code>[]</code> |
|
||||
| [routes](variables.tf#L284) | Network routes, keyed by name. | <code title="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)) }))">map(object({…}))</code> | | <code>{}</code> |
|
||||
| [routing_mode](variables.tf#L305) | The network routing mode (default 'GLOBAL'). | <code>string</code> | | <code>"GLOBAL"</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(string)</code> | | <code>[]</code> |
|
||||
| [subnets](variables.tf#L327) | Subnet configuration. | <code title="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) metadata_fields = optional(list(string)) })) ipv6 = optional(object({ access_type = optional(string, "INTERNAL") 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) })) })), {}) }))">list(object({…}))</code> | | <code>[]</code> |
|
||||
| [subnets_private_nat](variables.tf#L407) | List of private NAT subnets. | <code title="list(object({ name = string ip_cidr_range = string region = string description = optional(string) }))">list(object({…}))</code> | | <code>[]</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(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) })) })), {}) }))">list(object({…}))</code> | | <code>[]</code> |
|
||||
| [subnets_psc](variables.tf#L453) | List of subnets for Private Service Connect service producers. | <code title="list(object({ name = string ip_cidr_range = string region = string description = 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) })) })), {}) }))">list(object({…}))</code> | | <code>[]</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({ use_data_source = optional(bool, true) attributes = optional(object({ network_id = number })) })">object({…})</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 -->
|
||||
|
||||
100
modules/net-vpc/internal-ranges.tf
Normal file
100
modules/net-vpc/internal-ranges.tf
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
110
modules/net-vpc/schemas/internal-range.schema.json
Normal file
110
modules/net-vpc/schemas/internal-range.schema.json
Normal 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"]}
|
||||
]
|
||||
}
|
||||
@@ -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 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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" {
|
||||
|
||||
@@ -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" {
|
||||
|
||||
10
tests/fixtures/shared-vpc.tf
vendored
10
tests/fixtures/shared-vpc.tf
vendored
@@ -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" }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
76
tests/modules/net_vpc/examples/internal-ranges.yaml
Normal file
76
tests/modules/net_vpc/examples/internal-ranges.yaml
Normal 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: {}
|
||||
117
tests/modules/net_vpc/examples/subnets-internal-ranges.yaml
Normal file
117
tests/modules/net_vpc/examples/subnets-internal-ranges.yaml
Normal 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: {}
|
||||
Reference in New Issue
Block a user