# Shared Networking Resources

<!-- BEGIN TOC -->
- [Quickstart](#quickstart)
  - [Select and configure a factory dataset](#select-and-configure-a-factory-dataset)
  - [Defaults file](#defaults-file)
  - [Terraform variables configuration](#terraform-variables-configuration)
  - [Linking FAST output files](#linking-fast-output-files)
  - [Terraform init/apply cycle](#terraform-initapply-cycle)
- [Default Dataset: Hub and Spoke with VPC Peering](#default-dataset-hub-and-spoke-with-vpc-peering)
- [Design overview and choices](#design-overview-and-choices)
  - [Networking projects](#networking-projects)
  - [VPCs](#vpcs)
  - [DNS](#dns)
    - [Delegated Public Zones](#delegated-public-zones)
  - [Firewall Policies](#firewall-policies)
  - [Cloud NAT and Routers](#cloud-nat-and-routers)
  - [VPC Connectivity](#vpc-connectivity)
- [Context-based interpolation](#context-based-interpolation)
- [Files](#files)
- [Variables](#variables)
- [Outputs](#outputs)
<!-- END TOC -->

This stage sets up the networking infrastructure for the organization. It provides a flexible, factory-based approach to defining and managing networking resources, including:

- VPCs
  - Subnets
  - VPC Firewall rules
  - Routes
  - DNS policies
- DNS
  - Forwarding, Public, Private Zones
  - Response Policy Rules
- Firewall policies
- VPC connectivity
  - Network Connectivity Center (NCC)
  - Network Virtual Appliances (NVAs)
  - VPC Peering
  - VPN

Several common networking patterns are available as "[datasets](datasets/)" that can be easily used as is or adapted as needed.

## Quickstart

This stage is designed to be applied after stage 0, but like any other FAST stage, it can also be used in isolation, provided the required prerequisites are met. This section details the FAST usage, where prerequisites are already in place.

The high-level flow for running this stage is:

- check the **factory data set** as configured in `var.factories_config` and do any necessary edits to match your configuration (VPCs, Subnetting, DNS zones, Onprem connectivity, etc.)
- review the **defaults.yaml file** for your chosen dataset
- define a simple tfvars file if your dataset is in a non-standard path, and/or you want local output files (recommended for initial setups)
- bring in or link the prerequisite files generated by the previous stage
- run `terraform init` and `apply`

### Select and configure a factory dataset

The default dataset describes multiple different networking patterns.
It currently implements the following:

- **Hub and spoke (w/ NCC)**: Environment-based VPCs interconnected through an NCC full-mesh, resulting in full routing line-of-sight between spokes ([dataset](./datasets/hub-and-spokes-ncc/))
- **Hub and spoke (w/ NVA)**: Environment-based VPCs interconnected through a Network Virtual Appliance, allowing for centralized traffic inspection and control ([dataset](./datasets/hub-and-spokes-nva/))
- **Hub and spoke (w/ VPC Peering)**: Environment-based VPCs interconnected through VPC peering, resulting in full isolation between spokes ([dataset](./datasets/hub-and-spokes-peerings/))
- **Hub and spoke (w/ VPN)**: Environment-based VPCs interconnected through HA VPN, with full routing line-of-sight between spokes ([dataset](./datasets/hub-and-spokes-vpns/))

### Defaults file

Configurations defaults are stored in the `defaults.yaml` file in the selected dataset. Relocating the defaults file is good practice to avoid accidental changes from upstream, this is done via the `factories_config.default` variable attribute.

Once a suitable place has been found for the file, edit it to match the desired configuration. Several pieces of information coming from the previous stage (prefix, billing account, etc.) are pre-populated in the project defaults so they don't need to be explicitly set.

### Terraform variables configuration

A tfvars file allows you to control paths for the project factories data and to enable local output files generation. This example shows how to override all the factory paths and how to enable output files.

```hcl
factories_config = {
  defaults              = "/some/path/data/defaults.yaml"
  dns                   = "/some/path/data/dns/zones"
  dns-response-policies = "/some/path/data/dns/response-policies"
  firewall-policies     = "/some/path/data/firewall-policies"
  folders               = "/some/path/data/folders"
  interconnect          = "/some/path/data/interconnect"
  ncc-hubs              = "/some/path/data/ncc-hubs"
  nvas                  = "/some/path/data/nvas"
  projects              = "/some/path/data/projects"
  vpcs                  = "/some/path/data/vpcs"
}
outputs_location = "~/fast-config"
```

### Linking FAST output files

If you enabled local output files in the previous stage, run this command replacing the example path with the one for your output files.

```bash
../fast-links.sh ~/fast-config

# File linking commands for networking stage

# provider file
ln -s ~/fast-config/providers/2-networking-providers.tf ./

# input files from other stages
ln -s ~/fast-config/tfvars/0-globals.auto.tfvars.json ./
ln -s ~/fast-config/tfvars/0-org-setup.auto.tfvars.json ./

# conventional location for this stage terraform.tfvars (manually managed)
ln -s ~/fast-config/2-networking.auto.tfvars ./
```

If you have no local output files, check the previous state's outputs for the name of your GCS outputs bucket and replace it in the example below.

```bash
../fast-links.sh gs://myprefix-prod-iac-org-0-iac-outputs

# File linking commands for networking stage

# provider file
gcloud storage cp gs://myprefix-prod-iac-org-0-iac-outputs/providers/2-networking-providers.tf ./

# input files from other stages
gcloud storage cp gs://myprefix-prod-iac-org-0-iac-outputs/tfvars/0-globals.auto.tfvars.json ./
gcloud storage cp gs://myprefix-prod-iac-org-0-iac-outputs/tfvars/0-org-setup.auto.tfvars.json ./

# conventional location for this stage terraform.tfvars (manually managed)
gcloud storage cp gs://myprefix-prod-iac-org-0-iac-outputs/2-networking.auto.tfvars ./
```

Once you have one of the above outputs, copy/paste it in your terminal from within this stage's folder.

Note that the last command in both outputs is optional: this is our recommended best practice to centrally store the tfvars file you created for this stage. If this convention works for you, move the tfvars file created in the previous steps to the path shown in the output, then run the command.

### Terraform init/apply cycle

Once everything is set up, simply run the usual `init`/`apply` cycle.

```bash
terraform init
terraform apply
```

## Default Dataset: Hub and Spoke with VPC Peering

This stage includes a default dataset that implements a **Hub and Spoke** topology using [VPC Network Peering](https://cloud.google.com/vpc/docs/vpc-peering). This model is ideal for achieving network isolation between different environments (e.g., development and production) while centralizing shared services and connectivity in a hub network.

The architecture consists of:

- A **hub VPC** (`hub`) for centralized resources, such as DNS and connectivity to on-premises networks (VPN).
- Two **spoke VPCs** (`dev` and `prod`) for different environments.
- **VPC Peering** connections between the hub and each spoke. Because peering is not transitive, the spokes are isolated from each other.
- Each VPC is deployed in its own dedicated **Shared VPC host project**, allowing for clear separation of resources and IAM policies.

For more details on this dataset, refer to its [dedicated README file](./datasets/hub-and-spokes-peerings/README.md).

## Design overview and choices

The following diagram shows the canonical paths for the different factory configurations within a dataset.

```tree
.
├── dns
│   ├── response-policies    # Response Policy Rules for DNS.
│   └── zones                # DNS zones (private, forwarding, peering).
├── firewall-policies        # Hierarchical firewall policies.
├── ncc-hubs                 # NCC configurations.
├── nvas                     # NVA configurations.
├── projects                 # Project definitions.
└── vpcs
    └── [vpc-name]           # Each subfolder represents a VPC.
        ├── .config.yaml     # Main VPC configuration, peerings, NAT.
        ├── firewall-rules   # VPC-level firewall rules.
        ├── subnets          # Subnet definitions.
        ├── vlan-attachments # VLAN attachment configurations.
        └── vpns             # VPN configurations.
```

### Networking projects

A [project factory](../../../modules/project-factory/) is embedded in this stage ([factory-projects.tf](./factory-projects.tf)) to create the projects that will host networking resources.

The project factory is configured via YAML files in the `projects` directory of your chosen dataset. Each file defines a project, its parent folder, the services to be enabled, and IAM bindings. In the default dataset, three projects are created: `net-core-0` for the hub, `net-dev-0` for the development spoke, and `net-prod-0` for the production spoke. All are configured as Shared VPC hosts.

Note that the stage doesn't force you to create projects; factories also allow you to use externally-managed projects by specifying their project ID.

### VPCs

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, 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

The DNS factory manages Cloud DNS zones and Response Policy Rules. DNS zones are by default defined within the `dns/zones` directory of your chosen dataset. The factory supports public, private, peering, and forwarding zones.

In the default dataset, DNS is centralized in the `net-core-0` (hub) project. It hosts:

- A **forwarding zone** to resolve on-premises domains.
- A **private zone** for the cloud environment (e.g., `test.`).
- **Peering zones** to make its DNS resolution available to the spoke VPCs.

The spoke VPCs have their own private zones for subdomains (e.g., `dev.test.`) and use the hub for all other DNS lookups.

#### Delegated Public Zones

The factory also supports delegating public zones, which is useful for scenarios where a parent public zone is managed in one project, and you want to delegate a subdomain to a different project.

To configure this, you first define the parent public zone. Then, for the delegated zone, you specify `delegation_config` pointing to the parent zone. This automatically creates the necessary `NS` (and `DS` records if you are using DNSSEC) records in the parent zone.

Here's an example of how to set this up:

```yaml
# Parent public zone (e.g., in dns/zones/net-core-0/pub-gcp-example-com.yaml)
#
# yaml-language-server: $schema=../../../../../schemas/dns.schema.json

project_id: $project_ids:net-core-0
domain: gcp.example.com.
public:
  enable_logging: false
  dnssec_config:
    state: "on"
    non_existence: "nsec3"
    key_signing_key:
      algorithm: "ecdsap256sha256"
      key_length: 256
    zone_signing_key:
      algorithm: "ecdsap256sha256"
      key_length: 256
recordsets:
  "A localhost":
    records: ["127.0.0.1"]
delegations:
  - net-dev-0/pub-dev-gcp-example-com
```

```yaml
# Delegated child zone (e.g., in dns/zones/net-dev-0/pub-dev-gcp-example-com.yaml)
#
# yaml-language-server: $schema=../../../../../schemas/dns.schema.json

project_id: $project_ids:net-dev-0
domain: dev.gcp.example.com.
public:
  enable_logging: false
  dnssec_config:
    state: "on"
    non_existence: "nsec3"
    key_signing_key:
      algorithm: "ecdsap256sha256"
      key_length: 256
    zone_signing_key:
      algorithm: "ecdsap256sha256"
      key_length: 256
recordsets:
  "A localhost":
    records: ["127.0.0.1"]

```

In this example:
- A public zone for `gcp.example.com.` is created in the `net-core-0` project.
- A separate public zone for `dev.gcp.example.com.` is created in the `net-dev-0` project.
- The `delegation_config` in the parent zone tells the factory to find the child zone named `pub-dev-gcp-example-com` in the `net-dev-0` project and create the necessary `NS` and `DS` records to make the delegation effective.

### Firewall Policies

This stage supports both VPC-level firewall rules and hierarchical firewall policies.

- **Hierarchical Firewall Policies** are defined in the `firewall-policies` directory. They are attached to folders or the organization and are useful for enforcing baseline security rules across multiple projects (e.g., allowing health checks and IAP access).
- **VPC Firewall Rules** are defined within each VPC's configuration directory (`vpcs/[vpc-name]/firewall-rules`). These are used for more specific rules tailored to a particular VPC.

### Cloud NAT and Routers

- **Cloud NAT:** The NAT factory manages Cloud NAT gateways, allowing instances without external IP addresses to access the internet. NAT is configured within each VPC's `.config.yaml` file.

For example:

```yaml
# [...]
nat_config:
  nat-ew8:
    region: $locations:primary
# [...]
```

- **External IPs for NAT:** You can specify how many static IPs do you want, so they would be reserved and kept static. This is helpful when you need to connect to an external service that needs to whitelist the IPs. This is done by defining the number of static IP addresses in the `nat` section of a VPC's `.config.yaml` file.

For example:

```yaml
# [...]
nat_config:
  nat-ew8:
    num_nat_ips: 3
    region: $locations:primary
# [...]
```

- **Cloud Routers:** The `factory-routers.tf` file manages Cloud Routers, which are used with Cloud VPN and Cloud Interconnect to exchange routes with on-premises networks. Routers are configured within each VPC's `.config.yaml` file.

```yaml
# [...]
routers:
  vpn-router:
    region: $locations:primary
    asn: 64514
# [...]
```

### VPC Connectivity

This stage supports multiple ways to connect VPCs to other VPCs or other networks:

- **VPC Peering:** Managed via the `peering_config` section in a VPC's `.config.yaml` file.
- **VLAN Attachments:** Partner or Dedicated Interconnect VLAN attachments are defined in the `vpcs/[vpc-name]/vlan-attachments` directory. By default, they are disabled by passing a non-existing directory via `factories_config`.
- **VPNs:** High-availability VPNs are defined in the `vpcs/[vpc-name]/vpns` directory.
- **Network Connectivity Center (NCC):** Managed via the `ncc_config` section in a VPC's `.config.yaml` file.

## Context-based interpolation

Interpolation allows referring to resources that are either created at runtime or externally managed via short aliases.

This feature has two main benefits:

- Being able to refer to resource IDs which are not known before creation (e.g., a VPC's self-link).
- Making YAML configuration files more readable and portable by using mnemonic keys instead of hardcoded values.

The two VPC snippets below show an extended example:

```yaml
# Hub VPC configuration
project_id: $project_ids:net-core-0
name: hub
peering_config:
  to-prod:
    peer_network: $networks:prod
```

```yaml
# Prod spoke VPC configuration
project_id: $project_ids:net-prod-0
name: prod
peering_config:
  to-hub:
    peer_network: $networks:hub
```

Interpolations leverage contexts from two separate sources: resources managed by the different factories (projects, folders, vpcs, etc.) and user-defined resource IDs passed in via the `context` variable.

Context replacements use the `$` prefix and are accessible via namespaces that match the attributes in the context variable.

Context variables are accessed by keys that match the YAML file name for resources declared in individual files (projects, folders, custom roles, etc.), or the key in the YAML map where the resource is declared for other resources (service accounts, buckets, etc.). In case of objects that are logically part of another resource (e.g. NCC Groups part of NCC Hubs, Routers part of a VPC), the key will be composite.

Assuming keys of the form `my_folder`, `my_project`, `my_vpc`, etc. this is an example of referencing the actual IDs via interpolation in YAML files.

- `$addresses:my_vpc/my_address`
- `$folder_ids:my_folder`
- `$locations:my_region`
- `$ncc_groups:my_hub/my_group`
- `$ncc_hubs:my_hub`
- `$networks:my_vpc`
- `$project_ids:my_project`
- `$routers:my_vpc/my_vpn`
- `$vpn_gateways:my_vpc/my_gateway`

Internally created resources are mapped to context namespaces, and use specific prefixes to express the relationship with their container.

<!-- TFDOC OPTS files:1 -->
<!-- BEGIN TFDOC -->
## Files

| name | description | modules | resources |
|---|---|---|---|
| [factory-addresses.tf](./factory-addresses.tf) | Arbitrary addresses factory. | <code>net-address</code> |  |
| [factory-cloudnat.tf](./factory-cloudnat.tf) | Cloud NAT factory. | <code>net-address</code> · <code>net-cloudnat</code> |  |
| [factory-dns.tf](./factory-dns.tf) | DNS zones and RPZ factory. | <code>dns</code> · <code>dns-response-policy</code> |  |
| [factory-firewall-policies.tf](./factory-firewall-policies.tf) | Firewall policies factory. | <code>net-firewall-policy</code> |  |
| [factory-ncc.tf](./factory-ncc.tf) | NCC Hubs and Groups factory |  | <code>google_network_connectivity_group</code> · <code>google_network_connectivity_hub</code> · <code>google_network_connectivity_spoke</code> |
| [factory-nva.tf](./factory-nva.tf) | NVA factory | <code>compute-vm</code> · <code>net-lb-int</code> | <code>google_compute_instance_group</code> |
| [factory-peering.tf](./factory-peering.tf) | VPC Peering factory. |  | <code>google_compute_network_peering</code> |
| [factory-projects.tf](./factory-projects.tf) | Projects factory. | <code>project-factory</code> |  |
| [factory-routers.tf](./factory-routers.tf) | Routers factory. |  | <code>google_compute_router</code> |
| [factory-vlan-attachments.tf](./factory-vlan-attachments.tf) | VLAN attachments factory. | <code>net-vlan-attachment</code> | <code>google_compute_interconnect_attachment_group</code> |
| [factory-vpcs.tf](./factory-vpcs.tf) | VPC and firewall rules factory. | <code>net-vpc</code> · <code>net-vpc-factory</code> |  |
| [factory-vpns.tf](./factory-vpns.tf) | VPNs factory. | <code>net-vpn-ha</code> | <code>google_compute_ha_vpn_gateway</code> |
| [main.tf](./main.tf) | Module-level locals and resources. |  |  |
| [outputs.tf](./outputs.tf) | Module outputs. |  | <code>google_storage_bucket_object</code> · <code>local_file</code> |
| [variables-fast.tf](./variables-fast.tf) | None |  |  |
| [variables.tf](./variables.tf) | Module variables. |  |  |

## Variables

| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [billing_account](variables-fast.tf#L17) | Billing account id. | <code>object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ |  |
| [organization](variables-fast.tf#L58) | Organization details. | <code>object&#40;&#123;&#8230;&#125;&#41;</code> | ✓ |  |
| [prefix](variables-fast.tf#L75) | Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants. | <code>string</code> | ✓ |  |
| [context](variables.tf#L17) | Context-specific interpolations. | <code>object&#40;&#123;&#8230;&#125;&#41;</code> |  | <code>&#123;&#125;</code> |
| [custom_roles](variables-fast.tf#L25) | Custom roles defined at the org level, in key => id format. | <code>map&#40;string&#41;</code> |  | <code>&#123;&#125;</code> |
| [factories_config](variables.tf#L41) | Configuration for the resource factories or external data. | <code>object&#40;&#123;&#8230;&#125;&#41;</code> |  | <code>&#123;&#125;</code> |
| [folder_ids](variables-fast.tf#L33) | Folders created in the bootstrap stage. | <code>map&#40;string&#41;</code> |  | <code>&#123;&#125;</code> |
| [iam_principals](variables-fast.tf#L41) | IAM-format principals. | <code>map&#40;string&#41;</code> |  | <code>&#123;&#125;</code> |
| [kms_keys](variables-fast.tf#L50) | KMS key ids. | <code>map&#40;string&#41;</code> |  | <code>&#123;&#125;</code> |
| [perimeters](variables-fast.tf#L67) | Optional VPC-SC perimeter ids. | <code>map&#40;string&#41;</code> |  | <code>&#123;&#125;</code> |
| [project_ids](variables-fast.tf#L85) | Projects created in the bootstrap stage. | <code>map&#40;string&#41;</code> |  | <code>&#123;&#125;</code> |
| [service_accounts](variables-fast.tf#L93) | Service accounts created in the bootstrap stage. | <code>map&#40;string&#41;</code> |  | <code>&#123;&#125;</code> |
| [storage_buckets](variables-fast.tf#L101) | Storage buckets created in the bootstrap stage. | <code>map&#40;string&#41;</code> |  | <code>&#123;&#125;</code> |
| [tag_keys](variables-fast.tf#L109) | FAST-managed resource manager tag keys. | <code>map&#40;string&#41;</code> |  | <code>&#123;&#125;</code> |
| [tag_values](variables-fast.tf#L117) | FAST-managed resource manager tag values. | <code>map&#40;string&#41;</code> |  | <code>&#123;&#125;</code> |
| [tag_vars](variables-fast.tf#L125) | FAST-managed resource manager tag key namespaced names. | <code>object&#40;&#123;&#8230;&#125;&#41;</code> |  | <code>&#123;&#125;</code> |
| [universe](variables-fast.tf#L136) | GCP universe where to deploy projects. The prefix will be prepended to the project id. | <code>object&#40;&#123;&#8230;&#125;&#41;</code> |  | <code>null</code> |

## Outputs

| name | description | sensitive |
|---|---|:---:|
| [host_project_ids](outputs.tf#L73) | Project IDs. |  |
| [host_project_numbers](outputs.tf#L78) | Project numbers. |  |
| [subnet_ips](outputs.tf#L83) | Subnet IP ranges. |  |
| [subnet_proxy_only_self_links](outputs.tf#L88) | Subnet proxy-only self-links. |  |
| [subnet_psc_self_links](outputs.tf#L93) | Subnet PSC self-links. |  |
| [subnet_self_links](outputs.tf#L98) | Subnet self-links. |  |
| [vpc_self_links](outputs.tf#L103) | VPC self-links. |  |
<!-- END TFDOC -->
