Factory based FAST Networking stage (#3435)
New factory based networking stage, shipping with a single dataset (peering) to keep the PR size somewhat manageable.
This commit is contained in:
@@ -54,6 +54,7 @@ fast
|
|||||||
├── 0-bootstrap
|
├── 0-bootstrap
|
||||||
├── 1-resman
|
├── 1-resman
|
||||||
├── 1-vpcsc
|
├── 1-vpcsc
|
||||||
|
├── 2-networking
|
||||||
├── 2-networking-legacy-a-simple
|
├── 2-networking-legacy-a-simple
|
||||||
├── 2-networking-legacy-b-nva
|
├── 2-networking-legacy-b-nva
|
||||||
├── 2-networking-legacy-c-separate-envs
|
├── 2-networking-legacy-c-separate-envs
|
||||||
|
|||||||
5
fast/stages/2-networking/.fast-stage.env
Normal file
5
fast/stages/2-networking/.fast-stage.env
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
FAST_STAGE_DESCRIPTION="networking"
|
||||||
|
FAST_STAGE_LEVEL=2
|
||||||
|
FAST_STAGE_NAME=networking
|
||||||
|
FAST_STAGE_DEPS="0-globals 0-org-setup"
|
||||||
|
FAST_STAGE_OPTIONAL="2-networking-ngfw"
|
||||||
338
fast/stages/2-networking/README.md
Normal file
338
fast/stages/2-networking/README.md
Normal file
@@ -0,0 +1,338 @@
|
|||||||
|
# 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)
|
||||||
|
- [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/ VPC Peering)**: Environment-based VPCs interconnected through VPC peering, resulting in full isolation between spokes ([dataset](./datasets/hub-and-spokes-peerings/))
|
||||||
|
|
||||||
|
### 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.
|
||||||
|
├── 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.
|
||||||
|
└── 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, and VPNs are defined in subdirectories within each VPC's folder.
|
||||||
|
|
||||||
|
### 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 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.
|
||||||
|
|
||||||
|
### 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
|
||||||
|
# [...]
|
||||||
|
```
|
||||||
|
|
||||||
|
- **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:
|
||||||
|
|
||||||
|
- **VPC Peering:** Managed via the `peering_config` section in a VPC's `.config.yaml` file.
|
||||||
|
- **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-cloudnat.tf](./factory-cloudnat.tf) | Cloud NAT factory. | <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-vpcs.tf](./factory-vpcs.tf) | VPC and firewall rules factory. | <code>net-vpc</code> · <code>net-vpc-firewall</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 |
|
||||||
|
|---|---|:---:|:---:|:---:|
|
||||||
|
| [automation](variables-fast.tf#L17) | Automation resources created by the bootstrap stage. | <code title="object({ outputs_bucket = string })">object({…})</code> | ✓ | |
|
||||||
|
| [billing_account](variables-fast.tf#L26) | Billing account id. | <code title="object({ id = string })">object({…})</code> | ✓ | |
|
||||||
|
| [organization](variables-fast.tf#L58) | Organization details. | <code title="object({ id = number })">object({…})</code> | ✓ | |
|
||||||
|
| [prefix](variables-fast.tf#L67) | 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 title="object({ custom_roles = optional(map(string), {}) folder_ids = optional(map(string), {}) iam_principals = optional(map(string), {}) locations = optional(map(string), {}) project_ids = optional(map(string), {}) tag_keys = optional(map(string), {}) tag_values = optional(map(string), {}) })">object({…})</code> | | <code>{}</code> |
|
||||||
|
| [custom_roles](variables-fast.tf#L34) | Custom roles defined at the org level, in key => id format. | <code>map(string)</code> | | <code>{}</code> |
|
||||||
|
| [factories_config](variables.tf#L32) | Configuration for the resource factories or external data. | <code title="object({ defaults = optional(string, "datasets/hub-and-spokes-peerings/defaults.yaml") dns = optional(string, "datasets/hub-and-spokes-peerings/dns/zones") dns-response-policies = optional(string, "datasets/hub-and-spokes-peerings/dns/response-policies") firewall-policies = optional(string, "datasets/hub-and-spokes-peerings/firewall-policies") folders = optional(string, "datasets/hub-and-spokes-peerings/folders") ncc-hubs = optional(string, "datasets/hub-and-spokes-peerings/ncc-hubs") nvas = optional(string, "datasets/hub-and-spokes-peerings/nvas") projects = optional(string, "datasets/hub-and-spokes-peerings/projects") vpcs = optional(string, "datasets/hub-and-spokes-peerings/vpcs") })">object({…})</code> | | <code>{}</code> |
|
||||||
|
| [folder_ids](variables-fast.tf#L42) | Folders created in the bootstrap stage. | <code>map(string)</code> | | <code>{}</code> |
|
||||||
|
| [iam_principals](variables-fast.tf#L50) | IAM-format principals. | <code>map(string)</code> | | <code>{}</code> |
|
||||||
|
| [outputs_location](variables.tf#L49) | Path where tfvars files for the following stages are written. Leave empty to disable. | <code>string</code> | | <code>null</code> |
|
||||||
|
| [project_ids](variables-fast.tf#L77) | Projects created in the bootstrap stage. | <code>map(string)</code> | | <code>{}</code> |
|
||||||
|
| [service_accounts](variables-fast.tf#L85) | Service accounts created in the bootstrap stage. | <code>map(string)</code> | | <code>{}</code> |
|
||||||
|
| [tag_keys](variables-fast.tf#L93) | FAST-managed resource manager tag keys. | <code>map(string)</code> | | <code>{}</code> |
|
||||||
|
| [tag_values](variables-fast.tf#L101) | FAST-managed resource manager tag values. | <code>map(string)</code> | | <code>{}</code> |
|
||||||
|
| [universe](variables.tf#L55) | GCP universe where to deploy projects. The prefix will be prepended to the project id. | <code title="object({ domain = string prefix = string forced_jit_service_identities = optional(list(string), []) unavailable_services = optional(list(string), []) unavailable_service_identities = optional(list(string), []) })">object({…})</code> | | <code>null</code> |
|
||||||
|
|
||||||
|
## Outputs
|
||||||
|
|
||||||
|
| name | description | sensitive |
|
||||||
|
|---|---|:---:|
|
||||||
|
| [host_project_ids](outputs.tf#L65) | Project IDs. | |
|
||||||
|
| [host_project_numbers](outputs.tf#L70) | Project numbers. | |
|
||||||
|
| [subnet_proxy_only_self_links](outputs.tf#L75) | Subnet proxy-only self-links. | |
|
||||||
|
| [subnet_psc_self_links](outputs.tf#L80) | Subnet PSC self-links. | |
|
||||||
|
| [subnet_self_links](outputs.tf#L85) | Subnet self-links. | |
|
||||||
|
| [vpc_self_links](outputs.tf#L90) | VPC self-links. | |
|
||||||
|
<!-- END TFDOC -->
|
||||||
104
fast/stages/2-networking/assets/nva-startup-script.yaml.tpl
Normal file
104
fast/stages/2-networking/assets/nva-startup-script.yaml.tpl
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
#cloud-config
|
||||||
|
|
||||||
|
write_files:
|
||||||
|
- path: /var/run/nva/update-pbrs.sh
|
||||||
|
permissions: "0755"
|
||||||
|
content: |
|
||||||
|
#!/bin/bash
|
||||||
|
# Continuous update of PBRs for an interface.
|
||||||
|
set -e
|
||||||
|
|
||||||
|
IF_INDEX=$1
|
||||||
|
METADATA_URL="http://metadata.google.internal/computeMetadata/v1/instance/network-interfaces"
|
||||||
|
METADATA_HEADER="Metadata-Flavor: Google"
|
||||||
|
FORWARDED_IPS=$(curl -s -H "$${METADATA_HEADER}" "$${METADATA_URL}/$${IF_INDEX}/forwarded-ips/")
|
||||||
|
IF_NAME="eth$${IF_INDEX}"
|
||||||
|
TABLE_ID=$((110 + IF_INDEX))
|
||||||
|
|
||||||
|
echo "Starting."
|
||||||
|
|
||||||
|
GATEWAY=$(curl -s -H "$${METADATA_HEADER}" "$${METADATA_URL}/$${IF_INDEX}/gateway")
|
||||||
|
if [ -z "$${GATEWAY}" ]; then
|
||||||
|
echo "Could not retrieve gateway. Exiting."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Initial PBR configuration using table $${TABLE_ID}."
|
||||||
|
ip route add default via $${GATEWAY} dev $${IF_NAME} proto static onlink table $${TABLE_ID} || true
|
||||||
|
|
||||||
|
while true; do
|
||||||
|
echo "Checking for PBR updates."
|
||||||
|
FORWARDED_IP_IDS=$(curl -s -H "$${METADATA_HEADER}" "$${METADATA_URL}/$${IF_INDEX}/forwarded-ips/")
|
||||||
|
FORWARDED_IPS=""
|
||||||
|
for ID in $${FORWARDED_IP_IDS}; do
|
||||||
|
IP=$(curl -s -H "$${METADATA_HEADER}" "$${METADATA_URL}/$${IF_INDEX}/forwarded-ips/$${ID}")
|
||||||
|
FORWARDED_IPS="$${FORWARDED_IPS} $${IP}"
|
||||||
|
done
|
||||||
|
|
||||||
|
CURRENT_RULE_IPS=$(ip rule show | grep "lookup $${TABLE_ID}" | awk -F 'from ' '/from/ {split($2, a, " "); print a[1]}' | sort -u || true)
|
||||||
|
|
||||||
|
for FW_IP in $${FORWARDED_IPS}; do
|
||||||
|
if ! echo "$${CURRENT_RULE_IPS}" | grep -q "^$${FW_IP}$"; then
|
||||||
|
echo "Adding PBR rules for forwarded IP $${FW_IP}."
|
||||||
|
ip rule add from $${FW_IP} to 35.191.0.0/16 lookup $${TABLE_ID}
|
||||||
|
ip rule add from $${FW_IP} to 130.211.0.0/22 lookup $${TABLE_ID}
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
for RULE_IP in $${CURRENT_RULE_IPS}; do
|
||||||
|
if ! echo "$${FORWARDED_IPS}" | grep -q "^$${RULE_IP}$"; then
|
||||||
|
echo "Removing PBR rules for forwarded IP $${RULE_IP}."
|
||||||
|
ip rule del from $${RULE_IP} to 35.191.0.0/16 lookup $${TABLE_ID} || true
|
||||||
|
ip rule del from $${RULE_IP} to 130.211.0.0/22 lookup $${TABLE_ID} || true
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
|
|
||||||
|
runcmd:
|
||||||
|
- |
|
||||||
|
#!/bin/bash
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
echo "Starting NVA network configuration..."
|
||||||
|
|
||||||
|
echo "Enabling IP forwarding."
|
||||||
|
echo "net.ipv4.ip_forward = 1" > /etc/sysctl.d/90-ip-forwarding.conf
|
||||||
|
sysctl -p /etc/sysctl.d/90-ip-forwarding.conf
|
||||||
|
|
||||||
|
iptables -I FORWARD -j ACCEPT
|
||||||
|
|
||||||
|
METADATA_URL="http://metadata.google.internal/computeMetadata/v1/instance/network-interfaces"
|
||||||
|
METADATA_HEADER="Metadata-Flavor: Google"
|
||||||
|
INTERFACE_INDEXES=$(curl -s -H "$${METADATA_HEADER}" "$${METADATA_URL}/" | sed 's:/$::')
|
||||||
|
|
||||||
|
for i in $${INTERFACE_INDEXES}; do
|
||||||
|
IF_NAME="eth$${i}"
|
||||||
|
echo "Configuring interface $${IF_NAME}..."
|
||||||
|
|
||||||
|
GATEWAY=$(curl -s -H "$${METADATA_HEADER}" "$${METADATA_URL}/$${i}/gateway")
|
||||||
|
if [ -z "$${GATEWAY}" ]; then
|
||||||
|
echo "Could not retrieve gateway for $${IF_NAME}. Skipping."
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
echo "Gateway for $${IF_NAME} is $${GATEWAY}."
|
||||||
|
|
||||||
|
%{ for nic_index, nic in nva_nics_config ~}
|
||||||
|
if [ "$${i}" == "${nic_index}" ]; then
|
||||||
|
%{ for route in nic.routes ~}
|
||||||
|
echo "Adding route for ${route} via $${GATEWAY} on $${IF_NAME}"
|
||||||
|
ip route add ${route} via $${GATEWAY} dev $${IF_NAME} proto static
|
||||||
|
%{ endfor ~}
|
||||||
|
%{ if try(nic.masquerade, false) ~}
|
||||||
|
echo "Enabling NAT (Masquerade) on $${IF_NAME}."
|
||||||
|
iptables -t nat -A POSTROUTING -o $${IF_NAME} -j MASQUERADE
|
||||||
|
%{ endif ~}
|
||||||
|
fi
|
||||||
|
%{ endfor ~}
|
||||||
|
|
||||||
|
echo "Starting continuous PBR update for eth$${i} in the background."
|
||||||
|
nohup /var/run/nva/update-pbrs.sh $i >> /var/log/nva-pbr-update.echo 2>&1 &
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "NVA network configuration complete."
|
||||||
@@ -0,0 +1,114 @@
|
|||||||
|
# Hub and spoke with VPC Peerings
|
||||||
|
|
||||||
|
This stage sets up the shared network infrastructure environment, and leverages [VPC Network Peering](https://cloud.google.com/vpc/docs/vpc-peering) to implement a Hub and Spoke topology with a hub and two spokes.
|
||||||
|
|
||||||
|
Leveraging the lack of transitivity when using VPC peerings, the spoke VPCs have no routing line-of-sight with each other, which is ideal when implementing isolated environment. However anything that transit through the hub will be able to reach destinations in both spokes.
|
||||||
|
|
||||||
|
Transitivity between spokes could be achieved by adding a (potentially one-legged) network virtual appliance acting as a router.
|
||||||
|
|
||||||
|
The following diagram illustrates the high-level design, and should be used as a reference for the following sections.
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<img src="diagram.svg" alt="Peerings diagram">
|
||||||
|
</br>Peering diagram
|
||||||
|
</p>
|
||||||
|
|
||||||
|
### VPC design
|
||||||
|
|
||||||
|
The hub VPC hosts external connectivity (by default VPN tunnels), and centralises the DNS configuration.
|
||||||
|
|
||||||
|
The default dataset ships two different VPCs, mapping to hypotetical environments (dev and prod). Each VPC is created into its own project, and each project is configured as a Shared VPC host, so that network-related resources and access configurations via IAM are kept separate for each VPC.
|
||||||
|
|
||||||
|
The design easily lends itself to implementing additional environments, or adopting a different logical mapping for spokes (e.g. one spoke for each company entity, etc.).
|
||||||
|
|
||||||
|
### IP ranges, subnetting, routing
|
||||||
|
|
||||||
|
Minimizing the number of routes (and subnets) in use on the cloud environment is an important consideration, as it simplifies management and avoids hitting [Cloud Router](https://cloud.google.com/network-connectivity/docs/router/quotas) and [VPC](https://cloud.google.com/vpc/docs/quota) quotas and limits. For this reason, we recommend careful planning of the IP space used in your cloud environment, to be able to use large IP CIDR blocks in routes whenever possible.
|
||||||
|
|
||||||
|
This stage uses a dedicated /16 block (which should of course be sized to your needs) for each region in each VPC, and subnets created in each VPC should derive their ranges from the relevant block.
|
||||||
|
|
||||||
|
The Prod Spoke VPC also define and reserve - as an example - two "special" CIDR ranges dedicated to [PSA (Private Service Access)](https://cloud.google.com/vpc/docs/private-services-access) and [Internal Application Load Balancers (L7 LBs)](https://cloud.google.com/load-balancing/docs/l7-internal).
|
||||||
|
|
||||||
|
Routes in GCP are either automatically created for VPC subnets, manually created via static routes, programmed by the NCC hub or dynamically programmed by [Cloud Routers](https://cloud.google.com/network-connectivity/docs/router#docs) via BGP sessions, which can be configured to advertise VPC ranges, and/or custom ranges via custom advertisements.
|
||||||
|
|
||||||
|
Furthermore:
|
||||||
|
|
||||||
|
- routes between multiple subnets within the same VPC are automatically programmed by GCP
|
||||||
|
- each spoke exchanges routes with the HUB
|
||||||
|
- on-premises is connected to the hub VPC and dynamically exchanges BGP routes with GCP using HA VPN. The HA VPN tunnels program routes in the hub VPC which then exports them to the spoke VPCs.
|
||||||
|
|
||||||
|
### Peering Configuration
|
||||||
|
|
||||||
|
VPC peering is controlled by each VPC `.config` file, as per the example below.
|
||||||
|
|
||||||
|
[vpcs/hub/.config.yaml](./vpcs/hub/.config.yaml)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# [...]
|
||||||
|
peering_config:
|
||||||
|
to-prod:
|
||||||
|
peer_network: $networks:prod
|
||||||
|
to-dev:
|
||||||
|
peer_network: $networks:dev
|
||||||
|
# [...]
|
||||||
|
```
|
||||||
|
|
||||||
|
[vpcs/prod/.config.yaml](./vpcs/prod/.config.yaml)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# [...]
|
||||||
|
peering_config:
|
||||||
|
to-hub:
|
||||||
|
peer_network: $networks:hub
|
||||||
|
# [...]
|
||||||
|
```
|
||||||
|
|
||||||
|
For more informations about cross referencing resources, please check the [main README.md file](../../README.md)
|
||||||
|
|
||||||
|
### Internet egress
|
||||||
|
|
||||||
|
Cloud NAT provides the simplest path for internet egress. This setup uses Cloud NAT, which is enabled by default on the primary region on every VPC.
|
||||||
|
|
||||||
|
e.g. in [vpcs/prod/.config.yaml](./vpcs/prod/.config.yaml)
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# [...]
|
||||||
|
nat_config:
|
||||||
|
nat-primary:
|
||||||
|
region: $locations:primary
|
||||||
|
# [...]
|
||||||
|
```
|
||||||
|
|
||||||
|
Several other scenarios are possible through ad-hoc implementations, with varying degrees of complexity:
|
||||||
|
|
||||||
|
- a forward proxy (including [SWP](https://cloud.google.com/secure-web-proxy/docs/overview)), with optional URL filters
|
||||||
|
- a default route to on-prem to leverage existing egress infrastructure
|
||||||
|
- a full-fledged perimeter firewall to control egress and implement additional security features like IPS
|
||||||
|
|
||||||
|
### VPC and Hierarchical Firewall
|
||||||
|
|
||||||
|
The GCP Firewall is a stateful, distributed feature that allows the creation of L4 policies, either via VPC-level rules or more recently via hierarchical policies applied on the resource hierarchy (organization, folders).
|
||||||
|
|
||||||
|
The current setup adopts both firewall types, and uses [hierarchical rules on the Networking folder](./firewall-policies/networking-policy.yaml) for common ingress rules, e.g. from health check or IAP forwarders ranges, and [VPC rules](./vpcs/prod/firewall-rules) for the environment or workload-level ingress.
|
||||||
|
|
||||||
|
### DNS
|
||||||
|
|
||||||
|
This dataset implements a centralized DNS architecture that handles resolution between GCP and on-premises environments.
|
||||||
|
|
||||||
|
- **Cloud to on-prem:** A [forwarding zone](./dns/zones/net-core-0/fwd-root.yaml) for the `onprem.` domain is configured in the hub VPC. It forwards DNS queries for on-premises resources to the on-premises DNS resolvers.
|
||||||
|
- **On-prem to cloud:** An [inbound DNS policy](https://cloud.google.com/dns/docs/server-policies-overview#dns-server-policy-in) allows on-premises systems to resolve resources in GCP.
|
||||||
|
|
||||||
|
DNS configuration is centralized in the hub project (`net-core-0`) and shared with the spokes using DNS peering:
|
||||||
|
|
||||||
|
- The **hub** hosts:
|
||||||
|
- A top-level private zone for the cloud environment (e.g., `test.`).
|
||||||
|
- The forwarding zone to on-premises.
|
||||||
|
- The **spokes** (`net-dev-0`, `net-prod-0`) host private zones for their specific subdomains (e.g., `dev.test.`, `prod.test.`). These zones are visible to the hub.
|
||||||
|
- A **peering zone** for the `.` (root) domain is configured in the spokes, pointing to the hub. This delegates all DNS resolution from the spokes to the hub, creating a centralized model.
|
||||||
|
- **Private Google Access** is enabled via [DNS Response Policies](https://cloud.google.com/dns/docs/zones/manage-response-policies#create-response-policy-rule) for most of the [supported domains](https://cloud.google.com/vpc/docs/configure-private-google-access#domain-options).
|
||||||
|
|
||||||
|
To complete the configuration, on-premises DNS servers should be configured to forward queries for your cloud domain (e.g., `test.`) to the GCP inbound policy's IP addresses. Additionally, the `35.199.192.0/19` range (used by the inbound forwarder) should be routed over the VPN tunnels from on-premises.
|
||||||
|
|
||||||
|
### VPNs
|
||||||
|
|
||||||
|
Connectivity to on-prem is implemented with HA VPN ([`net-vpn`](../../../../../modules/net-vpn-ha/)) and defined in [`onprem.yaml`](./vpcs/hub/vpns/onprem.yaml). The file provisionally implements a single logical connection between onprem and the hub on the primary region through 2 IPSec tunnels.
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# 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.
|
||||||
|
|
||||||
|
context:
|
||||||
|
cidr_ranges_sets:
|
||||||
|
healthchecks:
|
||||||
|
- 35.191.0.0/16
|
||||||
|
- 130.211.0.0/22
|
||||||
|
- 209.85.152.0/22
|
||||||
|
- 209.85.204.0/22
|
||||||
|
rfc1918:
|
||||||
|
- 10.0.0.0/8
|
||||||
|
- 172.16.0.0/12
|
||||||
|
- 192.168.0.0/16
|
||||||
|
locations:
|
||||||
|
primary: europe-west8
|
||||||
|
secondary: europe-west12
|
||||||
|
iam_principals: {}
|
||||||
|
|
||||||
|
projects:
|
||||||
|
defaults:
|
||||||
|
locations:
|
||||||
|
storage: eu
|
||||||
|
|
||||||
|
vpcs:
|
||||||
|
auto_create_subnetworks: false
|
||||||
|
delete_default_route_on_create: true
|
||||||
|
mtu: 1500
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 85 KiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 281 KiB |
@@ -0,0 +1,156 @@
|
|||||||
|
# skip boilerplate check
|
||||||
|
---
|
||||||
|
# start of document (---) avoids errors if the file only contains comments
|
||||||
|
|
||||||
|
# yaml-language-server: $schema=../../../../schemas/dns-response-policy-rules.schema.json
|
||||||
|
|
||||||
|
project_id: $project_ids:net-core-0
|
||||||
|
networks:
|
||||||
|
- $networks:hub
|
||||||
|
- $networks:prod
|
||||||
|
- $networks:dev
|
||||||
|
rules:
|
||||||
|
accounts:
|
||||||
|
dns_name: "accounts.google.com."
|
||||||
|
behavior: bypassResponsePolicy
|
||||||
|
aiplatform-notebook-cloud-all:
|
||||||
|
dns_name: "*.aiplatform-notebook.cloud.google.com."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
aiplatform-notebook-gu-all:
|
||||||
|
dns_name: "*.aiplatform-notebook.googleusercontent.com."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
appengine:
|
||||||
|
dns_name: "appengine.google.com."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
appspot-all:
|
||||||
|
dns_name: "*.appspot.com."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
backupdr-cloud:
|
||||||
|
dns_name: "backupdr.cloud.google.com."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
backupdr-cloud-all:
|
||||||
|
dns_name: "*.backupdr.cloud.google.com."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
backupdr-gu:
|
||||||
|
dns_name: "backupdr.googleusercontent.google.com."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
backupdr-gu-all:
|
||||||
|
dns_name: "*.backupdr.googleusercontent.google.com."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
cloudfunctions:
|
||||||
|
dns_name: "*.cloudfunctions.net."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
cloudproxy:
|
||||||
|
dns_name: "*.cloudproxy.app."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
composer-cloud-all:
|
||||||
|
dns_name: "*.composer.cloud.google.com."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
composer-gu-all:
|
||||||
|
dns_name: "*.composer.googleusercontent.com."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
datafusion-all:
|
||||||
|
dns_name: "*.datafusion.cloud.google.com."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
datafusion-gu-all:
|
||||||
|
dns_name: "*.datafusion.googleusercontent.com."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
dataproc:
|
||||||
|
dns_name: "dataproc.cloud.google.com."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
dataproc-all:
|
||||||
|
dns_name: "*.dataproc.cloud.google.com."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
dataproc-gu:
|
||||||
|
dns_name: "dataproc.googleusercontent.com."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
dataproc-gu-all:
|
||||||
|
dns_name: "*.dataproc.googleusercontent.com."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
dl:
|
||||||
|
dns_name: "dl.google.com."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
gcr:
|
||||||
|
dns_name: "gcr.io."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
gcr-all:
|
||||||
|
dns_name: "*.gcr.io."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
gke-all:
|
||||||
|
dns_name: "*.gke.goog."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
googleapis-all:
|
||||||
|
dns_name: "*.googleapis.com."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
googleapis-private:
|
||||||
|
dns_name: "private.googleapis.com."
|
||||||
|
local_data:
|
||||||
|
A:
|
||||||
|
rrdatas:
|
||||||
|
- 199.36.153.8
|
||||||
|
- 199.36.153.9
|
||||||
|
- 199.36.153.10
|
||||||
|
- 199.36.153.11
|
||||||
|
AAAA:
|
||||||
|
rrdatas:
|
||||||
|
- "2600:2d00:2:2000::"
|
||||||
|
googleapis-restricted:
|
||||||
|
dns_name: "restricted.googleapis.com."
|
||||||
|
local_data:
|
||||||
|
A:
|
||||||
|
rrdatas:
|
||||||
|
- 199.36.153.4
|
||||||
|
- 199.36.153.5
|
||||||
|
- 199.36.153.6
|
||||||
|
- 199.36.153.7
|
||||||
|
AAAA:
|
||||||
|
rrdatas:
|
||||||
|
- "2600:2d00:2:1000::"
|
||||||
|
gstatic-all:
|
||||||
|
dns_name: "*.gstatic.com."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
kernels-gu:
|
||||||
|
dns_name: "kernels.googleusercontent.com."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
kernels-gu-all:
|
||||||
|
dns_name: "*.kernels.googleusercontent.com."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
ltsapis-all:
|
||||||
|
dns_name: "*.ltsapis.goog."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
notebooks:
|
||||||
|
dns_name: "notebooks.cloud.google.com."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
notebooks-all:
|
||||||
|
dns_name: "*.notebooks.cloud.google.com."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
notebooks-gu-all:
|
||||||
|
dns_name: "*.notebooks.googleusercontent.com."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
packages-cloud:
|
||||||
|
dns_name: "packages.cloud.google.com."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
packages-cloud-all:
|
||||||
|
dns_name: "*.packages.cloud.google.com."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
pkgdev:
|
||||||
|
dns_name: "pkg.dev."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
pkgdev-all:
|
||||||
|
dns_name: "*.pkg.dev."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
pkigoog:
|
||||||
|
dns_name: "pki.goog."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
pkigoog-all:
|
||||||
|
dns_name: "*.pki.goog."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
run-all:
|
||||||
|
dns_name: "*.run.app."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
source:
|
||||||
|
dns_name: "source.developers.google.com."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
|
storage:
|
||||||
|
dns_name: "storage.cloud.google.com."
|
||||||
|
local_data: { CNAME: { rrdatas: ["private.googleapis.com."] } }
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# skip boilerplate check
|
||||||
|
---
|
||||||
|
# start of document (---) avoids errors if the file only contains comments
|
||||||
|
|
||||||
|
# yaml-language-server: $schema=../../../../../schemas/dns.schema.json
|
||||||
|
|
||||||
|
project_id: $project_ids:net-core-0
|
||||||
|
domain: onprem.
|
||||||
|
forwarding:
|
||||||
|
forwarders:
|
||||||
|
"8.8.8.8": default
|
||||||
|
"1.1.1.1": default
|
||||||
|
client_networks:
|
||||||
|
- $networks:hub
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# skip boilerplate check
|
||||||
|
---
|
||||||
|
# start of document (---) avoids errors if the file only contains comments
|
||||||
|
|
||||||
|
# yaml-language-server: $schema=../../../../../schemas/dns.schema.json
|
||||||
|
|
||||||
|
project_id: $project_ids:net-core-0
|
||||||
|
domain: .
|
||||||
|
peering:
|
||||||
|
peer_network: $networks:hub
|
||||||
|
client_networks:
|
||||||
|
- $networks:prod
|
||||||
|
- $networks:dev
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
# skip boilerplate check
|
||||||
|
---
|
||||||
|
# start of document (---) avoids errors if the file only contains comments
|
||||||
|
|
||||||
|
# yaml-language-server: $schema=../../../../../schemas/dns.schema.json
|
||||||
|
|
||||||
|
project_id: $project_ids:net-core-0
|
||||||
|
domain: test.
|
||||||
|
private:
|
||||||
|
client_networks:
|
||||||
|
- $networks:hub
|
||||||
|
recordsets:
|
||||||
|
"A localhost":
|
||||||
|
records: ["127.0.0.1"]
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
# skip boilerplate check
|
||||||
|
---
|
||||||
|
# start of document (---) avoids errors if the file only contains comments
|
||||||
|
|
||||||
|
# yaml-language-server: $schema=../../../../../schemas/dns.schema.json
|
||||||
|
|
||||||
|
project_id: $project_ids:net-dev-0
|
||||||
|
domain: dev.test.
|
||||||
|
private:
|
||||||
|
client_networks:
|
||||||
|
- $networks:hub
|
||||||
|
- $networks:dev
|
||||||
|
recordsets:
|
||||||
|
"A localhost":
|
||||||
|
records: ["127.0.0.1"]
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
# skip boilerplate check
|
||||||
|
---
|
||||||
|
# start of document (---) avoids errors if the file only contains comments
|
||||||
|
|
||||||
|
# yaml-language-server: $schema=../../../../../schemas/dns.schema.json
|
||||||
|
|
||||||
|
project_id: $project_ids:net-prod-0
|
||||||
|
domain: prod.test.
|
||||||
|
private:
|
||||||
|
client_networks:
|
||||||
|
- $networks:hub
|
||||||
|
- $networks:prod
|
||||||
|
recordsets:
|
||||||
|
"A localhost":
|
||||||
|
records: ["127.0.0.1"]
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
# skip boilerplate check
|
||||||
|
|
||||||
|
# yaml-language-server: $schema=../../../schemas/firewall-policy.schema.json
|
||||||
|
|
||||||
|
parent_id: $folder_ids:networking
|
||||||
|
attachments:
|
||||||
|
networking: $folder_ids:networking
|
||||||
|
name: network-policies
|
||||||
|
ingress_rules:
|
||||||
|
allow-healthchecks:
|
||||||
|
description: Enable SSH, HTTP and HTTPS healthchecks
|
||||||
|
priority: 1001
|
||||||
|
match:
|
||||||
|
source_ranges:
|
||||||
|
- $cidr_ranges_sets:healthchecks
|
||||||
|
layer4_configs:
|
||||||
|
- protocol: tcp
|
||||||
|
ports: ["22", "80", "443"]
|
||||||
|
|
||||||
|
allow-ssh-from-iap:
|
||||||
|
description: Enable SSH from IAP
|
||||||
|
priority: 1002
|
||||||
|
enable_logging: true
|
||||||
|
match:
|
||||||
|
source_ranges:
|
||||||
|
- 35.235.240.0/20
|
||||||
|
layer4_configs:
|
||||||
|
- protocol: tcp
|
||||||
|
ports: ["22"]
|
||||||
|
|
||||||
|
allow-icmp:
|
||||||
|
description: Enable ICMP
|
||||||
|
priority: 1003
|
||||||
|
match:
|
||||||
|
source_ranges:
|
||||||
|
- 0.0.0.0/0
|
||||||
|
layer4_configs:
|
||||||
|
- protocol: icmp
|
||||||
|
|
||||||
|
allow-nat-ranges:
|
||||||
|
description: Enable NAT ranges for VPC serverless connector
|
||||||
|
priority: 1004
|
||||||
|
match:
|
||||||
|
source_ranges:
|
||||||
|
- 107.178.230.64/26
|
||||||
|
- 35.199.224.0/19
|
||||||
|
egress_rules:
|
||||||
|
deny-example-ip:
|
||||||
|
description: Allow internal traffic within the VPC
|
||||||
|
priority: 2000
|
||||||
|
match:
|
||||||
|
destination_ranges:
|
||||||
|
- 1.2.3.4/32
|
||||||
|
layer4_configs:
|
||||||
|
- protocol: all
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# skip boilerplate check
|
||||||
|
---
|
||||||
|
# start of document (---) avoids errors if the file only contains comments
|
||||||
|
|
||||||
|
# yaml-language-server: $schema=../../../schemas/project.schema.json
|
||||||
|
name: prod-net-core-0
|
||||||
|
parent: $folder_ids:networking
|
||||||
|
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
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# skip boilerplate check
|
||||||
|
---
|
||||||
|
# start of document (---) avoids errors if the file only contains comments
|
||||||
|
|
||||||
|
# yaml-language-server: $schema=../../../schemas/project.schema.json
|
||||||
|
|
||||||
|
name: dev-net-dev-0
|
||||||
|
parent: $folder_ids:networking/dev
|
||||||
|
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
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# skip boilerplate check
|
||||||
|
---
|
||||||
|
# start of document (---) avoids errors if the file only contains comments
|
||||||
|
|
||||||
|
# yaml-language-server: $schema=../../../schemas/project.schema.json
|
||||||
|
|
||||||
|
name: prod-net-prod-0
|
||||||
|
parent: $folder_ids:networking/prod
|
||||||
|
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
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
# skip boilerplate check
|
||||||
|
---
|
||||||
|
# start of document (---) avoids errors if the file only contains comments
|
||||||
|
|
||||||
|
# yaml-language-server: $schema=../../../../schemas/vpc.schema.json
|
||||||
|
|
||||||
|
project_id: $project_ids:net-dev-0
|
||||||
|
name: dev
|
||||||
|
delete_default_routes_on_create: false
|
||||||
|
mtu: 1500
|
||||||
|
nat_config:
|
||||||
|
nat-ew8:
|
||||||
|
region: $locations:primary
|
||||||
|
peering_config:
|
||||||
|
to-hub:
|
||||||
|
peer_network: $networks:hub
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# skip boilerplate check
|
||||||
|
---
|
||||||
|
# start of document (---) avoids errors if the file only contains comments
|
||||||
|
|
||||||
|
# yaml-language-server: $schema=../../../../../schemas/firewall-rules.schema.json
|
||||||
|
|
||||||
|
ingress:
|
||||||
|
ingress-default-dev-deny:
|
||||||
|
description: "Deny and log any unmatched ingress traffic."
|
||||||
|
deny: true
|
||||||
|
priority: 65535
|
||||||
|
enable_logging:
|
||||||
|
include_metadata: false
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# skip boilerplate check
|
||||||
|
|
||||||
|
# yaml-language-server: $schema=../../../../../schemas/subnet.schema.json
|
||||||
|
|
||||||
|
name: dev-default
|
||||||
|
region: $locations:secondary
|
||||||
|
ip_cidr_range: 10.73.0.0/24
|
||||||
|
description: Default europe-west12 subnet for dev
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# skip boilerplate check
|
||||||
|
---
|
||||||
|
# start of document (---) avoids errors if the file only contains comments
|
||||||
|
|
||||||
|
# yaml-language-server: $schema=../../../../schemas/vpc.schema.json
|
||||||
|
|
||||||
|
project_id: $project_ids:net-core-0
|
||||||
|
name: hub
|
||||||
|
delete_default_routes_on_create: false
|
||||||
|
nat_config:
|
||||||
|
nat-ew8:
|
||||||
|
region: $locations:primary
|
||||||
|
routers:
|
||||||
|
vpn-router:
|
||||||
|
region: $locations:primary
|
||||||
|
asn: 64514
|
||||||
|
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: $networks:prod
|
||||||
|
to-dev:
|
||||||
|
peer_network: $networks:dev
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# skip boilerplate check
|
||||||
|
---
|
||||||
|
# start of document (---) avoids errors if the file only contains comments
|
||||||
|
|
||||||
|
# yaml-language-server: $schema=../../../../../schemas/firewall-rules.schema.json
|
||||||
|
|
||||||
|
ingress:
|
||||||
|
ingress-default-landing-deny:
|
||||||
|
description: "Deny and log any unmatched ingress traffic."
|
||||||
|
deny: true
|
||||||
|
priority: 65535
|
||||||
|
enable_logging:
|
||||||
|
include_metadata: false
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# skip boilerplate check
|
||||||
|
|
||||||
|
# yaml-language-server: $schema=../../../../../schemas/subnet.schema.json
|
||||||
|
|
||||||
|
name: hub-default
|
||||||
|
region: $locations:secondary
|
||||||
|
ip_cidr_range: 10.71.0.0/24
|
||||||
|
description: Default europe-west12 subnet for hub
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
# skip boilerplate check
|
||||||
|
---
|
||||||
|
# start of document (---) avoids errors if the file only contains comments
|
||||||
|
|
||||||
|
# yaml-language-server: $schema=../../../../../schemas/vpn.schema.json
|
||||||
|
|
||||||
|
name: to-onprem
|
||||||
|
region: $locations:primary
|
||||||
|
peer_gateways:
|
||||||
|
default:
|
||||||
|
external:
|
||||||
|
redundancy_type: SINGLE_IP_INTERNALLY_REDUNDANT
|
||||||
|
interfaces:
|
||||||
|
- 8.8.8.8
|
||||||
|
router_config:
|
||||||
|
create: false
|
||||||
|
name: $routers: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
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# skip boilerplate check
|
||||||
|
---
|
||||||
|
# start of document (---) avoids errors if the file only contains comments
|
||||||
|
|
||||||
|
# yaml-language-server: $schema=../../../../schemas/vpc.schema.json
|
||||||
|
|
||||||
|
project_id: $project_ids:net-prod-0
|
||||||
|
name: prod
|
||||||
|
delete_default_routes_on_create: false
|
||||||
|
mtu: 1500
|
||||||
|
nat_config:
|
||||||
|
nat-ew8:
|
||||||
|
region: $locations:primary
|
||||||
|
peering_config:
|
||||||
|
to-hub:
|
||||||
|
peer_network: $networks:hub
|
||||||
|
psa_configs:
|
||||||
|
- ranges:
|
||||||
|
psa: 10.72.224.0/24
|
||||||
|
export_routes: true
|
||||||
|
import_routes: true
|
||||||
|
peered_domains:
|
||||||
|
- "test."
|
||||||
|
subnets_proxy_only:
|
||||||
|
- ip_cidr_range: 10.72.240.0/24
|
||||||
|
region: $locations:primary
|
||||||
|
name: primary-region-proxy-only
|
||||||
|
active: true
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
# skip boilerplate check
|
||||||
|
---
|
||||||
|
# start of document (---) avoids errors if the file only contains comments
|
||||||
|
|
||||||
|
# yaml-language-server: $schema=../../../../../schemas/firewall-rules.schema.json
|
||||||
|
|
||||||
|
ingress:
|
||||||
|
ingress-default-prod-deny:
|
||||||
|
description: "Deny and log any unmatched ingress traffic."
|
||||||
|
deny: true
|
||||||
|
priority: 65535
|
||||||
|
enable_logging:
|
||||||
|
include_metadata: false
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# skip boilerplate check
|
||||||
|
|
||||||
|
# yaml-language-server: $schema=../../../../../schemas/subnet.schema.json
|
||||||
|
|
||||||
|
name: prod-default
|
||||||
|
region: $locations:primary
|
||||||
|
ip_cidr_range: 10.72.0.0/24
|
||||||
|
description: Default europe-west12 subnet for prod
|
||||||
65
fast/stages/2-networking/factory-cloudnat.tf
Normal file
65
fast/stages/2-networking/factory-cloudnat.tf
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
/**
|
||||||
|
* 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 vpc_key, vpc_config in local.vpcs : [
|
||||||
|
for nat_key, nat_config in try(vpc_config.nat_config, {}) : {
|
||||||
|
"${vpc_key}/${nat_key}" = merge(nat_config, {
|
||||||
|
name = replace("${vpc_key}/${nat_key}", "/", "-")
|
||||||
|
project_id = vpc_config.project_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.vpcs[vpc_key].self_link
|
||||||
|
rules = try(nat_config.rules, [])
|
||||||
|
type = try(nat_config.type, "PUBLIC")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
]
|
||||||
|
])...)
|
||||||
|
}
|
||||||
|
|
||||||
|
module "nat" {
|
||||||
|
source = "../../../modules/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
|
||||||
|
context = merge(local.ctx, {
|
||||||
|
project_ids = local.ctx_projects.project_ids
|
||||||
|
vpc_self_links = local.ctx_vpcs.self_links
|
||||||
|
locations = local.ctx.locations
|
||||||
|
})
|
||||||
|
}
|
||||||
126
fast/stages/2-networking/factory-dns.tf
Normal file
126
fast/stages/2-networking/factory-dns.tf
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
/**
|
||||||
|
* 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 zones and RPZ factory.
|
||||||
|
|
||||||
|
locals {
|
||||||
|
_dns_path = try(pathexpand(var.factories_config.dns), null)
|
||||||
|
_dns_files = try(fileset(local._dns_path, "**/*.yaml"), [])
|
||||||
|
_dns_preprocess = [
|
||||||
|
for f in local._dns_files : merge(yamldecode(file("${coalesce(local._dns_path, "-")}/${f}")), {
|
||||||
|
key = replace(f, ".yaml", "")
|
||||||
|
})
|
||||||
|
]
|
||||||
|
dns_zones = {
|
||||||
|
for zone_config in local._dns_preprocess : "${zone_config.key}" => merge(
|
||||||
|
zone_config,
|
||||||
|
{
|
||||||
|
project_id = zone_config.project_id
|
||||||
|
name = replace(zone_config.key, "/", "-")
|
||||||
|
description = try(zone_config.description, "Terraform-managed.")
|
||||||
|
force_destroy = try(zone_config.force_destroy, null)
|
||||||
|
iam = try(zone_config.iam, null)
|
||||||
|
recordsets = try(zone_config.recordsets, null)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
zone_config = merge(
|
||||||
|
{ domain = try(zone_config.domain, null) },
|
||||||
|
contains(keys(try(zone_config, {})), "private")
|
||||||
|
? {
|
||||||
|
private = {
|
||||||
|
service_directory_namespace = try(
|
||||||
|
zone_config.private.service_directory_namespace, null
|
||||||
|
)
|
||||||
|
client_networks = zone_config.private.client_networks
|
||||||
|
}
|
||||||
|
}
|
||||||
|
: {},
|
||||||
|
contains(keys(try(zone_config, {})), "peering")
|
||||||
|
? {
|
||||||
|
peering = {
|
||||||
|
peer_network = zone_config.peering.peer_network
|
||||||
|
client_networks = zone_config.peering.client_networks
|
||||||
|
}
|
||||||
|
}
|
||||||
|
: {},
|
||||||
|
contains(keys(try(zone_config, {})), "forwarding")
|
||||||
|
? {
|
||||||
|
forwarding = {
|
||||||
|
forwarders = try(zone_config.forwarding.forwarders, {}),
|
||||||
|
client_networks = zone_config.forwarding.client_networks
|
||||||
|
}
|
||||||
|
}
|
||||||
|
: {}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
# DNS response policies
|
||||||
|
_dns_response_policies_path = try(
|
||||||
|
pathexpand(var.factories_config.dns-response-policies), null
|
||||||
|
)
|
||||||
|
_dns_response_policies_files = try(
|
||||||
|
fileset(local._dns_response_policies_path, "**/*.yaml"), []
|
||||||
|
)
|
||||||
|
_dns_response_policies_preprocess = [
|
||||||
|
for f in local._dns_response_policies_files :
|
||||||
|
merge(
|
||||||
|
yamldecode(file("${coalesce(local._dns_response_policies_path, "-")}/${f}")),
|
||||||
|
{
|
||||||
|
key = replace(f, ".yaml", "")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
]
|
||||||
|
dns_response_policies = {
|
||||||
|
for policy_config in local._dns_response_policies_preprocess : "${policy_config.key}" => {
|
||||||
|
project_id = policy_config.project_id
|
||||||
|
name = policy_config.key
|
||||||
|
networks = policy_config.networks
|
||||||
|
rules = policy_config.rules
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module "dns-zones" {
|
||||||
|
source = "../../../modules/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
|
||||||
|
context = {
|
||||||
|
project_ids = local.ctx_projects.project_ids
|
||||||
|
networks = local.ctx_vpcs.self_links
|
||||||
|
}
|
||||||
|
depends_on = [module.vpcs]
|
||||||
|
}
|
||||||
|
|
||||||
|
module "dns-response-policies" {
|
||||||
|
source = "../../../modules/dns-response-policy"
|
||||||
|
for_each = local.dns_response_policies
|
||||||
|
project_id = each.value.project_id
|
||||||
|
name = each.value.name
|
||||||
|
networks = { for n in each.value.networks : n => n }
|
||||||
|
rules = each.value.rules
|
||||||
|
context = {
|
||||||
|
project_ids = local.ctx_projects.project_ids
|
||||||
|
networks = local.ctx_vpcs.self_links
|
||||||
|
}
|
||||||
|
depends_on = [module.vpcs]
|
||||||
|
}
|
||||||
53
fast/stages/2-networking/factory-firewall-policies.tf
Normal file
53
fast/stages/2-networking/factory-firewall-policies.tf
Normal file
@@ -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 Firewall policies factory.
|
||||||
|
|
||||||
|
locals {
|
||||||
|
_firewall_policies_path = try(
|
||||||
|
pathexpand(var.factories_config.firewall-policies), null
|
||||||
|
)
|
||||||
|
_firewall_policies_files = local._firewall_policies_path == null ? [] : fileset(
|
||||||
|
local._firewall_policies_path, "**/*.yaml"
|
||||||
|
)
|
||||||
|
_firewall_policies_data = {
|
||||||
|
for f in local._firewall_policies_files : replace(f, ".yaml", "") => yamldecode(
|
||||||
|
file("${local._firewall_policies_path}/${f}")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
firewall_policies = {
|
||||||
|
for k, v in local._firewall_policies_data : try(v.name, k) => merge(v, {
|
||||||
|
parent = v.parent_id
|
||||||
|
attachments = try(v.attachments, {})
|
||||||
|
ingress_rules = try(v.ingress_rules, {})
|
||||||
|
egress_rules = try(v.egress_rules, {})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module "firewall_policies" {
|
||||||
|
source = "../../../modules/net-firewall-policy"
|
||||||
|
for_each = local.firewall_policies
|
||||||
|
attachments = each.value.attachments
|
||||||
|
name = each.key
|
||||||
|
parent_id = each.value.parent
|
||||||
|
egress_rules = each.value.egress_rules
|
||||||
|
ingress_rules = each.value.ingress_rules
|
||||||
|
context = {
|
||||||
|
folder_ids = local.ctx_folders
|
||||||
|
cidr_ranges_sets = local.ctx.cidr_ranges_sets
|
||||||
|
}
|
||||||
|
}
|
||||||
190
fast/stages/2-networking/factory-ncc.tf
Normal file
190
fast/stages/2-networking/factory-ncc.tf
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
/**
|
||||||
|
* 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 Hubs and Groups factory
|
||||||
|
|
||||||
|
locals {
|
||||||
|
_ncc_path = try(pathexpand(var.factories_config.ncc-hubs), null)
|
||||||
|
_ncc_files = try(fileset(local._ncc_path, "**/*.yaml"), [])
|
||||||
|
_ncc_preprocess = [
|
||||||
|
for f in local._ncc_files : yamldecode(
|
||||||
|
file("${coalesce(local._ncc_path, "-")}/${f}")
|
||||||
|
)
|
||||||
|
]
|
||||||
|
# Since NCC groups depend on NCC hubs, two different lookup maps avoid circular dependencies.
|
||||||
|
ctx_ncc_groups = {
|
||||||
|
for k, v in google_network_connectivity_group.default : k => v.id
|
||||||
|
}
|
||||||
|
ctx_ncc_hubs = {
|
||||||
|
for k, v in google_network_connectivity_hub.default : k => v.id
|
||||||
|
}
|
||||||
|
ncc_hubs = {
|
||||||
|
for k, v in local._ncc_preprocess : v.name => {
|
||||||
|
project_id = v.project_id
|
||||||
|
description = try(v.description, "Terraform-managed")
|
||||||
|
export_psc = try(v.export_psc, true)
|
||||||
|
preset_topology = try(v.preset_topology, "MESH")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ncc_groups = merge(flatten([
|
||||||
|
for k, v in local._ncc_preprocess : {
|
||||||
|
for gk, gv in try(v.groups, {}) : "${v.name}/${gk}" =>
|
||||||
|
{
|
||||||
|
name = gk
|
||||||
|
project_id = v.project_id
|
||||||
|
hub = v.name
|
||||||
|
description = try(gv.description, "Terraform-managed")
|
||||||
|
labels = try(gv.labels, {})
|
||||||
|
auto_accept = try(gv.auto_accept, [])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
])...)
|
||||||
|
ncc_vpc_spokes = {
|
||||||
|
for vpc_key, vpc_config in local.vpcs :
|
||||||
|
"${vpc_key}/${replace(vpc_config.ncc_config.hub, "$ncc_hubs:", "")}" => merge(
|
||||||
|
vpc_config.ncc_config,
|
||||||
|
{
|
||||||
|
project_id = vpc_config.project_id
|
||||||
|
vpc_self_link = vpc_key
|
||||||
|
labels = try(vpc_config.ncc_config.labels, {})
|
||||||
|
hub = vpc_config.ncc_config.hub
|
||||||
|
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(vpc_config.ncc_config.group, null)
|
||||||
|
}
|
||||||
|
) if try(vpc_config.ncc_config != null, false)
|
||||||
|
}
|
||||||
|
ncc_vpn_spokes = {
|
||||||
|
for vpn_key, vpn_config in local.vpns :
|
||||||
|
"${vpn_key}/${replace(vpn_config.ncc_spoke_config.hub, "$ncc_hubs:", "")}" => merge(
|
||||||
|
vpn_config.ncc_spoke_config,
|
||||||
|
{
|
||||||
|
name = replace("${vpn_key}/${vpn_config.ncc_spoke_config.hub}", "$ncc_hubs:", "") # TODO: eww
|
||||||
|
project_id = vpn_config.project_id
|
||||||
|
hub = vpn_config.ncc_spoke_config.hub
|
||||||
|
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[vpn_key].tunnel_self_links[t]]
|
||||||
|
}
|
||||||
|
) if try(vpn_config.ncc_spoke_config != null, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "google_network_connectivity_hub" "default" {
|
||||||
|
for_each = local.ncc_hubs
|
||||||
|
project = lookup(
|
||||||
|
local.ctx_projects.project_ids,
|
||||||
|
replace(each.value.project_id, "$project_ids:", ""),
|
||||||
|
each.value.project_id
|
||||||
|
)
|
||||||
|
name = each.key
|
||||||
|
description = each.value.description
|
||||||
|
export_psc = each.value.export_psc
|
||||||
|
preset_topology = each.value.preset_topology
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "google_network_connectivity_group" "default" {
|
||||||
|
for_each = local.ncc_groups
|
||||||
|
project = lookup(
|
||||||
|
local.ctx_projects.project_ids,
|
||||||
|
replace(each.value.project_id, "$project_ids:", ""),
|
||||||
|
each.value.project_id
|
||||||
|
)
|
||||||
|
name = each.value.name
|
||||||
|
hub = lookup(
|
||||||
|
local.ctx_ncc_hubs, replace(each.value.hub, "$ncc_hubs:", ""), 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 = [
|
||||||
|
for project_key in try(each.value.auto_accept, []) : lookup(
|
||||||
|
local.ctx_projects.project_ids,
|
||||||
|
replace(project_key, "$project_ids:", ""),
|
||||||
|
project_key
|
||||||
|
)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
depends_on = [google_network_connectivity_hub.default]
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "google_network_connectivity_spoke" "vpcs" {
|
||||||
|
for_each = local.ncc_vpc_spokes
|
||||||
|
project = lookup(
|
||||||
|
local.ctx_projects.project_ids,
|
||||||
|
replace(each.value.project_id, "$project_ids:", ""),
|
||||||
|
each.value.project_id
|
||||||
|
)
|
||||||
|
name = replace(each.key, "/", "-")
|
||||||
|
location = "global"
|
||||||
|
description = each.value.description
|
||||||
|
labels = each.value.labels
|
||||||
|
hub = lookup(
|
||||||
|
local.ctx_ncc_hubs,
|
||||||
|
replace(each.value.hub, "$ncc_hubs:", ""),
|
||||||
|
each.value.hub
|
||||||
|
)
|
||||||
|
linked_vpc_network {
|
||||||
|
uri = lookup(
|
||||||
|
local.ctx_vpcs.self_links,
|
||||||
|
each.value.vpc_self_link,
|
||||||
|
each.value.vpc_self_link
|
||||||
|
)
|
||||||
|
exclude_export_ranges = each.value.exclude_export_ranges
|
||||||
|
include_export_ranges = each.value.include_export_ranges
|
||||||
|
}
|
||||||
|
group = each.value.group == null ? null : lookup(
|
||||||
|
local.ctx_ncc_groups,
|
||||||
|
replace(each.value.group, "$ncc_groups:", ""),
|
||||||
|
each.value.group
|
||||||
|
)
|
||||||
|
depends_on = [google_network_connectivity_hub.default]
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "google_network_connectivity_spoke" "tunnels" {
|
||||||
|
for_each = local.ncc_vpn_spokes
|
||||||
|
project = lookup(
|
||||||
|
local.ctx_projects.project_ids,
|
||||||
|
replace(each.value.project_id, "$project_ids:", ""),
|
||||||
|
each.value.project_id
|
||||||
|
)
|
||||||
|
name = replace(each.key, "/", "-")
|
||||||
|
location = lookup(
|
||||||
|
local.ctx.locations,
|
||||||
|
replace(each.value.location, "$locations:", ""),
|
||||||
|
each.value.location
|
||||||
|
)
|
||||||
|
description = each.value.description
|
||||||
|
labels = each.value.labels
|
||||||
|
hub = lookup(
|
||||||
|
local.ctx_ncc_hubs,
|
||||||
|
replace(each.value.hub, "$ncc_hubs:", ""),
|
||||||
|
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]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
179
fast/stages/2-networking/factory-nva.tf
Normal file
179
fast/stages/2-networking/factory-nva.tf
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
/**
|
||||||
|
* 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 NVA factory
|
||||||
|
|
||||||
|
locals {
|
||||||
|
_nva_path = try(pathexpand(var.factories_config.nvas), null)
|
||||||
|
_nva_files = try(fileset(local._nva_path, "**/*.yaml"), [])
|
||||||
|
_nva_configs = [
|
||||||
|
for f in local._nva_files : merge(
|
||||||
|
yamldecode(file("${coalesce(local._nva_path, "-")}/${f}")),
|
||||||
|
{ filename = replace(f, ".yaml", "") }
|
||||||
|
)
|
||||||
|
]
|
||||||
|
ctx_nva = {
|
||||||
|
ilb_addresses = {
|
||||||
|
for k, v in module.ilb : k => v.forwarding_rule_addresses[""]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
nva_configs = {
|
||||||
|
for k, v in local._nva_configs : try(v.name, k) => merge(v, {
|
||||||
|
attachments = [for a in try(v.attachments, []) : merge(a, {
|
||||||
|
routes = try(a.routes, [])
|
||||||
|
create_ilb = try(a.create_ilb, true)
|
||||||
|
})]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
nva_instances = merge(flatten([
|
||||||
|
for nva_key, nva_def in local.nva_configs : [
|
||||||
|
for group_key, group_value in nva_def.instance_groups : [
|
||||||
|
for i in range(try(group_value.auto_create_instances, 0)) : {
|
||||||
|
"${nva_def.name}-${group_key}-${i}" = {
|
||||||
|
group_zone = group_key
|
||||||
|
zone = "${nva_def.region}-${group_key}"
|
||||||
|
project_id = nva_def.project_id
|
||||||
|
image = try(
|
||||||
|
nva_def.image, "projects/debian-cloud/global/images/family/debian-12"
|
||||||
|
)
|
||||||
|
instance_type = try(
|
||||||
|
nva_def.instance_type, "e2-standard-4"
|
||||||
|
)
|
||||||
|
metadata = coalesce(
|
||||||
|
try(nva_def.metadata, null),
|
||||||
|
{
|
||||||
|
user-data = templatefile(
|
||||||
|
"${path.module}/assets/nva-startup-script.yaml.tpl",
|
||||||
|
{ nva_nics_config = nva_def.attachments }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
attachments = nva_def.attachments
|
||||||
|
tags = try(nva_def.tags, ["nva"])
|
||||||
|
options = try(nva_def.options, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
])...)
|
||||||
|
nva_instance_groups = merge([
|
||||||
|
for nva_def in local.nva_configs : {
|
||||||
|
for group_key, group_value in try(nva_def.instance_groups, {}) :
|
||||||
|
"${nva_def.name}-${group_key}" => {
|
||||||
|
nva_config = nva_def.name
|
||||||
|
zone_key = group_key
|
||||||
|
name = "nva-${nva_def.name}-${group_key}"
|
||||||
|
project_id = nva_def.project_id
|
||||||
|
zone = "${nva_def.region}-${group_key}"
|
||||||
|
network = try(nva_def.attachments[0].network, null)
|
||||||
|
instances = toset(concat(
|
||||||
|
[
|
||||||
|
for i in range(try(group_value.auto_create_instances, 0)) :
|
||||||
|
module.nva-instance["${nva_def.name}-${group_key}-${i}"].self_link
|
||||||
|
],
|
||||||
|
flatten([
|
||||||
|
for v in try(group_value.attach_instances, {}) : values(v)
|
||||||
|
])
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]...)
|
||||||
|
nva_ilbs = merge(flatten([
|
||||||
|
for nva_def in local.nva_configs : [
|
||||||
|
for i, attachment in nva_def.attachments : {
|
||||||
|
"${replace(attachment.network, "$networks:", "")}/${nva_def.name}" = {
|
||||||
|
name = "ilb-${nva_def.name}-${i}"
|
||||||
|
nva_config = nva_def.name
|
||||||
|
project_id = nva_def.project_id
|
||||||
|
region = nva_def.region
|
||||||
|
vpc_config = {
|
||||||
|
network = attachment.network
|
||||||
|
subnetwork = attachment.subnet
|
||||||
|
}
|
||||||
|
health_check = try(nva_def.health_check, null)
|
||||||
|
}
|
||||||
|
} if attachment.create_ilb == true
|
||||||
|
]
|
||||||
|
])...)
|
||||||
|
}
|
||||||
|
|
||||||
|
module "nva-instance" {
|
||||||
|
for_each = local.nva_instances
|
||||||
|
source = "../../../modules/compute-vm"
|
||||||
|
project_id = each.value.project_id
|
||||||
|
name = "nva-${each.key}"
|
||||||
|
zone = each.value.zone
|
||||||
|
instance_type = each.value.instance_type
|
||||||
|
tags = each.value.tags
|
||||||
|
can_ip_forward = true
|
||||||
|
network_interfaces = [for k, v in each.value.attachments :
|
||||||
|
{
|
||||||
|
network = v.network
|
||||||
|
subnetwork = v.subnet
|
||||||
|
nat = false
|
||||||
|
addresses = null
|
||||||
|
}
|
||||||
|
]
|
||||||
|
boot_disk = {
|
||||||
|
initialize_params = {
|
||||||
|
image = each.value.image
|
||||||
|
google-logging-enabled = true
|
||||||
|
type = "pd-ssd"
|
||||||
|
size = 10 # TODO: make configurable?
|
||||||
|
}
|
||||||
|
}
|
||||||
|
metadata = each.value.metadata
|
||||||
|
context = {
|
||||||
|
project_ids = local.ctx_projects.project_ids
|
||||||
|
vpcs = local.ctx_vpcs.self_links
|
||||||
|
subnets = local.ctx_vpcs.subnets_by_vpc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "google_compute_instance_group" "nva" {
|
||||||
|
for_each = local.nva_instance_groups
|
||||||
|
project = lookup(
|
||||||
|
local.ctx_projects.project_ids,
|
||||||
|
replace(each.value.project_id, "$project_ids:", ""),
|
||||||
|
each.value.project_id
|
||||||
|
)
|
||||||
|
zone = each.value.zone
|
||||||
|
name = each.value.name
|
||||||
|
#network = lookup(local.ctx_vpcs.self_links, replace(each.value.network, "$networks:", ""), each.value.network)
|
||||||
|
instances = each.value.instances
|
||||||
|
depends_on = [module.nva-instance]
|
||||||
|
}
|
||||||
|
|
||||||
|
module "ilb" {
|
||||||
|
source = "../../../modules/net-lb-int"
|
||||||
|
for_each = local.nva_ilbs
|
||||||
|
project_id = each.value.project_id
|
||||||
|
region = each.value.region
|
||||||
|
name = replace("ilb-${each.key}", "/", "-")
|
||||||
|
vpc_config = each.value.vpc_config
|
||||||
|
backends = [
|
||||||
|
for k, v in local.nva_instance_groups : {
|
||||||
|
group = google_compute_instance_group.nva[k].id
|
||||||
|
} if v.nva_config == each.value.nva_config
|
||||||
|
]
|
||||||
|
health_check_config = each.value.health_check
|
||||||
|
context = {
|
||||||
|
project_ids = local.ctx_projects.project_ids
|
||||||
|
vpcs = local.ctx_vpcs.self_links
|
||||||
|
subnets = local.ctx_vpcs.subnets_by_vpc
|
||||||
|
}
|
||||||
|
depends_on = [module.nva-instance]
|
||||||
|
}
|
||||||
64
fast/stages/2-networking/factory-peering.tf
Normal file
64
fast/stages/2-networking/factory-peering.tf
Normal file
@@ -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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
# tfdoc:file:description VPC Peering factory.
|
||||||
|
|
||||||
|
locals {
|
||||||
|
peering_configs = merge(flatten([
|
||||||
|
for vpc_key, vpc_config in local.vpcs : {
|
||||||
|
for peering_key, peering_configs in try(vpc_config.peering_config, {}) :
|
||||||
|
"${vpc_key}/${peering_key}" => merge(peering_configs, {
|
||||||
|
project = vpc_config.project_id
|
||||||
|
name = replace("${vpc_key}/${peering_key}", "/", "-")
|
||||||
|
local_network = vpc_key
|
||||||
|
peer_network = peering_configs.peer_network
|
||||||
|
export_custom_routes = try(
|
||||||
|
peering_configs.routes_config.export, true
|
||||||
|
)
|
||||||
|
import_custom_routes = try(
|
||||||
|
peering_configs.routes_config.import, true
|
||||||
|
)
|
||||||
|
export_subnet_routes_with_public_ip = try(
|
||||||
|
peering_configs.routes_config.public_export, null
|
||||||
|
)
|
||||||
|
import_subnet_routes_with_public_ip = try(
|
||||||
|
peering_configs.routes_config.public_import, null
|
||||||
|
)
|
||||||
|
stack_type = try(
|
||||||
|
peering_configs.stack_type, null
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
])...)
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "google_compute_network_peering" "default" {
|
||||||
|
for_each = local.peering_configs
|
||||||
|
name = each.value.name
|
||||||
|
network = lookup(local.ctx_vpcs.self_links,
|
||||||
|
replace(each.value.local_network, "$networks:", ""),
|
||||||
|
each.value.local_network
|
||||||
|
)
|
||||||
|
peer_network = lookup(local.ctx_vpcs.self_links,
|
||||||
|
replace(each.value.peer_network, "$networks:", ""),
|
||||||
|
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
|
||||||
|
}
|
||||||
55
fast/stages/2-networking/factory-projects.tf
Normal file
55
fast/stages/2-networking/factory-projects.tf
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
/**
|
||||||
|
* 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 Projects factory.
|
||||||
|
|
||||||
|
locals {
|
||||||
|
ctx_folders = merge(local.ctx.folder_ids, module.projects.folder_ids, {
|
||||||
|
organization = "organizations/${var.organization.id}"
|
||||||
|
})
|
||||||
|
ctx_projects = {
|
||||||
|
project_ids = merge(local.ctx.project_ids, module.projects.project_ids)
|
||||||
|
}
|
||||||
|
project_defaults = {
|
||||||
|
defaults = merge(
|
||||||
|
{
|
||||||
|
billing_account = var.billing_account.id
|
||||||
|
prefix = var.prefix
|
||||||
|
},
|
||||||
|
lookup(var.folder_ids, local.defaults.folder_name, null) == null ? {} : {
|
||||||
|
parent = lookup(var.folder_ids, local.defaults.folder_name, null)
|
||||||
|
},
|
||||||
|
try(local._defaults.projects.defaults, {})
|
||||||
|
)
|
||||||
|
overrides = try(local._defaults.projects.overrides, {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
moved {
|
||||||
|
from = module.factory
|
||||||
|
to = module.projects
|
||||||
|
}
|
||||||
|
|
||||||
|
module "projects" {
|
||||||
|
source = "../../../modules/project-factory"
|
||||||
|
data_defaults = local.project_defaults.defaults
|
||||||
|
data_overrides = local.project_defaults.overrides
|
||||||
|
context = local.ctx
|
||||||
|
factories_config = {
|
||||||
|
folders = var.factories_config.folders
|
||||||
|
projects = var.factories_config.projects
|
||||||
|
}
|
||||||
|
}
|
||||||
74
fast/stages/2-networking/factory-routers.tf
Normal file
74
fast/stages/2-networking/factory-routers.tf
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
/**
|
||||||
|
* 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 Routers factory.
|
||||||
|
|
||||||
|
locals {
|
||||||
|
ctx_routers = {
|
||||||
|
ids = { for k, v in google_compute_router.default : k => v.id }
|
||||||
|
names = { for k, v in google_compute_router.default : k => v.name }
|
||||||
|
}
|
||||||
|
router_configs = merge(flatten([
|
||||||
|
for vpc_key, vpc_config in local.vpcs : [
|
||||||
|
for router_key, router_config in try(vpc_config.routers, {}) : {
|
||||||
|
"${vpc_key}/${router_key}" = merge(router_config, {
|
||||||
|
name = replace("${vpc_key}/${router_key}", "/", "-")
|
||||||
|
vpc_self_link = vpc_key
|
||||||
|
project_id = vpc_config.project_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)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
]
|
||||||
|
])...)
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "google_compute_router" "default" {
|
||||||
|
for_each = local.router_configs
|
||||||
|
name = replace(each.key, "/", "-")
|
||||||
|
project = lookup(
|
||||||
|
local.ctx_projects.project_ids,
|
||||||
|
replace(each.value.project_id, "$project_ids:", ""),
|
||||||
|
each.value.project_id
|
||||||
|
)
|
||||||
|
region = lookup(
|
||||||
|
local.ctx.locations,
|
||||||
|
replace(each.value.region, "$locations:", ""),
|
||||||
|
each.value.region
|
||||||
|
)
|
||||||
|
network = lookup(
|
||||||
|
local.ctx_vpcs.self_links,
|
||||||
|
each.value.vpc_self_link,
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
147
fast/stages/2-networking/factory-vpcs.tf
Normal file
147
fast/stages/2-networking/factory-vpcs.tf
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
/**
|
||||||
|
* 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 rules factory.
|
||||||
|
|
||||||
|
locals {
|
||||||
|
_vpcs_path = try(
|
||||||
|
pathexpand(var.factories_config.vpcs), null
|
||||||
|
)
|
||||||
|
_vpcs_files = try(
|
||||||
|
fileset(local._vpcs_path, "**/.config.yaml"),
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
_vpcs_preprocess = [
|
||||||
|
for f in local._vpcs_files : merge(
|
||||||
|
yamldecode(file("${coalesce(local._vpcs_path, "-")}/${f}")),
|
||||||
|
{
|
||||||
|
factory_dirname = dirname(f)
|
||||||
|
factory_basepath = "${local._vpcs_path}/${dirname(f)}"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
]
|
||||||
|
_vpcs = {
|
||||||
|
for v in local._vpcs_preprocess : v.factory_dirname => v
|
||||||
|
}
|
||||||
|
ctx_vpcs = {
|
||||||
|
ids = { for k, v in module.vpcs : k => v.id }
|
||||||
|
names = { for k, v in module.vpcs : k => v.name }
|
||||||
|
self_links = { for k, v in module.vpcs : k => v.self_link }
|
||||||
|
subnets_by_vpc = merge([
|
||||||
|
for vpc_key, vpc in module.vpcs : {
|
||||||
|
for subnet_key, subnet_self_link in vpc.subnet_self_links :
|
||||||
|
"${vpc_key}/${subnet_key}" => subnet_self_link
|
||||||
|
}
|
||||||
|
]...)
|
||||||
|
}
|
||||||
|
vpcs = {
|
||||||
|
for k, v in local._vpcs : k => merge(
|
||||||
|
local.defaults.vpcs, v,
|
||||||
|
{
|
||||||
|
project_id = v.project_id
|
||||||
|
description = try(v.description, "Terraform managed")
|
||||||
|
create_googleapis_routes = try(v.create_googleapis_routes, {})
|
||||||
|
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, {})
|
||||||
|
name = v.name
|
||||||
|
network_attachments = try(v.network_attachments, {})
|
||||||
|
policy_based_routes = try(v.policy_based_routes, {})
|
||||||
|
psa_configs = try(v.psa_configs, [])
|
||||||
|
routes = try(v.routes, {})
|
||||||
|
routing_mode = try(v.routing_mode, "GLOBAL")
|
||||||
|
subnets_private_nat = try(v.subnets_private_nat, [])
|
||||||
|
subnets_proxy_only = try(v.subnets_proxy_only, [])
|
||||||
|
subnets_psc = try(v.subnets_psc, [])
|
||||||
|
subnets = try(v.subnets, [])
|
||||||
|
subnets_factory_config = {
|
||||||
|
subnets_folder = "${v.factory_basepath}/subnets"
|
||||||
|
}
|
||||||
|
firewall_factory_config = {
|
||||||
|
rules_folder = "${v.factory_basepath}/firewall-rules"
|
||||||
|
}
|
||||||
|
peering_config = try(v.peering_config, {})
|
||||||
|
vpn_config = try(v.vpn_config, {})
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
vpc_defaults = try(local._defaults.vpcs.defaults, {})
|
||||||
|
}
|
||||||
|
|
||||||
|
module "vpcs" {
|
||||||
|
source = "../../../modules/net-vpc"
|
||||||
|
for_each = local.vpcs
|
||||||
|
project_id = each.value.project_id
|
||||||
|
name = each.value.name
|
||||||
|
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
|
||||||
|
description = each.value.description
|
||||||
|
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_configs
|
||||||
|
routing_mode = each.value.routing_mode
|
||||||
|
subnets = each.value.subnets
|
||||||
|
subnets_private_nat = each.value.subnets_private_nat
|
||||||
|
subnets_proxy_only = each.value.subnets_proxy_only
|
||||||
|
subnets_psc = each.value.subnets_psc
|
||||||
|
context = {
|
||||||
|
project_ids = local.ctx_projects.project_ids
|
||||||
|
locations = local.ctx.locations
|
||||||
|
}
|
||||||
|
depends_on = [module.projects]
|
||||||
|
}
|
||||||
|
|
||||||
|
module "vpc_routes" {
|
||||||
|
source = "../../../modules/net-vpc"
|
||||||
|
for_each = local.vpcs
|
||||||
|
vpc_reuse = {
|
||||||
|
use_data_source = false
|
||||||
|
attributes = { network_id = module.vpcs[each.key].network_id }
|
||||||
|
}
|
||||||
|
project_id = each.value.project_id
|
||||||
|
name = each.value.name
|
||||||
|
routes = each.value.routes
|
||||||
|
context = {
|
||||||
|
project_ids = local.ctx_projects.project_ids
|
||||||
|
locations = local.ctx.locations
|
||||||
|
addresses = local.ctx_nva.ilb_addresses
|
||||||
|
}
|
||||||
|
depends_on = [
|
||||||
|
module.projects,
|
||||||
|
module.vpcs
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
module "firewall" {
|
||||||
|
source = "../../../modules/net-vpc-firewall"
|
||||||
|
for_each = {
|
||||||
|
for k, v in local.vpcs : k => v if v.firewall_factory_config != null
|
||||||
|
}
|
||||||
|
project_id = each.value.project_id
|
||||||
|
network = each.value.name
|
||||||
|
factories_config = each.value.firewall_factory_config
|
||||||
|
default_rules_config = { disabled = true }
|
||||||
|
context = {
|
||||||
|
project_ids = local.ctx_projects.project_ids
|
||||||
|
}
|
||||||
|
depends_on = [module.vpcs]
|
||||||
|
}
|
||||||
94
fast/stages/2-networking/factory-vpns.tf
Normal file
94
fast/stages/2-networking/factory-vpns.tf
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
/**
|
||||||
|
* 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 VPNs factory.
|
||||||
|
|
||||||
|
locals {
|
||||||
|
_vpns_files = try(
|
||||||
|
fileset(local._vpcs_path, "**/vpns/*.yaml"),
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
_vpns_preprocess = [
|
||||||
|
for f in local._vpns_files : merge(
|
||||||
|
yamldecode(file("${coalesce(local._vpcs_path, "-")}/${f}")),
|
||||||
|
{
|
||||||
|
factory_basepath = dirname(dirname(f))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
]
|
||||||
|
ctx_gateways = { for k, v in google_compute_ha_vpn_gateway.default : k => v.id }
|
||||||
|
vpns = {
|
||||||
|
for v in local._vpns_preprocess : "${v.factory_basepath}/${v.name}" => merge(v, {
|
||||||
|
vpc_name = v.factory_basepath
|
||||||
|
# TODO: discuss - this is pushing context at any cost, as project could be easily resolved
|
||||||
|
# as module.vpcs[v.factory_basepath].project_id
|
||||||
|
project_id = local.vpcs[v.factory_basepath].project_id
|
||||||
|
router_config = try(v.router_config, {})
|
||||||
|
region = try(v.region, local.defaults.vpcs.region)
|
||||||
|
peer_gateways = try(v.peer_gateways, {})
|
||||||
|
tunnels = try(v.tunnels, {})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "google_compute_ha_vpn_gateway" "default" {
|
||||||
|
for_each = local.vpns
|
||||||
|
project = lookup(
|
||||||
|
merge(local.ctx.project_ids, module.projects.project_ids),
|
||||||
|
replace(each.value.project_id, "$project_ids:", ""),
|
||||||
|
each.value.project_id
|
||||||
|
)
|
||||||
|
region = lookup(
|
||||||
|
local.ctx.locations,
|
||||||
|
replace(each.value.region, "$locations:", ""),
|
||||||
|
each.value.region
|
||||||
|
)
|
||||||
|
network = lookup(
|
||||||
|
local.ctx_vpcs.names, each.value.vpc_name, each.value.vpc_name
|
||||||
|
)
|
||||||
|
name = replace(each.key, "/", "-")
|
||||||
|
stack_type = try(each.value.stack_type, null)
|
||||||
|
depends_on = [module.vpcs]
|
||||||
|
}
|
||||||
|
|
||||||
|
module "vpn-ha" {
|
||||||
|
source = "../../../modules/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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
context = {
|
||||||
|
gateways = local.ctx_gateways
|
||||||
|
locations = local.ctx.locations
|
||||||
|
network = local.ctx_vpcs.names
|
||||||
|
project_ids = local.ctx_projects.project_ids
|
||||||
|
routers = local.ctx_routers.names
|
||||||
|
}
|
||||||
|
}
|
||||||
15
fast/stages/2-networking/fast_version.txt
Normal file
15
fast/stages/2-networking/fast_version.txt
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
# 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
|
||||||
|
#
|
||||||
|
# https://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.
|
||||||
|
|
||||||
|
# FAST release: v45.0.0
|
||||||
56
fast/stages/2-networking/main.tf
Normal file
56
fast/stages/2-networking/main.tf
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2025 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
locals {
|
||||||
|
paths = {
|
||||||
|
for k, v in var.factories_config : k => try(pathexpand(v), null)
|
||||||
|
}
|
||||||
|
# fail if we have no valid defaults
|
||||||
|
_defaults = yamldecode(file(local.paths.defaults))
|
||||||
|
ctx = merge(var.context, {
|
||||||
|
cidr_ranges_sets = try(local._defaults.context.cidr_ranges_sets, {})
|
||||||
|
folder_ids = merge(
|
||||||
|
var.folder_ids, var.context.folder_ids
|
||||||
|
)
|
||||||
|
iam_principals = merge(
|
||||||
|
var.iam_principals,
|
||||||
|
{
|
||||||
|
for k, v in var.service_accounts :
|
||||||
|
"service_accounts/${k}" => "serviceAccount:${v}"
|
||||||
|
},
|
||||||
|
var.context.iam_principals,
|
||||||
|
try(local._defaults.context.iam_principals, {})
|
||||||
|
)
|
||||||
|
locations = merge(
|
||||||
|
var.context.locations,
|
||||||
|
try(local._defaults.context.locations, {})
|
||||||
|
)
|
||||||
|
project_ids = merge(var.project_ids, var.context.project_ids)
|
||||||
|
tag_keys = merge(var.tag_keys, var.context.tag_keys)
|
||||||
|
tag_values = merge(var.tag_values, var.context.tag_values)
|
||||||
|
})
|
||||||
|
|
||||||
|
defaults = {
|
||||||
|
folder_name = try(local._defaults.global.folder_id, "networking")
|
||||||
|
stage_name = try(local._defaults.global.stage_name, "2-networking")
|
||||||
|
vpcs = try(local._defaults.vpcs, {})
|
||||||
|
}
|
||||||
|
output_files = {
|
||||||
|
local_path = try(local._defaults.output_files.local_path, null)
|
||||||
|
storage_bucket = try(local._defaults.output_files.storage_bucket, null)
|
||||||
|
providers = try(local._defaults.output_files.providers, {})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
93
fast/stages/2-networking/outputs.tf
Normal file
93
fast/stages/2-networking/outputs.tf
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2025 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
locals {
|
||||||
|
tfvars = {
|
||||||
|
host_project_ids = module.projects.project_ids
|
||||||
|
host_project_numbers = module.projects.project_numbers
|
||||||
|
subnet_self_links = {
|
||||||
|
for vpc_key, vpc in module.vpcs : vpc_key => vpc.subnet_ids
|
||||||
|
}
|
||||||
|
subnet_proxy_only_self_links = {
|
||||||
|
for vpc_key, vpc in module.vpcs : vpc_key => {
|
||||||
|
for subnet_key, subnet in vpc.subnets_proxy_only : subnet_key => subnet.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
subnet_psc_self_links = {
|
||||||
|
for vpc_key, vpc in module.vpcs : vpc_key => {
|
||||||
|
for subnet_key, subnet in vpc.subnets_psc : subnet_key => subnet.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vpc_self_links = {
|
||||||
|
for vpc_key, vpc in module.vpcs : vpc_key => vpc.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# generate tfvars file for subsequent stages
|
||||||
|
|
||||||
|
resource "local_file" "tfvars" {
|
||||||
|
for_each = var.outputs_location == null ? {} : { 1 = 1 }
|
||||||
|
file_permission = "0644"
|
||||||
|
filename = "${try(pathexpand(var.outputs_location), "")}tfvars/2-networking.auto.tfvars.json"
|
||||||
|
content = jsonencode(local.tfvars)
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "google_storage_bucket_object" "tfvars" {
|
||||||
|
for_each = try(var.automation.outputs_bucket, null) == null ? {} : { 1 = 1 }
|
||||||
|
bucket = var.automation.outputs_bucket
|
||||||
|
name = "tfvars/2-networking.auto.tfvars.json"
|
||||||
|
content = jsonencode(local.tfvars)
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "google_storage_bucket_object" "version" {
|
||||||
|
for_each = try(var.automation.outputs_bucket, null) == null || !fileexists("fast_version.txt") ? {} : { 1 = 1 }
|
||||||
|
bucket = var.automation.outputs_bucket
|
||||||
|
name = "versions/2-networking-version.txt"
|
||||||
|
source = "fast_version.txt"
|
||||||
|
}
|
||||||
|
|
||||||
|
# outputs
|
||||||
|
|
||||||
|
output "host_project_ids" {
|
||||||
|
description = "Project IDs."
|
||||||
|
value = local.tfvars.host_project_ids
|
||||||
|
}
|
||||||
|
|
||||||
|
output "host_project_numbers" {
|
||||||
|
description = "Project numbers."
|
||||||
|
value = local.tfvars.host_project_numbers
|
||||||
|
}
|
||||||
|
|
||||||
|
output "subnet_proxy_only_self_links" {
|
||||||
|
description = "Subnet proxy-only self-links."
|
||||||
|
value = local.tfvars.subnet_proxy_only_self_links
|
||||||
|
}
|
||||||
|
|
||||||
|
output "subnet_psc_self_links" {
|
||||||
|
description = "Subnet PSC self-links."
|
||||||
|
value = local.tfvars.subnet_psc_self_links
|
||||||
|
}
|
||||||
|
|
||||||
|
output "subnet_self_links" {
|
||||||
|
description = "Subnet self-links."
|
||||||
|
value = local.tfvars.subnet_self_links
|
||||||
|
}
|
||||||
|
|
||||||
|
output "vpc_self_links" {
|
||||||
|
description = "VPC self-links."
|
||||||
|
value = local.tfvars.vpc_self_links
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"title": "DNS Response Policy Rules Factory",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"project_id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"networks": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-z0-9-]+$": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"dns_name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"behavior": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"local_data": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"patternProperties": {
|
||||||
|
"^(?:A|AAAA|CAA|CNAME|DNSKEY|DS|HTTPS|IPSECVPNKEY|MX|NAPTR|NS|PTR|SOA|SPF|SRV|SSHFP|SVCB|TLSA|TXT)$": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"ttl": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"rrdatas": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
183
fast/stages/2-networking/schemas/dns.schema.json
Normal file
183
fast/stages/2-networking/schemas/dns.schema.json
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"title": "DNS Zone configuration",
|
||||||
|
"description": "Schema for DNS zone YAML configuration files.",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"project_id": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Project ID for the DNS zone."
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Optional description for the DNS zone."
|
||||||
|
},
|
||||||
|
"force_destroy": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether to force deletion of the zone."
|
||||||
|
},
|
||||||
|
"domain": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Domain name for the zone (e.g. 'example.com.')."
|
||||||
|
},
|
||||||
|
"iam": {
|
||||||
|
"$ref": "#/$defs/iam"
|
||||||
|
},
|
||||||
|
"recordsets": {
|
||||||
|
"$ref": "#/$defs/recordsets"
|
||||||
|
},
|
||||||
|
"private": {
|
||||||
|
"$ref": "#/$defs/private_zone"
|
||||||
|
},
|
||||||
|
"peering": {
|
||||||
|
"$ref": "#/$defs/peering_zone"
|
||||||
|
},
|
||||||
|
"forwarding": {
|
||||||
|
"$ref": "#/$defs/forwarding_zone"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"project_id"
|
||||||
|
],
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"title": "Private Zone",
|
||||||
|
"required": [
|
||||||
|
"private"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"peering": {
|
||||||
|
"not": {}
|
||||||
|
},
|
||||||
|
"forwarding": {
|
||||||
|
"not": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Peering Zone",
|
||||||
|
"required": [
|
||||||
|
"peering"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"private": {
|
||||||
|
"not": {}
|
||||||
|
},
|
||||||
|
"forwarding": {
|
||||||
|
"not": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"title": "Forwarding Zone",
|
||||||
|
"required": [
|
||||||
|
"forwarding"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"private": {
|
||||||
|
"not": {}
|
||||||
|
},
|
||||||
|
"peering": {
|
||||||
|
"not": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"$defs": {
|
||||||
|
"iam": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"patternProperties": {
|
||||||
|
"^(?:roles/|\\$custom_roles:)": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^(?:domain:|group:|serviceAccount:|user:|principal:|principalSet:||\\$iam_principals:[a-z0-9_-]+)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"recordsets": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Map of DNS recordsets.",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"records": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"records"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"private_zone": {
|
||||||
|
"description": "Private zone specific configuration.",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"service_directory_namespace": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"client_networks": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"client_networks"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"peering_zone": {
|
||||||
|
"description": "Peering zone specific configuration.",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"peer_network": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"client_networks": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"peer_network",
|
||||||
|
"client_networks"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"forwarding_zone": {
|
||||||
|
"description": "Forwarding zone specific configuration.",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"forwarders": {
|
||||||
|
"type": "object",
|
||||||
|
"patternProperties": {
|
||||||
|
"^.*$": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"client_networks": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"client_networks"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
163
fast/stages/2-networking/schemas/firewall-policy.schema.json
Normal file
163
fast/stages/2-networking/schemas/firewall-policy.schema.json
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"title": "Network Firewall Policy",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"parent_id": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The parent folder or organization of the firewall policy."
|
||||||
|
},
|
||||||
|
"attachments": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Folders or organizations where the policy is attached.",
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-z0-9-]+$": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The name of the firewall policy."
|
||||||
|
},
|
||||||
|
"ingress_rules": {
|
||||||
|
"$ref": "#/$defs/rules",
|
||||||
|
"description": "A map of ingress firewall rules."
|
||||||
|
},
|
||||||
|
"egress_rules": {
|
||||||
|
"$ref": "#/$defs/rules",
|
||||||
|
"description": "A map of egress firewall rules."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"$defs": {
|
||||||
|
"rules": {
|
||||||
|
"title": "Firewall Rules",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-z0-9_-]+$": {
|
||||||
|
"$ref": "#/$defs/rule"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"rule": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"priority"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"priority": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"action": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"allow",
|
||||||
|
"deny",
|
||||||
|
"goto_next",
|
||||||
|
"apply_security_profile_group"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"disabled": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"enable_logging": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"security_profile_group": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"target_resources": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"target_service_accounts": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"target_tags": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tls_inspect": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"match": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"address_groups": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fqdns": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"region_codes": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"threat_intelligences": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"destination_ranges": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"source_ranges": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"source_tags": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"layer4_configs": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"protocol": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"ports": {
|
||||||
|
"type": "array"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
104
fast/stages/2-networking/schemas/firewall-rules.schema.json
Normal file
104
fast/stages/2-networking/schemas/firewall-rules.schema.json
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"title": "Firewall Rules",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"egress": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-z0-9_-]+$": {
|
||||||
|
"$ref": "#/$defs/rule"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ingress": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-z0-9_-]+$": {
|
||||||
|
"$ref": "#/$defs/rule"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"$defs": {
|
||||||
|
"rule": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"deny": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"destination_ranges": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"disabled": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"enable_logging": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"include_metadata": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"priority": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"source_ranges": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sources": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"targets": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"use_service_accounts": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"protocol": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"ports": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": [
|
||||||
|
"integer",
|
||||||
|
"string"
|
||||||
|
],
|
||||||
|
"pattern": "^[0-9]+(?:-[0-9]+)?$"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
555
fast/stages/2-networking/schemas/folder.schema.json
Normal file
555
fast/stages/2-networking/schemas/folder.schema.json
Normal file
@@ -0,0 +1,555 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"title": "Folder",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"automation": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"project"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"prefix": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"project": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"bucket": {
|
||||||
|
"$ref": "#/$defs/bucket"
|
||||||
|
},
|
||||||
|
"service_accounts": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-z0-9-]+$": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"iam": {
|
||||||
|
"$ref": "#/$defs/iam"
|
||||||
|
},
|
||||||
|
"iam_bindings": {
|
||||||
|
"$ref": "#/$defs/iam_bindings"
|
||||||
|
},
|
||||||
|
"iam_bindings_additive": {
|
||||||
|
"$ref": "#/$defs/iam_bindings_additive"
|
||||||
|
},
|
||||||
|
"iam_billing_roles": {
|
||||||
|
"$ref": "#/$defs/iam_billing_roles"
|
||||||
|
},
|
||||||
|
"iam_folder_roles": {
|
||||||
|
"$ref": "#/$defs/iam_folder_roles"
|
||||||
|
},
|
||||||
|
"iam_organization_roles": {
|
||||||
|
"$ref": "#/$defs/iam_organization_roles"
|
||||||
|
},
|
||||||
|
"iam_project_roles": {
|
||||||
|
"$ref": "#/$defs/iam_project_roles"
|
||||||
|
},
|
||||||
|
"iam_sa_roles": {
|
||||||
|
"$ref": "#/$defs/iam_sa_roles"
|
||||||
|
},
|
||||||
|
"iam_storage_roles": {
|
||||||
|
"$ref": "#/$defs/iam_storage_roles"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"factories_config": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"org_policies": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"pam_entitlements": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"scc_sha_custom_modules": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"iam": {
|
||||||
|
"$ref": "#/$defs/iam"
|
||||||
|
},
|
||||||
|
"iam_bindings": {
|
||||||
|
"$ref": "#/$defs/iam_bindings"
|
||||||
|
},
|
||||||
|
"iam_bindings_additive": {
|
||||||
|
"$ref": "#/$defs/iam_bindings_additive"
|
||||||
|
},
|
||||||
|
"iam_by_principals": {
|
||||||
|
"$ref": "#/$defs/iam_by_principals"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"org_policies": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-z]+\\.": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"inherit_from_parent": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"reset": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"rules": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"allow": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"all": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"values": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deny": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"all": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"values": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"enforce": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"condition": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"expression": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"location": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pam_entitlements": {
|
||||||
|
"$ref": "#/$defs/pam_entitlements"
|
||||||
|
},
|
||||||
|
"parent": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^(?:folders/[0-9]+|organizations/[0-9]+|\\$folder_ids:[a-z0-9_-]+)$"
|
||||||
|
},
|
||||||
|
"tag_bindings": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-z0-9_-]+$": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"$defs": {
|
||||||
|
"bucket": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"iam": {
|
||||||
|
"$ref": "#/$defs/iam"
|
||||||
|
},
|
||||||
|
"iam_bindings": {
|
||||||
|
"$ref": "#/$defs/iam_bindings"
|
||||||
|
},
|
||||||
|
"iam_bindings_additive": {
|
||||||
|
"$ref": "#/$defs/iam_bindings_additive"
|
||||||
|
},
|
||||||
|
"force_destroy": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"labels": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"location": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"managed_folders": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-zA-Z0-9][a-zA-Z0-9_/-]+$": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"force_destroy": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"iam": {
|
||||||
|
"$ref": "#/$defs/iam"
|
||||||
|
},
|
||||||
|
"iam_bindings": {
|
||||||
|
"$ref": "#/$defs/iam_bindings"
|
||||||
|
},
|
||||||
|
"iam_bindings_additive": {
|
||||||
|
"$ref": "#/$defs/iam_bindings_additive"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"prefix": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"storage_class": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"uniform_bucket_level_access": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"versioning": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"iam": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"patternProperties": {
|
||||||
|
"^(?:roles/|\\$custom_roles:)": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^(?:domain:|group:|serviceAccount:|user:|principal:|principalSet:|\\$iam_principals:)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"iam_bindings": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-z0-9_-]+$": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"members": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^(?:domain:|group:|serviceAccount:|user:|principal:|principalSet:|\\$iam_principals:)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"role": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^(?:roles/|\\$custom_roles:)"
|
||||||
|
},
|
||||||
|
"condition": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"expression",
|
||||||
|
"title"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"expression": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"iam_bindings_additive": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-z0-9_-]+$": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"member": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^(?:domain:|group:|serviceAccount:|user:|principal:|principalSet:|\\$iam_principals:)"
|
||||||
|
},
|
||||||
|
"role": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^(?:roles/|\\$custom_roles:)"
|
||||||
|
},
|
||||||
|
"condition": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"expression",
|
||||||
|
"title"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"expression": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"iam_by_principals": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"patternProperties": {
|
||||||
|
"^(?:domain:|group:|serviceAccount:|user:|principal:|principalSet:|\\$iam_principals:)": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^(?:roles/|\\$custom_roles:)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"iam_billing_roles": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-z0-9-]+$": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"iam_folder_roles": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-z0-9-]+$": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"iam_organization_roles": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-z0-9-]+$": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"iam_project_roles": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-z0-9-]+$": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"iam_sa_roles": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-z0-9-]+$": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"iam_storage_roles": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-z0-9-]+$": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pam_entitlements": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-z][a-z0-9-]{0,61}[a-z0-9]$": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"max_request_duration": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"eligible_users": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"privileged_access": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"role": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"condition": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"role"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"requester_justification_config": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"not_mandatory": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"unstructured": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"manual_approvals": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"require_approver_justification": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"steps": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"approvers": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"approvals_needed": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"approver_email_recipients": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"approvers"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"require_approver_justification",
|
||||||
|
"steps"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"additional_notification_targets": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"admin_email_recipients": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"requester_email_recipients": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"max_request_duration",
|
||||||
|
"eligible_users",
|
||||||
|
"privileged_access"
|
||||||
|
],
|
||||||
|
"additionalProperties": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
69
fast/stages/2-networking/schemas/ncc-hub.schema.json
Normal file
69
fast/stages/2-networking/schemas/ncc-hub.schema.json
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"title": "NCC Hub Configuration",
|
||||||
|
"description": "Schema for an NCC Hub YAML configuration file.",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"name",
|
||||||
|
"project_id"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The name of the NCC hub."
|
||||||
|
},
|
||||||
|
"project_id": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The project ID where the hub will be created."
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "An optional description for the hub."
|
||||||
|
},
|
||||||
|
"export_psc": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether to export PSC routes to other spokes."
|
||||||
|
},
|
||||||
|
"preset_topology": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The preset topology for the hub."
|
||||||
|
},
|
||||||
|
"groups": {
|
||||||
|
"$ref": "#/$defs/groups"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"$defs": {
|
||||||
|
"groups": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "A map of NCC groups to be created in the hub.",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-zA-Z0-9_-]+$": {
|
||||||
|
"$ref": "#/$defs/group"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"group": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"description": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "An optional description for the group."
|
||||||
|
},
|
||||||
|
"labels": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "Labels to apply to the group."
|
||||||
|
},
|
||||||
|
"auto_accept": {
|
||||||
|
"type": "array",
|
||||||
|
"description": "A list of project IDs to auto-accept for group membership.",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1008
fast/stages/2-networking/schemas/project.schema.json
Normal file
1008
fast/stages/2-networking/schemas/project.schema.json
Normal file
File diff suppressed because it is too large
Load Diff
231
fast/stages/2-networking/schemas/subnet.schema.json
Normal file
231
fast/stages/2-networking/schemas/subnet.schema.json
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"title": "Subnet",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"region"
|
||||||
|
],
|
||||||
|
"anyOf": [
|
||||||
|
{"required": ["ip_cidr_range"]},
|
||||||
|
{"required": ["reserved_internal_range"]},
|
||||||
|
{"required": ["ip_collection"]},
|
||||||
|
{
|
||||||
|
"allOf": [
|
||||||
|
{"not": {"required": ["ip_cidr_range"]}},
|
||||||
|
{"not": {"required": ["reserved_internal_range"]}},
|
||||||
|
{"not": {"required": ["ip_collection"]}},
|
||||||
|
{"properties": {"ipv6": {"properties": {"ipv6_only": {"const": true}}}}, "required": ["ipv6"]}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"active": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"enable_private_access": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"allow_subnet_cidr_routes_overlap": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"flow_logs_config": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"aggregation_interval": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"filter_expression": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"flow_sampling": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"metadata_fields": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"global": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"ip_cidr_range": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"reserved_internal_range": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Name of the internal range to use for this subnet. Mutually exclusive with ip_cidr_range and ip_collection."
|
||||||
|
},
|
||||||
|
"ipv6": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"access_type": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"ipv6_only": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ip_collection": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"region": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"psc": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"proxy_only": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"secondary_ip_ranges": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": {
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "IP CIDR range for backward compatibility"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"anyOf": [
|
||||||
|
{"required": ["ip_cidr_range"]},
|
||||||
|
{"required": ["reserved_internal_range"]}
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"ip_cidr_range": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "IP CIDR range for this secondary range"
|
||||||
|
},
|
||||||
|
"reserved_internal_range": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "Name of the internal range to use for this secondary range"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"iam": {
|
||||||
|
"$ref": "#/$defs/iam"
|
||||||
|
},
|
||||||
|
"iam_bindings": {
|
||||||
|
"$ref": "#/$defs/iam_bindings"
|
||||||
|
},
|
||||||
|
"iam_bindings_additive": {
|
||||||
|
"$ref": "#/$defs/iam_bindings_additive"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"$defs": {
|
||||||
|
"iam": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"patternProperties": {
|
||||||
|
"^roles/": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^(?:domain:|group:|serviceAccount:|user:|principal:|principalSet:|ro|rw)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"iam_bindings": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-z0-9_-]+$": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"members": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^(?:domain:|group:|serviceAccount:|user:|principal:|principalSet:|ro|rw)"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"role": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^roles/"
|
||||||
|
},
|
||||||
|
"condition": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"expression",
|
||||||
|
"title"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"expression": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"iam_bindings_additive": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-z0-9_-]+$": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {
|
||||||
|
"member": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^(?:domain:|group:|serviceAccount:|user:|principal:|principalSet:|ro|rw)"
|
||||||
|
},
|
||||||
|
"role": {
|
||||||
|
"type": "string",
|
||||||
|
"pattern": "^roles/"
|
||||||
|
},
|
||||||
|
"condition": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"expression",
|
||||||
|
"title"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"expression": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"title": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
427
fast/stages/2-networking/schemas/vpc.schema.json
Normal file
427
fast/stages/2-networking/schemas/vpc.schema.json
Normal file
@@ -0,0 +1,427 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"title": "VPC Configuration",
|
||||||
|
"description": "Schema for a VPC .config.yaml file.",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"name",
|
||||||
|
"project_id"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"project_id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"auto_create_subnetworks": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"delete_default_routes_on_create": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"mtu": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"routing_mode": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"GLOBAL",
|
||||||
|
"REGIONAL"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"firewall_policy_enforcement_order": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"BEFORE_CLASSIC_FIREWALL",
|
||||||
|
"AFTER_CLASSIC_FIREWALL"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"create_googleapis_routes": {
|
||||||
|
"$ref": "#/$defs/create_googleapis_routes"
|
||||||
|
},
|
||||||
|
"dns_policy": {
|
||||||
|
"$ref": "#/$defs/dns_policy"
|
||||||
|
},
|
||||||
|
"ipv6_config": {
|
||||||
|
"$ref": "#/$defs/ipv6_config"
|
||||||
|
},
|
||||||
|
"network_attachments": {
|
||||||
|
"$ref": "#/$defs/network_attachments"
|
||||||
|
},
|
||||||
|
"policy_based_routes": {
|
||||||
|
"$ref": "#/$defs/policy_based_routes"
|
||||||
|
},
|
||||||
|
"routes": {
|
||||||
|
"$ref": "#/$defs/routes"
|
||||||
|
},
|
||||||
|
"routers": {
|
||||||
|
"$ref": "#/$defs/routers"
|
||||||
|
},
|
||||||
|
"peering_config": {
|
||||||
|
"$ref": "#/$defs/peering_config"
|
||||||
|
},
|
||||||
|
"psa_configs": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/psa_config"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"subnets": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/subnet"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"subnets_private_nat": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/simple_subnet"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"subnets_proxy_only": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/proxy_only_subnet"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"subnets_psc": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/$defs/simple_subnet"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nat_config": {
|
||||||
|
"$ref": "#/$defs/nat_config"
|
||||||
|
},
|
||||||
|
"ncc_config": {
|
||||||
|
"$ref": "#/$defs/ncc_config"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"$defs": {
|
||||||
|
"create_googleapis_routes": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"directpath": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"directpath-6": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"private": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"private-6": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"restricted": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"restricted-6": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"dns_policy": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"inbound": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"logging": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"outbound": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"private_ns": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"public_ns": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ipv6_config": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"enable_ula_internal": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"internal_range": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nat_config": {
|
||||||
|
"type": "object",
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-z0-9-]+$": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"region"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"region": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ncc_config": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"hub"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"hub": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"group": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"network_attachments": {
|
||||||
|
"type": "object",
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-z0-9-]+$": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"subnet": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"automatic_connection": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"producer_accept_lists": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"producer_reject_lists": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"peering_config": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"peer_vpc_self_link": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"create_remote_peer": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"export_routes": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"import_routes": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"policy_based_routes": {
|
||||||
|
"type": "object",
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-z0-9-]+$": {
|
||||||
|
"type": "object"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"psa_config": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"deletion_policy": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"ranges": {
|
||||||
|
"type": "object",
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-z0-9-]+$": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"export_routes": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"import_routes": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"peered_domains": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"range_prefix": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"service_producer": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"routes": {
|
||||||
|
"type": "object",
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-z0-9-]+$": {
|
||||||
|
"type": "object",
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"routers": {
|
||||||
|
"type": "object",
|
||||||
|
"description": "A map of Cloud Routers to create in this VPC.",
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-z0-9-]+$": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"region",
|
||||||
|
"asn"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"region": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"asn": {
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"simple_subnet": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"name",
|
||||||
|
"ip_cidr_range",
|
||||||
|
"region"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"ip_cidr_range": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"region": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"subnet": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"name",
|
||||||
|
"region"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"ip_cidr_range": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"region": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"enable_private_access": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"allow_subnet_cidr_routes_overlap": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"reserved_internal_range": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"proxy_only_subnet": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"name",
|
||||||
|
"ip_cidr_range",
|
||||||
|
"region"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"ip_cidr_range": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"region": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"active": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"global": {
|
||||||
|
"type": "boolean"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
164
fast/stages/2-networking/schemas/vpn.schema.json
Normal file
164
fast/stages/2-networking/schemas/vpn.schema.json
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
||||||
|
"title": "VPN Configuration",
|
||||||
|
"description": "Schema for a VPN YAML configuration file.",
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"required": [
|
||||||
|
"name"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"region": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"stack_type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"IPV4_ONLY",
|
||||||
|
"IPV4_IPV6"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"peer_gateways": {
|
||||||
|
"$ref": "#/$defs/peer_gateways"
|
||||||
|
},
|
||||||
|
"router_config": {
|
||||||
|
"$ref": "#/$defs/router_config"
|
||||||
|
},
|
||||||
|
"tunnels": {
|
||||||
|
"$ref": "#/$defs/tunnels"
|
||||||
|
},
|
||||||
|
"ncc_spoke_config": {
|
||||||
|
"$ref": "#/$defs/ncc_spoke_config"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"$defs": {
|
||||||
|
"peer_gateways": {
|
||||||
|
"type": "object",
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-z0-9-]+$": {
|
||||||
|
"$ref": "#/$defs/peer_gateway"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"peer_gateway": {
|
||||||
|
"type": "object",
|
||||||
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"required": [
|
||||||
|
"external"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"external": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"redundancy_type": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"interfaces": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"gcp": false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"required": [
|
||||||
|
"gcp"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"gcp": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"external": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"router_config": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"asn": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"create": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tunnels": {
|
||||||
|
"type": "object",
|
||||||
|
"patternProperties": {
|
||||||
|
"^[a-z0-9-]+$": {
|
||||||
|
"$ref": "#/$defs/tunnel"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tunnel": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"bgp_peer": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"address": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"asn": {
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bgp_session_range": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"peer_external_gateway_interface": {
|
||||||
|
"type": "number"
|
||||||
|
},
|
||||||
|
"shared_secret": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"vpn_gateway_interface": {
|
||||||
|
"type": "number"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ncc_spoke_config": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"hub": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"labels": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"exclude_export_ranges": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include_export_ranges": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"group": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
107
fast/stages/2-networking/variables-fast.tf
Normal file
107
fast/stages/2-networking/variables-fast.tf
Normal file
@@ -0,0 +1,107 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2024 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 "automation" {
|
||||||
|
# tfdoc:variable:source 0-org-setup
|
||||||
|
description = "Automation resources created by the bootstrap stage."
|
||||||
|
type = object({
|
||||||
|
outputs_bucket = string
|
||||||
|
})
|
||||||
|
nullable = false
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "billing_account" {
|
||||||
|
# tfdoc:variable:source 0-org-setup
|
||||||
|
description = "Billing account id."
|
||||||
|
type = object({
|
||||||
|
id = string
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "custom_roles" {
|
||||||
|
# tfdoc:variable:source 0-org-setup
|
||||||
|
description = "Custom roles defined at the org level, in key => id format."
|
||||||
|
type = map(string)
|
||||||
|
nullable = false
|
||||||
|
default = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "folder_ids" {
|
||||||
|
# tfdoc:variable:source 0-org-setup
|
||||||
|
description = "Folders created in the bootstrap stage."
|
||||||
|
type = map(string)
|
||||||
|
nullable = false
|
||||||
|
default = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "iam_principals" {
|
||||||
|
# tfdoc:variable:source 0-org-setup
|
||||||
|
description = "IAM-format principals."
|
||||||
|
type = map(string)
|
||||||
|
nullable = false
|
||||||
|
default = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "organization" {
|
||||||
|
# tfdoc:variable:source 0-org-setup
|
||||||
|
description = "Organization details."
|
||||||
|
type = object({
|
||||||
|
id = number
|
||||||
|
})
|
||||||
|
nullable = false
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "prefix" {
|
||||||
|
# tfdoc:variable:source 0-org-setup
|
||||||
|
description = "Prefix used for resources that need unique names. Use a maximum of 9 chars for organizations, and 11 chars for tenants."
|
||||||
|
type = string
|
||||||
|
validation {
|
||||||
|
condition = try(length(var.prefix), 0) < 12
|
||||||
|
error_message = "Use a maximum of 9 chars for organizations, and 11 chars for tenants."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "project_ids" {
|
||||||
|
# tfdoc:variable:source 0-org-setup
|
||||||
|
description = "Projects created in the bootstrap stage."
|
||||||
|
type = map(string)
|
||||||
|
nullable = false
|
||||||
|
default = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "service_accounts" {
|
||||||
|
# tfdoc:variable:source 0-org-setup
|
||||||
|
description = "Service accounts created in the bootstrap stage."
|
||||||
|
type = map(string)
|
||||||
|
nullable = false
|
||||||
|
default = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "tag_keys" {
|
||||||
|
# tfdoc:variable:source 0-org-setup
|
||||||
|
description = "FAST-managed resource manager tag keys."
|
||||||
|
type = map(string)
|
||||||
|
nullable = false
|
||||||
|
default = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "tag_values" {
|
||||||
|
# tfdoc:variable:source 0-org-setup
|
||||||
|
description = "FAST-managed resource manager tag values."
|
||||||
|
type = map(string)
|
||||||
|
nullable = false
|
||||||
|
default = {}
|
||||||
|
}
|
||||||
66
fast/stages/2-networking/variables.tf
Normal file
66
fast/stages/2-networking/variables.tf
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2025 Google LLC
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
variable "context" {
|
||||||
|
description = "Context-specific interpolations."
|
||||||
|
type = object({
|
||||||
|
custom_roles = optional(map(string), {})
|
||||||
|
folder_ids = optional(map(string), {})
|
||||||
|
iam_principals = optional(map(string), {})
|
||||||
|
locations = optional(map(string), {})
|
||||||
|
project_ids = optional(map(string), {})
|
||||||
|
tag_keys = optional(map(string), {})
|
||||||
|
tag_values = optional(map(string), {})
|
||||||
|
})
|
||||||
|
default = {}
|
||||||
|
nullable = false
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "factories_config" {
|
||||||
|
description = "Configuration for the resource factories or external data."
|
||||||
|
type = object({
|
||||||
|
defaults = optional(string, "datasets/hub-and-spokes-peerings/defaults.yaml")
|
||||||
|
dns = optional(string, "datasets/hub-and-spokes-peerings/dns/zones")
|
||||||
|
dns-response-policies = optional(string, "datasets/hub-and-spokes-peerings/dns/response-policies")
|
||||||
|
firewall-policies = optional(string, "datasets/hub-and-spokes-peerings/firewall-policies")
|
||||||
|
folders = optional(string, "datasets/hub-and-spokes-peerings/folders")
|
||||||
|
ncc-hubs = optional(string, "datasets/hub-and-spokes-peerings/ncc-hubs")
|
||||||
|
nvas = optional(string, "datasets/hub-and-spokes-peerings/nvas")
|
||||||
|
projects = optional(string, "datasets/hub-and-spokes-peerings/projects")
|
||||||
|
vpcs = optional(string, "datasets/hub-and-spokes-peerings/vpcs")
|
||||||
|
})
|
||||||
|
nullable = false
|
||||||
|
default = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "outputs_location" {
|
||||||
|
description = "Path where tfvars files for the following stages are written. Leave empty to disable."
|
||||||
|
type = string
|
||||||
|
default = null
|
||||||
|
}
|
||||||
|
|
||||||
|
variable "universe" {
|
||||||
|
# tfdoc:variable:source 0-org-setup
|
||||||
|
description = "GCP universe where to deploy projects. The prefix will be prepended to the project id."
|
||||||
|
type = object({
|
||||||
|
domain = string
|
||||||
|
prefix = string
|
||||||
|
forced_jit_service_identities = optional(list(string), [])
|
||||||
|
unavailable_services = optional(list(string), [])
|
||||||
|
unavailable_service_identities = optional(list(string), [])
|
||||||
|
})
|
||||||
|
default = null
|
||||||
|
}
|
||||||
@@ -39,7 +39,7 @@ To destroy a previous FAST deployment follow the instructions detailed in [clean
|
|||||||
- [Security](2-security/README.md)
|
- [Security](2-security/README.md)
|
||||||
Manages centralized security configurations in a separate stage, and is typically owned by the security team. This stage implements VPC Security Controls via separate perimeters for environments and central services, and creates projects to host centralized KMS keys used by the whole organization. It's meant to be easily extended to include other security-related resources which are required, like Secret Manager.\
|
Manages centralized security configurations in a separate stage, and is typically owned by the security team. This stage implements VPC Security Controls via separate perimeters for environments and central services, and creates projects to host centralized KMS keys used by the whole organization. It's meant to be easily extended to include other security-related resources which are required, like Secret Manager.\
|
||||||
Exports: KMS key ids, CA ids
|
Exports: KMS key ids, CA ids
|
||||||
- Networking ([Peering/VPN](2-networking-legacy-a-simple/README.md)/[NVA (w/ optional BGP support)](2-networking-legacy-b-nva/README.md)/[Separate environments](2-networking-legacy-c-separate-envs/README.md))
|
- Networking ([Networking factory](2-networking/README.md))/Networking ([Peering/VPN](2-networking-legacy-a-simple/README.md)/[NVA (w/ optional BGP support)](2-networking-legacy-b-nva/README.md)/[Separate environments](2-networking-legacy-c-separate-envs/README.md))
|
||||||
Manages centralized network resources in a separate stage, and is typically owned by the networking team. This stage implements a hub-and-spoke design, and includes connectivity via VPN to on-premises, and YAML-based factories for firewall rules (hierarchical and VPC-level) and subnets. It's currently available in four flavors: [spokes connected via VPC peering/VPN](2-networking-legacy-a-simple/README.md), [spokes connected via appliances (w/ optional BGP support)](2-networking-legacy-b-nva/README.md) and [separated network environments](2-networking-legacy-c-separate-envs/README.md).\
|
Manages centralized network resources in a separate stage, and is typically owned by the networking team. This stage implements a hub-and-spoke design, and includes connectivity via VPN to on-premises, and YAML-based factories for firewall rules (hierarchical and VPC-level) and subnets. It's currently available in four flavors: [spokes connected via VPC peering/VPN](2-networking-legacy-a-simple/README.md), [spokes connected via appliances (w/ optional BGP support)](2-networking-legacy-b-nva/README.md) and [separated network environments](2-networking-legacy-c-separate-envs/README.md).\
|
||||||
Exports: host project ids and numbers, vpc self links
|
Exports: host project ids and numbers, vpc self links
|
||||||
- [Project Factory](./2-project-factory/)
|
- [Project Factory](./2-project-factory/)
|
||||||
|
|||||||
38
tests/fast/stages/s2_networking/peerings.tfvars
Normal file
38
tests/fast/stages/s2_networking/peerings.tfvars
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
automation = {
|
||||||
|
outputs_bucket = "test"
|
||||||
|
}
|
||||||
|
billing_account = {
|
||||||
|
id = "000000-111111-222222"
|
||||||
|
}
|
||||||
|
factories_config = {
|
||||||
|
defaults = "datasets/hub-and-spokes-peerings/defaults.yaml"
|
||||||
|
dns = "datasets/hub-and-spokes-peerings/dns/zones"
|
||||||
|
dns-response-policies = "datasets/hub-and-spokes-peerings/dns/response-policies"
|
||||||
|
firewall-policies = "datasets/hub-and-spokes-peerings/firewall-policies"
|
||||||
|
folders = "datasets/hub-and-spokes-peerings/folders"
|
||||||
|
interconnect = "datasets/hub-and-spokes-peerings/interconnect"
|
||||||
|
ncc-hubs = "datasets/hub-and-spokes-peerings/ncc-hubs"
|
||||||
|
nvas = "datasets/hub-and-spokes-peerings/nvas"
|
||||||
|
projects = "datasets/hub-and-spokes-peerings/projects"
|
||||||
|
vpcs = "datasets/hub-and-spokes-peerings/vpcs"
|
||||||
|
}
|
||||||
|
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
tag_values = {
|
||||||
|
"environment/development" = "tagValues/12345"
|
||||||
|
"environment/production" = "tagValues/12346"
|
||||||
|
}
|
||||||
2058
tests/fast/stages/s2_networking/peerings.yaml
Normal file
2058
tests/fast/stages/s2_networking/peerings.yaml
Normal file
File diff suppressed because it is too large
Load Diff
18
tests/fast/stages/s2_networking/tftest.yaml
Normal file
18
tests/fast/stages/s2_networking/tftest.yaml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
# 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: fast/stages/2-networking
|
||||||
|
|
||||||
|
tests:
|
||||||
|
peerings:
|
||||||
@@ -84,6 +84,7 @@ duplicates = [
|
|||||||
"modules/net-firewall-policy/schemas/firewall-policy-rules.schema.json",
|
"modules/net-firewall-policy/schemas/firewall-policy-rules.schema.json",
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
|
"fast/stages/2-networking/schemas/firewall-rules.schema.json",
|
||||||
"fast/stages/2-networking-legacy-a-simple/schemas/firewall-rules.schema.json",
|
"fast/stages/2-networking-legacy-a-simple/schemas/firewall-rules.schema.json",
|
||||||
"fast/stages/2-networking-legacy-c-separate-envs/schemas/firewall-rules.schema.json",
|
"fast/stages/2-networking-legacy-c-separate-envs/schemas/firewall-rules.schema.json",
|
||||||
"fast/stages/2-networking-legacy-b-nva/schemas/firewall-rules.schema.json",
|
"fast/stages/2-networking-legacy-b-nva/schemas/firewall-rules.schema.json",
|
||||||
@@ -91,6 +92,7 @@ duplicates = [
|
|||||||
],
|
],
|
||||||
[
|
[
|
||||||
"fast/stages/2-project-factory/schemas/folder.schema.json",
|
"fast/stages/2-project-factory/schemas/folder.schema.json",
|
||||||
|
"fast/stages/2-networking/schemas/folder.schema.json",
|
||||||
"fast/stages/0-org-setup/schemas/folder.schema.json",
|
"fast/stages/0-org-setup/schemas/folder.schema.json",
|
||||||
"modules/project-factory/schemas/folder.schema.json",
|
"modules/project-factory/schemas/folder.schema.json",
|
||||||
],
|
],
|
||||||
@@ -114,6 +116,7 @@ duplicates = [
|
|||||||
"modules/vpc-sc/schemas/perimeter.schema.json",
|
"modules/vpc-sc/schemas/perimeter.schema.json",
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
|
"fast/stages/2-networking/schemas/project.schema.json",
|
||||||
"fast/stages/2-project-factory/schemas/project.schema.json",
|
"fast/stages/2-project-factory/schemas/project.schema.json",
|
||||||
"fast/stages/0-org-setup/schemas/project.schema.json",
|
"fast/stages/0-org-setup/schemas/project.schema.json",
|
||||||
"fast/stages/2-security/schemas/project.schema.json",
|
"fast/stages/2-security/schemas/project.schema.json",
|
||||||
@@ -125,6 +128,7 @@ duplicates = [
|
|||||||
"modules/organization/schemas/scc-sha-custom-modules.schema.json",
|
"modules/organization/schemas/scc-sha-custom-modules.schema.json",
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
|
"fast/stages/2-networking/schemas/subnet.schema.json",
|
||||||
"fast/stages/2-networking-legacy-a-simple/schemas/subnet.schema.json",
|
"fast/stages/2-networking-legacy-a-simple/schemas/subnet.schema.json",
|
||||||
"fast/stages/2-networking-legacy-c-separate-envs/schemas/subnet.schema.json",
|
"fast/stages/2-networking-legacy-c-separate-envs/schemas/subnet.schema.json",
|
||||||
"fast/stages/2-networking-legacy-b-nva/schemas/subnet.schema.json",
|
"fast/stages/2-networking-legacy-b-nva/schemas/subnet.schema.json",
|
||||||
|
|||||||
Reference in New Issue
Block a user