Bicep でVPN gateway をデプロイする

こんにちは。前回はBicep の基本的なことをまとめました。今回は実際に使ってみようと思います。
モチベーションとしては、繰り返しポータルでポチポチする作業をなくしたい＆たまに設定を間違えてリソース作成してしまうことを防ぎたいことにあります。
VPN gateway をデプロイする最小限の構成からやってみました。
## 概要
ゲートウェイサブネットを持つ仮想ネットワークを作成し、
パブリックIPアドレスを作成し、
VPN gateway を作成する。
Bicep テンプレートファイルだけでデプロイする。
### 参考
参考の公式ページは[これ](https://learn.microsoft.com/ja-jp/azure/templates/microsoft.network/virtualnetworkgateways?pivots=deployment-language-bicep)です。
さらにポータルでVPN gateway を作成してみて、JSON ファイルを抜き出しておきます。
### 設定
VPN gateway の設定は以下の通り。
* japan east　
* ゲートウェイの種類：VPN (ほかにExpress Route がある項目)
* VPN の種類：ルートベース
* アクティブアクティブ：無効（デフォルトがどっちなのかは不明なので明示的に無効にする）
* SKU：VPNGW1 （検証なので）

とりあえずこのくらいで挑戦。
## 作業
「概要」で示した通りリソースをデプロイしていきます。
### Bicep を書くときの注意点
私が書くときはたくさん回り道をしたので備忘録として注意点をば。
* リソースごとに指定したいパラメーターをあらかじめ列挙して名前を付ける
* デプロイする順番にリソースを列挙する
* 上記の公式参考ページに書かれていない定義はjson ファイルをそのまま真似してみる

以降では、リソースごとにパラメーターの定義、リソースの定義を説明していきます。
### 仮想ネットワーク
#### パラメーター（仮想ネットワーク）
仮想ネットワークのパラメーターは、
* 名前
* アドレス空間
    * サブネットの名前
    * サブネットのアドレス空間

くらいかと思います。注意点の通り、名前を先に付けます。
| パラメーター | Bicep内での名前(シンボリック名） | 設定したい値 |
|:-:|:-:|:-:|
|  VNetの名前 |  VNetVName |vnet-hub  |
|  アドレス空間 | VNetAddress  |10.0.0.0/16   |
|   サブネットの名前|gatewaysubnetName   |GatewaySubnet   |
|   サブネットのアドレス空間|vpngwAddress   | 10.0.255.0/24  |


VPN gateway を置くサブネットの名前は'GatewaySubnet'でなければなりません。
ではパラメーターの宣言は下記のようになります。.bicep ファイルの先頭に書きます。
```
param VNetName string = 'vnet-hub'
param VNetAddress string = '10.0.0.0/16'
param gatewaysubnetName string ='GatewaySubnet'
param gatewaysubnetAddress string = '10.0.255.0/24'
```
パラメーターの宣言の最後に、「= 値」とつけると既定値にできます。この既定値はパラメーターファイルで渡す値よりも優先されます。

#### リソース（仮想ネットワーク）
リソースの宣言に移ります。
先にコードから。
```
resource VNet 'Microsoft.Network/virtualNetworks@2022-11-01' = {
  name: VNetName
  location: location
  properties: {
    addressSpace: {
      addressPrefixes: [
        VNetAddress
      ]
    }
    subnets: [
      {
        name: gatewaysubnetName
        properties: {
          addressPrefix:gatewaysubnetAddress
        }
      }
     }
    ]
  }
}
```


:::note info
それぞれのパラメーターがどこに出てくるか確認しましょう。
* properties
    * addressSpace
        * addressPrefixes: [**VNet のアドレス空間**]
    * subnets
        *name: **サブネットの名前**
        * properties
            * addressPrefixes: **サブネットのアドレス空間**

:::
VNet のアドレス空間は、プロパティ→アドレス空間→アドレスプレフィックスに書き、
サブネットの定義はプロパティの中に並べて書くようになっています。

違う仮想ネットワークで恐縮ですが、参考のjson を貼っておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617557/18559fde-2c00-2baf-243c-c45d79f01629.png)
リソースID は、Bicep ファイルの実行のときに指定できるので省略しました。そのほか、明示的に書かなくてもよいものはたくさんあります。

### パブリックIP アドレス
パブリックIP アドレスもVPN gateway とは別に書きます。
#### パラメーター（パブリックIP アドレス）
仮想ネットワークの時と同じく表に書き出すと、
| パラメーター | Bicep内での名前(シンボリック名） | 設定したい値 |
|:-:|:-:|:-:|
|  IPアドレスの名前 |  publicIPName | pip-vpngw  |
|  SKU | publicIpsku  |'Standard'   |
|   アドレスの割り当て| publicIpAllocation   | 'Dynamic'   |

:::note warn
アドレスの割り当ては、SKU がStandard の時は静的Static にしないといけないので、ここで宣言していますが、あとでハードコードしました。
:::
```
param publicIPName string ='pip-vpngw'
param location string = 'japaneast'
param publicIpsku string = 'Standard'
//param publicIpAllocation = 'Dynamic'
```
#### リソース（パブリックIP アドレス）

```
resource publicIp 'Microsoft.Network/publicIPAddresses@2022-11-01' = {
  name: publicIPName
  location: location
  properties: {
    publicIPAllocationMethod: 'Static'
  }
  sku: {
    name: publicIpsku
    tier: 'Regional'
  }
}
```
これは短いのでわかりますね。
前述の通り、アドレスの割り当てはハードコードで'Static'にしました。
### VPN gateway
さて本丸です。パラメーターは多くないのですが、他のリソースからとってくる情報があり複雑になります。
#### パラメーター（VPN gateway）
宣言するパラメーターは名前のほかに、SKU とアクティブアクティブの有効無効くらいです。
慣れてきたので宣言だけ。
```
param vpngwName string = 'vpngw'
param vpngwSKU string = 'VpnGw1'
param activeactive bool = false
```
有効無効はstring ではなくbool で、今回は無効にしたいのでfalse です。'(クオーテーション)はつけません。
#### リソース（VPN gateway）
さらに、VPN gateway をデプロイするには、
1. デプロイするサブネット（必ずゲートウェイサブネットとなる）を指定し、
1. 紐づけるパブリックIPアドレスを指定する必要があります。

指定するにはリソースID を書くのですが、どうやって取得するのかというと、「リソースのシンボリック名.id」のように書くとよいようです。
```
resource vpngw 'Microsoft.Network/virtualNetworkGateways@2022-11-01' = {
  name: vpngwName
  location: location
  properties: {
    ipConfigurations: [
      {
        id: 'string'
        name: 'string'
        properties: {
          privateIPAllocationMethod: 'Dynamic'
          publicIPAddress: {
            id: publicIp.id
          }
          subnet: {
            id: VNet.properties.subnets[0].id
          }
        }
      }
    ]
    gatewayType: 'Vpn'
    vpnType: 'RouteBased'
    sku: {
      name: vpngwSKU
      tier: vpngwSKU
    }
    activeActive: activeactive
  }
}
```
:::note info
注目すべきは、
* properties
    * ipConfiguration
        * properties
            * publicIPAddress
                * **id**
            * subnet
                * **id**

:::
パブリックIPアドレスは、リソースの宣言で使った名前（シンボリック名）である"publicIP" を使って、「publicIp.id」と書いています。
ゲートウェイサブネットは、仮想ネットワークのリソースの宣言で使った名前（シンボリック名）である"VNet"を使って「VNet.properties.subnets[0].id」と書いています。
"." (ドット）を使って、インデントを下っていくような形で欲しい部分のID を取得します。

[]は配列を表すので、複数あるサブネットの中から指定しているのでしょう。ゲートウェイサブネットは仮想ネットワーク内に１個しか置けないので、必ず１個目のサブネット（項番は0）になるのでしょう。

:::note warn
ipConfigurations の'name' と'id' が気になりますが、'string' だけでいいので書いておかないとデプロイに失敗します。
:::
## さあデプロイ
今までのを全部書いてvpngw.bicep という名前で保存します。
ポータルでリソースグループを適当にデプロイします（読み間違いではありません、ポータルでぽちぽちしてください）。
そうしたらAzure PowerShell を使ってサインイン、デプロイの実行を行います。
```
Connect-AzAccount
new-AzResourceGroupDeployment -TemplateFile vpngw.bicep
```
実行すると、リソースグループ名を聞かれるので、入力してエンター。
ターミナルが待機になったら、ポータルでリソースグループのデプロイ画面を見ましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617557/ce23bb5f-9820-46da-ce57-67d35285173f.png)

デプロイ中です！
VPN gateway はデプロイに２０分以上かかるので寝る支度でもします。

デプロイできました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617557/a257dc10-a2e3-f38d-0d1f-653d593236c8.png)

デプロイされたVPN gateway のjson を見てみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617557/010b3628-591a-c2a9-b7aa-616cc41bff2f.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617557/ae7a2f9d-3b6d-8042-0add-af21e3abc1d8.png)
渡した通りに設定されていますね。

