diff --git a/fast/stages/2-networking/README.md b/fast/stages/2-networking/README.md
index f16324d60..fbff5c7ee 100644
--- a/fast/stages/2-networking/README.md
+++ b/fast/stages/2-networking/README.md
@@ -187,7 +187,7 @@ Note that the stage doesn't force you to create projects; factories also allow y
The VPC factory allows for the definition of an arbitrary number of VPCs, along with their subnets, routes, firewall rules, and connectivity settings, all through YAML files.
-VPCs are defined in `.config.yaml` files within the `vpcs/[vpc-name]` directory of a dataset. This file contains the VPC's main configuration. Subnets, firewall rules, and VPNs are defined in subdirectories within each VPC's folder.
+VPCs are defined in `.config.yaml` files within the `vpcs/[vpc-name]` directory of a dataset. This file contains the VPC's main configuration. Subnets, firewall rules, VPNs, and VLAN attachments are defined in subdirectories within each VPC's folder. An optional `addresses.yaml` file can be placed in the VPC directory to define arbitrary addresses (external, internal, global, PSC).
### DNS
@@ -372,6 +372,7 @@ Internally created resources are mapped to context namespaces, and use specific
| name | description | modules | resources |
|---|---|---|---|
+| [factory-addresses.tf](./factory-addresses.tf) | Arbitrary addresses factory. | net-address | |
| [factory-cloudnat.tf](./factory-cloudnat.tf) | Cloud NAT factory. | net-address · net-cloudnat | |
| [factory-dns.tf](./factory-dns.tf) | DNS zones and RPZ factory. | dns · dns-response-policy | |
| [factory-firewall-policies.tf](./factory-firewall-policies.tf) | Firewall policies factory. | net-firewall-policy | |
diff --git a/fast/stages/2-networking/factory-addresses.tf b/fast/stages/2-networking/factory-addresses.tf
new file mode 100644
index 000000000..872310068
--- /dev/null
+++ b/fast/stages/2-networking/factory-addresses.tf
@@ -0,0 +1,38 @@
+/**
+ * Copyright 2026 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 Arbitrary addresses factory.
+
+module "vpc-addresses" {
+ source = "../../../modules/net-address"
+ for_each = { for k, v in local.vpcs : k => v if length(v.addresses) > 0 }
+
+ project_id = each.value.project_id
+
+ external_addresses = try(each.value.addresses.external, {})
+ global_addresses = try(each.value.addresses.global, {})
+ internal_addresses = try(each.value.addresses.internal, {})
+ psa_addresses = try(each.value.addresses.psa, {})
+ psc_addresses = try(each.value.addresses.psc, {})
+ network_attachments = try(each.value.addresses.network_attachments, {})
+
+ context = {
+ locations = local.ctx.locations
+ networks = local.ctx_vpcs.self_links
+ project_ids = local.ctx_projects.project_ids
+ subnets = local.ctx_vpcs.subnets_by_vpc
+ }
+}
diff --git a/fast/stages/2-networking/factory-vpcs.tf b/fast/stages/2-networking/factory-vpcs.tf
index a580a1ced..f8faab097 100644
--- a/fast/stages/2-networking/factory-vpcs.tf
+++ b/fast/stages/2-networking/factory-vpcs.tf
@@ -27,6 +27,7 @@ locals {
{
factory_dirname = dirname(f)
factory_basepath = "${local.paths.vpcs}/${dirname(f)}"
+ addresses = try(yamldecode(file("${local.paths.vpcs}/${dirname(f)}/addresses.yaml")), {})
}
)
]
diff --git a/fast/stages/2-networking/schemas/addresses.schema.json b/fast/stages/2-networking/schemas/addresses.schema.json
new file mode 100644
index 000000000..3aea00deb
--- /dev/null
+++ b/fast/stages/2-networking/schemas/addresses.schema.json
@@ -0,0 +1,116 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "title": "Addresses",
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "external": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["region"],
+ "properties": {
+ "region": { "type": "string" },
+ "description": { "type": "string" },
+ "ipv6": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["endpoint_type"],
+ "properties": {
+ "endpoint_type": { "type": "string", "enum": ["NETLB", "VM"] }
+ }
+ },
+ "labels": { "type": "object", "additionalProperties": { "type": "string" } },
+ "name": { "type": "string" },
+ "subnetwork": { "type": "string" },
+ "tier": { "type": "string", "enum": ["PREMIUM", "STANDARD"] }
+ }
+ }
+ },
+ "global": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "description": { "type": "string" },
+ "ipv6": { "type": "object", "additionalProperties": true },
+ "name": { "type": "string" }
+ }
+ }
+ },
+ "internal": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["region", "subnetwork"],
+ "properties": {
+ "region": { "type": "string" },
+ "subnetwork": { "type": "string" },
+ "address": { "type": "string" },
+ "description": { "type": "string" },
+ "labels": { "type": "object", "additionalProperties": { "type": "string" } },
+ "name": { "type": "string" },
+ "purpose": { "type": "string" },
+ "tier": { "type": "string" }
+ }
+ }
+ },
+ "psa": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["address", "network", "prefix_length"],
+ "properties": {
+ "address": { "type": "string" },
+ "network": { "type": "string" },
+ "prefix_length": { "type": "number" },
+ "description": { "type": "string" },
+ "name": { "type": "string" }
+ }
+ }
+ },
+ "psc": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "object",
+ "additionalProperties": false,
+ "properties": {
+ "address": { "type": "string" },
+ "description": { "type": "string" },
+ "name": { "type": "string" },
+ "network": { "type": "string" },
+ "region": { "type": "string" },
+ "subnet_self_link": { "type": "string" },
+ "service_attachment": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["psc_service_attachment_link"],
+ "properties": {
+ "psc_service_attachment_link": { "type": "string" },
+ "global_access": { "type": "boolean" }
+ }
+ }
+ }
+ }
+ },
+ "network_attachments": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["subnet_self_link"],
+ "properties": {
+ "subnet_self_link": { "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" } }
+ }
+ }
+ }
+ }
+}
diff --git a/fast/stages/2-networking/schemas/addresses.schema.md b/fast/stages/2-networking/schemas/addresses.schema.md
new file mode 100644
index 000000000..5b13669b4
--- /dev/null
+++ b/fast/stages/2-networking/schemas/addresses.schema.md
@@ -0,0 +1,22 @@
+# Addresses
+
+
+
+## Properties
+
+*additional properties: false*
+
+- **external**: *object*
+
*additional properties: object*
+- **global**: *object*
+
*additional properties: object*
+- **internal**: *object*
+
*additional properties: object*
+- **psa**: *object*
+
*additional properties: object*
+- **psc**: *object*
+
*additional properties: object*
+- **network_attachments**: *object*
+
*additional properties: object*
+
+## Definitions
diff --git a/tests/fast/stages/s2_networking/addresses.tfvars b/tests/fast/stages/s2_networking/addresses.tfvars
new file mode 100644
index 000000000..ea72087f4
--- /dev/null
+++ b/tests/fast/stages/s2_networking/addresses.tfvars
@@ -0,0 +1,22 @@
+automation = { outputs_bucket = "test" }
+billing_account = { id = "000000-111111-222222" }
+factories_config = {
+ dataset = "./data-testaddresses"
+ paths = { defaults = "defaults.yaml" }
+}
+folder_ids = {
+ "networking" = "folders/12345678"
+ "networking/prod" = "folders/23456789"
+ "networking/dev" = "folders/34567890"
+}
+organization = { domain = "fast.example.com", id = 123456789012, customer_id = "C00000000" }
+prefix = "fast"
+service_accounts = {
+ "iac-0/iac-pf-rw" = "iac-pf-rw@test.iam.gserviceaccount.com"
+ "iac-0/iac-pf-ro" = "iac-pf-ro@test.iam.gserviceaccount.com"
+}
+storage_buckets = { "iac-0/iac-outputs" = "test" }
+tag_values = {
+ "environment/development" = "tagValues/12345"
+ "environment/production" = "tagValues/12346"
+}
diff --git a/tests/fast/stages/s2_networking/addresses.yaml b/tests/fast/stages/s2_networking/addresses.yaml
new file mode 100644
index 000000000..d4735e01f
--- /dev/null
+++ b/tests/fast/stages/s2_networking/addresses.yaml
@@ -0,0 +1,27 @@
+# Copyright 2026 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_address: 4
+ google_compute_forwarding_rule: 1
+ google_compute_global_address: 3
+ google_compute_global_forwarding_rule: 1
+ google_compute_network: 1
+ google_compute_network_attachment: 1
+ google_compute_route: 3
+ google_compute_subnetwork: 1
+ google_storage_bucket_object: 2
+ modules: 4
+ resources: 19
+ terraform_data: 2
diff --git a/tests/fast/stages/s2_networking/data-testaddresses/defaults.yaml b/tests/fast/stages/s2_networking/data-testaddresses/defaults.yaml
new file mode 100644
index 000000000..ee767201f
--- /dev/null
+++ b/tests/fast/stages/s2_networking/data-testaddresses/defaults.yaml
@@ -0,0 +1,30 @@
+# Copyright 2026 Google LLC
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# yaml-language-server: $schema=../../schemas/defaults.schema.json
+context:
+ locations:
+ primary: europe-west1
+ secondary: europe-west3
+ iam_principals: {}
+projects:
+ defaults:
+ locations:
+ storage: eu
+vpcs:
+ auto_create_subnetworks: false
+ delete_default_routes_on_create: true
+ mtu: 1500
+output_files:
+ storage_bucket: $storage_buckets:iac-0/iac-outputs
diff --git a/tests/fast/stages/s2_networking/data-testaddresses/vpcs/core/.config.yaml b/tests/fast/stages/s2_networking/data-testaddresses/vpcs/core/.config.yaml
new file mode 100644
index 000000000..b97e563cd
--- /dev/null
+++ b/tests/fast/stages/s2_networking/data-testaddresses/vpcs/core/.config.yaml
@@ -0,0 +1,18 @@
+# Copyright 2026 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/vpc.schema.json
+project_id: $project_ids:net-core-0
+name: core-0
+delete_default_routes_on_create: true
diff --git a/tests/fast/stages/s2_networking/data-testaddresses/vpcs/core/addresses.yaml b/tests/fast/stages/s2_networking/data-testaddresses/vpcs/core/addresses.yaml
new file mode 100644
index 000000000..953824475
--- /dev/null
+++ b/tests/fast/stages/s2_networking/data-testaddresses/vpcs/core/addresses.yaml
@@ -0,0 +1,57 @@
+# Copyright 2026 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/addresses.schema.json
+
+external:
+ test-ext:
+ region: $locations:primary
+
+global:
+ test-global: {}
+
+internal:
+ test-int:
+ region: $locations:primary
+ subnetwork: $subnets:core/europe-west1/core-default
+
+psc:
+ # 1. Regional PSC Address only
+ psc-reg:
+ region: $locations:primary
+ subnet_self_link: $subnets:core/europe-west1/core-default
+
+ # 2. Regional PSC Address + Forwarding Rule
+ psc-reg-fr:
+ region: $locations:primary
+ subnet_self_link: $subnets:core/europe-west1/core-default
+ service_attachment:
+ psc_service_attachment_link: projects/producer-project/regions/europe-west1/serviceAttachments/my-sa
+
+ # 3. Global PSC Address only
+ psc-global:
+ network: $networks:core
+ address: "10.0.0.32"
+
+ # 4. Global PSC Address + Forwarding Rule
+ psc-global-fr:
+ network: $networks:core
+ address: "10.0.0.33"
+ service_attachment:
+ psc_service_attachment_link: projects/producer-project/global/serviceAttachments/my-global-sa
+
+network_attachments:
+ # 5. PSC Network Attachment (Producer side)
+ test-na:
+ subnet_self_link: projects/test-project/regions/europe-west1/subnetworks/core-default
diff --git a/tests/fast/stages/s2_networking/data-testaddresses/vpcs/core/subnets/core-default.yaml b/tests/fast/stages/s2_networking/data-testaddresses/vpcs/core/subnets/core-default.yaml
new file mode 100644
index 000000000..3ed79aea0
--- /dev/null
+++ b/tests/fast/stages/s2_networking/data-testaddresses/vpcs/core/subnets/core-default.yaml
@@ -0,0 +1,19 @@
+# Copyright 2026 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/subnet.schema.json
+name: core-default
+region: $locations:primary
+ip_cidr_range: 10.71.0.0/24
+description: Default primary-region subnet for core
diff --git a/tests/fast/stages/s2_networking/tftest.yaml b/tests/fast/stages/s2_networking/tftest.yaml
index c4aea7a8a..10f3c4945 100644
--- a/tests/fast/stages/s2_networking/tftest.yaml
+++ b/tests/fast/stages/s2_networking/tftest.yaml
@@ -35,3 +35,8 @@ tests:
extra_dirs:
- ../../../tests/fast/stages/s2_networking/data-testvlan
vpns:
+ addresses:
+ inventory:
+ - addresses.yaml
+ extra_dirs:
+ - ../../../tests/fast/stages/s2_networking/data-testaddresses
diff --git a/tests/fast/stages/s2_networking/vlan_attachments.yaml b/tests/fast/stages/s2_networking/vlan_attachments.yaml
index a014db5fd..cab8cfd48 100644
--- a/tests/fast/stages/s2_networking/vlan_attachments.yaml
+++ b/tests/fast/stages/s2_networking/vlan_attachments.yaml
@@ -4,7 +4,7 @@
# 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
+# 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,
@@ -155,7 +155,6 @@ values:
content_language: null
contexts: []
customer_encryption: []
- deletion_policy: null
detect_md5hash: null
event_based_hold: null
force_empty_content_type: null
@@ -173,7 +172,6 @@ values:
module.projects.module.projects["net-core-0"].google_project.project[0]:
auto_create_network: false
billing_account: 000000-111111-222222
- deletion_policy: DELETE
effective_labels:
goog-terraform-provisioned: 'true'
folder_id: '12345678'
@@ -718,4 +716,3 @@ outputs:
hub: {}
subnet_self_links: __missing__
vpc_self_links: __missing__
-