概要
AWS-GCP間のVPNの接続をCloudFormation(AWS)とTerraform(GCP)を利用して構成する方法について紹介します。Terraformだけで構成したほうがきれいですが、普段AWSはCloudFormationで構成しているため統一性を持たせるためAWS側はCloudFormationを利用しました。
本記事で構築したVPNはすでに破棄済みです。
VPNの構成
以下の記事で紹介されている「高可用性(HA)VPN接続 パターン4」をTerraformとCloudFormationを利用して作成します。
https://dev.classmethod.jp/articles/aws_gcp_vpn/
最終形
AWS側(CloudFormation)
Parameters:
MainRouteTableID:
Type: 'String'
Default: 'rtb-xxxxxxxxxxxxxx'
IPCustomerGateway1:
Type: 'String'
Default: '35.242.58.51'
AllowedPattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$'
IPCustomerGateway2:
Type: 'String'
Default: '35.220.56.158'
AllowedPattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$'
Resources:
VGW:
Type: AWS::EC2::VPNGateway
Properties:
AmazonSideAsn: 64512
Type: ipsec.1
AttachVPNGateway1:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref 'EC2VPC'
VpnGatewayId: !Ref 'VGW'
EnableVGWPropagation1:
Type: AWS::EC2::VPNGatewayRoutePropagation
DependsOn: AttachVPNGateway1
Properties:
RouteTableIds:
- !Ref 'MainRouteTableID'
VpnGatewayId: !Ref 'VGW'
CustomerGateway1:
Type : AWS::EC2::CustomerGateway
Properties:
Type: "ipsec.1"
BgpAsn: "65000"
IpAddress: !Ref "IPCustomerGateway1"
Tags:
- Key: 'Name'
Value: !Sub ${AWS::StackName}-customer-gateway1
VPNConnection1:
Type: AWS::EC2::VPNConnection
Properties:
CustomerGatewayId: !Ref 'CustomerGateway1'
StaticRoutesOnly: false
Type: "ipsec.1"
VpnGatewayId: !Ref 'VGW'
Tags:
- Key: 'Name'
Value: !Sub ${AWS::StackName}-Connection1
AttachVPNGateway2:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref 'EC2VPC'
VpnGatewayId: !Ref 'VGW'
EnableVGWPropagation2:
Type: AWS::EC2::VPNGatewayRoutePropagation
DependsOn: AttachVPNGateway2
Properties:
RouteTableIds:
- !Ref 'MainRouteTableID'
VpnGatewayId: !Ref 'VGW'
CustomerGateway2:
Type : AWS::EC2::CustomerGateway
Properties:
Type: "ipsec.1"
BgpAsn: "65000"
IpAddress: !Ref "IPCustomerGateway2"
Tags:
- Key: 'Name'
Value: !Sub ${AWS::StackName}-customer-gateway2
VPNConnection2:
Type: AWS::EC2::VPNConnection
Properties:
CustomerGatewayId: !Ref 'CustomerGateway2'
StaticRoutesOnly: false
Type: "ipsec.1"
VpnGatewayId: !Ref 'VGW'
Tags:
- Key: 'Name'
Value: !Sub ${AWS::StackName}-Connection2
GCP側(Terraform)
variable "router_google_asn" {
default = 65000
}
variable "router_peer_asn" {
default = 64512
}
variable "aws_outside_ip_vgw1" {
default = "13.114.3.172"
}
variable "aws_outside_ip_vgw2" {
default = "52.199.177.210"
}
variable "aws_outside_ip_vgw3" {
default = "13.112.92.171"
}
variable "aws_outside_ip_vgw4" {
default = "18.182.165.1"
}
variable "aws_inside_ip_cgw1" {
default = "169.254.185.158"
}
variable "aws_inside_ip_cgw2" {
default = "169.254.41.166"
}
variable "aws_inside_ip_cgw3" {
default = "169.254.133.50"
}
variable "aws_inside_ip_cgw4" {
default = "169.254.187.230"
}
variable "aws_inside_ip_vgw1" {
default = "169.254.185.157"
}
variable "aws_inside_ip_vgw2" {
default = "169.254.41.165"
}
variable "aws_inside_ip_vgw3" {
default = "169.254.133.49"
}
variable "aws_inside_ip_vgw4" {
default = "169.254.187.229"
}
variable "pre_shared_key1" {
default = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
}
variable "pre_shared_key2" {
default = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
}
variable "pre_shared_key3" {
default = "cccccccccccccccccccccccccccccccc"
}
variable "pre_shared_key4" {
default = "dddddddddddddddddddddddddddddddd"
}
resource "google_compute_ha_vpn_gateway" "vpn_gateway" {
provider = google-beta
name = "vpn-gateway"
network = google_compute_network.vpc.id
region = "asia-northeast1"
}
resource "google_compute_router" "router" {
name = "router"
network = google_compute_network.vpc.id
bgp {
asn = var.router_google_asn
advertise_mode = "CUSTOM"
advertised_groups = ["ALL_SUBNETS"]
}
region = "asia-northeast1"
}
// 対AWS用のVPNゲートウェイの設定(AWS側の設定が完了後に設定)
resource "google_compute_external_vpn_gateway" "vpn_interface" {
provider = google-beta
name = "aws-gateway"
redundancy_type = "FOUR_IPS_REDUNDANCY"
interface {
id = 0
ip_address = var.aws_outside_ip_vgw1
}
interface {
id = 1
ip_address = var.aws_outside_ip_vgw2
}
interface {
id = 2
ip_address = var.aws_outside_ip_vgw3
}
interface {
id = 3
ip_address = var.aws_outside_ip_vgw4
}
}
resource "google_compute_vpn_tunnel" "vpn_tunnel1" {
provider = google-beta
name = "vpn-tunnel1"
shared_secret = var.pre_shared_key1
vpn_gateway = google_compute_ha_vpn_gateway.vpn_gateway.self_link
vpn_gateway_interface = 0
peer_external_gateway = google_compute_external_vpn_gateway.vpn_interface.self_link
peer_external_gateway_interface = 0
router = google_compute_router.router.name
ike_version = 1
region = "asia-northeast1"
}
resource "google_compute_router_interface" "interface1" {
provider = google-beta
name = "${google_compute_vpn_tunnel.vpn_tunnel1.name}-interface1"
router = google_compute_router.router.name
ip_range = "${var.aws_inside_ip_cgw1}/30"
vpn_tunnel = google_compute_vpn_tunnel.vpn_tunnel1.name
region = "asia-northeast1"
}
resource "google_compute_router_peer" "peer1" {
provider = google-beta
name = "${google_compute_vpn_tunnel.vpn_tunnel1.name}-peer1"
router = google_compute_router.router.name
peer_ip_address = var.aws_inside_ip_vgw1
peer_asn = var.router_peer_asn
interface = google_compute_router_interface.interface1.name
region = "asia-northeast1"
}
resource "google_compute_vpn_tunnel" "vpn_tunnel2" {
provider = google-beta
name = "vpn-tunnel2"
shared_secret = var.pre_shared_key2
vpn_gateway = google_compute_ha_vpn_gateway.vpn_gateway.self_link
vpn_gateway_interface = 0
peer_external_gateway = google_compute_external_vpn_gateway.vpn_interface.self_link
peer_external_gateway_interface = 1
router = google_compute_router.router.name
ike_version = 1
region = "asia-northeast1"
}
resource "google_compute_router_interface" "interface2" {
provider = google-beta
name = "${google_compute_vpn_tunnel.vpn_tunnel2.name}-interface2"
router = google_compute_router.router.name
ip_range = "${var.aws_inside_ip_cgw2}/30"
vpn_tunnel = google_compute_vpn_tunnel.vpn_tunnel2.name
region = "asia-northeast1"
}
resource "google_compute_router_peer" "peer2" {
provider = google-beta
name = "${google_compute_vpn_tunnel.vpn_tunnel2.name}-peer2"
router = google_compute_router.router.name
peer_ip_address = var.aws_inside_ip_vgw2
peer_asn = var.router_peer_asn
interface = google_compute_router_interface.interface2.name
region = "asia-northeast1"
}
resource "google_compute_vpn_tunnel" "vpn_tunnel3" {
provider = google-beta
name = "vpn-tunnel3"
shared_secret = var.pre_shared_key3
vpn_gateway = google_compute_ha_vpn_gateway.vpn_gateway.self_link
vpn_gateway_interface = 1
peer_external_gateway = google_compute_external_vpn_gateway.vpn_interface.self_link
peer_external_gateway_interface = 2
router = google_compute_router.router.name
ike_version = 1
region = "asia-northeast1"
}
resource "google_compute_router_interface" "interface3" {
provider = google-beta
name = "${google_compute_vpn_tunnel.vpn_tunnel3.name}-interface3"
router = google_compute_router.router.name
ip_range = "${var.aws_inside_ip_cgw3}/30"
vpn_tunnel = google_compute_vpn_tunnel.vpn_tunnel3.name
region = "asia-northeast1"
}
resource "google_compute_router_peer" "peer3" {
provider = google-beta
name = "${google_compute_vpn_tunnel.vpn_tunnel3.name}-peer3"
router = google_compute_router.router.name
peer_ip_address = var.aws_inside_ip_vgw3
peer_asn = var.router_peer_asn
interface = google_compute_router_interface.interface3.name
region = "asia-northeast1"
}
resource "google_compute_vpn_tunnel" "vpn_tunnel4" {
provider = google-beta
name = "vpn-tunnel4"
shared_secret = var.pre_shared_key4
vpn_gateway = google_compute_ha_vpn_gateway.vpn_gateway.self_link
vpn_gateway_interface = 1
peer_external_gateway = google_compute_external_vpn_gateway.vpn_interface.self_link
peer_external_gateway_interface = 3
router = google_compute_router.router.name
ike_version = 1
region = "asia-northeast1"
}
resource "google_compute_router_interface" "interface4" {
provider = google-beta
name = "${google_compute_vpn_tunnel.vpn_tunnel4.name}-interface4"
router = google_compute_router.router.name
ip_range = "${var.aws_inside_ip_cgw4}/30"
vpn_tunnel = google_compute_vpn_tunnel.vpn_tunnel4.name
region = "asia-northeast1"
}
resource "google_compute_router_peer" "peer4" {
provider = google-beta
name = "${google_compute_vpn_tunnel.vpn_tunnel4.name}-peer4"
router = google_compute_router.router.name
peer_ip_address = var.aws_inside_ip_vgw4
peer_asn = var.router_peer_asn
interface = google_compute_router_interface.interface4.name
region = "asia-northeast1"
}
反映手順
AWSリソースとGCPリソースに関してお互いに依存関係が存在するため、反映の順番を工夫する必要があります。以下でその反映の手順を紹介します。
1. GCPのVPN GatewayとCloud Routerを作成
最初にGCP側のVPN GatewayとCloud Routerを作成します。以下をterraform applyすることで作成可能です。
variable "router_google_asn" {
default = 65000
}
// VPC
resource "google_compute_network" "vpc" {
name = "vpc"
auto_create_subnetworks = false
routing_mode = "GLOBAL"
}
resource "google_compute_ha_vpn_gateway" "vpn_gateway" {
provider = google-beta
name = "vpn-gateway"
network = google_compute_network.vpc.id
region = "asia-northeast1"
}
resource "google_compute_router" "router" {
name = "router"
network = google_compute_network.vpc.id
bgp {
asn = var.router_google_asn
advertise_mode = "CUSTOM"
advertised_groups = ["ALL_SUBNETS"]
}
region = "asia-northeast1"
}
2. 作成されたVPN GatewayのGlobal IPをメモ
作られたVPN GatewayのIPを次のステップで利用するためメモします。
3. AWSリソースの作成
先程メモしたIPを以下のIPCustomerGateway1
と IPCustomerGateway2
にそれぞれ設定します。またMainRouteTableID
にVPCのメインルートテーブルを変数として設定します(自動取得する方法があったら教えて下さい)。なお、本記事ではVPNが作られている状態を前提として説明を進めます。
この状態で変更セットを作成しCFnを反映することでAWS側で必要なリソースを作成します。リソースに関しては以下のテンプレートをご参照ください。
Parameters:
MainRouteTableID:
Type: 'String'
Default: 'rtb-0115ded3e67eb5833'
IPCustomerGateway1:
Type: 'String'
Default: '35.242.58.51'
AllowedPattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$'
IPCustomerGateway2:
Type: 'String'
Default: '35.220.56.158'
AllowedPattern: '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$'
Resources:
VGW:
Type: AWS::EC2::VPNGateway
Properties:
AmazonSideAsn: 64512
Type: ipsec.1
AttachVPNGateway1:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref 'EC2VPC'
VpnGatewayId: !Ref 'VGW'
EnableVGWPropagation1:
Type: AWS::EC2::VPNGatewayRoutePropagation
DependsOn: AttachVPNGateway1
Properties:
RouteTableIds:
- !Ref 'MainRouteTableID'
VpnGatewayId: !Ref 'VGW'
CustomerGateway1:
Type : AWS::EC2::CustomerGateway
Properties:
Type: "ipsec.1"
BgpAsn: "65000"
IpAddress: !Ref "IPCustomerGateway1"
Tags:
- Key: 'Name'
Value: !Sub ${AWS::StackName}-customer-gateway1
VPNConnection1:
Type: AWS::EC2::VPNConnection
Properties:
CustomerGatewayId: !Ref 'CustomerGateway1'
StaticRoutesOnly: false
Type: "ipsec.1"
VpnGatewayId: !Ref 'VGW'
Tags:
- Key: 'Name'
Value: !Sub ${AWS::StackName}-Connection1
AttachVPNGateway2:
Type: AWS::EC2::VPCGatewayAttachment
Properties:
VpcId: !Ref 'EC2VPC'
VpnGatewayId: !Ref 'VGW'
EnableVGWPropagation2:
Type: AWS::EC2::VPNGatewayRoutePropagation
DependsOn: AttachVPNGateway2
Properties:
RouteTableIds:
- !Ref 'MainRouteTableID'
VpnGatewayId: !Ref 'VGW'
CustomerGateway2:
Type : AWS::EC2::CustomerGateway
Properties:
Type: "ipsec.1"
BgpAsn: "65000"
IpAddress: !Ref "IPCustomerGateway2"
Tags:
- Key: 'Name'
Value: !Sub ${AWS::StackName}-customer-gateway2
VPNConnection2:
Type: AWS::EC2::VPNConnection
Properties:
CustomerGatewayId: !Ref 'CustomerGateway2'
StaticRoutesOnly: false
Type: "ipsec.1"
VpnGatewayId: !Ref 'VGW'
Tags:
- Key: 'Name'
Value: !Sub ${AWS::StackName}-Connection2
4. 作成したVPN接続の設定情報をダウンロード
作成した2つのVPN接続の設定情報をダウンロードします。AWSのコンソールからVPC → サイト間のVPN接続の画面で作成したVPN接続を選択し設定のダウンロードからダウンロードします。ベンダーはGenericを選択します。
5. 設定情報から必要なものをGCPのTerraformに当てはめて反映
以下の対応表を元に、variables.tfの各変数を埋めます。1~4まで変数がありますが、それぞれの接続ごとに設定します。
aa | aa |
---|---|
aws_outside_ip_vgw | Outside IP Addresses → Customer Gateway |
aws_inside_ip_cgw | Inside IP Addresses → Customer Gateway |
aws_inside_ip_vgw | Inside IP Addresses → Virtual Private Gateway |
pre_shared_key | Pre-Shared Key |
変数がセットできたら、terraform applyでリソースを作成します。こちらについても作られるリソースはテンプレートをご参照ください。
variable "router_peer_asn" {
default = 64512
}
variable "aws_outside_ip_vgw1" {
default = "13.114.3.172"
}
variable "aws_outside_ip_vgw2" {
default = "52.199.177.210"
}
variable "aws_outside_ip_vgw3" {
default = "13.112.92.171"
}
variable "aws_outside_ip_vgw4" {
default = "18.182.165.1"
}
variable "aws_inside_ip_cgw1" {
default = "169.254.185.158"
}
variable "aws_inside_ip_cgw2" {
default = "169.254.41.166"
}
variable "aws_inside_ip_cgw3" {
default = "169.254.133.50"
}
variable "aws_inside_ip_cgw4" {
default = "169.254.187.230"
}
variable "aws_inside_ip_vgw1" {
default = "169.254.185.157"
}
variable "aws_inside_ip_vgw2" {
default = "169.254.41.165"
}
variable "aws_inside_ip_vgw3" {
default = "169.254.133.49"
}
variable "aws_inside_ip_vgw4" {
default = "169.254.187.229"
}
variable "pre_shared_key1" {
default = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
}
variable "pre_shared_key2" {
default = "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb"
}
variable "pre_shared_key3" {
default = "cccccccccccccccccccccccccccccccc"
}
variable "pre_shared_key4" {
default = "dddddddddddddddddddddddddddddddd"
}
resource "google_compute_external_vpn_gateway" "vpn_interface" {
provider = google-beta
name = "aws-gateway"
redundancy_type = "FOUR_IPS_REDUNDANCY"
interface {
id = 0
ip_address = var.aws_outside_ip_vgw1
}
interface {
id = 1
ip_address = var.aws_outside_ip_vgw2
}
interface {
id = 2
ip_address = var.aws_outside_ip_vgw3
}
interface {
id = 3
ip_address = var.aws_outside_ip_vgw4
}
}
resource "google_compute_vpn_tunnel" "vpn_tunnel1" {
provider = google-beta
name = "vpn-tunnel1"
shared_secret = var.pre_shared_key1
vpn_gateway = google_compute_ha_vpn_gateway.vpn_gateway.self_link
vpn_gateway_interface = 0
peer_external_gateway = google_compute_external_vpn_gateway.vpn_interface.self_link
peer_external_gateway_interface = 0
router = google_compute_router.router.name
ike_version = 1
region = "asia-northeast1"
}
resource "google_compute_router_interface" "interface1" {
provider = google-beta
name = "${google_compute_vpn_tunnel.vpn_tunnel1.name}-interface1"
router = google_compute_router.router.name
ip_range = "${var.aws_inside_ip_cgw1}/30"
vpn_tunnel = google_compute_vpn_tunnel.vpn_tunnel1.name
region = "asia-northeast1"
}
resource "google_compute_router_peer" "peer1" {
provider = google-beta
name = "${google_compute_vpn_tunnel.vpn_tunnel1.name}-peer1"
router = google_compute_router.router.name
peer_ip_address = var.aws_inside_ip_vgw1
peer_asn = var.router_peer_asn
interface = google_compute_router_interface.interface1.name
region = "asia-northeast1"
}
resource "google_compute_vpn_tunnel" "vpn_tunnel2" {
provider = google-beta
name = "vpn-tunnel2"
shared_secret = var.pre_shared_key2
vpn_gateway = google_compute_ha_vpn_gateway.vpn_gateway.self_link
vpn_gateway_interface = 0
peer_external_gateway = google_compute_external_vpn_gateway.vpn_interface.self_link
peer_external_gateway_interface = 1
router = google_compute_router.router.name
ike_version = 1
region = "asia-northeast1"
}
resource "google_compute_router_interface" "interface2" {
provider = google-beta
name = "${google_compute_vpn_tunnel.vpn_tunnel2.name}-interface2"
router = google_compute_router.router.name
ip_range = "${var.aws_inside_ip_cgw2}/30"
vpn_tunnel = google_compute_vpn_tunnel.vpn_tunnel2.name
region = "asia-northeast1"
}
resource "google_compute_router_peer" "peer2" {
provider = google-beta
name = "${google_compute_vpn_tunnel.vpn_tunnel2.name}-peer2"
router = google_compute_router.router.name
peer_ip_address = var.aws_inside_ip_vgw2
peer_asn = var.router_peer_asn
interface = google_compute_router_interface.interface2.name
region = "asia-northeast1"
}
resource "google_compute_vpn_tunnel" "vpn_tunnel3" {
provider = google-beta
name = "vpn-tunnel3"
shared_secret = var.pre_shared_key3
vpn_gateway = google_compute_ha_vpn_gateway.vpn_gateway.self_link
vpn_gateway_interface = 1
peer_external_gateway = google_compute_external_vpn_gateway.vpn_interface.self_link
peer_external_gateway_interface = 2
router = google_compute_router.router.name
ike_version = 1
region = "asia-northeast1"
}
resource "google_compute_router_interface" "interface3" {
provider = google-beta
name = "${google_compute_vpn_tunnel.vpn_tunnel3.name}-interface3"
router = google_compute_router.router.name
ip_range = "${var.aws_inside_ip_cgw3}/30"
vpn_tunnel = google_compute_vpn_tunnel.vpn_tunnel3.name
region = "asia-northeast1"
}
resource "google_compute_router_peer" "peer3" {
provider = google-beta
name = "${google_compute_vpn_tunnel.vpn_tunnel3.name}-peer3"
router = google_compute_router.router.name
peer_ip_address = var.aws_inside_ip_vgw3
peer_asn = var.router_peer_asn
interface = google_compute_router_interface.interface3.name
region = "asia-northeast1"
}
resource "google_compute_vpn_tunnel" "vpn_tunnel4" {
provider = google-beta
name = "vpn-tunnel4"
shared_secret = var.pre_shared_key4
vpn_gateway = google_compute_ha_vpn_gateway.vpn_gateway.self_link
vpn_gateway_interface = 1
peer_external_gateway = google_compute_external_vpn_gateway.vpn_interface.self_link
peer_external_gateway_interface = 3
router = google_compute_router.router.name
ike_version = 1
region = "asia-northeast1"
}
resource "google_compute_router_interface" "interface4" {
provider = google-beta
name = "${google_compute_vpn_tunnel.vpn_tunnel4.name}-interface4"
router = google_compute_router.router.name
ip_range = "${var.aws_inside_ip_cgw4}/30"
vpn_tunnel = google_compute_vpn_tunnel.vpn_tunnel4.name
region = "asia-northeast1"
}
resource "google_compute_router_peer" "peer4" {
provider = google-beta
name = "${google_compute_vpn_tunnel.vpn_tunnel4.name}-peer4"
router = google_compute_router.router.name
peer_ip_address = var.aws_inside_ip_vgw4
peer_asn = var.router_peer_asn
interface = google_compute_router_interface.interface4.name
region = "asia-northeast1"
}
6. 確認
GCP
反映後しばらく待って、Bgp session status
が BGP established
に変われば成功です。
AWS
また、AWSについてもVPN接続のTunnel Detailsのステータスが4つともアップになっていれば成功です。
まとめ
今回はAWSとGCPをつなげるVPNの設定をCloudFormationとTerraformで行う方法を紹介しました。コピペで使ってもらえれば幸いです。
参考
本構成は以下の記事を大変参考にさせていただきました。