【Azureハンズオン】カスタムログを収集してアラートを出す

# はじめに
　今回はAzureでデプロイしている**仮想マシンから任意のログを収集し、Azure上の機能でアラートを出す方法**をご紹介します。
 **検証用のPowerShellスクリプト**も添付いたしますので、検証したい方はこの記事に沿って検証ができるように**ハンズオンとして構成しております**。


# 対象読者

- Azureを使用する現場に従事している方
- Azureの資格勉強をしている方
- Azureに興味がある方
- クラウドに興味がある方

# 免責事項
ハンズオンにあたって料金がかかります。**筆者が記事作成の間リソースを使用した分で200円ほど**でした。記事の最後に**リソースの削除の仕方**も解説しておりますので、ハンズオンが終了したら**削除を実施**してもらうようお願いします。
また、Azureの画面に関しては、**2024年08月22日時点のもの**を使用しております。


# 使用サービス・技術

### ・Azure Monitor
Microsoft Azure上のリソースやアプリケーションのパフォーマンスと可用性を監視・管理するサービスです。メトリクスやログを収集・分析し、問題を早期に発見して対応を促すアラート通知する機能を持ちます。

### ・Azure Monitor エージェント(以下、AMA)
Azure環境における監視データを収集するためのエージェントです。仮想マシンやコンテナからメトリクスやログを集め、Azure Monitorに送信します。

### ・データ収集ルール(以下、DCR)
Azure Monitorにおいて監視データを収集するための設定ルールです。DCRを使用すると、（AMA）などを通じて収集するデータの種類、送信先（例えばLog AnalyticsワークスペースやAzure Monitor Metrics）を柔軟に指定できます。

### ・データ収集エンドポイント(以下、DCE)
Azure Monitorがさまざまなソースからメトリック、ログ、イベントなどのデータを収集するためのエンドポイントです。これらのエンドポイントを通じて、Azureリソースやオンプレミス環境、およびハイブリッドクラウド環境からデータを収集し、監視と診断に役立てることができます。

### ・Log Analyticsワークスペース(以下、ワークスペース)
Azure Monitorの一部として提供されるデータストレージと分析のプラットフォームです。Azureリソースやアプリケーションから収集されたログやメトリクスデータを集中管理し、クエリやダッシュボードを通じてデータの分析や可視化を行います。

### ・クストクエリ言語(以下、KQL)
Azure MonitorやLog Analyticsなどで使用されるクエリ言語です。大量のログデータやメトリクスデータを効率的に検索、フィルタ、集計、可視化するために設計されています。SQLに似た構文を持ち、ログの解析、異常検出、トレンド分析などに強力です。

### ・アラートルール
Azure Monitorで定義される監視ルールであり、特定の条件やしきい値を満たした際に通知や自動アクションをトリガーする仕組みです。システムの異常やリソースの過剰利用などを迅速に検知し、運用チームに通知を送ったり、指定の自動化プロセスを実行したりできます。


# 1. 要件

### 独自のアプリケーションのログをAzure上に収集・分析し、アラートを出したい。
お客様の要件は、アプリケーションが出力するテキストログをAzure上に収集し、その中で異常を示す文言があった場合メールでアラートを出したいという要件でした。

# 2. 解決策

### **AMAとDCRでカスタムログを収集し、KQLを使ってアラートを定義する**
AzureではAMAをVMにインストールし、DCRを定義することでワークスペースにログを収集することができます。
ログを収集できればKQLで検索できるようになるので、KQLの検索結果を使ってアラートを定義することで、要件を満たすことができます。


# 3. ハンズオン

それでは実際に設定していきます。

### 3-1.VM作成
まず、ログを出力するためのWindows serverインスタンスを起動します。
Azureポータルを開き、VM作成画面を開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/8e4768a7-0503-a252-9b3d-73c8eab2c1d8.png)

「基本」タブでリソースグループ・仮想マシン名・イメージ・管理者アカウント情報等を入力します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/6ada62da-a41f-4fa2-9c46-b7f169018fff.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/0a7d7b5b-95e9-a059-975c-dd857d792068.png)

[確認及び作成]をクリックして検証に成功したら作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/b4a84cb9-7add-f21f-5d33-fc73db08bd9d.png)

作成が完了したら[リソースに移動]をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/aac6d8a7-1b9d-af9a-4df6-f0b19432fe17.png)

[接続]→[接続]をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/73a2c105-8f39-db70-5f9a-d6057fa72113.png)

[RDPファイルのダウンロード]をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/71b7a9b7-b2a0-4653-b96c-3cd032a47587.png)

