AWSとGCPのVPN接続をTerraformでまとめる

2020.05.11

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

はじめに

データアナリティクス事業本部のkobayashiです。

AWSとGCPをセキュアな環境で接続してそれぞれのサービスを相互に利用できないか検証するためそれぞれのVPC間をVPNで接続してみました。

前回、前々回とでAWSとGCPの接続方法である「Classic VPN」「高可用性(HA)VPN」接続の方法をまとめましたが、AWSとGCPのリソースを扱うならTerraformで構成管理した方が便利なのでは?と考えたことと、今までTerraformは興味がありましたがなかなか触る機会がなかったのでその勉強も兼ねて簡単にリソースの構築と削除を行えるようにTerraformテンプレートを作成しましたのでその内容をまとめます。

前回、前々回の記事

環境

  • Terraform v0.12.24

接続パターン

Classic VPN接続

AWSでAWS VPN connectionを作成すると2つのIPSec tunnelが利用できます。 上のパターン(以降パターン1)ではそのうちの1本を利用するパターン、下のパターン(以降パターン2)は2本とも利用してフェイルオーバーに対応するパターンになります。

高可用性(HA)VPN接続

HA VPNの接続パターンになります。 上のパターン(以降パターン3)ではAWS、GCPのVPN connectionを冗長化しています。下のパターン(以降パターン4)ではパターン3で冗長化した上でさらにAWS VPN connectionのIPSec tunnelを全て利用しています。GCPが推奨する高可用性(HA)VPN接続方法となります。

パターン1とパターン4をTerraformテンプレートにしましたのでその説明をします。

Classic VPN接続(パターン1)のTerraformテンプレート

テンプレートファイルの構成

以下のようなファイルの構成となります。

gcp_credentioal.jsonはGCPのコンソール内のIAMと管理よりサービスアカウントを作成し、サービスアカウントの詳細より作成した秘密鍵のファイルになります。それ以外の*.tfファイルの説明を以降の項で説明します。

├── aws_vpn.tf
├── gcp_credentioal.json
├── gcp_vpn.tf
├── provider.tf
└── variables.tf

variables.tfの内容

AWS、GCPともにVPCの構築が終わっていてそれぞれのVPC間をVPNで接続する想定ですので、variables.tfvpc_idなどの値をまとめます。

variable "gcp_credential_filename" {
  default = "gcp_credentioal.json"
}
variable "gcp_project" {
  default = "{GCPのプロジェクト名}"
}
variable "gcp_network" {
  default = "{VPCネットワークの名前}"
}

variable "aws_access_key" {
  default = "{IAMユーザーのaccess_key}"
}
variable "aws_secret_key" {
  default = "{IAMユーザーのsecret_key}"
}
variable "aws_resname_prefix" {
  default = "{AWSのリソース名につけるPrefix:適当で}"
}
variable "aws_vpc_id" {
  default = "{VPC ID}"
}
variable "aws_vpc_route_table_id" {
  default = "{ルートテーブルID}"
}

provider.tfの内容

先に説明したvariables.tfの変数を利用してプロバイダー情報をまとめます。regionはAWS、GCPとも東京リージョンを指定してあります。

provider "google" {
  credentials = file(var.gcp_credential_filename)
  project = var.gcp_project
  region = "asia-northeast1"
}

provider "aws" {
  access_key = var.aws_access_key
  secret_key = var.aws_secret_key
  region = "ap-northeast-1"
}

gcp_vpn.tfの内容

GCPのリソースを定義して

  • 外部IPアドレスの作成
  • クラウドルーターの作成
  • VPNゲートウェイの作成
  • VPNトンネルの作成

を行います。

// 外部IPアドレスの設定
resource "google_compute_address" "cmk_vgw_address" {
  name = "aws-gcp-vgw"
}

// クラウドルーターの設定
resource "google_compute_router" "cmk_cloud_router" {
  name = "connect-aws-router"
  network = var.gcp_network
  bgp {
    asn = 65000
  }
}

// VPNゲートウェイの設定
resource "google_compute_vpn_gateway" "cmk_vgw" {
  name = "aws-gcp-vgw"
  network = var.gcp_network
}

