TerraformでAzureとAWS間でインターネットVPNを張る

前回の記事を書いた段階から構想としてあった、AzureとAWS間でのインターネットVPN、しかもTerraformで完全コード化したものを公開します。
GUIだと楽勝なんで簡単に考えていたんですが、コード化するとなると中々手強かったです。
お盆前には公開できる準備は整っていたのですが、子どもの夏休みの宿題と自身の体調不良で時間が空いてしまいました。
体調不良は置いといて、夏休みの宿題、面白いですね。
大人になってから見てみるととても感慨深いです。
今は読書感想文のほかに、映画鑑賞による感想文があり、うちの子はこの映画鑑賞の感想文を選んでいました。
私も小学校５年生あたりで夏休みの宿題とは別で新聞社がやってる作文コンクールに応募して佳作だったか努力賞だったかの賞をもらったことがあります。
父に何度も校正されて、子どもの頃の生意気な私なら途中で投げ出していたと思うのですが、なぜかとても楽しく、書きたいことがいっぱいあるタイプの人間なので文字数制限内に収めるためいろいろな表現を学ぶ良い経験になったので、子どもにも勧めました。
**いや、作文コンクール、読書感想文、映画の感想文、よく考えられた仕組みですよね。**
文字数制限ってなんであるの？って子供心ながら思ってたんですが、やっぱり要約する力や語彙力を高めるためなんですかね？
宿題を見る側になって改めて気付きました。
**元々日本には俳句みたいに字数制限のある文化が根付いているのもあり、ある意味縛りプレイが好きなのかもしれませんね。**
子どもの感想文も良くかけていて、私と同じタイプなのか文字数制限に収めるため四苦八苦していました。
「書きたいことを如何に絞るか、如何に少ない字数で読み手に映画の面白さを伝えるか」が目的であると子どもに伝えつつ、この目的を達成するために要約する力と語彙力を学ぶんだよ、と教えました。
日本語の様々な表現に触れる良い機会になったかなと思います。
他にも書き方として演繹法や帰納法というワードも含めて子どもに伝えていますが、まだそこまで意識する世代でもないかなというのと、あまり情報を与えすぎで子どもの表現する自由を制限するのも良くないかなと思い私自身も子どもに伝える情報を取捨選択しました。

字数制限についてのメリットは理解している**つもり**ですが、あくまで**つもり**
なので、**私はQiitaでは字数なんて気にせず言いたいことを言いたいだけ書いちゃいます。**
ダメな親だ・・・

# 構成図
はい。
今回も与太話が長くなってしまいましたが構成図です。

![構成図_総合.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/67dbb2e2-f250-1e51-1198-6ac47f713223.png)
今回はちょっと横長になっちゃいましたが、こんな感じです。
Azure、AWS、双方NAT Gatewayを作っても良かったんですが、<font color="Red">今回も例に漏れず**自腹**なので</font>節約の意味からもNAT Gatewayは作りませんでした。
お金以外にもNAT Gatewayの有無は本件の意図するところとそこまで大きな影響は無いので省略でも問題ないですね。
構成をいたずらに複雑にする意味もありませんし。

# 今回こだわったところ
本記事の構成は冒頭でも書きましたがGUIだと楽勝です。
**今回こだわった部分は、Azure、AWS、2つの環境を別々でコード実行して最終的にVPNを張るのではなく、<font color="Red">1回のコード実行でVPNを張る部分までノンストップで完遂すること**</font>です。
GUIではどうしてもAzureかAWSかで先に環境を作成し、その後他方のクラウドキャリアで先に作成した環境に合わせて構築を行うという順序にならざるを得ませんが、これが面倒なので1回のコード実行、具体的には```terraform apply```でAzure、AWS双方の環境を構築することにこだわりました。

# 今回難しかったところ
コードの紹介しようかなと思ったんですが、演繹法的に本論前半で難しかった部分を記載します。

## AzureとAWS両方のProvider情報を併記する
**今回こだわった部分は、先にも記載の通りAzure、AWS、2つの環境を別々でコード実行して最終的にVPNを張るのではなく、<font color="Red">1回のコード実行でVPNを張る部分までノンストップで完遂すること**</font>です。
なのでProvider情報をAzure、AWSそれぞれで分けて記載していた部分、具体的には```main.tf```を1つにまとめなければなりませんでした。
これが難しかったです。

## AzureとAWS、両方で同じ変数名を使っていたので、これをそれぞれ一意の変数名に再命名しなければならない
これも<font color="Red">**1回のコード実行でAzure-AWS間のVPN接続まで完遂することにこだわると当たり前**</font>なのですが、まぁまぁめんどくさかったです。
Resource GroupみたいにAzureにしか存在しない設定値(≒変数)や、NSG、Security Groupのように同じような役割でもリソース名が違うので変数名が分かれているものは楽なんですが、**サブネット名、サブネットのIPアドレス帯Suffix(第3オクテットと第4オクテット)、リージョン**のようにAzure、AWSに共通する考え方、かつ必須の設定は運悪く、というかある意味当然なのですが変数名が重複していました。
これを再命名し、関連するコード部分をすべて変更するのが手間でした。

## AWSのVPN接続リソース(AWS VPN Connection)で出力されるAWSのVPNゲートウェイのパブリックIPアドレスを変数として受け取り、AzureのLocal GatewayのパブリックIPアドレスとして処理する
本記事ではこれに一番時間がかかりました。
Terraform公式にも直接的に言及している記載はなく、Try＆Errorを繰り返しました。
**<font color="Red">AzureのVPN Gatewayが構築に最大30分かかるという制約があることから、このTry＆Errorにも時間がかかり、かつお金もかかりました。</font>**
実際に検証していた際30分はかかりませんでしたが、それでも15分程度は毎回時間がかかるので、Azure VPN Gateway構築の待ち時間にAWS側の課金がかかるのがちょと嫌でした。
さすがにこのTry＆Error部分ではAzure VMやAWS EC2の構築は無視して、Azure Virtual Gateway、AWS 仮想プライベートゲートウェイ、Azure Local Gateway、AWS Customer Gatewayの作成のみに絞って検証を行いました。

# Terraform
ではこだわりポイントや難しかった部分も説明できたので、コードのご紹介です。

AzureとAWS両方のコードを記載していきますが、変数設定やmain.tfのような共通項目以外はそれぞれのコードを記載したファイルにどちらのクラウドキャリアの設定なのかプレフィックスを付けました。
いつもなるべく環境の外側からコードを記載しているので、今回もこの順で、かつアルファベット順でコードを紹介します。

