diff --git a/fast/stages/2-networking-b-nva/README.md b/fast/stages/2-networking-b-nva/README.md index 2df42668e..a3d244a86 100644 --- a/fast/stages/2-networking-b-nva/README.md +++ b/fast/stages/2-networking-b-nva/README.md @@ -10,11 +10,12 @@ It adopts the common “hub and spoke” reference design, which is well suited - the "dmz" or "untrusted" VPC centralizes the external connectivity towards untrusted network resources, such as Internet (inbound and outbound) or 3P service providers or parties connected through VPN or Interconnect. - the "spoke" VPCs allow partitioning workloads (e.g. by environment like in this setup), while still retaining controlled access to central connectivity and services - Shared VPCs -both in hub and spokes- split the management of the network resources into specific (host) projects, while still allowing them to be consumed from the workload (service) projects +- if Regional VPC network mode is selected two additional regional trusted hub VPCs are deployed to provide connectivity to GCP services (eg. GCVE) that don't support multi-regional routing. - the design facilitates DNS centralization Connectivity between the hub and the spokes is established via [VPC network peerings](https://cloud.google.com/vpc/docs/vpc-peering), which offer uncapped bandwidth, lower latencies, at no additional costs and with a very low management overhead. Different ways of implementing connectivity, and related some pros and cons, are discussed below. -The diagram shows the high-level designs of the two proposed flavors ("Simple NVA" and "NCC-RA") and it should be used as a reference throughout the following sections. +The diagram shows the high-level designs of the three proposed network options ("Simple NVA", "NCC-RA" and "Regional VPC") and it should be used as a reference throughout the following sections. The final number of subnets, and their IP addressing will depend on the user-specific requirements. It can be easily changed via variables or external data files, without any need to edit the code. @@ -27,21 +28,26 @@ The final number of subnets, and their IP addressing will depend on the user-spe NCC-RA diagram
NCC-RA diagram

- +
+

+ GCVE diagram +
Regional VPC diagram +

