diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 99fd92406..5204f6b40 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -154,7 +154,8 @@ module "project" {
billing_account = local.billing_account
services = [
"container.googleapis.com",
- "stackdriver.googleapis.com",
+ "logging.googleapis.com",
+ "monitoring.googleapis.com",
"storage.googleapis.com",
]
iam = {
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/apigee/bigquery-analytics/main.tf b/blueprints/apigee/bigquery-analytics/main.tf
index ef8d65cc3..a6a1bf863 100644
--- a/blueprints/apigee/bigquery-analytics/main.tf
+++ b/blueprints/apigee/bigquery-analytics/main.tf
@@ -32,8 +32,8 @@ module "project" {
"cloudbuild.googleapis.com",
"cloudfunctions.googleapis.com",
"cloudscheduler.googleapis.com",
- "logging.googleapis.com",
"compute.googleapis.com",
+ "logging.googleapis.com",
"pubsub.googleapis.com",
"servicenetworking.googleapis.com",
"storage.googleapis.com"
diff --git a/blueprints/cloud-operations/vm-migration/host-target-projects/main.tf b/blueprints/cloud-operations/vm-migration/host-target-projects/main.tf
index cd9ce7e9b..2ea5c6968 100644
--- a/blueprints/cloud-operations/vm-migration/host-target-projects/main.tf
+++ b/blueprints/cloud-operations/vm-migration/host-target-projects/main.tf
@@ -29,8 +29,8 @@ module "host-project" {
"compute.googleapis.com",
"iam.googleapis.com",
"logging.googleapis.com",
- "servicemanagement.googleapis.com",
"servicecontrol.googleapis.com",
+ "servicemanagement.googleapis.com",
"vmmigration.googleapis.com",
]
project_reuse = var.project_create != null ? null : {}
diff --git a/blueprints/cloud-operations/vm-migration/host-target-sharedvpc/main.tf b/blueprints/cloud-operations/vm-migration/host-target-sharedvpc/main.tf
index b86d18694..42a3e3a04 100644
--- a/blueprints/cloud-operations/vm-migration/host-target-sharedvpc/main.tf
+++ b/blueprints/cloud-operations/vm-migration/host-target-sharedvpc/main.tf
@@ -28,8 +28,8 @@ module "host-project" {
"compute.googleapis.com",
"iam.googleapis.com",
"logging.googleapis.com",
- "servicemanagement.googleapis.com",
"servicecontrol.googleapis.com",
+ "servicemanagement.googleapis.com",
"vmmigration.googleapis.com",
]
project_reuse = var.project_create != null ? null : {}
diff --git a/blueprints/cloud-operations/vm-migration/single-project/main.tf b/blueprints/cloud-operations/vm-migration/single-project/main.tf
index f21d2a187..d396f8b39 100644
--- a/blueprints/cloud-operations/vm-migration/single-project/main.tf
+++ b/blueprints/cloud-operations/vm-migration/single-project/main.tf
@@ -29,8 +29,8 @@ module "landing-project" {
"iam.googleapis.com",
"logging.googleapis.com",
"networkconnectivity.googleapis.com",
- "servicemanagement.googleapis.com",
"servicecontrol.googleapis.com",
+ "servicemanagement.googleapis.com",
"vmmigration.googleapis.com"
]
project_reuse = var.project_create != null ? null : {}
diff --git a/blueprints/data-solutions/bq-ml/README.md b/blueprints/data-solutions/bq-ml/README.md
index d0fc2023d..ddc82891f 100644
--- a/blueprints/data-solutions/bq-ml/README.md
+++ b/blueprints/data-solutions/bq-ml/README.md
@@ -97,5 +97,5 @@ module "test" {
prefix = "prefix"
}
-# tftest modules=9 resources=71
+# tftest modules=9 resources=74
```
diff --git a/blueprints/data-solutions/bq-ml/main.tf b/blueprints/data-solutions/bq-ml/main.tf
index a26e8ee5b..cbadd3dce 100644
--- a/blueprints/data-solutions/bq-ml/main.tf
+++ b/blueprints/data-solutions/bq-ml/main.tf
@@ -39,15 +39,16 @@ module "project" {
services = [
"aiplatform.googleapis.com",
"bigquery.googleapis.com",
- "bigquerystorage.googleapis.com",
"bigqueryreservation.googleapis.com",
+ "bigquerystorage.googleapis.com",
"compute.googleapis.com",
+ "logging.googleapis.com",
"ml.googleapis.com",
+ "monitoring.googleapis.com",
"notebooks.googleapis.com",
"servicenetworking.googleapis.com",
- "stackdriver.googleapis.com",
+ "storage-component.googleapis.com",
"storage.googleapis.com",
- "storage-component.googleapis.com"
]
shared_vpc_service_config = local.shared_vpc_project == null ? null : {
attach = true
diff --git a/blueprints/data-solutions/data-platform-minimal/01-landing.tf b/blueprints/data-solutions/data-platform-minimal/01-landing.tf
index e9a036102..4a7571371 100644
--- a/blueprints/data-solutions/data-platform-minimal/01-landing.tf
+++ b/blueprints/data-solutions/data-platform-minimal/01-landing.tf
@@ -67,10 +67,11 @@ module "land-project" {
"cloudresourcemanager.googleapis.com",
"datalineage.googleapis.com",
"iam.googleapis.com",
+ "logging.googleapis.com",
+ "monitoring.googleapis.com",
"serviceusage.googleapis.com",
- "stackdriver.googleapis.com",
- "storage.googleapis.com",
"storage-component.googleapis.com",
+ "storage.googleapis.com",
]
service_encryption_key_ids = {
"bigquery.googleapis.com" = compact([var.service_encryption_keys.bq])
diff --git a/blueprints/data-solutions/data-platform-minimal/02-processing.tf b/blueprints/data-solutions/data-platform-minimal/02-processing.tf
index d19695f5f..8ac1aaeb9 100644
--- a/blueprints/data-solutions/data-platform-minimal/02-processing.tf
+++ b/blueprints/data-solutions/data-platform-minimal/02-processing.tf
@@ -119,11 +119,12 @@ module "processing-project" {
"datalineage.googleapis.com",
"dataproc.googleapis.com",
"iam.googleapis.com",
+ "logging.googleapis.com",
+ "monitoring.googleapis.com",
"servicenetworking.googleapis.com",
"serviceusage.googleapis.com",
- "stackdriver.googleapis.com",
+ "storage-component.googleapis.com",
"storage.googleapis.com",
- "storage-component.googleapis.com"
]
service_encryption_key_ids = {
"composer.googleapis.com" = compact([var.service_encryption_keys.composer])
diff --git a/blueprints/data-solutions/data-platform-minimal/03-curated.tf b/blueprints/data-solutions/data-platform-minimal/03-curated.tf
index f825065a5..927d94a81 100644
--- a/blueprints/data-solutions/data-platform-minimal/03-curated.tf
+++ b/blueprints/data-solutions/data-platform-minimal/03-curated.tf
@@ -24,11 +24,12 @@ locals {
"compute.googleapis.com",
"datalineage.googleapis.com",
"iam.googleapis.com",
+ "logging.googleapis.com",
+ "monitoring.googleapis.com",
"servicenetworking.googleapis.com",
"serviceusage.googleapis.com",
- "stackdriver.googleapis.com",
+ "storage-component.googleapis.com",
"storage.googleapis.com",
- "storage-component.googleapis.com"
]
iam_cur = {
"roles/bigquery.dataOwner" = [
diff --git a/blueprints/data-solutions/data-platform-minimal/04-common.tf b/blueprints/data-solutions/data-platform-minimal/04-common.tf
index 0431d774c..6c0ad57eb 100644
--- a/blueprints/data-solutions/data-platform-minimal/04-common.tf
+++ b/blueprints/data-solutions/data-platform-minimal/04-common.tf
@@ -79,8 +79,9 @@ module "common-project" {
"datacatalog.googleapis.com",
"dlp.googleapis.com",
"iam.googleapis.com",
+ "logging.googleapis.com",
+ "monitoring.googleapis.com",
"serviceusage.googleapis.com",
- "stackdriver.googleapis.com",
]
}
diff --git a/blueprints/data-solutions/data-platform-minimal/README.md b/blueprints/data-solutions/data-platform-minimal/README.md
index 8639630b7..c74bbbffb 100644
--- a/blueprints/data-solutions/data-platform-minimal/README.md
+++ b/blueprints/data-solutions/data-platform-minimal/README.md
@@ -229,7 +229,7 @@ module "data-platform" {
prefix = "myprefix"
}
-# tftest modules=23 resources=158
+# tftest modules=23 resources=170
```
## Customizations
diff --git a/blueprints/data-solutions/data-playground/README.md b/blueprints/data-solutions/data-playground/README.md
index 347be2fbb..70c0c95f7 100644
--- a/blueprints/data-solutions/data-playground/README.md
+++ b/blueprints/data-solutions/data-playground/README.md
@@ -84,5 +84,5 @@ module "test" {
parent = "folders/467898377"
}
}
-# tftest modules=8 resources=70
+# tftest modules=8 resources=73
```
diff --git a/blueprints/data-solutions/data-playground/main.tf b/blueprints/data-solutions/data-playground/main.tf
index 15e97729a..f3618e3cb 100644
--- a/blueprints/data-solutions/data-playground/main.tf
+++ b/blueprints/data-solutions/data-playground/main.tf
@@ -61,19 +61,20 @@ module "project" {
services = [
"aiplatform.googleapis.com",
"bigquery.googleapis.com",
- "bigquerystorage.googleapis.com",
"bigqueryreservation.googleapis.com",
+ "bigquerystorage.googleapis.com",
"composer.googleapis.com",
"compute.googleapis.com",
- "dialogflow.googleapis.com",
"dataflow.googleapis.com",
+ "dialogflow.googleapis.com",
+ "logging.googleapis.com",
"ml.googleapis.com",
+ "monitoring.googleapis.com",
"notebooks.googleapis.com",
"orgpolicy.googleapis.com",
"servicenetworking.googleapis.com",
- "stackdriver.googleapis.com",
+ "storage-component.googleapis.com",
"storage.googleapis.com",
- "storage-component.googleapis.com"
]
shared_vpc_service_config = local.shared_vpc_project == null ? null : {
diff --git a/blueprints/data-solutions/vertex-mlops/main.tf b/blueprints/data-solutions/vertex-mlops/main.tf
index 18c01019b..2a55e2fd1 100644
--- a/blueprints/data-solutions/vertex-mlops/main.tf
+++ b/blueprints/data-solutions/vertex-mlops/main.tf
@@ -271,20 +271,20 @@ module "project" {
"bigquery.googleapis.com",
"bigquerystorage.googleapis.com",
"cloudbuild.googleapis.com",
- "containerfilesystem.googleapis.com",
"compute.googleapis.com",
+ "containerfilesystem.googleapis.com",
"datacatalog.googleapis.com",
"dataflow.googleapis.com",
"iam.googleapis.com",
+ "logging.googleapis.com",
"ml.googleapis.com",
"monitoring.googleapis.com",
"notebooks.googleapis.com",
"secretmanager.googleapis.com",
"servicenetworking.googleapis.com",
"serviceusage.googleapis.com",
- "stackdriver.googleapis.com",
+ "storage-component.googleapis.com",
"storage.googleapis.com",
- "storage-component.googleapis.com"
]
shared_vpc_service_config = local.shared_vpc_project == null ? null : {
attach = true
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/blueprints/gcve/monitoring/main.tf b/blueprints/gcve/monitoring/main.tf
index efa53ddf4..a725d2a59 100644
--- a/blueprints/gcve/monitoring/main.tf
+++ b/blueprints/gcve/monitoring/main.tf
@@ -36,8 +36,8 @@ module "project" {
project_reuse = var.project_create != null ? null : {}
services = [
"compute.googleapis.com",
- "monitoring.googleapis.com",
"logging.googleapis.com",
+ "monitoring.googleapis.com",
"secretmanager.googleapis.com"
]
shared_vpc_service_config = !local.use_shared_vpc ? null : {
diff --git a/blueprints/gke/multi-cluster-mesh-gke-fleet-api/README.md b/blueprints/gke/multi-cluster-mesh-gke-fleet-api/README.md
index a689da60c..efea4a5ea 100644
--- a/blueprints/gke/multi-cluster-mesh-gke-fleet-api/README.md
+++ b/blueprints/gke/multi-cluster-mesh-gke-fleet-api/README.md
@@ -102,5 +102,5 @@ module "test" {
mgmt_subnet_cidr_block = "10.0.0.0/24"
istio_version = "1.14.1-asm.3"
}
-# tftest modules=13 resources=74
+# tftest modules=13 resources=73
```
diff --git a/blueprints/gke/multi-cluster-mesh-gke-fleet-api/main.tf b/blueprints/gke/multi-cluster-mesh-gke-fleet-api/main.tf
index bc2ed20a0..9187697f9 100644
--- a/blueprints/gke/multi-cluster-mesh-gke-fleet-api/main.tf
+++ b/blueprints/gke/multi-cluster-mesh-gke-fleet-api/main.tf
@@ -72,12 +72,11 @@ module "fleet_project" {
"anthos.googleapis.com",
"cloudresourcemanager.googleapis.com",
"container.googleapis.com",
- "gkehub.googleapis.com",
"gkeconnect.googleapis.com",
+ "gkehub.googleapis.com",
"logging.googleapis.com",
"mesh.googleapis.com",
"monitoring.googleapis.com",
- "stackdriver.googleapis.com"
]
iam = {
"roles/container.admin" = [module.mgmt_server.service_account_iam_email]
diff --git a/blueprints/networking/glb-hybrid-neg-internal/main.tf b/blueprints/networking/glb-hybrid-neg-internal/main.tf
index ca4a9513a..d788aba8d 100644
--- a/blueprints/networking/glb-hybrid-neg-internal/main.tf
+++ b/blueprints/networking/glb-hybrid-neg-internal/main.tf
@@ -40,7 +40,7 @@ module "project_landing" {
"networkmanagement.googleapis.com",
# Logging and Monitoring
"logging.googleapis.com",
- "monitoring.googleapis.com"
+ "monitoring.googleapis.com",
]
}
diff --git a/blueprints/networking/glb-hybrid-neg-internal/spoke.tf b/blueprints/networking/glb-hybrid-neg-internal/spoke.tf
index b54f402f2..9e6349f3a 100644
--- a/blueprints/networking/glb-hybrid-neg-internal/spoke.tf
+++ b/blueprints/networking/glb-hybrid-neg-internal/spoke.tf
@@ -34,7 +34,7 @@ module "project_spoke_01" {
"networkmanagement.googleapis.com",
# Logging and Monitoring
"logging.googleapis.com",
- "monitoring.googleapis.com"
+ "monitoring.googleapis.com",
]
}
diff --git a/blueprints/networking/shared-vpc-gke/README.md b/blueprints/networking/shared-vpc-gke/README.md
index b43339b03..f00d0a8d7 100644
--- a/blueprints/networking/shared-vpc-gke/README.md
+++ b/blueprints/networking/shared-vpc-gke/README.md
@@ -48,7 +48,7 @@ There's a minor glitch that can surface running `terraform destroy`, where the s
|---|---|:---:|:---:|:---:|
| [billing_account_id](variables.tf#L15) | Billing account id used as default for new projects. | string | ✓ | |
| [prefix](variables.tf#L69) | Prefix used for resource names. | string | ✓ | |
-| [root_node](variables.tf#L93) | Hierarchy node where projects will be created, 'organizations/org_id' or 'folders/folder_id'. | string | ✓ | |
+| [root_node](variables.tf#L94) | Hierarchy node where projects will be created, 'organizations/org_id' or 'folders/folder_id'. | string | ✓ | |
| [cluster_create](variables.tf#L20) | Create GKE cluster and nodepool. | bool | | true |
| [deletion_protection](variables.tf#L26) | Prevent Terraform from destroying data storage resources (storage buckets, GKE clusters, CloudSQL instances) in this blueprint. When this field is set in Terraform state, a terraform destroy or terraform apply that would delete data storage resources will fail. | bool | | false |
| [ip_ranges](variables.tf#L33) | Subnet IP CIDR ranges. | map(string) | | {…} |
@@ -56,8 +56,8 @@ There's a minor glitch that can surface running `terraform destroy`, where the s
| [owners_gce](variables.tf#L51) | GCE project owners, in IAM format. | list(string) | | [] |
| [owners_gke](variables.tf#L57) | GKE project owners, in IAM format. | list(string) | | [] |
| [owners_host](variables.tf#L63) | Host project owners, in IAM format. | list(string) | | [] |
-| [project_services](variables.tf#L78) | Service APIs enabled by default in new projects. | list(string) | | […] |
-| [region](variables.tf#L87) | Region used. | string | | "europe-west1" |
+| [project_services](variables.tf#L78) | Service APIs enabled by default in new projects. | list(string) | | […] |
+| [region](variables.tf#L88) | Region used. | string | | "europe-west1" |
## Outputs
@@ -77,5 +77,5 @@ module "test" {
prefix = "test"
root_node = "organizations/0123456789"
}
-# tftest modules=11 resources=57
+# tftest modules=11 resources=66
```
diff --git a/blueprints/networking/shared-vpc-gke/variables.tf b/blueprints/networking/shared-vpc-gke/variables.tf
index 07980148b..be1541753 100644
--- a/blueprints/networking/shared-vpc-gke/variables.tf
+++ b/blueprints/networking/shared-vpc-gke/variables.tf
@@ -80,7 +80,8 @@ variable "project_services" {
type = list(string)
default = [
"container.googleapis.com",
- "stackdriver.googleapis.com",
+ "logging.googleapis.com",
+ "monitoring.googleapis.com",
]
}
diff --git a/blueprints/networking/vpc-connectivity-lab/README.md b/blueprints/networking/vpc-connectivity-lab/README.md
index cf72831f9..0c835bdbd 100644
--- a/blueprints/networking/vpc-connectivity-lab/README.md
+++ b/blueprints/networking/vpc-connectivity-lab/README.md
@@ -108,5 +108,5 @@ module "test" {
prefix = "fast-sr0-sbox"
}
-# tftest modules=35 resources=150
+# tftest modules=35 resources=153
```
diff --git a/blueprints/networking/vpc-connectivity-lab/main.tf b/blueprints/networking/vpc-connectivity-lab/main.tf
index bc99dc601..20f978086 100644
--- a/blueprints/networking/vpc-connectivity-lab/main.tf
+++ b/blueprints/networking/vpc-connectivity-lab/main.tf
@@ -31,6 +31,7 @@ module "project" {
"compute.googleapis.com",
"dns.googleapis.com",
"networkconnectivity.googleapis.com",
- "stackdriver.googleapis.com",
+ "logging.googleapis.com",
+ "monitoring.googleapis.com",
]
}
diff --git a/blueprints/secops/bindplane-gke/README.md b/blueprints/secops/bindplane-gke/README.md
index a89beeb99..e557cb77d 100644
--- a/blueprints/secops/bindplane-gke/README.md
+++ b/blueprints/secops/bindplane-gke/README.md
@@ -38,10 +38,10 @@ git clone https://github.com/GoogleCloudPlatform/cloud-foundation-fabric.git
Before you deploy the architecture, you will need at least the following
information/configurations in place (for more precise configuration see the Variables section):
-* The project ID
-* The VPC host project
-* VPC and subnets should already exist
-* Subnet must be configured with pods and services secondary ranges (default names for secondary ranges is "pod" and "services")
+- The project ID
+- The VPC host project
+- VPC and subnets should already exist
+- Subnet must be configured with pods and services secondary ranges (default names for secondary ranges is "pod" and "services")
#### Step 2: Prepare the variables
@@ -98,7 +98,7 @@ Then running the command `kubectl get pods` you should receive the following mes
"No resources found in default namespace."
```
-In case private connection is available and DNS configuration is properly in place you should be able to reach the BindPlane OP Management console navigating the url (e.g. https://bindplane.example.com/), the following login page should show up.
+In case private connection is available and DNS configuration is properly in place you should be able to reach the BindPlane OP Management console navigating the url (e.g. ), the following login page should show up.

@@ -158,5 +158,5 @@ module "bindplane-gke" {
region = "europe-west8"
prefix = "tmp"
}
-# tftest modules=10 resources=45
+# tftest modules=10 resources=48
```
diff --git a/blueprints/secops/bindplane-gke/main.tf b/blueprints/secops/bindplane-gke/main.tf
index 8c47fe3b1..53b058e8a 100644
--- a/blueprints/secops/bindplane-gke/main.tf
+++ b/blueprints/secops/bindplane-gke/main.tf
@@ -36,14 +36,15 @@ module "project" {
project_reuse = var.project_create != null ? null : {}
name = var.project_id
services = concat([
- "compute.googleapis.com",
- "iap.googleapis.com",
- "stackdriver.googleapis.com",
"chronicle.googleapis.com",
- "container.googleapis.com",
- "gkehub.googleapis.com",
+ "compute.googleapis.com",
"connectgateway.googleapis.com",
- "gkeconnect.googleapis.com"
+ "container.googleapis.com",
+ "gkeconnect.googleapis.com",
+ "gkehub.googleapis.com",
+ "iap.googleapis.com",
+ "logging.googleapis.com",
+ "monitoring.googleapis.com",
])
iam = {
"roles/pubsub.editor" = ["principal://iam.googleapis.com/projects/${module.project.number}/locations/global/workloadIdentityPools/${module.project.project_id}.svc.id.goog/subject/ns/bindplane/sa/bindplane"]
diff --git a/blueprints/secops/secops-gke-forwarder/README.md b/blueprints/secops/secops-gke-forwarder/README.md
index a75cc970a..e2e3a6f8e 100644
--- a/blueprints/secops/secops-gke-forwarder/README.md
+++ b/blueprints/secops/secops-gke-forwarder/README.md
@@ -33,9 +33,9 @@ git clone https://github.com/GoogleCloudPlatform/cloud-foundation-fabric.git
Before you deploy the architecture, you will need at least the following
information/configurations in place (for more precise configuration see the Variables section):
-* The project ID
-* The VPC host project
-* VPC and subnets should already exist
+- The project ID
+- The VPC host project
+- VPC and subnets should already exist
#### Step 2: Prepare the variables
@@ -147,7 +147,7 @@ module "test" {
}
}
}
-# tftest modules=5 resources=34 files=credentials,config
+# tftest modules=5 resources=37 files=credentials,config
```
```
@@ -198,4 +198,4 @@ collectors:
tcp_address: 0.0.0.0:10515
connection_timeout_sec: 60
tcp_buffer_size: 524288
-```
\ No newline at end of file
+```
diff --git a/blueprints/secops/secops-gke-forwarder/main.tf b/blueprints/secops/secops-gke-forwarder/main.tf
index 0a65090ad..cd6f7b131 100644
--- a/blueprints/secops/secops-gke-forwarder/main.tf
+++ b/blueprints/secops/secops-gke-forwarder/main.tf
@@ -37,13 +37,14 @@ module "project" {
name = var.project_id
services = concat([
"compute.googleapis.com",
- "iap.googleapis.com",
- "stackdriver.googleapis.com",
"chronicle.googleapis.com",
- "container.googleapis.com",
- "gkehub.googleapis.com",
"connectgateway.googleapis.com",
- "gkeconnect.googleapis.com"
+ "container.googleapis.com",
+ "gkeconnect.googleapis.com",
+ "gkehub.googleapis.com",
+ "iap.googleapis.com",
+ "logging.googleapis.com",
+ "monitoring.googleapis.com",
])
}
diff --git a/blueprints/third-party-solutions/gitlab-runner/main.tf b/blueprints/third-party-solutions/gitlab-runner/main.tf
index 5090a6f7a..c95a54c51 100644
--- a/blueprints/third-party-solutions/gitlab-runner/main.tf
+++ b/blueprints/third-party-solutions/gitlab-runner/main.tf
@@ -48,10 +48,11 @@ module "project" {
services = [
"compute.googleapis.com",
"storage.googleapis.com",
- "stackdriver.googleapis.com",
+ "logging.googleapis.com",
+ "monitoring.googleapis.com",
"dns.googleapis.com",
"iam.googleapis.com",
- "secretmanager.googleapis.com"
+ "secretmanager.googleapis.com",
]
shared_vpc_service_config = {
attach = true
diff --git a/blueprints/third-party-solutions/gitlab/README.md b/blueprints/third-party-solutions/gitlab/README.md
index 4889c43da..ff58b58d1 100644
--- a/blueprints/third-party-solutions/gitlab/README.md
+++ b/blueprints/third-party-solutions/gitlab/README.md
@@ -55,12 +55,12 @@ managed services incorporated:
Benefits:
-- Reduced Operational Overhead: Google handles infrastructure setup,
+* Reduced Operational Overhead: Google handles infrastructure setup,
maintenance, and updates, freeing up your time and resources.
-- Enhanced Security: Managed services often benefit from Google's comprehensive
+* Enhanced Security: Managed services often benefit from Google's comprehensive
security measures and expertise.
-- Scalability: Easily adjust resource allocation to meet evolving demands.
-- Cost Optimization: Pay for the resources you use, benefiting from Google's
+* Scalability: Easily adjust resource allocation to meet evolving demands.
+* Cost Optimization: Pay for the resources you use, benefiting from Google's
infrastructure optimization.
Integration: Managed services seamlessly integrate with other GCP services,
promoting a cohesive cloud environment.
@@ -80,25 +80,25 @@ type, the table below summarized such a configuration:
| Object Type | Description | Cloud Storage Bucket |
|------------------|----------------------------------------|-----------------------------------|
-| artifacts | CI artifacts | ${prefix}-gitlab-artifacts |
+| artifacts | CI artifacts | ${prefix}-gitlab-artifacts |
| external_diffs | Merge request diffs | ${prefix}-mr-diffs |
-| uploads | User uploads | ${prefix}-gitlab-uploads |
-| lfs | Git Large File Storage objects | ${prefix}-gitlab-lfs |
-| packages | Project packages (e.g. PyPI, Maven ..) | ${prefix}-gitlab-packages |
+| uploads | User uploads | ${prefix}-gitlab-uploads |
+| lfs | Git Large File Storage objects | ${prefix}-gitlab-lfs |
+| packages | Project packages (e.g. PyPI, Maven ..) | ${prefix}-gitlab-packages |
| dependency_proxy | Dependency Proxy | ${prefix}-gitlab-dependency-proxy |
| terraform_state | Terraform state files | ${prefix}-gitlab-terraform-state |
-| pages | Pages | ${prefix}-gitlab-pages |
+| pages | Pages | ${prefix}-gitlab-pages |
For more information on Gitlab object storage and Google Cloud Storage
integration please refer to the official Gitlab documentation available at the
following [link](https://docs.gitlab.com/ee/administration/object_storage.html).
-- [PostgreSQL service](https://docs.gitlab.com/ee/administration/postgresql/external.html)
+* [PostgreSQL service](https://docs.gitlab.com/ee/administration/postgresql/external.html)
Updated postgres configuration to match documentation, created required database
in postgres instance.
-- [Redis](https://docs.gitlab.com/ee/administration/redis/replication_and_failover_external.html)
+* [Redis](https://docs.gitlab.com/ee/administration/redis/replication_and_failover_external.html)
## Identity
@@ -116,7 +116,7 @@ SP). This allows GitLab to consume assertions from a SAML identity provider (
IdP), such as Cloud Identity, to authenticate users. Please find instructions
below for integration with:
-- [Google Workspace](#google-workspace-setup)
+* [Google Workspace](#google-workspace-setup)
#### Google Workspace Setup
@@ -131,11 +131,11 @@ information in the service provider configuration:
| Configuration | Typical Value | Cloud Storage Bucket |
|-------------------|--------------------------------------------------|---------------------------------------------------------------------------------------------|
-| Name of SAML App | Gitlab | Name of the app |
+| Name of SAML App | Gitlab | Name of the app |
| ACS URL | https:///users/auth/saml/callback | Assertion Consumer Service URL. |
-| GITLAB_DOMAIN | gitlab.example.com | Your GitLab instance domain. |
-| Entity ID | https://gitlab.example.com | A value unique to your SAML application. Set it to the issuer in your GitLab configuration. |
-| Name ID | EMAIL | Required value. Also known as name_identifier_format. |
+| GITLAB_DOMAIN | gitlab.example.com | Your GitLab instance domain. |
+| Entity ID | | A value unique to your SAML application. Set it to the issuer in your GitLab configuration. |
+| Name ID | EMAIL | Required value. Also known as name_identifier_format. |
Then setup the following SAML attribute mappings:
@@ -155,14 +155,14 @@ information:
### Others Identity Integration
-- [OpenID Connect OmniAuth](https://docs.gitlab.com/ee/administration/auth/oidc.html#configure-google)
-- [Google Secure LDAP](https://docs.gitlab.com/ee/administration/auth/ldap/google_secure_ldap.html)
+* [OpenID Connect OmniAuth](https://docs.gitlab.com/ee/administration/auth/oidc.html#configure-google)
+* [Google Secure LDAP](https://docs.gitlab.com/ee/administration/auth/ldap/google_secure_ldap.html)
## Email
### Gmail / Workspace
-- [ ] [documentation](https://docs.gitlab.com/ee/administration/incoming_email.html#gmail)
+* [ ] [documentation](https://docs.gitlab.com/ee/administration/incoming_email.html#gmail)
### Sendgrid integration
@@ -229,11 +229,11 @@ following [link](https://docs.gitlab.com/omnibus/settings/ssl/#configure-https-m
## Networking and scalability
-- [Load balancer](https://docs.gitlab.com/ee/administration/load_balancer.html)
+* [Load balancer](https://docs.gitlab.com/ee/administration/load_balancer.html)
## HA
-- [High Availability](http://ubimol.it/12.0/ee/administration/high_availability/README.html)
+* [High Availability](http://ubimol.it/12.0/ee/administration/high_availability/README.html)
### Deployment
@@ -256,10 +256,10 @@ information (for more precise configuration see the Variables section):
* The project ID
The VPC host project, VPC and subnets should already exist and the following networking requirements are satisfied:
-- configured PSA for Cloud SQL on the VPC
-- subnets configured with PGA and Cloud NAT for internet access
-- Inbound firewall rule for IAP on port 22
-- Inbound firewall rule for TCP ports 80, 443, 2222 from proxy subnet CIDR (gitlab)
+* configured PSA for Cloud SQL on the VPC
+* subnets configured with PGA and Cloud NAT for internet access
+* Inbound firewall rule for IAP on port 22
+* Inbound firewall rule for TCP ports 80, 443, 2222 from proxy subnet CIDR (gitlab)
#### Step 2: Prepare the variables
@@ -295,7 +295,7 @@ A gcloud command like the following should be available
gcloud compute ssh squid-vm --project ${project} --zone europe-west8-b -- -L 3128:127.0.0.1:3128 -N -q -f
```
-Set as system proxy ip 127.0.0.1 and port 3128 and connect to Gitlab hostname https://gitlab.gcp.example.com.
+Set as system proxy ip 127.0.0.1 and port 3128 and connect to Gitlab hostname .
Use default admin password available in /run/gitlab/config/initial_root_password or reset admin password via the following command on the Docker container:
```bash
@@ -304,9 +304,9 @@ gitlab-rake “gitlab:password:reset”
## Reference and useful links
-- [Reference architecture up to 1k users](https://docs.gitlab.com/ee/administration/reference_architectures/1k_users.html)
-- [`/etc/gitlab/gitlab.rb` template](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-config-template/gitlab.rb.template)
-- [`/etc/gitlab/gitlab.rb` default options](https://docs.gitlab.com/ee/administration/package_information/defaults.html)
+* [Reference architecture up to 1k users](https://docs.gitlab.com/ee/administration/reference_architectures/1k_users.html)
+* [`/etc/gitlab/gitlab.rb` template](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/files/gitlab-config-template/gitlab.rb.template)
+* [`/etc/gitlab/gitlab.rb` default options](https://docs.gitlab.com/ee/administration/package_information/defaults.html)
@@ -385,5 +385,5 @@ module "test" {
project_id = "my-project"
region = "europe-west8"
}
-# tftest modules=15 resources=60
+# tftest modules=15 resources=63
```
diff --git a/blueprints/third-party-solutions/gitlab/main.tf b/blueprints/third-party-solutions/gitlab/main.tf
index 1b839d7fd..dade6b311 100644
--- a/blueprints/third-party-solutions/gitlab/main.tf
+++ b/blueprints/third-party-solutions/gitlab/main.tf
@@ -23,13 +23,14 @@ module "project" {
project_reuse = var.project_create != null ? null : {}
services = [
"compute.googleapis.com",
- "memcache.googleapis.com",
- "redis.googleapis.com",
- "sqladmin.googleapis.com",
- "sql-component.googleapis.com",
- "stackdriver.googleapis.com",
"dns.googleapis.com",
"iam.googleapis.com",
+ "logging.googleapis.com",
+ "memcache.googleapis.com",
+ "redis.googleapis.com",
+ "sql-component.googleapis.com",
+ "sqladmin.googleapis.com",
+ "monitoring.googleapis.com",
]
shared_vpc_service_config = {
attach = true
diff --git a/blueprints/third-party-solutions/phpipam/main.tf b/blueprints/third-party-solutions/phpipam/main.tf
index 0dc826ae0..4fdf56200 100644
--- a/blueprints/third-party-solutions/phpipam/main.tf
+++ b/blueprints/third-party-solutions/phpipam/main.tf
@@ -62,9 +62,9 @@ module "project" {
"monitoring.googleapis.com",
"run.googleapis.com",
"servicenetworking.googleapis.com",
- "sqladmin.googleapis.com",
"sql-component.googleapis.com",
- "vpcaccess.googleapis.com"
+ "sqladmin.googleapis.com",
+ "vpcaccess.googleapis.com",
]
}
diff --git a/blueprints/third-party-solutions/wordpress/cloudrun/main.tf b/blueprints/third-party-solutions/wordpress/cloudrun/main.tf
index 56a7b6bae..31ba131bf 100644
--- a/blueprints/third-party-solutions/wordpress/cloudrun/main.tf
+++ b/blueprints/third-party-solutions/wordpress/cloudrun/main.tf
@@ -63,10 +63,10 @@ module "project" {
"run.googleapis.com",
"logging.googleapis.com",
"monitoring.googleapis.com",
- "sqladmin.googleapis.com",
+ "servicenetworking.googleapis.com",
"sql-component.googleapis.com",
+ "sqladmin.googleapis.com",
"vpcaccess.googleapis.com",
- "servicenetworking.googleapis.com"
]
}
diff --git a/fast/addons/1-resman-tenants/tenant-fast-automation.tf b/fast/addons/1-resman-tenants/tenant-fast-automation.tf
index 385aa248c..b5c3cc0d8 100644
--- a/fast/addons/1-resman-tenants/tenant-fast-automation.tf
+++ b/fast/addons/1-resman-tenants/tenant-fast-automation.tf
@@ -61,23 +61,24 @@ module "tenant-automation-project" {
"billingbudgets.googleapis.com",
"cloudasset.googleapis.com",
"cloudbilling.googleapis.com",
+ "cloudbuild.googleapis.com",
"cloudkms.googleapis.com",
"cloudquotas.googleapis.com",
"cloudresourcemanager.googleapis.com",
+ "compute.googleapis.com",
+ "container.googleapis.com",
"essentialcontacts.googleapis.com",
"iam.googleapis.com",
"iamcredentials.googleapis.com",
+ "logging.googleapis.com",
+ "monitoring.googleapis.com",
"orgpolicy.googleapis.com",
"pubsub.googleapis.com",
"servicenetworking.googleapis.com",
"serviceusage.googleapis.com",
- "stackdriver.googleapis.com",
"storage-component.googleapis.com",
"storage.googleapis.com",
"sts.googleapis.com",
- "cloudbuild.googleapis.com",
- "compute.googleapis.com",
- "container.googleapis.com",
]
logging_data_access = {
"iam.googleapis.com" = {
diff --git a/fast/stages/0-bootstrap/automation.tf b/fast/stages/0-bootstrap/automation.tf
index d3c174233..28b1f6476 100644
--- a/fast/stages/0-bootstrap/automation.tf
+++ b/fast/stages/0-bootstrap/automation.tf
@@ -143,15 +143,16 @@ module "automation-project" {
"essentialcontacts.googleapis.com",
"iam.googleapis.com",
"iamcredentials.googleapis.com",
+ "logging.googleapis.com",
+ "monitoring.googleapis.com",
"networksecurity.googleapis.com",
"orgpolicy.googleapis.com",
"pubsub.googleapis.com",
"servicenetworking.googleapis.com",
"serviceusage.googleapis.com",
- "stackdriver.googleapis.com",
"storage-component.googleapis.com",
"storage.googleapis.com",
- "sts.googleapis.com"
+ "sts.googleapis.com",
],
# enable specific service only after org policies have been applied
var.bootstrap_user != null ? [] : [
diff --git a/fast/stages/0-bootstrap/data/custom-roles/storage_viewer.yaml b/fast/stages/0-bootstrap/data/custom-roles/storage_viewer.yaml
index a80786e69..59522313d 100644
--- a/fast/stages/0-bootstrap/data/custom-roles/storage_viewer.yaml
+++ b/fast/stages/0-bootstrap/data/custom-roles/storage_viewer.yaml
@@ -28,7 +28,6 @@ includedPermissions:
- storage.managedFolders.list
- storage.multipartUploads.list
- storage.multipartUploads.listParts
- - storage.objects.create
- storage.objects.get
- storage.objects.getIamPolicy
- storage.objects.list
diff --git a/fast/stages/2-networking-a-simple/net-dev.tf b/fast/stages/2-networking-a-simple/net-dev.tf
index 6a7c4aa5b..c372d06c0 100644
--- a/fast/stages/2-networking-a-simple/net-dev.tf
+++ b/fast/stages/2-networking-a-simple/net-dev.tf
@@ -42,16 +42,17 @@ module "dev-spoke-project" {
)
prefix = var.prefix
services = [
- "container.googleapis.com",
"compute.googleapis.com",
+ "container.googleapis.com",
"dns.googleapis.com",
"iap.googleapis.com",
+ "logging.googleapis.com",
+ "monitoring.googleapis.com",
"networkmanagement.googleapis.com",
"networksecurity.googleapis.com",
"servicenetworking.googleapis.com",
- "stackdriver.googleapis.com",
+ "vmwareengine.googleapis.com",
"vpcaccess.googleapis.com",
- "vmwareengine.googleapis.com"
]
shared_vpc_host_config = {
enabled = true
diff --git a/fast/stages/2-networking-a-simple/net-prod.tf b/fast/stages/2-networking-a-simple/net-prod.tf
index 1d3411ff9..cfb5298a1 100644
--- a/fast/stages/2-networking-a-simple/net-prod.tf
+++ b/fast/stages/2-networking-a-simple/net-prod.tf
@@ -42,16 +42,17 @@ module "prod-spoke-project" {
)
prefix = var.prefix
services = [
- "container.googleapis.com",
"compute.googleapis.com",
+ "container.googleapis.com",
"dns.googleapis.com",
"iap.googleapis.com",
+ "logging.googleapis.com",
+ "monitoring.googleapis.com",
"networkmanagement.googleapis.com",
"networksecurity.googleapis.com",
"servicenetworking.googleapis.com",
- "stackdriver.googleapis.com",
+ "vmwareengine.googleapis.com",
"vpcaccess.googleapis.com",
- "vmwareengine.googleapis.com"
]
shared_vpc_host_config = {
enabled = true
diff --git a/fast/stages/2-networking-b-nva/net-dev.tf b/fast/stages/2-networking-b-nva/net-dev.tf
index a6897d044..49a9097f9 100644
--- a/fast/stages/2-networking-b-nva/net-dev.tf
+++ b/fast/stages/2-networking-b-nva/net-dev.tf
@@ -45,11 +45,12 @@ module "dev-spoke-project" {
"compute.googleapis.com",
"dns.googleapis.com",
"iap.googleapis.com",
+ "logging.googleapis.com",
+ "monitoring.googleapis.com",
"networkmanagement.googleapis.com",
"networksecurity.googleapis.com",
"servicenetworking.googleapis.com",
- "stackdriver.googleapis.com",
- "vpcaccess.googleapis.com"
+ "vpcaccess.googleapis.com",
]
shared_vpc_host_config = {
enabled = true
diff --git a/fast/stages/2-networking-b-nva/net-landing.tf b/fast/stages/2-networking-b-nva/net-landing.tf
index 702946765..cdb3efd4d 100644
--- a/fast/stages/2-networking-b-nva/net-landing.tf
+++ b/fast/stages/2-networking-b-nva/net-landing.tf
@@ -58,8 +58,9 @@ module "landing-project" {
"iap.googleapis.com",
"networkconnectivity.googleapis.com",
"networkmanagement.googleapis.com",
- "stackdriver.googleapis.com",
- "vmwareengine.googleapis.com"
+ "logging.googleapis.com",
+ "monitoring.googleapis.com",
+ "vmwareengine.googleapis.com",
]
shared_vpc_host_config = {
enabled = true
diff --git a/fast/stages/2-networking-b-nva/net-prod.tf b/fast/stages/2-networking-b-nva/net-prod.tf
index c37a9a4d1..6c3c6f5a3 100644
--- a/fast/stages/2-networking-b-nva/net-prod.tf
+++ b/fast/stages/2-networking-b-nva/net-prod.tf
@@ -45,11 +45,12 @@ module "prod-spoke-project" {
"compute.googleapis.com",
"dns.googleapis.com",
"iap.googleapis.com",
+ "logging.googleapis.com",
+ "monitoring.googleapis.com",
"networkmanagement.googleapis.com",
"networksecurity.googleapis.com",
"servicenetworking.googleapis.com",
- "stackdriver.googleapis.com",
- "vpcaccess.googleapis.com"
+ "vpcaccess.googleapis.com",
]
shared_vpc_host_config = {
enabled = true
diff --git a/fast/stages/2-networking-c-separate-envs/net-dev.tf b/fast/stages/2-networking-c-separate-envs/net-dev.tf
index a81084f32..108499996 100644
--- a/fast/stages/2-networking-c-separate-envs/net-dev.tf
+++ b/fast/stages/2-networking-c-separate-envs/net-dev.tf
@@ -46,12 +46,13 @@ module "dev-spoke-project" {
"compute.googleapis.com",
"dns.googleapis.com",
"iap.googleapis.com",
+ "logging.googleapis.com",
+ "monitoring.googleapis.com",
"networkmanagement.googleapis.com",
"networksecurity.googleapis.com",
"servicenetworking.googleapis.com",
- "stackdriver.googleapis.com",
"vmwareengine.googleapis.com",
- "vpcaccess.googleapis.com"
+ "vpcaccess.googleapis.com",
]
shared_vpc_host_config = {
enabled = true
diff --git a/fast/stages/2-networking-c-separate-envs/net-prod.tf b/fast/stages/2-networking-c-separate-envs/net-prod.tf
index 9f7ce84d9..59a15bc89 100644
--- a/fast/stages/2-networking-c-separate-envs/net-prod.tf
+++ b/fast/stages/2-networking-c-separate-envs/net-prod.tf
@@ -46,12 +46,13 @@ module "prod-spoke-project" {
"compute.googleapis.com",
"dns.googleapis.com",
"iap.googleapis.com",
+ "logging.googleapis.com",
+ "monitoring.googleapis.com",
"networkmanagement.googleapis.com",
"networksecurity.googleapis.com",
"servicenetworking.googleapis.com",
- "stackdriver.googleapis.com",
"vmwareengine.googleapis.com",
- "vpcaccess.googleapis.com"
+ "vpcaccess.googleapis.com",
]
shared_vpc_host_config = {
enabled = true
diff --git a/fast/stages/3-gke-dev/main.tf b/fast/stages/3-gke-dev/main.tf
index 5c8b9b331..d34b5434e 100644
--- a/fast/stages/3-gke-dev/main.tf
+++ b/fast/stages/3-gke-dev/main.tf
@@ -58,9 +58,10 @@ module "gke-project-0" {
"gkeconnect.googleapis.com",
"gkehub.googleapis.com",
"iam.googleapis.com",
+ "logging.googleapis.com",
+ "monitoring.googleapis.com",
"multiclusteringress.googleapis.com",
"multiclusterservicediscovery.googleapis.com",
- "stackdriver.googleapis.com",
"trafficdirector.googleapis.com"
]
shared_vpc_service_config = {
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/apigee/README.md b/modules/apigee/README.md
index a4be05599..d209a6a81 100644
--- a/modules/apigee/README.md
+++ b/modules/apigee/README.md
@@ -359,13 +359,13 @@ module "apigee" {
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
-| [project_id](variables.tf#L131) | Project ID. | string | ✓ | |
+| [project_id](variables.tf#L132) | Project ID. | string | ✓ | |
| [addons_config](variables.tf#L17) | Addons configuration. | object({…}) | | null |
| [endpoint_attachments](variables.tf#L29) | Endpoint attachments. | map(object({…})) | | {} |
| [envgroups](variables.tf#L39) | Environment groups (NAME => [HOSTNAMES]). | map(list(string)) | | {} |
| [environments](variables.tf#L46) | Environments. | map(object({…})) | | {} |
-| [instances](variables.tf#L74) | Instances ([REGION] => [INSTANCE]). | map(object({…})) | | {} |
-| [organization](variables.tf#L99) | Apigee organization. If set to null the organization must already exist. | object({…}) | | null |
+| [instances](variables.tf#L74) | Instances ([REGION] => [INSTANCE]). | map(object({…})) | | {} |
+| [organization](variables.tf#L100) | Apigee organization. If set to null the organization must already exist. | object({…}) | | null |
## Outputs
diff --git a/modules/apigee/main.tf b/modules/apigee/main.tf
index 480d19feb..0c8f6bcfa 100644
--- a/modules/apigee/main.tf
+++ b/modules/apigee/main.tf
@@ -113,6 +113,7 @@ resource "google_apigee_nat_address" "apigee_nat" {
}
name = each.key
instance_id = each.value
+ activate = var.instances[each.key].activate_nat
}
resource "google_apigee_instance_attachment" "instance_attachments" {
diff --git a/modules/apigee/variables.tf b/modules/apigee/variables.tf
index e8d1ffb66..b7f9aa6d8 100644
--- a/modules/apigee/variables.tf
+++ b/modules/apigee/variables.tf
@@ -79,6 +79,7 @@ variable "instances" {
disk_encryption_key = optional(string)
display_name = optional(string)
enable_nat = optional(bool, false)
+ activate_nat = optional(bool, false)
environments = optional(list(string), [])
name = optional(string)
runtime_ip_cidr_range = optional(string)
diff --git a/modules/cloud-run-v2/README.md b/modules/cloud-run-v2/README.md
index d4b0825dc..33dd0eaf4 100644
--- a/modules/cloud-run-v2/README.md
+++ b/modules/cloud-run-v2/README.md
@@ -254,7 +254,7 @@ module "cloud_run" {
}
deletion_protection = false
}
-# tftest modules=4 resources=56 fixtures=fixtures/shared-vpc.tf inventory=service-vpc-access-connector-create-sharedvpc.yaml e2e
+# tftest modules=4 resources=59 fixtures=fixtures/shared-vpc.tf inventory=service-vpc-access-connector-create-sharedvpc.yaml e2e
```
## Using Customer-Managed Encryption Key
@@ -420,7 +420,7 @@ module "cloud_run" {
## Cloud Run Invoker IAM Disable
-To disables IAM permission check for `run.routes.invoke` for callers of this service set the `invoker_iam_disabled` variable of the module to `true` (default `false`). There should be no requirement to pass the `roles/run.invoker` to the IAM block to enable public access. This allows for the org policy `domain restricted sharing` org policy remain enabled.
+To disables IAM permission check for `run.routes.invoke` for callers of this service set the `invoker_iam_disabled` variable of the module to `true` (default `false`). There should be no requirement to pass the `roles/run.invoker` to the IAM block to enable public access. This allows for the org policy `domain restricted sharing` org policy remain enabled.
```hcl
module "cloud_run" {
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/examples_e2e/setup_module/main.tf b/tests/examples_e2e/setup_module/main.tf
index 00345532d..d5d6470b4 100644
--- a/tests/examples_e2e/setup_module/main.tf
+++ b/tests/examples_e2e/setup_module/main.tf
@@ -54,7 +54,6 @@ locals {
"servicenetworking.googleapis.com",
"serviceusage.googleapis.com",
"sqladmin.googleapis.com",
- "stackdriver.googleapis.com",
"storage-component.googleapis.com",
"storage.googleapis.com",
"vpcaccess.googleapis.com",
diff --git a/tests/fast/addons/a1_resman_tenants/simple.yaml b/tests/fast/addons/a1_resman_tenants/simple.yaml
index 46cb95a94..2696be7f4 100644
--- a/tests/fast/addons/a1_resman_tenants/simple.yaml
+++ b/tests/fast/addons/a1_resman_tenants/simple.yaml
@@ -28,9 +28,9 @@ counts:
google_project: 4
google_project_iam_audit_config: 2
google_project_iam_binding: 32
- google_project_iam_member: 34
- google_project_service: 54
- google_project_service_identity: 10
+ google_project_iam_member: 36
+ google_project_service: 56
+ google_project_service_identity: 12
google_service_account: 16
google_service_account_iam_binding: 6
google_service_account_iam_member: 2
@@ -43,4 +43,4 @@ counts:
google_tags_tag_key: 1
google_tags_tag_value: 4
modules: 50
- resources: 289
+ resources: 295
diff --git a/tests/fast/stages/s0_bootstrap/cicd.yaml b/tests/fast/stages/s0_bootstrap/cicd.yaml
index f3d8dd4c1..d91dae7c6 100644
--- a/tests/fast/stages/s0_bootstrap/cicd.yaml
+++ b/tests/fast/stages/s0_bootstrap/cicd.yaml
@@ -35,9 +35,9 @@ values:
disabled: null
display_name: null
oidc:
- - allowed_audiences: []
- issuer_uri: https://token.actions.githubusercontent.com
- jwks_json: null
+ - allowed_audiences: []
+ issuer_uri: https://token.actions.githubusercontent.com
+ jwks_json: null
project: fast-prod-iac-core-0
saml: []
timeouts: null
@@ -66,9 +66,9 @@ values:
disabled: null
display_name: null
oidc:
- - allowed_audiences: []
- issuer_uri: https://gitlab.com
- jwks_json: null
+ - allowed_audiences: []
+ issuer_uri: https://gitlab.com
+ jwks_json: null
project: fast-prod-iac-core-0
saml: []
timeouts: null
@@ -133,7 +133,7 @@ values:
? module.automation-tf-bootstrap-r-sa.google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]
: condition: []
members:
- - serviceAccount:fast-prod-bootstrap-1r@fast-prod-iac-core-0.iam.gserviceaccount.com
+ - serviceAccount:fast-prod-bootstrap-1r@fast-prod-iac-core-0.iam.gserviceaccount.com
role: roles/iam.serviceAccountTokenCreator
? module.automation-tf-bootstrap-r-sa.google_storage_bucket_iam_member.bucket-roles["fast-prod-iac-core-outputs-0-organizations/123456789012/roles/storageViewer"]
: bucket: fast-prod-iac-core-outputs-0
@@ -149,10 +149,10 @@ values:
member: serviceAccount:fast-prod-bootstrap-0@fast-prod-iac-core-0.iam.gserviceaccount.com
project: fast-prod-iac-core-0
timeouts: null
- module.automation-tf-bootstrap-sa.google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]:
- condition: []
+ ? module.automation-tf-bootstrap-sa.google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]
+ : condition: []
members:
- - serviceAccount:fast-prod-bootstrap-1@fast-prod-iac-core-0.iam.gserviceaccount.com
+ - serviceAccount:fast-prod-bootstrap-1@fast-prod-iac-core-0.iam.gserviceaccount.com
role: roles/iam.serviceAccountTokenCreator
? module.automation-tf-bootstrap-sa.google_storage_bucket_iam_member.bucket-roles["fast-prod-iac-core-outputs-0-roles/storage.admin"]
: bucket: fast-prod-iac-core-outputs-0
@@ -162,8 +162,8 @@ values:
: condition: []
project: fast-prod-iac-core-0
role: roles/logging.logWriter
- module.automation-tf-cicd-r-sa["bootstrap"].google_service_account.service_account[0]:
- account_id: fast-prod-bootstrap-1r
+ ? module.automation-tf-cicd-r-sa["bootstrap"].google_service_account.service_account[0]
+ : account_id: fast-prod-bootstrap-1r
create_ignore_already_exists: null
description: null
disabled: false
@@ -183,8 +183,8 @@ values:
: condition: []
project: fast-prod-iac-core-0
role: roles/logging.logWriter
- module.automation-tf-cicd-r-sa["resman"].google_service_account.service_account[0]:
- account_id: fast-prod-resman-1r
+ ? module.automation-tf-cicd-r-sa["resman"].google_service_account.service_account[0]
+ : account_id: fast-prod-resman-1r
create_ignore_already_exists: null
description: null
disabled: false
@@ -204,8 +204,8 @@ values:
: condition: []
project: fast-prod-iac-core-0
role: roles/logging.logWriter
- module.automation-tf-cicd-r-sa["resman-tenants"].google_service_account.service_account[0]:
- account_id: fast-prod-resman-tenants-1r
+ ? module.automation-tf-cicd-r-sa["resman-tenants"].google_service_account.service_account[0]
+ : account_id: fast-prod-resman-tenants-1r
create_ignore_already_exists: null
description: null
disabled: false
@@ -225,8 +225,8 @@ values:
: condition: []
project: fast-prod-iac-core-0
role: roles/logging.logWriter
- module.automation-tf-cicd-sa["bootstrap"].google_service_account.service_account[0]:
- account_id: fast-prod-bootstrap-1
+ ? module.automation-tf-cicd-sa["bootstrap"].google_service_account.service_account[0]
+ : account_id: fast-prod-bootstrap-1
create_ignore_already_exists: null
description: null
disabled: false
@@ -246,8 +246,8 @@ values:
: condition: []
project: fast-prod-iac-core-0
role: roles/logging.logWriter
- module.automation-tf-cicd-sa["resman"].google_service_account.service_account[0]:
- account_id: fast-prod-resman-1
+ ? module.automation-tf-cicd-sa["resman"].google_service_account.service_account[0]
+ : account_id: fast-prod-resman-1
create_ignore_already_exists: null
description: null
disabled: false
@@ -256,8 +256,8 @@ values:
member: serviceAccount:fast-prod-resman-1@fast-prod-iac-core-0.iam.gserviceaccount.com
project: fast-prod-iac-core-0
timeouts: null
- module.automation-tf-cicd-sa["resman"].google_service_account_iam_binding.authoritative["roles/iam.workloadIdentityUser"]:
- condition: []
+ ? module.automation-tf-cicd-sa["resman"].google_service_account_iam_binding.authoritative["roles/iam.workloadIdentityUser"]
+ : condition: []
role: roles/iam.workloadIdentityUser
? module.automation-tf-cicd-sa["resman"].google_storage_bucket_iam_member.bucket-roles["fast-prod-iac-core-outputs-0-roles/storage.objectViewer"]
: bucket: fast-prod-iac-core-outputs-0
@@ -267,8 +267,8 @@ values:
: condition: []
project: fast-prod-iac-core-0
role: roles/logging.logWriter
- module.automation-tf-cicd-sa["resman-tenants"].google_service_account.service_account[0]:
- account_id: fast-prod-resman-tenants-1
+ ? module.automation-tf-cicd-sa["resman-tenants"].google_service_account.service_account[0]
+ : account_id: fast-prod-resman-tenants-1
create_ignore_already_exists: null
description: null
disabled: false
@@ -277,8 +277,8 @@ values:
member: serviceAccount:fast-prod-resman-tenants-1@fast-prod-iac-core-0.iam.gserviceaccount.com
project: fast-prod-iac-core-0
timeouts: null
- module.automation-tf-cicd-sa["resman-tenants"].google_service_account_iam_binding.authoritative["roles/iam.workloadIdentityUser"]:
- condition: []
+ ? module.automation-tf-cicd-sa["resman-tenants"].google_service_account_iam_binding.authoritative["roles/iam.workloadIdentityUser"]
+ : condition: []
role: roles/iam.workloadIdentityUser
? module.automation-tf-cicd-sa["resman-tenants"].google_storage_bucket_iam_member.bucket-roles["fast-prod-iac-core-outputs-0-roles/storage.objectViewer"]
: bucket: fast-prod-iac-core-outputs-0
@@ -294,11 +294,11 @@ values:
member: serviceAccount:fast-prod-resman-0r@fast-prod-iac-core-0.iam.gserviceaccount.com
project: fast-prod-iac-core-0
timeouts: null
- module.automation-tf-resman-r-sa.google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]:
- condition: []
+ ? module.automation-tf-resman-r-sa.google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]
+ : condition: []
members:
- - serviceAccount:fast-prod-resman-1r@fast-prod-iac-core-0.iam.gserviceaccount.com
- - serviceAccount:fast-prod-resman-tenants-1r@fast-prod-iac-core-0.iam.gserviceaccount.com
+ - serviceAccount:fast-prod-resman-1r@fast-prod-iac-core-0.iam.gserviceaccount.com
+ - serviceAccount:fast-prod-resman-tenants-1r@fast-prod-iac-core-0.iam.gserviceaccount.com
role: roles/iam.serviceAccountTokenCreator
? module.automation-tf-resman-r-sa.google_storage_bucket_iam_member.bucket-roles["fast-prod-iac-core-outputs-0-organizations/123456789012/roles/storageViewer"]
: bucket: fast-prod-iac-core-outputs-0
@@ -314,11 +314,11 @@ values:
member: serviceAccount:fast-prod-resman-0@fast-prod-iac-core-0.iam.gserviceaccount.com
project: fast-prod-iac-core-0
timeouts: null
- module.automation-tf-resman-sa.google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]:
- condition: []
+ ? module.automation-tf-resman-sa.google_service_account_iam_binding.authoritative["roles/iam.serviceAccountTokenCreator"]
+ : condition: []
members:
- - serviceAccount:fast-prod-resman-1@fast-prod-iac-core-0.iam.gserviceaccount.com
- - serviceAccount:fast-prod-resman-tenants-1@fast-prod-iac-core-0.iam.gserviceaccount.com
+ - serviceAccount:fast-prod-resman-1@fast-prod-iac-core-0.iam.gserviceaccount.com
+ - serviceAccount:fast-prod-resman-tenants-1@fast-prod-iac-core-0.iam.gserviceaccount.com
role: roles/iam.serviceAccountTokenCreator
? module.automation-tf-resman-sa.google_storage_bucket_iam_member.bucket-roles["fast-prod-iac-core-outputs-0-roles/storage.admin"]
: bucket: fast-prod-iac-core-outputs-0
@@ -342,9 +342,9 @@ counts:
google_project: 3
google_project_iam_audit_config: 1
google_project_iam_binding: 19
- google_project_iam_member: 22
- google_project_service: 31
- google_project_service_identity: 7
+ google_project_iam_member: 23
+ google_project_service: 32
+ google_project_service_identity: 8
google_service_account: 12
google_service_account_iam_binding: 12
google_storage_bucket: 4
@@ -356,4 +356,4 @@ counts:
google_tags_tag_value: 2
local_file: 13
modules: 26
- resources: 284
+ resources: 287
diff --git a/tests/fast/stages/s0_bootstrap/simple.yaml b/tests/fast/stages/s0_bootstrap/simple.yaml
index 0dd8ed491..bf907f3e7 100644
--- a/tests/fast/stages/s0_bootstrap/simple.yaml
+++ b/tests/fast/stages/s0_bootstrap/simple.yaml
@@ -27,9 +27,9 @@ counts:
google_project: 3
google_project_iam_audit_config: 1
google_project_iam_binding: 19
- google_project_iam_member: 16
- google_project_service: 31
- google_project_service_identity: 7
+ google_project_iam_member: 17
+ google_project_service: 32
+ google_project_service_identity: 8
google_service_account: 6
google_service_account_iam_binding: 6
google_storage_bucket: 4
@@ -41,7 +41,7 @@ counts:
google_tags_tag_value: 2
local_file: 8
modules: 20
- resources: 247
+ resources: 250
outputs:
automation: __missing__
diff --git a/tests/fast/stages/s2_networking_a_simple/ncc.yaml b/tests/fast/stages/s2_networking_a_simple/ncc.yaml
index 25bc7e9cb..d6eb8f486 100644
--- a/tests/fast/stages/s2_networking_a_simple/ncc.yaml
+++ b/tests/fast/stages/s2_networking_a_simple/ncc.yaml
@@ -36,10 +36,10 @@ counts:
google_network_connectivity_spoke: 2
google_project: 3
google_project_iam_binding: 2
- google_project_iam_member: 20
- google_project_service: 26
- google_project_service_identity: 20
+ google_project_iam_member: 22
+ google_project_service: 28
+ google_project_service_identity: 22
google_storage_bucket_object: 1
google_tags_tag_binding: 3
modules: 23
- resources: 179
+ resources: 185
diff --git a/tests/fast/stages/s2_networking_a_simple/simple.yaml b/tests/fast/stages/s2_networking_a_simple/simple.yaml
index d1f614fed..4e161008e 100644
--- a/tests/fast/stages/s2_networking_a_simple/simple.yaml
+++ b/tests/fast/stages/s2_networking_a_simple/simple.yaml
@@ -40,11 +40,11 @@ counts:
google_monitoring_monitored_project: 2
google_project: 3
google_project_iam_binding: 2
- google_project_iam_member: 20
- google_project_service: 26
- google_project_service_identity: 20
+ google_project_iam_member: 22
+ google_project_service: 28
+ google_project_service_identity: 22
google_storage_bucket_object: 1
google_tags_tag_binding: 3
modules: 28
random_id: 3
- resources: 196
+ resources: 202
diff --git a/tests/fast/stages/s2_networking_a_simple/vpn.yaml b/tests/fast/stages/s2_networking_a_simple/vpn.yaml
index 6d0ceb945..b319e4eab 100644
--- a/tests/fast/stages/s2_networking_a_simple/vpn.yaml
+++ b/tests/fast/stages/s2_networking_a_simple/vpn.yaml
@@ -38,11 +38,11 @@ counts:
google_monitoring_monitored_project: 2
google_project: 3
google_project_iam_binding: 2
- google_project_iam_member: 20
- google_project_service: 26
- google_project_service_identity: 20
+ google_project_iam_member: 22
+ google_project_service: 28
+ google_project_service_identity: 22
google_storage_bucket_object: 1
google_tags_tag_binding: 3
modules: 30
random_id: 17
- resources: 243
+ resources: 249
diff --git a/tests/fast/stages/s2_networking_b_nva/ncc-ra.yaml b/tests/fast/stages/s2_networking_b_nva/ncc-ra.yaml
index 098cfee97..40d81156d 100644
--- a/tests/fast/stages/s2_networking_b_nva/ncc-ra.yaml
+++ b/tests/fast/stages/s2_networking_b_nva/ncc-ra.yaml
@@ -43,11 +43,11 @@ counts:
google_network_connectivity_spoke: 4
google_project: 3
google_project_iam_binding: 2
- google_project_iam_member: 19
- google_project_service: 25
- google_project_service_identity: 19
+ google_project_iam_member: 22
+ google_project_service: 28
+ google_project_service_identity: 22
google_storage_bucket_object: 1
google_tags_tag_binding: 3
modules: 38
random_id: 6
- resources: 260
+ resources: 269
diff --git a/tests/fast/stages/s2_networking_b_nva/regional.yaml b/tests/fast/stages/s2_networking_b_nva/regional.yaml
index 0ccc09abf..5a26ccf55 100644
--- a/tests/fast/stages/s2_networking_b_nva/regional.yaml
+++ b/tests/fast/stages/s2_networking_b_nva/regional.yaml
@@ -45,11 +45,11 @@ counts:
google_monitoring_monitored_project: 2
google_project: 3
google_project_iam_binding: 2
- google_project_iam_member: 19
- google_project_service: 25
- google_project_service_identity: 19
+ google_project_iam_member: 22
+ google_project_service: 28
+ google_project_service_identity: 22
google_storage_bucket_object: 1
google_tags_tag_binding: 3
modules: 46
random_id: 6
- resources: 270
+ resources: 279
diff --git a/tests/fast/stages/s2_networking_b_nva/simple.yaml b/tests/fast/stages/s2_networking_b_nva/simple.yaml
index 631e6aa69..c43610986 100644
--- a/tests/fast/stages/s2_networking_b_nva/simple.yaml
+++ b/tests/fast/stages/s2_networking_b_nva/simple.yaml
@@ -45,11 +45,11 @@ counts:
google_monitoring_monitored_project: 2
google_project: 3
google_project_iam_binding: 2
- google_project_iam_member: 19
- google_project_service: 25
- google_project_service_identity: 19
+ google_project_iam_member: 22
+ google_project_service: 28
+ google_project_service_identity: 22
google_storage_bucket_object: 1
google_tags_tag_binding: 3
modules: 42
random_id: 6
- resources: 246
+ resources: 255
diff --git a/tests/fast/stages/s2_networking_c_separate_envs/simple.yaml b/tests/fast/stages/s2_networking_c_separate_envs/simple.yaml
index 3b74dc348..a66f3e6c0 100644
--- a/tests/fast/stages/s2_networking_c_separate_envs/simple.yaml
+++ b/tests/fast/stages/s2_networking_c_separate_envs/simple.yaml
@@ -38,11 +38,11 @@ counts:
google_monitoring_dashboard: 6
google_project: 2
google_project_iam_binding: 2
- google_project_iam_member: 16
- google_project_service: 20
- google_project_service_identity: 16
+ google_project_iam_member: 18
+ google_project_service: 22
+ google_project_service_identity: 18
google_storage_bucket_object: 1
google_tags_tag_binding: 2
modules: 22
random_id: 6
- resources: 216
+ resources: 222
diff --git a/tests/fast/stages/s3_gke_dev/simple.yaml b/tests/fast/stages/s3_gke_dev/simple.yaml
index b18b091e4..26ca07c82 100644
--- a/tests/fast/stages/s3_gke_dev/simple.yaml
+++ b/tests/fast/stages/s3_gke_dev/simple.yaml
@@ -19,9 +19,9 @@ counts:
google_container_node_pool: 1
google_project: 1
google_project_iam_binding: 1
- google_project_iam_member: 16
- google_project_service: 12
- google_project_service_identity: 7
+ google_project_iam_member: 17
+ google_project_service: 13
+ google_project_service_identity: 8
google_service_account: 1
modules: 5
- resources: 42
+ resources: 45
diff --git a/tests/fixtures/shared-vpc.tf b/tests/fixtures/shared-vpc.tf
index 24767d228..fcab1b9a7 100644
--- a/tests/fixtures/shared-vpc.tf
+++ b/tests/fixtures/shared-vpc.tf
@@ -43,11 +43,12 @@ module "project-service" {
"dns.googleapis.com",
"eventarc.googleapis.com",
"iam.googleapis.com",
+ "logging.googleapis.com",
+ "monitoring.googleapis.com",
"run.googleapis.com",
"secretmanager.googleapis.com",
"servicenetworking.googleapis.com",
"serviceusage.googleapis.com",
- "stackdriver.googleapis.com",
"storage-component.googleapis.com",
"storage.googleapis.com",
"vpcaccess.googleapis.com",
diff --git a/tests/modules/apigee/test_apigee_nat_activate.tfvars b/tests/modules/apigee/test_apigee_nat_activate.tfvars
new file mode 100644
index 000000000..d313605c3
--- /dev/null
+++ b/tests/modules/apigee/test_apigee_nat_activate.tfvars
@@ -0,0 +1,24 @@
+project_id = "my-project"
+organization = {
+ display_name = "My Organization"
+ description = "My Organization"
+ runtime_type = "CLOUD"
+ billing_type = "Pay-as-you-go"
+ database_encryption_key = "123456789"
+ analytics_region = "europe-west1"
+ disable_vpc_peering = true
+}
+environments = {
+ apis-test = {
+ display_name = "APIs test"
+ description = "APIs Test"
+ envgroups = ["test"]
+ }
+}
+instances = {
+ europe-west1 = {
+ environments = ["europe-west1"]
+ enable_nat = true
+ activate_nat = true
+ }
+}
\ No newline at end of file
diff --git a/tests/modules/apigee/test_apigee_nat_activate.yaml b/tests/modules/apigee/test_apigee_nat_activate.yaml
new file mode 100644
index 000000000..3bf65d5b7
--- /dev/null
+++ b/tests/modules/apigee/test_apigee_nat_activate.yaml
@@ -0,0 +1,45 @@
+# 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.
+
+values:
+ google_apigee_environment.environments["apis-test"]:
+ description: APIs Test
+ display_name: APIs test
+ name: apis-test
+ google_apigee_instance.instances["europe-west1"]:
+ description: Terraform-managed
+ disk_encryption_key_name: null
+ display_name: null
+ location: europe-west1
+ name: instance-europe-west1
+ google_apigee_organization.organization[0]:
+ analytics_region: europe-west1
+ authorized_network: null
+ billing_type: Pay-as-you-go
+ description: null
+ display_name: null
+ project_id: my-project
+ retention: DELETION_RETENTION_UNSPECIFIED
+ runtime_database_encryption_key_name: '123456789'
+ runtime_type: CLOUD
+ disable_vpc_peering: true
+ google_apigee_nat_address.apigee_nat["europe-west1"]:
+ activate: true
+
+counts:
+ google_apigee_environment: 1
+ google_apigee_instance: 1
+ google_apigee_instance_attachment: 1
+ google_apigee_organization: 1
+ google_apigee_nat_address: 1
\ No newline at end of file
diff --git a/tests/modules/apigee/tftest.yaml b/tests/modules/apigee/tftest.yaml
index 6449de757..8452a0b89 100644
--- a/tests/modules/apigee/tftest.yaml
+++ b/tests/modules/apigee/tftest.yaml
@@ -28,3 +28,4 @@ tests:
organization_only_psc_mode:
organization_only_vpc_mode:
organization_retention:
+ test_apigee_nat_activate:
diff --git a/tests/modules/cloud_run_v2/examples/service-vpc-access-connector-create-sharedvpc.yaml b/tests/modules/cloud_run_v2/examples/service-vpc-access-connector-create-sharedvpc.yaml
index 5aaffe211..d675f70c1 100644
--- a/tests/modules/cloud_run_v2/examples/service-vpc-access-connector-create-sharedvpc.yaml
+++ b/tests/modules/cloud_run_v2/examples/service-vpc-access-connector-create-sharedvpc.yaml
@@ -44,6 +44,6 @@ counts:
google_cloud_run_v2_service: 1
google_vpc_access_connector: 1
modules: 4
- resources: 56
+ resources: 59
outputs: {}
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