## 変数設定
```varriables.tf
# ---------------------------
# AWS Variables - 変数設定
# ---------------------------

# region
# ap-northeast-1 東京リージョン
# ap-south-1 ムンバイリージョン
variable "aws_region" {
  default = "ap-south-1"
}

# 環境種別(本番:prd,ステージング:stg,開発:dev)
variable "env_type" {
  default = "dev"
}

# システム名
variable "sys_name" {
  default = "aws-azure-vpn01"
}

# availability_zone
variable "availability_zone" {
  type = object({
    a = string
    b = string #ムンバイリージョンで利用
    c = string
  })
  default = {
    a = "ap-south-1a" # ムンバイ(インド)のアベイラビリティゾーン
    b = "ap-south-1b" # ムンバイ(インド)のアベイラビリティゾーン
    c = "ap-south-1c" # ムンバイ(インド)のアベイラビリティゾーン
  }
}

# ------------------------------
# VPC 関連
# ------------------------------

# vpc address
variable "vpc_address_pre" {
  default = "10.0."
}

variable "vpc_address_suf" {
  default = "0.0/23"
}

# private subnet suffix address01
variable "private_sn_address_suf01" {
  default = "1.0/24"
}

# public subnet suffix address01
variable "public_sn_address_suf01" {
  default = "0.0/24"
}

# VPC Endpoint
variable "vpc_endpoints" {
  type    = list(any)
  default = ["ssm", "ssmmessages", "ec2messages"]
}

# ------------------------------
# EC2 作成
# ------------------------------

# Linux EC2のインスタンスタイプ
variable "ec2_instance_type_lin" {
    default = "t2.micro"
}

# Windows EC2のインスタンスタイプ
variable "ec2_instance_type_win" {
    default = "m4.large"
}

# ---------------------------
# Azure Variables - 変数設定
# ---------------------------

# Import Subscription ID variable from OS's Enviroment variable
variable "ARM_SUBSCRIPTION_ID" {
  type        = string
}

# Import Tenant ID variable from OS's Enviroment variable
variable "ARM_TENANT_ID" {
  type        = string
}

# Import Client ID variable from OS's Enviroment variable
variable "ARM_CLIENT_ID" {
  type        = string
}

# Import Client Secret variable from OS's Enviroment variable
variable "ARM_CLIENT_SECRET" {
  type        = string
}

# Generate random text for a unique storage account name
resource "random_id" "random_id" {
  keepers = {
    # Generate a new ID only when a new resource group is defined
    resource_group = azurerm_resource_group.rg.name
  }
  byte_length = 8
}

# system name
variable "system_name" {
  type        = string
  default     = "azure-aws-vpn01"
}

# region
variable "azure_region" {
  type        = string
  default     = "southeastasia"
}

# resource group (rg) name prefix
variable "rg_name_pre" {
  type        = string
  default     = "rg-"
}

# virtual network (vnet) name prefix
variable "vnet_name_pre" {
  type        = string
  default     = "vnet-"
}

# virtual network (vnet) name suffix for client
variable "vnet_name_suf01" {
  type        = string
  default     = "azure-aws-vpn01"
}

# vnet ip address prefix01 for client
variable "vnet_addr_pre01" {
  type        = string
  default     = "192.168."
}

# vnet ip address suffix for client
variable "vnet_addr_suf01" {
  type        = string
  default     = "200.0/23"
}

# subnet (sn) name prefix
variable "sn_name_pre" {
  type        = string
  default     = "sn-"
}

# sn for client name suffix for client
variable "sn_name_suf01" {
  type        = string
  default     = "client01"
}

# sn for clinet ip address suffix for client
variable "sn_addr_suf01" {
  type        = string
  default     = "201.0/24"
}

# sn for clinet ip address suffix for Virtual Network Gateway
variable "sn_addr_suf02" {
  type        = string
  default     = "200.0/24"
}

# Network Interface Card (nic) name prefix
variable "nic_name_pre" {
  type        = string
  default     = "nic-"  
}

# Network Security Group (nsg) name prefix
variable "nsg_name_pre" {
  type        = string
  default     = "nsg-"  
}

# Public IP Address (pip) prefix
variable "pip_name_pre" {
  type        = string
  default     = "pip-"
}

# IP Configuration (ipconf) suffix
variable "ipconf_suf" {
  type        = string
  default     = "-configuration"
}

# Private IP Address Allocation (priv_ip_alloc)
variable "priv_ip_alloc" {
  type        = string
  default     = "Dynamic" # Dynamic or Static
}

# admin user (adminuser) name
variable "adminuser" {
  type        = string
  default     = "init-Admin001"
}

# Virtual Machine OS Disk (osdisk) name prefix
variable "vm_osdisk_pre" {
  type        = string
  default     = "osdisk-"
}

# Virtual Machine OS Disk SKU (osdisk_sku)
variable "vm_osdisk_sku" {
  type        = string
  default     = "StandardSSD_LRS"
}

# Virtual Machine SKU (vmsku) name
variable "vm_sku" {
  type        = string
  default     = "Standard_D4_v5"
}

# Virtual Machine (vm) name prefix
variable "vm_name_pre" {
  type        = string
  default     = "vm-"
}

# vm name suffix01
variable "vm_name_suf01" {
  type        = string
  default     = "win11-01"
}

# vm name suffix02
variable "vm_name_suf02" {
  type        = string
  default     = "alma-01"
}

# Virtual Machine OS Publisher (os_pub) name
# "MicrosoftWindowsDesktop" is Client OS 
# "MicrosoftWindowsServer" is Server OS
# "Canonical" is Ubuntu
# "almalinux" is Almalinux
variable "os_pub01" {
  type        = string
  default     = "MicrosoftWindowsDesktop"
}

variable "os_pub02" {
  type        = string
  default     = "almalinux"
}

# Virtual Machine OS Offer (os_offer) name
# "Windows-10" is Windows 10 
# "Windows-11" is Windows 11
# "WindowsServer" os Windows Server OS
# "0001-com-ubuntu-server-jammy" is Ubuntu
# "almalinux-x86_64" is Almalinux
variable "os_offer01" {
  type        = string
  default     = "Windows-11"
}

variable "os_offer02" {
  type        = string
  default     = "almalinux-x86_64"
}

# Virtual Machine OS (os_sku) name
# 2016-Datacenter
# 2019-Datacenter
# 2022-datacenter-azure-edition
# 22_04-lts-gen2 (Ubuntu 22.04 LTS)
# 9-gen2 (Almalinux 9)
variable "os_sku01" {
  type        = string
  default     = "win11-22h2-pro"
}

variable "os_sku02" {
  type        = string
  default     = "9-gen2"
}

# Virtual Machine OS Version (os_ver) name
variable "os_ver" {
  type        = string
  default     = "latest"
}

# Local Gateway name prefix
variable "LG_name_pre" {
  type        = string
  default     = "lg-"
}

# Local Gateway name suffix
variable "LG_name_suf" {
  type        = string
  default     = "aws01"
}

# VPN connection name prefix
variable "connection_name_pre" {
  type        = string
  default     = "con-"
}
```
<font color="Red">努力の跡は見ていただけたでしょうか？</font>
今まで設定していた変数名を変更しています。
変数設定の項目はAzureの方がぶっちぎりで多いですね。
私自身がAzureの方がAWSより詳しいので、Azureの方をより詳細に設定できる、というのが大きな要因の気もしますが(ようは私がAWSにあまり詳しくない)これが実際に作成されるリソース数と比較したらどうなるかも動作確認の項目で確認しましょう。