// パケット転送ルールの設定(espパケット)
resource "google_compute_forwarding_rule" "fr_esp" {
  name = "aws-gcp-vgw-esp"
  ip_protocol = "ESP"
  ip_address = google_compute_address.cmk_vgw_address.address
  target = google_compute_vpn_gateway.cmk_vgw.self_link
}
// パケット転送ルールの設定(IKEパケット(UDP 500番))
resource "google_compute_forwarding_rule" "fr_udp500" {
  name = "aws-gcp-vgw-udp500"
  ip_protocol = "UDP"
  port_range = "500"
  ip_address = google_compute_address.cmk_vgw_address.address
  target = google_compute_vpn_gateway.cmk_vgw.self_link
}
// パケット転送ルールの設定(NAT-Tパケット(UDP 4500番))
resource "google_compute_forwarding_rule" "fr_udp4500" {
  name = "aws-gcp-vgw-udp4500"
  ip_protocol = "UDP"
  port_range = "4500"
  ip_address = google_compute_address.cmk_vgw_address.address
  target = google_compute_vpn_gateway.cmk_vgw.self_link
}

// VPNトンネルの設定
resource "google_compute_vpn_tunnel" "cmk_vpn_tunnel1" {
  name = "aws-gcp-tunnel1"
  peer_ip = aws_vpn_connection.cmk_vpnc.tunnel1_address
  shared_secret = aws_vpn_connection.cmk_vpnc.tunnel1_preshared_key

  target_vpn_gateway = google_compute_vpn_gateway.cmk_vgw.self_link
  router = google_compute_router.cmk_cloud_router.self_link
  ike_version = 1
  depends_on = [
    google_compute_forwarding_rule.fr_udp500,
    google_compute_forwarding_rule.fr_esp,
    google_compute_forwarding_rule.fr_udp4500,
  ]
}
// Cloud Routerインターフェースの設定
resource "google_compute_router_interface" "cmk_interface" {
  name = "${google_compute_vpn_tunnel.cmk_vpn_tunnel1.name}-interface"
  router = google_compute_router.cmk_cloud_router.name
  ip_range = "${aws_vpn_connection.cmk_vpnc.tunnel1_cgw_inside_address}/30"
  vpn_tunnel = google_compute_vpn_tunnel.cmk_vpn_tunnel1.name
}
// BGPピアリング用のBGP情報の設定
resource "google_compute_router_peer" "cmk_peer" {
  name = "${google_compute_vpn_tunnel.cmk_vpn_tunnel1.name}-peer"
  router = google_compute_router.cmk_cloud_router.name
  peer_ip_address = aws_vpn_connection.cmk_vpnc.tunnel1_vgw_inside_address
  peer_asn = aws_vpn_connection.cmk_vpnc.tunnel1_bgp_asn
  interface = google_compute_router_interface.cmk_interface.name
}

aws_vpn.tfの内容

AWSのリソースを定義して

  • 仮想プライベートゲートウェイの作成
  • クラウドルーターの作成
  • カスタマーゲートウェイの作成
  • サイト間のVPN接続の作成

を行います。

// 仮想プライベートゲートウェイの設定
resource "aws_vpn_gateway" "cmk_vgw" {
  vpc_id = var.aws_vpc_id
  tags = {
    Name = "${var.aws_resname_prefix}vgw"
  }
}

// 仮想プライベートゲートウェイのルート伝播の設定
resource "aws_vpn_gateway_route_propagation" "cmk_vgw_rp" {
  vpn_gateway_id = aws_vpn_gateway.cmk_vgw.id
  route_table_id = var.aws_vpc_route_table_id
}

// カスタマーゲートウェイの設定
resource "aws_customer_gateway" "cmk_cgw" {
  bgp_asn = 65000
  ip_address = google_compute_address.cmk_vgw_address.address
  type = "ipsec.1"

  tags = {
    Name = "${var.aws_resname_prefix}cgw"
  }
}

// サイト間のVPN接続の設定
resource "aws_vpn_connection" "cmk_vpnc" {
  vpn_gateway_id = aws_vpn_gateway.cmk_vgw.id
  customer_gateway_id = aws_customer_gateway.cmk_cgw.id
  type = "ipsec.1"

  tags = {
    Name = "${var.aws_resname_prefix}vpn-connection"
  }
}

これでテンプレートファイルの作成は終わったので後は

terraform plan

で内容を確認して問題がなければ、

terraform apply

