Azure Application Gateway は PowerShell で作るもの

とあるお客様から「Application Gateway の機能 (とくにルーティング) がどんなものか知りたい」とおっしゃるので、ちょっとやってみたらとても苦労したので、備忘録としてここに残しておく。

# デモの構成
バックエンドに仮想マシンを立てて Web サーバーを構築して...というのがとても面倒くさく思い、Web Apps で賄いました。

![agw.png](https://qiita-image-store.s3.amazonaws.com/0/230264/68f239fe-76c1-599b-e8b9-c1e5016011db.png)

アクセス URL のパスが "/site01/" なら「アプリ01」へ、"/site02/" なら「アプリ02」へとルーティングしようと思います。
後で、Azure AD 認証も組み込もうと思うので、Application Gateway にカスタムドメインの SSL 証明書もアップロードしておきます。

# Application Gateway に実装したいこと
- SSL オフロード
- バックエンドは Web Apps (FQDN で指定)
- URL パスベースでルーティング
- セッションアフィニティを有効

これらをすべて実装するには、Azure ポータルでは作れないので、全部 PowerShell を使って作ってみることにしました。

# PowerShell スクリプト
そして出来上がった PowerShell スクリプトがこちら。
要素間の関連がちょっと厳しいので、以下のようにいくつか分けて実行しました。
※なお、それぞれ実行するのに、10 ～ 20 分くらいかかる。

まずは基本的な Application Gateway を作成します。

```PowerShell
## AzureRM モジュールをインポート
# Import-Module AzureRM

## Azure にサインイン 
# Login-AzureRmAccount

# Azure サブスクリプションを選択
Select-AzureRmSubscription -Subscription xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

# リソースグループの名前
$RGName = 'demodemo-rg'
# パブリック IP アドレスの名前
$pipName = 'demodemo-agw-ip'
# 仮想ネットワークの名前
$vnetName = 'demodemo-vnet'
# アプリケーションゲートウェイ用サブネットの名前
$subnetName = 'AGW-subnet'
# アプリケーションゲートウェイの名前
$agwName = 'demodemo-agw'

############################################################
#
# 基本的なアプリケーションゲートウェイの作成
#
############################################################

# 以下のリソースは事前に作成しておく

# 仮想ネットワークを取得
$vnet = Get-AzureRmVirtualNetwork -ResourceGroupName $RGName -Name $vnetName
# アプリケーションゲートウェイ用サブネットを取得
$subnet = Get-AzureRmVirtualNetworkSubnetConfig -VirtualNetwork $vnet -Name $subnetName
# パブリック IP アドレスを取得
$pip = Get-AzureRmPublicIpAddress -ResourceGroupName $RGName -Name $pipName

# ここから Application Gateway の作成

# IP 構成を作成
$gipconfig = New-AzureRmApplicationGatewayIPConfiguration -Name gatewayIP01 -Subnet $subnet
# バックエンドプールを作成
$pool = New-AzureRmApplicationGatewayBackendAddressPool -Name appGatewayBackendPool -BackendFqdns 'sitedef.azurewebsites.net'
# プローブ用のステータスコードを定義
$match = New-AzureRmApplicationGatewayProbeHealthResponseMatch -StatusCode 200-401
# プローブを作成
$probeconfig = New-AzureRmApplicationGatewayProbeConfig -Name webappprobe -Protocol Http -Path / -Interval 30 -Timeout 120 -UnhealthyThreshold 3 -PickHostNameFromBackendHttpSettings -Match $match
# バックエンドの HTTP 設定を作成
$poolSetting = New-AzureRmApplicationGatewayBackendHttpSettings -Name appGatewayBackendHttpSettings -Port 80 -Protocol Http -CookieBasedAffinity Enabled -RequestTimeout 120 -PickHostNameFromBackendAddress -Probe $probeconfig
# フロントエンド IP ポートを作成
$fp = New-AzureRmApplicationGatewayFrontendPort -Name frontendport01 -Port 80
# フロントエンド IP 構成を作成
$fipconfig = New-AzureRmApplicationGatewayFrontendIPConfig -Name fipconfig01 -PublicIPAddress $pip
# HTTP リスナーを作成
$listener = New-AzureRmApplicationGatewayHttpListener -Name listener01 -Protocol Http -FrontendIPConfiguration $fipconfig -FrontendPort $fp
# ルーティング規則を構成
$rule = New-AzureRmApplicationGatewayRequestRoutingRule -Name rule01 -RuleType Basic -BackendHttpSettings $poolSetting -HttpListener $listener -BackendAddressPool $pool 

# アプリケーションゲートウェイ SKU を定義
$sku = New-AzureRmApplicationGatewaySku -Name Standard_Small -Tier Standard -Capacity 2

# アプリケーションゲートウェイを作成
$appgw = New-AzureRmApplicationGateway -Name $agwName -ResourceGroupName $RGName -Location JapanEast -BackendAddressPools $pool -BackendHttpSettingsCollection $poolSetting -Probes $probeconfig -FrontendIpConfigurations $fipconfig  -GatewayIpConfigurations $gipconfig -FrontendPorts $fp -HttpListeners $listener -RequestRoutingRules $rule -Sku $sku
```

ここからは SSL の構成と URL パスベースのルーティング規則を構成します。
以下の要素を追加します。

- フロントエンド IP ポート
- バックエンドプール (Web アプリケーション)
- サーバー証明書

```PowerShell
############################################################
#
# SSL の構成と URL パスベースのルーティング規則
#
############################################################

############################################################
### フロントエンド IP ポートを追加
### バックエンドプールを追加
### サーバー証明書(PFX)を追加 

# アプリケーションゲートウェイを取得
$appgw = Get-AzureRmApplicationGateway -Name $agwName -ResourceGroupName $RGName

# フロントエンド IP ポートを追加
Add-AzureRmApplicationGatewayFrontendPort -ApplicationGateway $appgw -Name frontendport02 -Port 443

# バックエンドプールを追加
Add-AzureRmApplicationGatewayBackendAddressPool -ApplicationGateway $appgw -Name site01BackendPool -BackendFqdns site01a.azurewebsites.net,site01b.azurewebsites.net
Add-AzureRmApplicationGatewayBackendAddressPool -ApplicationGateway $appgw -Name site02BackendPool -BackendFqdns site02a.azurewebsites.net,site02b.azurewebsites.net

# サーバー証明書 (PFX) は事前に準備しておく

# サーバー証明書を追加
$password = ConvertTo-SecureString 'demodemo@201805' -AsPlainText -Force
Add-AzureRmApplicationGatewaySslCertificate -ApplicationGateway $appgw -Name SSLCert02 -CertificateFile 'C:\temp\www.demodemo.pgw.jp.pfx' -Password $password

# アプリケーションゲートウェイに反映
Set-AzureRmApplicationGateway -ApplicationGateway $appgw
```

次に、以下の要素を使って「フロントのリスナー条件と SSL オフロード」を追加します。

- フロントエンド IP ポート
- フロントエンド IP 構成
- サーバー証明書

```PowerShell
############################################################
### フロントのリスナー条件や SSL オフロードの指定

# アプリケーションゲートウェイを取得
$appgw = Get-AzureRmApplicationGateway -Name $agwName -ResourceGroupName $RGName
# フロントエンド IP ポートを取得
$fport = Get-AzureRmApplicationGatewayFrontendPort -ApplicationGateway $appgw -Name FrontendPort02
# フロントエンド IP 構成を取得
$fipconfig = Get-AzureRmApplicationGatewayFrontendIPConfig -ApplicationGateway $appgw -Name fipconfig01
# サーバー証明書を取得
$sslcert = Get-AzureRmApplicationGatewaySslCertificate -ApplicationGateway $appgw -Name SSLCert02

# フロントのリスナー条件や SSL オフロードの指定
Add-AzureRmApplicationGatewayHttpListener -ApplicationGateway $appgw -Name Listener02 -Protocol Https -FrontendIPConfiguration $fipconfig -FrontendPort $fport -SslCertificate $sslcert

# アプリケーションゲートウェイに反映
Set-AzureRmApplicationGateway -ApplicationGateway $appgw
```

さらに、以下の要素を使って「URL パスマップ (パスとバックエンドプールの規則)」を追加します。

- バックエンドプール HTTP 設定
- バックエンドプール (デフォルトと Web アプリケーション)

```PowerShell
############################################################
### URL パスマップを追加

# アプリケーションゲートウェイを取得
$appgw = Get-AzureRmApplicationGateway -Name $agwName -ResourceGroupName $RGName
# バックエンドの HTTP 設定を取得
$poolSetting = Get-AzureRmApplicationGatewayBackendHttpSettings -ApplicationGateway $appgw -Name appGatewayBackendHttpSettings
# バックエンドプールを取得
$defaultpool = Get-AzureRmApplicationGatewayBackendAddressPool -ApplicationGateway $appgw -Name appGatewayBackendPool
$site01Pool = Get-AzureRmApplicationGatewayBackendAddressPool -ApplicationGateway $appgw -Name site01BackendPool
$site02Pool = Get-AzureRmApplicationGatewayBackendAddressPool -ApplicationGateway $appgw -Name site02BackendPool

# パス規則を作成
$site01PathRule = New-AzureRmApplicationGatewayPathRuleConfig -Name site01PathRule -Paths "/site01/*" -BackendAddressPool $site01Pool -BackendHttpSettings $poolSetting
$site02PathRule = New-AzureRmApplicationGatewayPathRuleConfig -Name site02PathRule -Paths "/site02/*" -BackendAddressPool $site02Pool -BackendHttpSettings $poolSetting

# URL パスマップ構成を追加
Add-AzureRmApplicationGatewayUrlPathMapConfig -ApplicationGateway $appgw -Name urlpathmap -PathRules $site01PathRule,$site02PathRule -DefaultBackendAddressPool $defaultpool -DefaultBackendHttpSettings $poolSetting

# アプリケーションゲートウェイに反映
Set-AzureRmApplicationGateway -ApplicationGateway $appgw
```

最後に、以下の要素を使って「ルーティング規則」を追加します。

- HTTP リスナー
- URL パスマップ構成

```PowerShell
############################################################
### ルーティング規則を追加

# アプリケーションゲートウェイを取得
$appgw = Get-AzureRmApplicationGateway -Name $agwName -ResourceGroupName $RGName
# HTTP リスナーを取得
$listener = Get-AzureRmApplicationGatewayHttpListener -ApplicationGateway $appgw -Name Listener02
# URL パスマップ構成を取得
$urlpathmap = Get-AzureRmApplicationGatewayUrlPathMapConfig -ApplicationGateway $appgw -Name urlpathmap

# フロントとバックエンドとのルーティンングルールを指定
Add-AzureRmApplicationGatewayRequestRoutingRule -ApplicationGateway $appgw -Name rule02 -RuleType PathBasedRouting -HttpListener $listener -UrlPathMap $urlpathmap

# アプリケーションゲートウェイに反映
Set-AzureRmApplicationGateway -ApplicationGateway $appgw
```

# アクセス制御も忘れずに
Application Gateway リソースは仮想ネットワークのサブネットに配置します。そのサブネットにネットワーク セキュリティ グループ (NSG) を設定しましょう。
NSG の受信トラフィック規則に、以下のルールを追加します。

| Name | 優先順位 | 発信元 IP | 発信元ポート | 宛先 IP | 宛先ポート | プロトコル | アクセス |
|:---------------------------|:---:|:---------|:---:|:----|:------------|:----|:-----|
| AllowHttpsInBound | 100 | Internet | * | Any | 443 | TCP | 許可 |
| AllowAGWHealthCheck InBound | 110 | Internet | * | Any | 65503-65534 | Any | 許可 |

「AllowHttpsInBound」は、Web サーバーにアクセスする通信を許可するためのルール。
「AllowAGWHealthCheckInBound」はバックエンド正常性のチェックで行う通信を許可するためのルールで、これを許可していないと正常にルーティングしてくれないのでご注意。

![healthchk.png](https://qiita-image-store.s3.amazonaws.com/0/230264/fb50e99e-bf27-1aa0-62fa-ef8677b76d6d.png)

# まとめ
Application Gateway 内の構成を把握しておかないと、Azure ポータルで作るにせよ、PowerShell を使って作るにせよ、迷子になりそうです。
それと細かな設定はやはり PowerShell を使わないと...となるので、一から PowerShell で作りましょう。

あと、Application Gateway には URL を書き換える機能は残念ながらありません。よって、www.demodemo.pgw.jp/site01/ に対する要求は対応するバックエンドへルーティングされますが、その際には /site01/ のパスも含めた URL でバックエンドの Web サーバーにアクセスされます。

# 参考文献
- [Application Gateway を使用した App Service Web Apps の構成](https://docs.microsoft.com/ja-jp/azure/application-gateway/application-gateway-web-app-powershell?WT.mc_id=AZ-MVP-5003113)
- [Azure PowerShell を使用して URL パス ベースのルーティング規則のあるアプリケーション ゲートウェイを作成する](https://docs.microsoft.com/ja-jp/azure/application-gateway/application-gateway-create-url-route-arm-ps?WT.mc_id=AZ-MVP-5003113)
- [Azure PowerShell を使用して SSL termination でアプリケーション ゲートウェイを作成する](https://docs.microsoft.com/ja-jp/azure/application-gateway/application-gateway-ssl-arm?WT.mc_id=AZ-MVP-5003113)