ダウンロードされたファイルを開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/c9093bf4-b22d-503b-9f17-336431974fd8.png)

VM作成時のアカウント情報を入力し、接続します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/86d84690-e6b4-7599-f5cf-523c56d8d26b.png)

VMへの接続が成功したら、この記事に添付しているログ生成スクリプトをVM内のデスクトップに貼り付けます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/20eee664-8628-5488-b601-c631ba94e2d8.png)

Powershellスクリプトを管理者で実行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/e7a2e45d-1e82-7665-14ff-298b87f8cec5.png)

Powershellの画面が開き、ログが生成されます。もし入力が要求された場合「A」と入力してEnterを押してください。
C:\log\testファイルが作成され、ログが10秒ごとに追記されていきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/7e1feca3-5004-6db6-1443-e440550abd6a.png)

ログファイルは以下のような形式になっています。
```
2022-12-31T01:00:00.1234567Z | Computer=TestVM01 | Log=Service stop
2022-12-31T01:01:00.1234567Z | Computer=TestVM01 | Log=Service start
2022-12-31T01:02:00.1234567Z | Computer=TestVM01 | Log=Service stop
```
Powershellは実行したままにしておいてください。
3-2以降で、こちらのログファイルをAzureに収集するように設定していきます。

### 3-2.ワークスペースの作成

Azureポータルから[Log Analyticsワークスペース]に移動します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/3ad441c2-6e4e-26e0-4ddd-6babd95e82a0.png)

[作成]画面に移動します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/02cab2b1-b93c-0ae3-65e7-bc8621916ded.png)

各項目を入力し、[確認および作成]をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/2c111bbb-81c3-5f78-afc8-7c70541e633a.png)

検証に成功したら、[作成]をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/fe506000-8f29-de0d-8332-83f762d20ae6.png)


### 3-3.DCEの作成

Azureポータルから[モニター]に移動します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/478ba1f7-1449-ff1c-e808-8dd2528d7ced.png)

データ収集エンドポイント作成画面に移動します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/f408fb4a-aedd-5871-5aa1-2e0ac76dbd67.png)

各項目を入力し、[確認と作成]をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/b74d8d23-be25-47a1-1562-ac320aa713e6.png)

検証に成功したら、[作成]をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/6469ff7a-afe3-d421-f6a0-d61b2ac3bd61.png)

### 3-4.カスタムテーブルの作成

参考：[カスタム テーブルを作成する](https://learn.microsoft.com/ja-jp/azure/azure-monitor/agents/data-collection-text-log?tabs=portal#create-a-custom-table)

Azureポータル画面でCloudShellを開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/8592d337-dfdc-671d-0a01-26216160e2af.png)

Powershellで開きます。(CloudShell用のストレージアカウントは無くても大丈夫です。)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/e6a103d7-fc4c-5d12-5fce-39cd36d34391.png)

以下のスクリプトでワークスペースにカスタムテーブルを作成します。{}で囲まれた部分は環境によって異なるので、適宜置き換えてください。

```
$tableParams = @'
{
    "properties": {
        "schema": {
               "name": "{TableName}_CL",
               "columns": [
        {
                                "name": "TimeGenerated",
                                "type": "DateTime"
                        }, 
                       {
                                "name": "RawData",
                                "type": "String"
                       },
                       {
                                "name": "HostName",
                                "type": "String"
                       },
                      {
                                "name": "Log",
                                "type": "String"
                     }
              ]
        }
    }
}
'@

Invoke-AzRestMethod -Path "/subscriptions/{subscriptionID}/resourcegroups/{resourcegroup}/providers/microsoft.operationalinsights/workspaces/{WorkspaceName}/tables/{TableName}_CL?api-version=2021-12-01-preview" -Method PUT -payload $tableParams
```

参考までに、筆者は以下のように編集しました。※サブスクリプションIDなどは環境によって異なるので、適宜編集してください。
```
$tableParams = @'
{
    "properties": {
        "schema": {
               "name": "Test_CL",
               "columns": [
        {
                                "name": "TimeGenerated",
                                "type": "DateTime"
                        }, 
                       {
                                "name": "RawData",
                                "type": "String"
                       },
                       {
                                "name": "HostName",
                                "type": "String"
                       },
                      {
                                "name": "Log",
                                "type": "String"
                     }
              ]
        }
    }
}
'@

Invoke-AzRestMethod -Path "/subscriptions/4400b795-f4d1-42d3-8862-90e1cd037744/resourcegroups/testvm_group/providers/microsoft.operationalinsights/workspaces/testLA/tables/Test_CL?api-version=2021-12-01-preview" -Method PUT -payload $tableParams
```

