【Azure】Bicepを用いてAzureVMをお手軽に死活監視する。

# 1. はじめに
### 1-1 ご挨拶
初めまして、井村と申します。
AzureのBicepをもちいてAzureVMのお手軽死活監視を作成しました。

お手軽の意味としては診断設定を利用するのではなく、AzureVMを構築後、自動で取得するVM可用性メトリックを利用します。その為、必須コンポーネントはAzureVMのみになります。
※動作確認用でメール通知しますが、それについてはアラートルール＆アクショングループ作ります。

[公式ドキュメント](https://learn.microsoft.com/ja-jp/azure/azure-monitor/vm/tutorial-monitor-vm-alert-availability)にてポータルからポチポチする手順がありますので、それをIaC化しますよって話です。

### 1-2 対象読者
- Azureに興味がある
- Bicepに興味がある

# 2. 構築
### 2-1 フォルダ構成
今回作成したBicepファイルは階層型にしています。「main.bicep」ファイルにパラメーターを記述し「module」フォルダ配下にある各モジュールファイルへパラメーターを渡します。

```
.
├── main.bicep
└── modules
    ├── ag.bicep
    ├── ar.bicep
    └── vm.bicep
```

### 2-2 main.bicep構成
利用するBicepテンプレートは以下になります。
[Github](https://github.com/hawk0722/Azure_Bicep_04_Availability-alert-rule-for-Azure-virtual-machine)にあげてます。

<details><summary>main.bicep</summary>

```bicep:bicep

     1  targetScope = 'subscription'
     2
     3  // Parameters for common
     4  param location string = 'japaneast'
     5  param systemCode string = 'imura5'
     6  param env string = 'dev'
     7
     8  // Parameters for resorce group
     9  param resourceGroupName string = 'rg-${systemCode}-${env}'
    10
    11  // Parameters for virtual machine
    12  param adminUsername string = 'azureuser'
    13  @minLength(12)
    14  @secure()
    15  param adminPassword string
    16
    17  // Parameters for action groups
    18  param emailAddress string = '${systemCode}@sample.com'
    19  param emailReceiversName string = '${systemCode}-${env}'
    20
    21  // deploy resource groups.
    22  resource rg 'Microsoft.Resources/resourceGroups@2022-09-01' = {
    23    name: resourceGroupName
    24    location: location
    25  }
    26
    27  // deploy virtual machine.
    28  module vmModule 'modules/vm.bicep' = {
    29    scope: rg
    30    name: 'Deploy_Virtual_machine'
    31    params: {
    32      location: location
    33      systemCode: systemCode
    34      env: env
    35      adminUsername: adminUsername
    36      adminPassword: adminPassword
    37    }
    38  }
    39
    40  // deploy action groups.
    41  module agModule 'modules/ag.bicep' = {
    42    scope: rg
    43    name: 'Deploy_action_groups'
    44    params: {
    45      emailAddress: emailAddress
    46      emailReceiversName: emailReceiversName
    47      env: env
    48      systemCode: systemCode
    49    }
    50  }
    51
    52  // deploy alert rule.
    53  module alModule 'modules/ar.bicep' = {
    54    scope: rg
    55    name: 'Deploy_alert_rule'
    56    params: {
    57      location: location
    58      env: env
    59      systemCode: systemCode
    60      resourceId: vmModule.outputs.id
    61      actionGroupId: agModule.outputs.id
    62    }
    63  }

```
</details>

補足は以下の通りです。
- 1行目の「target.scope」について今回のファイルはリソースグループを作成するため変更しています。
- 4行目から19行目の「param」でパラメーターを定義します。
- 22行目の「resource」でリソースグループを作成します。
- 28行目以降の「module」で「modules」フォルダの各ファイルへパラメーターを渡します。

### 2-3 ag.bicep構成
利用するBicepテンプレートは以下になります。
「ag.bicep」ファイルはアクショングループを作成するファイルになります。
今回はメール通知するメールアドレスの設定をします。

<details><summary>ag.bicep</summary>

```bicep:bicep

     1  @description('System code')
     2  param systemCode string
     3
     4  @description('Environment')
     5  param env string
     6
     7  @description('Name of action groups')
     8  param agName string = 'ag-${systemCode}-${env}'
     9
    10  @description('Short name of action groups')
    11  @minLength(1)
    12  @maxLength(12)
    13  param groupShortName string = 'ag-${systemCode}'
    14
    15  @description('Name of email Address')
    16  param emailAddress string
    17
    18  @description('Name of email Receivers')
    19  param emailReceiversName string
    20
    21  resource actionGroup 'Microsoft.Insights/actionGroups@2022-06-01' = {
    22    name: agName
    23    location: 'Global'
    24    properties: {
    25      enabled: true
    26      groupShortName: groupShortName
    27      emailReceivers: [
    28        {
    29          emailAddress: emailAddress
    30          name: emailReceiversName
    31          useCommonAlertSchema: true
    32        }
    33      ]
    34
    35    }
    36  }
    37
    38  @description('action group id')
    39  output id string = actionGroup.id
```
</details>

補足は以下の通りです。
- 4行目から19行目の「param」でパラメーターを定義します。
- 21行目の「resource」でアクショングループを作成します。
- 39行目の「output」でアクショングループのidを「main.bicep」へ渡します。渡す理由としては「ar.bicep」であるアラートルール作成時で利用するためです。

### 2-4 vm.bicep構成
利用するBicepテンプレートは以下になります。
「vm.bicep」ファイルは仮想マシンを作成するファイルになります。
[公式ドキュメント](https://learn.microsoft.com/ja-jp/azure/virtual-machines/windows/quick-create-bicep?tabs=CLI)から引用していますが、検証するうえで不要なリソースを削除し、低スペックに置き換えています。
- 不要なリソース：NSG、パブリックIPアドレス、診断設定用のストレージアカウント、仮想マシンのデータディスク等
- スペック変更：VMサイズ、OSディスクの種類

<details><summary>vm.bicep</summary>

```bicep:bicep

     1  @description('Location for all resources.')      
     2  param location string
     3
     4  @description('System code')
     5  param systemCode string
     6
     7  @description('Environment')
     8  param env string
     9
    10  @description('Username for the Virtual Machine.')
    11  param adminUsername string
    12
    13  @description('Password for the Virtual Machine.')
    14  @minLength(12)
    15  @secure()
    16  param adminPassword string
    17
    18  @description('The Windows version for the VM. This will pick a fully patched image of this given Windows ver
    19  @allowed([
    20    '2022-datacenter'
    21    '2022-datacenter-azure-edition'
    22    '2022-datacenter-azure-edition-core'
    23    '2022-datacenter-azure-edition-core-smalldisk'
    24    '2022-datacenter-azure-edition-smalldisk'
    25    '2022-datacenter-core'
    26    '2022-datacenter-core-g2'
    27    '2022-datacenter-core-smalldisk'
    28    '2022-datacenter-core-smalldisk-g2'
    29    '2022-datacenter-g2'
    30    '2022-datacenter-smalldisk'
    31    '2022-datacenter-smalldisk-g2'
    32  ])
    33  param OSVersion string = '2022-datacenter-azure-edition-core-smalldisk'
    34
    35  @description('Size of the virtual machine.')
    36  @allowed([
    37    'Standard_B1s' // 02/04/2023 $12.85/month
    38    'Standard_B1ms' // 02/04/2023 $22.78/month
    39    'Standard_B2s' // 02/04/2023 $45.55/month
    40    'Standard_F1' // 02/04/2023 $79.55/month
    41    'Standard_B2ms' // 02/04/2023 $85.41/month
    42  ])
    43  param vmSize string = 'Standard_B1s'
    44
    45  @description('managedDisk Type of osDisk.')
    46  @allowed([
    47    'PremiumV2_LRS'
    48    'Premium_LRS'
    49    'Premium_ZRS'
    50    'StandardSSD_LRS'
    51    'StandardSSD_ZRS'
    52    'Standard_LRS'
    53    'UltraSSD_LRS'
    54  ])
    55  param storageAccountType string = 'Standard_LRS'
    56
    57  @description('Name of the virtual machine.')
    58  param vmName string = 'vm-${systemCode}-${env}'
    59
    60  @description('Name of the network interface.')
    61  var nicName = 'nic-${systemCode}-${env}'
    62
    63  var addressPrefix = '10.0.0.0/16'
    64  var subnetName = 'Subnet'
    65  var subnetPrefix = '10.0.0.0/24'
    66  var virtualNetworkName = 'vnet-${systemCode}-${env}'
    67
    68  resource vn 'Microsoft.Network/virtualNetworks@2021-02-01' = {
    69    name: virtualNetworkName
    70    location: location
    71    properties: {
    72      addressSpace: {
    73        addressPrefixes: [
    74          addressPrefix
    75        ]
    76      }
    77      subnets: [
    78        {
    79          name: subnetName
    80          properties: {
    81            addressPrefix: subnetPrefix
    82          }
    83        }
    84      ]
    85    }
    86  }
    87
    88  resource nic 'Microsoft.Network/networkInterfaces@2021-02-01' = {
    89    name: nicName
    90    location: location
    91    properties: {
    92      ipConfigurations: [
    93        {
    94          name: 'ipconfig1'
    95          properties: {
    96            privateIPAllocationMethod: 'Dynamic'
    97            subnet: {
    98              id: resourceId('Microsoft.Network/virtualNetworks/subnets', vn.name, subnetName)
    99            }
   100          }
   101        }
   102      ]
   103    }
   104  }
   105
   106  resource vm 'Microsoft.Compute/virtualMachines@2021-03-01' = {
   107    name: vmName
   108    location: location
   109    properties: {
   110      hardwareProfile: {
   111        vmSize: vmSize
   112      }
   113      osProfile: {
   114        computerName: vmName
   115        adminUsername: adminUsername
   116        adminPassword: adminPassword
   117      }
   118      storageProfile: {
   119        imageReference: {
   120          publisher: 'MicrosoftWindowsServer'
   121          offer: 'WindowsServer'
   122          sku: OSVersion
   123          version: 'latest'
   124        }
   125        osDisk: {
   126          createOption: 'FromImage'
   127          managedDisk: {
   128            storageAccountType: storageAccountType
   129          }
   130        }
   131      }
   132      networkProfile: {
   135            id: nic.id
   136          }
   137        ]
   138      }
   139      diagnosticsProfile: {
   140        bootDiagnostics: {
   141          enabled: true
   142        }
   143      }
   144    }
   145  }
   146
   147  @description('virtual machine id')
   148  output id string = vm.id
```
</details>

補足は以下の通りです。
- 2行目から66行目の「param」でパラメーター、「var」で変数を定義します。
- 68行目の「resource」で仮想ネットワークを作成します。
- 88行目の「resource」でネットワークインターフェースを作成します。
- 106行目の「resource」で仮想マシンを作成します。
- 39行目の「output」で仮想マシンのidを「main.bicep」へ渡します。渡す理由としては「ar.bicep」であるアラートルール作成時で利用するためです。

### 2-5 ar.bicep構成
利用するBicepテンプレートは以下になります。
「ar.bicep」ファイルはアラートルールを作成するファイルになります。
今回は仮想マシンのVM可用性メトリックの値が1以下の場合に登録メールアドレスにメール通知をする設定になります。


<details><summary>ar.bicep</summary>

```bicep:bicep

     1  @description('Location for all resources.')
     2  param location string
     3
     4  @description('System code')
     5  param systemCode string
     6
     7  @description('Environment')
     8  param env string
     9
    10  @description('Name of alert rule')
    11  param alName string = 'vm-${systemCode}-${env} is down'
    12
    13  @description('Description of alert')
    14  param alertDescription string = 'This is a metric alert'
    15
    16  @description('Severity of alert {0,1,2,3,4}')
    17  @allowed([
    18    0
    19    1
    20    2
    21    3
    22    4
    23  ])
    24  param alertSeverity int = 1
    25
    26  @description('Specifies whether the alert is enabled')
    27  param isEnabled bool = true
    28
    29  @description('Full Resource ID of the resource emitting the metric that will be used for the comparison. For example /subscriptions/00000000-0000-0000-0000-0000-00000000/resourceGroups/ResourceGroupName/providers/Microsoft.compute/virtualMachines/VM_xyz')
    30  @minLength(1)
    31  param resourceId string
    32
    33  @description('Period of time used to monitor alert activity based on the threshold. Must be between five minutes and one hour. ISO 8601 duration format.')
    34  @allowed([
    35    'PT1M'
    36    'PT5M'
    37    'PT15M'
    38    'PT30M'
    39    'PT1H'
    40  ])
    41  param windowSize string = 'PT1M'
    42
    43  @description('how often the metric alert is evaluated represented in ISO 8601 duration format')
    44  @allowed([
    45    'PT1M'
    46    'PT5M'
    47    'PT15M'
    48    'PT30M'
    49    'PT1H'
    50  ])
    51  param evaluationFrequency string = 'PT1M'
    52
    53  @description('the flag that indicates whether the alert should be auto resolved or not. The default is true.')
    54  param autoMitigate bool = true
    55
    56  param targetResourceType string = 'Microsoft.Compute/virtualMachines'
    57
    58  @description('action group id')
    59  param actionGroupId string
    60
    61  resource metricAlert 'Microsoft.Insights/metricAlerts@2018-03-01' = {
    62    name: alName
    63    location: 'global'
    64    properties: {
    65      criteria: {
    66        'odata.type': 'Microsoft.Azure.Monitor.SingleResourceMultipleMetricCriteria'
    67        allOf: [
    68          {
    69            name: '1st criterion'
    70            metricName: 'VmAvailabilityMetric'
    71            dimensions: []
    72            operator: 'LessThan'
    73            threshold: 1
    74            timeAggregation: 'Average'
    75            criterionType: 'StaticThresholdCriterion'
    76          }
    77        ]
    78      }
    79      description: alertDescription
    80      enabled: isEnabled
    81      evaluationFrequency: evaluationFrequency
    82      scopes: [
    83        resourceId
    84      ]
    85      severity: alertSeverity
    86      windowSize: windowSize
    87      autoMitigate: autoMitigate
    88      targetResourceType: targetResourceType
    89      targetResourceRegion: location
    90      actions: [
    91        {
    92          actionGroupId: actionGroupId
    93        }
    94      ]
    95    }
    96  }
```
</details>

補足は以下の通りです。
- 2行目から59行目の「param」でパラメーターを定義します。
- 61行目の「resource」でアラートルールを作成します。
- 39行目の「output」でアクショングループのidを「main.bicep」へ渡します。渡す理由としては「ar.bicep」であるアラートルール作成時で利用するためです。
- 67行目から77行目の「allOf」でVM可用性メトリックの値（平均値）の閾値が1以下の場合的な事を記述しています。
- 82行目のスコープでリソースidを指定します。このidは仮想マシンになります。
- 90行目のアクションでアクショングループidを指定します。このidはアクショングループになります。

# 3. 検証
### 3-1 リソースの構築
下記コマンドを実行します。仮想マシンのログインユーザ名とロケーションを変更します。仮想マシンのログインパスワードを求められますので記述すると構築完了です。

```cmd:cmd (Azure CLI)
$ ls
main.bicep  modules

$ az login

$ az deployment sub create --template-file main.bicep --parameters adminUsername=<VM login account> --location <Your location>

```


しばらくすると登録したメールアドレス宛にアクショングループに追加された旨のメールが来ます。

![AddActionGroup.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1989465/019a5c0c-866e-90c4-0389-951fc0837484.png)

### 3-2 動作確認
VM可用性メトリックの値が1であることを確認し、仮想マシンをシャットダウンさせます。

![StopVM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1989465/a53e7bb7-9fb3-5ccd-8915-53cccf1b39c7.png)

条件にした閾値を下回ると以下のメールが来ます。
件名や本文中のDescriptionは「ar.bicep」ファイルで指定できます。

![Fied1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1989465/3ce73a63-bd88-6c46-6cca-2b4bf8d1f7d2.png)

![Fied2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1989465/de7b4ba9-157a-c425-0c24-70b6d1e4e36b.png)

最後に仮想マシンを起動させ、VM可用性メトリックの値が1に戻ると以下メールが来ます。

![Resolved1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1989465/3f7b9cc3-058e-0b0c-5aa2-141ef6e5433d.png)

![Resolved2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1989465/efc1f0a5-c5c2-e4a1-8417-bbb11d2ef797.png)

以上で検証は終わりになります。

# 4. おわりに
本記事を最後まで読んで頂きましてありがとうございます。
設定項目がわかりやすく、とりあえず試してみたい方にはお勧めできるかなと思いました。
ですが実運用するには要注意です。
- 2023/02/07現在、プレビュー機能であること。
- アプリのログ、ゲストOS上のパフォーマンス、イベントログは取得できない。

これからも色々と試してみようと思います。

# 5. 参考記事
- [チュートリアル: Azure 仮想マシンの可用性アラート ルールを作成する (プレビュー)](https://learn.microsoft.com/ja-jp/azure/azure-monitor/vm/tutorial-monitor-vm-alert-availability)
- [Bicep を使用した監視リソースの作成](https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/scenarios-monitoring)
- [Microsoft.Insights metricAlerts](https://learn.microsoft.com/en-us/azure/templates/microsoft.insights/metricalerts?pivots=deployment-language-bicep)
- [Azure Monitor のメトリック アラート ルール用の Resource Manager テンプレート サンプル](https://learn.microsoft.com/ja-jp/azure/azure-monitor/alerts/resource-manager-alerts-metric?tabs=bicep)
- [クイックスタート: Bicep ファイルを使用して Windows 仮想マシンを作成する](https://learn.microsoft.com/ja-jp/azure/virtual-machines/windows/quick-create-bicep?tabs=CLI)