tunnelIpAddresses はもちろんデプロイしたパブリックIPアドレスのことです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3617557/f7d93bc7-e231-ff87-966f-a07e9ada7472.png)

## 出くわしたエラー
>New-AzResourceGroupDeployment: Cannot retrieve the dynamic parameters for the cmdlet. C:\Users\maade\biceptemplate\vpngw.bicep(11,31) : Error BCP279: Expected a type at this location. Please specify a valid type expression or one of the following types: "array", "bool", "int", "object", "string".

パラメーターの宣言時に、string とか書き忘れたのが原因
***

>Warning BCP081: Resource type "Microsoft.Network/virtualNetworks@2023-11-01" does not have types available. Bicep is unable to validate resource properties prior to deployment, but this will not block the resource from being deployed.

API バージョンが見つからないエラー。最新のバージョンを指定したときこのエラーが出た。工夫の方法がわからず、API バージョンを１年くらい古いのに替えたら回避できた。
***

>New-AzResourceGroupDeployment: 14:29:00 - Error: Code=InvalidTemplate; Message=Deployment template validation failed: 'The provided value for the template parameter 'activeactive' is not valid. Expected a value of type 'Boolean', but received a value of type 'String'. Please see https://aka.ms/arm-create-parameter-file for usage details.'.   

activeactive のところで、bool なのでクオーテーションなしでfalse と書けばいいところ、'false' と書いたことで出たエラー。
***
>Status Message: Standard sku publicIp /subscriptions/xxxxxxxxxxx8/resourceGroups/bicep-tamesu/providers/Microsoft.Network/publicIPAddresses/pip-vpngw must have AllocationMethod set to Static. (Code: StandardSkuPublicIPAddressesMustBeStatic)

エラー文のままだが、standard SKU では、動的IPアドレスを振れませんエラー
***
>New-AzResourceGroupDeployment: 14:35:43 - The deployment '84ce9465-51d1-4d45-b1c9-e1d2ceafa683' failed with error(s). Showing 1 out of 1 error(s).
Status Message: Cannot parse the request. (Code: InvalidRequestFormat)
> - System.ArgumentException: item.Name is null (Code:InvalidRequestFormat)

これはまじで困った。VPN gateway のipConfigurations にid を 'string' として入れたら回避できた。他の人のコードと見比べないとわからなかった。
***

他にも、ターミナルの実行環境によるエラーとか、単純にファイル名間違ってるとかのエラーはたくさんでました。基本的なことですがご確認ください。

今回はここまで。おつかれさまでした。