diff --git a/README.md b/README.md index aa537ca80..58d4907fd 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ Currently available modules: - **foundational** - [billing account](./modules/billing-account), [Cloud Identity group](./modules/cloud-identity-group/), [folder](./modules/folder), [service accounts](./modules/iam-service-account), [logging bucket](./modules/logging-bucket), [organization](./modules/organization), [project](./modules/project), [projects-data-source](./modules/projects-data-source) - **process factories** - [project factory](./modules/project-factory/README.md) -- **networking** - [DNS](./modules/dns), [DNS Response Policy](./modules/dns-response-policy/), [Cloud Endpoints](./modules/endpoints), [address reservation](./modules/net-address), [NAT](./modules/net-cloudnat), [VLAN Attachment](./modules/net-vlan-attachment/), [External Application LB](./modules/net-lb-app-ext/), [External Passthrough Network LB](./modules/net-lb-ext), [External Regional Application Load Balancer](./modules/net-lb-app-ext-regional/), [Firewall policy](./modules/net-firewall-policy), [Internal Application LB](./modules/net-lb-app-int), [Cross-region Internal Application LB](./modules/net-lb-app-int-cross-region), [Internal Passthrough Network LB](./modules/net-lb-int), [Internal Proxy Network LB](./modules/net-lb-proxy-int), [IPSec over Interconnect](./modules/net-ipsec-over-interconnect), [VPC](./modules/net-vpc), [VPC firewall](./modules/net-vpc-firewall), [VPC peering](./modules/net-vpc-peering), [VPN dynamic](./modules/net-vpn-dynamic), [HA VPN](./modules/net-vpn-ha), [VPN static](./modules/net-vpn-static), [Service Directory](./modules/service-directory), [Secure Web Proxy](./modules/net-swp) +- **networking** - [DNS](./modules/dns), [DNS Response Policy](./modules/dns-response-policy/), [Cloud Endpoints](./modules/endpoints), [address reservation](./modules/net-address), [NAT](./modules/net-cloudnat), [VLAN Attachment](./modules/net-vlan-attachment/), [External Application LB](./modules/net-lb-app-ext/), [External Passthrough Network LB](./modules/net-lb-ext), [External Regional Application Load Balancer](./modules/net-lb-app-ext-regional/), [Firewall policy](./modules/net-firewall-policy), [Internal Application LB](./modules/net-lb-app-int), [Cross-region Internal Application LB](./modules/net-lb-app-int-cross-region), [Internal Passthrough Network LB](./modules/net-lb-int), [Internal Proxy Network LB](./modules/net-lb-proxy-int), [IPSec over Interconnect](./modules/net-ipsec-over-interconnect), [VPC](./modules/net-vpc), [VPC factory](./modules/net-vpc-factory/README.md), [VPC firewall](./modules/net-vpc-firewall), [VPC peering](./modules/net-vpc-peering), [VPN dynamic](./modules/net-vpn-dynamic), [HA VPN](./modules/net-vpn-ha), [VPN static](./modules/net-vpn-static), [Service Directory](./modules/service-directory), [Secure Web Proxy](./modules/net-swp) - **compute** - [VM/VM group](./modules/compute-vm), [MIG](./modules/compute-mig), [COS container](./modules/cloud-config-container/cos-generic-metadata/) (coredns, mysql, onprem, squid), [GKE cluster](./modules/gke-cluster-standard), [GKE hub](./modules/gke-hub), [GKE nodepool](./modules/gke-nodepool), [GCVE private cloud](./modules/gcve-private-cloud) - **data** - [AlloyDB instance](./modules/alloydb), [Analytics Hub](./modules/analytics-hub), [BigQuery dataset](./modules/bigquery-dataset), [Biglake Catalog](./modules/biglake-catalog), [Bigtable instance](./modules/bigtable-instance), [Dataplex](./modules/dataplex), [Dataplex DataScan](./modules/dataplex-datascan), [Cloud SQL instance](./modules/cloudsql-instance), [Spanner instance](./modules/spanner-instance), [Firestore](./modules/firestore), [Data Catalog Policy Tag](./modules/data-catalog-policy-tag), [Data Catalog Tag](./modules/data-catalog-tag), [Data Catalog Tag Template](./modules/data-catalog-tag-template), [Datafusion](./modules/datafusion), [Dataproc](./modules/dataproc), [GCS](./modules/gcs), [Pub/Sub](./modules/pubsub), [Dataform Repository](./modules/dataform-repository/), [Looker Core](./modules/looker-core) - **development** - [API Gateway](./modules/api-gateway), [Apigee](./modules/apigee), [Artifact Registry](./modules/artifact-registry), [Container Registry](./modules/container-registry), [Cloud Source Repository](./modules/source-repository), [Secure Source Manager instance](./modules/secure-source-manager-instance), [Workstation cluster](./modules/workstation-cluster) diff --git a/blueprints/factories/README.md b/blueprints/factories/README.md index 814ad3086..cfe174392 100644 --- a/blueprints/factories/README.md +++ b/blueprints/factories/README.md @@ -74,3 +74,5 @@ The second factory type is implemented as a standalone module that internally re - **projects** - [`project-factory`](../../modules/project-factory/) +- **VPCs** + - [`net-vpc-factory`](../../modules/net-vpc-factory/) diff --git a/modules/README.md b/modules/README.md index 682419f19..05f9d636d 100644 --- a/modules/README.md +++ b/modules/README.md @@ -60,6 +60,7 @@ These modules are used in the examples included in this repository. If you are u - [NAT](./net-cloudnat) - [Service Directory](./service-directory) - [VPC](./net-vpc) +- [VPC factory](./net-vpc-factory) - [VPC firewall](./net-vpc-firewall) - [VPN dynamic](./net-vpn-dynamic) - [VPC peering](./net-vpc-peering) diff --git a/modules/net-vpc-factory/README.md b/modules/net-vpc-factory/README.md new file mode 100644 index 000000000..aa40b3fd8 --- /dev/null +++ b/modules/net-vpc-factory/README.md @@ -0,0 +1,831 @@ +# Networking Factory + +The factory is implemented as a thin data translation layer for 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 service account or a user with a wide set of permissions: + +- roles/resourcemanager.projectCreator (only if creating projects) +- roles/billing.user (only if creating projects) +- roles/resourcemanager.folderViewer (if parent is folder) +- roles/orgpolicy.policyAdmin (at the organization level, in case you're configuring Org Policies) +- roles/compute.xpnAdmin (at the parent folder level, if Shared VPC is used) + +This factory module acts as a wrapper around several core Terraform modules: + +- [`../project`](../project/): For managing GCP projects. +- [`../net-vpc`](../net-vpc): For managing VPC networks, subnets, routes, PSA, etc. +- [`../net-vpc-firewall`](../net-vpc-firewall/): For managing VPC firewall rules. +- [`../net-vpn-ha`](../net-vpn-ha): For managing Cloud HA VPN connections. +- [`../net-cloudnat`](../net-cloudnat/): For managing Cloud NAT instances. +- [`../dns`](../dns): For managing Cloud DNS zones and record sets. + +## TOC + + +- [TOC](#toc) +- [Factory configuration](#factory-configuration) + - [Configuration Methods](#configuration-methods) + - [Cross-Referencing Resources](#cross-referencing-resources) +- [Projects](#projects) +- [VPCs](#vpcs) + - [Subnets](#subnets) + - [Firewall Rules](#firewall-rules) + - [Cloud NAT](#cloud-nat) + - [Routes](#routes) + - [Private Service Access (PSA)](#private-service-access-psa) + - [VPC DNS Policy](#vpc-dns-policy) +- [Connectivity](#connectivity) + - [VPC Peering](#vpc-peering) + - [Cloud VPN](#cloud-vpn) + - [GCP to OnPrem](#gcp-to-onprem) + - [GCP to GCP VPN](#gcp-to-gcp-vpn) + - [Network Connectivity Center (NCC)](#network-connectivity-center-ncc) + - [NCC VPC Spokes](#ncc-vpc-spokes) + - [NCC VPN Spokes](#ncc-vpn-spokes) +- [DNS](#dns) + - [Private Zone](#private-zone) + - [Forwarding Zone](#forwarding-zone) + - [Peering Zone](#peering-zone) + - [Public Zone with DNSSEC](#public-zone-with-dnssec) +- [Variables](#variables) +- [Outputs](#outputs) + + +## Factory configuration + +At a high level, the factory consumes all YAML files in a directory (whose path is `var.factories_config.vpcs`). +Each file defines a single project, which can either be created by the factory or pre-created externally, and all the network infrastructure to be deployed in that project, including + +- Project + - NCC Hubs + - VPCs + - Connectivity (including VPNs, VPC peerings, NCC VPC Spokes and NCC VPN hybrid spokes) + - DNS policies + - DNS zones + - Firewall rules/policies (via a sub-factory) + - NAT (public or private) + - PSA configuration + - Routes (including PBRs) + - Subnets (via a sub-factory) + +### Configuration Methods + +- **YAML Factory:** The primary method is to define project and network configurations within individual YAML files placed in the directory specified by `var.factories_config.vpcs`. This factory approach allows for modular and organized management of complex setups. +- **Direct Variable Input (Discouraged):** The module also defines `var.network_project_config`. While it's *technically possible* to populate this variable directly in a .tfvars file, this is **not the recommended approach**. It bypasses the intended factory pattern and makes managing multiple project configurations cumbersome. The variable definition primarily serves as **documentation** for the structure expected within the YAML configuration files. + +### Cross-Referencing Resources + +A key capability of this factory is establishing relationships between resources defined across different VPCs or projects. This is achieved through cross-referencing using logical keys within the YAML configuration. These keys strictly follow the pattern `project_key/vpc_key/resource_key` for resources like VPCs, VPN gateways, routers, or NCC hubs/groups. During Terraform execution, the factory code translates these logical keys into the actual resource IDs or self-links required by the underlying modules. This mechanism avoids hardcoding resource IDs and enables the declarative definition of complex topologies. It's extensively used in configurations for: + +VPC Peering: Specifying the peer_network (e.g., `peer_network: net-land-01/hub`). + +DNS Peering/Forwarding: Defining peer_network or client_networks for DNS zones (e.g., `client_networks: [net-dev-01/dev-spoke]`). + +GCP-to-GCP HA VPN: Referencing the peer HA VPN gateway using its logical key (e.g., `peer_gateways.default.gcp: net-dev-01/dev-spoke/to-hub`). + +Network Connectivity Center (NCC): Linking spokes to hubs (`ncc_config.hub: net-land-01/hub`) and groups (`ncc_config.group: net-land-01/hub/default`), or auto-accepting spokes in hub groups (`auto_accept: [net-prod-01, net-dev-01]`). + +## Projects + +The `project_config` block within each YAML file implements a large subset of the [project module](../project/) variable interface, which allows for project creation or reuse, services enablement and IAM/Organization policies definition. + +Below is a valid YAML file which simply creates a project, enables a minimal set of services, configures the project as a host project, adds an authoritative +role binding and sets an Organization Policy at the project level: + +```hcl +module "net-vpc-factory" { + source = "./fabric/modules/net-vpc-factory" + billing_account = "123456-789012-345678" + parent_id = "folders/123456789012" + prefix = "myprefix" + factories_config = { vpcs = "recipes/examples" } +} +# tftest files=project-config +``` + +```yaml +project_config: + name: project-config + services: + - compute.googleapis.com + - dns.googleapis.com + - networkmanagement.googleapis.com + - networksecurity.googleapis.com + - servicenetworking.googleapis.com + - stackdriver.googleapis.com + - vpcaccess.googleapis.com + shared_vpc_host_config: + enabled: true + iam: + roles/owner: + - group:prj-admins@example.com + org_policies: + compute.restrictLoadBalancerCreationForTypes: + rules: + - allow: + values: + - EXTERNAL_HTTP_HTTPS +# tftest-file id=project-config path=recipes/examples/project-config.yaml schema=network-project.schema.json +``` + +## VPCs + +The `vpc_config` block within each project's YAML file defines one or more VPCs to be created in that project. It implements a large subset of the [net-vpc module](../net-vpc/) variable interface, allowing for the creation of VPCs, subnets, routes, etc. + +Below is a valid YAML file excerpt with a minimal set of configurations creating a project and two VPCs, and pointing to sub-factories for subnets and firewall rules: + +```hcl +module "net-vpc-factory" { + source = "./fabric/modules/net-vpc-factory" + billing_account = "123456-789012-345678" + parent_id = "folders/123456789012" + prefix = "myprefix" + factories_config = { vpcs = "recipes/examples" } +} +# tftest files=vpc-config +``` + +```yaml +project_config: + name: vpc-config + services: + - compute.googleapis.com +vpc_config: + net-00: + subnets_factory_config: + subnets_folder: data/subnets/net-00 + firewall_factory_config: + rules_folder: data/firewall/net-00 +# tftest-file id=vpc-config path=recipes/examples/vpc-config.yaml schema=network-project.schema.json +``` + +### Subnets + +Subnets are managed via the `vpc_config..subnets_factory_config` parameter within the project's YAML file. This leverages the [`net-vpc`](../net-vpc/) module for subnet creation and management. Please refer to its documentation for detailed information on subnet configuration options and the expected YAML structure within the subnets_folder. + +### Firewall Rules + +Firewall rules are managed via the `vpc_config..firewall_factory_config` parameter, which leverages the [net-vpc-firewall](../net-vpc-firewall/) module for firewall rule creation and management. Please refer to its documentation for detailed information on firewall rule configuration options and the expected YAML structure within the rules_folder. + +### Cloud NAT + +Cloud NAT is configured via the `vpc_config..nat_config` block. This uses the [net-cloudnat](../net-cloudnat/) module. + +```hcl +module "net-vpc-factory" { + source = "./fabric/modules/net-vpc-factory" + billing_account = "123456-789012-345678" + parent_id = "folders/123456789012" + prefix = "myprefix" + factories_config = { vpcs = "recipes/examples" } +} +# tftest files=nat-config +``` + +```yaml +project_config: + name: nat-config + services: + - compute.googleapis.com +vpc_config: + net-00: + delete_default_routes_on_create: false + nat_config: + nat-ew8: + region: europe-west8 + subnets_factory_config: + subnets_folder: data/subnets/foobar + firewall_factory_config: + rules_folder: data/firewall/foobar +# tftest-file id=nat-config path=recipes/examples/nat-config.yaml schema=network-project.schema.json +``` + +### Routes + +Static routes and Policy-Based Routes (PBRs) are configured within the `vpc_config..routes` and `vpc_config..policy_based_routes` blocks. + +```hcl +module "net-vpc-factory" { + source = "./fabric/modules/net-vpc-factory" + billing_account = "123456-789012-345678" + parent_id = "folders/123456789012" + prefix = "myprefix" + factories_config = { vpcs = "recipes/examples" } +} +# tftest files=routes-config +``` + +```yaml +project_config: + name: routes-config + services: + - compute.googleapis.com +vpc_config: + net-00: + routes: + default-internet: + dest_range: 0.0.0.0/0 + next_hop_type: gateway + next_hop: default-internet-gateway + priority: 1000 + policy_based_routes: + pbr-to-nva: + filter: + src_range: 10.0.1.0/24 + dest_range: 0.0.0.0/0 + next_hop_ilb_ip: 10.0.100.5 + priority: 100 +# tftest-file id=routes-config path=recipes/examples/routes-config.yaml schema=network-project.schema.json +``` + +Refer to the [net-vpc](../net-vpc/) module documentation for details on routes and policy_based_routes. + +### Private Service Access (PSA) + +PSA configuration is managed via `vpc_config..psa_config`. + +```hcl +module "net-vpc-factory" { + source = "./fabric/modules/net-vpc-factory" + billing_account = "123456-789012-345678" + parent_id = "folders/123456789012" + prefix = "myprefix" + factories_config = { vpcs = "recipes/examples" } +} +# tftest files=psa-config +``` + +```yaml +project_config: + name: psa-config + services: + - compute.googleapis.com +vpc_config: + net-00: + psa_config: + - ranges: + global-psa-range: 10.100.0.0/24 + peered_domains: ["onprem.example."] +# tftest-file id=psa-config path=recipes/examples/psa-config.yaml schema=network-project.schema.json +``` + +Refer to the [net-vpc](../net-vpc) module documentation for details on psa_config. + +### VPC DNS Policy + +Configure VPC-level DNS behavior, such as enabling inbound query forwarding or logging, using the `dns_policy` block. + +```hcl +module "net-vpc-factory" { + source = "./fabric/modules/net-vpc-factory" + billing_account = "123456-789012-345678" + parent_id = "folders/123456789012" + prefix = "myprefix" + factories_config = { vpcs = "recipes/examples" } +} +# tftest files=vpc-dns-policy +``` + +```yaml +project_config: + name: vpc-dns-policy + services: + - compute.googleapis.com + - dns.googleapis.com +vpc_config: + net-dns-policy-demo: + dns_policy: + inbound: true + logging: true +# tftest-file id=vpc-dns-policy path=recipes/examples/vpc-dns-policy.yaml schema=network-project.schema.json +``` + +## Connectivity + +This module supports various connectivity options: + +### VPC Peering + +The example below implements a Hub-and-spoke design, where spokes are connected to the Hub via VPC Peerings: + +```hcl +module "net-vpc-factory" { + source = "./fabric/modules/net-vpc-factory" + billing_account = "123456-789012-345678" + parent_id = "folders/123456789012" + prefix = "myprefix" + factories_config = { vpcs = "recipes/examples" } +} +# tftest files=peerings-hub,peerings-dev,peerings-prod +``` + +Hub: + +```yaml +project_config: + name: net-land-01 + services: + - compute.googleapis.com +vpc_config: + hub: + peering_config: + to-prod: + peer_network: net-prod-01/prod-spoke + to-dev: + peer_network: net-dev-01/dev-spoke + stack_type: IPV4_IPV6 + +# tftest-file id=peerings-hub path=recipes/examples/net-land-01.yaml schema=network-project.schema.json +``` + +Dev Spoke: + +```yaml +project_config: + name: net-dev-01 + services: + - compute.googleapis.com +vpc_config: + dev-spoke: + peering_config: + to-hub: + peer_network: net-land-01/hub + stack_type: IPV4_IPV6 + +# tftest-file id=peerings-dev path=recipes/examples/net-dev-01.yaml schema=network-project.schema.json +``` + +Prod Spoke: + +```yaml +project_config: + name: net-prod-01 + services: + - compute.googleapis.com +vpc_config: + prod-spoke: + peering_config: + to-hub: + peer_network: net-land-01/hub + +# tftest-file id=peerings-prod path=recipes/examples/net-prod-01.yaml schema=network-project.schema.json +``` + +### Cloud VPN + +The example below implements a Hub-and-spoke design, where spokes are connected to the Hub via HA-VPN: + +#### GCP to OnPrem + +This example demonstrates connecting an on-premises network to GCP via HA-VPN. The `vpn_config` implements the same interface as module [net-vpn-ha](../net-vpn-ha/). + +In this example, the configuration `vpc_config.net-00.routers` creates a router named `vpn-router` in europe-west8, and `vpc_config.net-00.vpn_config.to-onprem.router_config.name` refers to it, using the key `//` (e.g., prj-01/net-00/vpn-router. +Per module `net-vpn-ha`, omitting the `router_config` configuration results in the router being automatically created and managed by the VPN module itself. + +Note that - given the limit of 5 Cloud Routers per VPC per region - we recommend creating routers as required and using them across multiple VPNs/Interconnects by setting and referencing the pre-created router. + +```hcl +module "net-vpc-factory" { + source = "./fabric/modules/net-vpc-factory" + billing_account = "123456-789012-345678" + parent_id = "folders/123456789012" + prefix = "myprefix" + factories_config = { vpcs = "recipes/examples" } +} +# tftest files=vpn-config +``` + +```yaml +project_config: + name: prj-01 + services: + - compute.googleapis.com +vpc_config: + net-00: + routers: + vpn-router: + region: europe-west8 + asn: 64514 + vpn_config: + to-onprem: + region: europe-west8 + peer_gateways: + default: + external: + redundancy_type: SINGLE_IP_INTERNALLY_REDUNDANT + interfaces: + - 8.8.8.8 + router_config: + create: false + name: prj-01/net-00/vpn-router + tunnels: + remote-0: + bgp_peer: + address: 169.254.1.1 + asn: 64513 + bgp_session_range: "169.254.1.2/30" + peer_external_gateway_interface: 0 + shared_secret: "mySecret" + vpn_gateway_interface: 0 + remote-1: + bgp_peer: + address: 169.254.2.1 + asn: 64513 + bgp_session_range: "169.254.2.2/30" + peer_external_gateway_interface: 0 + shared_secret: "mySecret" + vpn_gateway_interface: 1 +# tftest-file id=vpn-config path=recipes/examples/vpn-config.yaml schema=network-project.schema.json +``` + +#### GCP to GCP VPN + +These examples demonstrates VPC to VPC connectivity via HA VPN. +In this examples, project `net-land-01` has a VPC named `hub` and project `net-dev-01` has a VPC named `dev-spoke`; the two VPCs are connected together via HA VPN. In order to do so, the `vpc_config.vpn_config.to-hub.peer_gateways.default.gcp` on each side is configured by cross-referencing the VPN gateway in the other side, whose reference is `//`. + +```hcl +module "net-vpc-factory" { + source = "./fabric/modules/net-vpc-factory" + billing_account = "123456-789012-345678" + parent_id = "folders/123456789012" + prefix = "myprefix" + factories_config = { vpcs = "recipes/examples" } +} +# tftest files=vpn-hub,vpn-spoke +``` + +Hub: + +```yaml +project_config: + name: net-land-01 + services: + - compute.googleapis.com +vpc_config: + hub: + vpn_config: + to-dev: + region: europe-west12 + peer_gateways: + default: + gcp: net-dev-01/dev-spoke/to-hub + router_config: + asn: 64521 + tunnels: + remote-0: + shared_secret: foobar + bgp_peer: + address: 169.254.2.2 + asn: 64520 + bgp_session_range: "169.254.2.1/30" + vpn_gateway_interface: 0 + remote-1: + shared_secret: foobar + bgp_peer: + address: 169.254.2.6 + asn: 64520 + bgp_session_range: "169.254.2.5/30" + vpn_gateway_interface: 1 +# tftest-file id=vpn-hub path=recipes/examples/net-land-01.yaml schema=network-project.schema.json +``` + +Dev Spoke: + +```yaml +project_config: + name: net-dev-01 + services: + - compute.googleapis.com +vpc_config: + dev-spoke: + vpn_config: + to-hub: + region: europe-west12 + peer_gateways: + default: + gcp: net-land-01/hub/to-dev + router_config: + asn: 64520 + tunnels: + remote-0: + shared_secret: foobar + bgp_peer: + address: 169.254.2.1 + asn: 64521 + bgp_session_range: "169.254.2.2/30" + vpn_gateway_interface: 0 + remote-1: + shared_secret: foobar + bgp_peer: + address: 169.254.2.5 + asn: 64521 + bgp_session_range: "169.254.2.6/30" + vpn_gateway_interface: 1 +# tftest-file id=vpn-spoke path=recipes/examples/net-dev-01.yaml schema=network-project.schema.json +``` + +### Network Connectivity Center (NCC) + +#### NCC VPC Spokes + +This example demonstrates how to create an NCC hub, and how to connect multiple spokes to it. On the `net-land-01` project, `ncc_hub_config.groups.default.auto_accept` is configured to automatically accept the listed spoke projects. +On the spokes definition, `vpc_config.$env-spoke.ncc_config` cross references the hub and the default group. + +NCC Hub: + +```hcl +module "net-vpc-factory" { + source = "./fabric/modules/net-vpc-factory" + billing_account = "123456-789012-345678" + parent_id = "folders/123456789012" + prefix = "myprefix" + factories_config = { vpcs = "recipes/examples" } +} +# tftest files=ncc-hub,ncc-dev,ncc-prod +``` + +```yaml +project_config: + name: net-land-01 + services: + - compute.googleapis.com + - networkmanagement.googleapis.com +ncc_hub_config: + name: hub + groups: + default: + auto_accept: + - net-prod-01 + - net-dev-01 +# tftest-file id=ncc-hub path=recipes/examples/net-land-01.yaml schema=network-project.schema.json +``` + +Dev Spoke: + +```yaml +project_config: + name: net-dev-01 + services: + - compute.googleapis.com + - networkmanagement.googleapis.com +vpc_config: + dev-spoke: + ncc_config: + hub: net-land-01/hub +# tftest-file id=ncc-dev path=recipes/examples/net-dev-01.yaml schema=network-project.schema.json +``` + +Prod Spoke: + +```yaml +project_config: + name: net-prod-01 + services: + - compute.googleapis.com + - networkmanagement.googleapis.com +vpc_config: + prod-spoke: + ncc_config: + hub: net-land-01/hub + group: net-land-01/hub/default +# tftest-file id=ncc-prod path=recipes/examples/net-prod-01.yaml schema=network-project.schema.json +``` + +#### NCC VPN Spokes + +This example shows how to connect HA VPN tunnels, typically used for on-premises or other cloud connections, as spokes in an NCC Hub. This enables transitive routing between VPC spokes and the VPN connection via the NCC Hub. + +The key is the `ncc_spoke_config` block within the `vpn_config` definition on the hub project (`net-land-01/hub`). + +```hcl +module "net-vpc-factory" { + source = "./fabric/modules/net-vpc-factory" + billing_account = "123456-789012-345678" + parent_id = "folders/123456789012" + prefix = "myprefix" + factories_config = { vpcs = "recipes/examples" } +} +# tftest files=ncc-vpn +``` + +```yaml +project_config: + name: net-land-01 + services: + - compute.googleapis.com + - networkmanagement.googleapis.com +ncc_hub_config: + name: hub +vpc_config: + hub: + mtu: 1500 + routers: + vpn-router: + region: europe-west8 + asn: 64514 + vpn_config: + to-onprem: + ncc_spoke_config: + hub: net-land-01/hub + region: europe-west8 + peer_gateways: + default: + external: + redundancy_type: SINGLE_IP_INTERNALLY_REDUNDANT + interfaces: + - 8.8.8.8 + router_config: + create: false + name: net-land-01/hub/vpn-router + tunnels: + remote-0: + bgp_peer: + address: 169.254.128.1 + asn: 64513 + bgp_session_range: "169.254.128.2/30" + peer_external_gateway_interface: 0 + shared_secret: "mySecret" + vpn_gateway_interface: 0 + remote-1: + bgp_peer: + address: 169.254.128.5 + asn: 64513 + bgp_session_range: "169.254.128.6/30" + peer_external_gateway_interface: 0 + shared_secret: "mySecret" + vpn_gateway_interface: 1 +# tftest-file id=ncc-vpn path=recipes/examples/net-land-01.yaml schema=network-project.schema.json +``` + +## DNS + +This module supports the creation of forwarding zones, peering zones, public and private zones, and recordsets within these zones. `vpc_config.$vpc.dns_zones` implements a large subset of the [net-dns](../dns/) module variable interface. + +Below a few configuration examples: + +### Private Zone + +This example demonstrates how to create a private zone for the internal.example.com domain, and how to add a record set to it. + +```hcl +module "net-vpc-factory" { + source = "./fabric/modules/net-vpc-factory" + billing_account = "123456-789012-345678" + parent_id = "folders/123456789012" + prefix = "myprefix" + factories_config = { vpcs = "recipes/examples" } +} +# tftest files=dns-private +``` + +```yaml +project_config: + name: net-land-01 + services: + - compute.googleapis.com + - dns.googleapis.com +vpc_config: + hub: + dns_zones: + internal-example: + zone_config: + domain: internal.example.com. + private: + client_networks: + - net-land-01/hub + recordsets: + "A localhost": + records: ["127.0.0.1"] +# tftest-file id=dns-private path=recipes/examples/net-land-01.yaml schema=network-project.schema.json +``` + +### Forwarding Zone + +This example demonstrates how to create a forwarding zone that forwards queries for the `example.com` domain to on-premises DNS servers. + +```hcl +module "net-vpc-factory" { + source = "./fabric/modules/net-vpc-factory" + billing_account = "123456-789012-345678" + parent_id = "folders/123456789012" + prefix = "myprefix" + factories_config = { vpcs = "recipes/examples" } +} +# tftest files=dns-fwd +``` + +```yaml +project_config: + name: net-land-01 + services: + - compute.googleapis.com + - dns.googleapis.com +vpc_config: + hub: + dns_zones: + onprem-fwd: + zone_config: + domain: example.com. + forwarding: + forwarders: + "10.0.0.1": default + "10.0.0.2": default + client_networks: + - net-land-01/hub +# tftest-file id=dns-fwd path=recipes/examples/net-land-01.yaml schema=network-project.schema.json +``` + +### Peering Zone + +This example demonstrates how to create a peering zone that allows the dev-spoke VPC to resolve names in the net-land-01 project's hub VPC. + +```hcl +module "net-vpc-factory" { + source = "./fabric/modules/net-vpc-factory" + billing_account = "123456-789012-345678" + parent_id = "folders/123456789012" + prefix = "myprefix" + factories_config = { vpcs = "recipes/examples" } +} +# tftest files=dns-peering +``` + +```yaml +project_config: + name: net-dev-01 + services: + - compute.googleapis.com + - dns.googleapis.com +vpc_config: + dev-spoke: + dns_zones: + root-peering: + zone_config: + domain: . + peering: + peer_network: net-land-01/hub + client_networks: + - net-dev-01/dev-spoke +# tftest-file id=dns-peering path=recipes/examples/net-land-01.yaml schema=network-project.schema.json +``` + +All of the above combined implements a DNS hub-and-spoke design, where the DNS configuration is mostly centralised in the `net-land-01/hub` VPC, including private zones and forwarding to onprem, and spokes (in this case `net-dev-01/dev-spoke`) "delegate" the root zone (`.`, which is DNS for "*") to the central location via DNS peering. + +### Public Zone with DNSSEC + +This example demonstrates creating a public DNS zone and enabling DNSSEC. + +```hcl +module "net-vpc-factory" { + source = "./fabric/modules/net-vpc-factory" + billing_account = "123456-789012-345678" + parent_id = "folders/123456789012" + prefix = "myprefix" + factories_config = { vpcs = "recipes/examples" } +} +# tftest files=dns-public-dnssec +``` + +```yaml +project_config: + name: net-dns-public + services: + - compute.googleapis.com + - dns.googleapis.com +vpc_config: + hub: + dns_zones: + public-example-com: + zone_config: + domain: my-public-domain.example.com. + public: + dnssec_config: + state: "on" + recordsets: + "A www": + records: ["192.0.2.1"] +# tftest-file id=dns-public-dnssec path=recipes/examples/net-dns-public.yaml schema=network-project.schema.json +``` + +## Variables + +| name | description | type | required | default | +|---|---|:---:|:---:|:---:| +| [billing_account](variables.tf#L17) | Billing account id. | string | ✓ | | +| [parent_id](variables.tf#L374) | Root node for the projects created by the factory. Must be either organizations/XXXXXXXX or folders/XXXXXXXX. | string | ✓ | | +| [prefix](variables.tf#L379) | Prefix used for projects. | string | ✓ | | +| [factories_config](variables.tf#L22) | Configuration for network resource factories. | object({…}) | | {…} | +| [network_project_config](variables.tf#L33) | Consolidated configuration for project, VPCs and their associated resources. | map(object({…})) | | null | + +## Outputs + +| name | description | sensitive | +|---|---|:---:| +| [host_project_ids](outputs.tf#L17) | Network project ids. | | +| [host_project_numbers](outputs.tf#L22) | Network project numbers. | | +| [subnet_ids](outputs.tf#L27) | IDs of subnets created within each VPC. | | +| [subnet_proxy_only_self_links](outputs.tf#L32) | IDs of proxy-only subnets created within each VPC. | | +| [subnet_psc_self_links](outputs.tf#L42) | IDs of PSC subnets created within each VPC. | | +| [vpc_self_links](outputs.tf#L52) | Self-links for the VPCs created on each project. | | +| [vpn_gateway_endpoints](outputs.tf#L57) | External IP Addresses for the GCP VPN gateways. | | + diff --git a/modules/net-vpc-factory/data/firewall/dev-spoke/default-ingress.yaml b/modules/net-vpc-factory/data/firewall/dev-spoke/default-ingress.yaml new file mode 100644 index 000000000..377a74f41 --- /dev/null +++ b/modules/net-vpc-factory/data/firewall/dev-spoke/default-ingress.yaml @@ -0,0 +1,28 @@ +# 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-icmp-allow: + description: "Allow ICMP from anywhere." + rules: + - protocol: icmp + ports: [] + priority: 1000 + ingress-ssh-from-iap-allow: + description: "Allow SSH connections from IAP ranges." + source_ranges: + - 35.235.240.0/20 + rules: + - protocol: tcp + ports: + - 22 + priority: 1001 + ingress-default-deny: + description: "Deny and log any unmatched ingress traffic." + deny: true + priority: 65535 + enable_logging: + include_metadata: false diff --git a/modules/net-vpc-factory/data/firewall/hub/default-ingress.yaml b/modules/net-vpc-factory/data/firewall/hub/default-ingress.yaml new file mode 100644 index 000000000..377a74f41 --- /dev/null +++ b/modules/net-vpc-factory/data/firewall/hub/default-ingress.yaml @@ -0,0 +1,28 @@ +# 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-icmp-allow: + description: "Allow ICMP from anywhere." + rules: + - protocol: icmp + ports: [] + priority: 1000 + ingress-ssh-from-iap-allow: + description: "Allow SSH connections from IAP ranges." + source_ranges: + - 35.235.240.0/20 + rules: + - protocol: tcp + ports: + - 22 + priority: 1001 + ingress-default-deny: + description: "Deny and log any unmatched ingress traffic." + deny: true + priority: 65535 + enable_logging: + include_metadata: false diff --git a/modules/net-vpc-factory/data/firewall/prod-spoke/default-ingress.yaml b/modules/net-vpc-factory/data/firewall/prod-spoke/default-ingress.yaml new file mode 100644 index 000000000..377a74f41 --- /dev/null +++ b/modules/net-vpc-factory/data/firewall/prod-spoke/default-ingress.yaml @@ -0,0 +1,28 @@ +# 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-icmp-allow: + description: "Allow ICMP from anywhere." + rules: + - protocol: icmp + ports: [] + priority: 1000 + ingress-ssh-from-iap-allow: + description: "Allow SSH connections from IAP ranges." + source_ranges: + - 35.235.240.0/20 + rules: + - protocol: tcp + ports: + - 22 + priority: 1001 + ingress-default-deny: + description: "Deny and log any unmatched ingress traffic." + deny: true + priority: 65535 + enable_logging: + include_metadata: false diff --git a/modules/net-vpc-factory/data/subnets/dev-spoke/dev-default.yaml b/modules/net-vpc-factory/data/subnets/dev-spoke/dev-default.yaml new file mode 100644 index 000000000..ebc7037e2 --- /dev/null +++ b/modules/net-vpc-factory/data/subnets/dev-spoke/dev-default.yaml @@ -0,0 +1,8 @@ +# skip boilerplate check + +# yaml-language-server: $schema=../../../schemas/subnet.schema.json + +name: dev-default +region: europe-west8 +ip_cidr_range: 10.68.0.0/24 +description: Default europe-west8 subnet for dev diff --git a/modules/net-vpc-factory/data/subnets/hub/hub-default.yaml b/modules/net-vpc-factory/data/subnets/hub/hub-default.yaml new file mode 100644 index 000000000..e46babe9b --- /dev/null +++ b/modules/net-vpc-factory/data/subnets/hub/hub-default.yaml @@ -0,0 +1,8 @@ +# skip boilerplate check + +# yaml-language-server: $schema=../../../schemas/subnet.schema.json + +name: hub-default +region: europe-west12 +ip_cidr_range: 10.70.0.0/24 +description: Default europe-west12 subnet for hub diff --git a/modules/net-vpc-factory/data/subnets/prod-spoke/prod-default.yaml b/modules/net-vpc-factory/data/subnets/prod-spoke/prod-default.yaml new file mode 100644 index 000000000..50abeb192 --- /dev/null +++ b/modules/net-vpc-factory/data/subnets/prod-spoke/prod-default.yaml @@ -0,0 +1,8 @@ +# skip boilerplate check + +# yaml-language-server: $schema=../../../schemas/subnet.schema.json + +name: prod-default +region: europe-west12 +ip_cidr_range: 10.69.0.0/24 +description: Default europe-west12 subnet for prod diff --git a/modules/net-vpc-factory/factory-dns.tf b/modules/net-vpc-factory/factory-dns.tf new file mode 100644 index 000000000..bf30b580e --- /dev/null +++ b/modules/net-vpc-factory/factory-dns.tf @@ -0,0 +1,87 @@ +/** + * 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. + */ + +# tfdoc:file:description DNS factory. + +locals { + dns_zone_entries = flatten([ + for factory_key, factory_config in local.network_projects : [ + for vpc_key, vpc_config in try(factory_config.vpc_config, {}) : [ + for zone_key, zone in try(vpc_config.dns_zones, {}) : { + key = "${factory_key}/${vpc_key}/${zone_key}" + value = merge( + { + name = replace("${vpc_key}-${zone_key}", "/", "-") + project_id = module.projects[factory_key].id + description = try(zone.description, "Terraform-managed.") + force_destroy = try(zone.force_destroy, null) + iam = try(zone.iam, null) + recordsets = try(zone.recordsets, null) + }, + { + zone_config = merge( + { domain = try(zone.zone_config.domain, null) }, + contains(keys(try(zone.zone_config, {})), "private") ? { + private = { + service_directory_namespace = try(zone.zone_config.private.service_directory_namespace, null) + client_networks = [ + for net in zone.zone_config.private.client_networks : + try(module.vpc[net].self_link, net) + ] + } + } : {}, + contains(keys(try(zone.zone_config, {})), "peering") ? { + peering = { + peer_network = try(module.vpc[zone.zone_config.peering.peer_network].self_link, zone.zone_config.peering.peer_network), + client_networks = [ + for net in zone.zone_config.peering.client_networks : + try(module.vpc[net].self_link, net) + ] + } + } : {}, + contains(keys(try(zone.zone_config, {})), "forwarding") ? { + forwarding = { + forwarders = try(zone.zone_config.forwarding.forwarders, {}), + client_networks = [ + for net in zone.zone_config.forwarding.client_networks : + try(module.vpc[net].self_link, net) + ] + } + } : {} + ) + } + ) + } + ] + ] + ]) + + # Convert the flattened list into a map. + dns_zones = { for entry in local.dns_zone_entries : entry.key => entry.value } +} + +module "dns-zones" { + source = "../dns" + for_each = local.dns_zones + project_id = each.value.project_id + name = each.value.name + description = each.value.description + force_destroy = each.value.force_destroy + iam = each.value.iam + zone_config = each.value.zone_config + recordsets = each.value.recordsets + depends_on = [module.vpc] +} diff --git a/modules/net-vpc-factory/factory-nat.tf b/modules/net-vpc-factory/factory-nat.tf new file mode 100644 index 000000000..eb2e87f6c --- /dev/null +++ b/modules/net-vpc-factory/factory-nat.tf @@ -0,0 +1,62 @@ +/** + * 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. + */ + +# tfdoc:file:description Cloud NAT factory. + +locals { + nat_configs = merge(flatten([ + for factory_key, factory_config in local.network_projects : [ + for vpc_key, vpc_config in try(factory_config.vpc_config, {}) : [ + for nat_key, nat_config in try(vpc_config.nat_config, {}) : { + "${factory_key}/${vpc_key}/${nat_key}" = merge(nat_config, { + name = replace("${vpc_key}/${nat_key}", "/", "-") + project_id = module.projects[factory_key].id + addresses = try(nat_config.addresses, []) + config_port_allocation = try(nat_config.config_port_allocation, {}) + config_source_subnetworks = try(nat_config.config_source_subnetworks, {}) + config_timeouts = try(nat_config.config_timeouts, {}) + endpoint_types = try(nat_config.endpoint_types, null) + logging_filter = try(nat_config.logging_filter, null) + router_asn = try(nat_config.router_asn, null) + router_create = try(nat_config.router_create, true) + router_network = module.vpc["${factory_key}/${vpc_key}"].self_link + rules = try(nat_config.rules, []) + type = try(nat_config.type, "PUBLIC") + }) + } + ] + ] + ])...) +} + +module "nat" { + source = "../net-cloudnat" + for_each = local.nat_configs + project_id = each.value.project_id + name = each.value.name + addresses = each.value.addresses + config_port_allocation = each.value.config_port_allocation + config_source_subnetworks = each.value.config_source_subnetworks + config_timeouts = each.value.config_timeouts + endpoint_types = each.value.endpoint_types + logging_filter = each.value.logging_filter + region = each.value.region + router_asn = each.value.router_asn + router_create = each.value.router_create + router_network = each.value.router_network + rules = each.value.rules + type = each.value.type +} diff --git a/modules/net-vpc-factory/factory-ncc.tf b/modules/net-vpc-factory/factory-ncc.tf new file mode 100644 index 000000000..1a2bdc70a --- /dev/null +++ b/modules/net-vpc-factory/factory-ncc.tf @@ -0,0 +1,139 @@ +/** + * 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. + */ + +# tfdoc:file:description NCC factory. + +locals { + ncc_hubs = { for k, v in local.network_projects : "${k}/${v.ncc_hub_config.name}" => + { + name = v.ncc_hub_config.name + project_id = module.projects[k].id + description = try(v.ncc_hub_config.description, "Terraform-managed") + export_psc = try(v.ncc_hub_config.export_psc, true) + preset_topology = try(v.ncc_hub_config.preset_topology, "MESH") + } + if try(v.ncc_hub_config != null, false) + } + + ncc_groups = merge(flatten([for k, v in local.network_projects : + { + for gk, gv in try(v.ncc_hub_config.groups, {}) : "${k}/${v.ncc_hub_config.name}/${gk}" => + { + name = gk + project = module.projects[k].id + hub = google_network_connectivity_hub.default["${k}/${v.ncc_hub_config.name}"].id + description = try(gv.description, "Terraform-managed") + labels = try(gv.labels, {}) + auto_accept = [for project_key in try(gv.auto_accept, []) : module.projects[project_key].id] + } + } + if try(v.ncc_hub_config != null, false) + ])...) + + ncc_vpn_spokes = merge(flatten([ + for factory_key, factory_config in local.network_projects : [ + for vpc_key, vpc_config in try(factory_config.vpc_config, {}) : [ + for vpn_key, vpn_config in try(vpc_config.vpn_config, {}) : { + "${factory_key}/${vpc_key}/${vpn_key}" = { + name = replace("${factory_key}/${vpc_key}/${vpn_key}", "/", "-") + project_id = module.projects[factory_key].id + hub = google_network_connectivity_hub.default[vpn_config.ncc_spoke_config.hub].id + location = vpn_config.region + description = lookup(vpn_config.ncc_spoke_config, "description", "Terraform-managed.") + labels = lookup(vpn_config.ncc_spoke_config, "labels", {}) + tunnel_self_link = [for t, _ in vpn_config.tunnels : module.vpn-ha["${factory_key}/${vpc_key}/${vpn_key}"].tunnel_self_links[t]] + } + } + if try(vpn_config.ncc_spoke_config != null, false) + ] + ] + ])...) + + ncc_vpc_spokes = merge(flatten([ + for factory_key, factory_config in local.network_projects : { + for vpc_key, vpc_config in try(factory_config.vpc_config, {}) : "${factory_key}/${vpc_key}" => merge(vpc_config.ncc_config, { + project_id = module.projects[factory_key].id + network_self_link = module.vpc["${factory_key}/${vpc_key}"].self_link + labels = try(vpc_config.ncc_config.labels, {}) + hub = google_network_connectivity_hub.default[vpc_config.ncc_config.hub].id + description = try(vpc_config.ncc_config.description, "Terraform-managed") + exclude_export_ranges = try(vpc_config.ncc_config.exclude_export_ranges, null) + include_export_ranges = try(vpc_config.ncc_config.include_export_ranges, null) + group = try(google_network_connectivity_group.default[vpc_config.ncc_config.group].id, null) + }) + if try(vpc_config.ncc_config != null, false) + } + ])...) + +} + +resource "google_network_connectivity_hub" "default" { + for_each = local.ncc_hubs + name = each.value.name + description = each.value.description + export_psc = each.value.export_psc + preset_topology = each.value.preset_topology + project = each.value.project_id +} + +resource "google_network_connectivity_spoke" "vpcs" { + for_each = local.ncc_vpc_spokes + project = each.value.project_id + name = replace(each.key, "/", "-") + location = "global" + description = each.value.description + labels = each.value.labels + hub = each.value.hub + linked_vpc_network { + uri = each.value.network_self_link + exclude_export_ranges = each.value.exclude_export_ranges + include_export_ranges = each.value.include_export_ranges + } + depends_on = [google_network_connectivity_hub.default] + group = each.value.group +} + +resource "google_network_connectivity_group" "default" { + for_each = local.ncc_groups + project = each.value.project + name = each.value.name + hub = each.value.hub + labels = each.value.labels + description = each.value.description + dynamic "auto_accept" { + for_each = try(each.value.auto_accept != null, false) ? [""] : [] + content { + auto_accept_projects = each.value.auto_accept + } + } + depends_on = [google_network_connectivity_hub.default] +} + +resource "google_network_connectivity_spoke" "tunnels" { + for_each = local.ncc_vpn_spokes + project = each.value.project_id + name = each.value.name + location = each.value.location + description = each.value.description + labels = each.value.labels + hub = each.value.hub + linked_vpn_tunnels { + uris = each.value.tunnel_self_link + site_to_site_data_transfer = true + include_import_ranges = ["ALL_IPV4_RANGES"] + } + depends_on = [module.vpn-ha] +} diff --git a/modules/net-vpc-factory/factory-peering.tf b/modules/net-vpc-factory/factory-peering.tf new file mode 100644 index 000000000..5cfab8889 --- /dev/null +++ b/modules/net-vpc-factory/factory-peering.tf @@ -0,0 +1,51 @@ +/** + * 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. + */ + +# tfdoc:file:description Peering factory. + +locals { + peerings = merge(flatten([ + for factory_key, factory_config in local.network_projects : [ + for vpc_key, vpc_config in try(factory_config.vpc_config, {}) : [ + for k, v in try(vpc_config.peering_config, {}) : { + "${factory_key}/${vpc_key}/${k}" = { + project = factory_key + name = replace("${vpc_key}/${k}", "/", "-") + local_network = module.vpc["${factory_key}/${vpc_key}"].self_link + peer_network = module.vpc[v.peer_network].self_link + export_custom_routes = try(v.routes_config.export, true) + import_custom_routes = try(v.routes_config.import, true) + export_subnet_routes_with_public_ip = try(v.routes_config.public_export, null) + import_subnet_routes_with_public_ip = try(v.routes_config.public_import, null) + stack_type = try(v.stack_type, null) + } + } + ] + ] + ])...) +} + +resource "google_compute_network_peering" "default" { + for_each = local.peerings + name = each.value.name + network = each.value.local_network + peer_network = each.value.peer_network + export_custom_routes = each.value.export_custom_routes + import_custom_routes = each.value.import_custom_routes + export_subnet_routes_with_public_ip = each.value.export_subnet_routes_with_public_ip + import_subnet_routes_with_public_ip = each.value.import_subnet_routes_with_public_ip + stack_type = each.value.stack_type +} diff --git a/modules/net-vpc-factory/factory-project.tf b/modules/net-vpc-factory/factory-project.tf new file mode 100644 index 000000000..00f783ba3 --- /dev/null +++ b/modules/net-vpc-factory/factory-project.tf @@ -0,0 +1,53 @@ +/** + * 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. + */ + +# tfdoc:file:description Dedicated project factory. +locals { + + projects = { for k, v in local.network_projects : k => merge( + { + billing_account = try(v.project_config.billing_account, var.billing_account) + prefix = try(v.project_config.prefix, var.prefix) + parent = try(v.project_config.parent, var.parent_id) + shared_vpc_host_config = try(v.project_config.shared_vpc_host_config, null) + iam = try(v.project_config.iam, {}) + iam_bindings = try(v.project_config.iam_bindings, {}) + iam_bindings_additive = try(v.project_config.iam_bindings_additive, {}) + iam_by_principals = try(v.project_config.iam_by_principals, {}) + iam_by_principals_additive = try(v.project_config.iam_by_principals_additive, {}) + services = try(v.project_config.services, []) + org_policies = try(v.project_config.org_policies, {}) + }, + v.project_config) + } +} + +module "projects" { + source = "../project" + for_each = local.projects + billing_account = each.value.billing_account + name = each.value.name + parent = each.value.parent + prefix = each.value.prefix + services = each.value.services + shared_vpc_host_config = each.value.shared_vpc_host_config + iam = each.value.iam + iam_bindings = each.value.iam_bindings + iam_bindings_additive = each.value.iam_bindings_additive + iam_by_principals = each.value.iam_by_principals + iam_by_principals_additive = each.value.iam_by_principals_additive + org_policies = each.value.org_policies +} diff --git a/modules/net-vpc-factory/factory-vpc.tf b/modules/net-vpc-factory/factory-vpc.tf new file mode 100644 index 000000000..59d868e52 --- /dev/null +++ b/modules/net-vpc-factory/factory-vpc.tf @@ -0,0 +1,80 @@ +/** + * 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. + */ + +# tfdoc:file:description VPC and firewall factory. + +locals { + _vpcs_preprocess = [for factory_key, factory_config in local.network_projects : { + for k, v in try(factory_config.vpc_config, {}) : "${factory_key}/${k}" => { + project_id = module.projects[factory_key].id + name = k + auto_create_subnetworks = try(v.auto_create_subnetworks, false) + create_googleapis_routes = try(v.create_googleapis_routes, {}) + delete_default_routes_on_create = try(v.delete_default_routes_on_create, false) + description = try(v.description, "Terraform-managed.") + dns_policy = try(v.dns_policy, {}) + firewall_policy_enforcement_order = try(v.firewall_policy_enforcement_order, "AFTER_CLASSIC_FIREWALL") + ipv6_config = try(v.ipv6_config, {}) + mtu = try(v.mtu, null) + network_attachments = try(v.network_attachments, {}) + policy_based_routes = try(v.policy_based_routes, {}) + psa_config = try(v.psa_config, []) + routes = try(v.routes, {}) + routing_mode = try(v.routing_mode, "GLOBAL") + subnets_factory_config = try(v.subnets_factory_config, {}) + firewall_factory_config = try(v.firewall_factory_config, {}) + peering_config = try(v.peering_config, {}) + vpn_config = try(v.vpn_config, {}) + } + }] + + vpcs = merge( + merge(local._vpcs_preprocess...), + var.network_project_config + ) +} + +module "vpc" { + source = "../net-vpc" + for_each = local.vpcs + project_id = each.value.project_id + name = each.value.name + description = each.value.description + auto_create_subnetworks = each.value.auto_create_subnetworks + create_googleapis_routes = each.value.create_googleapis_routes + delete_default_routes_on_create = each.value.delete_default_routes_on_create + dns_policy = each.value.dns_policy + factories_config = each.value.subnets_factory_config + firewall_policy_enforcement_order = each.value.firewall_policy_enforcement_order + ipv6_config = each.value.ipv6_config + mtu = each.value.mtu + network_attachments = each.value.network_attachments + policy_based_routes = each.value.policy_based_routes + psa_configs = each.value.psa_config + routes = each.value.routes + routing_mode = each.value.routing_mode + depends_on = [module.projects] +} + +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 } + depends_on = [module.vpc] +} diff --git a/modules/net-vpc-factory/factory-vpn.tf b/modules/net-vpc-factory/factory-vpn.tf new file mode 100644 index 000000000..937db9a6a --- /dev/null +++ b/modules/net-vpc-factory/factory-vpn.tf @@ -0,0 +1,111 @@ +/** + * 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. + */ + +# tfdoc:file:description VPN factory. + +locals { + routers = merge(flatten([ + for factory_key, factory_config in local.network_projects : [ + for vpc_key, vpc_config in try(factory_config.vpc_config, {}) : [ + for router_key, router_config in try(vpc_config.routers, {}) : { + "${factory_key}/${vpc_key}/${router_key}" = merge(router_config, { + vpc_self_link = module.vpc["${factory_key}/${vpc_key}"].self_link + project_id = module.projects[factory_key].id + custom_advertise = try(router_config.custom_advertise, {}) + advertise_mode = try(router_config.custom_advertise != null, false) ? "CUSTOM" : "DEFAULT" + advertised_groups = try(router_config.custom_advertise.all_subnets, false) ? ["ALL_SUBNETS"] : [] + keepalive = try(router_config.keepalive, null) + asn = try(router_config.asn, null) + }) + } + ] + ] + ])...) + + vpns = merge(flatten([ + for factory_key, factory_config in local.network_projects : [ + for vpc_key, vpc_config in try(factory_config.vpc_config, {}) : [ + for k, v in try(vpc_config.vpn_config, {}) : { + "${factory_key}/${vpc_key}/${k}" = merge(v, { + vpc_name = module.vpc["${factory_key}/${vpc_key}"].name + vpn_name = replace("${factory_key}/${vpc_key}/${k}", "/", "-") + project_id = module.projects[factory_key].id + }, + { + router_config = merge(v.router_config, + try(v.router_config.create, false) == false && can(v.router_config.name) ? { + name = try(google_compute_router.default[v.router_config.name].name, v.router_config.name) + } : {} + ) + } + ) + } + ] + ] + ])...) +} + +resource "google_compute_router" "default" { + for_each = local.routers + name = replace(each.key, "/", "-") + project = each.value.project_id + region = each.value.region + network = each.value.vpc_self_link + bgp { + advertise_mode = each.value.advertise_mode + advertised_groups = each.value.advertised_groups + dynamic "advertised_ip_ranges" { + for_each = try(each.value.custom_advertise.ip_ranges, {}) + iterator = range + content { + range = range.key + description = range.value + } + } + keepalive_interval = each.value.keepalive + asn = each.value.asn + } +} + +resource "google_compute_ha_vpn_gateway" "default" { + for_each = local.vpns + project = each.value.project_id + region = each.value.region + name = replace(each.key, "/", "-") + network = each.value.vpc_name + stack_type = try(each.value.stack_type, null) + depends_on = [module.vpc] +} + +module "vpn-ha" { + source = "../net-vpn-ha" + for_each = local.vpns + project_id = each.value.project_id + name = replace(each.key, "/", "-") + network = each.value.vpc_name + region = each.value.region + router_config = each.value.router_config + tunnels = each.value.tunnels + vpn_gateway = google_compute_ha_vpn_gateway.default[each.key].id + vpn_gateway_create = null + peer_gateways = { + for k, gw in each.value.peer_gateways : k => { + for gw_type, value in gw : gw_type => ( + gw_type == "gcp" ? try(google_compute_ha_vpn_gateway.default[value].id, value) : value + ) + } + } +} diff --git a/modules/net-vpc-factory/main.tf b/modules/net-vpc-factory/main.tf new file mode 100644 index 000000000..393b92c71 --- /dev/null +++ b/modules/net-vpc-factory/main.tf @@ -0,0 +1,38 @@ +/** + * 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. + */ + +# tfdoc:file:description Read and process YaML factory files and variables. +locals { + _network_factory_path = try( + pathexpand(var.factories_config.vpcs), null + ) + _network_factory_files = try( + fileset(local._network_factory_path, "**/*.yaml"), + [] + ) + + _network_projects_from_files = { + for f in local._network_factory_files : + f => yamldecode(file("${local._network_factory_path}/${f}")) + } + + _network_projects = { + for _, v in local._network_projects_from_files : + v.project_config.name => v + } + + network_projects = merge(local._network_projects, var.network_project_config) +} diff --git a/modules/net-vpc-factory/outputs.tf b/modules/net-vpc-factory/outputs.tf new file mode 100644 index 000000000..3c54eb6f9 --- /dev/null +++ b/modules/net-vpc-factory/outputs.tf @@ -0,0 +1,64 @@ +/** + * 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 "host_project_ids" { + description = "Network project ids." + value = { for k, v in module.projects : k => v.project_id } +} + +output "host_project_numbers" { + description = "Network project numbers." + value = { for k, v in module.projects : k => v.number } +} + +output "subnet_ids" { + description = "IDs of subnets created within each VPC." + value = { for k, v in module.vpc : k => v.subnet_ids } +} + +output "subnet_proxy_only_self_links" { + description = "IDs of proxy-only subnets created within each VPC." + value = { + for k, v in module.vpc : k => + { + for subnet_key, subnet_value in v.subnets_proxy_only : subnet_key => subnet_value.id + } + } +} + +output "subnet_psc_self_links" { + description = "IDs of PSC subnets created within each VPC." + value = { + for k, v in module.vpc : k => + { + for subnet_key, subnet_value in v.subnets_psc : subnet_key => subnet_value.id + } + } +} + +output "vpc_self_links" { + description = "Self-links for the VPCs created on each project." + value = { for k, v in module.vpc : k => v.self_link } +} + +output "vpn_gateway_endpoints" { + description = "External IP Addresses for the GCP VPN gateways." + value = { for k, v in google_compute_ha_vpn_gateway.default : k => + { + for interface_key, interface_value in v.vpn_interfaces : interface_key => interface_value.ip_address + } + } +} diff --git a/modules/net-vpc-factory/recipes/hub-and-spoke-ncc/net-dev-01.yaml b/modules/net-vpc-factory/recipes/hub-and-spoke-ncc/net-dev-01.yaml new file mode 100644 index 000000000..5029be709 --- /dev/null +++ b/modules/net-vpc-factory/recipes/hub-and-spoke-ncc/net-dev-01.yaml @@ -0,0 +1,58 @@ +# 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/network-project.schema.json + +project_config: + name: net-dev-01 + services: + - container.googleapis.com + - compute.googleapis.com + - dns.googleapis.com + - iap.googleapis.com + - networkmanagement.googleapis.com + - networksecurity.googleapis.com + - servicenetworking.googleapis.com + - stackdriver.googleapis.com + - vpcaccess.googleapis.com + shared_vpc_host_config: + enabled: true +vpc_config: + dev-spoke: + delete_default_routes_on_create: false + mtu: 1500 + nat_config: + nat-ew8: + region: europe-west8 + dns_zones: + root-peering: + zone_config: + domain: . + peering: + peer_network: net-land-01/hub + client_networks: + - net-dev-01/dev-spoke + subnets_factory_config: + subnets_folder: data/subnets/dev-spoke + firewall_factory_config: + rules_folder: data/firewall/dev-spoke + routes: + gateway: + dest_range: "8.8.8.8/32" + priority: 100 + next_hop_type: "gateway" + next_hop: "default-internet-gateway" + ncc_config: + hub: net-land-01/hub + group: net-land-01/hub/default diff --git a/modules/net-vpc-factory/recipes/hub-and-spoke-ncc/net-land-01.yaml b/modules/net-vpc-factory/recipes/hub-and-spoke-ncc/net-land-01.yaml new file mode 100644 index 000000000..9d5c1562f --- /dev/null +++ b/modules/net-vpc-factory/recipes/hub-and-spoke-ncc/net-land-01.yaml @@ -0,0 +1,109 @@ +# 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/network-project.schema.json + +project_config: + name: net-land-01 + services: + - container.googleapis.com + - compute.googleapis.com + - dns.googleapis.com + - iap.googleapis.com + - networkmanagement.googleapis.com + - networksecurity.googleapis.com + - servicenetworking.googleapis.com + - stackdriver.googleapis.com + - vpcaccess.googleapis.com + shared_vpc_host_config: + enabled: true +ncc_hub_config: + name: hub + groups: + default: + auto_accept: + - net-prod-01 + - net-dev-01 +vpc_config: + test: + delete_default_routes_on_create: false + mtu: 1500 + auto_create_subnetworks: true + hub: + delete_default_routes_on_create: false + mtu: 1500 + routers: + vpn-router: + region: europe-west8 + asn: 64514 + dns_zones: + onprem-fwd: + zone_config: + domain: . + forwarding: + forwarders: + "8.8.8.8": default + "1.1.1.1": default + client_networks: + - net-land-01/hub + dot-test: + zone_config: + domain: test. + private: + client_networks: + - net-land-01/hub + recordsets: + "A localhost": + records: ["127.0.0.1"] + subnets_factory_config: + subnets_folder: data/subnets/hub + firewall_factory_config: + rules_folder: data/firewall/hub + routes: + gateway: + dest_range: "8.8.8.8/32" + priority: 100 + next_hop_type: "gateway" + next_hop: "default-internet-gateway" + vpn_config: + to-onprem: + ncc_spoke_config: + hub: net-land-01/hub + region: europe-west8 + peer_gateways: + default: + external: + redundancy_type: SINGLE_IP_INTERNALLY_REDUNDANT + interfaces: + - 8.8.8.8 + router_config: + create: false + name: net-land-01/hub/vpn-router + tunnels: + remote-0: + bgp_peer: + address: 169.254.128.1 + asn: 64513 + bgp_session_range: "169.254.128.2/30" + peer_external_gateway_interface: 0 + shared_secret: "mySecret" + vpn_gateway_interface: 0 + remote-1: + bgp_peer: + address: 169.254.128.5 + asn: 64513 + bgp_session_range: "169.254.128.6/30" + peer_external_gateway_interface: 0 + shared_secret: "mySecret" + vpn_gateway_interface: 1 diff --git a/modules/net-vpc-factory/recipes/hub-and-spoke-ncc/net-prod-01.yaml b/modules/net-vpc-factory/recipes/hub-and-spoke-ncc/net-prod-01.yaml new file mode 100644 index 000000000..ba37a7393 --- /dev/null +++ b/modules/net-vpc-factory/recipes/hub-and-spoke-ncc/net-prod-01.yaml @@ -0,0 +1,58 @@ +# 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/network-project.schema.json + +project_config: + name: net-prod-01 + services: + - container.googleapis.com + - compute.googleapis.com + - dns.googleapis.com + - iap.googleapis.com + - networkmanagement.googleapis.com + - networksecurity.googleapis.com + - servicenetworking.googleapis.com + - stackdriver.googleapis.com + - vpcaccess.googleapis.com + shared_vpc_host_config: + enabled: true +vpc_config: + prod-spoke: + delete_default_routes_on_create: false + mtu: 1500 + nat_config: + nat-ew8: + region: europe-west8 + dns_zones: + root-peering: + zone_config: + domain: . + peering: + peer_network: net-land-01/hub + client_networks: + - net-prod-01/prod-spoke + subnets_factory_config: + subnets_folder: data/subnets/prod-spoke + firewall_factory_config: + rules_folder: data/firewall/prod-spoke + routes: + gateway: + dest_range: "8.8.8.8/32" + priority: 100 + next_hop_type: "gateway" + next_hop: "default-internet-gateway" + ncc_config: + hub: net-land-01/hub + group: net-land-01/hub/default diff --git a/modules/net-vpc-factory/recipes/hub-and-spoke-peering/net-dev-01.yaml b/modules/net-vpc-factory/recipes/hub-and-spoke-peering/net-dev-01.yaml new file mode 100644 index 000000000..4128c4374 --- /dev/null +++ b/modules/net-vpc-factory/recipes/hub-and-spoke-peering/net-dev-01.yaml @@ -0,0 +1,58 @@ +# 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/network-project.schema.json + +project_config: + name: net-dev-01 + services: + - container.googleapis.com + - compute.googleapis.com + - dns.googleapis.com + - iap.googleapis.com + - networkmanagement.googleapis.com + - networksecurity.googleapis.com + - servicenetworking.googleapis.com + - stackdriver.googleapis.com + - vpcaccess.googleapis.com + shared_vpc_host_config: + enabled: true +vpc_config: + dev-spoke: + delete_default_routes_on_create: false + mtu: 1500 + nat_config: + nat-ew8: + region: europe-west8 + dns_zones: + root-peering: + zone_config: + domain: . + peering: + peer_network: net-land-01/hub + client_networks: + - net-dev-01/dev-spoke + subnets_factory_config: + subnets_folder: data/subnets/dev-spoke + firewall_factory_config: + rules_folder: data/firewall/dev-spoke + routes: + gateway: + dest_range: "8.8.8.8/32" + priority: 100 + next_hop_type: "gateway" + next_hop: "default-internet-gateway" + peering_config: + to-hub: + peer_network: net-land-01/hub diff --git a/modules/net-vpc-factory/recipes/hub-and-spoke-peering/net-land-01.yaml b/modules/net-vpc-factory/recipes/hub-and-spoke-peering/net-land-01.yaml new file mode 100644 index 000000000..84a99a393 --- /dev/null +++ b/modules/net-vpc-factory/recipes/hub-and-spoke-peering/net-land-01.yaml @@ -0,0 +1,113 @@ +# 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/network-project.schema.json + +project_config: + name: net-land-01 + services: + - container.googleapis.com + - compute.googleapis.com + - dns.googleapis.com + - iap.googleapis.com + - networkmanagement.googleapis.com + - networksecurity.googleapis.com + - servicenetworking.googleapis.com + - stackdriver.googleapis.com + - vpcaccess.googleapis.com + shared_vpc_host_config: + enabled: true +vpc_config: + test: + delete_default_routes_on_create: false + mtu: 1500 + auto_create_subnetworks: true + peering_config: + to-hub: + peer_network: net-land-01/hub + hub: + delete_default_routes_on_create: false + mtu: 1500 + nat_config: + nat-ew8: + region: europe-west8 + routers: + vpn-router: + region: europe-west8 + asn: 64514 + dns_zones: + onprem-fwd: + zone_config: + domain: . + forwarding: + forwarders: + "8.8.8.8": default + "1.1.1.1": default + client_networks: + - net-land-01/hub + dot-test: + zone_config: + domain: test. + private: + client_networks: + - net-land-01/hub + recordsets: + "A localhost": + records: ["127.0.0.1"] + subnets_factory_config: + subnets_folder: data/subnets/hub + firewall_factory_config: + rules_folder: data/firewall/hub + routes: + gateway: + dest_range: "8.8.8.8/32" + priority: 100 + next_hop_type: "gateway" + next_hop: "default-internet-gateway" + peering_config: + to-prod: + peer_network: net-prod-01/prod-spoke + to-dev: + peer_network: net-dev-01/dev-spoke + to-test: + peer_network: net-land-01/test + vpn_config: + to-onprem: + region: europe-west8 + peer_gateways: + default: + external: + redundancy_type: SINGLE_IP_INTERNALLY_REDUNDANT + interfaces: + - 8.8.8.8 + router_config: + create: false + name: net-land-01/hub/vpn-router + tunnels: + remote-0: + bgp_peer: + address: 169.254.128.1 + asn: 64513 + bgp_session_range: "169.254.128.2/30" + peer_external_gateway_interface: 0 + shared_secret: "mySecret" + vpn_gateway_interface: 0 + remote-1: + bgp_peer: + address: 169.254.128.5 + asn: 64513 + bgp_session_range: "169.254.128.6/30" + peer_external_gateway_interface: 0 + shared_secret: "mySecret" + vpn_gateway_interface: 1 diff --git a/modules/net-vpc-factory/recipes/hub-and-spoke-peering/net-prod-01.yaml b/modules/net-vpc-factory/recipes/hub-and-spoke-peering/net-prod-01.yaml new file mode 100644 index 000000000..33039b243 --- /dev/null +++ b/modules/net-vpc-factory/recipes/hub-and-spoke-peering/net-prod-01.yaml @@ -0,0 +1,58 @@ +# 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/network-project.schema.json + +project_config: + name: net-prod-01 + services: + - container.googleapis.com + - compute.googleapis.com + - dns.googleapis.com + - iap.googleapis.com + - networkmanagement.googleapis.com + - networksecurity.googleapis.com + - servicenetworking.googleapis.com + - stackdriver.googleapis.com + - vpcaccess.googleapis.com + shared_vpc_host_config: + enabled: true +vpc_config: + prod-spoke: + delete_default_routes_on_create: false + mtu: 1500 + nat_config: + nat-ew8: + region: europe-west8 + dns_zones: + root-peering: + zone_config: + domain: . + peering: + peer_network: net-land-01/hub + client_networks: + - net-prod-01/prod-spoke + subnets_factory_config: + subnets_folder: data/subnets/prod-spoke + firewall_factory_config: + rules_folder: data/firewall/prod-spoke + routes: + gateway: + dest_range: "8.8.8.8/32" + priority: 100 + next_hop_type: "gateway" + next_hop: "default-internet-gateway" + peering_config: + to-hub: + peer_network: net-land-01/hub diff --git a/modules/net-vpc-factory/recipes/hub-and-spoke-vpn/net-dev-01.yaml b/modules/net-vpc-factory/recipes/hub-and-spoke-vpn/net-dev-01.yaml new file mode 100644 index 000000000..b26f3a005 --- /dev/null +++ b/modules/net-vpc-factory/recipes/hub-and-spoke-vpn/net-dev-01.yaml @@ -0,0 +1,83 @@ +# 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/network-project.schema.json + +project_config: + name: net-dev-01 + services: + - container.googleapis.com + - compute.googleapis.com + - dns.googleapis.com + - iap.googleapis.com + - networkmanagement.googleapis.com + - networksecurity.googleapis.com + - servicenetworking.googleapis.com + - stackdriver.googleapis.com + - vpcaccess.googleapis.com + shared_vpc_host_config: + enabled: true +vpc_config: + dev-spoke: + delete_default_routes_on_create: false + mtu: 1500 + nat_config: + nat-ew8: + region: europe-west8 + routers: + vpn-router: + region: europe-west8 + asn: 64520 + dns_zones: + root-peering: + zone_config: + domain: . + peering: + peer_network: net-land-01/hub + client_networks: + - net-dev-01/dev-spoke + subnets_factory_config: + subnets_folder: data/subnets/dev-spoke + firewall_factory_config: + rules_folder: data/firewall/dev-spoke + routes: + gateway: + dest_range: "8.8.8.8/32" + priority: 100 + next_hop_type: "gateway" + next_hop: "default-internet-gateway" + vpn_config: + to-hub: + region: europe-west8 + peer_gateways: + default: + gcp: net-land-01/hub/to-dev + router_config: + create: false + name: net-dev-01/dev-spoke/vpn-router + tunnels: + remote-0: + shared_secret: foobar + bgp_peer: + address: 169.254.2.1 + asn: 64514 + bgp_session_range: "169.254.2.2/30" + vpn_gateway_interface: 0 + remote-1: + shared_secret: foobar + bgp_peer: + address: 169.254.2.5 + asn: 64514 + bgp_session_range: "169.254.2.6/30" + vpn_gateway_interface: 1 diff --git a/modules/net-vpc-factory/recipes/hub-and-spoke-vpn/net-land-01.yaml b/modules/net-vpc-factory/recipes/hub-and-spoke-vpn/net-land-01.yaml new file mode 100644 index 000000000..effe127a7 --- /dev/null +++ b/modules/net-vpc-factory/recipes/hub-and-spoke-vpn/net-land-01.yaml @@ -0,0 +1,153 @@ +# 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/network-project.schema.json + +project_config: + name: net-land-01 + services: + - container.googleapis.com + - compute.googleapis.com + - dns.googleapis.com + - iap.googleapis.com + - networkmanagement.googleapis.com + - networksecurity.googleapis.com + - servicenetworking.googleapis.com + - stackdriver.googleapis.com + - vpcaccess.googleapis.com + shared_vpc_host_config: + enabled: true +vpc_config: + test: + delete_default_routes_on_create: false + mtu: 1500 + auto_create_subnetworks: true + hub: + delete_default_routes_on_create: false + mtu: 1500 + nat_config: + nat-ew8: + region: europe-west8 + routers: + vpn-router: + region: europe-west8 + asn: 64514 + custom_advertise: + all_subnets: false + ip_ranges: + "10.0.0.0/8": "rfc1918_10" + dns_zones: + onprem-fwd: + zone_config: + domain: . + forwarding: + forwarders: + "8.8.8.8": default + "1.1.1.1": default + client_networks: + - net-land-01/hub + dot-test: + zone_config: + domain: test. + private: + client_networks: + - net-land-01/hub + recordsets: + "A localhost": + records: ["127.0.0.1"] + subnets_factory_config: + subnets_folder: data/subnets/hub + firewall_factory_config: + rules_folder: data/firewall/hub + routes: + gateway: + dest_range: "8.8.8.8/32" + priority: 100 + next_hop_type: "gateway" + next_hop: "default-internet-gateway" + vpn_config: + to-onprem: + region: europe-west8 + peer_gateways: + default: + external: + redundancy_type: SINGLE_IP_INTERNALLY_REDUNDANT + interfaces: + - 8.8.8.8 + router_config: + create: false + name: net-land-01/hub/vpn-router + tunnels: + remote-0: + bgp_peer: + address: 169.254.128.1 + asn: 64513 + bgp_session_range: "169.254.128.2/30" + peer_external_gateway_interface: 0 + shared_secret: "mySecret" + vpn_gateway_interface: 0 + remote-1: + bgp_peer: + address: 169.254.128.5 + asn: 64513 + bgp_session_range: "169.254.128.6/30" + peer_external_gateway_interface: 0 + shared_secret: "mySecret" + vpn_gateway_interface: 1 + to-dev: + region: europe-west8 + peer_gateways: + default: + gcp: net-dev-01/dev-spoke/to-hub + router_config: + create: false + name: net-land-01/hub/vpn-router + tunnels: + remote-0: + shared_secret: foobar + bgp_peer: + address: 169.254.2.2 + asn: 64520 + bgp_session_range: "169.254.2.1/30" + vpn_gateway_interface: 0 + remote-1: + shared_secret: foobar + bgp_peer: + address: 169.254.2.6 + asn: 64520 + bgp_session_range: "169.254.2.5/30" + vpn_gateway_interface: 1 + to-prod: + region: europe-west8 + peer_gateways: + default: + gcp: net-prod-01/prod-spoke/to-hub + router_config: + create: false + name: net-land-01/hub/vpn-router + tunnels: + remote-0: + shared_secret: foobar + bgp_peer: + address: 169.254.3.2 + asn: 64523 + bgp_session_range: "169.254.3.1/30" + vpn_gateway_interface: 0 + remote-1: + shared_secret: foobar + bgp_peer: + address: 169.254.3.6 + asn: 64523 + bgp_session_range: "169.254.3.5/30" + vpn_gateway_interface: 1 diff --git a/modules/net-vpc-factory/recipes/hub-and-spoke-vpn/net-prod-01.yaml b/modules/net-vpc-factory/recipes/hub-and-spoke-vpn/net-prod-01.yaml new file mode 100644 index 000000000..4b835e70d --- /dev/null +++ b/modules/net-vpc-factory/recipes/hub-and-spoke-vpn/net-prod-01.yaml @@ -0,0 +1,83 @@ +# 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/network-project.schema.json + +project_config: + name: net-prod-01 + services: + - container.googleapis.com + - compute.googleapis.com + - dns.googleapis.com + - iap.googleapis.com + - networkmanagement.googleapis.com + - networksecurity.googleapis.com + - servicenetworking.googleapis.com + - stackdriver.googleapis.com + - vpcaccess.googleapis.com + shared_vpc_host_config: + enabled: true +vpc_config: + prod-spoke: + delete_default_routes_on_create: false + mtu: 1500 + nat_config: + nat-ew8: + region: europe-west8 + routers: + vpn-router: + region: europe-west8 + asn: 64523 + dns_zones: + root-peering: + zone_config: + domain: . + peering: + peer_network: net-land-01/hub + client_networks: + - net-prod-01/prod-spoke + subnets_factory_config: + subnets_folder: data/subnets/prod-spoke + firewall_factory_config: + rules_folder: data/firewall/prod-spoke + routes: + gateway: + dest_range: "8.8.8.8/32" + priority: 100 + next_hop_type: "gateway" + next_hop: "default-internet-gateway" + vpn_config: + to-hub: + region: europe-west8 + peer_gateways: + default: + gcp: net-land-01/hub/to-prod + router_config: + create: false + name: net-prod-01/prod-spoke/vpn-router + tunnels: + remote-0: + shared_secret: foobar + bgp_peer: + address: 169.254.3.1 + asn: 64514 + bgp_session_range: "169.254.3.2/30" + vpn_gateway_interface: 0 + remote-1: + shared_secret: foobar + bgp_peer: + address: 169.254.3.5 + asn: 64514 + bgp_session_range: "169.254.3.6/30" + vpn_gateway_interface: 1 diff --git a/modules/net-vpc-factory/recipes/only-projects/net-dev-01.yaml b/modules/net-vpc-factory/recipes/only-projects/net-dev-01.yaml new file mode 100644 index 000000000..fe2f82580 --- /dev/null +++ b/modules/net-vpc-factory/recipes/only-projects/net-dev-01.yaml @@ -0,0 +1,28 @@ +# 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/network-project.schema.json + +project_config: + name: net-dev-01 + services: + - container.googleapis.com + - compute.googleapis.com + - dns.googleapis.com + - iap.googleapis.com + - networkmanagement.googleapis.com + - networksecurity.googleapis.com + - servicenetworking.googleapis.com + - stackdriver.googleapis.com + - vpcaccess.googleapis.com diff --git a/modules/net-vpc-factory/recipes/only-projects/net-land-01.yaml b/modules/net-vpc-factory/recipes/only-projects/net-land-01.yaml new file mode 100644 index 000000000..55d0cbaec --- /dev/null +++ b/modules/net-vpc-factory/recipes/only-projects/net-land-01.yaml @@ -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. + +# yaml-language-server: $schema=../../schemas/network-project.schema.json + +# This file creates an empty project, and exists to keep consistency with the other recipes. + +project_config: + name: net-land-01 + services: + - container.googleapis.com + - compute.googleapis.com + - dns.googleapis.com + - iap.googleapis.com + - networkmanagement.googleapis.com + - networksecurity.googleapis.com + - servicenetworking.googleapis.com + - stackdriver.googleapis.com + - vpcaccess.googleapis.com diff --git a/modules/net-vpc-factory/recipes/only-projects/net-prod-01.yaml b/modules/net-vpc-factory/recipes/only-projects/net-prod-01.yaml new file mode 100644 index 000000000..72b7d0145 --- /dev/null +++ b/modules/net-vpc-factory/recipes/only-projects/net-prod-01.yaml @@ -0,0 +1,28 @@ +# 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/network-project.schema.json + +project_config: + name: net-prod-01 + services: + - container.googleapis.com + - compute.googleapis.com + - dns.googleapis.com + - iap.googleapis.com + - networkmanagement.googleapis.com + - networksecurity.googleapis.com + - servicenetworking.googleapis.com + - stackdriver.googleapis.com + - vpcaccess.googleapis.com diff --git a/modules/net-vpc-factory/recipes/separate-envs/net-dev-01.yaml b/modules/net-vpc-factory/recipes/separate-envs/net-dev-01.yaml new file mode 100644 index 000000000..d3e1b13f9 --- /dev/null +++ b/modules/net-vpc-factory/recipes/separate-envs/net-dev-01.yaml @@ -0,0 +1,72 @@ +# 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/network-project.schema.json + +project_config: + name: net-dev-01 + services: + - container.googleapis.com + - compute.googleapis.com + - dns.googleapis.com + - iap.googleapis.com + - networkmanagement.googleapis.com + - networksecurity.googleapis.com + - servicenetworking.googleapis.com + - stackdriver.googleapis.com + - vpcaccess.googleapis.com + shared_vpc_host_config: + enabled: true +vpc_config: + dev-spoke: + delete_default_routes_on_create: false + mtu: 1500 + nat_config: + nat-ew8: + region: europe-west8 + routers: + vpn-router: + region: europe-west8 + asn: 64514 + subnets_factory_config: + subnets_folder: data/subnets/dev-spoke + firewall_factory_config: + rules_folder: data/firewall/dev-spoke + routes: + gateway: + dest_range: "8.8.8.8/32" + priority: 100 + next_hop_type: "gateway" + next_hop: "default-internet-gateway" + vpn_config: + to-onprem: + region: europe-west8 + peer_gateways: + default: + external: + redundancy_type: SINGLE_IP_INTERNALLY_REDUNDANT + interfaces: + - 8.8.8.8 + router_config: + create: false + name: net-dev-01/dev-spoke/vpn-router + tunnels: + remote-0: + bgp_peer: + address: 169.254.1.1 + asn: 64513 + bgp_session_range: "169.254.1.2/30" + peer_external_gateway_interface: 0 + shared_secret: "mySecret" + vpn_gateway_interface: 0 diff --git a/modules/net-vpc-factory/recipes/separate-envs/net-land-01.yaml b/modules/net-vpc-factory/recipes/separate-envs/net-land-01.yaml new file mode 100644 index 000000000..55d0cbaec --- /dev/null +++ b/modules/net-vpc-factory/recipes/separate-envs/net-land-01.yaml @@ -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. + +# yaml-language-server: $schema=../../schemas/network-project.schema.json + +# This file creates an empty project, and exists to keep consistency with the other recipes. + +project_config: + name: net-land-01 + services: + - container.googleapis.com + - compute.googleapis.com + - dns.googleapis.com + - iap.googleapis.com + - networkmanagement.googleapis.com + - networksecurity.googleapis.com + - servicenetworking.googleapis.com + - stackdriver.googleapis.com + - vpcaccess.googleapis.com diff --git a/modules/net-vpc-factory/recipes/separate-envs/net-prod-01.yaml b/modules/net-vpc-factory/recipes/separate-envs/net-prod-01.yaml new file mode 100644 index 000000000..4da9151e0 --- /dev/null +++ b/modules/net-vpc-factory/recipes/separate-envs/net-prod-01.yaml @@ -0,0 +1,72 @@ +# 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/network-project.schema.json + +project_config: + name: net-prod-01 + services: + - container.googleapis.com + - compute.googleapis.com + - dns.googleapis.com + - iap.googleapis.com + - networkmanagement.googleapis.com + - networksecurity.googleapis.com + - servicenetworking.googleapis.com + - stackdriver.googleapis.com + - vpcaccess.googleapis.com + shared_vpc_host_config: + enabled: true +vpc_config: + prod-spoke: + delete_default_routes_on_create: false + mtu: 1500 + nat_config: + nat-ew8: + region: europe-west8 + routers: + vpn-router: + region: europe-west8 + asn: 64514 + subnets_factory_config: + subnets_folder: data/subnets/prod-spoke + firewall_factory_config: + rules_folder: data/firewall/prod-spoke + routes: + gateway: + dest_range: "8.8.8.8/32" + priority: 100 + next_hop_type: "gateway" + next_hop: "default-internet-gateway" + vpn_config: + to-onprem: + region: europe-west8 + peer_gateways: + default: + external: + redundancy_type: SINGLE_IP_INTERNALLY_REDUNDANT + interfaces: + - 8.8.8.8 + router_config: + create: false + name: net-prod-01/prod-spoke/vpn-router + tunnels: + remote-0: + bgp_peer: + address: 169.254.1.1 + asn: 64513 + bgp_session_range: "169.254.1.2/30" + peer_external_gateway_interface: 0 + shared_secret: "mySecret" + vpn_gateway_interface: 0 diff --git a/modules/net-vpc-factory/schemas/network-project.schema.json b/modules/net-vpc-factory/schemas/network-project.schema.json new file mode 100644 index 000000000..130f42868 --- /dev/null +++ b/modules/net-vpc-factory/schemas/network-project.schema.json @@ -0,0 +1,1479 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "Network Project Configuration (Single)", + "description": "Schema for a single network project configuration defined at the root.", + "type": "object", + "additionalProperties": false, + "required": [ + "project_config" + ], + "properties": { + "project_config": { + "$ref": "#/$defs/projectConfig" + }, + "ncc_hub_config": { + "$ref": "#/$defs/nccHubConfig" + }, + "vpc_config": { + "$ref": "#/$defs/vpcConfigMap" + } + }, + "$defs": { + "projectConfig": { + "type": "object", + "additionalProperties": false, + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + }, + "prefix": { + "type": "string" + }, + "parent": { + "type": "string" + }, + "billing_account": { + "type": "string" + }, + "deletion_policy": { + "type": "string", + "enum": [ + "DELETE", + "ABANDON" + ] + }, + "default_service_account": { + "type": "string", + "enum": [ + "deprovision", + "disable", + "keep" + ] + }, + "auto_create_network": { + "type": "boolean" + }, + "project_create": { + "type": "boolean" + }, + "shared_vpc_host_config": { + "type": "object", + "additionalProperties": false, + "required": [ + "enabled" + ], + "properties": { + "enabled": { + "type": "boolean" + }, + "service_projects": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "services": { + "type": "array", + "items": { + "type": "string", + "pattern": "^[a-z-]+\\.googleapis\\.com$" + } + }, + "org_policies": { + "$ref": "#/$defs/orgPolicies" + }, + "metric_scopes": { + "type": "array", + "items": { + "type": "string" + } + }, + "iam": { + "$ref": "#/$defs/iam" + }, + "iam_bindings": { + "$ref": "#/$defs/iamBindings" + }, + "iam_bindings_additive": { + "$ref": "#/$defs/iamBindingsAdditive" + }, + "iam_by_principals": { + "$ref": "#/$defs/iamByPrincipals" + }, + "iam_by_principals_additive": { + "$ref": "#/$defs/iamByPrincipals" + } + } + }, + "nccHubConfig": { + "type": "object", + "additionalProperties": false, + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + }, + "preset_topology": { + "type": "string", + "enum": [ + "MESH", + "STAR", + "PLANETARY" + ] + }, + "export_psc": { + "type": "boolean" + }, + "groups": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9_-]+$": { + "$ref": "#/$defs/nccGroup" + } + }, + "additionalProperties": false + } + } + }, + "nccGroup": { + "type": "object", + "additionalProperties": false, + "properties": { + "labels": { + "$ref": "#/$defs/stringMap" + }, + "description": { + "type": "string" + }, + "auto_accept": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "vpcConfigMap": { + "type": "object", + "patternProperties": { + "^[a-z0-9-]+$": { + "$ref": "#/$defs/vpcConfigEntry" + } + }, + "additionalProperties": false + }, + "vpcConfigEntry": { + "type": "object", + "additionalProperties": false, + "properties": { + "auto_create_subnetworks": { + "type": "boolean" + }, + "create_googleapis_routes": { + "type": "object", + "additionalProperties": false, + "properties": { + "private": { + "type": "boolean" + }, + "private-6": { + "type": "boolean" + }, + "restricted": { + "type": "boolean" + }, + "restricted-6": { + "type": "boolean" + } + } + }, + "delete_default_routes_on_create": { + "type": "boolean" + }, + "description": { + "type": "string" + }, + "dns_policy": { + "type": "object", + "additionalProperties": false, + "properties": { + "inbound": { + "type": "boolean" + }, + "logging": { + "type": "boolean" + }, + "outbound": { + "type": "object", + "additionalProperties": false, + "properties": { + "private_ns": { + "type": "array", + "items": { + "type": "string" + } + }, + "public_ns": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, + "dns_zones": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9-]+$": { + "$ref": "#/$defs/dnsZone" + } + }, + "additionalProperties": false + }, + "firewall_policy_enforcement_order": { + "type": "string", + "enum": [ + "AFTER_CLASSIC_FIREWALL", + "BEFORE_CLASSIC_FIREWALL" + ] + }, + "ipv6_config": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable_ula_internal": { + "type": "boolean" + }, + "internal_range": { + "type": "string" + } + } + }, + "mtu": { + "type": "number" + }, + "nat_config": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9-]+$": { + "$ref": "#/$defs/natConfig" + } + }, + "additionalProperties": false + }, + "network_attachments": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9-]+$": { + "$ref": "#/$defs/networkAttachment" + } + }, + "additionalProperties": false + }, + "policy_based_routes": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9-]+$": { + "$ref": "#/$defs/policyBasedRoute" + } + }, + "additionalProperties": false + }, + "psa_config": { + "type": "array", + "items": { + "$ref": "#/$defs/psaConfig" + } + }, + "routers": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9-]+$": { + "$ref": "#/$defs/routerConfig" + } + }, + "additionalProperties": false + }, + "routes": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9-]+$": { + "$ref": "#/$defs/routeConfig" + } + }, + "additionalProperties": false + }, + "routing_mode": { + "type": "string", + "enum": [ + "GLOBAL", + "REGIONAL" + ] + }, + "subnets_factory_config": { + "type": "object", + "additionalProperties": false, + "properties": { + "context": { + "type": "object", + "additionalProperties": false, + "properties": { + "regions": { + "$ref": "#/$defs/stringMap" + } + } + }, + "subnets_folder": { + "type": "string" + } + } + }, + "firewall_factory_config": { + "type": "object", + "additionalProperties": false, + "properties": { + "cidr_tpl_file": { + "type": "string" + }, + "rules_folder": { + "type": "string" + } + } + }, + "vpn_config": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9-]+$": { + "$ref": "#/$defs/vpnConfig" + } + }, + "additionalProperties": false + }, + "peering_config": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9-]+$": { + "$ref": "#/$defs/peeringConfig" + } + }, + "additionalProperties": false + }, + "ncc_config": { + "$ref": "#/$defs/vpcNccConfig" + } + } + }, + "dnsZone": { + "type": "object", + "additionalProperties": false, + "required": [ + "zone_config" + ], + "properties": { + "force_destroy": { + "type": "boolean" + }, + "description": { + "type": "string" + }, + "iam": { + "$ref": "#/$defs/iam" + }, + "zone_config": { + "$ref": "#/$defs/dnsZoneConfig" + }, + "recordsets": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9_. -]+$": { + "$ref": "#/$defs/dnsRecordSet" + } + }, + "additionalProperties": false + } + } + }, + "dnsZoneConfig": { + "type": "object", + "additionalProperties": false, + "required": [ + "domain" + ], + "properties": { + "domain": { + "type": "string" + }, + "forwarding": { + "type": "object", + "additionalProperties": false, + "properties": { + "forwarders": { + "$ref": "#/$defs/stringMap" + }, + "client_networks": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "peering": { + "type": "object", + "additionalProperties": false, + "required": [ + "peer_network" + ], + "properties": { + "client_networks": { + "type": "array", + "items": { + "type": "string" + } + }, + "peer_network": { + "type": "string" + } + } + }, + "public": { + "type": "object", + "additionalProperties": false, + "properties": { + "dnssec_config": { + "$ref": "#/$defs/dnssecConfig" + }, + "enable_logging": { + "type": "boolean" + } + } + }, + "private": { + "type": "object", + "additionalProperties": false, + "properties": { + "client_networks": { + "type": "array", + "items": { + "type": "string" + } + }, + "service_directory_namespace": { + "type": "string" + } + } + } + } + }, + "dnssecConfig": { + "type": "object", + "additionalProperties": false, + "required": [ + "state" + ], + "properties": { + "non_existence": { + "type": "string", + "enum": [ + "nsec", + "nsec3" + ] + }, + "state": { + "type": "string", + "enum": [ + "on", + "off", + "transfer" + ] + }, + "key_signing_key": { + "$ref": "#/$defs/dnsKeySpec" + }, + "zone_signing_key": { + "$ref": "#/$defs/dnsKeySpec" + } + } + }, + "dnsKeySpec": { + "type": "object", + "additionalProperties": false, + "required": [ + "algorithm", + "key_length" + ], + "properties": { + "algorithm": { + "type": "string", + "enum": [ + "rsasha1", + "rsasha256", + "rsasha512", + "ecdsap256sha256", + "ecdsap384sha384" + ] + }, + "key_length": { + "type": "number" + } + } + }, + "dnsRecordSet": { + "type": "object", + "additionalProperties": false, + "properties": { + "ttl": { + "type": "number" + }, + "records": { + "type": "array", + "items": { + "type": "string" + } + }, + "geo_routing": { + "type": "array", + "items": { + "$ref": "#/$defs/dnsGeoRoutingRule" + } + }, + "wrr_routing": { + "type": "array", + "items": { + "$ref": "#/$defs/dnsWrrRoutingRule" + } + } + } + }, + "dnsGeoRoutingRule": { + "type": "object", + "additionalProperties": false, + "required": [ + "location" + ], + "properties": { + "location": { + "type": "string" + }, + "records": { + "type": "array", + "items": { + "type": "string" + } + }, + "health_checked_targets": { + "type": "array", + "items": { + "$ref": "#/$defs/dnsHealthCheckedTarget" + } + } + } + }, + "dnsHealthCheckedTarget": { + "type": "object", + "additionalProperties": false, + "required": [ + "load_balancer_type", + "ip_address", + "port", + "ip_protocol", + "network_url", + "project" + ], + "properties": { + "load_balancer_type": { + "type": "string" + }, + "ip_address": { + "type": "string" + }, + "port": { + "type": "string" + }, + "ip_protocol": { + "type": "string" + }, + "network_url": { + "type": "string" + }, + "project": { + "type": "string" + }, + "region": { + "type": "string" + } + } + }, + "dnsWrrRoutingRule": { + "type": "object", + "additionalProperties": false, + "required": [ + "weight", + "records" + ], + "properties": { + "weight": { + "type": "number" + }, + "records": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "natConfig": { + "type": "object", + "additionalProperties": false, + "required": [ + "region" + ], + "properties": { + "region": { + "type": "string" + }, + "router_create": { + "type": "boolean" + }, + "router_name": { + "type": "string" + }, + "router_network": { + "type": "string" + }, + "router_asn": { + "type": "number" + }, + "type": { + "type": "string", + "enum": [ + "PUBLIC", + "PRIVATE" + ] + }, + "addresses": { + "type": "array", + "items": { + "type": "string" + } + }, + "endpoint_types": { + "type": "array", + "items": { + "type": "string", + "enum": [ + "ENDPOINT_TYPE_VM", + "ENDPOINT_TYPE_SWG", + "ENDPOINT_TYPE_MANAGED_PROXY_LB" + ] + } + }, + "logging_filter": { + "type": "string", + "enum": [ + "ERRORS_ONLY", + "TRANSLATIONS_ONLY", + "ALL" + ] + }, + "config_port_allocation": { + "type": "object", + "additionalProperties": false, + "properties": { + "enable_endpoint_independent_mapping": { + "type": "boolean" + }, + "enable_dynamic_port_allocation": { + "type": "boolean" + }, + "min_ports_per_vm": { + "type": "number" + }, + "max_ports_per_vm": { + "type": "number" + } + } + }, + "config_source_subnetworks": { + "type": "object", + "additionalProperties": false, + "properties": { + "all": { + "type": "boolean" + }, + "primary_ranges_only": { + "type": "boolean" + }, + "subnetworks": { + "type": "array", + "items": { + "$ref": "#/$defs/natSourceSubnetwork" + } + } + } + }, + "config_timeouts": { + "type": "object", + "additionalProperties": false, + "properties": { + "icmp": { + "type": "number" + }, + "tcp_established": { + "type": "number" + }, + "tcp_time_wait": { + "type": "number" + }, + "tcp_transitory": { + "type": "number" + }, + "udp": { + "type": "number" + } + } + }, + "rules": { + "type": "array", + "items": { + "$ref": "#/$defs/natRule" + } + } + } + }, + "natSourceSubnetwork": { + "type": "object", + "additionalProperties": false, + "required": [ + "self_link" + ], + "properties": { + "self_link": { + "type": "string" + }, + "all_ranges": { + "type": "boolean" + }, + "primary_range": { + "type": "boolean" + }, + "secondary_ranges": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "natRule": { + "type": "object", + "additionalProperties": false, + "required": [ + "match" + ], + "properties": { + "description": { + "type": "string" + }, + "match": { + "type": "string" + }, + "source_ips": { + "type": "array", + "items": { + "type": "string" + } + }, + "source_ranges": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "networkAttachment": { + "type": "object", + "additionalProperties": false, + "required": [ + "subnet" + ], + "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" + } + } + } + }, + "policyBasedRoute": { + "type": "object", + "additionalProperties": false, + "properties": { + "description": { + "type": "string" + }, + "labels": { + "$ref": "#/$defs/stringMap" + }, + "priority": { + "type": "number" + }, + "next_hop_ilb_ip": { + "type": "string" + }, + "use_default_routing": { + "type": "boolean" + }, + "filter": { + "type": "object", + "additionalProperties": false, + "properties": { + "ip_protocol": { + "type": "string" + }, + "dest_range": { + "type": "string" + }, + "src_range": { + "type": "string" + } + } + }, + "target": { + "type": "object", + "additionalProperties": false, + "properties": { + "interconnect_attachment": { + "type": "string" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + }, + "psaConfig": { + "type": "object", + "additionalProperties": false, + "required": [ + "ranges" + ], + "properties": { + "deletion_policy": { + "type": "string", + "enum": [ + "delete", + "abandon" + ] + }, + "ranges": { + "$ref": "#/$defs/stringMap" + }, + "export_routes": { + "type": "boolean" + }, + "import_routes": { + "type": "boolean" + }, + "peered_domains": { + "type": "array", + "items": { + "type": "string" + } + }, + "range_prefix": { + "type": "string" + }, + "service_producer": { + "type": "string" + } + } + }, + "routerConfig": { + "type": "object", + "additionalProperties": false, + "required": [ + "region" + ], + "properties": { + "region": { + "type": "string" + }, + "asn": { + "type": "number" + }, + "custom_advertise": { + "$ref": "#/$defs/customAdvertiseConfig" + }, + "keepalive": { + "type": "number" + }, + "name": { + "type": "string" + } + } + }, + "routeConfig": { + "type": "object", + "additionalProperties": false, + "required": [ + "dest_range", + "next_hop_type", + "next_hop" + ], + "properties": { + "description": { + "type": "string" + }, + "dest_range": { + "type": "string" + }, + "next_hop_type": { + "type": "string" + }, + "next_hop": { + "type": "string" + }, + "priority": { + "type": "number" + }, + "tags": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "vpnConfig": { + "type": "object", + "additionalProperties": false, + "required": [ + "region", + "peer_gateways", + "router_config", + "tunnels" + ], + "properties": { + "region": { + "type": "string" + }, + "ncc_spoke_config": { + "type": "object", + "additionalProperties": false, + "properties": { + "hub": { + "type": "string" + }, + "description": { + "type": "string" + }, + "labels": { + "$ref": "#/$defs/stringMap" + } + } + }, + "peer_gateways": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9-]+$": { + "$ref": "#/$defs/peerGateway" + } + }, + "additionalProperties": false + }, + "router_config": { + "$ref": "#/$defs/vpnRouterConfig" + }, + "stack_type": { + "type": "string", + "enum": [ + "IPV4_ONLY", + "IPV4_IPV6" + ] + }, + "tunnels": { + "type": "object", + "patternProperties": { + "^[a-zA-Z0-9-]+$": { + "$ref": "#/$defs/vpnTunnel" + } + }, + "additionalProperties": false + } + } + }, + "peerGateway": { + "type": "object", + "additionalProperties": false, + "properties": { + "external": { + "$ref": "#/$defs/externalPeerGateway" + }, + "gcp": { + "type": "string" + } + } + }, + "externalPeerGateway": { + "type": "object", + "additionalProperties": false, + "required": [ + "redundancy_type", + "interfaces" + ], + "properties": { + "redundancy_type": { + "type": "string", + "enum": [ + "SINGLE_IP_INTERNALLY_REDUNDANT", + "TWO_IPS_REDUNDANCY", + "FOUR_IPS_REDUNDANCY" + ] + }, + "interfaces": { + "type": "array", + "items": { + "type": "string" + } + }, + "description": { + "type": "string" + }, + "name": { + "type": "string" + } + } + }, + "vpnRouterConfig": { + "type": "object", + "additionalProperties": false, + "properties": { + "asn": { + "type": "number" + }, + "create": { + "type": "boolean" + }, + "custom_advertise": { + "$ref": "#/$defs/customAdvertiseConfig" + }, + "keepalive": { + "type": "number" + }, + "name": { + "type": "string" + }, + "override_name": { + "type": "string" + } + } + }, + "vpnTunnel": { + "type": "object", + "additionalProperties": false, + "required": [ + "bgp_peer", + "bgp_session_range", + "vpn_gateway_interface" + ], + "properties": { + "bgp_peer": { + "$ref": "#/$defs/bgpPeerConfig" + }, + "bgp_session_range": { + "type": "string" + }, + "ike_version": { + "type": "number", + "enum": [ + 1, + 2 + ] + }, + "name": { + "type": "string" + }, + "peer_external_gateway_interface": { + "type": "number" + }, + "peer_router_interface_name": { + "type": "string" + }, + "peer_gateway": { + "type": "string" + }, + "router": { + "type": "string" + }, + "shared_secret": { + "type": "string" + }, + "vpn_gateway_interface": { + "type": "number" + } + } + }, + "bgpPeerConfig": { + "type": "object", + "additionalProperties": false, + "required": [ + "address", + "asn" + ], + "properties": { + "address": { + "type": "string" + }, + "asn": { + "type": "number" + }, + "route_priority": { + "type": "number" + }, + "custom_advertise": { + "$ref": "#/$defs/customAdvertiseConfig" + }, + "md5_authentication_key": { + "type": "object", + "additionalProperties": false, + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + }, + "key": { + "type": "string" + } + } + }, + "ipv6": { + "type": "object", + "additionalProperties": false, + "properties": { + "nexthop_address": { + "type": "string" + }, + "peer_nexthop_address": { + "type": "string" + } + } + }, + "name": { + "type": "string" + } + } + }, + "customAdvertiseConfig": { + "type": "object", + "additionalProperties": false, + "required": [ + "all_subnets" + ], + "properties": { + "all_subnets": { + "type": "boolean" + }, + "ip_ranges": { + "$ref": "#/$defs/stringMap" + } + } + }, + "peeringConfig": { + "type": "object", + "additionalProperties": false, + "required": [ + "peer_network" + ], + "properties": { + "peer_network": { + "type": "string" + }, + "routes_config": { + "type": "object", + "additionalProperties": false, + "properties": { + "export": { + "type": "boolean" + }, + "import": { + "type": "boolean" + }, + "public_export": { + "type": "boolean" + }, + "public_import": { + "type": "boolean" + } + } + }, + "stack_type": { + "type": "string", + "enum": [ + "IPV4_ONLY", + "IPV4_IPV6" + ] + } + } + }, + "vpcNccConfig": { + "type": "object", + "additionalProperties": false, + "required": [ + "hub" + ], + "properties": { + "hub": { + "type": "string" + }, + "description": { + "type": "string" + }, + "labels": { + "$ref": "#/$defs/stringMap" + }, + "group": { + "type": "string" + }, + "exclude_export_ranges": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + }, + "include_export_ranges": { + "type": [ + "array", + "null" + ], + "items": { + "type": "string" + } + } + } + }, + "stringMap": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "condition": { + "type": "object", + "additionalProperties": false, + "required": [ + "expression", + "title" + ], + "properties": { + "expression": { + "type": "string" + }, + "title": { + "type": "string" + }, + "description": { + "type": "string" + } + } + }, + "principalPattern": { + "type": "string", + "pattern": "^(?:domain:|group:|serviceAccount:|user:|principal:|principalSet:|[a-z])" + }, + "rolePattern": { + "type": "string", + "pattern": "^roles/" + }, + "iam": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^roles/": { + "type": "array", + "items": { + "$ref": "#/$defs/principalPattern" + } + } + } + }, + "iamBindings": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[a-z0-9_-]+$": { + "type": "object", + "additionalProperties": false, + "required": [ + "members", + "role" + ], + "properties": { + "members": { + "type": "array", + "items": { + "$ref": "#/$defs/principalPattern" + } + }, + "role": { + "$ref": "#/$defs/rolePattern" + }, + "condition": { + "$ref": "#/$defs/condition" + } + } + } + } + }, + "iamBindingsAdditive": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[a-z0-9_-]+$": { + "type": "object", + "additionalProperties": false, + "required": [ + "member", + "role" + ], + "properties": { + "member": { + "$ref": "#/$defs/principalPattern" + }, + "role": { + "$ref": "#/$defs/rolePattern" + }, + "condition": { + "$ref": "#/$defs/condition" + } + } + } + } + }, + "iamByPrincipals": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^(?:domain:|group:|serviceAccount:|user:|principal:|principalSet:|[a-z])": { + "type": "array", + "items": { + "$ref": "#/$defs/rolePattern" + } + } + } + }, + "orgPolicies": { + "type": "object", + "additionalProperties": false, + "patternProperties": { + "^[a-z]+\\.": { + "$ref": "#/$defs/orgPolicyConfig" + } + } + }, + "orgPolicyConfig": { + "type": "object", + "properties": { + "inherit_from_parent": { + "type": "boolean" + }, + "reset": { + "type": "boolean" + }, + "rules": { + "type": "array", + "items": { + "$ref": "#/$defs/orgPolicyRule" + } + } + } + }, + "orgPolicyRule": { + "type": "object", + "additionalProperties": false, + "properties": { + "allow": { + "$ref": "#/$defs/orgPolicyRuleAllowDeny" + }, + "deny": { + "$ref": "#/$defs/orgPolicyRuleAllowDeny" + }, + "enforce": { + "type": "boolean" + }, + "condition": { + "type": "object", + "additionalProperties": false, + "properties": { + "description": { + "type": "string" + }, + "expression": { + "type": "string" + }, + "location": { + "type": "string" + }, + "title": { + "type": "string" + } + } + } + } + }, + "orgPolicyRuleAllowDeny": { + "type": "object", + "additionalProperties": false, + "properties": { + "all": { + "type": "boolean" + }, + "values": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } +} \ No newline at end of file diff --git a/modules/net-vpc-factory/variables.tf b/modules/net-vpc-factory/variables.tf new file mode 100644 index 000000000..efab6f08c --- /dev/null +++ b/modules/net-vpc-factory/variables.tf @@ -0,0 +1,382 @@ +/** + * 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 "billing_account" { + description = "Billing account id." + type = string +} + +variable "factories_config" { + description = "Configuration for network resource factories." + type = object({ + vpcs = optional(string, "recipes/hub-and-spoke-ncc") + firewall_policy_name = optional(string, "net-default") + }) + default = { + vpcs = "recipes/hub-and-spoke-ncc" + } +} + +variable "network_project_config" { + description = "Consolidated configuration for project, VPCs and their associated resources." + type = map(object({ + project_config = object({ + name = string + prefix = optional(string) + parent = optional(string) + billing_account = optional(string) + deletion_policy = optional(string, "DELETE") + default_service_account = optional(string, "keep") + auto_create_network = optional(bool, false) + project_create = optional(bool, true) + shared_vpc_host_config = optional(object({ + enabled = bool + service_projects = optional(list(string), []) + })) + services = optional(list(string), ) + org_policies = optional(map(object({ + inherit_from_parent = optional(bool) + reset = optional(bool) + rules = optional(list(object({ + allow = optional(object({ + all = optional(bool) + values = optional(list(string)) + })) + deny = optional(object({ + all = optional(bool) + values = optional(list(string)) + })) + enforce = optional(bool) + condition = optional(object({ + description = optional(string) + expression = optional(string) + location = optional(string) + title = optional(string) + }), {}) + })), ) + })), {}) + metric_scopes = optional(list(string), []) + iam = optional(map(list(string)), {}) + iam_bindings = optional(map(object({ + members = list(string) + role = 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) + })) + })), {}) + iam_by_principals_additive = optional(map(list(string)), {}) + iam_by_principals = optional(map(list(string)), {}) + }) + ncc_hub_config = optional(object({ + name = string + description = optional(string, "Terraform-managed.") + preset_topology = optional(string, "MESH") + export_psc = optional(bool, true) + groups = optional(map(object({ + labels = optional(map(string)) + description = optional(string, "Terraform-managed.") + auto_accept = optional(list(string), []) + }))) + })) + vpc_config = optional(map(object({ + auto_create_subnetworks = optional(bool, false) + create_googleapis_routes = optional(object({ + private = optional(bool, true) + private-6 = optional(bool, false) + restricted = optional(bool, true) + restricted-6 = optional(bool, false) + }), {}) + delete_default_routes_on_create = optional(bool, false) + description = optional(string, "Terraform-managed.") + dns_policy = optional(object({ + inbound = optional(bool) + logging = optional(bool) + outbound = optional(object({ + private_ns = list(string) + public_ns = list(string) + })) + })) + dns_zones = optional(map(object({ + force_destroy = optional(bool) + description = optional(string, "Terraform managed.") + iam = optional(map(list(string)), {}) + zone_config = object({ + domain = string + forwarding = optional(object({ + forwarders = optional(map(string), {}) + client_networks = optional(list(string), ) + })) + peering = optional(object({ + client_networks = optional(list(string), ) + peer_network = string + })) + public = optional(object({ + dnssec_config = optional(object({ + non_existence = optional(string, "nsec3") + state = string + key_signing_key = optional(object( + { algorithm = string, key_length = number }), + { algorithm = "rsasha256", key_length = 2048 } + ) + zone_signing_key = optional(object( + { algorithm = string, key_length = number }), + { algorithm = "rsasha256", key_length = 1024 } + ) + })) + enable_logging = optional(bool, false) + })) + private = optional(object({ + client_networks = optional(list(string), ) + service_directory_namespace = optional(string) + })) + }) + recordsets = optional(map(object({ + ttl = optional(number, 300) + records = optional(list(string)) + geo_routing = optional(list(object({ + location = string + records = optional(list(string)) + health_checked_targets = optional(list(object({ + load_balancer_type = string + ip_address = string + port = string + ip_protocol = string + network_url = string + project = string + region = optional(string) + }))) + }))) + wrr_routing = optional(list(object({ + weight = number + records = list(string) + }))) + })), {}) + }))) + firewall_policy_enforcement_order = optional(string, "AFTER_CLASSIC_FIREWALL") + ipv6_config = optional(object({ + enable_ula_internal = optional(bool) + internal_range = optional(string) + }), {}) + mtu = optional(number) + name = string + nat_config = optional(map(object({ + region = string + router_create = optional(bool, true) + router_name = optional(string) + router_network = optional(string) + router_asn = optional(number) + type = optional(string, "PUBLIC") + addresses = optional(list(string), []) + endpoint_types = optional(list(string)) + logging_filter = optional(string) + config_port_allocation = optional(object({ + enable_endpoint_independent_mapping = optional(bool, true) + enable_dynamic_port_allocation = optional(bool, false) + min_ports_per_vm = optional(number) + max_ports_per_vm = optional(number, 65536) + }), {}) + config_source_subnetworks = optional(object({ + all = optional(bool, true) + primary_ranges_only = optional(bool) + subnetworks = optional(list(object({ + self_link = string + all_ranges = optional(bool, true) + primary_range = optional(bool, false) + secondary_ranges = optional(list(string)) + })), []) + }), {}) + config_timeouts = optional(object({ + icmp = optional(number) + tcp_established = optional(number) + tcp_time_wait = optional(number) + tcp_transitory = optional(number) + udp = optional(number) + }), {}) + rules = optional(list(object({ + description = optional(string) + match = string + source_ips = optional(list(string)) + source_ranges = optional(list(string)) + })), []) + + }))) + network_attachments = optional(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)) + })), {}) + policy_based_routes = optional(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)) + }), {}) + })), {}) + psa_config = optional(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") + })), []) + routers = optional(map(object({ + region = string + asn = optional(number) + custom_advertise = optional(object({ + all_subnets = bool + ip_ranges = map(string) + })) + keepalive = optional(number) + name = optional(string) + }))) + routes = optional(map(object({ + description = optional(string, "Terraform-managed.") + dest_range = string + next_hop_type = string + next_hop = string + priority = optional(number) + tags = optional(list(string)) + })), {}) + routing_mode = optional(string, "GLOBAL") + subnets_factory_config = optional(object({ + context = optional(object({ + regions = optional(map(string), {}) + }), {}) + subnets_folder = optional(string) + }), {}) + firewall_factory_config = optional(object({ + cidr_tpl_file = optional(string) + rules_folder = optional(string) + }), {}) + vpn_config = optional(map(object({ + #TOFIX: are we even using name? + name = string + region = string + ncc_spoke_config = optional(object({ + hub = string + description = string + labels = map(string) + })) + peer_gateways = map(object({ + external = optional(object({ + redundancy_type = string + interfaces = list(string) + description = optional(string, "Terraform managed external VPN gateway") + name = optional(string) + })) + gcp = optional(string) + })) + router_config = object({ + asn = optional(number) + create = optional(bool, true) + custom_advertise = optional(object({ + all_subnets = bool + ip_ranges = map(string) + })) + keepalive = optional(number) + name = optional(string) + override_name = optional(string) + }) + stack_type = optional(string) + tunnels = map(object({ + bgp_peer = object({ + address = string + asn = number + route_priority = optional(number, 1000) + custom_advertise = optional(object({ + all_subnets = bool + ip_ranges = map(string) + })) + md5_authentication_key = optional(object({ + name = string + key = optional(string) + })) + ipv6 = optional(object({ + nexthop_address = optional(string) + peer_nexthop_address = optional(string) + })) + name = optional(string) + }) + # each BGP session on the same Cloud Router must use a unique /30 CIDR + # from the 169.254.0.0/16 block. + bgp_session_range = string + ike_version = optional(number, 2) + name = optional(string) + peer_external_gateway_interface = optional(number) + peer_router_interface_name = optional(string) + peer_gateway = optional(string, "default") + router = optional(string) + shared_secret = optional(string) + vpn_gateway_interface = number + })) + })), {}) + peering_config = optional(map(object({ + peer_network = string + routes_config = optional(object({ + export = optional(bool, true) + import = optional(bool, true) + public_export = optional(bool) + public_import = optional(bool) + } + ), {}) + stack_type = optional(string) + })), {}) + ncc_config = optional(object({ + hub = string + description = optional(string, "Terraform-managed.") + labels = optional(map(string)) + group = optional(string) + exclude_export_ranges = optional(list(string), null) + include_export_ranges = optional(list(string), null) + })) + }))) + })) + default = null +} + +variable "parent_id" { + description = "Root node for the projects created by the factory. Must be either organizations/XXXXXXXX or folders/XXXXXXXX." + type = string +} + +variable "prefix" { + description = "Prefix used for projects." + type = string +} diff --git a/tests/modules/net_vpc_factory/common.tfvars b/tests/modules/net_vpc_factory/common.tfvars new file mode 100644 index 000000000..4abab596e --- /dev/null +++ b/tests/modules/net_vpc_factory/common.tfvars @@ -0,0 +1,3 @@ +billing_account = "123456-789012-345678" +parent_id = "folders/123456789012" +prefix = "myprefix" diff --git a/tests/modules/net_vpc_factory/ncc.tfvars b/tests/modules/net_vpc_factory/ncc.tfvars new file mode 100644 index 000000000..bd034cf87 --- /dev/null +++ b/tests/modules/net_vpc_factory/ncc.tfvars @@ -0,0 +1,2 @@ +factories_config = { vpcs = "recipes/hub-and-spoke-ncc" } + diff --git a/tests/modules/net_vpc_factory/ncc.yaml b/tests/modules/net_vpc_factory/ncc.yaml new file mode 100644 index 000000000..30a070167 --- /dev/null +++ b/tests/modules/net_vpc_factory/ncc.yaml @@ -0,0 +1,40 @@ +# Copyright 2023 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. + +counts: + google_compute_external_vpn_gateway: 1 + google_compute_firewall: 9 + google_compute_ha_vpn_gateway: 1 + google_compute_network: 4 + google_compute_route: 15 + google_compute_router: 3 + google_compute_router_interface: 2 + google_compute_router_nat: 2 + google_compute_router_peer: 2 + google_compute_shared_vpc_host_project: 3 + google_compute_subnetwork: 3 + google_compute_vpn_tunnel: 2 + google_dns_managed_zone: 4 + google_dns_policy: 4 + google_dns_record_set: 1 + google_network_connectivity_group: 1 + google_network_connectivity_hub: 1 + google_network_connectivity_spoke: 3 + google_project: 3 + google_project_iam_member: 21 + google_project_service: 27 + google_project_service_identity: 21 + modules: 17 + random_id: 3 + resources: 136 diff --git a/tests/modules/net_vpc_factory/only_projects.tfvars b/tests/modules/net_vpc_factory/only_projects.tfvars new file mode 100644 index 000000000..8e5157506 --- /dev/null +++ b/tests/modules/net_vpc_factory/only_projects.tfvars @@ -0,0 +1,2 @@ +factories_config = { vpcs = "recipes/only-projects" } + diff --git a/tests/modules/net_vpc_factory/only_projects.yaml b/tests/modules/net_vpc_factory/only_projects.yaml new file mode 100644 index 000000000..e67e4ff6d --- /dev/null +++ b/tests/modules/net_vpc_factory/only_projects.yaml @@ -0,0 +1,21 @@ +# Copyright 2023 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. + +counts: + google_project: 3 + google_project_iam_member: 21 + google_project_service: 27 + google_project_service_identity: 21 + modules: 3 + resources: 72 diff --git a/tests/modules/net_vpc_factory/peering.tfvars b/tests/modules/net_vpc_factory/peering.tfvars new file mode 100644 index 000000000..1209220ef --- /dev/null +++ b/tests/modules/net_vpc_factory/peering.tfvars @@ -0,0 +1,2 @@ +factories_config = { vpcs = "recipes/hub-and-spoke-peering" } + diff --git a/tests/modules/net_vpc_factory/peering.yaml b/tests/modules/net_vpc_factory/peering.yaml new file mode 100644 index 000000000..fd734ffcd --- /dev/null +++ b/tests/modules/net_vpc_factory/peering.yaml @@ -0,0 +1,38 @@ +# Copyright 2023 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. + +counts: + google_compute_external_vpn_gateway: 1 + google_compute_firewall: 9 + google_compute_ha_vpn_gateway: 1 + google_compute_network: 4 + google_compute_network_peering: 6 + google_compute_route: 15 + google_compute_router: 4 + google_compute_router_interface: 2 + google_compute_router_nat: 3 + google_compute_router_peer: 2 + google_compute_shared_vpc_host_project: 3 + google_compute_subnetwork: 3 + google_compute_vpn_tunnel: 2 + google_dns_managed_zone: 4 + google_dns_policy: 4 + google_dns_record_set: 1 + google_project: 3 + google_project_iam_member: 21 + google_project_service: 27 + google_project_service_identity: 21 + modules: 18 + random_id: 3 + resources: 139 diff --git a/tests/modules/net_vpc_factory/separate_envs.tfvars b/tests/modules/net_vpc_factory/separate_envs.tfvars new file mode 100644 index 000000000..041cce5ba --- /dev/null +++ b/tests/modules/net_vpc_factory/separate_envs.tfvars @@ -0,0 +1,2 @@ +factories_config = { vpcs = "recipes/separate-envs" } + diff --git a/tests/modules/net_vpc_factory/separate_envs.yaml b/tests/modules/net_vpc_factory/separate_envs.yaml new file mode 100644 index 000000000..f3b1399a0 --- /dev/null +++ b/tests/modules/net_vpc_factory/separate_envs.yaml @@ -0,0 +1,35 @@ +# Copyright 2023 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. + +counts: + google_compute_external_vpn_gateway: 2 + google_compute_firewall: 6 + google_compute_ha_vpn_gateway: 2 + google_compute_network: 2 + google_compute_route: 8 + google_compute_router: 4 + google_compute_router_interface: 2 + google_compute_router_nat: 2 + google_compute_router_peer: 2 + google_compute_shared_vpc_host_project: 2 + google_compute_subnetwork: 2 + google_compute_vpn_tunnel: 2 + google_dns_policy: 2 + google_project: 3 + google_project_iam_member: 21 + google_project_service: 27 + google_project_service_identity: 21 + modules: 11 + random_id: 4 + resources: 114 diff --git a/tests/modules/net_vpc_factory/tftest.yaml b/tests/modules/net_vpc_factory/tftest.yaml new file mode 100644 index 000000000..3c90b73d8 --- /dev/null +++ b/tests/modules/net_vpc_factory/tftest.yaml @@ -0,0 +1,23 @@ +# 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. + +module: modules/net-vpc-factory +common_tfvars: + - common.tfvars +tests: + ncc: + only_projects: + peering: + separate_envs: + vpn: diff --git a/tests/modules/net_vpc_factory/vpn.tfvars b/tests/modules/net_vpc_factory/vpn.tfvars new file mode 100644 index 000000000..0d5c56b95 --- /dev/null +++ b/tests/modules/net_vpc_factory/vpn.tfvars @@ -0,0 +1 @@ +factories_config = { vpcs = "recipes/hub-and-spoke-vpn" } diff --git a/tests/modules/net_vpc_factory/vpn.yaml b/tests/modules/net_vpc_factory/vpn.yaml new file mode 100644 index 000000000..7598121b7 --- /dev/null +++ b/tests/modules/net_vpc_factory/vpn.yaml @@ -0,0 +1,37 @@ +# Copyright 2023 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. + +counts: + google_compute_external_vpn_gateway: 1 + google_compute_firewall: 9 + google_compute_ha_vpn_gateway: 5 + google_compute_network: 4 + google_compute_route: 15 + google_compute_router: 6 + google_compute_router_interface: 10 + google_compute_router_nat: 3 + google_compute_router_peer: 10 + google_compute_shared_vpc_host_project: 3 + google_compute_subnetwork: 3 + google_compute_vpn_tunnel: 10 + google_dns_managed_zone: 4 + google_dns_policy: 4 + google_dns_record_set: 1 + google_project: 3 + google_project_iam_member: 21 + google_project_service: 27 + google_project_service_identity: 21 + modules: 22 + random_id: 15 + resources: 175