でリソースを作成します。サイト間のVPN接続の作成に時間がかかる場合がありますが、おおよそ5分ほどで作成完了します。

高可用性(HA)VPN接続(パターン4)のTerraformテンプレート

テンプレートファイルの構成

Classic VPN接続(パターン1)のTerraformテンプレートと同じファイル構成で、variables.tfはパターン1と同じ内容なので割愛します。

provider.tfの内容

先に説明したvariables.tfの変数を利用してプロバイダー情報をまとめます。regionはAWS、GCPとも東京リージョンを指定してあります。

パターン1と唯一異なるのがproviderで、GCPのリソースを記述する際にgoogle-betaプロバイダーで記述する必要があるため、provider "google"の部分をprovider "google-beta"に書き換えます。

provider "google-beta" {
  credentials = file(var.gcp_credential_filename)
  project = var.gcp_project
  region = "asia-northeast1"
}

provider "aws" {
  access_key = var.aws_access_key
  secret_key = var.aws_secret_key
  region = "ap-northeast-1"
}

gcp_vpn.tfの内容

GCPのリソースを定義して

  • 高可用性(HA)VPN用のゲートウェイの作成
  • クラウドルーターの作成
  • 4つのインターフェースを持つVPNゲートウェイの作成
  • 4つのVPNトンネルの作成

を行います。

// 高可用性(HA)VPN用のゲートウェイの設定
resource "google_compute_ha_vpn_gateway" "cmk_ha_gateway" {
  provider = google-beta
  name = "ha-vpn-1"
  network = "https://www.googleapis.com/compute/v1/projects/${var.gcp_project}/global/networks/${var.gcp_network}"
}

// クラウドルーターの設定
resource "google_compute_router" "cmk_cloud_router" {
  provider = google-beta
  name = "ha-vpn-router1"
  network = var.gcp_network
  bgp {
    asn = 65000
  }
}

// 対AWS用のVPNゲートウェイの設定
// 4つのVPNトンネルを作成するのでinterfaceを4つ定義する
resource "google_compute_external_vpn_gateway" "cmk_external_vgw" {
  provider = google-beta
  name = "aws-gateway"
  redundancy_type = "FOUR_IPS_REDUNDANCY"
  interface {
    id = 0
    ip_address = aws_vpn_connection.cmk_vpnc1.tunnel1_address
  }
  interface {
    id = 1
    ip_address = aws_vpn_connection.cmk_vpnc1.tunnel2_address
  }
  interface {
    id = 2
    ip_address = aws_vpn_connection.cmk_vpnc2.tunnel1_address
  }
  interface {
    id = 3
    ip_address = aws_vpn_connection.cmk_vpnc2.tunnel2_address
  }
}

// VPNトンネル1の設定
// VPNトンネルの接続設定(トンネル1用)
resource "google_compute_vpn_tunnel" "cmk_vpn_tunnel1" {
  provider = google-beta
  name = "ha-vpn-tunnel1"
  shared_secret = aws_vpn_connection.cmk_vpnc1.tunnel1_preshared_key
  vpn_gateway = google_compute_ha_vpn_gateway.cmk_ha_gateway.self_link
  vpn_gateway_interface = 0
  peer_external_gateway = google_compute_external_vpn_gateway.cmk_external_vgw.self_link
  peer_external_gateway_interface = 0
  router = google_compute_router.cmk_cloud_router.name
  ike_version = 1
}
// Cloud Routerインターフェースの設定(トンネル1用)
resource "google_compute_router_interface" "cmk_interface1" {
  provider = google-beta
  name = "${google_compute_vpn_tunnel.cmk_vpn_tunnel1.name}-interface1"
  router = google_compute_router.cmk_cloud_router.name
  ip_range = "${aws_vpn_connection.cmk_vpnc1.tunnel1_cgw_inside_address}/30"
  vpn_tunnel = google_compute_vpn_tunnel.cmk_vpn_tunnel1.name
}
// BGPピアリング用のBGP情報の設定(トンネル1用)
resource "google_compute_router_peer" "cmk_peer1" {
  provider = google-beta
  name = "${google_compute_vpn_tunnel.cmk_vpn_tunnel1.name}-peer1"
  router = google_compute_router.cmk_cloud_router.name
  peer_ip_address = aws_vpn_connection.cmk_vpnc1.tunnel1_vgw_inside_address
  peer_asn = aws_vpn_connection.cmk_vpnc1.tunnel1_bgp_asn
  interface = google_compute_router_interface.cmk_interface1.name
}

