Merge branch 'master' into fast-dev

This commit is contained in:
Julio Castillo
2026-02-05 12:25:31 +01:00
57 changed files with 1998 additions and 6534 deletions

View File

@@ -0,0 +1,197 @@
# Net VPC Factory
This module implements the creation of VPCs, subnets, and firewall rules via YAML configurations. It is designed to be embedded in other factories such as the [FAST networking stage](../../fast/stages/2-networking).
It supports:
- **VPCs** and **Subnets** leveraging the [net-vpc](../net-vpc/) module.
- **Firewall rules** leveraging the [net-vpc-firewall](../net-vpc-firewall/) module.
- **Context-based interpolation** for referring to resources dynamically (e.g., project IDs, IAM principals, Locations).
The factory is implemented as a thin data translation layer over the underlying modules, ensuring transparency and ease of debugging.
The factory is implemented as a thin data translation layer over the underlying modules, so that no "magic" or hidden side effects are implemented in code, and debugging or integration of new features are simple.
The code is meant to be executed by a principal with permissions over the network infrastructure across the projects where VPCs are defined:
- **Network Admin** (`roles/compute.networkAdmin`): to manage VPCs, subnets, routes, and firewall rules.
- **DNS Admin** (`roles/dns.admin`): to manage DNS policies.
- **Security Admin** (`roles/compute.securityAdmin`): to manage firewall policies.
## Contents
<!-- BEGIN TOC -->
- [VPC Factory](#vpc-factory)
- [Defaults](#defaults)
- [Subnets](#subnets)
- [Firewall rules](#firewall-rules)
- [Context-based interpolation](#context-based-interpolation)
- [Project context ids](#project-context-ids)
- [Other context ids](#other-context-ids)
- [Example](#example)
- [Variables](#variables)
- [Outputs](#outputs)
<!-- END TOC -->
## VPC Factory
The VPC factory is configured via the `factories_config.vpcs` variable, which sets the path containing the YAML definitions for VPCs, where each VPC and their dependent resources are defined in a dedicated directory.
Each VPC directory contains a `.config.yaml` file. The structure of the YAML file mirrors the variables of the [`net-vpc`](../net-vpc/) module.
```yaml
project_id: $project_ids:my-project # Or use the project id directly
description: "My VPC"
routing_mode: GLOBAL
subnets:
- name: subnet-a
region: europe-west1
ip_cidr_range: 10.0.0.0/24
```
### Defaults
In addition to the YAML-based VPC configurations, the factory accepts three additional sets of inputs via Terraform variables to control defaults:
- `data_defaults`: defaults for specific VPC attributes, used if not present in YAML.
- `data_overrides`: overrides that take precedence over YAML values.
- `factories_config.defaults`: path to a YAML file containing global context and VPC defaults.
```hcl
module "net-vpc-factory" {
source = "./modules/net-vpc-factory"
data_defaults = {
routing_mode = "REGIONAL"
}
factories_config = {
vpcs = "data/vpcs"
}
}
```
### Subnets
Subnets can be defined inline in the VPC `.config.yaml` file (as shown above) or in separate files within a `subnets` subdirectory in the VPC's folder. The factory automatically scans the `subnets` folder if it exists.
```text
data/vpcs/
└── my-vpc/
├── .config.yaml
└── subnets/
├── subnet-a.yaml
└── subnet-b.yaml
```
This allows splitting complex subnet configurations (like those with massive secondary ranges or specialized IAM bindings) into manageable files.
### Firewall rules
Firewall rules are managed via a `firewall-rules` subdirectory in the VPC's folder. The factory uses the [`net-vpc-firewall`](../net-vpc-firewall/) module to provision these rules - the YAML format for firewall rules follows the structure expected by the module itself.
```text
data/vpcs/
└── my-vpc/
├── .config.yaml
└── firewall-rules/
├── allow-ssh.yaml
└── allow-internal.yaml
```
## Context-based interpolation
Interpolation allows referring to resources which are external or created at runtime via short aliases. This is particularly useful for Project IDs, which might be generated by the Project Factory.
Contexts are passed via the `context` variable or the `factories_config.defaults` file.
### Project context ids
Project IDs use the `$project_ids:` namespace. This allows decoupling the VPC definition from the actual Project ID string.
```yaml
# data/vpcs/vpc-0/.config.yaml
project_id: $project_ids:data-project
name: vpc-0
```
```hcl
module "net-vpc-factory" {
# ...
context = {
project_ids = {
data-project = "prefix-prod-data-app-0"
}
}
}
```
### Other context ids
Other contexts can be defined freely. Common uses include:
- `$locations:` for GCP regions.
- `$iam_principals:` for IAM principals.
## Example
```hcl
module "net-vpc-factory" {
source = "./fabric/modules/net-vpc-factory"
context = {
project_ids = {
net-project = "my-host-project-id"
}
locations = {
primary = "europe-west1"
}
}
factories_config = {
vpcs = "data/vpcs"
}
}
# tftest files=vpc,fw modules=3 inventory=example.yaml
```
**data/vpcs/shared-vpc/.config.yaml**
```yaml
project_id: $project_ids:net-project
name: data-vpc-0
subnets:
- name: primary-subnet
region: $locations:primary
ip_cidr_range: 10.10.0.0/24
# tftest-file id=vpc path=data/vpcs/data-vpc-0/.config.yaml schema=vpc.schema.json
```
**data/vpcs/data-vpc-0/firewall-rules/allow-iap.yaml**
```yaml
ingress:
allow-iap:
description: Allow IAP for SSH
source_ranges:
- 35.235.240.0/20
rules:
- protocol: tcp
ports: [22]
targets: ["ssh"]
# tftest-file id=fw path=data/vpcs/data-vpc-0/firewall-rules/allow-iap.yaml schema=firewall-rules.schema.json
```
<!-- BEGIN TFDOC -->
## Variables
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [context](variables.tf#L17) | Context-specific interpolations. | <code title="object&#40;&#123;&#10; locations &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10; project_ids &#61; optional&#40;map&#40;string&#41;, &#123;&#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [data_defaults](variables.tf#L27) | Optional default values used when corresponding vpc data from files are missing. | <code title="object&#40;&#123;&#10; project_id &#61; optional&#40;string&#41;&#10; description &#61; optional&#40;string, &#34;Terraform managed&#34;&#41;&#10; auto_create_subnetworks &#61; optional&#40;bool&#41;&#10; delete_default_routes_on_create &#61; optional&#40;bool, true&#41;&#10; mtu &#61; optional&#40;number&#41;&#10; routing_mode &#61; optional&#40;string, &#34;GLOBAL&#34;&#41;&#10; firewall_policy_enforcement_order &#61; optional&#40;string, &#34;AFTER_CLASSIC_FIREWALL&#34;&#41;&#10; create_googleapis_routes &#61; optional&#40;object&#40;&#123;&#10; directpath &#61; optional&#40;bool&#41;&#10; directpath-6 &#61; optional&#40;bool&#41;&#10; private &#61; optional&#40;bool&#41;&#10; private-6 &#61; optional&#40;bool&#41;&#10; restricted &#61; optional&#40;bool&#41;&#10; restricted-6 &#61; optional&#40;bool&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10; dns_policy &#61; optional&#40;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; optional&#40;list&#40;string&#41;&#41;&#10; public_ns &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;&#10; ipv6_config &#61; optional&#40;object&#40;&#123;&#10; enable_ula_internal &#61; optional&#40;bool&#41;&#10; internal_range &#61; optional&#40;string&#41;&#10; &#125;&#41;, &#123;&#125;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [data_overrides](variables.tf#L62) | Optional values that override corresponding data from files. Takes precedence over file data and `data_defaults`. | <code title="object&#40;&#123;&#10; project_id &#61; optional&#40;string&#41;&#10; description &#61; optional&#40;string&#41;&#10; auto_create_subnetworks &#61; optional&#40;bool&#41;&#10; delete_default_routes_on_create &#61; optional&#40;bool&#41;&#10; mtu &#61; optional&#40;number&#41;&#10; routing_mode &#61; optional&#40;string&#41;&#10; firewall_policy_enforcement_order &#61; optional&#40;string&#41;&#10; create_googleapis_routes &#61; optional&#40;object&#40;&#123;&#10; directpath &#61; optional&#40;bool&#41;&#10; directpath-6 &#61; optional&#40;bool&#41;&#10; private &#61; optional&#40;bool&#41;&#10; private-6 &#61; optional&#40;bool&#41;&#10; restricted &#61; optional&#40;bool&#41;&#10; restricted-6 &#61; optional&#40;bool&#41;&#10; &#125;&#41;&#41;&#10; dns_policy &#61; optional&#40;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; optional&#40;list&#40;string&#41;&#41;&#10; public_ns &#61; optional&#40;list&#40;string&#41;&#41;&#10; &#125;&#41;&#41;&#10; &#125;&#41;&#41;&#10; ipv6_config &#61; optional&#40;object&#40;&#123;&#10; enable_ula_internal &#61; optional&#40;bool&#41;&#10; internal_range &#61; optional&#40;string&#41;&#10; &#125;&#41;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
| [factories_config](variables.tf#L97) | Path to folder with YAML resource description data files. | <code title="object&#40;&#123;&#10; vpcs &#61; optional&#40;string&#41;&#10; defaults &#61; optional&#40;string&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>&#123;&#125;</code> |
## Outputs
| name | description | sensitive |
|---|---|:---:|
| [firewall_rules](outputs.tf#L17) | Firewall rules. | |
| [vpcs](outputs.tf#L22) | VPCs. | |
| [vpcs_config](outputs.tf#L27) | Processed VPC configuration data. | |
<!-- END TFDOC -->

View File

@@ -0,0 +1,35 @@
# 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.
# yaml-language-server: $schema=../../schemas/defaults.schema.json
context:
cidr_ranges_sets:
healthchecks:
- 35.191.0.0/16
- 130.211.0.0/22
- 209.85.152.0/22
- 209.85.204.0/22
rfc1918:
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
locations:
primary: europe-west1
secondary: europe-west3
iam_principals: {}
vpcs:
auto_create_subnetworks: false
delete_default_route_on_create: true
mtu: 1500

View File

@@ -0,0 +1,13 @@
# skip boilerplate check
---
# start of document (---) avoids errors if the file only contains comments
# yaml-language-server: $schema=../../../schemas/vpc.schema.json
name: example
project_id: $project_ids:net
auto_create_subnetworks: false
subnets:
- name: example-default-primary
region: $locations:primary
ip_cidr_range: 172.16.0.0/24

View File

@@ -0,0 +1,13 @@
# skip boilerplate check
---
# start of document (---) avoids errors if the file only contains comments
# yaml-language-server: $schema=../../../../schemas/firewall-rules.schema.json
ingress:
ingress-default-prod-deny:
description: "Deny and log any unmatched ingress traffic."
deny: true
priority: 65535
enable_logging:
include_metadata: false

View File

@@ -0,0 +1,8 @@
# skip boilerplate check
# yaml-language-server: $schema=../../../../schemas/subnet.schema.json
name: example-default-secondary
region: $locations:secondary
ip_cidr_range: 10.0.0.0/24
description: Default primary-region subnet for prod

View File

@@ -0,0 +1,102 @@
/**
* 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.
*/
locals {
_vpcs_path = try(
pathexpand(var.factories_config.vpcs), null
)
_vpcs_files = try(
fileset(local._vpcs_path, "**/.config.yaml"),
[]
)
_defaults = try(
yamldecode(file(var.factories_config.defaults)), {}
)
context = {
locations = merge(var.context.locations, try(local._defaults.context.locations, {}))
project_ids = merge(var.context.project_ids, try(local._defaults.context.project_ids, {}))
cidr_ranges_sets = try(local._defaults.context.cidr_ranges_sets, {})
iam_principals = try(local._defaults.context.iam_principals, {})
}
_vpcs_preprocess = [
for f in local._vpcs_files : merge(
yamldecode(file("${coalesce(local._vpcs_path, "-")}/${f}")),
{
factory_dirname = dirname(f)
factory_basepath = "${local._vpcs_path}/${dirname(f)}"
}
)
if f != "defaults.yaml"
]
_vpcs = {
for v in local._vpcs_preprocess : v.factory_dirname => v
}
vpcs = {
for k, v in local._vpcs : k => merge(
try(local._defaults.vpcs, {}),
{ for k, v in var.data_defaults : k => v if v != null },
v,
{ for k, v in var.data_overrides : k => v if v != null },
{
subnets_factory_config = {
subnets_folder = "${v.factory_basepath}/subnets"
}
firewall_factory_config = {
rules_folder = "${v.factory_basepath}/firewall-rules"
}
}
)
}
}
module "vpcs" {
source = "../net-vpc"
for_each = local.vpcs
project_id = try(each.value.project_id, null)
name = try(each.value.name, null)
auto_create_subnetworks = try(each.value.auto_create_subnetworks, null)
create_googleapis_routes = try(each.value.create_googleapis_routes, null)
delete_default_routes_on_create = try(each.value.delete_default_routes_on_create, true)
description = try(each.value.description, "Terraform managed")
dns_policy = try(each.value.dns_policy, null)
factories_config = each.value.subnets_factory_config
firewall_policy_enforcement_order = try(each.value.firewall_policy_enforcement_order, "AFTER_CLASSIC_FIREWALL")
ipv6_config = try(each.value.ipv6_config, null)
mtu = try(each.value.mtu, null)
network_attachments = try(each.value.network_attachments, {})
psa_configs = try(each.value.psa_configs, [])
routing_mode = try(each.value.routing_mode, "GLOBAL")
subnets = try(each.value.subnets, [])
subnets_private_nat = try(each.value.subnets_private_nat, [])
subnets_proxy_only = try(each.value.subnets_proxy_only, [])
subnets_psc = try(each.value.subnets_psc, [])
context = local.context
}
module "firewall" {
source = "../net-vpc-firewall"
for_each = {
for k, v in local.vpcs : k => v if v.firewall_factory_config != null
}
project_id = each.value.project_id
network = each.value.name
factories_config = each.value.firewall_factory_config
default_rules_config = { disabled = true }
context = {
project_ids = local.context.project_ids
}
depends_on = [module.vpcs]
}

View File

@@ -0,0 +1,30 @@
/**
* 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.
*/
output "firewall_rules" {
description = "Firewall rules."
value = module.firewall
}
output "vpcs" {
description = "VPCs."
value = module.vpcs
}
output "vpcs_config" {
description = "Processed VPC configuration data."
value = local.vpcs
}

View File

@@ -0,0 +1,104 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Firewall Rules",
"type": "object",
"additionalProperties": false,
"properties": {
"egress": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^[a-z0-9_-]+$": {
"$ref": "#/$defs/rule"
}
}
},
"ingress": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^[a-z0-9_-]+$": {
"$ref": "#/$defs/rule"
}
}
}
},
"$defs": {
"rule": {
"type": "object",
"additionalProperties": false,
"properties": {
"deny": {
"type": "boolean"
},
"description": {
"type": "string"
},
"destination_ranges": {
"type": "array",
"items": {
"type": "string"
}
},
"disabled": {
"type": "boolean"
},
"enable_logging": {
"type": "object",
"additionalProperties": false,
"properties": {
"include_metadata": {
"type": "boolean"
}
}
},
"priority": {
"type": "number"
},
"source_ranges": {
"type": "array",
"items": {
"type": "string"
}
},
"sources": {
"type": "array",
"items": {
"type": "string"
}
},
"targets": {
"type": "array",
"items": {
"type": "string"
}
},
"use_service_accounts": {
"type": "boolean"
},
"rules": {
"type": "array",
"items": {
"type": "object",
"additionalProperties": false,
"properties": {
"protocol": {
"type": "string"
},
"ports": {
"type": "array",
"items": {
"type": [
"integer",
"string"
],
"pattern": "^[0-9]+(?:-[0-9]+)?$"
}
}
}
}
}
}
}
}
}

View File

@@ -0,0 +1,42 @@
# Firewall Rules
<!-- markdownlint-disable MD036 -->
## Properties
*additional properties: false*
- **egress**: *object*
<br>*additional properties: false*
- **`^[a-z0-9_-]+$`**: *reference([rule](#refs-rule))*
- **ingress**: *object*
<br>*additional properties: false*
- **`^[a-z0-9_-]+$`**: *reference([rule](#refs-rule))*
## Definitions
- **rule**<a name="refs-rule"></a>: *object*
<br>*additional properties: false*
- **deny**: *boolean*
- **description**: *string*
- **destination_ranges**: *array*
- items: *string*
- **disabled**: *boolean*
- **enable_logging**: *object*
<br>*additional properties: false*
- **include_metadata**: *boolean*
- **priority**: *number*
- **source_ranges**: *array*
- items: *string*
- **sources**: *array*
- items: *string*
- **targets**: *array*
- items: *string*
- **use_service_accounts**: *boolean*
- **rules**: *array*
- items: *object*
<br>*additional properties: false*
- **protocol**: *string*
- **ports**: *array*
- items: *(integer|string)*
<br>*pattern: `^[0-9]+(?:-[0-9]+)?$`*

View File

@@ -0,0 +1,231 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Subnet",
"type": "object",
"additionalProperties": false,
"required": [
"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"
},
"description": {
"type": "string"
},
"enable_private_access": {
"type": "boolean"
},
"allow_subnet_cidr_routes_overlap": {
"type": "boolean"
},
"flow_logs_config": {
"type": "object",
"additionalProperties": false,
"properties": {
"aggregation_interval": {
"type": "string"
},
"filter_expression": {
"type": "string"
},
"flow_sampling": {
"type": "number"
},
"metadata": {
"type": "string"
},
"metadata_fields": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"global": {
"type": "boolean"
},
"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,
"properties": {
"access_type": {
"type": "string"
},
"ipv6_only": {
"type": "boolean"
}
}
},
"ip_collection": {
"type": "string"
},
"name": {
"type": "string"
},
"region": {
"type": "string"
},
"psc": {
"type": "boolean"
},
"proxy_only": {
"type": "boolean"
},
"secondary_ip_ranges": {
"type": "object",
"additionalProperties": {
"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": {
"$ref": "#/$defs/iam"
},
"iam_bindings": {
"$ref": "#/$defs/iam_bindings"
},
"iam_bindings_additive": {
"$ref": "#/$defs/iam_bindings_additive"
}
},
"$defs": {
"iam": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^roles/": {
"type": "array",
"items": {
"type": "string",
"pattern": "^(?:domain:|group:|serviceAccount:|user:|principal:|principalSet:|ro|rw)"
}
}
}
},
"iam_bindings": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^[a-z0-9_-]+$": {
"type": "object",
"additionalProperties": false,
"properties": {
"members": {
"type": "array",
"items": {
"type": "string",
"pattern": "^(?:domain:|group:|serviceAccount:|user:|principal:|principalSet:|ro|rw)"
}
},
"role": {
"type": "string",
"pattern": "^roles/"
},
"condition": {
"type": "object",
"additionalProperties": false,
"required": [
"expression",
"title"
],
"properties": {
"expression": {
"type": "string"
},
"title": {
"type": "string"
},
"description": {
"type": "string"
}
}
}
}
}
}
},
"iam_bindings_additive": {
"type": "object",
"additionalProperties": false,
"patternProperties": {
"^[a-z0-9_-]+$": {
"type": "object",
"additionalProperties": false,
"properties": {
"member": {
"type": "string",
"pattern": "^(?:domain:|group:|serviceAccount:|user:|principal:|principalSet:|ro|rw)"
},
"role": {
"type": "string",
"pattern": "^roles/"
},
"condition": {
"type": "object",
"additionalProperties": false,
"required": [
"expression",
"title"
],
"properties": {
"expression": {
"type": "string"
},
"title": {
"type": "string"
},
"description": {
"type": "string"
}
}
}
}
}
}
}
}
}

View File

@@ -0,0 +1,77 @@
# Subnet
<!-- markdownlint-disable MD036 -->
## Properties
*additional properties: false*
- **active**: *boolean*
- **description**: *string*
- **enable_private_access**: *boolean*
- **allow_subnet_cidr_routes_overlap**: *boolean*
- **flow_logs_config**: *object*
<br>*additional properties: false*
- **aggregation_interval**: *string*
- **filter_expression**: *string*
- **flow_sampling**: *number*
- **metadata**: *string*
- **metadata_fields**: *array*
- items: *string*
- **global**: *boolean*
- **ip_cidr_range**: *string*
- **reserved_internal_range**: *string*
- **ipv6**: *object*
<br>*additional properties: false*
- **access_type**: *string*
- **ipv6_only**: *boolean*
- **ip_collection**: *string*
- **name**: *string*
- ⁺**region**: *string*
- **psc**: *boolean*
- **proxy_only**: *boolean*
- **secondary_ip_ranges**: *object*
<br>*additional properties: oneof*
- *string*
- *object*
<br>*additional properties: false*
- **ip_cidr_range**: *string*
- **reserved_internal_range**: *string*
- **iam**: *reference([iam](#refs-iam))*
- **iam_bindings**: *reference([iam_bindings](#refs-iam_bindings))*
- **iam_bindings_additive**: *reference([iam_bindings_additive](#refs-iam_bindings_additive))*
## Definitions
- **iam**<a name="refs-iam"></a>: *object*
<br>*additional properties: false*
- **`^roles/`**: *array*
- items: *string*
<br>*pattern: ^(?:domain:|group:|serviceAccount:|user:|principal:|principalSet:|ro|rw)*
- **iam_bindings**<a name="refs-iam_bindings"></a>: *object*
<br>*additional properties: false*
- **`^[a-z0-9_-]+$`**: *object*
<br>*additional properties: false*
- **members**: *array*
- items: *string*
<br>*pattern: ^(?:domain:|group:|serviceAccount:|user:|principal:|principalSet:|ro|rw)*
- **role**: *string*
<br>*pattern: ^roles/*
- **condition**: *object*
<br>*additional properties: false*
- ⁺**expression**: *string*
- ⁺**title**: *string*
- **description**: *string*
- **iam_bindings_additive**<a name="refs-iam_bindings_additive"></a>: *object*
<br>*additional properties: false*
- **`^[a-z0-9_-]+$`**: *object*
<br>*additional properties: false*
- **member**: *string*
<br>*pattern: ^(?:domain:|group:|serviceAccount:|user:|principal:|principalSet:|ro|rw)*
- **role**: *string*
<br>*pattern: ^roles/*
- **condition**: *object*
<br>*additional properties: false*
- ⁺**expression**: *string*
- ⁺**title**: *string*
- **description**: *string*

View File

@@ -0,0 +1,402 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "VPC Configuration",
"description": "Schema for a VPC .config.yaml file.",
"type": "object",
"additionalProperties": false,
"required": [
"name",
"project_id"
],
"properties": {
"project_id": {
"type": "string"
},
"name": {
"type": "string"
},
"description": {
"type": "string"
},
"auto_create_subnetworks": {
"type": "boolean"
},
"delete_default_routes_on_create": {
"type": "boolean"
},
"mtu": {
"type": "number"
},
"routing_mode": {
"type": "string",
"enum": [
"GLOBAL",
"REGIONAL"
]
},
"firewall_policy_enforcement_order": {
"type": "string",
"enum": [
"BEFORE_CLASSIC_FIREWALL",
"AFTER_CLASSIC_FIREWALL"
]
},
"create_googleapis_routes": {
"$ref": "#/$defs/create_googleapis_routes"
},
"dns_policy": {
"$ref": "#/$defs/dns_policy"
},
"ipv6_config": {
"$ref": "#/$defs/ipv6_config"
},
"network_attachments": {
"$ref": "#/$defs/network_attachments"
},
"routers": {
"$ref": "#/$defs/routers"
},
"peering_config": {
"$ref": "#/$defs/peering_config"
},
"psa_configs": {
"type": "array",
"items": {
"$ref": "#/$defs/psa_config"
}
},
"subnets": {
"type": "array",
"items": {
"$ref": "#/$defs/subnet"
}
},
"subnets_private_nat": {
"type": "array",
"items": {
"$ref": "#/$defs/simple_subnet"
}
},
"subnets_proxy_only": {
"type": "array",
"items": {
"$ref": "#/$defs/proxy_only_subnet"
}
},
"subnets_psc": {
"type": "array",
"items": {
"$ref": "#/$defs/simple_subnet"
}
},
"nat_config": {
"$ref": "#/$defs/nat_config"
},
"ncc_config": {
"$ref": "#/$defs/ncc_config"
},
"routes": {
"type": "object"
},
"policy_based_routes": {
"type": "object"
},
"vpn_config": {
"type": "object"
}
},
"$defs": {
"create_googleapis_routes": {
"type": "object",
"properties": {
"directpath": {
"type": "boolean"
},
"directpath-6": {
"type": "boolean"
},
"private": {
"type": "boolean"
},
"private-6": {
"type": "boolean"
},
"restricted": {
"type": "boolean"
},
"restricted-6": {
"type": "boolean"
}
}
},
"dns_policy": {
"type": "object",
"properties": {
"inbound": {
"type": "boolean"
},
"logging": {
"type": "boolean"
},
"outbound": {
"type": "object",
"properties": {
"private_ns": {
"type": "array",
"items": {
"type": "string"
}
},
"public_ns": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
},
"ipv6_config": {
"type": "object",
"properties": {
"enable_ula_internal": {
"type": "boolean"
},
"internal_range": {
"type": "string"
}
}
},
"nat_config": {
"type": "object",
"patternProperties": {
"^[a-z0-9-]+$": {
"type": "object",
"required": [
"region"
],
"properties": {
"region": {
"type": "string"
}
}
}
}
},
"ncc_config": {
"type": "object",
"required": [
"hub"
],
"properties": {
"hub": {
"type": "string"
},
"group": {
"type": "string"
}
}
},
"network_attachments": {
"type": "object",
"patternProperties": {
"^[a-z0-9-]+$": {
"type": "object",
"properties": {
"subnet": {
"type": "string"
},
"automatic_connection": {
"type": "boolean"
},
"description": {
"type": "string"
},
"producer_accept_lists": {
"type": "array",
"items": {
"type": "string"
}
},
"producer_reject_lists": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
},
"peering_config": {
"type": "object",
"properties": {
"peer_vpc_self_link": {
"type": "string"
},
"create_remote_peer": {
"type": "boolean"
},
"export_routes": {
"type": "boolean"
},
"import_routes": {
"type": "boolean"
}
}
},
"psa_config": {
"type": "object",
"properties": {
"deletion_policy": {
"type": "string"
},
"ranges": {
"type": "object",
"patternProperties": {
"^[a-z0-9-]+$": {
"type": "string"
}
}
},
"export_routes": {
"type": "boolean"
},
"import_routes": {
"type": "boolean"
},
"peered_domains": {
"type": "array",
"items": {
"type": "string"
}
},
"range_prefix": {
"type": "string"
},
"service_producer": {
"type": "string"
}
}
},
"routers": {
"type": "object",
"description": "A map of Cloud Routers to create in this VPC.",
"patternProperties": {
"^[a-z0-9-]+$": {
"type": "object",
"additionalProperties": false,
"required": [
"region",
"asn"
],
"properties": {
"region": {
"type": "string"
},
"asn": {
"type": "number"
},
"custom_advertise": {
"type": "object",
"properties": {
"all_subnets": {
"type": "boolean"
},
"ip_ranges": {
"type": "object",
"patternProperties": {
".*": {
"type": "string"
}
}
}
}
}
}
}
}
},
"simple_subnet": {
"type": "object",
"required": [
"name",
"ip_cidr_range",
"region"
],
"properties": {
"name": {
"type": "string"
},
"ip_cidr_range": {
"type": "string"
},
"region": {
"type": "string"
},
"description": {
"type": "string"
}
}
},
"subnet": {
"type": "object",
"required": [
"name",
"region"
],
"properties": {
"name": {
"type": "string"
},
"ip_cidr_range": {
"type": "string"
},
"region": {
"type": "string"
},
"description": {
"type": "string"
},
"enable_private_access": {
"type": "boolean"
},
"allow_subnet_cidr_routes_overlap": {
"type": "boolean"
},
"reserved_internal_range": {
"type": "string"
}
}
},
"proxy_only_subnet": {
"type": "object",
"required": [
"name",
"ip_cidr_range",
"region"
],
"properties": {
"name": {
"type": "string"
},
"ip_cidr_range": {
"type": "string"
},
"region": {
"type": "string"
},
"description": {
"type": "string"
},
"active": {
"type": "boolean"
},
"global": {
"type": "boolean"
}
}
}
}
}

View File

@@ -0,0 +1,119 @@
# VPC Configuration
<!-- markdownlint-disable MD036 -->
## Properties
*additional properties: false*
- ⁺**project_id**: *string*
- ⁺**name**: *string*
- **description**: *string*
- **auto_create_subnetworks**: *boolean*
- **delete_default_routes_on_create**: *boolean*
- **mtu**: *number*
- **routing_mode**: *string*
<br>*enum: ['GLOBAL', 'REGIONAL']*
- **firewall_policy_enforcement_order**: *string*
<br>*enum: ['BEFORE_CLASSIC_FIREWALL', 'AFTER_CLASSIC_FIREWALL']*
- **create_googleapis_routes**: *reference([create_googleapis_routes](#refs-create_googleapis_routes))*
- **dns_policy**: *reference([dns_policy](#refs-dns_policy))*
- **ipv6_config**: *reference([ipv6_config](#refs-ipv6_config))*
- **network_attachments**: *reference([network_attachments](#refs-network_attachments))*
- **routers**: *reference([routers](#refs-routers))*
- **peering_config**: *reference([peering_config](#refs-peering_config))*
- **psa_configs**: *array*
- items: *reference([psa_config](#refs-psa_config))*
- **subnets**: *array*
- items: *reference([subnet](#refs-subnet))*
- **subnets_private_nat**: *array*
- items: *reference([simple_subnet](#refs-simple_subnet))*
- **subnets_proxy_only**: *array*
- items: *reference([proxy_only_subnet](#refs-proxy_only_subnet))*
- **subnets_psc**: *array*
- items: *reference([simple_subnet](#refs-simple_subnet))*
- **nat_config**: *reference([nat_config](#refs-nat_config))*
- **ncc_config**: *reference([ncc_config](#refs-ncc_config))*
- **routes**: *object*
- **policy_based_routes**: *object*
- **vpn_config**: *object*
## Definitions
- **create_googleapis_routes**<a name="refs-create_googleapis_routes"></a>: *object*
- **directpath**: *boolean*
- **directpath-6**: *boolean*
- **private**: *boolean*
- **private-6**: *boolean*
- **restricted**: *boolean*
- **restricted-6**: *boolean*
- **dns_policy**<a name="refs-dns_policy"></a>: *object*
- **inbound**: *boolean*
- **logging**: *boolean*
- **outbound**: *object*
- **private_ns**: *array*
- items: *string*
- **public_ns**: *array*
- items: *string*
- **ipv6_config**<a name="refs-ipv6_config"></a>: *object*
- **enable_ula_internal**: *boolean*
- **internal_range**: *string*
- **nat_config**<a name="refs-nat_config"></a>: *object*
- **`^[a-z0-9-]+$`**: *object*
- ⁺**region**: *string*
- **ncc_config**<a name="refs-ncc_config"></a>: *object*
- ⁺**hub**: *string*
- **group**: *string*
- **network_attachments**<a name="refs-network_attachments"></a>: *object*
- **`^[a-z0-9-]+$`**: *object*
- **subnet**: *string*
- **automatic_connection**: *boolean*
- **description**: *string*
- **producer_accept_lists**: *array*
- items: *string*
- **producer_reject_lists**: *array*
- items: *string*
- **peering_config**<a name="refs-peering_config"></a>: *object*
- **peer_vpc_self_link**: *string*
- **create_remote_peer**: *boolean*
- **export_routes**: *boolean*
- **import_routes**: *boolean*
- **psa_config**<a name="refs-psa_config"></a>: *object*
- **deletion_policy**: *string*
- **ranges**: *object*
- **`^[a-z0-9-]+$`**: *string*
- **export_routes**: *boolean*
- **import_routes**: *boolean*
- **peered_domains**: *array*
- items: *string*
- **range_prefix**: *string*
- **service_producer**: *string*
- **routers**<a name="refs-routers"></a>: *object*
- **`^[a-z0-9-]+$`**: *object*
<br>*additional properties: false*
- ⁺**region**: *string*
- ⁺**asn**: *number*
- **custom_advertise**: *object*
- **all_subnets**: *boolean*
- **ip_ranges**: *object*
- **`.*`**: *string*
- **simple_subnet**<a name="refs-simple_subnet"></a>: *object*
- ⁺**name**: *string*
- ⁺**ip_cidr_range**: *string*
- ⁺**region**: *string*
- **description**: *string*
- **subnet**<a name="refs-subnet"></a>: *object*
- ⁺**name**: *string*
- **ip_cidr_range**: *string*
- ⁺**region**: *string*
- **description**: *string*
- **enable_private_access**: *boolean*
- **allow_subnet_cidr_routes_overlap**: *boolean*
- **reserved_internal_range**: *string*
- **proxy_only_subnet**<a name="refs-proxy_only_subnet"></a>: *object*
- ⁺**name**: *string*
- ⁺**ip_cidr_range**: *string*
- ⁺**region**: *string*
- **description**: *string*
- **active**: *boolean*
- **global**: *boolean*

View File

@@ -0,0 +1,105 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
variable "context" {
description = "Context-specific interpolations."
type = object({
locations = optional(map(string), {})
project_ids = optional(map(string), {})
})
default = {}
nullable = false
}
variable "data_defaults" {
description = "Optional default values used when corresponding vpc data from files are missing."
type = object({
project_id = optional(string)
description = optional(string, "Terraform managed")
auto_create_subnetworks = optional(bool)
delete_default_routes_on_create = optional(bool, true)
mtu = optional(number)
routing_mode = optional(string, "GLOBAL")
firewall_policy_enforcement_order = optional(string, "AFTER_CLASSIC_FIREWALL")
create_googleapis_routes = optional(object({
directpath = optional(bool)
directpath-6 = optional(bool)
private = optional(bool)
private-6 = optional(bool)
restricted = optional(bool)
restricted-6 = optional(bool)
}), {})
dns_policy = optional(object({
inbound = optional(bool)
logging = optional(bool)
outbound = optional(object({
private_ns = optional(list(string))
public_ns = optional(list(string))
}))
}))
ipv6_config = optional(object({
enable_ula_internal = optional(bool)
internal_range = optional(string)
}), {})
})
default = {}
nullable = false
}
variable "data_overrides" {
description = "Optional values that override corresponding data from files. Takes precedence over file data and `data_defaults`."
type = object({
project_id = optional(string)
description = optional(string)
auto_create_subnetworks = optional(bool)
delete_default_routes_on_create = optional(bool)
mtu = optional(number)
routing_mode = optional(string)
firewall_policy_enforcement_order = optional(string)
create_googleapis_routes = optional(object({
directpath = optional(bool)
directpath-6 = optional(bool)
private = optional(bool)
private-6 = optional(bool)
restricted = optional(bool)
restricted-6 = optional(bool)
}))
dns_policy = optional(object({
inbound = optional(bool)
logging = optional(bool)
outbound = optional(object({
private_ns = optional(list(string))
public_ns = optional(list(string))
}))
}))
ipv6_config = optional(object({
enable_ula_internal = optional(bool)
internal_range = optional(string)
}))
})
default = {}
nullable = false
}
variable "factories_config" {
description = "Path to folder with YAML resource description data files."
type = object({
vpcs = optional(string)
defaults = optional(string)
})
default = {}
nullable = false
}