Azure で 2リージョンでの VNET の Hub-Spoke 構成・オンプレ接続 (VPN) を Terraformで構築

## はじめに
Azure でインフラを検討する上で自宅ラボで試した内容を記載する
実施事項は下記の通り

- Azure での VNET 構築およびオンプレミス(自宅ラボ)接続で VPN の構築を実施する
- Hub-Spoke 型の VNET 構成を構築する
- リージョンは 2 リージョン(japaneast, japanwest)で作成する
- Spokeがリージョン間を通信できる構成を構築する
- 構築は IaC にするため terraform を使用する

![スクリーンショット 2022-04-09 11.18.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/a96922fc-aabf-a521-cbcf-e438f5970e51.png)



## VNET Peering / Virtual Network Gateway / Azure Firewall
Azure のネットワークを構成するために、VNET 間の接続である`VNET Peering`と VNET のゲートウェイとして VPN に使用する `Virtual Network Gateway` と Azure が提供する Firewall として `Azure Firewall`について概要を記載する

### VNET Peering (仮想ネットワーク ピアリング)
[ドキュメント](https://docs.microsoft.com/ja-jp/azure/virtual-network/virtual-network-peering-overview)参照

- Azure で 2 つ以上の Virtual Network をシームレスに接続できる
- ピアリング間のトラフィックには、Microsoft のバックボーンインフラストラクチャが使用される
- ピアリングには 2種類ある
    - 仮想ネットワーク ピアリング: 同じ Azure リージョン内
    - グローバル仮想ネットワーク ピアリング: Azure リージョン間

各仮想ネットワークには、独自のゲートウェイを持つことができ、[ゲートウェイとオンプレミスの接続](https://docs.microsoft.com/ja-jp/azure/virtual-network/virtual-network-peering-overview#gateways-and-on-premises-connectivity)ができる

- ピアリングされた仮想ネットワークのゲートウェイを、オンプレミスのネットワークへのトランジット ポイントとして構成できる
- この場合、リモート ゲートウェイを使用する仮想ネットワークが、その独自のゲートウェイを持つことはできない
- 1 つの仮想ネットワークが所有できるゲートウェイは 1 つに限られる
- ゲートウェイは、ローカル ゲートウェイとピアリングされた仮想ネットワークのリモート ゲートウェイのどちらかになる

#### Hub Spoke 構成

Google Cloud でいうところの、共有 VPC 構成で下記のような対比になる

| Google Cloud | Azure |
|:-:|:-:|
| 共有 VPC 構成 | Hub Spoke ネットワーク構成 |
| ホストプロジェクト | Hub ネットワーク |
| サービスプロジェクト | Spoke ネットワーク |

ハブとスポークの構成を使用する利点としては、コストの削減、サブスクリプションの制限の克服、ワークロードの分離などがある

#### Spoke to Spoke 通信
[ドキュメント](https://docs.microsoft.com/ja-jp/azure/architecture/reference-architectures/hybrid-networking/hub-spoke?tabs=cli#spoke-connectivity)参照

スポーク間ではデフォルトで通信ができない
通信するには、2つの方法がある

- Azure Firewall またはネットワーク仮想アプライアンスを構築してルーティング
- 仮想ネットワークゲートウェイ (VPNゲートウェイ) を使用して、スポーク間でトラフィックをルーティング

### Virtual Network Gateway (仮想ネットワークゲートウェイ)

[ドキュメント](https://docs.microsoft.com/ja-jp/azure/vpn-gateway/vpn-gateway-about-vpngateways#what-is-a-virtual-network-gateway)参照

- 仮想ネットワーク ゲートウェイは、"ゲートウェイ サブネット" と呼ばれる、作成する特定のサブネットにデプロイされる 2 台以上の VM で構成される
- 仮想ネットワーク ゲートウェイ VM には、ルーティング テーブルが含まれ、特定のゲートウェイ サービスが実行される
- これらの VM は、仮想ネットワーク ゲートウェイを作成するときに作成され、直接構成することはできない
- [SKU が複数](https://docs.microsoft.com/ja-jp/azure/vpn-gateway/vpn-gateway-about-vpngateways#gateway-skus-by-tunnel-connection-and-throughput)あり、種類によってスループットやトンネル数が異なる

作成には固定命名のサブネットである`GatewaySubnet`を作成する必要がある
サブネットサイズは`/27`であれば[既存のほとんどの構成で IP アドレスに十分対応](https://docs.microsoft.com/ja-jp/azure/vpn-gateway/vpn-gateway-about-vpn-gateway-settings#gwsub)できる

### Azure Firewall

[Azure Firewall](https://docs.microsoft.com/ja-jp/azure/firewall/overview) は、SKU が [Standard](https://docs.microsoft.com/ja-jp/azure/firewall/features)/[Premium](https://docs.microsoft.com/ja-jp/azure/firewall/premium-features) があり、一般的な通信制御(L3/L4)以外にも、L7でのフィルタリングや NAT、既知の悪意ある IP のブロックなどもあり、Premium にすると IDPS や TLS 終端なども可能
[強制トンネリング](https://docs.microsoft.com/ja-jp/azure/firewall/forced-tunneling)を有効化でき、トラフィックを Firewall を通すように、各 VNET へ設定も可能

作成には固定命名のサブネットである`AzureFirewallubnet`を作成する必要がある
サブネットサイズは`/26`が必要で[それ以上は不要](https://docs.microsoft.com/en-us/azure/firewall/firewall-faq#does-the-firewall-subnet-size-need-to-change-as-the-service-scales)

後述するが、個人利用としては無料枠がなく費用が高額になるため試験利用の場合は注意が必要

## 構成
下記が今回試験した構成概要図となる (アドレス等は個人環境用なので各環境で要置き換え)

![Azure概要図_2022-04-03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/58aa9575-7da7-af29-cad5-baa9db7a73b0.png)

下記ざっくり凡例
<img width="400" alt="Azure構成図凡例_2022-04-03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/85fdb870-2b39-1e5d-1620-d3add5fe0663.png">


下記に各構成の補足説明をする

### Hub / オンプレ
オンプレとの接続や他リージョン・他　Spoke　との接続を集約する Hub を構成する
#### Virtual Network Gateway (vgw)
無料枠が1台分しかないため、片リージョンのみの構成で試験をしている (本番環境なら両リージョンが良い)
vgw がないリージョンへルーティングをするために、GatewaySubnet へユーザ定義ルート(UDR)を追加してルーティングをできるようにしている
オンプレとは VPN の Connection 設定をして、BGP を有効化することで経路交換をする
vgw がある VNET を直接ピアリングして spoke 側が Hub 側の vgw をゲートウェイとして利用しないと、spoke サブネットがオンプレへ経路広報されないので注意が必要 (ここでは、japaneast の hub と spoke の VNET アドレスのみ BGP で広報される)

#### Local Network Gateway (lgw)
オンプレ側ルータの VPN 情報を記載する

#### オンプレ側ルータ
lgw 対象のルータ
vgw 設定後に vgw の`構成`画面に表示される`BGP ピアの IP アドレス`を VPN Interface 宛に Static Route に追加する必要がある
![スクリーンショット 2022-04-03 12.33.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/8ea57990-1fd5-f890-e680-15d8127c3528.png)

また、Azure の経路は VPN 越しで BGP で伝搬されるが、本構成では japanwest 側の経路は伝搬されないので japanwest 側サブネットは VPN Interface 宛に Static ルートを追加する

下記は本環境では EdgeRouterX を利用しているので、そのconfig例 (VPN Interface が vti2 の場合)

```txt:onprem_router_static_routes
set protocols static interface-route 172.22.1.254/32 next-hop-interface vti2
set protocols static interface-route 172.23.0.0/16 next-hop-interface vti2
```

#### VirtualMachine (ルータ)
[Spoke to Spoke 通信](#spoke-to-spoke-通信)を通信するには、「Azure Firewall またはネットワーク仮想アプライアンスを構築してルーティング」が必要
本構成では、Linux の仮想マシンを構築してIPフォワードを許可(net.ipv4.ip_forward=1)することで「ネットワーク仮想アプライアンス」のルータ代わりにしている
仮想マシンのサブネットに UDR を設定して別リージョンのサブネットのルートを別リージョンのルータ代わりの仮想マシンを NextHop に記載してルーティングできるようにする

費用が許すならば、Azure Firewall で構築するのが良さそう

### Spoke
リソースを設置し Hub 経由で他ネットワークと接続する Spoke を構成する
システム単位で Spoke が置かれるイメージで、ここでは疎通試験用に VM を各 Spoke へ設置する
#### VNET Peering
Hub 側ゲートウェイを使用するように VNET Peering を設定する
これをしないと、オンプレへ Spoke のアドレスが広報されない

下記設定例
![スクリーンショット 2022-04-03 12.53.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/67d56402-4a8e-2fab-50a6-2eb2d76c595c.png)

#### RouteTable / UDR
Hub のルータ代わりの VM のアドレスを NextHop にルーティングを記載する (AzureFirewall の場合は、AzureFirewall の IP を設定する)

下記設定例
![スクリーンショット 2022-04-03 12.58.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/b53553ab-167a-485a-3a10-34c50b1239e6.png)


#### VirtualMachine
japaneast に Linux、japanwest に Windows の VM を構築して疎通確認をできるようにしている

#### NSG
試験用の VirtualMachine のサブネットへ NSG を設定してリモートログインの送信元 IP を制御している

### リソース命名規則
[Azure が推奨する命名規則](https://docs.microsoft.com/ja-jp/azure/cloud-adoption-framework/ready/azure-best-practices/resource-naming)を参考にしている

https://docs.microsoft.com/ja-jp/azure/cloud-adoption-framework/ready/azure-best-practices/resource-naming

ただしリソースグループはリージョン名を追加している

## Terraform 実施準備

Terraform は事前 install 済み (本環境では、terraform version `v1.1.7`) として、
下記を Azure 向けに実施するために設定する
azure provider の version　は `2.89.0`　を使用する

1. azure-cli install
1. az login
1. backend 用 Blob コンテナ作成
1. サービスプリンシパル作成
1. terraform init

### 1. az install

azure-cli (az) をインストールする
下記は、CentOS 向けのインストールコマンド例 (CLI, Ansible 両パターン)

#### CLI

```
sudo rpm --import https://packages.microsoft.com/keys/microsoft.asc
```

```
echo -e "[azure-cli]
name=Azure CLI
baseurl=https://packages.microsoft.com/yumrepos/azure-cli
enabled=1
gpgcheck=1
gpgkey=https://packages.microsoft.com/keys/microsoft.asc" | sudo tee /etc/yum.repos.d/azure-cli.repo
```

```
sudo dnf install azure-cli
```

#### Ansible

```yaml:roles/az/tasks/main.yml
# https://docs.microsoft.com/ja-jp/cli/azure/install-azure-cli-linux?pivots=dnf#install

---
- name: az / install rpm key
  rpm_key:
    state: present
    key: https://packages.microsoft.com/keys/microsoft.asc
  become: true
  when:
    - ansible_distribution == "CentOS"

- name: az / install yum repository
  yum_repository:
    name: azure-cli
    description: "Azure CLI"
    baseurl: https://packages.microsoft.com/yumrepos/azure-cli
    enabled: no
    gpgcheck: yes
    repo_gpgcheck: yes
    gpgkey:
      - https://packages.microsoft.com/keys/microsoft.asc
  become: true
  when:
    - ansible_distribution == "CentOS"

- name: az / install azure-cli centos7
  yum:
    name: azure-cli
    state: latest
    enablerepo: "azure-cli"
  become: true
  when:
    - ansible_distribution == "CentOS"
    - ansible_distribution_major_version == "7"


- name: az / install azure-cli centos8
  dnf:
    name: azure-cli
    state: latest
    enablerepo: "azure-cli"
  become: true
  when:
    - ansible_distribution == "CentOS"
    - ansible_distribution_major_version == "8"
```

### 2. az login
az コマンドを使用するために、初回ログインを実施する

```
% az login --use-device-code
To sign in, use a web browser to open the page https://microsoft.com/devicelogin and enter the code XXXXXXXXX to authenticate.
```

https://microsoft.com/devicelogin

![スクリーンショット 2021-12-11 16.23.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/10646694-2a55-2d5f-1e62-7ca7af92aab5.png)

`az login --use-device-code`で出力された code (例:`XXXXXXXXX`)を入力する

![スクリーンショット 2021-12-11 16.23.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/0bbdddd6-e9fa-5a71-7e18-3383555b40f7.png)

上記で認証が終わったので、サーバに戻って Enter を入力すると認証が完了する



### 3. backend 用 Blob 作成

Terraform のバックエンドとして State ファイル置き場にするストレージアカウント・Blob コンテナを作成する

```sh:blob_container.sh
#!/bin/bash

RESOURCE_GROUP_NAME=tfstate
STORAGE_ACCOUNT_NAME=tfstate$RANDOM
CONTAINER_NAME=tfstate
AZURE_LOCATION=japaneast

# Create resource group
az group create --name $RESOURCE_GROUP_NAME --location $AZURE_LOCATION

# Create storage account
az storage account create --resource-group $RESOURCE_GROUP_NAME --name $STORAGE_ACCOUNT_NAME --sku Standard_LRS --encryption-services blob -l $AZURE_LOCATION

# Create blob container
az storage container create --name $CONTAINER_NAME --account-name $STORAGE_ACCOUNT_NAME
```

```sh:出力例(一部特定情報はマスク)
% ./blob_container.sh                
{
  "id": "/subscriptions/<subscription_id>/resourceGroups/tfstate",
  "location": "japaneast",
  "managedBy": null,
  "name": "tfstate",
  "properties": {
    "provisioningState": "Succeeded"
  },
  "tags": null,
  "type": "Microsoft.Resources/resourceGroups"
}
{
  "accessTier": "Hot",
  "allowBlobPublicAccess": true,
  "allowCrossTenantReplication": null,
  "allowSharedKeyAccess": null,
  "azureFilesIdentityBasedAuthentication": null,
  "blobRestoreStatus": null,
  "creationTime": "2021-12-11T14:45:37.543633+00:00",
  "customDomain": null,
  "defaultToOAuthAuthentication": null,
  "enableHttpsTrafficOnly": true,
  "enableNfsV3": null,
  "encryption": {
    "encryptionIdentity": null,
    "keySource": "Microsoft.Storage",
    "keyVaultProperties": null,
    "requireInfrastructureEncryption": null,
    "services": {
      "blob": {
        "enabled": true,
        "keyType": "Account",
        "lastEnabledTime": "2021-12-11T14:45:37.621755+00:00"
      },
      "file": {
        "enabled": true,
        "keyType": "Account",
        "lastEnabledTime": "2021-12-11T14:45:37.621755+00:00"
      },
      "queue": null,
      "table": null
    }
  },
  "extendedLocation": null,
  "failoverInProgress": null,
  "geoReplicationStats": null,
  "id": "/subscriptions/<subscription_id>/resourceGroups/tfstate/providers/Microsoft.Storage/storageAccounts/tfstate19540",
  "identity": null,
  "immutableStorageWithVersioning": null,
  "isHnsEnabled": null,
  "keyCreationTime": {
    "key1": "2021-12-11T14:45:37.621755+00:00",
    "key2": "2021-12-11T14:45:37.621755+00:00"
  },
  "keyPolicy": null,
  "kind": "StorageV2",
  "largeFileSharesState": null,
  "lastGeoFailoverTime": null,
  "location": "japaneast",
  "minimumTlsVersion": "TLS1_0",
  "name": "tfstate<RANDOM_NUMBER>”,
  "networkRuleSet": {
    "bypass": "AzureServices",
    "defaultAction": "Allow",
    "ipRules": [],
    "resourceAccessRules": null,
    "virtualNetworkRules": []
  },
  "primaryEndpoints": {
    "blob": "https://tfstate<RANDOM_NUMBER>.blob.core.windows.net/",
    "dfs": "https://tfstate<RANDOM_NUMBER>.dfs.core.windows.net/",
    "file": "https://tfstate<RANDOM_NUMBER>.file.core.windows.net/",
    "internetEndpoints": null,
    "microsoftEndpoints": null,
    "queue": "https://tfstate<RANDOM_NUMBER>.queue.core.windows.net/",
    "table": "https://tfstate<RANDOM_NUMBER>.table.core.windows.net/",
    "web": "https://tfstate<RANDOM_NUMBER>.z11.web.core.windows.net/"
  },
  "primaryLocation": "japaneast",
  "privateEndpointConnections": [],
  "provisioningState": "Succeeded",
  "publicNetworkAccess": null,
  "resourceGroup": "tfstate",
  "routingPreference": null,
  "sasPolicy": null,
  "secondaryEndpoints": null,
  "secondaryLocation": null,
  "sku": {
    "name": "Standard_LRS",
    "tier": "Standard"
  },
  "statusOfPrimary": "available",
  "statusOfSecondary": null,
  "tags": {},
  "type": "Microsoft.Storage/storageAccounts"
}

There are no credentials provided in your command and environment, we will query for account key for your storage account.
It is recommended to provide --connection-string, --account-key or --sas-token in your command as credentials.

You also can add `--auth-mode login` in your command to use Azure Active Directory (Azure AD) for authorization if your login account is assigned required RBAC roles.
For more information about RBAC roles in storage, visit https://docs.microsoft.com/azure/storage/common/storage-auth-aad-rbac-cli.

In addition, setting the corresponding environment variables can avoid inputting credentials in your command. Please use --help to get more information about environment variable usage.
{
  "created": true
}
```

### 4. サービスプリンシパル作成
サービスプリンシパルを作成して環境変数を設定する

```
az ad sp create-for-rbac --name terraform --role Contributor
```

```sh:出力例(パラメータはマスクでテキトーな値)
% az ad sp create-for-rbac --name terraform --role Contributor

Creating 'Contributor' role assignment under scope '/subscriptions/12345678-abcd-efgh-ijkl-123456789abc’
The output includes credentials that you must protect. Be sure that you do not include these credentials in your code or check the credentials into your source control. For more information, see https://aka.ms/azadsp-cli
'name' property in the output is deprecated and will be removed in the future. Use 'appId' instead.
{
  "appId": "87654321-4321-abcd-efgh-123456789abc",
  "displayName": "terraform",
  "name": "87654321-4321-abcd-efgh-123456789abc",
  "password": “XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX”,
  "tenant": “abcdefgh-abcd-4321-efgh-123456789abc"
}
```

```sh:環境変数設定
export ARM_SUBSCRIPTION_ID=12345678-abcd-efgh-ijkl-123456789abc
export ARM_CLIENT_ID=87654321-4321-abcd-efgh-123456789abc
export ARM_CLIENT_SECRET=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
export ARM_TENANT_ID=abcdefgh-abcd-4321-efgh-123456789abc
```

### 5. terraform init
下記、terraform の state　ファイルのバックエンドを azure のストレージに設定する例

<RANDOM_NUMBER>箇所は、3.で作成した実際の名前に置き換える

```tf:main.tf
terraform {
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "=2.89.0"
    }
  }
    backend "azurerm" {
        resource_group_name  = "tfstate"
        storage_account_name = "tfstate<RANDOM_NUMBER>"
        container_name       = "tfstate"
        key                  = "terraform.tfstate"
    }

}

provider "azurerm" {
  features {}
}
```

```
terraform init
terraform plan
```

```
% terraform init       

Initializing the backend...

Successfully configured the backend "azurerm"! Terraform will automatically
use this backend unless the backend configuration changes.

Initializing provider plugins...
- Finding hashicorp/azurerm versions matching "2.89.0"...
- Installing hashicorp/azurerm v2.89.0...
- Installed hashicorp/azurerm v2.89.0 (self-signed, key ID XXXXXXXXXXXXXXXX)

Partner and community providers are signed by their developers.
If you'd like to know more about provider signing, you can read about it here:
https://www.terraform.io/docs/cli/plugins/signing.html

Terraform has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that Terraform can guarantee to make the same selections by default when
you run "terraform init" in the future.

Terraform has been successfully initialized!

You may now begin working with Terraform. Try running "terraform plan" to see
any changes that are required for your infrastructure. All Terraform commands
should now work.

If you ever set or change modules or backend configuration for Terraform,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.

% terraform plan

No changes. Infrastructure is up-to-date.

This means that Terraform did not detect any differences between your
configuration and real physical resources that exist. As a result, no
actions need to be performed.

```

上記実行後に、コンテナ内を見るとステートファイルが作成されていることが見える

![スクリーンショット 2021-12-12 0.22.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/ab4aeab7-5c2d-0492-4a89-7d32ac1522f0.png)


## Terraform Code

コード自体は長くなったため、下記 GitHub で公開している
https://github.com/suzuyu/azure-terraform-public/tree/v1.0

ディレクトリ構成は正解がわかってないが、stateファイルをある程度分割しないと運用が厳しいのが他クラウドで実感したので、下記のように分けた


- ディレクトリ方針
    - 環境ごとにディレクトリを分ける (環境差分に備えて)
    - リージョン単位でディレクトリを分ける
    - リソースグループ単位でディレクトリを分ける (広過ぎないようにする)
    - リソースグループ内でも運用頻度が高い or 運用単位が分かれそうなものは state ファイルを分けるためにディレクトリを掘る (セキュリティやピアリングなど)
    - トップ(./)での共通な.tfファイルは作成しない(全て modules もしくはディレクトリ直下)
    - modules を切って共通化する。hub, spoke で設計が分かれるものはディレクトリを分ける

modules で共通化したのに、env 配下を variables ファイルを分けて管理するのは、二度手間感があるのでしない方がいいかもしれない (本コードはしている)


## 構築

[Terraform Code](#terraform-code)を使用して構築

下記順番で実施 (hub を作成して spoke を作成してから vnet peering をする)

```txt
./env/test/japaneast/hub/
./env/test/japanwest/hub/
./env/test/japaneast/hub/vnet/
./env/test/japanwest/hub/vnet/
./env/test/japaneast/hub/virtual-machines/
./env/test/japanwest/hub/virtual-machines/
./env/test/japaneast/spoke1/
./env/test/japanwest/spoke1/
./env/test/japaneast/spoke1/vnet/
./env/test/japanwest/spoke1/vnet/
./env/test/japaneast/spoke1/virtual-machines/
./env/test/japanwest/spoke1/virtual-machines/
./env/test/japaneast/hub/vnet/spoke_peering/
./env/test/japanwest/hub/vnet/spoke_peering/
```

下記は構築後のリソース表示例

![スクリーンショット 2022-04-03 13.16.42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/c4105210-578f-1f3f-a492-00c048838e85.png)

![スクリーンショット 2022-04-03 13.14.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/801ae546-1ddf-c3d4-ee29-c30845ee4eb3.png)

![スクリーンショット 2022-04-03 13.17.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/d51c231a-bfc1-8884-86bb-34cfc2bdda72.png)

![スクリーンショット 2022-04-03 13.17.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/4001d76b-5340-d9a9-bc45-23a29cde3b98.png)



## 疎通試験

Ping / Traceroute で「Spoke間」「オンプレ・Spoke間」の疎通試験結果を記載する

### Spoke 間 japaneast → japanwest
japaneast の Linux VM (172.22.16.4) から japanwest の Windows VM (172.23.16.4) ※へ疎通試験する
※ windows は初期設定では ping 許可されてないので要設定 ([設定方法](#windows-ping-許可))

Ping
![スクリーンショット 2022-04-02 16.11.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/6a10d69e-71e5-b06d-e31a-280214c920cb.png)

Traceroute
![スクリーンショット 2022-04-03 13.20.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/b7e803b4-e5a5-2bd4-a460-b4599adec63f.png)

### Spoke 間 japanwest → japaneast
japaneast の Windows VM (172.23.16.4) から japanwest の Linux Windows VM (172.22.16.4) へ疎通試験する

Ping
![スクリーンショット 2022-04-02 16.07.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/f265cdcf-7445-5a99-9a8c-f7171eab3b24.png)

Traceroute
![スクリーンショット 2022-04-03 13.29.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/2c84c9a4-e93e-b5cf-85e4-a79f70eeff28.png)

### オンプレ → Spoke
オンプレ VM (192.168.129.13) から各 Azure VM へ疎通試験する

Ping
```txt
% ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
2: ens192: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 00:0c:29:XX:XX:XX brd ff:ff:ff:ff:ff:ff
    inet 192.168.129.13/24 brd 192.168.129.255 scope global dynamic noprefixroute ens192
       valid_lft 80532sec preferred_lft 80532sec
    inet6 fe80::XXXX:X:X:X/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever

% ping 172.22.16.4
PING 172.22.16.4 (172.22.16.4) 56(84) bytes of data.
64 bytes from 172.22.16.4: icmp_seq=1 ttl=63 time=11.1 ms
64 bytes from 172.22.16.4: icmp_seq=2 ttl=63 time=9.78 ms
64 bytes from 172.22.16.4: icmp_seq=3 ttl=63 time=11.4 ms
^C
--- 172.22.16.4 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2004ms
rtt min/avg/max/mdev = 9.777/10.772/11.396/0.711 ms

% ping 172.23.16.4
PING 172.23.16.4 (172.23.16.4) 56(84) bytes of data.
64 bytes from 172.23.16.4: icmp_seq=1 ttl=125 time=21.8 ms
64 bytes from 172.23.16.4: icmp_seq=2 ttl=125 time=19.7 ms
64 bytes from 172.23.16.4: icmp_seq=3 ttl=125 time=21.10 ms
64 bytes from 172.23.16.4: icmp_seq=4 ttl=125 time=19.4 ms
^C
--- 172.23.16.4 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3003ms
rtt min/avg/max/mdev = 19.363/20.719/21.965/1.181 ms
```

Traceroute ※ VPN 箇所は mtu 1291 に調整している

```txt
% tracepath 172.22.16.4                                                
 1?: [LOCALHOST]                      pmtu 1500
 1:  192.168.129.250                                       0.700ms 
 1:  192.168.129.250                                       0.589ms 
 2:  192.168.129.250                                       0.367ms pmtu 1291
 2:  172.22.16.4                                          10.996ms reached
     Resume: pmtu 1291 hops 2 back 2 

% tracepath 172.23.16.4
 1?: [LOCALHOST]                      pmtu 1500
 1:  192.168.129.250                                       0.522ms 
 1:  192.168.129.250                                       0.322ms 
 2:  192.168.129.250                                       0.334ms pmtu 1291
 2:  172.23.3.254                                         20.402ms asymm  3 
 3:  172.23.16.4
```

### spoke → オンプレ
japaneast traceroute
![スクリーンショット 2022-04-03 13.37.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/e10a094f-1e98-7b2d-2496-fc8a877ac931.png)


japanwest traceroute
![スクリーンショット 2022-04-03 13.35.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/97566e8a-0e4b-65e4-e50c-3ede513667d6.png)


上記から、リージョン間の spoke 間で、Hub を経由した通信が可能になったことが確認できた
また、spoke から同じく hub 経由してオンプレへも疎通できることが確認できた
VM のサブネットのゲートウェイおよび vgw のアドレスは icmp (TTL切れ) を返さない動作のように見える


以上で、構築・疎通試験(想定通りルーティングできているか試験)完了

## 費用
最後に Azure 費用いついて今回のリソースを中心に概要を記載する

Azure は[無料枠](https://azure.microsoft.com/ja-jp/free/search/)があるが、12ヶ月など期間付きが多く、通常価格も Network リソースが割と高いため無料期間が終わったら個人的な勉強でインフラを維持するのは難しそう

通常価格は 2022.04.02時点 (1 USD = 122.71 JPY) での価格URLからの表示から記載

| Azure サービス | 毎月の無料分	 | 無料期間 | 通常価格 | 価格 URL | 備考 |
|:-:|:-:|:-:|:-:|:-:|:-:|
| VPN Gateway | VpnGw1 ゲートウェイ タイプ 750 時間 | 12 か月 | **¥17,019.8770/月** | https://azure.microsoft.com/ja-jp/pricing/details/vpn-gateway/ | BGP 等を試すには VpnGw1 以上が必要 |
| Virtual Machines - Linux | B1s バースト可能仮想マシン 750 時間 | 12 か月 | ¥1,218.2649/月(B1s), ¥573.8385/月 (B1ls) | https://azure.microsoft.com/ja-jp/pricing/details/virtual-machines/linux/ |   |
| Virtual Machines - Windows | B1s バースト可能仮想マシン 750 時間 | 12 か月 | ¥1,218.2649/月 | https://azure.microsoft.com/ja-jp/pricing/details/virtual-machines/windows/ |  |
| Virtual Network | 50 仮想ネットワーク | 常に |   |   |   |
| Managed Disks | 64 GB (P6) のソリッド ステート ドライブ 2 個の SSD ストレージに加えて、1 GB のスナップショットと 200 万件の I/O 操作 | 12 か月 |  ¥188.49 (Standard_LRS 32 GiB)  | https://azure.microsoft.com/ja-jp/pricing/details/managed-disks/ |   |
| Bandwidth (データ転送) | 送信 15 GB(12 か月) 送信 5 GB(常に) | 12 か月 |   |   |   |
| Blob Storage | 5 GB のローカル冗長ストレージ (LRS) ホット ブロック、20,000 件の読み取りおよび 10,000 件の書き込み操作 | 12 か月 | ¥2.4542/GB (ホット) | https://azure.microsoft.com/ja-jp/pricing/details/storage/blobs/ |   |
| Azure Firewall | 無 | 無 | **¥114,120.672**(デプロイメント*31日分) + 処理 GB あたり ¥1.964 | https://azure.microsoft.com/ja-jp/pricing/details/azure-firewall/ |   |

特に、VPN と Firewall は勉強の環境で使用するには高いので要注意 (今回のテスト構成では、無料枠に収めるため、VPN は片リージョンのみ、Firewall は少し試したが高過ぎて消したためコードのみで、LinuxVM でのルーティング構成にしている)

下記 Azure Firewall を 1リソース立てただけで 1日 約3500円請求された例 
(下記は2022.01での状態なので ¥/$ が若干今より低い。現時点(2022.04)だと約3800円/日になる)
![スクリーンショット 2022-04-03 13.50.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/97e96466-6f56-7430-76fb-b5702f9c27c7.png)


本構成では、無料期間中で、片リージョンだと 1500 円程度、2リージョンだと 5000 円程度になる見込み (ざっくりなので参考程度の価格)
Windows VM を無料枠にあったので使ってみたが、Diskサイズが127GB以上を要求されるので、最低スペックの Linux の方がテストには向いているかもしれない

下記は本構成での現状のコスト予想結果例
![スクリーンショット 2022-04-03 13.46.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/c6161f72-5782-388d-f58e-58b378b6bde4.png)


## その他
### 参考リンク

https://docs.microsoft.com/ja-jp/cli/azure/install-azure-cli-linux?pivots=dnf

https://docs.microsoft.com/ja-jp/cli/azure/create-an-azure-service-principal-azure-cli

https://docs.microsoft.com/ja-jp/azure/developer/terraform/get-started-cloud-shell-bash?tabs=bash

https://docs.microsoft.com/ja-jp/azure/storage/blobs/authorize-data-operations-cli#authorize-with-the-account-access-key

https://docs.microsoft.com/ja-jp/azure/developer/terraform/store-state-in-azure-storage?tabs=azure-cli

https://docs.microsoft.com/ja-jp/azure/vpn-gateway/tutorial-create-gateway-portal

https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs

https://docs.microsoft.com/ja-jp/azure/developer/terraform/hub-spoke-on-prem

https://docs.microsoft.com/ja-jp/azure/developer/terraform/hub-spoke-hub-network

https://docs.microsoft.com/ja-jp/azure/vpn-gateway/vpn-gateway-about-vpngateways#what-is-a-virtual-network-gateway

https://azure.microsoft.com/ja-jp/free/search/

### Windows Ping 許可

Windows はデフォルトで外部からの Ping 許可がないので、試験では下記手順で有効化しておく必要がある

`Windows Defender Firewall` > `Advanced settings`を開く

![スクリーンショット 2022-01-15 12.41.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/fa595193-7601-6171-5b8a-d4215070df9d.png)

`Virtual Machine Monitoring (Echo Request -ICMPv4-In)` を右クリックして`Enable Rule`をクリックして、有効化する

![スクリーンショット 2022-01-15 12.37.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/92cc8284-7cec-3d9f-5443-54f7c8dd0279.png)

上記で Windows への Ping 許可完了