## Table of contents -- [Table of contents](#table-of-contents) - [Design overview and choices](#design-overview-and-choices) - - [Multi-regional deployment](#multi-regional-deployment) + - [Deployment models](#deployment-models) - [VPC design](#vpc-design) - [Simple NVA](#simple-nva) - [NCC-RA](#ncc-ra) + - [Regional-VPC NVA](#regional-vpc-nva) - [External connectivity](#external-connectivity) - [Internal connectivity](#internal-connectivity) - [IP ranges, subnetting, routing](#ip-ranges-subnetting-routing) - [Simple NVA](#simple-nva) - [NCC-RA](#ncc-ra) + - [Regional-VPC NVA](#regional-vpc-nva) - [Internet egress](#internet-egress) - [VPC and Hierarchical Firewall](#vpc-and-hierarchical-firewall) - [DNS](#dns) @@ -75,34 +81,31 @@ The final number of subnets, and their IP addressing will depend on the user-spe ## Design overview and choices -### Multi-regional deployment +### Deployment models +This stage support three different deployment models that can be controlled by `var.network_mode`. The stage deploys networking resources in two different regions and supports both regional and multi-regional VPCs. Depending on the selected deployment model different routing strategies and NVAs failover modes can be implemented. -This stage deploys networking resources in two different regions, deployed and configured in order to allow for a manual ("simple" mode) or automated ("ncc-ra") failover in case of failures. -Two different architectural flavors are provided which, while similar, implement a completely different routing strategy: - -- **Simple NVA**, where the network appliances are configured behind a "ILB Sandwitch" (two different network passthrough internal load balancers on each of `dmz` and `landing` VPCs), with static routes sending traffic for specific destinations to specific network appliances group through the load balancer. -- **NCC-RA**, where the network appliances establish BGP sessions with a Cloud Router on both `dmz` and `landing` VPCs, which comes with the following benefits, at the cost of additional initial setup complexity: +- **Simple NVA**: This network mode deploys multi-regional VPCs, the network appliances are configured behind a "ILB Sandwitch" (two different network passthrough internal load balancers on each of `dmz` and `landing` VPCs), with static routes sending traffic for specific destinations to specific network appliances group through the load balancer. +- **NCC-RA**: This network mode deploys multi-regional VPCs as the simple mode but provides a different routing strategy. The network appliances establish BGP sessions with a Cloud Router on both `dmz` and `landing` VPCs, which comes with the following benefits, at the cost of additional initial setup complexity: - avoid using network tags to route traffic - automatically send all traffic through the cross-regional NVAs if the ones in-region fail - avoid cross-regional traffic unless absolutely necessary for disaster recovery - -Switching between the two different models is controlled by `var.enable_ncc_ra`. +- **Regional VPC**: This network mode is based on the Simple NVA model but deploys two additional regional VPCs to support use cases where multi-regional (tag based, policy based) routing cannot be used. ### VPC design -The "landing zone" is divided into two VPC networks: +The "landing zone" is divided into two main networks area: -- the landing VPC: the connectivity hub towards other trusted networks -- the DMZ VPC: the connectivity hub towards any other untrusted network +- landing: a multi-regional VPC provides the connectivity hub towards other trusted networks. If the **regional network mode** is selected, two additional regional landing VPC provides connectivity to trusted services that don't support multi regional VPC routing (eg. GCVE). +- DMZ: a multi-regional VPC provides the connectivity hub towards any other untrusted network By default, the design assumes the following: -- on-premise networks (and related resources) are considered trusted. As such, the VPNs connecting with on-premises are terminated in GCP, in the landing VPC +- on-premise networks (and related resources) are considered trusted. As such, the VPNs connecting with on-premises are terminated in GCP, in the multi-region landing VPC - the public Internet is considered untrusted. As such [Cloud NAT](https://cloud.google.com/nat/docs/overview) is deployed in the dmz landing VPC only - cross-environment traffic and traffic from any dmz network to any landing network (and vice versa) pass through the NVAs. For demo purposes, the current NVA performs simple routing/natting only - any traffic from a landing network to an dmz network (e.g. Internet) is natted by the NVAs. Users can configure further exclusions -The landing VPC acts as a hub: it bridges internal resources with the outside world and it hosts the shared services consumed by the spoke VPCs, connected to the hub through VPC network peerings. Spokes are used to partition the environments. By default: +The landing network area acts as a hub: the multi-region landing VPC bridges internal resources with the outside world and it hosts the shared services consumed by the spoke VPCs, connected to the hub through VPC network peerings. Spokes are used to partition the environments. By default: - one spoke VPC hosts the development environment resources - one spoke VPC hosts the production environment resources @@ -110,6 +113,8 @@ The landing VPC acts as a hub: it bridges internal resources with the outside wo Each virtual network is a [shared VPC](https://cloud.google.com/vpc/docs/shared-vpc): shared VPCs are managed in dedicated *host projects* and shared with other *service projects* that consume the network resources. Shared VPC lets organization administrators delegate administrative responsibilities, such as creating and managing instances, to Service Project Admins while maintaining centralized control over network resources like subnets, routes, and firewalls. +When the **regional network mode** is selected, the stage deploys two additional landing VPCs each one with a regional scope. If required the regional VPCs can be exteded as shared VPC and cosumed by other service (spoke) projects. + Users can easily extend the design to host additional environments, or adopt different logical mappings for the spokes (for example, in order to create a new spoke for each company entity). Adding spokes is trivial and it does not increase the design complexity. The steps to add more spokes are provided in the following sections. In multi-organization scenarios, where production and non-production resources use different Cloud Identity and GCP organizations, the hub/landing VPC is usually part of the production organization. It establishes connections with the production spokes within the same organization, and with non-production spokes in a different organization. @@ -134,6 +139,11 @@ NVAs establish **extra BGP sessions with both cross-regional NVAs**. In this cas Following the majority of real-life deployments, **we assume appliances to be stateful and not able to synchronize sessions between multiple NVAs within the same regional cluster**. For this reason, within each regional cluster, NVAs announce the same routes with different MED costs (1 point of difference between the primary and the secondary). This will cause traffic to go deterministically through one applaiance at the time within each region. You can change this default behavior modifying the cost settings in the [NVAs BGP configuration file](./data/bgp-config.tftpl). +#### Regional-VPC NVA + +When the **regional network mode** is selected, the VPCs are connected with two sets of sample NVA machines, grouped in regional (multi-zone) [Managed Instance Groups (MIGs)](https://cloud.google.com/compute/docs/instance-groups). The appliances connects are multi-nic instances that connect the DMZ VPC with the landing VPCs and provides simple routing/natting functionalities. The appliances are suited for demo purposes only and they should be replaced with enterprise-grade solutions before moving to production. +The traffic destined to the VMs in each MIG is mediated through regional internal load balancers, both in the landing and in the dmz networks. + ### External connectivity External connectivity to on-prem is implemented leveraging [Cloud HA VPN](https://cloud.google.com/network-connectivity/docs/vpn/concepts/topologies) (two tunnels per region). This is what users normally deploy as a final solution, or to validate routing and to transfer data, while waiting for [interconnects](https://cloud.google.com/network-connectivity/docs/interconnect) to be provisioned. @@ -162,34 +172,36 @@ Minimizing the number of routes (and subnets) in the cloud environment is import This stage uses a dedicated /11 block (10.64.0.0/11), which should be sized to the own needs. The subnets created in each VPC derive from this range. -The /11 block is evenly split in eight, smaller /16 blocks, assigned to different areas of the GCP network: *landing untrusted europe-west1*, *landing untrusted europe-west4*, *landing trusted europe-west1*, *landing untrusted europe-west4*, *development europe-west1*, *development europe-west4*, *production europe-west1*, *production europe-west4*. +The /11 block is evenly split in eight, smaller /16 blocks, assigned to different areas of the GCP network: *landing untrusted europe-west1*, *landing untrusted europe-west4*, *landing trusted europe-west1*, *regional trusted landing europe-west1*, *landing untrusted europe-west4*, *development europe-west1*, *development europe-west4*, *production europe-west1*, *production europe-west4*, *regional trusted landing europe-west4*. The first /24 range in every area is allocated for a default subnet, which can be removed or modified as needed. The last three /24 ranges can be used for [PSA (Private Service Access)](https://cloud.google.com/vpc/docs/private-services-access)via the `psa_ranges` variable, or for [Internal Application Load Balancers (L7 LBs)](https://cloud.google.com/load-balancing/docs/l7-internal) subnets via the factory. This is a summary of the subnets allocated by default in this setup: -| name | description | CIDR | -| ------------------- | --------------------------------------- | -------------- | -| landing-default-ew1 | Trusted landing subnet - europe-west1 | 10.128.64.0/24 | -| landing-default-ew4 | Trusted landing subnet - europe-west4 | 10.128.96.0/24 | -| dmz-default-ew1 | Untrusted landing subnet - europe-west1 | 10.128.0.0/24 | -| dmz-default-ew4 | Untrusted landing subnet - europe-west4 | 10.128.32.0/24 | -| dev-default-ew1 | Dev spoke subnet - europe-west1 | 10.68.0.0/24 | -| dev-default-ew1 | Free (PSA) - europe-west1 | 10.68.253.0/24 | -| dev-default-ew1 | Free (PSA) - europe-west1 | 10.68.254.0/24 | -| dev-default-ew1 | Free (L7 ILB) - europe-west1 | 10.68.255.0/24 | -| dev-default-ew4 | Dev spoke subnet - europe-west4 | 10.84.0.0/24 | -| dev-default-ew4 | Free (PSA) - europe-west4 | 10.84.253.0/24 | -| dev-default-ew4 | Free (PSA) - europe-west4 | 10.84.254.0/24 | -| dev-default-ew4 | Free (L7 ILB) - europe-west4 | 10.84.255.0/24 | -| prod-default-ew1 | Prod spoke subnet - europe-west1 | 10.72.0.0/24 | -| prod-default-ew1 | Free (PSA) - europe-west1 | 10.72.253.0/24 | -| prod-default-ew1 | Free (PSA) - europe-west1 | 10.72.254.0/24 | -| prod-default-ew1 | Free (L7 ILB) - europe-west1 | 10.72.255.0/24 | -| prod-default-ew4 | Prod spoke subnet - europe-west4 | 10.88.0.0/24 | -| prod-default-ew4 | Free (PSA) - europe-west4 | 10.88.253.0/24 | -| prod-default-ew4 | Free (PSA) - europe-west4 | 10.88.254.0/24 | -| prod-default-ew4 | Free (L7 ILB) - europe-west4 | 10.88.255.0/24 | +| name | description | CIDR | +| ---------------------------- | ------------------------------------------------ | -------------- | +| landing-default-ew1 | Trusted landing subnet - europe-west1 | 10.64.0.0/24 | +| landing-default-ew4 | Trusted landing subnet - europe-west4 | 10.80.0.0/24 | +| regional-landing-default-ew1 | Regional trusted landing subnet - europe-west1 | 10.65.0.0/24 | +| regional-landing-default-ew4 | Regional trusted landing subnet - europe-west4 | 10.81.0.0/24 | +| dmz-default-ew1 | Untrusted landing subnet - europe-west1 | 10.64.128.0/24 | +| dmz-default-ew4 | Untrusted landing subnet - europe-west4 | 10.80.128.0/24 | +| dev-default-ew1 | Dev spoke subnet - europe-west1 | 10.68.0.0/24 | +| dev-default-ew1 | Free (PSA) - europe-west1 | 10.68.253.0/24 | +| dev-default-ew1 | Free (PSA) - europe-west1 | 10.68.254.0/24 | +| dev-default-ew1 | Free (L7 ILB) - europe-west1 | 10.68.255.0/24 | +| dev-default-ew4 | Dev spoke subnet - europe-west4 | 10.84.0.0/24 | +| dev-default-ew4 | Free (PSA) - europe-west4 | 10.84.253.0/24 | +| dev-default-ew4 | Free (PSA) - europe-west4 | 10.84.254.0/24 | +| dev-default-ew4 | Free (L7 ILB) - europe-west4 | 10.84.255.0/24 | +| prod-default-ew1 | Prod spoke subnet - europe-west1 | 10.72.0.0/24 | +| prod-default-ew1 | Free (PSA) - europe-west1 | 10.72.253.0/24 | +| prod-default-ew1 | Free (PSA) - europe-west1 | 10.72.254.0/24 | +| prod-default-ew1 | Free (L7 ILB) - europe-west1 | 10.72.255.0/24 | +| prod-default-ew4 | Prod spoke subnet - europe-west4 | 10.88.0.0/24 | +| prod-default-ew4 | Free (PSA) - europe-west4 | 10.88.253.0/24 | +| prod-default-ew4 | Free (PSA) - europe-west4 | 10.88.254.0/24 | +| prod-default-ew4 | Free (L7 ILB) - europe-west4 | 10.88.255.0/24 | These subnets can be advertised to on-premises as an aggregate /11 range (10.64.0.0/11). Refer to the `var.vpn_onprem_primary_config.router_config` and `var.vpn_onprem_secondary_config.router_config` variables to configure it. @@ -213,6 +225,13 @@ The Cloud Routers (connected to the VPN gateways in the landing VPC) are configu - on-premises is connected to the trusted landing VPC and it dynamically exchanges BGP routes with GCP (with the landing) using HA VPN - the NVAs exchange dynamic routes using BGP with Cloud Routers in the DMZ, Cloud Routers in the landing and cross-regional NVAs. This allows VMs in different environments and different regions to communicate. +#### Regional-VPC NVA + +- routes between multiple subnets within the same VPC are automatically exchanged by GCP +- if configured, traffic between regional VPCs is managed by the NVAs though the DMZ VPC +- services that need to be shared by regional VPC hosts can be deployed on the multi-region landing VPC. The NVAs route the traffic from the regional landing VPCs to multi-region VPC +- on-premises is connected to the multi-region landing VPC and consumed by regional landing VPCs as shared service. + ### Internet egress In this setup, Internet egress is realized through [Cloud NAT](https://cloud.google.com/nat/docs/overview), deployed in the dmz landing VPC. This allows instances in all other VPCs to reach the Internet, passing through the NVAs (being the public Internet considered dmz). Cloud NAT is disabled by default; enable it by setting the `enable_cloud_nat` variable @@ -502,8 +521,10 @@ DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS | [net-dev.tf](./net-dev.tf) | Dev spoke VPC and related resources. | net-vpc · net-vpc-firewall · net-vpc-peering · project | | | [net-landing.tf](./net-landing.tf) | Landing VPC and related resources. | net-cloudnat · net-vpc · net-vpc-firewall · project | | | [net-prod.tf](./net-prod.tf) | Production spoke VPC and related resources. | net-vpc · net-vpc-firewall · net-vpc-peering · project | | +| [net-regional-vpc.tf](./net-regional-vpc.tf) | None | net-vpc · net-vpc-firewall | | | [nva-bgp-ncc.tf](./nva-bgp-ncc.tf) | None | | google_network_connectivity_hub | | [nva-bgp.tf](./nva-bgp.tf) | None | | google_compute_address | +| [nva-regional-vpc.tf](./nva-regional-vpc.tf) | None | | | | [nva-simple.tf](./nva-simple.tf) | None | simple-nva | | | [outputs.tf](./outputs.tf) | Module outputs. | | google_storage_bucket_object · local_file | | [regions.tf](./regions.tf) | Compute short names for regions. | | | @@ -525,26 +546,26 @@ DNS configurations are centralised in the `dns-*.tf` files. Spokes delegate DNS | [create_test_instances](variables.tf#L42) | Enables the creation of test VMs in each VPC, useful to test and troubleshoot connectivity. | bool | | false | | | [dns](variables.tf#L48) | DNS configuration. | object({…}) | | {} | | | [enable_cloud_nat](variables.tf#L58) | Deploy Cloud NAT. | bool | | false | | -| [enable_ncc_ra](variables.tf#L65) | Deploy NCC Router Appliance to create a BGP session between core VPCs and the appliances. | bool | | false | | -| [essential_contacts](variables.tf#L72) | Email used for essential contacts, unset if null. | string | | null | | -| [factories_config](variables.tf#L78) | Configuration for network resource factories. | object({…}) | | {…} | | +| [essential_contacts](variables.tf#L65) | Email used for essential contacts, unset if null. | string | | null | | +| [factories_config](variables.tf#L71) | Configuration for network resource factories. | object({…}) | | {…} | | | [fast_features](variables-fast.tf#L38) | Selective control for top-level FAST features. | object({…}) | | {} | 0-0-bootstrap | -| [gcp_ranges](variables.tf#L99) | GCP address ranges in name => range format. | map(string) | | {…} | | -| [outputs_location](variables.tf#L114) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | -| [psa_ranges](variables.tf#L120) | IP ranges used for Private Service Access (e.g. CloudSQL). Ranges is in name => range format. | object({…}) | | {} | | -| [regions](variables.tf#L140) | Region definitions. | object({…}) | | {…} | | +| [gcp_ranges](variables.tf#L92) | GCP address ranges in name => range format. | map(string) | | {…} | | +| [network_mode](variables.tf#L109) | Selection of the network design to deploy. | string | | "simple" | | +| [outputs_location](variables.tf#L120) | Path where providers and tfvars files for the following stages are written. Leave empty to disable. | string | | null | | +| [psa_ranges](variables.tf#L126) | IP ranges used for Private Service Access (e.g. CloudSQL). Ranges is in name => range format. | object({…}) | | {} | | +| [regions](variables.tf#L146) | Region definitions. | object({…}) | | {…} | | | [service_accounts](variables-fast.tf#L78) | Automation service accounts in name => email format. | object({…}) | | null | 1-resman | -| [vpn_onprem_primary_config](variables.tf#L152) | VPN gateway configuration for onprem interconnection in the primary region. | object({…}) | | null | | -| [vpn_onprem_secondary_config](variables.tf#L195) | VPN gateway configuration for onprem interconnection in the secondary region. | object({…}) | | null | | +| [vpn_onprem_primary_config](variables.tf#L158) | VPN gateway configuration for onprem interconnection in the primary region. | object({…}) | | null | | +| [vpn_onprem_secondary_config](variables.tf#L201) | VPN gateway configuration for onprem interconnection in the secondary region. | object({…}) | | null | | ## Outputs | name | description | sensitive | consumers | |---|---|:---:|---| -| [host_project_ids](outputs.tf#L95) | Network project ids. | | | -| [host_project_numbers](outputs.tf#L100) | Network project numbers. | | | -| [ping_commands](outputs.tf#L105) | Ping commands for test instances to be run to check VPC reachability. | | | -| [shared_vpc_self_links](outputs.tf#L110) | Shared VPC host projects. | | | -| [tfvars](outputs.tf#L115) | Terraform variables file for the following stages. | ✓ | | -| [vpn_gateway_endpoints](outputs.tf#L121) | External IP Addresses for the GCP VPN gateways. | | | +| [host_project_ids](outputs.tf#L106) | Network project ids. | | | +| [host_project_numbers](outputs.tf#L111) | Network project numbers. | | | +| [ping_commands](outputs.tf#L116) | Ping commands for test instances to be run to check VPC reachability. | | | +| [shared_vpc_self_links](outputs.tf#L121) | Shared VPC host projects. | | | +| [tfvars](outputs.tf#L126) | Terraform variables file for the following stages. | ✓ | | +| [vpn_gateway_endpoints](outputs.tf#L132) | External IP Addresses for the GCP VPN gateways. | | | diff --git a/fast/stages/2-networking-b-nva/data/firewall-rules/regional-pri/default-ingress.yaml b/fast/stages/2-networking-b-nva/data/firewall-rules/regional-pri/default-ingress.yaml new file mode 100644 index 000000000..70610416b --- /dev/null +++ b/fast/stages/2-networking-b-nva/data/firewall-rules/regional-pri/default-ingress.yaml @@ -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-regional-pri-deny: + description: "Deny and log any unmatched ingress traffic." + deny: true + priority: 65535 + enable_logging: + include_metadata: false diff --git a/fast/stages/2-networking-b-nva/data/firewall-rules/regional-pri/rules.yaml b/fast/stages/2-networking-b-nva/data/firewall-rules/regional-pri/rules.yaml new file mode 100644 index 000000000..8f0e92d3c --- /dev/null +++ b/fast/stages/2-networking-b-nva/data/firewall-rules/regional-pri/rules.yaml @@ -0,0 +1,23 @@ +# skip boilerplate check +--- +# start of document (---) avoids errors if the file only contains comments + +# yaml-language-server: $schema=../../../schemas/firewall-rules.schema.json + +ingress: + allow-hc-nva-ssh-regional-pri: + description: "Allow traffic from Google healthchecks to NVA appliances" + source_ranges: + - healthchecks + rules: + - protocol: tcp + ports: + - 22 + allow-onprem-probes-regional-pri-example: + description: "Allow traffic from onprem probes" + source_ranges: + - onprem_probes + rules: + - protocol: tcp + ports: + - 12345 diff --git a/fast/stages/2-networking-b-nva/data/firewall-rules/regional-sec/default-ingress.yaml b/fast/stages/2-networking-b-nva/data/firewall-rules/regional-sec/default-ingress.yaml new file mode 100644 index 000000000..e5f934f8c --- /dev/null +++ b/fast/stages/2-networking-b-nva/data/firewall-rules/regional-sec/default-ingress.yaml @@ -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-regional-sec-deny: + description: "Deny and log any unmatched ingress traffic." + deny: true + priority: 65535 + enable_logging: + include_metadata: false diff --git a/fast/stages/2-networking-b-nva/data/firewall-rules/regional-sec/rules.yaml b/fast/stages/2-networking-b-nva/data/firewall-rules/regional-sec/rules.yaml new file mode 100644 index 000000000..8e1b19ea4 --- /dev/null +++ b/fast/stages/2-networking-b-nva/data/firewall-rules/regional-sec/rules.yaml @@ -0,0 +1,23 @@ +# skip boilerplate check +--- +# start of document (---) avoids errors if the file only contains comments + +# yaml-language-server: $schema=../../../schemas/firewall-rules.schema.json + +ingress: + allow-hc-nva-ssh-regional-sec: + description: "Allow traffic from Google healthchecks to NVA appliances" + source_ranges: + - healthchecks + rules: + - protocol: tcp + ports: + - 22 + allow-onprem-probes-regional-sec-example: + description: "Allow traffic from onprem probes" + source_ranges: + - onprem_probes + rules: + - protocol: tcp + ports: + - 12345 diff --git a/fast/stages/2-networking-b-nva/data/subnets/regional-pri/landing-default-pri.yaml b/fast/stages/2-networking-b-nva/data/subnets/regional-pri/landing-default-pri.yaml new file mode 100644 index 000000000..bd9a96924 --- /dev/null +++ b/fast/stages/2-networking-b-nva/data/subnets/regional-pri/landing-default-pri.yaml @@ -0,0 +1,8 @@ +# skip boilerplate check + +# yaml-language-server: $schema=../../../schemas/subnet.schema.json + +name: regional-default +region: primary +ip_cidr_range: 10.65.0.0/17 +description: Default primary-region subnet for regional VPC diff --git a/fast/stages/2-networking-b-nva/data/subnets/regional-sec/landing-default-sec.yaml b/fast/stages/2-networking-b-nva/data/subnets/regional-sec/landing-default-sec.yaml new file mode 100644 index 000000000..3a6aefa2f --- /dev/null +++ b/fast/stages/2-networking-b-nva/data/subnets/regional-sec/landing-default-sec.yaml @@ -0,0 +1,8 @@ +# skip boilerplate check + +# yaml-language-server: $schema=../../../schemas/subnet.schema.json + +name: regional-default +region: secondary +ip_cidr_range: 10.81.0.0/17 +description: Default secondary-region subnet for regional VPC diff --git a/fast/stages/2-networking-b-nva/diagram-regional.svg b/fast/stages/2-networking-b-nva/diagram-regional.svg new file mode 100644 index 000000000..64d50d705 --- /dev/null +++ b/fast/stages/2-networking-b-nva/diagram-regional.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/fast/stages/2-networking-b-nva/dns-landing.tf b/fast/stages/2-networking-b-nva/dns-landing.tf index e60f17649..f36965a2f 100644 --- a/fast/stages/2-networking-b-nva/dns-landing.tf +++ b/fast/stages/2-networking-b-nva/dns-landing.tf @@ -26,10 +26,17 @@ module "landing-dns-fwd-onprem-example" { zone_config = { domain = "onprem.example.com." forwarding = { - client_networks = [ - module.dmz-vpc.self_link, - module.landing-vpc.self_link - ] + client_networks = concat( + [ + module.dmz-vpc.self_link, + module.landing-vpc.self_link + ], + (var.network_mode == "regional_vpc") ? + [ + module.regional-primary-vpc[0].self_link, + module.regional-secondary-vpc[0].self_link + ] : [] + ) forwarders = { for ip in var.dns.resolvers : ip => null } } } @@ -43,10 +50,17 @@ module "landing-dns-fwd-onprem-rev-10" { zone_config = { domain = "10.in-addr.arpa." forwarding = { - client_networks = [ - module.dmz-vpc.self_link, - module.landing-vpc.self_link - ] + client_networks = concat( + [ + module.dmz-vpc.self_link, + module.landing-vpc.self_link + ], + (var.network_mode == "regional_vpc") ? + [ + module.regional-primary-vpc[0].self_link, + module.regional-secondary-vpc[0].self_link + ] : [] + ) forwarders = { for ip in var.dns.resolvers : ip => null } } } @@ -59,10 +73,17 @@ module "landing-dns-priv-gcp" { zone_config = { domain = "gcp.example.com." private = { - client_networks = [ - # module.dmz-vpc.self_link, - module.landing-vpc.self_link - ] + client_networks = concat( + [ + # module.dmz-vpc.self_link, + module.landing-vpc.self_link + ], + (var.network_mode == "regional_vpc") ? + [ + module.regional-primary-vpc[0].self_link, + module.regional-secondary-vpc[0].self_link + ] : [] + ) } } recordsets = { @@ -79,8 +100,15 @@ module "landing-dns-policy-googleapis" { factories_config = { rules = var.factories_config.dns_policy_rules_file } - networks = { - landing = module.landing-vpc.self_link - dmz = module.dmz-vpc.self_link - } + networks = merge( + { + landing = module.landing-vpc.self_link + dmz = module.dmz-vpc.self_link + }, + (var.network_mode == "regional_vpc") ? + { + regional-primary = module.regional-primary-vpc[0].self_link, + regional-secondary = module.regional-secondary-vpc[0].self_link + } : {} + ) } diff --git a/fast/stages/2-networking-b-nva/net-dev.tf b/fast/stages/2-networking-b-nva/net-dev.tf index 7e1b7e38d..858a4d25b 100644 --- a/fast/stages/2-networking-b-nva/net-dev.tf +++ b/fast/stages/2-networking-b-nva/net-dev.tf @@ -33,11 +33,6 @@ module "dev-spoke-project" { "stackdriver.googleapis.com", "vpcaccess.googleapis.com" ], - ( - var.fast_features.gcve - ? ["vmwareengine.googleapis.com"] - : [] - ) ) shared_vpc_host_config = { enabled = true @@ -90,34 +85,34 @@ module "dev-spoke-vpc" { private = true restricted = true } - routes = var.enable_ncc_ra ? null : { + routes = (var.network_mode == "ncc_ra") ? null : { nva-primary-to-primary = { dest_range = "0.0.0.0/0" priority = 1000 tags = [local.region_shortnames[var.regions.primary]] next_hop_type = "ilb" - next_hop = module.ilb-nva-landing["primary"].forwarding_rule_addresses[""] + next_hop = local.nva_load_balancers.primary } nva-secondary-to-secondary = { dest_range = "0.0.0.0/0" priority = 1000 tags = [local.region_shortnames[var.regions.secondary]] next_hop_type = "ilb" - next_hop = module.ilb-nva-landing["secondary"].forwarding_rule_addresses[""] + next_hop = local.nva_load_balancers.secondary } nva-primary-to-secondary = { dest_range = "0.0.0.0/0" priority = 1001 tags = [local.region_shortnames[var.regions.primary]] next_hop_type = "ilb" - next_hop = module.ilb-nva-landing["primary"].forwarding_rule_addresses[""] + next_hop = local.nva_load_balancers.primary } nva-secondary-to-primary = { dest_range = "0.0.0.0/0" priority = 1001 tags = [local.region_shortnames[var.regions.secondary]] next_hop_type = "ilb" - next_hop = module.ilb-nva-landing["secondary"].forwarding_rule_addresses[""] + next_hop = local.nva_load_balancers.secondary } } } diff --git a/fast/stages/2-networking-b-nva/net-landing.tf b/fast/stages/2-networking-b-nva/net-landing.tf index a5960efeb..095260ae5 100644 --- a/fast/stages/2-networking-b-nva/net-landing.tf +++ b/fast/stages/2-networking-b-nva/net-landing.tf @@ -30,9 +30,14 @@ module "landing-project" { "stackdriver.googleapis.com", ], ( - var.enable_ncc_ra + var.network_mode == "ncc_ra" ? ["networkconnectivity.googleapis.com"] : [] + ), + ( + var.fast_features.gcve + ? ["vmwareengine.googleapis.com"] + : [] ) ) shared_vpc_host_config = { @@ -57,14 +62,31 @@ module "dmz-vpc" { subnets_folder = "${var.factories_config.data_dir}/subnets/dmz" } delete_default_routes_on_create = true - routes = { - default = { - dest_range = "0.0.0.0/0" - next_hop = "default-internet-gateway" - next_hop_type = "gateway" - priority = 1000 - } - } + routes = merge( + { + default = { + dest_range = "0.0.0.0/0" + next_hop = "default-internet-gateway" + next_hop_type = "gateway" + priority = 1000 + } + }, + # Uncomment to enable cross regional VPC traffic + # (var.network_mode == "regional_vpc") ? { + # to-regional-vpc-primary = { + # dest_range = var.gcp_ranges.gcp_regional_vpc_primary + # priority = 1000 + # next_hop_type = "ilb" + # next_hop = module.ilb-regional-nva-dmz["primary"].forwarding_rule_addresses[""] + # } + # to-regional-vpc-secondary = { + # dest_range = var.gcp_ranges.gcp_regional_vpc_secondary + # priority = 1000 + # next_hop_type = "ilb" + # next_hop = module.ilb-regional-nva-dmz["secondary"].forwarding_rule_addresses[""] + # } + # } : {} + ) } module "dmz-firewall" { diff --git a/fast/stages/2-networking-b-nva/net-prod.tf b/fast/stages/2-networking-b-nva/net-prod.tf index 3d2447647..2c2d344af 100644 --- a/fast/stages/2-networking-b-nva/net-prod.tf +++ b/fast/stages/2-networking-b-nva/net-prod.tf @@ -15,6 +15,21 @@ */ # tfdoc:file:description Production spoke VPC and related resources. +locals { + _simple_nva_lb = { + primary = (var.network_mode == "simple" ? module.ilb-nva-landing["primary"].forwarding_rule_addresses[""] : null) + secondary = (var.network_mode == "simple" ? module.ilb-nva-landing["secondary"].forwarding_rule_addresses[""] : null) + } + _regional_nva_lb = { + primary = (var.network_mode == "regional_vpc" ? module.ilb-regional-nva-landing["primary"].forwarding_rule_addresses[""] : null) + secondary = (var.network_mode == "regional_vpc" ? module.ilb-regional-nva-landing["secondary"].forwarding_rule_addresses[""] : null) + } + # On the basis of the network modes slects the NVA internal load balacer as next hop for spoke VPC routing + nva_load_balancers = (var.network_mode == "ncc_ra") ? null : { + primary = (var.network_mode == "simple" ? local._simple_nva_lb.primary : local._regional_nva_lb.primary) + secondary = (var.network_mode == "simple" ? local._simple_nva_lb.secondary : local._regional_nva_lb.secondary) + } +} module "prod-spoke-project" { source = "../../../modules/project" @@ -32,12 +47,7 @@ module "prod-spoke-project" { "servicenetworking.googleapis.com", "stackdriver.googleapis.com", "vpcaccess.googleapis.com" - ], - ( - var.fast_features.gcve - ? ["vmwareengine.googleapis.com"] - : [] - ) + ] ) shared_vpc_host_config = { enabled = true @@ -89,34 +99,34 @@ module "prod-spoke-vpc" { private = true restricted = true } - routes = var.enable_ncc_ra ? null : { + routes = (var.network_mode == "ncc_ra") ? null : { nva-primary-to-primary = { dest_range = "0.0.0.0/0" priority = 1000 tags = [local.region_shortnames[var.regions.primary]] next_hop_type = "ilb" - next_hop = module.ilb-nva-landing["primary"].forwarding_rule_addresses[""] + next_hop = local.nva_load_balancers.primary } nva-secondary-to-secondary = { dest_range = "0.0.0.0/0" priority = 1000 tags = [local.region_shortnames[var.regions.secondary]] next_hop_type = "ilb" - next_hop = module.ilb-nva-landing["secondary"].forwarding_rule_addresses[""] + next_hop = local.nva_load_balancers.secondary } nva-primary-to-secondary = { dest_range = "0.0.0.0/0" priority = 1001 tags = [local.region_shortnames[var.regions.primary]] next_hop_type = "ilb" - next_hop = module.ilb-nva-landing["secondary"].forwarding_rule_addresses[""] + next_hop = local.nva_load_balancers.secondary } nva-secondary-to-primary = { dest_range = "0.0.0.0/0" priority = 1001 tags = [local.region_shortnames[var.regions.secondary]] next_hop_type = "ilb" - next_hop = module.ilb-nva-landing["primary"].forwarding_rule_addresses[""] + next_hop = local.nva_load_balancers.primary } } } diff --git a/fast/stages/2-networking-b-nva/net-regional-vpc.tf b/fast/stages/2-networking-b-nva/net-regional-vpc.tf new file mode 100644 index 000000000..72e225f7f --- /dev/null +++ b/fast/stages/2-networking-b-nva/net-regional-vpc.tf @@ -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. + */ + +# Regional Primary VPC + +module "regional-primary-vpc" { + count = (var.network_mode == "regional_vpc") ? 1 : 0 + source = "../../../modules/net-vpc" + project_id = module.landing-project.project_id + name = "prod-regional-primary-0" + delete_default_routes_on_create = true + mtu = 1500 + factories_config = { + context = { regions = var.regions } + subnets_folder = "${var.factories_config.data_dir}/subnets/regional-pri" + } + dns_policy = { + inbound = true + } + routes = { + default = { + dest_range = "0.0.0.0/0" + priority = 1000 + next_hop_type = "ilb" + next_hop = module.ilb-regional-nva-regional-vpc["primary"].forwarding_rule_addresses[""] + } + } + # Set explicit routes for googleapis in case the default route is deleted + create_googleapis_routes = { + private = true + restricted = true + } +} + +module "regional-primary-firewall" { + count = (var.network_mode == "regional_vpc") ? 1 : 0 + source = "../../../modules/net-vpc-firewall" + project_id = module.landing-project.project_id + network = module.regional-primary-vpc[0].name + default_rules_config = { + disabled = true + } + factories_config = { + cidr_tpl_file = "${var.factories_config.data_dir}/cidrs.yaml" + rules_folder = "${var.factories_config.data_dir}/firewall-rules/regional-pri" + } +} + +# Regional Secondary VPC + +module "regional-secondary-vpc" { + count = (var.network_mode == "regional_vpc") ? 1 : 0 + + source = "../../../modules/net-vpc" + project_id = module.landing-project.project_id + name = "prod-regional-secondary-0" + delete_default_routes_on_create = true + mtu = 1500 + factories_config = { + context = { regions = var.regions } + subnets_folder = "${var.factories_config.data_dir}/subnets/regional-sec" + } + dns_policy = { + inbound = true + } + routes = { + default = { + dest_range = "0.0.0.0/0" + priority = 1000 + next_hop_type = "ilb" + next_hop = module.ilb-regional-nva-regional-vpc["secondary"].forwarding_rule_addresses[""] + } + } + # Set explicit routes for googleapis in case the default route is deleted + create_googleapis_routes = { + private = true + restricted = true + } +} + +module "regional-secondary-firewall" { + count = (var.network_mode == "regional_vpc") ? 1 : 0 + + source = "../../../modules/net-vpc-firewall" + project_id = module.landing-project.project_id + network = module.regional-secondary-vpc[0].name + default_rules_config = { + disabled = true + } + factories_config = { + cidr_tpl_file = "${var.factories_config.data_dir}/cidrs.yaml" + rules_folder = "${var.factories_config.data_dir}/firewall-rules/regional-sec" + } +} diff --git a/fast/stages/2-networking-b-nva/nva-bgp-ncc.tf b/fast/stages/2-networking-b-nva/nva-bgp-ncc.tf index c34033e27..19e7c322f 100644 --- a/fast/stages/2-networking-b-nva/nva-bgp-ncc.tf +++ b/fast/stages/2-networking-b-nva/nva-bgp-ncc.tf @@ -24,21 +24,21 @@ locals { } resource "google_network_connectivity_hub" "hub_landing" { - count = var.enable_ncc_ra ? 1 : 0 + count = (var.network_mode == "ncc_ra") ? 1 : 0 name = "prod-hub-landing" description = "Prod hub landing (trusted)" project = module.landing-project.project_id } resource "google_network_connectivity_hub" "hub_dmz" { - count = var.enable_ncc_ra ? 1 : 0 + count = (var.network_mode == "ncc_ra") ? 1 : 0 name = "prod-hub-dmz" description = "Prod hub DMZ (untrusted)" project = module.landing-project.project_id } module "ncc-spokes-landing" { - for_each = var.enable_ncc_ra ? var.regions : {} + for_each = (var.network_mode == "ncc_ra") ? var.regions : {} source = "../../../modules/ncc-spoke-ra" name = "prod-spoke-landing-${local.region_shortnames[each.value]}" project_id = module.landing-project.project_id @@ -92,7 +92,7 @@ module "ncc-spokes-landing" { } module "ncc-spokes-dmz" { - for_each = var.enable_ncc_ra ? var.regions : {} + for_each = (var.network_mode == "ncc_ra") ? var.regions : {} source = "../../../modules/ncc-spoke-ra" name = "prod-spoke-dmz-${local.region_shortnames[each.value]}" project_id = module.landing-project.project_id diff --git a/fast/stages/2-networking-b-nva/nva-bgp.tf b/fast/stages/2-networking-b-nva/nva-bgp.tf index 4c30bc0f6..6d1f3801e 100644 --- a/fast/stages/2-networking-b-nva/nva-bgp.tf +++ b/fast/stages/2-networking-b-nva/nva-bgp.tf @@ -122,7 +122,7 @@ locals { } module "nva-bgp-cloud-config" { - for_each = var.enable_ncc_ra ? local.bgp_nva_configs : {} + for_each = (var.network_mode == "ncc_ra") ? local.bgp_nva_configs : {} source = "../../../modules/cloud-config-container/simple-nva" enable_health_checks = true network_interfaces = local.bgp_routing_config @@ -135,7 +135,7 @@ module "nva-bgp-cloud-config" { # TODO: use address module resource "google_compute_address" "nva_static_ip_landing" { - for_each = var.enable_ncc_ra ? local.bgp_nva_configs : {} + for_each = (var.network_mode == "ncc_ra") ? local.bgp_nva_configs : {} name = "nva-ip-landing-${each.value.shortname}-${each.value.zone}" project = module.landing-project.project_id subnetwork = module.landing-vpc.subnet_self_links["${each.value.region}/landing-default"] @@ -145,7 +145,7 @@ resource "google_compute_address" "nva_static_ip_landing" { } resource "google_compute_address" "nva_static_ip_dmz" { - for_each = var.enable_ncc_ra ? local.bgp_nva_configs : {} + for_each = (var.network_mode == "ncc_ra") ? local.bgp_nva_configs : {} name = "nva-ip-dmz-${each.value.shortname}-${each.value.zone}" project = module.landing-project.project_id subnetwork = module.dmz-vpc.subnet_self_links["${each.value.region}/dmz-default"] @@ -155,7 +155,7 @@ resource "google_compute_address" "nva_static_ip_dmz" { } module "nva-bgp" { - for_each = var.enable_ncc_ra ? local.bgp_nva_configs : {} + for_each = (var.network_mode == "ncc_ra") ? local.bgp_nva_configs : {} source = "../../../modules/compute-vm" project_id = module.landing-project.project_id name = "nva-${each.value.shortname}-${each.value.zone}" diff --git a/fast/stages/2-networking-b-nva/nva-regional-vpc.tf b/fast/stages/2-networking-b-nva/nva-regional-vpc.tf new file mode 100644 index 000000000..d711d1775 --- /dev/null +++ b/fast/stages/2-networking-b-nva/nva-regional-vpc.tf @@ -0,0 +1,263 @@ +/** + * Copyright 2022 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 { + # routing_config should be aligned to the NVA network interfaces - i.e. + # local.simple_routing_config[0] sets up the first interface, and so on. + regional_vpc_routing_config = { + dmz-pri = { + name = "dmz-pri" + enable_masquerading = true + routes = [ + var.gcp_ranges.gcp_dmz_primary, + var.gcp_ranges.gcp_dmz_secondary, + var.gcp_ranges.gcp_regional_vpc_secondary + ] + }, + dmz-sec = { + name = "dmz-sec" + enable_masquerading = true + routes = [ + var.gcp_ranges.gcp_dmz_primary, + var.gcp_ranges.gcp_dmz_secondary, + var.gcp_ranges.gcp_regional_vpc_primary + ] + }, + landing = { + name = "landing" + routes = [ + var.gcp_ranges.gcp_dev_primary, + var.gcp_ranges.gcp_dev_secondary, + var.gcp_ranges.gcp_landing_primary, + var.gcp_ranges.gcp_landing_secondary, + var.gcp_ranges.gcp_prod_primary, + var.gcp_ranges.gcp_prod_secondary, + ] + }, + regional-vpc-pri = { + name = "regional-vpc-pri" + routes = [ + var.gcp_ranges.gcp_regional_vpc_primary + ] + }, + regional-vpc-sec = { + name = "regional-vpc-sec" + routes = [ + var.gcp_ranges.gcp_regional_vpc_secondary + ] + } + } +} + +# NVA config +module "nva-regional-cloud-config" { + for_each = (var.network_mode == "regional_vpc") ? var.regions : {} + source = "../../../modules/cloud-config-container/simple-nva" + enable_health_checks = true + network_interfaces = concat( + [each.key == "primary" ? local.regional_vpc_routing_config.dmz-pri : local.regional_vpc_routing_config.dmz-sec], + [local.regional_vpc_routing_config.landing], + [each.key == "primary" ? local.regional_vpc_routing_config.regional-vpc-pri : local.regional_vpc_routing_config.regional-vpc-sec] + ) +} + + +module "nva-regional-template" { + for_each = (var.network_mode == "regional_vpc") ? var.regions : {} + source = "../../../modules/compute-vm" + project_id = module.landing-project.project_id + name = "nva-regional-template-${each.key}" + zone = "${each.value}-${local.nva_zones[0]}" + instance_type = "e2-standard-4" + tags = ["nva"] + create_template = true + can_ip_forward = true + network_interfaces = [ + { + network = module.dmz-vpc.self_link + subnetwork = try( + module.dmz-vpc.subnet_self_links["${each.value}/dmz-default"], null + ) + nat = false + addresses = null + }, + { + network = module.landing-vpc.self_link + subnetwork = try( + module.landing-vpc.subnet_self_links["${each.value}/landing-default"], null + ) + nat = false + addresses = null + }, + ((each.key == "primary") ? #Select the Right VPC con the basis of locality + { + network = module.regional-primary-vpc[0].self_link + subnetwork = try( + module.regional-primary-vpc[0].subnet_self_links["${each.value}/regional-default"], null + ) + nat = false + addresses = null + } + : + { + network = module.regional-secondary-vpc[0].self_link + subnetwork = try( + module.regional-secondary-vpc[0].subnet_self_links["${each.value}/regional-default"], null + ) + nat = false + addresses = null + }) + ] + boot_disk = { + initialize_params = { + image = "projects/cos-cloud/global/images/family/cos-stable" + } + } + options = { + allow_stopping_for_update = true + deletion_protection = false + spot = true + termination_action = "STOP" + } + metadata = { + user-data = module.nva-regional-cloud-config[each.key].cloud_config + } +} + +module "nva-regional-mig" { + for_each = (var.network_mode == "regional_vpc") ? local.nva_locality : {} + source = "../../../modules/compute-mig" + project_id = module.landing-project.project_id + location = "${each.value.region}-${each.value.zone}" + name = "nva-cos-${each.key}" + instance_template = module.nva-regional-template[each.value.name].template.self_link + target_size = 1 + auto_healing_policies = { + initial_delay_sec = 30 + } + health_check_config = { + enable_logging = true + tcp = { + port = 22 + } + } +} + +module "ilb-regional-nva-dmz" { + for_each = (var.network_mode == "regional_vpc") ? { + for k, v in var.regions : k => { + region = v + subnet = "${v}/dmz-default" + } + } : {} + source = "../../../modules/net-lb-int" + project_id = module.landing-project.project_id + region = each.value.region + name = "nva-dmz-${each.key}" + service_label = var.prefix + forwarding_rules_config = { + "" = { + global_access = true + } + } + vpc_config = { + network = module.dmz-vpc.self_link + subnetwork = try(module.dmz-vpc.subnet_self_links[each.value.subnet], null) + } + backends = [ + for k, v in module.nva-regional-mig : + { group = v.group_manager.instance_group } + if startswith(k, each.key) + ] + health_check_config = { + enable_logging = true + tcp = { + port = 22 + } + } +} + +module "ilb-regional-nva-landing" { + for_each = (var.network_mode == "regional_vpc") ? { + for k, v in var.regions : k => { + region = v + subnet = "${v}/landing-default" + } + } : {} + source = "../../../modules/net-lb-int" + project_id = module.landing-project.project_id + region = each.value.region + name = "nva-landing-${each.key}" + service_label = var.prefix + forwarding_rules_config = { + "" = { + global_access = true + } + } + vpc_config = { + network = module.landing-vpc.self_link + subnetwork = try(module.landing-vpc.subnet_self_links[each.value.subnet], null) + } + backends = [ + for k, v in module.nva-regional-mig : + { group = v.group_manager.instance_group } + if startswith(k, each.key) + ] + health_check_config = { + enable_logging = true + tcp = { + port = 22 + } + } +} + +module "ilb-regional-nva-regional-vpc" { + for_each = (var.network_mode == "regional_vpc") ? { + for k, v in var.regions : k => { + region = v + subnet = "${v}/regional-default" + } + } : {} + source = "../../../modules/net-lb-int" + project_id = module.landing-project.project_id + region = each.value.region + name = "nva-regional-${each.key}" + service_label = var.prefix + forwarding_rules_config = { + "" = { + global_access = true + } + } + vpc_config = (each.key == "primary") ? { + network = module.regional-primary-vpc[0].self_link + subnetwork = try(module.regional-primary-vpc[0].subnet_self_links[each.value.subnet], null) + } : { + network = module.regional-secondary-vpc[0].self_link + subnetwork = try(module.regional-secondary-vpc[0].subnet_self_links[each.value.subnet], null) + } + + backends = [ + for k, v in module.nva-regional-mig : + { group = v.group_manager.instance_group } + if startswith(k, each.key) + ] + health_check_config = { + enable_logging = true + tcp = { + port = 22 + } + } +} diff --git a/fast/stages/2-networking-b-nva/nva-simple.tf b/fast/stages/2-networking-b-nva/nva-simple.tf index ba4c5a690..7b5ff507b 100644 --- a/fast/stages/2-networking-b-nva/nva-simple.tf +++ b/fast/stages/2-networking-b-nva/nva-simple.tf @@ -50,14 +50,14 @@ locals { # NVA config module "nva-simple-cloud-config" { - count = var.enable_ncc_ra ? 0 : 1 + count = (var.network_mode == "simple") ? 1 : 0 source = "../../../modules/cloud-config-container/simple-nva" enable_health_checks = true network_interfaces = local.simple_routing_config } module "nva-simple-template" { - for_each = var.enable_ncc_ra ? {} : local.nva_locality + for_each = (var.network_mode == "simple") ? local.nva_locality : {} source = "../../../modules/compute-vm" project_id = module.landing-project.project_id name = "nva-simple-template-${each.key}" @@ -101,10 +101,10 @@ module "nva-simple-template" { } module "nva-simple-mig" { - for_each = var.enable_ncc_ra ? {} : local.nva_locality + for_each = (var.network_mode == "simple") ? local.nva_locality : {} source = "../../../modules/compute-mig" project_id = module.landing-project.project_id - location = each.value.region + location = each.value.zone name = "nva-cos-${each.key}" instance_template = module.nva-simple-template[each.key].template.self_link target_size = 1 @@ -120,12 +120,12 @@ module "nva-simple-mig" { } module "ilb-nva-dmz" { - for_each = var.enable_ncc_ra ? {} : { + for_each = (var.network_mode == "simple") ? { for k, v in var.regions : k => { region = v subnet = "${v}/dmz-default" } - } + } : {} source = "../../../modules/net-lb-int" project_id = module.landing-project.project_id region = each.value.region @@ -154,12 +154,12 @@ module "ilb-nva-dmz" { } module "ilb-nva-landing" { - for_each = var.enable_ncc_ra ? {} : { + for_each = (var.network_mode == "simple") ? { for k, v in var.regions : k => { region = v subnet = "${v}/landing-default" } - } + } : {} source = "../../../modules/net-lb-int" project_id = module.landing-project.project_id region = each.value.region diff --git a/fast/stages/2-networking-b-nva/outputs.tf b/fast/stages/2-networking-b-nva/outputs.tf index e3c2be07c..cc16c4e84 100644 --- a/fast/stages/2-networking-b-nva/outputs.tf +++ b/fast/stages/2-networking-b-nva/outputs.tf @@ -25,12 +25,17 @@ locals { prod-landing = module.landing-project.number prod-spoke-0 = module.prod-spoke-project.number } - subnet_self_links = { + subnet_self_links = merge({ prod-dmz = module.dmz-vpc.subnet_self_links prod-landing = module.landing-vpc.subnet_self_links dev-spoke-0 = module.dev-spoke-vpc.subnet_self_links prod-spoke-0 = module.prod-spoke-vpc.subnet_self_links - } + }, + (var.network_mode == "regional_vpc") ? { + regional-vpc-primary-0 = module.regional-primary-vpc[0].subnet_self_links + regional-vpc-secondary-0 = module.regional-secondary-vpc[0].subnet_self_links + } : {} + ) subnet_proxy_only_self_links = { prod-dmz = { for k, v in module.dmz-vpc.subnets_proxy_only : k => v.id @@ -67,12 +72,18 @@ locals { subnet_psc_self_links = local.subnet_psc_self_links vpc_self_links = local.vpc_self_links } - vpc_self_links = { - prod-landing = module.landing-vpc.self_link - prod-dmz = module.dmz-vpc.self_link - dev-spoke-0 = module.dev-spoke-vpc.self_link - prod-spoke-0 = module.prod-spoke-vpc.self_link - } + vpc_self_links = merge( + { + prod-landing = module.landing-vpc.self_link + prod-dmz = module.dmz-vpc.self_link + dev-spoke-0 = module.dev-spoke-vpc.self_link + prod-spoke-0 = module.prod-spoke-vpc.self_link + }, + (var.network_mode == "regional_vpc") ? { + regional-vpc-primary-0 = module.regional-primary-vpc[0].self_link + regional-vpc-secondary-0 = module.regional-secondary-vpc[0].self_link + } : {} + ) } # generate tfvars file for subsequent stages diff --git a/fast/stages/2-networking-b-nva/test-resources.tf b/fast/stages/2-networking-b-nva/test-resources.tf index a07cb0eef..ff510b12a 100644 --- a/fast/stages/2-networking-b-nva/test-resources.tf +++ b/fast/stages/2-networking-b-nva/test-resources.tf @@ -17,72 +17,93 @@ # tfdoc:file:description Temporary instances for testing locals { - test-vms = { - dev-spoke-primary = { - network = module.dev-spoke-vpc.self_link - project_id = module.dev-spoke-project.project_id - region = var.regions.primary - subnetwork = module.dev-spoke-vpc.subnet_self_links["${var.regions.primary}/dev-default"] - tags = [local.region_shortnames[var.regions.primary]] - zone = "b" - } - dev-spoke-secondary = { - network = module.dev-spoke-vpc.self_link - project_id = module.dev-spoke-project.project_id - region = var.regions.secondary - subnetwork = module.dev-spoke-vpc.subnet_self_links["${var.regions.secondary}/dev-default"] - tags = [local.region_shortnames[var.regions.secondary]] - zone = "b" - } - dmz-primary = { - network = module.dmz-vpc.self_link - project_id = module.landing-project.project_id - region = var.regions.primary - subnetwork = module.dmz-vpc.subnet_self_links["${var.regions.primary}/dmz-default"] - tags = [local.region_shortnames[var.regions.primary]] - zone = "b" - } - dmz-secondary = { - network = module.dmz-vpc.self_link - project_id = module.landing-project.project_id - region = var.regions.secondary - subnetwork = module.dmz-vpc.subnet_self_links["${var.regions.secondary}/dmz-default"] - tags = [local.region_shortnames[var.regions.secondary]] - zone = "b" - } - landing-primary = { - network = module.landing-vpc.self_link - project_id = module.landing-project.project_id - region = var.regions.primary - subnetwork = module.landing-vpc.subnet_self_links["${var.regions.primary}/landing-default"] - tags = [local.region_shortnames[var.regions.primary]] - zone = "b" - } - landing-secondary = { - network = module.landing-vpc.self_link - project_id = module.landing-project.project_id - region = var.regions.secondary - subnetwork = module.landing-vpc.subnet_self_links["${var.regions.secondary}/landing-default"] - tags = [local.region_shortnames[var.regions.secondary]] - zone = "b" - } - prod-spoke-primary = { - network = module.prod-spoke-vpc.self_link - project_id = module.prod-spoke-project.project_id - region = var.regions.primary - subnetwork = module.prod-spoke-vpc.subnet_self_links["${var.regions.primary}/prod-default"] - tags = [local.region_shortnames[var.regions.primary]] - zone = "b" - } - prod-spoke-secondary = { - network = module.prod-spoke-vpc.self_link - project_id = module.prod-spoke-project.project_id - region = var.regions.secondary - subnetwork = module.prod-spoke-vpc.subnet_self_links["${var.regions.secondary}/prod-default"] - tags = [local.region_shortnames[var.regions.secondary]] - zone = "b" - } - } + test-vms = merge( + { + dev-spoke-primary = { + network = module.dev-spoke-vpc.self_link + project_id = module.dev-spoke-project.project_id + region = var.regions.primary + subnetwork = module.dev-spoke-vpc.subnet_self_links["${var.regions.primary}/dev-default"] + tags = [local.region_shortnames[var.regions.primary]] + zone = "b" + } + dev-spoke-secondary = { + network = module.dev-spoke-vpc.self_link + project_id = module.dev-spoke-project.project_id + region = var.regions.secondary + subnetwork = module.dev-spoke-vpc.subnet_self_links["${var.regions.secondary}/dev-default"] + tags = [local.region_shortnames[var.regions.secondary]] + zone = "b" + } + dmz-primary = { + network = module.dmz-vpc.self_link + project_id = module.landing-project.project_id + region = var.regions.primary + subnetwork = module.dmz-vpc.subnet_self_links["${var.regions.primary}/dmz-default"] + tags = [local.region_shortnames[var.regions.primary]] + zone = "b" + } + dmz-secondary = { + network = module.dmz-vpc.self_link + project_id = module.landing-project.project_id + region = var.regions.secondary + subnetwork = module.dmz-vpc.subnet_self_links["${var.regions.secondary}/dmz-default"] + tags = [local.region_shortnames[var.regions.secondary]] + zone = "b" + } + landing-primary = { + network = module.landing-vpc.self_link + project_id = module.landing-project.project_id + region = var.regions.primary + subnetwork = module.landing-vpc.subnet_self_links["${var.regions.primary}/landing-default"] + tags = [local.region_shortnames[var.regions.primary]] + zone = "b" + } + landing-secondary = { + network = module.landing-vpc.self_link + project_id = module.landing-project.project_id + region = var.regions.secondary + subnetwork = module.landing-vpc.subnet_self_links["${var.regions.secondary}/landing-default"] + tags = [local.region_shortnames[var.regions.secondary]] + zone = "b" + } + prod-spoke-primary = { + network = module.prod-spoke-vpc.self_link + project_id = module.prod-spoke-project.project_id + region = var.regions.primary + subnetwork = module.prod-spoke-vpc.subnet_self_links["${var.regions.primary}/prod-default"] + tags = [local.region_shortnames[var.regions.primary]] + zone = "b" + } + prod-spoke-secondary = { + network = module.prod-spoke-vpc.self_link + project_id = module.prod-spoke-project.project_id + region = var.regions.secondary + subnetwork = module.prod-spoke-vpc.subnet_self_links["${var.regions.secondary}/prod-default"] + tags = [local.region_shortnames[var.regions.secondary]] + zone = "b" + } + }, + (var.network_mode == "regional_vpc") ? + { + regional-vpc-primary = { + network = module.regional-primary-vpc[0].self_link + project_id = module.landing-project.project_id + region = var.regions.primary + subnetwork = module.regional-primary-vpc[0].subnet_self_links["${var.regions.primary}/regional-default"] + tags = [local.region_shortnames[var.regions.primary]] + zone = "b" + } + regional-vpc-secondary = { + network = module.regional-secondary-vpc[0].self_link + project_id = module.landing-project.project_id + region = var.regions.secondary + subnetwork = module.regional-secondary-vpc[0].subnet_self_links["${var.regions.secondary}/regional-default"] + tags = [local.region_shortnames[var.regions.secondary]] + zone = "b" + } + } : {} + ) } module "test-vms" { diff --git a/fast/stages/2-networking-b-nva/variables.tf b/fast/stages/2-networking-b-nva/variables.tf index c4e85f8dd..cc8e8065d 100644 --- a/fast/stages/2-networking-b-nva/variables.tf +++ b/fast/stages/2-networking-b-nva/variables.tf @@ -62,13 +62,6 @@ variable "enable_cloud_nat" { nullable = false } -variable "enable_ncc_ra" { - description = "Deploy NCC Router Appliance to create a BGP session between core VPCs and the appliances." - type = bool - default = false - nullable = false -} - variable "essential_contacts" { description = "Email used for essential contacts, unset if null." type = string @@ -100,14 +93,27 @@ variable "gcp_ranges" { description = "GCP address ranges in name => range format." type = map(string) default = { - gcp_dev_primary = "10.68.0.0/16" - gcp_dev_secondary = "10.84.0.0/16" - gcp_landing_primary = "10.64.0.0/17" - gcp_landing_secondary = "10.80.0.0/17" - gcp_dmz_primary = "10.64.128.0/17" - gcp_dmz_secondary = "10.80.128.0/17" - gcp_prod_primary = "10.72.0.0/16" - gcp_prod_secondary = "10.88.0.0/16" + gcp_dev_primary = "10.68.0.0/16" + gcp_dev_secondary = "10.84.0.0/16" + gcp_regional_vpc_primary = "10.65.0.0/17" + gcp_regional_vpc_secondary = "10.81.0.0/17" + gcp_landing_primary = "10.64.0.0/17" + gcp_landing_secondary = "10.80.0.0/17" + gcp_dmz_primary = "10.64.128.0/17" + gcp_dmz_secondary = "10.80.128.0/17" + gcp_prod_primary = "10.72.0.0/16" + gcp_prod_secondary = "10.88.0.0/16" + } +} + +variable "network_mode" { + description = "Selection of the network design to deploy." + type = string + default = "simple" + nullable = false + validation { + condition = contains(["simple", "ncc_ra", "regional_vpc"], var.network_mode) + error_message = "Network mode must be either \"simple\" or \"ncc_ra\" or \"regional_vpc\"." } } diff --git a/tests/fast/stages/s2_networking_b_nva/ncc-ra.tfvars b/tests/fast/stages/s2_networking_b_nva/ncc-ra.tfvars index 78af2e15c..8f6b1eeed 100644 --- a/tests/fast/stages/s2_networking_b_nva/ncc-ra.tfvars +++ b/tests/fast/stages/s2_networking_b_nva/ncc-ra.tfvars @@ -12,7 +12,6 @@ dns = { enable_logging = true } enable_cloud_nat = true -enable_ncc_ra = true enable_test_instances = true essential_contacts = "gcp-network-admins@fast.example.com" folder_ids = { @@ -23,6 +22,7 @@ folder_ids = { groups = { gcp-network-admins = "gcp-vpc-network-admins" } +network_mode = "ncc_ra" service_accounts = { data-platform-dev = "string" data-platform-prod = "string" diff --git a/tests/fast/stages/s2_networking_b_nva/regional.tfvars b/tests/fast/stages/s2_networking_b_nva/regional.tfvars new file mode 100644 index 000000000..36c04f78f --- /dev/null +++ b/tests/fast/stages/s2_networking_b_nva/regional.tfvars @@ -0,0 +1,118 @@ +automation = { + outputs_bucket = "test" +} +billing_account = { + id = "000000-111111-222222" +} +custom_roles = { + service_project_network_admin = "organizations/123456789012/roles/foo" +} +dns = { + resolvers = ["10.10.10.10"] + enable_logging = true +} +enable_cloud_nat = true +enable_test_instances = true +essential_contacts = "gcp-network-admins@fast.example.com" +folder_ids = { + networking = null + networking-dev = null + networking-prod = null +} +groups = { + gcp-network-admins = "gcp-vpc-network-admins" +} +network_mode = "regional_vpc" +service_accounts = { + data-platform-dev = "string" + data-platform-prod = "string" + gke-dev = "string" + gke-prod = "string" + project-factory = "string" + project-factory-dev = "string" + project-factory-prod = "string" +} +organization = { + domain = "fast.example.com" + id = 123456789012 + customer_id = "C00000000" +} +prefix = "fast2" +vpn_onprem_primary_config = { + peer_external_gateways = { + default = { + redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT" + interfaces = ["8.8.8.8"] + } + } + router_config = { + asn = 65501 + custom_advertise = { + all_subnets = false + ip_ranges = { + "10.1.0.0/16" = "gcp" + "35.199.192.0/19" = "gcp-dns" + "199.36.153.4/30" = "gcp-restricted" + } + } + } + tunnels = { + "0" = { + bgp_peer = { + address = "169.254.1.1" + asn = 65500 + } + bgp_session_range = "169.254.1.2/30" + shared_secret = "foo" + vpn_gateway_interface = 0 + } + "1" = { + bgp_peer = { + address = "169.254.2.1" + asn = 64513 + } + bgp_session_range = "169.254.2.2/30" + shared_secret = "foo" + vpn_gateway_interface = 1 + } + } +} +vpn_onprem_secondary_config = { + peer_external_gateways = { + default = { + redundancy_type = "SINGLE_IP_INTERNALLY_REDUNDANT" + interfaces = ["8.8.4.4"] + } + } + router_config = { + asn = 65501 + custom_advertise = { + all_subnets = false + ip_ranges = { + "10.1.0.0/16" = "gcp" + "35.199.192.0/19" = "gcp-dns" + "199.36.153.4/30" = "gcp-restricted" + } + } + } + tunnels = { + "0" = { + bgp_peer = { + address = "169.254.1.1" + asn = 65500 + } + bgp_session_range = "169.254.3.2/30" + shared_secret = "foo" + vpn_gateway_interface = 0 + } + "1" = { + bgp_peer = { + address = "169.254.2.1" + asn = 64513 + } + bgp_session_range = "169.254.4.2/30" + shared_secret = "foo" + vpn_gateway_interface = 1 + } + } +} diff --git a/tests/fast/stages/s2_networking_b_nva/regional.yaml b/tests/fast/stages/s2_networking_b_nva/regional.yaml new file mode 100644 index 000000000..8f9bcaad5 --- /dev/null +++ b/tests/fast/stages/s2_networking_b_nva/regional.yaml @@ -0,0 +1,55 @@ +# 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. + +counts: + google_compute_external_vpn_gateway: 2 + google_compute_firewall: 18 + google_compute_firewall_policy: 1 + google_compute_firewall_policy_association: 1 + google_compute_firewall_policy_rule: 4 + google_compute_forwarding_rule: 6 + google_compute_ha_vpn_gateway: 2 + google_compute_health_check: 10 + google_compute_instance_group_manager: 4 + google_compute_instance_template: 2 + google_compute_network: 6 + google_compute_network_peering: 4 + google_compute_region_backend_service: 6 + google_compute_route: 21 + google_compute_router: 4 + google_compute_router_interface: 4 + google_compute_router_nat: 2 + google_compute_router_peer: 4 + google_compute_shared_vpc_host_project: 3 + google_compute_subnetwork: 14 + google_compute_vpn_tunnel: 4 + google_dns_managed_zone: 9 + google_dns_policy: 6 + google_dns_record_set: 3 + google_dns_response_policy: 1 + google_dns_response_policy_rule: 38 + google_essential_contacts_contact: 1 + google_folder: 1 + google_monitoring_alert_policy: 2 + google_monitoring_dashboard: 3 + google_monitoring_monitored_project: 2 + google_project: 3 + google_project_iam_binding: 4 + google_project_iam_member: 17 + google_project_service: 23 + google_project_service_identity: 17 + google_storage_bucket_object: 2 + modules: 47 + random_id: 2 + resources: 258 diff --git a/tests/fast/stages/s2_networking_b_nva/simple.tfvars b/tests/fast/stages/s2_networking_b_nva/simple.tfvars index 8be668502..090774298 100644 --- a/tests/fast/stages/s2_networking_b_nva/simple.tfvars +++ b/tests/fast/stages/s2_networking_b_nva/simple.tfvars @@ -12,7 +12,6 @@ dns = { enable_logging = true } enable_cloud_nat = true -enable_ncc_ra = false enable_test_instances = true essential_contacts = "gcp-network-admins@fast.example.com" folder_ids = { @@ -20,6 +19,7 @@ folder_ids = { networking-dev = null networking-prod = null } +network_mode = "simple" groups = { gcp-network-admins = "gcp-vpc-network-admins" } diff --git a/tests/fast/stages/s2_networking_b_nva/simple.yaml b/tests/fast/stages/s2_networking_b_nva/simple.yaml index d88b77b4a..a6c90d566 100644 --- a/tests/fast/stages/s2_networking_b_nva/simple.yaml +++ b/tests/fast/stages/s2_networking_b_nva/simple.yaml @@ -21,11 +21,11 @@ counts: google_compute_forwarding_rule: 4 google_compute_ha_vpn_gateway: 2 google_compute_health_check: 8 + google_compute_instance_group_manager: 4 google_compute_instance_template: 4 google_compute_network: 4 google_compute_network_peering: 4 google_compute_region_backend_service: 4 - google_compute_region_instance_group_manager: 4 google_compute_route: 15 google_compute_router: 4 google_compute_router_interface: 4 diff --git a/tests/fast/stages/s2_networking_b_nva/tftest.yaml b/tests/fast/stages/s2_networking_b_nva/tftest.yaml index 1f01f3a91..a60ec3adb 100644 --- a/tests/fast/stages/s2_networking_b_nva/tftest.yaml +++ b/tests/fast/stages/s2_networking_b_nva/tftest.yaml @@ -21,3 +21,6 @@ tests: ncc-ra: extra_files: - ../../plugins/2-networking-serverless-connector/*.tf + regional: + extra_files: + - ../../plugins/2-networking-serverless-connector/*.tf \ No newline at end of file