APIからステータスコード200が返れば、作成成功です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/a0c6045a-ebad-f0a9-3d0a-56b6f3e71d55.png)

### 3-5.DCRの作成

Azureポータルから[モニター]に移動します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/478ba1f7-1449-ff1c-e808-8dd2528d7ced.png)

[データ収集ルール]→[作成]をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/26e74e74-6700-034b-9d1c-f2ae338636bf.png)

「基本」タブの各項目を入力し、[次へ：リソース]をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/b9ba7281-cc4b-50db-05df-6612ee109575.png)

「リソース」タブにて3-1で作成したVMを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/1b514aff-0df9-f476-b3d3-53e2b039aeca.png)

VMが追加されたことを確認し、[次へ：収集と配信]をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/715d2f79-4f28-d4ca-05af-a7fd2159e9f4.png)

「収集と配信」タブで、[+データソースの追加]をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/11a3279d-597a-fc6c-85b9-25d3429590c6.png)

[データソースの種類] にて [カスタムテキストログ] を選択します。
[ファイルパターン] にて収集先のログファイル パスを指定します。
今回はWindows上のテキストログを取得することを想定し、以下のパスを入力します
```
C:\log\test.log
```

[テーブル名] には3-4で作成したテーブル名 (_CL 含む) を指定します。
[Transform] には、以下のクエリを設定してデータを整形します。
```
source| extend TimeGenerated = now()| parse RawData with * "Computer="HostName "| " *| parse RawData with * "Log="Log"
```

参考：[Azure Monitorでの変換の構造](https://learn.microsoft.com/ja-jp/azure/azure-monitor/essentials/data-collection-transformations-structure)

各項目を入力したら、[次へ：ターゲット]をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/5b002700-7a68-b85d-1e71-94c167ed9670.png)


「ターゲット」タブにて3-2で作成したワークスペースを追加し、[データソースの追加]をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/982ee393-5c1d-3d31-6d83-4ba309d0e4e0.png)

[確認と作成]をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/31803c17-9655-1dee-c037-dd65a530bd9d.png)

検証が成功したら、作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/aaba2180-283a-e35d-f193-0f75a9b4df50.png)

### 3-6.データ収集の確認

データが収集できているか確認します。(※データ収集はDCRを作成してから、10～30分ほどかかります。最長で2時間かかることもあるみたいです。)

Azureポータルから[モニター]に移動します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/478ba1f7-1449-ff1c-e808-8dd2528d7ced.png)

左のメニューから[ログ]をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/911d0d31-b186-9f48-f55c-a0161be9a910.png)

新しいクエリ右側の[・・・]→[スコープの変更]をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/90e3198d-baee-cecc-dbb8-00a8d946223e.png)

「範囲の選択」画面にて3-2で作成したワークスペースを選択し、［適用］をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/25c812a0-9c57-b948-eb54-d8ff8206c13d.png)

画面右側のモードを[KQLモード]に変更します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/cd6caba3-c2f8-fb3d-635c-db7dc6a0904d.png)