## main.tf
```main.tf
# ---------------------------
# main
# ---------------------------

terraform {
  required_version = ">= 1.4" # Terraformのバージョンを1.4以上に指定
  required_providers {
# AWSのProvider情報
    aws = {
      source  = "hashicorp/aws"
      version = "= 5.22.0" # AWSプロバイダのバージョンを5.22.0に固定
    }
# AzureのProvider情報
    azapi = {
      source  = "azure/azapi"
      version = "~>1.5"
    }
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~>3.0"
    }
    random = {
      source  = "hashicorp/random"
      version = "~>3.0"
    }
  }
}

# プロバイダー設定
provider "aws" {
  region = var.aws_region
}

provider "azapi" {
}

provider "azurerm" {
  features {}
  subscription_id   = "${var.ARM_SUBSCRIPTION_ID}"
  tenant_id         = "${var.ARM_TENANT_ID}"
  client_id         = "${var.ARM_CLIENT_ID}"
  client_secret     = "${var.ARM_CLIENT_SECRET}"
}
```
はい。
<font color="Red">ここでも努力の跡は見ていただけたでしょうか？</font>
[今回難しかったところ](https://qiita.com/hidekko/items/374e65d30a726c87899c#%E4%BB%8A%E5%9B%9E%E9%9B%A3%E3%81%97%E3%81%8B%E3%81%A3%E3%81%9F%E3%81%A8%E3%81%93%E3%82%8D)でも挙げていますが、このmain.tfがそもそもこの記述方法でちゃんと動くのか全然わからないところからのスタートでした。
ちゃんと動いたので良かったですが、動かなかったらその時点で本記事は詰みなんですよね。

## aws_iam.tf
```aws_iam.tf
# ---------------------------
# IAM
# ---------------------------

# IAM ROLE 定義
resource "aws_iam_role" "ec2_role" {
  name               = "${var.env_type}-${var.sys_name}-iam-role"
  assume_role_policy = data.aws_iam_policy_document.ec2_assume_role.json
}

# IAMポリシー定義
data "aws_iam_policy_document" "ec2_assume_role" {
  statement {
    actions = ["sts:AssumeRole"]

    principals {
      type        = "Service"
      identifiers = ["ec2.amazonaws.com"]
    }
  }
}

# IAM ROLEのインスタンスプロフィールの作成
resource "aws_iam_instance_profile" "instance_prof" {
  name = "${var.env_type}-${var.sys_name}-instance-profile-ssm"
  role = aws_iam_role.ec2_role.name
}

# ---------------------------
# FOR SSM
# ---------------------------
# SSM用ポリシーをEC2ロールに設定
resource "aws_iam_role_policy_attachment" "ssm_control" {
  role       = aws_iam_role.ec2_role.name
  policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
}
```
はい。
安定の```aws_iam.tf```ですね。
SSMを実装します。

## aws_vpc.tf
```aws_vpc.tf
# ---------------------------
# VPC 構築
# ---------------------------

# VPC
resource "aws_vpc" "vpc" {
  cidr_block           = "${var.vpc_address_pre}${var.vpc_address_suf}"
  enable_dns_hostnames = true
  enable_dns_support   = true
  tags = {
    Name = "${var.env_type}-${var.sys_name}-vpc"
  }
}

# ---------------------------
# Public Subnet 構築
# ---------------------------

resource "aws_subnet" "sn_public_1a" {
  vpc_id            = aws_vpc.vpc.id
  cidr_block        = "${var.vpc_address_pre}${var.public_sn_address_suf01}"
  availability_zone = var.availability_zone.a
  tags = {
    Name = "${var.env_type}-${var.sys_name}-sn-public-1a"
  }
}

# ---------------------------
# Private Subnet 構築
# ---------------------------

resource "aws_subnet" "sn_private_1b" {
  vpc_id            = aws_vpc.vpc.id
  cidr_block        = "${var.vpc_address_pre}${var.private_sn_address_suf01}"
  availability_zone = var.availability_zone.b
  tags = {
    Name = "${var.env_type}-${var.sys_name}-sn-private-1b"
  }
}

# ---------------------------
# Internet Gateway 作成
# ---------------------------
resource "aws_internet_gateway" "igw" {
  vpc_id = aws_vpc.vpc.id
  tags = {
    Name = "${var.env_type}-${var.sys_name}-igw"
  }
}

# ---------------------------
# Route table 作成
# ---------------------------

# Public Subnet 用Route Table 作成
resource "aws_route_table" "rt_sn_public_1a" {
  vpc_id = aws_vpc.vpc.id
  # Default GatewayをInternet Gatewayに向ける
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.igw.id
  }
  tags = {
    Name = "${var.env_type}-${var.sys_name}-rt-sn-public-1a"
  }
}

# Private Subnet 用Route Table 作成
resource "aws_route_table" "rt_sn_private_1b" {
  vpc_id = aws_vpc.vpc.id
  # Default GatewayをInternet Gatewayに向ける
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.igw.id
  }
  route {
    cidr_block = "${var.vnet_addr_pre01}${var.vnet_addr_suf01}"
    gateway_id = aws_vpn_gateway.vgw.id
  }
  tags = {
    Name = "${var.env_type}-${var.sys_name}-rt-sn-private-1b"
  }
}

# Public Subnet とDefault Route table の関連付け
resource "aws_route_table_association" "associate_rt_sn_public_1a_sn_public_1a" {
  subnet_id      = aws_subnet.sn_public_1a.id
  route_table_id = aws_route_table.rt_sn_public_1a.id
}

# Private Subnet とDefault Route table の関連付け
resource "aws_route_table_association" "associate_rt_sn_private_1b_sn_private_1b" {
  subnet_id      = aws_subnet.sn_private_1b.id
  route_table_id = aws_route_table.rt_sn_private_1b.id
}
```
VPCですね。
大した設定はしていません。

## aws_vpc_endpoint.tf
```aws_vpc_endpoint.tf
# ---------------------------
# VPC Endpoint
# ---------------------------

# SSM用 VPC endpointの作成
resource "aws_vpc_endpoint" "interface" {
  for_each            = toset(var.vpc_endpoints)
  vpc_id              = aws_vpc.vpc.id
  service_name        = "com.amazonaws.${var.aws_region}.${each.value}"
  vpc_endpoint_type   = "Interface"
  subnet_ids          = [aws_subnet.sn_public_1a.id]
  private_dns_enabled = true
  security_group_ids  = [aws_security_group.sg_ssm.id]
  tags                = { "Name" = "${var.env_type}-${var.sys_name}-vpc-endpoint-${each.value}" }
}
```
はい。
こちらもいつもの安定の```vpc_endpoint.tf```ですね。
SSMに必須ですね。
Linux OSにはsshで、Windows OSにはfleet managerで接続できる方法を確保します。
動作確認もこのSSMで行います。

## aws_security_group.tf
```aws_security_group.tf
# ---------------------------
# Security Group
# ---------------------------

# ---------------------------
# EC2用 Security Group作成
# ---------------------------

#Linux EC2 用SG
resource "aws_security_group" "sg_ec2_linux01" {
  name   = "${var.env_type}-${var.sys_name}-sg-ec2-linux01"
  vpc_id = aws_vpc.vpc.id
  tags   = {
    Name = "${var.env_type}-${var.sys_name}-sg-ec2-linux01"
  }

  # インバウンドルール
  # from SSM
  ingress {
    description     = "AllowHttpsInBoundFromVPC for SSM"
    from_port       = 443
    to_port         = 443
    protocol        = "tcp"
    cidr_blocks = ["${var.vpc_address_pre}${var.vpc_address_suf}"]
  }

  ingress {
    description     = "AllowICMPv4InBoundFromVPC"
    from_port       = -1
    to_port         = -1
    protocol        = "icmp"
    cidr_blocks = ["${var.vpc_address_pre}${var.vpc_address_suf}"]
  }

  # from Azure Vnetアクセス
  ingress {
    description     = "AllowInBoundFrom_Azure-Vnet01"
    from_port       = 0
    to_port         = 0
    protocol        = "-1"
    cidr_blocks = ["${var.vnet_addr_pre01}${var.vnet_addr_suf01}"]
  }

  # アウトバウンドルール
  egress {
    description = "AllowAnyOutBound"
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

# Windows Server 用SG
resource "aws_security_group" "sg_ec2_windows01" {
  name   = "${var.env_type}-${var.sys_name}-sg-ec2-windows01"
  vpc_id = aws_vpc.vpc.id
  tags   = {
    Name = "${var.env_type}-${var.sys_name}-sg-ec2-windows01"
  }

  # インバウンドルール
  # from VPC ICMPv4
  ingress {
    description     = "AllowICMPv4InBoundFromVPC"
    from_port       = -1
    to_port         = -1
    protocol        = "icmp"
    cidr_blocks = ["${var.vpc_address_pre}${var.vpc_address_suf}"]
  }

  # from Azure Vnetアクセス
  ingress {
    description     = "AllowInBoundFrom_Azure-Vnet01"
    from_port       = 0
    to_port         = 0
    protocol        = "-1"
    cidr_blocks = ["${var.vnet_addr_pre01}${var.vnet_addr_suf01}"]
  }

  # アウトバウンドルール
  egress {
    description = "AllowAnyOutBound"
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

# ---------------------------
# SSM用 Vpc endpoint Security Group作成
# ---------------------------

resource "aws_security_group" "sg_ssm" {
  name   = "${var.env_type}-${var.sys_name}-sg-ssm"
  vpc_id = aws_vpc.vpc.id
  tags   = {
    Name = "${var.env_type}-${var.sys_name}-sg-ssm"
  }

  # インバウンドルール
  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["${var.vpc_address_pre}${var.vpc_address_suf}"]
  }

  # アウトバウンドルール
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_security_group" "sg_ssmmessages" {
  name   = "${var.env_type}-${var.sys_name}-sg-ssmmessages"
  vpc_id = aws_vpc.vpc.id
  tags   = {
    Name = "${var.env_type}-${var.sys_name}-sg-ssmmessages"
  }

  # インバウンドルール
  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["${var.vpc_address_pre}${var.vpc_address_suf}"]
  }

  # アウトバウンドルール
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}

resource "aws_security_group" "sg_ec2messages" {
  name   = "${var.env_type}-${var.sys_name}-sg-ec2messages"
  vpc_id = aws_vpc.vpc.id
  tags   = {
    Name = "${var.env_type}-${var.sys_name}-sg-ec2messages"
  }

  # インバウンドルール
  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["${var.vpc_address_pre}${var.vpc_address_suf}"]
  }

  # アウトバウンドルール
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }
}
```
はい。
SSM許可してます。
**特筆すべきは<font color="Red">Azure Virtual Networkからの接続を許可している</font>くらいですかね。**

## aws_vpn_gateway.tf
```aws_vpn_gateway.tf
# ---------------------------
# AWS VPN Gateway 作成
# ---------------------------
resource "aws_vpn_gateway" "vgw" {
  vpc_id = aws_vpc.vpc.id
  tags = {
    Name = "${var.env_type}-${var.sys_name}-vgw"
  }
}
```
ここではAWS_VPN_GatewayとTerraformのコードから命名していますが、AWSの日本語のGUIの表現だと仮想プライベートゲートウェイです。
コード自身はめっちゃ短いですね。
AzureとAWSの違いからこのような設定数の差があるのですが、**VPNを張る際<font color="Orange">AWSはVPN Connectionの項目でVPN関連の多くを設定します。</font>
<font color="Blue">AzureはVPN Gatewayの項目でVPN関連の多くを設定します。</font>**
理由はわかりませんが、こうなっています。
クラウドキャリアの設計思想の違いなのでしょうが、VPN Gatewayはオンプレミスで言うところのルーター、もうちょっと言及するとボーダールーターにあたるので、ルーター関連の設定はルーターに相当するリソースの設定値に寄せてほしいな、というのが私個人の意見です。

## aws_customer_gateway.tf
```aws_customer_gateway.tf
# ---------------------------
# Customer Gateway 作成 (実質対抗のAzure 仮想ネットワーク ゲートウェイ)
# ---------------------------
resource "aws_customer_gateway" "cgw" {
  bgp_asn    = 65000 # default 
  ip_address = "${data.azurerm_public_ip.pip-vgw.ip_address}"
  type       = "ipsec.1"
  tags = {
    Name = "${var.env_type}-${var.sys_name}-cgw"
  }
  depends_on = [
    azurerm_virtual_network_gateway.vgw01
  ]
}
```
はい。
<font color="Red">**ここですね！難しかったポイントは！**</font>
```ip_address = "${data.azurerm_public_ip.pip-vgw.ip_address}"```
<font color="Red">**ここです！**</font>
お分かりいただけますでしょうか？
わざわざ、```data```という変数を作って、ここにAzureのVPN GatewayのパブリックIPアドレスの構築後に出力されるIPアドレスの実値を格納しなければなりませんでした。
直接的にこの部分に言及されている情報はないのですが、間接的に言及されている部分がTerraformの公式にありました。
こちらです。
https://github.com/hashicorp/terraform-provider-azurerm/issues/159
ここにissueとして挙げられているのはAzure VMですが、Azure VPN Gatewayも挙動としては同じです。
わざわざこんな変数(```data```)を作らないといけないのもAzure VMとAzure VPN Gateway共通の理由で、<font color="Blue">**AzureのPublic IP Addressの仕様として、DynamicでIP Addressを指定した場合は、Public IP Addressが関連付く対象リソースのデプロイが完了してからでないとIP Addressが割り当てられない**</font>というのがその理由です。
これは私も思い当たる節があり、今までAzure VPN GatewayやAzure VM、他のPublic IP Addressを必要とする各種リソースの構築時に毎度この挙動はGUIでも確認していました。
先のissue報告内容は
「Azure VMに割り当てたPublic IP Addressはすぐに出力することはできず、構築完了後に```terraform refresh```というコマンドを実行してから再取得しなければならない。」
となっています。
確かにその通りなのですが、これもまた先ほどのissue報告の返信に記載されている通りこの挙動は<font color="Blue">**Azureの仕様を受けたTerraformの仕様**</font>です。
今回のこだわりポイントはあくまで<font color="Red">**1回のコード実行でAzure-AWS間のVPNを張る部分までノンストップで完遂すること**</font>です。
ですのでこのissueにある通り```terraform refresh```を実行し、そこでAzure VPN GatewayのPublic IP Addressを取得し、そこから再度```terraform apply```を実行する、ではこだわりを満たせません。
ですので、いったん出力される予定のPublic IP Addressを変数```data```に入れ、これをAWS Customer Gatewayのパラメーターとして指定します。
具体的な変数```data```の作成の仕方はAzure VPN Gatewayで設定していますのでもう少々お待ちください。

## aws_ec2.tf
```aws_ec2.tf
# -------------------------------
# EC2作成
# -------------------------------

# -------------------------------
# Amazon Linux EC2を作成
# -------------------------------

# Amazon Linux 2023 の最新版AMIを取得
data "aws_ssm_parameter" "al2023-ami-kernel-default-x86_64" {
  name = "/aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64"
}

 resource "aws_spot_instance_request" "ec2_linux01" { #spot_priceを設定する。ムンバイリージョンではAvailability Zone CではSpot Instanceは使えない。
   spot_price                  = "0.0029"
   ami                         = data.aws_ssm_parameter.al2023-ami-kernel-default-x86_64.value
   instance_type               = var.ec2_instance_type_lin
   availability_zone           = var.availability_zone.b
   vpc_security_group_ids      = [aws_security_group.sg_ec2_linux01.id]
   subnet_id                   = aws_subnet.sn_private_1b.id
   associate_public_ip_address = "false"
   iam_instance_profile        = aws_iam_instance_profile.instance_prof.name
   user_data                   = <<-EOF
                                 #!/bin/bash
                                 sudo hostnamectl set-hostname ${var.env_type}-${var.sys_name}-ec2-linux01
                                 EOF

   root_block_device {
     volume_size = 30   # GB
     volume_type = "gp3" # 汎用SSD
     encrypted   = false
     tags        = {
       Snapshot = "false"      
     }
   }
   tags = {
     Name = "${var.env_type}-${var.sys_name}-ec2-amazon-linux01"
   }
      depends_on = [
    aws_route_table_association.associate_rt_sn_private_1b_sn_private_1b
   ]
 }

# -------------------------------
# Windows Server EC2を作成
# -------------------------------

# -------------------------------
# Windows Server 用のKey pair作成
# -------------------------------
variable "key_name" {
  default = "kp-ec2-01"
}
variable "key_path" {
  # - Windowsの場合はフォルダを"\\"で区切る（エスケープする必要がある）実行中のtfファイルと同じ場所にキーファイルを出力する
  default = ".\\"
}
resource "tls_private_key" "kp-windows-ec2-01" {
  algorithm = "RSA"
  rsa_bits  = 2048
}

# クライアントPCにKey pair（秘密鍵と公開鍵）を作成
# - Windowsの場合はフォルダを"\\"で区切る（エスケープする必要がある）
# - [terraform apply] 実行後はクライアントPCの公開鍵は自動削除される
locals {
  public_key_file  = "${var.key_path}\\${var.key_name}.id_rsa.pub"
  private_key_file = "${var.key_path}\\${var.key_name}.id_rsa"
}

resource "local_file" "kp_windows_ec2_01_pem" {
  filename = "${local.private_key_file}"
  content  = "${tls_private_key.kp-windows-ec2-01.private_key_pem}"
}

# 上記で作成した公開鍵をAWSのKey pairにインポート
resource "aws_key_pair" "kp_windows_ec2_01" {
  key_name   = "${var.key_name}"
  public_key = "${tls_private_key.kp-windows-ec2-01.public_key_openssh}"
}

#20240523時点のAMI
#Microsoft Windows Server 2022 Base:ami-0f346136f3b372267
#Microsoft Windows Server 2019 Base:ami-01bd28d73d0053a15
#Microsoft Windows Server 2016 Base:ami-063d3d00f8a97a6d1

 resource "aws_spot_instance_request" "ec2_windows01" { #spot_priceを設定する。ムンバイリージョンではAvailability Zone CではSpot Instanceは使えない。
   spot_price                  = "0.1161"
   ami                         = "ami-0f346136f3b372267" #Microsoft Windows Server 2022 Base
   instance_type               = var.ec2_instance_type_win
   availability_zone           = var.availability_zone.b
   vpc_security_group_ids      = [aws_security_group.sg_ec2_windows01.id]
   subnet_id                   = aws_subnet.sn_private_1b.id
   associate_public_ip_address = "false"
   key_name                    = "${var.key_name}"
   iam_instance_profile        = aws_iam_instance_profile.instance_prof.name
   root_block_device {
     volume_size = 50   # GB
     volume_type = "gp3" # 汎用SSD
     encrypted   = false
     tags        = {
       Snapshot = "false"      
     }
   }
   tags = {
     Name = "${var.env_type}-${var.sys_name}-ec2-windows01"
   }
   depends_on = [
    aws_route_table_association.associate_rt_sn_private_1b_sn_private_1b
   ]
 }
```
はい。
EC2です。
今回も<font color="Red">**Spot Instance**</font>です！
<font color="Red">**お金は大事ですからね！**</font>

つづいてAzureです。

## azure_resource_group.tf
```azure_resource_group.tf
# --------------------------------------
# Create Resource Group
# --------------------------------------
resource "azurerm_resource_group" "rg" {
  location = var.azure_region
  name     = "${var.rg_name_pre}${var.system_name}"
}
```
はい。
安定のAzure Resource Groupですね。
何も言うことはありません。

## azure_vnet.tf
```azure_vnet.tf
#----------------------------------------
# client vnet Create
#----------------------------------------
resource "azurerm_virtual_network" "vnet-client" {
  resource_group_name = azurerm_resource_group.rg.name
  location = azurerm_resource_group.rg.location
  name     = "${var.vnet_name_pre}${var.vnet_name_suf01}"
  address_space = [ "${var.vnet_addr_pre01}${var.vnet_addr_suf01}" ]
}

# sn-client01 Create
resource "azurerm_subnet" "sn-client01" {
  resource_group_name = azurerm_resource_group.rg.name
  name = "${var.sn_name_pre}${var.sn_name_suf01}"
  virtual_network_name = azurerm_virtual_network.vnet-client.name
  address_prefixes = [ "${var.vnet_addr_pre01}${var.sn_addr_suf01}" ]
  service_endpoints = [ "Microsoft.Storage" ]
}
```
はい。
これも特に言及するところはありません。

## azure_vnet_gateway.tf
```azure_vnet_gateway.tf
# ------------------------------------------
# Create Virtual Network Gateway Subnet
# ------------------------------------------

resource "azurerm_subnet" "GatewaySubnet" {
  resource_group_name = azurerm_resource_group.rg.name
  name = "GatewaySubnet"
  virtual_network_name = azurerm_virtual_network.vnet-client.name
  address_prefixes = [ "${var.vnet_addr_pre01}${var.sn_addr_suf02}" ]
}

# ------------------------------------------
# Create Virtual Network Gateway
# ------------------------------------------

resource "azurerm_public_ip" "pip-vgw" {
  name                = "pip-vgw01"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name
  allocation_method = "Dynamic"
}

resource "azurerm_virtual_network_gateway" "vgw01" {
  name                = "vgw01"
  location            = azurerm_resource_group.rg.location
  resource_group_name = azurerm_resource_group.rg.name

  type     = "Vpn"
  vpn_type = "RouteBased"

  active_active = false
  enable_bgp    = false
  sku           = "Basic"

  ip_configuration {
    name                          = "vnetGatewayConfig"
    public_ip_address_id          = azurerm_public_ip.pip-vgw.id
    private_ip_address_allocation = "Dynamic"
    subnet_id                     = azurerm_subnet.GatewaySubnet.id
  }
}

# VGWのPublic IP Addressを取得するためのコード
data "azurerm_public_ip" "pip-vgw" {
  name                = "${azurerm_public_ip.pip-vgw.name}"
  resource_group_name = azurerm_resource_group.rg.name
  depends_on          = [
    azurerm_virtual_network_gateway.vgw01
    ]
}
```
はい。
ここで出てきますね。
[本記事の前述部分](https://qiita.com/hidekko/items/374e65d30a726c87899c#aws_customer_gatewaytf)で言及している```data```です。
最後のブロックですね。
ここで```depends_on```を使って構成順序を調整しており、先のAzureの仕様、「Public IP Addressが付与されているリソースの構築が完了しないとPublic IP Addressが割り当てられない」を回避しています。

ここに行きつくまで多分4回から5回くらい再構築しましたね・・・

## azure_local_gateway.tf
```azure_local_gateway.tf
# --------------------------------------
# Create Azure Local Network Gateway
# --------------------------------------

resource "azurerm_local_network_gateway" "lgw" {
  name                = "${var.LG_name_pre}${var.LG_name_suf}"
  resource_group_name = azurerm_resource_group.rg.name
  location            = azurerm_resource_group.rg.location
  gateway_address     = "${aws_vpn_connection.vpn_connection.tunnel1_address}"
  address_space       = ["${var.vpc_address_pre}${var.vpc_address_suf}"]
}
```
こちらもAWSのCustomer GatewayからAzure Local GatewayのPublic IP Addressを参照しています。
でもAWS Customer GatewayはAzure VPN Gatewayのようにややこしい仕様では無いので単純な指定、
```"${aws_vpn_connection.vpn_connection.tunnel1_address}"```で済んでいます。
シンプルで良いですね。

## azure_nsg.tf
```azure_nsg.tf
#------------------------------------
# nsg-sn-client01 Create
#------------------------------------

resource "azurerm_network_security_group" "nsg-sn-client01" {
  resource_group_name = azurerm_resource_group.rg.name
  location = azurerm_resource_group.rg.location
  name = "${var.nsg_name_pre}${azurerm_subnet.sn-client01.name}"

## InBound Rule
  security_rule  {
    name = "AllowVnetInBound"
    priority = 100
    direction = "Inbound"
    access = "Allow"
    protocol = "*"
    source_port_range = "*"
    destination_port_range = "*"
    source_address_prefix = "VirtualNetwork"
    destination_address_prefix = "VirtualNetwork"
  }

 security_rule  {
    name = "AllowAzureLoadBalancerInBound"
    priority = 101
    direction = "Inbound"
    access = "Allow"
    protocol = "*"
    source_port_range = "*"
    destination_port_range = "*"
    source_address_prefix = "AzureLoadBalancer"
    destination_address_prefix = "*"
  }

  security_rule {
    name = "DenyAllInBound"
    priority = 4096
    direction = "Inbound"
    access = "Deny"
    protocol = "*"
    source_port_range = "*"
    destination_port_range = "*"
    source_address_prefix = "*"
    destination_address_prefix = "*"
  }

## OutBound Rule
  security_rule  {
    name = "AllowVnetOutBound"
    priority = 100
    direction = "Outbound"
    access = "Allow"
    protocol = "*"
    source_port_range = "*"
    destination_port_range = "*"
    source_address_prefix = "VirtualNetwork"
    destination_address_prefix = "VirtualNetwork"
  }

 security_rule  {
    name = "AllowAzureFrontDoor.FirstPartyHttpOutBound"
    priority = 101
    direction = "Outbound"
    access = "Allow"
    protocol = "Tcp"
    source_port_range = "*"
    destination_port_range = "80"
    source_address_prefix = "*"
    destination_address_prefix = "AzureFrontDoor.FirstParty"
    description = "Allow Windows Update Rule01"
  }

 security_rule  {
    name = "AllowAzureUpdateDeliveryHttpsOutBound"
    priority = 102
    direction = "Outbound"
    access = "Allow"
    protocol = "Tcp"
    source_port_range = "*"
    destination_port_range = "443"
    source_address_prefix = "*"
    destination_address_prefix = "AzureUpdateDelivery"
    description = "Allow Windows Update Rule02"
  }

  security_rule {
    name = "AllowAllOutBound"
    priority = 4095
    direction = "Outbound"
    access = "Allow"
    protocol = "*"
    source_port_range = "*"
    destination_port_range = "*"
    source_address_prefix = "*"
    destination_address_prefix = "*"
  }

  security_rule {
    name = "DenyAllOutBound"
    priority = 4096
    direction = "Outbound"
    access = "Deny"
    protocol = "*"
    source_port_range = "*"
    destination_port_range = "*"
    source_address_prefix = "*"
    destination_address_prefix = "*"
  }

}

# Connect nsg-sn-client01 to sn-client01
resource "azurerm_subnet_network_security_group_association" "nsg-sn-client01-to-sn-client01" {
  subnet_id =  azurerm_subnet.sn-client01.id
  network_security_group_id = azurerm_network_security_group.nsg-sn-client01.id
  depends_on = [
    azurerm_virtual_network_gateway_connection.vpn-connection
    ]
}
```
はい。
ここもAWSのSecurity Groupでも言及していますが、<font color="Blue">**AzureのNSGではVPNの対向先、今回だとAWSのVPCで指定されているローカルIPアドレスセグメントを許可する必要はありません。
これはAzureの仕様です。
もう少し細かく言及すると、Azureの仕様上、VPNの対向先で指定されるローカルIPアドレスセグメント、設定値としてはAzure Local Gatewayで指定されているローカルIPアドレスセグメントは暗黙的にNSGのタグのVirtual Networkに含まれるからです。**</font>
これも豆知識でAzure構築の際にひっかかるあるあるなので覚えておいて損はないでしょう。
そして私はNSGのコード内で最上位の優先順位100でInBoundもOutBoundもVirtual Network to Virtual NetworkはAny Any Permitしてあるので、このVPN間の通信は最初に許可されます。

## azure_storage.tf
```azure_storage.tf
# Create storage account for boot diagnostics
resource "azurerm_storage_account" "storage_account" {
  resource_group_name      = azurerm_resource_group.rg.name
  location                 = azurerm_resource_group.rg.location
  name                     = "stdiag${random_id.random_id.hex}"
  account_tier             = "Standard"
  account_replication_type = "LRS"
}

resource "azurerm_storage_account_network_rules" "for_multiple_subnet_id" {
  storage_account_id = azurerm_storage_account.storage_account.id
  default_action = "Deny"
  virtual_network_subnet_ids = [azurerm_subnet.sn-client01.id]
  ip_rules = ["123.225.10.132"]
}
```
はい。
ここは特に何かっていうことはないですね。

## azure_vm.tf
```azure_vm.tf
# --------------------------------------
# Create Windows 11
# --------------------------------------

# nic01 (attached for vm01) Create
resource "azurerm_network_interface" "nic-win11-01" {
  resource_group_name = azurerm_resource_group.rg.name
  location = azurerm_resource_group.rg.location
  name = "${var.nic_name_pre}${var.vm_name_pre}${var.vm_name_suf01}"

  ip_configuration {
    name = "${var.nic_name_pre}${var.vm_name_pre}${var.vm_name_suf01}${var.ipconf_suf}"
    subnet_id = azurerm_subnet.sn-client01.id
    private_ip_address_allocation = "${var.priv_ip_alloc}"
  }
}

# windows vm01 Create
resource "azurerm_windows_virtual_machine" "vm-win11-01" {
  name                  = "${var.vm_name_pre}${var.vm_name_suf01}"
  admin_username        = "${var.adminuser}"
#  admin_password        = random_password.password.result
  admin_password        = "P@ssw0rd0123"
  location              = azurerm_resource_group.rg.location
  resource_group_name   = azurerm_resource_group.rg.name
  network_interface_ids = [azurerm_network_interface.nic-win11-01.id]
  size                  = "${var.vm_sku}"
  priority              = "Spot" # or Regular
  eviction_policy       = "Deallocate"
  
  os_disk {
    name                 = "${var.vm_osdisk_pre}${var.vm_name_pre}${var.vm_name_suf01}"
    caching              = "ReadWrite"
    storage_account_type = "${var.vm_osdisk_sku}"
  }

  source_image_reference {
    publisher = "${var.os_pub01}"
    offer     = "${var.os_offer01}"
    sku       = "${var.os_sku01}"
    version   = "${var.os_ver}"
  }

  boot_diagnostics {
    storage_account_uri = azurerm_storage_account.storage_account.primary_blob_endpoint
  }
  depends_on = [
    azurerm_subnet_network_security_group_association.nsg-sn-client01-to-sn-client01
    ]
}

# --------------------------------------
# Create Almalinux
# --------------------------------------

# nic02 (attached for vm01) Create
resource "azurerm_network_interface" "nic-alma-01" {
  resource_group_name = azurerm_resource_group.rg.name
  location = azurerm_resource_group.rg.location
  name = "${var.nic_name_pre}${var.vm_name_pre}${var.vm_name_suf02}"

  ip_configuration {
    name = "${var.nic_name_pre}${var.vm_name_pre}${var.vm_name_suf02}${var.ipconf_suf}"
    subnet_id = azurerm_subnet.sn-client01.id
    private_ip_address_allocation = "${var.priv_ip_alloc}"
  }
}

# linux vm01 Create
resource "azurerm_linux_virtual_machine" "vm-alma-01" {
  name                  = "${var.vm_name_pre}${var.vm_name_suf02}"
  admin_username        = "${var.adminuser}"
  admin_password        = "P@ssw0rd0123"
  disable_password_authentication = false
  location              = azurerm_resource_group.rg.location
  resource_group_name   = azurerm_resource_group.rg.name
  network_interface_ids = [azurerm_network_interface.nic-alma-01.id]
  size                  = "${var.vm_sku}"
  
  os_disk {
    name                 = "${var.vm_osdisk_pre}${var.vm_name_pre}${var.vm_name_suf02}"
    caching              = "ReadWrite"
    storage_account_type = "${var.vm_osdisk_sku}"
  }

  source_image_reference {
    publisher = "${var.os_pub02}"
    offer     = "${var.os_offer02}"
    sku       = "${var.os_sku02}"
    version   = "${var.os_ver}"
  }

  computer_name  = "${var.vm_name_pre}${var.vm_name_suf02}"

  boot_diagnostics {
    storage_account_uri = azurerm_storage_account.storage_account.primary_blob_endpoint
  }
  depends_on = [
    azurerm_subnet_network_security_group_association.nsg-sn-client01-to-sn-client01
    ]
}
```
はい。
ここでもちゃんと<font color="Red">**Spot Instance**</font>使っています！

## vpn_connection.tf
```vpn_connection.tf
# ------------------------------
# VPN 接続作成
# ------------------------------

# ------------------------------
# AWS VPN 接続作成
# ------------------------------

resource "aws_vpn_connection" "vpn_connection" {
  vpn_gateway_id           = aws_vpn_gateway.vgw.id
  customer_gateway_id      = aws_customer_gateway.cgw.id
  type                     = "ipsec.1"
  static_routes_only       = true
  tunnel1_preshared_key    = "Passw0rd0123"
  tags = {
    Name = "${var.env_type}-${var.sys_name}-vpn-connection"
  }
}

resource "aws_vpn_connection_route" "azure-vnet" {
  destination_cidr_block = "${var.vnet_addr_pre01}${var.vnet_addr_suf01}"
  vpn_connection_id      = aws_vpn_connection.vpn_connection.id
}

# ------------------------------
# Azure VPN 接続作成
# ------------------------------

resource "azurerm_virtual_network_gateway_connection" "vpn-connection" {
    name                       = "${var.connection_name_pre}${var.vnet_name_pre}${var.vnet_name_suf01}-${var.LG_name_pre}${var.LG_name_suf}"
    location                   = azurerm_resource_group.rg.location
    resource_group_name        = azurerm_resource_group.rg.name
    type                       = "IPsec"
    virtual_network_gateway_id = azurerm_virtual_network_gateway.vgw01.id
    local_network_gateway_id   = azurerm_local_network_gateway.lgw.id
    shared_key                 = "Passw0rd0123"
}
```
はい。
ここのコードはAzureとAWS両方一緒に書いています。
どちらもVPN Connectionというリソース名だったので、まぁ良いかなと思い一緒に記載しました。
この設定値は言わば表裏一体、PSKやIKEのバージョンなど双方で設定値を合わせないといけない設定項目が多いため同一のファイルでコードを記載した方がわかりやすいという利点もあります。
ここで注意したいのはPSKです。
<font color="Orange">AWSは記号が使えず</font>、<font color="Blue">Azureは記号が使えます。</font>
なのでAWSの仕様に引っ張られてAzureはPSKで記号が使えなくなりました。
小さいですがこれくらいですね。

## output.tf
```output.tf
# ---------------------------
# Output
# ---------------------------

# AWS VPN Cpnnection のTunnel 1のパブリックIPアドレスを出力
output "aws_vpn_gateway_tunnel1_global_ip_address" {
   value = aws_vpn_connection.vpn_connection.tunnel1_address 
 }

# Azure 仮想ネットワーク ゲートウェイのパブリックIPアドレスを出力
output "azure_vpn_gateway_global_ip_address" {
  value = "${data.azurerm_public_ip.pip-vgw.ip_address}"
 }

# Azure Storage Account Nameを出力
output "diag_storage_account" {
  value = azurerm_storage_account.storage_account.name
}
```
Outputですが、これもまぁ何にもなくても良かったんですが、今回はAzure、AWS双方のVPN GatewayのPublic IP Addressを出力しました。
あとはStorage Account名です。

# 動作確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/739a3c77-4e6c-f885-79de-3f42cf9355eb.png)
普段変数で呼んでいるAzureの認証関連の情報ですが、VS Codeでは呼べなかったのでPower Shellでいきます。
(色々調べてみたんですが、解決していません・・・)
```terraform apply```したら問題なさそうなのでそのまま```yes```を入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/5e281cc6-dfd8-052c-a63b-ee451a4ea697.png)

## Azure VPN Gateway構築
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/18462767-10ae-4f99-cf41-deeccf92db19.png)
はい。
こんな感じで時間かかります。

