Merge pull request #1219 from juliodiez/ncc
Network Connectivity Center module
This commit is contained in:
commit
1ff3bf9327
|
@ -0,0 +1,152 @@
|
|||
# NCC Spoke RA Module
|
||||
|
||||
This module allows management of NCC Spokes backed by Router Appliances. Network virtual appliances used as router appliances allow to connect an external network to Google Cloud by using a SD-WAN router or another appliance with BGP capabilities (_site-to-cloud_ connectivity). It is also possible to enable site-to-site data transfer, although this feature is not available in all regions, particularly not in EMEA.
|
||||
|
||||
The module manages a hub (optionally), a spoke, and the corresponding Cloud Router and BGP sessions to the router appliance(s).
|
||||
|
||||
## Examples
|
||||
|
||||
### Simple hub & spoke
|
||||
|
||||
```hcl
|
||||
module "spoke-ra" {
|
||||
source = "./fabric/modules/ncc-spoke-ra"
|
||||
hub = { create = true, name = "ncc-hub" }
|
||||
name = "spoke-ra"
|
||||
project_id = "my-project"
|
||||
region = "europe-west1"
|
||||
router_appliances = [
|
||||
{
|
||||
internal_ip = "10.0.0.3"
|
||||
vm_self_link = "projects/my-project/zones/europe-west1-b/instances/router-app"
|
||||
}
|
||||
]
|
||||
router_config = {
|
||||
asn = 65000
|
||||
ip_interface1 = "10.0.0.14"
|
||||
ip_interface2 = "10.0.0.15"
|
||||
peer_asn = 65001
|
||||
}
|
||||
vpc_config = {
|
||||
network_name = "my-vpc"
|
||||
subnet_self_link = var.subnet.self_link
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=7
|
||||
```
|
||||
|
||||
### Two spokes
|
||||
|
||||
```hcl
|
||||
module "spoke-ra-a" {
|
||||
source = "./fabric/modules/ncc-spoke-ra"
|
||||
hub = { name = "ncc-hub" }
|
||||
name = "spoke-ra-a"
|
||||
project_id = "my-project"
|
||||
region = "europe-west1"
|
||||
router_appliances = [
|
||||
{
|
||||
internal_ip = "10.0.0.3"
|
||||
vm_self_link = "projects/my-project/zones/europe-west1-b/instances/router-app-a"
|
||||
}
|
||||
]
|
||||
router_config = {
|
||||
asn = 65000
|
||||
ip_interface1 = "10.0.0.14"
|
||||
ip_interface2 = "10.0.0.15"
|
||||
peer_asn = 65001
|
||||
}
|
||||
vpc_config = {
|
||||
network_name = "my-vpc1"
|
||||
subnet_self_link = "projects/my-project/regions/europe-west1/subnetworks/subnet"
|
||||
}
|
||||
}
|
||||
|
||||
module "spoke-ra-b" {
|
||||
source = "./fabric/modules/ncc-spoke-ra"
|
||||
hub = { name = "ncc-hub" }
|
||||
name = "spoke-ra-b"
|
||||
project_id = "my-project"
|
||||
region = "europe-west3"
|
||||
router_appliances = [
|
||||
{
|
||||
internal_ip = "10.1.0.5"
|
||||
vm_self_link = "projects/my-project/zones/europe-west3-b/instances/router-app-b"
|
||||
}
|
||||
]
|
||||
router_config = {
|
||||
asn = 65000
|
||||
ip_interface1 = "10.0.0.14"
|
||||
ip_interface2 = "10.0.0.15"
|
||||
peer_asn = 65002
|
||||
}
|
||||
vpc_config = {
|
||||
network_name = "my-vpc2"
|
||||
subnet_self_link = "projects/my-project/regions/europe-west3/subnetworks/subnet"
|
||||
}
|
||||
}
|
||||
# tftest modules=2 resources=12
|
||||
```
|
||||
|
||||
### Spoke with load-balanced router appliances
|
||||
|
||||
```hcl
|
||||
module "spoke-ra" {
|
||||
source = "./fabric/modules/ncc-spoke-ra"
|
||||
hub = { name = "ncc-hub" }
|
||||
name = "spoke-ra"
|
||||
project_id = "my-project"
|
||||
region = "europe-west1"
|
||||
router_appliances = [
|
||||
{
|
||||
internal_ip = "10.0.0.3"
|
||||
vm_self_link = "projects/my-project/zones/europe-west1-b/instances/router-app-a"
|
||||
},
|
||||
{
|
||||
internal_ip = "10.0.0.4"
|
||||
vm_self_link = "projects/my-project/zones/europe-west1-c/instances/router-app-b"
|
||||
}
|
||||
]
|
||||
router_config = {
|
||||
asn = 65000
|
||||
custom_advertise = {
|
||||
all_subnets = true
|
||||
ip_ranges = {
|
||||
"10.10.0.0/24" = "peered-vpc"
|
||||
}
|
||||
}
|
||||
ip_interface1 = "10.0.0.14"
|
||||
ip_interface2 = "10.0.0.15"
|
||||
peer_asn = 65001
|
||||
}
|
||||
vpc_config = {
|
||||
network_name = "my-vpc"
|
||||
subnet_self_link = var.subnet.self_link
|
||||
}
|
||||
}
|
||||
# tftest modules=1 resources=8
|
||||
```
|
||||
<!-- BEGIN TFDOC -->
|
||||
|
||||
## Variables
|
||||
|
||||
| name | description | type | required | default |
|
||||
|---|---|:---:|:---:|:---:|
|
||||
| [hub](variables.tf#L23) | The name of the NCC hub to create or use. | <code title="object({ create = optional(bool, false) description = optional(string) name = string })">object({…})</code> | ✓ | |
|
||||
| [name](variables.tf#L32) | The name of the NCC spoke. | <code>string</code> | ✓ | |
|
||||
| [project_id](variables.tf#L37) | The ID of the project where the NCC hub & spokes will be created. | <code>string</code> | ✓ | |
|
||||
| [region](variables.tf#L42) | Region where the spoke is located. | <code>string</code> | ✓ | |
|
||||
| [router_appliances](variables.tf#L47) | List of router appliances this spoke is associated with. | <code title="list(object({ internal_ip = string vm_self_link = string }))">list(object({…}))</code> | ✓ | |
|
||||
| [router_config](variables.tf#L55) | Configuration of the Cloud Router. | <code title="object({ asn = number custom_advertise = optional(object({ all_subnets = bool ip_ranges = map(string) })) ip_interface1 = string ip_interface2 = string keepalive = optional(number) peer_asn = number })">object({…})</code> | ✓ | |
|
||||
| [vpc_config](variables.tf#L70) | Network and subnetwork for the CR interfaces. | <code title="object({ network_name = string subnet_self_link = string })">object({…})</code> | ✓ | |
|
||||
| [data_transfer](variables.tf#L17) | Site-to-site data transfer feature, available only in some regions. | <code>bool</code> | | <code>false</code> |
|
||||
|
||||
## Outputs
|
||||
|
||||
| name | description | sensitive |
|
||||
|---|---|:---:|
|
||||
| [hub](outputs.tf#L17) | NCC hub resource (only if auto-created). | |
|
||||
| [router](outputs.tf#L22) | Cloud Router resource. | |
|
||||
| [spoke-ra](outputs.tf#L27) | NCC spoke resource. | |
|
||||
|
||||
<!-- END TFDOC -->
|
|
@ -0,0 +1,123 @@
|
|||
/**
|
||||
* Copyright 2023 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 {
|
||||
spoke_vms = [
|
||||
for ras in var.router_appliances : {
|
||||
ip = ras.internal_ip
|
||||
vm = ras.vm_self_link
|
||||
vm_name = element(
|
||||
split("/", ras.vm_self_link), length(split("/", ras.vm_self_link)) - 1
|
||||
)
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
resource "google_network_connectivity_hub" "hub" {
|
||||
count = var.hub.create ? 1 : 0
|
||||
project = var.project_id
|
||||
name = var.hub.name
|
||||
description = var.hub.description
|
||||
}
|
||||
|
||||
resource "google_network_connectivity_spoke" "spoke-ra" {
|
||||
project = var.project_id
|
||||
hub = try(google_network_connectivity_hub.hub[0].name, var.hub.name)
|
||||
location = var.region
|
||||
name = var.name
|
||||
linked_router_appliance_instances {
|
||||
dynamic "instances" {
|
||||
for_each = var.router_appliances
|
||||
content {
|
||||
ip_address = instances.value["internal_ip"]
|
||||
virtual_machine = instances.value["vm_self_link"]
|
||||
}
|
||||
}
|
||||
site_to_site_data_transfer = var.data_transfer
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_compute_router" "cr" {
|
||||
project = var.project_id
|
||||
name = "${var.name}-cr"
|
||||
network = var.vpc_config.network_name
|
||||
region = var.region
|
||||
bgp {
|
||||
advertise_mode = (
|
||||
var.router_config.custom_advertise != null ? "CUSTOM" : "DEFAULT"
|
||||
)
|
||||
advertised_groups = (
|
||||
try(var.router_config.custom_advertise.all_subnets, false)
|
||||
? ["ALL_SUBNETS"] : []
|
||||
)
|
||||
dynamic "advertised_ip_ranges" {
|
||||
for_each = try(var.router_config.custom_advertise.ip_ranges, {})
|
||||
content {
|
||||
description = advertised_ip_ranges.value
|
||||
range = advertised_ip_ranges.key
|
||||
}
|
||||
}
|
||||
asn = var.router_config.asn
|
||||
keepalive_interval = try(var.router_config.keepalive, null)
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_compute_router_interface" "intf1" {
|
||||
project = var.project_id
|
||||
name = "intf1"
|
||||
router = google_compute_router.cr.name
|
||||
region = var.region
|
||||
subnetwork = var.vpc_config.subnet_self_link
|
||||
private_ip_address = var.router_config.ip_interface1
|
||||
}
|
||||
|
||||
resource "google_compute_router_interface" "intf2" {
|
||||
project = var.project_id
|
||||
name = "intf2"
|
||||
router = google_compute_router.cr.name
|
||||
region = var.region
|
||||
subnetwork = var.vpc_config.subnet_self_link
|
||||
private_ip_address = var.router_config.ip_interface2
|
||||
redundant_interface = google_compute_router_interface.intf1.name
|
||||
}
|
||||
|
||||
resource "google_compute_router_peer" "peer1" {
|
||||
for_each = {
|
||||
for idx, entry in local.spoke_vms : idx => entry
|
||||
}
|
||||
project = var.project_id
|
||||
name = "peer1-${each.value.vm_name}"
|
||||
router = google_compute_router.cr.name
|
||||
region = var.region
|
||||
interface = google_compute_router_interface.intf1.name
|
||||
peer_asn = var.router_config.peer_asn
|
||||
peer_ip_address = each.value.ip
|
||||
router_appliance_instance = each.value.vm
|
||||
}
|
||||
|
||||
resource "google_compute_router_peer" "peer2" {
|
||||
for_each = {
|
||||
for idx, entry in local.spoke_vms : idx => entry
|
||||
}
|
||||
project = var.project_id
|
||||
name = "peer2-${each.value.vm_name}"
|
||||
router = google_compute_router.cr.name
|
||||
region = var.region
|
||||
interface = google_compute_router_interface.intf2.name
|
||||
peer_asn = var.router_config.peer_asn
|
||||
peer_ip_address = each.value.ip
|
||||
router_appliance_instance = each.value.vm
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* Copyright 2023 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.
|
||||
*/
|
||||
|
||||
output "hub" {
|
||||
description = "NCC hub resource (only if auto-created)."
|
||||
value = one(google_network_connectivity_hub.hub[*])
|
||||
}
|
||||
|
||||
output "router" {
|
||||
description = "Cloud Router resource."
|
||||
value = google_compute_router.cr
|
||||
}
|
||||
|
||||
output "spoke-ra" {
|
||||
description = "NCC spoke resource."
|
||||
value = google_network_connectivity_spoke.spoke-ra
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/**
|
||||
* Copyright 2023 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
variable "data_transfer" {
|
||||
description = "Site-to-site data transfer feature, available only in some regions."
|
||||
type = bool
|
||||
default = false
|
||||
}
|
||||
|
||||
variable "hub" {
|
||||
description = "The name of the NCC hub to create or use."
|
||||
type = object({
|
||||
create = optional(bool, false)
|
||||
description = optional(string)
|
||||
name = string
|
||||
})
|
||||
}
|
||||
|
||||
variable "name" {
|
||||
description = "The name of the NCC spoke."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "project_id" {
|
||||
description = "The ID of the project where the NCC hub & spokes will be created."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "region" {
|
||||
description = "Region where the spoke is located."
|
||||
type = string
|
||||
}
|
||||
|
||||
variable "router_appliances" {
|
||||
description = "List of router appliances this spoke is associated with."
|
||||
type = list(object({
|
||||
internal_ip = string
|
||||
vm_self_link = string
|
||||
}))
|
||||
}
|
||||
|
||||
variable "router_config" {
|
||||
description = "Configuration of the Cloud Router."
|
||||
type = object({
|
||||
asn = number
|
||||
custom_advertise = optional(object({
|
||||
all_subnets = bool
|
||||
ip_ranges = map(string)
|
||||
}))
|
||||
ip_interface1 = string
|
||||
ip_interface2 = string
|
||||
keepalive = optional(number)
|
||||
peer_asn = number
|
||||
})
|
||||
}
|
||||
|
||||
variable "vpc_config" {
|
||||
description = "Network and subnetwork for the CR interfaces."
|
||||
type = object({
|
||||
network_name = string
|
||||
subnet_self_link = string
|
||||
})
|
||||
}
|
Loading…
Reference in New Issue