Change hub and spoke VPN example to use VPN HA (#643)

* missing outputs and diagram

* fix names and outputs, update diagram

* fix vm name in example

* tfdoc files on

* fix test

* address comments
This commit is contained in:
Ludovico Magnocavallo 2022-05-02 09:01:05 +02:00 committed by GitHub
parent c2323fed41
commit 4114f9995f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 624 additions and 370 deletions

View File

@ -1,8 +1,15 @@
# Hub and Spoke via VPN
This example creates a simple **Hub and Spoke VPN** setup, where the VPC network connects satellite locations (spokes) through a single intermediary location (hub) via [IPsec VPN](https://cloud.google.com/vpn/docs/concepts/overview), optionally providing full-mesh networking via [custom route advertisements](https://cloud.google.com/router/docs/how-to/advertising-overview).
This example creates a simple **Hub and Spoke VPN** setup, where the VPC network connects satellite locations (spokes) through a single intermediary location (hub) via [IPsec HA VPN](https://cloud.google.com/network-connectivity/docs/vpn/concepts/overview#ha-vpn).
The example has been purposefully kept simple to show how to use and wire the VPC and VPN modules together, and so that it can be used as a basis for more complex scenarios. This is the high level diagram:
A few additional features are also shown:
- [custom BGP advertisements](https://cloud.google.com/router/docs/how-to/advertising-overview) to implement transitivity between spokes
- [VPC Global Routing](https://cloud.google.com/network-connectivity/docs/router/how-to/configuring-routing-mode) to leverage a regional set of VPN gateways in different regions as next hops (used here for illustrative/study purpose, not usually done in real life)
The example has been purposefully kept simple to show how to use and wire the VPC and VPN-HA modules together, and so that it can be used as a basis for experimentation. For a more complex scenario that better reflects real-life usage, including [Shared VPC](https://cloud.google.com/vpc/docs/shared-vpc) and [DNS cross-project binding](https://cloud.google.com/dns/docs/zones/cross-project-binding) please refer to the [FAST network stage](../../../fast/stages/02-networking-vpn/).
This is the high level diagram of this example:
![High-level diagram](diagram.png "High-level diagram")
@ -12,44 +19,87 @@ This sample creates several distinct groups of resources:
- one VPC for each hub and each spoke
- one set of firewall rules for each VPC
- one VPN gateway, one tunnel and one Cloud Router for each spoke
- two VPN gateways, two tunnels and two Cloud Routers for the hub (one for each spoke)
- one HA VPN gateway with two tunnels and one Cloud Router for each spoke
- two HA VPN gateways with two tunnels and a shared Cloud Routers for the hub
- one DNS private zone in the hub
- one DNS peering zone in each spoke
- one Cloud NAT configuration for each spoke
- one test instance for each spoke
- one DNS peering zone and one DNS private zone in each spoke
- one test instance for the hub each spoke
## Operational considerations
## Prerequisites
A single pre-existing project is used in this example to keep variables and complexity to a minimum, in a real world scenarios each spoke would probably use a separate project. The provided project needs a valid billing account and the Compute and DNS APIs enabled. You can easily create such a project with the [project module](../../../modules/project) or with the following commands:
A single pre-existing project is used in this example to keep variables and complexity to a minimum, in a real world scenarios each spoke would probably use a separate project.
``` shell
MY_PROJECT_ID="<desired project id>"
gcloud projects create $MY_PROJECT_ID
gcloud alpha billing projects link --billing-account=XXXXXX-XXXXXX-XXXXXX $MY_PROJECT_ID
gcloud services enable --project=$MY_PROJECT_ID {compute,dns}.googleapis.com
The provided project needs a valid billing account, the Compute and DNS APIs are enabled by the example.
You can easily create such a project by commenting turning on project creation in the project module contained in `main.tf`, as shown in this snippet:
```hcl
module "project" {
source = "../../..//modules/project"
name = var.project_id
# comment or remove this line to enable project creation
# project_create = false
# add the following line with your billing account id value
billing_account = "12345-ABCD-12345"
services = [
"compute.googleapis.com",
"dns.googleapis.com"
]
service_config = {
disable_on_destroy = false
disable_dependent_services = false
}
}
```
The example does not account for HA, but the VPN gateways can be easily upgraded to use HA VPN via the [net-vpn-ha module](../../../modules/net-vpn-ha).
## Testing
If a single router and VPN gateway are used in the hub to manage all tunnels, particular care must be taken in announcing ranges from hub to spokes, as Cloud Router does not explicitly support transitivity and overlapping routes received from both sides create unintended side effects. The simple workaround is to announce a single aggregated route from hub to spokes so that it does not overlap with any of the ranges advertised by each spoke to the hub.
Once the example is up, you can quickly test features by logging in to one of the test VMs:
```bash
gcloud compute ssh hs-ha-lnd-test-r1
# test DNS resolution of the landing zone
ping test-r1.example.com
# test DNS resolution of the prod zone, and prod reachability
ping test-r1.prod.example.com
# test DNS resolution of the dev zone, and dev reachability via global routing
ping test-r2.dev.example.com
```
<!-- TFDOC OPTS files:1 -->
<!-- BEGIN TFDOC -->
## Files
| name | description | modules |
|---|---|---|
| [main.tf](./main.tf) | Module-level locals and resources. | <code>compute-vm</code> · <code>project</code> |
| [net-dev.tf](./net-dev.tf) | Development spoke VPC. | <code>dns</code> · <code>net-vpc</code> · <code>net-vpc-firewall</code> |
| [net-landing.tf](./net-landing.tf) | Landing hub VPC. | <code>dns</code> · <code>net-vpc</code> · <code>net-vpc-firewall</code> |
| [net-prod.tf](./net-prod.tf) | Production spoke VPC. | <code>dns</code> · <code>net-vpc</code> · <code>net-vpc-firewall</code> |
| [outputs.tf](./outputs.tf) | Module outputs. | |
| [variables.tf](./variables.tf) | Module variables. | |
| [versions.tf](./versions.tf) | Version pins. | |
| [vpn-dev-r1.tf](./vpn-dev-r1.tf) | Landing to Development VPN for region 1. | <code>net-vpn-ha</code> |
| [vpn-prod-r1.tf](./vpn-prod-r1.tf) | Landing to Production VPN for region 1. | <code>net-vpn-ha</code> |
## Variables
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [project_id](variables.tf#L56) | Project id for all resources. | <code>string</code> | ✓ | |
| [bgp_asn](variables.tf#L15) | BGP ASNs. | <code>map&#40;number&#41;</code> | | <code title="&#123;&#10; hub &#61; 64513&#10; spoke-1 &#61; 64514&#10; spoke-2 &#61; 64515&#10;&#125;">&#123;&#8230;&#125;</code> |
| [bgp_custom_advertisements](variables.tf#L25) | BGP custom advertisement IP CIDR ranges. | <code>map&#40;string&#41;</code> | | <code title="&#123;&#10; hub-to-spoke-1 &#61; &#34;10.0.32.0&#47;20&#34;&#10; hub-to-spoke-2 &#61; &#34;10.0.16.0&#47;20&#34;&#10;&#125;">&#123;&#8230;&#125;</code> |
| [bgp_interface_ranges](variables.tf#L34) | BGP interface IP CIDR ranges. | <code>map&#40;string&#41;</code> | | <code title="&#123;&#10; spoke-1 &#61; &#34;169.254.1.0&#47;30&#34;&#10; spoke-2 &#61; &#34;169.254.1.4&#47;30&#34;&#10;&#125;">&#123;&#8230;&#125;</code> |
| [ip_ranges](variables.tf#L43) | IP CIDR ranges. | <code>map&#40;string&#41;</code> | | <code title="&#123;&#10; hub-a &#61; &#34;10.0.0.0&#47;24&#34;&#10; hub-b &#61; &#34;10.0.8.0&#47;24&#34;&#10; spoke-1-a &#61; &#34;10.0.16.0&#47;24&#34;&#10; spoke-1-b &#61; &#34;10.0.24.0&#47;24&#34;&#10; spoke-2-a &#61; &#34;10.0.32.0&#47;24&#34;&#10; spoke-2-b &#61; &#34;10.0.40.0&#47;24&#34;&#10;&#125;">&#123;&#8230;&#125;</code> |
| [regions](variables.tf#L61) | VPC regions. | <code>map&#40;string&#41;</code> | | <code title="&#123;&#10; a &#61; &#34;europe-west1&#34;&#10; b &#61; &#34;europe-west2&#34;&#10;&#125;">&#123;&#8230;&#125;</code> |
| [project_id](variables.tf#L49) | Project id for all resources. | <code>string</code> | ✓ | |
| [ip_ranges](variables.tf#L15) | Subnet IP CIDR ranges. | <code>map&#40;string&#41;</code> | | <code title="&#123;&#10; land-0-r1 &#61; &#34;10.0.0.0&#47;24&#34;&#10; land-0-r2 &#61; &#34;10.0.8.0&#47;24&#34;&#10; dev-0-r1 &#61; &#34;10.0.16.0&#47;24&#34;&#10; dev-0-r2 &#61; &#34;10.0.24.0&#47;24&#34;&#10; prod-0-r1 &#61; &#34;10.0.32.0&#47;24&#34;&#10; prod-0-r2 &#61; &#34;10.0.40.0&#47;24&#34;&#10;&#125;">&#123;&#8230;&#125;</code> |
| [ip_secondary_ranges](variables.tf#L28) | Subnet secondary ranges. | <code>map&#40;map&#40;string&#41;&#41;</code> | | <code>&#123;&#125;</code> |
| [prefix](variables.tf#L34) | Prefix used in resource names. | <code>string</code> | | <code>null</code> |
| [project_create_config](variables.tf#L40) | Populate with billing account id to trigger project creation. | <code title="object&#40;&#123;&#10; billing_account_id &#61; string&#10; parent_id &#61; string&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [regions](variables.tf#L54) | VPC regions. | <code>map&#40;string&#41;</code> | | <code title="&#123;&#10; r1 &#61; &#34;europe-west1&#34;&#10; r2 &#61; &#34;europe-west4&#34;&#10;&#125;">&#123;&#8230;&#125;</code> |
| [vpn_configs](variables.tf#L63) | VPN configurations. | <code title="map&#40;object&#40;&#123;&#10; asn &#61; number&#10; custom_ranges &#61; map&#40;string&#41;&#10;&#125;&#41;&#41;">map&#40;object&#40;&#123;&#8230;&#125;&#41;&#41;</code> | | <code title="&#123;&#10; land-r1 &#61; &#123;&#10; asn &#61; 64513&#10; custom_ranges &#61; &#123;&#10; &#34;10.0.0.0&#47;8&#34; &#61; &#34;internal default&#34;&#10; &#125;&#10; &#125;&#10; dev-r1 &#61; &#123;&#10; asn &#61; 64514&#10; custom_ranges &#61; null&#10; &#125;&#10; prod-r1 &#61; &#123;&#10; asn &#61; 64515&#10; custom_ranges &#61; null&#10; &#125;&#10;&#125;">&#123;&#8230;&#125;</code> |
## Outputs
| name | description | sensitive |
|---|---|:---:|
| [vms](outputs.tf#L15) | GCE VMs. | |
| [subnets](outputs.tf#L15) | Subnet details. | |
| [vms](outputs.tf#L39) | GCE VMs. | |
<!-- END TFDOC -->

Binary file not shown.

Before

Width:  |  Height:  |  Size: 195 KiB

After

Width:  |  Height:  |  Size: 90 KiB

View File

@ -13,297 +13,71 @@
# limitations under the License.
locals {
vm-startup-script = join("\n", [
"#! /bin/bash",
"apt-get update && apt-get install -y dnsutils"
])
prefix = var.prefix == null ? "" : "${var.prefix}-"
}
################################################################################
# Hub networking #
################################################################################
# enable services in the project used
module "vpc-hub" {
source = "../../../modules/net-vpc"
project_id = var.project_id
name = "hub"
subnets = [
{
ip_cidr_range = var.ip_ranges.hub-a
name = "hub-a"
region = var.regions.a
secondary_ip_range = {}
},
{
ip_cidr_range = var.ip_ranges.hub-b
name = "hub-b"
region = var.regions.b
secondary_ip_range = {}
}
module "project" {
source = "../../..//modules/project"
name = var.project_id
parent = try(var.project_create_config.parent, null)
billing_account = try(var.project_create_config.billing_account_id, null)
project_create = try(var.project_create_config.billing_account_id, null) != null
services = [
"compute.googleapis.com",
"dns.googleapis.com"
]
}
module "vpc-hub-firewall" {
source = "../../../modules/net-vpc-firewall"
project_id = var.project_id
network = module.vpc-hub.name
admin_ranges = values(var.ip_ranges)
}
module "vpn-hub-a" {
source = "../../../modules/net-vpn-dynamic"
project_id = var.project_id
region = var.regions.a
network = module.vpc-hub.name
name = "hub-a"
router_asn = var.bgp_asn.hub
tunnels = {
spoke-1 = {
bgp_peer = {
address = cidrhost(var.bgp_interface_ranges.spoke-1, 2)
asn = var.bgp_asn.spoke-1
}
bgp_peer_options = {
advertise_groups = ["ALL_SUBNETS"]
advertise_ip_ranges = {
(var.bgp_custom_advertisements.hub-to-spoke-1) = "spoke-2"
}
advertise_mode = "CUSTOM"
route_priority = 1000
}
bgp_session_range = "${cidrhost(var.bgp_interface_ranges.spoke-1, 1)}/30"
ike_version = 2
peer_ip = module.vpn-spoke-1.address
router = null
shared_secret = ""
}
service_config = {
disable_on_destroy = false
disable_dependent_services = false
}
}
module "vpn-hub-b" {
source = "../../../modules/net-vpn-dynamic"
project_id = var.project_id
region = var.regions.b
network = module.vpc-hub.name
name = "hub-b"
router_asn = var.bgp_asn.hub
tunnels = {
spoke-2 = {
bgp_peer = {
address = cidrhost(var.bgp_interface_ranges.spoke-2, 2)
asn = var.bgp_asn.spoke-2
}
bgp_peer_options = {
advertise_groups = ["ALL_SUBNETS"]
advertise_ip_ranges = {
(var.bgp_custom_advertisements.hub-to-spoke-2) = "spoke-1"
}
advertise_mode = "CUSTOM"
route_priority = 1000
}
bgp_session_range = "${cidrhost(var.bgp_interface_ranges.spoke-2, 1)}/30"
ike_version = 2
peer_ip = module.vpn-spoke-2.address
router = null
shared_secret = ""
}
}
}
# test VM in landing region 1
################################################################################
# Spoke 1 networking #
################################################################################
module "vpc-spoke-1" {
source = "../../../modules/net-vpc"
project_id = var.project_id
name = "spoke-1"
subnets = [
{
ip_cidr_range = var.ip_ranges.spoke-1-a
name = "spoke-1-a"
region = var.regions.a
secondary_ip_range = {}
},
{
ip_cidr_range = var.ip_ranges.spoke-1-b
name = "spoke-1-b"
region = var.regions.b
secondary_ip_range = {}
}
]
}
module "vpc-spoke-1-firewall" {
source = "../../../modules/net-vpc-firewall"
project_id = var.project_id
network = module.vpc-spoke-1.name
admin_ranges = values(var.ip_ranges)
}
module "vpn-spoke-1" {
source = "../../../modules/net-vpn-dynamic"
project_id = var.project_id
region = var.regions.a
network = module.vpc-spoke-1.name
name = "spoke-1"
router_asn = var.bgp_asn.spoke-1
tunnels = {
hub = {
bgp_peer = {
address = cidrhost(var.bgp_interface_ranges.spoke-1, 1)
asn = var.bgp_asn.hub
}
bgp_peer_options = null
bgp_session_range = "${cidrhost(var.bgp_interface_ranges.spoke-1, 2)}/30"
ike_version = 2
peer_ip = module.vpn-hub-a.address
router = null
shared_secret = module.vpn-hub-a.random_secret
}
}
}
module "nat-spoke-1" {
source = "../../../modules/net-cloudnat"
project_id = var.project_id
region = var.regions.a
name = "spoke-1"
router_create = false
router_name = module.vpn-spoke-1.router_name
}
################################################################################
# Spoke 2 networking #
################################################################################
module "vpc-spoke-2" {
source = "../../../modules/net-vpc"
project_id = var.project_id
name = "spoke-2"
subnets = [
{
ip_cidr_range = var.ip_ranges.spoke-2-a
name = "spoke-2-a"
region = var.regions.a
secondary_ip_range = {}
},
{
ip_cidr_range = var.ip_ranges.spoke-2-b
name = "spoke-2-b"
region = var.regions.b
secondary_ip_range = {}
}
]
}
module "vpc-spoke-2-firewall" {
source = "../../../modules/net-vpc-firewall"
project_id = var.project_id
network = module.vpc-spoke-2.name
admin_ranges = values(var.ip_ranges)
}
module "vpn-spoke-2" {
source = "../../../modules/net-vpn-dynamic"
project_id = var.project_id
region = var.regions.a
network = module.vpc-spoke-2.name
name = "spoke-2"
router_asn = var.bgp_asn.spoke-2
tunnels = {
hub = {
bgp_peer = {
address = cidrhost(var.bgp_interface_ranges.spoke-2, 1)
asn = var.bgp_asn.hub
}
bgp_peer_options = null
bgp_session_range = "${cidrhost(var.bgp_interface_ranges.spoke-2, 2)}/30"
ike_version = 2
peer_ip = module.vpn-hub-b.address
router = null
shared_secret = module.vpn-hub-b.random_secret
}
}
}
module "nat-spoke-2" {
source = "../../../modules/net-cloudnat"
project_id = var.project_id
region = var.regions.a
name = "spoke-2"
router_create = false
router_name = module.vpn-spoke-2.router_name
}
################################################################################
# Test VMs #
################################################################################
module "vm-spoke-1" {
module "landing-r1-vm" {
source = "../../../modules/compute-vm"
project_id = var.project_id
zone = "${var.regions.b}-b"
name = "spoke-1-test"
name = "${local.prefix}lnd-test-r1"
zone = "${var.regions.r1}-b"
network_interfaces = [{
network = module.vpc-spoke-1.self_link
subnetwork = module.vpc-spoke-1.subnet_self_links["${var.regions.b}/spoke-1-b"]
network = module.landing-vpc.self_link
subnetwork = module.landing-vpc.subnet_self_links["${var.regions.r1}/${local.prefix}lnd-0"]
nat = false
addresses = null
}]
tags = ["ssh"]
metadata = { startup-script = local.vm-startup-script }
tags = ["ssh"]
}
module "vm-spoke-2" {
# test VM in prod region 1
module "prod-r1-vm" {
source = "../../../modules/compute-vm"
project_id = var.project_id
zone = "${var.regions.b}-b"
name = "spoke-2-test"
name = "${local.prefix}prd-test-r1"
zone = "${var.regions.r1}-b"
network_interfaces = [{
network = module.vpc-spoke-2.self_link
subnetwork = module.vpc-spoke-2.subnet_self_links["${var.regions.b}/spoke-2-b"]
network = module.prod-vpc.self_link
subnetwork = module.prod-vpc.subnet_self_links["${var.regions.r1}/${local.prefix}prd-0"]
nat = false
addresses = null
}]
tags = ["ssh"]
metadata = { startup-script = local.vm-startup-script }
tags = ["ssh"]
}
################################################################################
# DNS zones #
################################################################################
# test VM in dev region 1
module "dns-host" {
source = "../../../modules/dns"
project_id = var.project_id
type = "private"
name = "example"
domain = "example.com."
client_networks = [module.vpc-hub.self_link]
recordsets = {
"A localhost" = { ttl = 300, records = ["127.0.0.1"] }
"A spoke-1-test" = { ttl = 300, records = [module.vm-spoke-1.internal_ip] }
"A spoke-2-test" = { ttl = 300, records = [module.vm-spoke-2.internal_ip] }
}
}
module "dns-spoke-1" {
source = "../../../modules/dns"
project_id = var.project_id
type = "peering"
name = "spoke-1"
domain = "example.com."
client_networks = [module.vpc-spoke-1.self_link]
peer_network = module.vpc-hub.self_link
}
module "dns-spoke-2" {
source = "../../../modules/dns"
project_id = var.project_id
type = "peering"
name = "spoke-2"
domain = "example.com."
client_networks = [module.vpc-spoke-2.self_link]
peer_network = module.vpc-hub.self_link
module "dev-r2-vm" {
source = "../../../modules/compute-vm"
project_id = var.project_id
name = "${local.prefix}dev-test-r2"
zone = "${var.regions.r2}-b"
network_interfaces = [{
network = module.dev-vpc.self_link
subnetwork = module.dev-vpc.subnet_self_links["${var.regions.r2}/${local.prefix}dev-0"]
nat = false
addresses = null
}]
tags = ["ssh"]
}

View File

@ -0,0 +1,69 @@
# 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
#
# 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.
# tfdoc:file:description Development spoke VPC.
module "dev-vpc" {
source = "../../../modules/net-vpc"
project_id = var.project_id
name = "${local.prefix}dev"
subnets = [
{
ip_cidr_range = var.ip_ranges.dev-0-r1
name = "${local.prefix}dev-0"
region = var.regions.r1
secondary_ip_range = try(
var.ip_secondary_ranges.dev-0-r1, {}
)
},
{
ip_cidr_range = var.ip_ranges.dev-0-r2
name = "${local.prefix}dev-0"
region = var.regions.r2
secondary_ip_range = try(
var.ip_secondary_ranges.dev-0-r2, {}
)
}
]
}
module "dev-firewall" {
source = "../../../modules/net-vpc-firewall"
project_id = var.project_id
network = module.dev-vpc.name
admin_ranges = values(var.ip_ranges)
}
module "dev-dns-peering" {
source = "../../../modules/dns"
project_id = var.project_id
type = "peering"
name = "${local.prefix}example-com-dev-peering"
domain = "example.com."
client_networks = [module.dev-vpc.self_link]
peer_network = module.landing-vpc.self_link
}
module "dev-dns-zone" {
source = "../../../modules/dns"
project_id = var.project_id
type = "private"
name = "${local.prefix}dev-example-com"
domain = "dev.example.com."
client_networks = [module.landing-vpc.self_link]
recordsets = {
"A localhost" = { ttl = 300, records = ["127.0.0.1"] }
"A test-r2" = { ttl = 300, records = [module.dev-r2-vm.internal_ip] }
}
}

View File

@ -0,0 +1,59 @@
# 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
#
# 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.
# tfdoc:file:description Landing hub VPC.
module "landing-vpc" {
source = "../../../modules/net-vpc"
project_id = var.project_id
name = "${local.prefix}lnd"
subnets = [
{
ip_cidr_range = var.ip_ranges.land-0-r1
name = "${local.prefix}lnd-0"
region = var.regions.r1
secondary_ip_range = try(
var.ip_secondary_ranges.land-0-r1, {}
)
},
{
ip_cidr_range = var.ip_ranges.land-0-r2
name = "${local.prefix}lnd-0"
region = var.regions.r2
secondary_ip_range = try(
var.ip_secondary_ranges.land-0-r2, {}
)
}
]
}
module "landing-firewall" {
source = "../../../modules/net-vpc-firewall"
project_id = var.project_id
network = module.landing-vpc.name
admin_ranges = values(var.ip_ranges)
}
module "landing-dns-zone" {
source = "../../../modules/dns"
project_id = var.project_id
type = "private"
name = "${local.prefix}example-com"
domain = "example.com."
client_networks = [module.landing-vpc.self_link]
recordsets = {
"A localhost" = { ttl = 300, records = ["127.0.0.1"] }
"A test-r1" = { ttl = 300, records = [module.landing-r1-vm.internal_ip] }
}
}

View File

@ -0,0 +1,69 @@
# 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
#
# 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.
# tfdoc:file:description Production spoke VPC.
module "prod-vpc" {
source = "../../../modules/net-vpc"
project_id = var.project_id
name = "${local.prefix}prd"
subnets = [
{
ip_cidr_range = var.ip_ranges.prod-0-r1
name = "${local.prefix}prd-0"
region = var.regions.r1
secondary_ip_range = try(
var.ip_secondary_ranges.prod-0-r1, {}
)
},
{
ip_cidr_range = var.ip_ranges.prod-0-r2
name = "${local.prefix}prd-0"
region = var.regions.r2
secondary_ip_range = try(
var.ip_secondary_ranges.prod-0-r2, {}
)
}
]
}
module "prod-firewall" {
source = "../../../modules/net-vpc-firewall"
project_id = var.project_id
network = module.prod-vpc.name
admin_ranges = values(var.ip_ranges)
}
module "prod-dns-peering" {
source = "../../../modules/dns"
project_id = var.project_id
type = "peering"
name = "${local.prefix}example-com-prd-peering"
domain = "example.com."
client_networks = [module.prod-vpc.self_link]
peer_network = module.landing-vpc.self_link
}
module "prod-dns-zone" {
source = "../../../modules/dns"
project_id = var.project_id
type = "private"
name = "${local.prefix}prd-example-com"
domain = "prd.example.com."
client_networks = [module.landing-vpc.self_link]
recordsets = {
"A localhost" = { ttl = 300, records = ["127.0.0.1"] }
"A test-r1" = { ttl = 300, records = [module.prod-r1-vm.internal_ip] }
}
}

View File

@ -12,10 +12,34 @@
# See the License for the specific language governing permissions and
# limitations under the License.
output "subnets" {
description = "Subnet details."
value = {
dev = {
for k, v in module.dev-vpc.subnets : k => {
id = v.id
ip_cidr_range = v.ip_cidr_range
}
}
landing = {
for k, v in module.landing-vpc.subnets : k => {
id = v.id
ip_cidr_range = v.ip_cidr_range
}
}
prod = {
for k, v in module.prod-vpc.subnets : k => {
id = v.id
ip_cidr_range = v.ip_cidr_range
}
}
}
}
output "vms" {
description = "GCE VMs."
value = {
for instance in [module.vm-spoke-1.instance, module.vm-spoke-2.instance] :
instance.name => instance.network_interface.0.network_ip
for mod in [module.landing-r1-vm, module.dev-r2-vm, module.prod-r1-vm] :
mod.instance.name => mod.internal_ip
}
}

View File

@ -1,18 +0,0 @@
# 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
#
# 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.
provider "google" {
}
provider "google-beta" {
}

View File

@ -12,47 +12,40 @@
# See the License for the specific language governing permissions and
# limitations under the License.
variable "bgp_asn" {
description = "BGP ASNs."
type = map(number)
default = {
hub = 64513
spoke-1 = 64514
spoke-2 = 64515
}
}
variable "bgp_custom_advertisements" {
description = "BGP custom advertisement IP CIDR ranges."
type = map(string)
default = {
hub-to-spoke-1 = "10.0.32.0/20"
hub-to-spoke-2 = "10.0.16.0/20"
}
}
variable "bgp_interface_ranges" {
description = "BGP interface IP CIDR ranges."
type = map(string)
default = {
spoke-1 = "169.254.1.0/30"
spoke-2 = "169.254.1.4/30"
}
}
variable "ip_ranges" {
description = "IP CIDR ranges."
description = "Subnet IP CIDR ranges."
type = map(string)
default = {
hub-a = "10.0.0.0/24"
hub-b = "10.0.8.0/24"
spoke-1-a = "10.0.16.0/24"
spoke-1-b = "10.0.24.0/24"
spoke-2-a = "10.0.32.0/24"
spoke-2-b = "10.0.40.0/24"
land-0-r1 = "10.0.0.0/24"
land-0-r2 = "10.0.8.0/24"
dev-0-r1 = "10.0.16.0/24"
dev-0-r2 = "10.0.24.0/24"
prod-0-r1 = "10.0.32.0/24"
prod-0-r2 = "10.0.40.0/24"
}
}
variable "ip_secondary_ranges" {
description = "Subnet secondary ranges."
type = map(map(string))
default = {}
}
variable "prefix" {
description = "Prefix used in resource names."
type = string
default = null
}
variable "project_create_config" {
description = "Populate with billing account id to trigger project creation."
type = object({
billing_account_id = string
parent_id = string
})
default = null
}
variable "project_id" {
description = "Project id for all resources."
type = string
@ -62,7 +55,31 @@ variable "regions" {
description = "VPC regions."
type = map(string)
default = {
a = "europe-west1"
b = "europe-west2"
r1 = "europe-west1"
r2 = "europe-west4"
}
}
variable "vpn_configs" {
description = "VPN configurations."
type = map(object({
asn = number
custom_ranges = map(string)
}))
default = {
land-r1 = {
asn = 64513
custom_ranges = {
"10.0.0.0/8" = "internal default"
}
}
dev-r1 = {
asn = 64514
custom_ranges = null
}
prod-r1 = {
asn = 64515
custom_ranges = null
}
}
}

View File

@ -0,0 +1,107 @@
# 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
#
# 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.
# tfdoc:file:description Landing to Development VPN for region 1.
module "landing-to-dev-vpn-r1" {
source = "../../../modules/net-vpn-ha"
project_id = var.project_id
network = module.landing-vpc.self_link
region = var.regions.r1
name = "${local.prefix}lnd-to-dev-r1"
router_create = false
router_name = "${local.prefix}lnd-vpn-r1"
# router is created and managed by the production VPN module
# so we don't configure advertisements here
peer_gcp_gateway = module.dev-to-landing-vpn-r1.self_link
tunnels = {
0 = {
bgp_peer = {
address = "169.254.2.2"
asn = var.vpn_configs.dev-r1.asn
}
# use this attribute to configure different advertisements for dev
bgp_peer_options = null
bgp_session_range = "169.254.2.1/30"
ike_version = 2
peer_external_gateway_interface = null
router = null
shared_secret = null
vpn_gateway_interface = 0
}
1 = {
bgp_peer = {
address = "169.254.2.6"
asn = var.vpn_configs.dev-r1.asn
}
# use this attribute to configure different advertisements for dev
bgp_peer_options = null
bgp_session_range = "169.254.2.5/30"
ike_version = 2
peer_external_gateway_interface = null
router = null
shared_secret = null
vpn_gateway_interface = 1
}
}
}
module "dev-to-landing-vpn-r1" {
source = "../../../modules/net-vpn-ha"
project_id = var.project_id
network = module.dev-vpc.self_link
region = var.regions.r1
name = "${local.prefix}dev-to-lnd-r1"
router_create = true
router_name = "${local.prefix}dev-vpn-r1"
router_asn = var.vpn_configs.dev-r1.asn
router_advertise_config = (
var.vpn_configs.dev-r1.custom_ranges == null
? null
: {
groups = null
ip_ranges = coalesce(var.vpn_configs.dev-r1.custom_ranges, {})
mode = "CUSTOM"
}
)
peer_gcp_gateway = module.landing-to-dev-vpn-r1.self_link
tunnels = {
0 = {
bgp_peer = {
address = "169.254.2.1"
asn = var.vpn_configs.land-r1.asn
}
bgp_peer_options = null
bgp_session_range = "169.254.2.2/30"
ike_version = 2
peer_external_gateway_interface = null
router = null
shared_secret = module.landing-to-dev-vpn-r1.random_secret
vpn_gateway_interface = 0
}
1 = {
bgp_peer = {
address = "169.254.2.5"
asn = var.vpn_configs.land-r1.asn
}
bgp_peer_options = null
bgp_session_range = "169.254.2.6/30"
ike_version = 2
peer_external_gateway_interface = null
router = null
shared_secret = module.landing-to-dev-vpn-r1.random_secret
vpn_gateway_interface = 1
}
}
}

View File

@ -0,0 +1,116 @@
# 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
#
# 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.
# tfdoc:file:description Landing to Production VPN for region 1.
module "landing-to-prod-vpn-r1" {
source = "../../../modules/net-vpn-ha"
project_id = var.project_id
network = module.landing-vpc.self_link
region = var.regions.r1
name = "${local.prefix}lnd-to-prd-r1"
router_create = true
router_name = "${local.prefix}lnd-vpn-r1"
router_asn = var.vpn_configs.land-r1.asn
router_advertise_config = (
var.vpn_configs.land-r1.custom_ranges == null
? null
: {
groups = null
ip_ranges = coalesce(var.vpn_configs.land-r1.custom_ranges, {})
mode = "CUSTOM"
}
)
peer_gcp_gateway = module.prod-to-landing-vpn-r1.self_link
tunnels = {
0 = {
bgp_peer = {
address = "169.254.0.2"
asn = var.vpn_configs.prod-r1.asn
}
bgp_peer_options = null
bgp_session_range = "169.254.0.1/30"
ike_version = 2
peer_external_gateway_interface = null
router = null
shared_secret = null
vpn_gateway_interface = 0
}
1 = {
bgp_peer = {
address = "169.254.0.6"
asn = var.vpn_configs.prod-r1.asn
}
bgp_peer_options = null
bgp_session_range = "169.254.0.5/30"
ike_version = 2
peer_external_gateway_interface = null
router = null
shared_secret = null
vpn_gateway_interface = 1
}
}
}
module "prod-to-landing-vpn-r1" {
source = "../../../modules/net-vpn-ha"
project_id = var.project_id
network = module.prod-vpc.self_link
region = var.regions.r1
name = "${local.prefix}prd-to-lnd-r1"
router_create = true
router_name = "${local.prefix}prd-vpn-r1"
router_asn = var.vpn_configs.prod-r1.asn
# the router is managed here but shared with the dev VPN
router_advertise_config = (
var.vpn_configs.prod-r1.custom_ranges == null
? null
: {
groups = null
ip_ranges = coalesce(var.vpn_configs.prod-r1.custom_ranges, {})
mode = "CUSTOM"
}
)
peer_gcp_gateway = module.landing-to-prod-vpn-r1.self_link
tunnels = {
0 = {
bgp_peer = {
address = "169.254.0.1"
asn = var.vpn_configs.land-r1.asn
}
# use this attribute to configure different advertisements for prod
bgp_peer_options = null
bgp_session_range = "169.254.0.2/30"
ike_version = 2
peer_external_gateway_interface = null
router = null
shared_secret = module.landing-to-prod-vpn-r1.random_secret
vpn_gateway_interface = 0
}
1 = {
bgp_peer = {
address = "169.254.0.5"
asn = var.vpn_configs.land-r1.asn
}
# use this attribute to configure different advertisements for prod
bgp_peer_options = null
bgp_session_range = "169.254.0.6/30"
ike_version = 2
peer_external_gateway_interface = null
router = null
shared_secret = module.landing-to-prod-vpn-r1.random_secret
vpn_gateway_interface = 1
}
}
}

View File

@ -15,6 +15,10 @@
*/
module "test" {
source = "../../../../../examples/networking/hub-and-spoke-vpn"
project_id = var.project_id
source = "../../../../../examples/networking/hub-and-spoke-vpn"
project_create_config = {
billing_account_id = "ABCDE-123456-ABCDE"
parent_id = null
}
project_id = "test-1"
}

View File

@ -1,18 +0,0 @@
# 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
#
# 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.
variable "project_id" {
type = string
default = "project-1"
}

View File

@ -12,8 +12,9 @@
# See the License for the specific language governing permissions and
# limitations under the License.
def test_resources(e2e_plan_runner):
"Test that plan works and the numbers of resources is as expected."
modules, resources = e2e_plan_runner()
assert len(modules) == 17
assert len(resources) == 71
assert len(modules) == 19
assert len(resources) == 73