GUIで確認しても構築中ですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/84b45e9d-22d6-cc4b-cd22-1e0f8e9d29d6.png)
そしてAWS側も
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/d5c6e2ab-cdba-ddcf-c0a3-f1a1354532fd.png)
こんな感じで仮想プライベートゲートウェイができていますね。
そして・・・
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/698e7610-c0c1-84ee-b363-45391edd6534.png)
今回は15分もかからず6分30秒で済みましたね。
ではちゃんとPublic IP Addressが取得できているでしょうか？

## Azure VPN GatewayのPublic IP Address確認
まずはAzure Portalから
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/ec2f0b7b-57bf-f96a-9b95-07949951dac5.png)
はい。
ちゃんとPublic IP Addressが取得できていて、Azure VPN Gatewayに関連付いていますね。
文句なしです。

## AWS Customer GaetwayのPublic IP Address確認
![AWS_Customer_Gateway_集中線.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/bacb4303-2dd7-f8de-bb3a-d531f8a37b9a.png)
<font color="Red">**はい！
完璧ですね！
やっぱりTerraformは凄い！**</font>
ちょっと集中線がうるさいの元画像もおいときます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/64988f8c-b6a6-3878-8e6e-f47e1a7fced3.png)

## Terraform完了確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/978cb808-0765-3cef-3c53-50e7592628e8.png)
はい。
ここでも確認できていますね。
ついでにAWSの仮想プライベートゲートウェイのPublic IP Address(厳密にはSite to Site VPN接続の設定時に割り当てられるPublic IP Address)も見てみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/11087c46-318d-00db-3dee-4f82cc16a6b8.png)
Terraformだとこの<font color="Orange">オレンジ色の囲い</font>の通りですが、AWS Consoleだと
![AWS_Connection_集中線.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/fe7acba7-52db-0cbf-ce14-e4b0fdee9524.png)
<font color="Red">**こっちも完璧ですね！
やっぱりTerraformは凄い！**</font>