// VPNトンネル2の設定
// VPNトンネルの接続設定(トンネル2用)
resource "google_compute_vpn_tunnel" "cmk_vpn_tunnel2" {
  provider = google-beta
  name = "ha-vpn-tunnel2"
  shared_secret = aws_vpn_connection.cmk_vpnc1.tunnel2_preshared_key
  vpn_gateway = google_compute_ha_vpn_gateway.cmk_ha_gateway.self_link
  vpn_gateway_interface = 0
  peer_external_gateway = google_compute_external_vpn_gateway.cmk_external_vgw.self_link
  peer_external_gateway_interface = 1
  router = google_compute_router.cmk_cloud_router.name
  ike_version = 1
}
// Cloud Routerインターフェースの設定(トンネル2用)
resource "google_compute_router_interface" "cmk_interface2" {
  provider = google-beta
  name = "${google_compute_vpn_tunnel.cmk_vpn_tunnel2.name}-interface2"
  router = google_compute_router.cmk_cloud_router.name
  ip_range = "${aws_vpn_connection.cmk_vpnc1.tunnel2_cgw_inside_address}/30"
  vpn_tunnel = google_compute_vpn_tunnel.cmk_vpn_tunnel2.name
}
// BGPピアリング用のBGP情報の設定(トンネル2用)
resource "google_compute_router_peer" "cmk_peer2" {
  provider = google-beta
  name = "${google_compute_vpn_tunnel.cmk_vpn_tunnel2.name}-peer2"
  router = google_compute_router.cmk_cloud_router.name
  peer_ip_address = aws_vpn_connection.cmk_vpnc1.tunnel2_vgw_inside_address
  peer_asn = aws_vpn_connection.cmk_vpnc1.tunnel2_bgp_asn
  interface = google_compute_router_interface.cmk_interface2.name
}

// VPNトンネル3の設定
// VPNトンネルの接続設定(トンネル3用)
resource "google_compute_vpn_tunnel" "cmk_vpn_tunnel3" {
  provider = google-beta
  name = "ha-vpn-tunnel3"
  shared_secret = aws_vpn_connection.cmk_vpnc2.tunnel1_preshared_key
  vpn_gateway = google_compute_ha_vpn_gateway.cmk_ha_gateway.self_link
  vpn_gateway_interface = 1
  peer_external_gateway = google_compute_external_vpn_gateway.cmk_external_vgw.self_link
  peer_external_gateway_interface = 2
  router = google_compute_router.cmk_cloud_router.name
  ike_version = 1
}
// Cloud Routerインターフェースの設定(トンネル3用)
resource "google_compute_router_interface" "cmk_interface3" {
  provider = google-beta
  name = "${google_compute_vpn_tunnel.cmk_vpn_tunnel3.name}-interface3"
  router = google_compute_router.cmk_cloud_router.name
  ip_range = "${aws_vpn_connection.cmk_vpnc2.tunnel1_cgw_inside_address}/30"
  vpn_tunnel = google_compute_vpn_tunnel.cmk_vpn_tunnel3.name
}
// BGPピアリング用のBGP情報の設定(トンネル3用)
resource "google_compute_router_peer" "cmk_peer3" {
  provider = google-beta
  name = "${google_compute_vpn_tunnel.cmk_vpn_tunnel3.name}-peer3"
  router = google_compute_router.cmk_cloud_router.name
  peer_ip_address = aws_vpn_connection.cmk_vpnc2.tunnel1_vgw_inside_address
  peer_asn = aws_vpn_connection.cmk_vpnc2.tunnel1_bgp_asn
  interface = google_compute_router_interface.cmk_interface3.name
}