以下のクエリを入力し、実行します。結果が表示されれば、問題なく収集できています。
```
Test_CL
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/b15d4540-25f0-43ad-0a79-c4a3fc370f8e.png)

### 3-7.アラートルールの作成

最後に、Azureに収集したログを使用してアラートを設定します。
今回は、Logテーブルに"Service stop"という文言が記録されたときにアラートが出るように設定します。

Azureポータルから[モニター]に移動します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/478ba1f7-1449-ff1c-e808-8dd2528d7ced.png)

[アラート]→[作成]→[アラートルール]をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/e7ccef38-c710-65cd-e460-f4d62da31b4d.png)

「リソースの選択」にて、3-2で作成したワークスペースを選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/a2a5db3b-80e6-ed41-00bf-a9d15103e1ed.png)

「条件」タブ内のシグナル名で「カスタムログ検索」を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/91cac92b-b240-0073-5497-7b2e9f540743.png)

「ログ」画面が開くので、以下のクエリを入力し、[アラートの編集を続行する]をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/e297c983-e607-c965-8922-834008efa34e.png)

「条件」タブの残りの項目を以下のように入力し、[次へ:アクション]をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/19d92bee-d88b-5d3d-04e3-16913fd8715c.png)

「アクション」タブにて[アクショングループの作成]をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/83e124e4-cd0a-14b1-da87-6d4aeef6af23.png)

「アクショングループの作成」画面が開くので、「基本」タブの項目を入力し、[次へ:通知>]をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/7dc746d7-1b8b-b68e-e940-8d072bb9bde8.png)

「通知」タブの「通知タイプ」で[電子メール/SMS メッセージ/プッシュ/音声]を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/5368cdda-8c70-de0b-cd4c-0ef858588ee4.png)

開いた「電子メール/SMS メッセージ/プッシュ/音声」画面で通知先を設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/44229ab8-9c79-af7f-c2c5-fcfcd89ca1b3.png)

通知の名前を入力し、[確認と作成]をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/d2eea45d-01b7-511e-f35a-29cdfaa86c4e.png)

検証に成功したら、作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/57076918-bea6-71ab-30e2-75084137d6fe.png)

「アラートの作成」画面に戻るので、[次へ:詳細>]をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/2f6190cd-e66c-07c8-54bc-e0035b7df231.png)

「詳細」タブで各項目を入力し、[確認及び作成]をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/9815154e-ffcf-cf68-648a-49003a397e74.png)

検証に成功したら、作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/c0d5e520-d7a2-8ca4-6fc4-40fb093de15f.png)

### 3-8.アラートの確認
アラートが発報されるか確認します。

アラートが発報されると「アラート」画面に以下のように表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/4e5c5e43-43be-3d50-03e4-9a0b4828a2b8.png)

メールアドレスを設定した場合、メールが来ます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/f16ffa47-5b51-8e51-2a23-00b39df253a0.png)

以上で、すべての設定が完了しました。

### 3-9.リソースの削除
ハンズオンが終了したら、課金が発生しないようにリソースを削除しておきましょう。

Azureポータルから[リソースグループ]をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/9b969d88-05ce-19a3-fef7-886723d4dbca.png)

ハンズオンで作成したリソースグループをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/266dd505-49f6-1e60-e3e0-c091f335d764.png)

[リソースの削除]をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/0a1e84bf-beb3-41dd-0ad4-e45732b8d5c5.png)

リソースグループ名を入力し、削除します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/a125e574-eb3f-43cf-e354-0d526d6304a6.png)

しばらくして、以下のように通知が来たらリソースの削除完了です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3585159/252ea451-e2af-cd6f-541b-f138e970ae0a.png)


## おわりに

今回は、VM内の任意のログをAzureに収集してアラートを定義する方法を紹介しました。
設定によってはオンプレミスの任意のログも収集することができます。
アプリケーションのログもAzureで収集して一元的にアラートを出すことができるので、便利です。
現場で私と同じような機会があれば、上記ハンズオンを参考にして使用してみてください。

## 使用するPowershellスクリプト
下記のスクリプトをテクストファイルに貼り付け保存して使用してください。
```
# ログファイルのパスを指定
$logFilePath = "C:\log\test.log"

# ディレクトリが存在しない場合は作成
$logDirectory = [System.IO.Path]::GetDirectoryName($logFilePath)
if (-not (Test-Path $logDirectory)) {
    New-Item -Path $logDirectory -ItemType Directory -Force
}

# タイムスタンプを取得する関数
function Get-Timestamp {
    return (Get-Date).ToString("yyyy-MM-ddTHH:mm:ss.fffffff")
}

# ホスト名を取得
$hostname = [System.Net.Dns]::GetHostName()

# ログメッセージを追加する関数
function Write-Log {
    param (
        [string]$logMessage
    )

    $timestamp = Get-Timestamp
    $message = "$timestamp | Computer=$hostname | Log=$logMessage"

    $maxRetries = 5
    $retryCount = 0
    $retryDelay = 1  # seconds

    while ($retryCount -lt $maxRetries) {
        try {
            $fileStream = [System.IO.File]::Open($logFilePath, [System.IO.FileMode]::Append, [System.IO.FileAccess]::Write, [System.IO.FileShare]::Read)
            $writer = New-Object System.IO.StreamWriter($fileStream)
            $writer.WriteLine($message)
            $writer.Close()
            $fileStream.Close()
            break
        } catch {
            $retryCount++
            Start-Sleep -Seconds $retryDelay
        } finally {
            if ($writer -ne $null) {
                $writer.Close()
            }
            if ($fileStream -ne $null) {
                $fileStream.Close()
            }
        }
    }

    if ($retryCount -ge $maxRetries) {
        Write-Error "Failed to write log after $maxRetries attempts."
    }
}

# 無限ループで10秒毎にログを書き込む
while ($true) {
    Write-Log "Service stop"
    Start-Sleep -Seconds 10
    Write-Log "Service start"
    Start-Sleep -Seconds 10
}
```