## VPN接続確認
はい。
ではいよいよ本丸です。
ちゃんとAzure-AWS間でVPN接続はできているのでしょうか？
まずはAWS Console！
![AWS_VPN_Connection02_集中線.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/3f7f8817-ecf8-724d-e6d0-b5b97fc85b4d.png)

そしてAzure Portal！
![Azure_VPN_Connection01_集中線.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/f4298a1c-ea53-4b02-eb8e-317f1f45f60a.png)
<font color="Red">**世界よ刮目せよ！
これがTerraformの実力だ！
いや、ホント、マジで凄い！
便利！
これぞ人類の英知の結晶ですね！**</font>

はい。
興奮しすぎてまたしても集中線がうるさいので、それぞれ集中線なしの画像も載せておきます。
![AWS_VPN_Connection02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/ffa45201-4964-2d4c-21dd-8dbd9524b809.png)
![Azure_VPN_Connection01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/e7d2c8b6-4a2e-78d9-6ee9-8dc54676ae47.png)

## 通信確認
はい。
ここまででGUI上でのAzure-AWS間のVPN接続確認はできましたが、<font color="Red">**果たして実際の通信はできるのか？**</font>
という疑問が残りますね。
当然検証します！

Fleet ManagerでAWS側のWindows EC2にログインします。
と思ったら・・・
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/18c30dc0-7c69-1971-2a21-803410c4cebd.png)
何！？
Fleet Managerを更新しただと！？
何がどう変わったかわかりませんが、Public Cloudあるあるですね。
納品直前にUIがごっそり変わってしまってGUIの手順書が役に立たなくなる・・・っていうやつですね。
では気を取り直して通信確認していきましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/e8ecb791-1dcc-ef31-5677-0d0c028f9a3e.png)
Fleet ManagerでWindows EC2のログインし、コマンドプロンプトとRDPクライアントの画面を立ち上げます。
Azure VMのAlmalinuxが192.168.201.4
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/9bb5773b-d730-b760-c574-65e3ae7b54d4.png)
Azure VMのWindows 11が192.168.201.5
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/0cda602d-604b-537c-aaca-d62282fc2aeb.png)
となっていますね。
それぞれにWindows EC2からアクセスしてみると・・・
![connection_check01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/105e2b03-beb8-4014-5334-60edc418dde4.png)
![connection_Check02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/97a9f8e8-38f4-4171-1c23-ebf19ac1f920.png)
<font color="Red">**やった！
通信できた！
完璧ですね！**</font>

まずはAlmalinuxログインします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/c21b7e19-2f9b-2a51-6188-6538658e956d.png)
はい。
言うこと無いですね。
そうこうしてるうちにAzure VMのWindows 11にもログインでき、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/17687b08-4d8d-b447-120f-7928e906f10e.png)
やべーな。
完璧だ。
もう語彙力どっかいっちゃいましたよ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/2ec26bdd-223c-e819-6a54-4da0a1fc83c9.png)
はい。
もう何も問題ないですね。
完璧としか言いようがないです。

皆様どうでしょうか？
<font color="Gold">**Terraform、マジ便利！マジ神！**</font>

はい。
ということでとても面白い実験でした。

本日はここまで。