// VPNトンネル4の設定
// VPNトンネルの接続設定(トンネル4用)
resource "google_compute_vpn_tunnel" "cmk_vpn_tunnel4" {
  provider = google-beta
  name = "ha-vpn-tunnel4"
  shared_secret = aws_vpn_connection.cmk_vpnc2.tunnel2_preshared_key
  vpn_gateway = google_compute_ha_vpn_gateway.cmk_ha_gateway.self_link
  vpn_gateway_interface = 1
  peer_external_gateway = google_compute_external_vpn_gateway.cmk_external_vgw.self_link
  peer_external_gateway_interface = 3
  router = google_compute_router.cmk_cloud_router.name
  ike_version = 1
}
// Cloud Routerインターフェースの設定(トンネル4用)
resource "google_compute_router_interface" "cmk_interface4" {
  provider = google-beta
  name = "${google_compute_vpn_tunnel.cmk_vpn_tunnel4.name}-interface4"
  router = google_compute_router.cmk_cloud_router.name
  ip_range = "${aws_vpn_connection.cmk_vpnc2.tunnel2_cgw_inside_address}/30"
  vpn_tunnel = google_compute_vpn_tunnel.cmk_vpn_tunnel4.name
}
// BGPピアリング用のBGP情報の設定(トンネル4用)
resource "google_compute_router_peer" "cmk_peer4" {
  provider = google-beta
  name = "${google_compute_vpn_tunnel.cmk_vpn_tunnel4.name}-peer4"
  router = google_compute_router.cmk_cloud_router.name
  peer_ip_address = aws_vpn_connection.cmk_vpnc2.tunnel2_vgw_inside_address
  peer_asn = aws_vpn_connection.cmk_vpnc2.tunnel2_bgp_asn
  interface = google_compute_router_interface.cmk_interface4.name
}

aws_vpn.tfの内容

AWSのリソースを定義して

  • 仮想プライベートゲートウェイの作成
  • クラウドルーターの作成
  • 2つのカスタマーゲートウェイの作成
  • 2つのサイト間のVPN接続の作成

を行います。

// 仮想プライベートゲートウェイの設定
resource "aws_vpn_gateway" "cmk_vgw" {
  vpc_id = var.aws_vpc_id
  tags = {
    Name = "${var.aws_resname_prefix}vgw"
  }
}

// 仮想プライベートゲートウェイのルート伝播の設定
resource "aws_vpn_gateway_route_propagation" "cmk_vgw_rp" {
  vpn_gateway_id = aws_vpn_gateway.cmk_vgw.id
  route_table_id = var.aws_vpc_route_table_id
}

// 1つ目のカスタマーゲートウェイの設定
resource "aws_customer_gateway" "cmk_cgw1" {
  bgp_asn = 65000
  ip_address = google_compute_ha_vpn_gateway.cmk_ha_gateway.vpn_interfaces[0].ip_address
  type = "ipsec.1"

  tags = {
    Name = "${var.aws_resname_prefix}cgw"
  }
}
// 1つ目のサイト間のVPN接続の設定
resource "aws_vpn_connection" "cmk_vpnc1" {
  vpn_gateway_id = aws_vpn_gateway.cmk_vgw.id
  customer_gateway_id = aws_customer_gateway.cmk_cgw1.id
  type = "ipsec.1"

  tags = {
    Name = "${var.aws_resname_prefix}vpn-connection"
  }
}

// 2つ目のカスタマーゲートウェイの設定
resource "aws_customer_gateway" "cmk_cgw2" {
  bgp_asn = 65000
  ip_address = google_compute_ha_vpn_gateway.cmk_ha_gateway.vpn_interfaces[1].ip_address
  type = "ipsec.1"

  tags = {
    Name = "${var.aws_resname_prefix}cgw"
  }
}
// 2つ目のサイト間のVPN接続の設定
resource "aws_vpn_connection" "cmk_vpnc2" {
  vpn_gateway_id = aws_vpn_gateway.cmk_vgw.id
  customer_gateway_id = aws_customer_gateway.cmk_cgw2.id
  type = "ipsec.1"

  tags = {
    Name = "${var.aws_resname_prefix}vpn-connection"
  }
}

以上でテンプレートファイル群の作成は終わりです。

こちらもリソースの作成まで5分ほどかかります。

まとめ

当初の目的はAWS/GCPリソースの構成管理を簡単にして手軽にAWS-GCPの環境を作ることでしたが、Terraformテンプレートファイルを作成するにあたりTerraformをイチから調べてテンプレートファイルを作成しました。Terraformは記述方法もそれほど難しくなくマルチクラウドに対応できるので今後はリソース管理をTerrarormで行っていこうと思います。

最後まで読んで頂いてありがとうございました。