Added recipe HA VPN between AWS and GCP (#3034)

* Added recipe HA VPN between AWS and GCP

* Fix typo

* Update providers to work without credentials

* Add AWS resource to tools/lockfile/main.tf

* Fixed error in recipe docs

---------

Co-authored-by: Julio Castillo <jccb@google.com>
This commit is contained in:
apichick
2025-04-14 12:47:21 +02:00
committed by GitHub
parent 2d54911415
commit ff7e7d299c
9 changed files with 367 additions and 1 deletions

View File

@@ -2,6 +2,17 @@
This module makes it easy to deploy either GCP-to-GCP or GCP-to-On-prem [Cloud HA VPN](https://cloud.google.com/network-connectivity/docs/vpn/concepts/overview#ha-vpn).
<!-- BEGIN TOC -->
- [Examples](#examples)
- [GCP to GCP](#gcp-to-gcp)
- [GCP to on-prem](#gcp-to-on-prem)
- [IPv6 (dual-stack)](#ipv6-dual-stack)
- [Recipes](#recipes)
- [Recipes](#recipes)
- [Variables](#variables)
- [Outputs](#outputs)
<!-- END TOC -->
## Examples
### GCP to GCP
@@ -204,7 +215,16 @@ module "vpn_ha" {
```
You can optionally avoid to specify MD5 keys and the module will automatically generate them for you.
## Recipes
- [HA VPN connections between Google Cloud and AWS](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/blob/master/modules/net-vpn-ha/recipe-vpn-aws-gcp)
<!-- BEGIN TFDOC -->
## Recipes
- [HA VPN connections between Google Cloud and AWS](https://github.com/GoogleCloudPlatform/cloud-foundation-fabric/blob/master/modules/net-vpn-ha/recipe-vpn-aws-gcp)
## Variables
| name | description | type | required | default |

View File

@@ -0,0 +1,48 @@
# HA VPN connections between Google Cloud and AWS
This recipe demonstrates how to create highly available VPN connections between Google Cloud and Amazon Web Services (AWS) for direct communication between VPC networks across the two cloud platforms using a Virtual Private Gateway in AWS. For more details on this architecture have a look [here](https://cloud.google.com/network-connectivity/docs/vpn/tutorials/create-ha-vpn-connections-google-cloud-aws)
The architecture deployed by this recipe is the one depicted below:
![Architecture](./diagram.png)
<!-- BEGIN TFDOC -->
## Variables
| name | description | type | required | default |
|---|---|:---:|:---:|:---:|
| [aws_asn](variables.tf#L27) | AWS ASN. | <code>string</code> | ✓ | |
| [aws_region](variables.tf#L32) | AWS Region. | <code>string</code> | ✓ | |
| [aws_vpc_cidr_block](variables.tf#L37) | CIDR block. | <code>string</code> | ✓ | |
| [gcp_asn](variables.tf#L42) | Google ASN. | <code>string</code> | ✓ | |
| [gcp_region](variables.tf#L47) | GCP Region. | <code>string</code> | ✓ | |
| [project_id](variables.tf#L52) | Project ID. | <code>string</code> | ✓ | |
| [shared_secret](variables.tf#L63) | Shared secret. | <code>string</code> | ✓ | |
| [_testing](variables.tf#L17) | Populate this variable to avoid triggering the data source. | <code title="object&#40;&#123;&#10; name &#61; string&#10; number &#61; number&#10; services_enabled &#61; optional&#40;list&#40;string&#41;, &#91;&#93;&#41;&#10;&#125;&#41;">object&#40;&#123;&#8230;&#125;&#41;</code> | | <code>null</code> |
| [propagate_routes](variables.tf#L57) | Flag indicating whether routed received by AWS's Virtual Private Gateway should be propagated to main route table. | <code>bool</code> | | <code>false</code> |
## Outputs
| name | description | sensitive |
|---|---|:---:|
| [external_gateway](outputs.tf#L17) | External VPN gateway resource. | |
| [gateway](outputs.tf#L22) | VPN gateway resource (only if auto-created). | |
| [id](outputs.tf#L27) | Fully qualified VPN gateway id. | |
<!-- END TFDOC -->
## Test
```hcl
module "gcp_vpn" {
source = "./fabric/modules/net-vpn-ha/recipe-vpn-aws-gcp"
project_id = "project-1"
_testing = {
name = "project-1"
number = 1234567890
}
aws_asn = 65001
gcp_asn = 65534
aws_region = "us-east-1"
gcp_region = "us-east1"
aws_vpc_cidr_block = "10.0.0.0/16"
shared_secret = "test123456"
}
# tftest modules=4 resources=36

View File

@@ -0,0 +1,69 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
resource "aws_vpc" "vpc" {
cidr_block = var.aws_vpc_cidr_block
}
resource "aws_vpn_gateway" "vpn_gateway" {
vpc_id = aws_vpc.vpc.id
amazon_side_asn = var.aws_asn
tags = {
Name = "vpn_gateway"
}
}
resource "aws_customer_gateway" "customer_gateways" {
count = 2
bgp_asn = var.gcp_asn
ip_address = module.gcp_vpn.gateway.vpn_interfaces[count.index].ip_address
type = "ipsec.1"
tags = {
Name = "customer-gateway-${count.index}"
}
}
resource "aws_vpn_gateway_attachment" "vpn_gateway_attachment" {
vpc_id = aws_vpc.vpc.id
vpn_gateway_id = aws_vpn_gateway.vpn_gateway.id
}
resource "aws_vpn_connection" "vpn_connections" {
count = 2
vpn_gateway_id = aws_vpn_gateway.vpn_gateway.id
customer_gateway_id = aws_customer_gateway.customer_gateways[count.index].id
type = "ipsec.1"
tunnel1_preshared_key = var.shared_secret
tunnel2_preshared_key = var.shared_secret
}
data "aws_route_table" "route_table" {
count = var.propagate_routes ? 1 : 0
vpc_id = aws_vpc.vpc.id
filter {
name = "association.main"
values = ["true"]
}
}
resource "aws_vpn_gateway_route_propagation" "vpn_gateway_route_propagation" {
count = var.propagate_routes ? 1 : 0
vpn_gateway_id = aws_vpn_gateway.vpn_gateway.id
route_table_id = data.aws_route_table.route_table[0].id
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 87 KiB

View File

@@ -0,0 +1,105 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
module "project" {
source = "../../../modules/project"
name = var.project_id
project_reuse = {
use_data_source = var._testing == null
project_attributes = var._testing
}
services = [
"compute.googleapis.com",
"dns.googleapis.com",
]
}
module "vpc" {
source = "../../../modules/net-vpc"
project_id = module.project.project_id
name = "vpc"
}
module "gcp_vpn" {
source = "../../../modules/net-vpn-ha"
project_id = module.project.project_id
region = var.gcp_region
network = module.vpc.self_link
name = "vpn"
peer_gateways = {
default = {
external = {
redundancy_type = "FOUR_IPS_REDUNDANCY"
interfaces = [
aws_vpn_connection.vpn_connections[0].tunnel1_address,
aws_vpn_connection.vpn_connections[0].tunnel2_address,
aws_vpn_connection.vpn_connections[1].tunnel1_address,
aws_vpn_connection.vpn_connections[1].tunnel2_address
] # on-prem router ip address
}
}
}
router_config = {
asn = var.gcp_asn
custom_advertise = {
all_subnets = true
ip_ranges = {
}
}
}
tunnels = {
remote-0 = {
bgp_peer = {
address = aws_vpn_connection.vpn_connections[0].tunnel1_vgw_inside_address
asn = var.aws_asn
}
bgp_session_range = "${aws_vpn_connection.vpn_connections[0].tunnel1_cgw_inside_address}/30"
peer_external_gateway_interface = 0
shared_secret = var.shared_secret
vpn_gateway_interface = 0
}
remote-1 = {
bgp_peer = {
address = aws_vpn_connection.vpn_connections[0].tunnel2_vgw_inside_address
asn = var.aws_asn
}
bgp_session_range = "${aws_vpn_connection.vpn_connections[0].tunnel2_cgw_inside_address}/30"
peer_external_gateway_interface = 1
shared_secret = var.shared_secret
vpn_gateway_interface = 0
}
remote-2 = {
bgp_peer = {
address = aws_vpn_connection.vpn_connections[1].tunnel1_vgw_inside_address
asn = var.aws_asn
}
bgp_session_range = "${aws_vpn_connection.vpn_connections[1].tunnel1_cgw_inside_address}/30"
peer_external_gateway_interface = 2
shared_secret = var.shared_secret
vpn_gateway_interface = 1
}
remote-3 = {
bgp_peer = {
address = aws_vpn_connection.vpn_connections[1].tunnel2_vgw_inside_address
asn = var.aws_asn
}
bgp_session_range = "${aws_vpn_connection.vpn_connections[1].tunnel2_cgw_inside_address}/30"
peer_external_gateway_interface = 3
shared_secret = var.shared_secret
vpn_gateway_interface = 1
}
}
}

View File

@@ -0,0 +1,30 @@
/**
* 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.
*/
output "external_gateway" {
description = "External VPN gateway resource."
value = module.gcp_vpn.external_gateway
}
output "gateway" {
description = "VPN gateway resource (only if auto-created)."
value = module.gcp_vpn.gateway
}
output "id" {
description = "Fully qualified VPN gateway id."
value = module.gcp_vpn.id
}

View File

@@ -0,0 +1,26 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
provider "aws" {
region = var.aws_region
# The values below are needed to allow the tests to work for this
# recipe without requiring credentials
skip_credentials_validation = true
skip_requesting_account_id = true
skip_metadata_api_check = true
access_key = "mock_access_key"
secret_key = "mock_secret_key"
}

View File

@@ -0,0 +1,67 @@
/**
* Copyright 2025 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
variable "_testing" {
description = "Populate this variable to avoid triggering the data source."
type = object({
name = string
number = number
services_enabled = optional(list(string), [])
})
default = null
}
variable "aws_asn" {
description = "AWS ASN."
type = string
}
variable "aws_region" {
description = "AWS Region."
type = string
}
variable "aws_vpc_cidr_block" {
description = "CIDR block."
type = string
}
variable "gcp_asn" {
description = "Google ASN."
type = string
}
variable "gcp_region" {
description = "GCP Region."
type = string
}
variable "project_id" {
description = "Project ID."
type = string
}
variable "propagate_routes" {
description = "Flag indicating whether routed received by AWS's Virtual Private Gateway should be propagated to main route table."
type = bool
default = false
}
variable "shared_secret" {
description = "Shared secret."
type = string
}

View File

@@ -1,4 +1,4 @@
# Copyright 2024 Google LLC
# Copyright 2025 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -24,3 +24,4 @@ resource "time_static" "default" {}
resource "tls_private_key" "default" {}
resource "vsphere_role" "default" {}
resource "kubernetes_secret" "default" {}
resource "aws_vpc" "default" {}