[Azure] 仮想マシンの操作履歴(アクティビティログ)をazurecliで取得する

Azure仮想マシンの操作履歴はポータルの「アクティビティ ログ」メニューから確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172826/cb168dbf-8699-bd1f-3c81-b5497590241e.png)

この履歴には、ユーザが行った操作とAzureサービスの操作(リソース正常性チェックなど)が混在して表示されるため、頻繁に変更される仮想マシンの履歴は現状のGUIでは少々見にくい面があります。

そこで今回は、これをCLIで取得する方法を調査しました。

# 履歴を取得するコマンド(az monitor activity-log)

履歴はaz monitor activity-logコマンドで取得できます。

```shell
$ az monitor activity-log list --resource-id <仮想マシンのリソースID> \
  --start-time <履歴の対象期間(開始日)> --offset <履歴の取得範囲> --query <フィルタクエリ>

・--resource-id： 仮想マシンのリソースID。
                  "/subscriptions"から始まる長い文字列で、仮想マシンのプロパティで確認できる。
・--start-time： 履歴の対象期間(開始日)を指定する。
                 形式は date (yyyy-mm-dd) time (hh:mm:ss.xxxxx) timezone (+/-hh:mm)
                 省略した場合は現在日時が起点となる。
・--offset： 履歴の取得範囲を指定する。
             形式は 時間: ##h、または 日数: ##d で指定。
             省略した場合は6時間(6d)となる。
・--query: 追加の検索条件を指定する。
```

後ほど実行例を記載していますが、start-time指定の有無でoffsetの向きが変わることには注意が必要です。具体的には、指定した場合は「start-time以降、offset期間内の履歴が取得」され、指定しない場合は「現在日時を起点とし、過去に遡ってoffset期間内の履歴が取得」されます。

結果はこのように取得されます（項目が多いので、参照されそうなものを抜粋しています）。

```json
[
  {
    "caller": "user_id",
    "category": {
      "localizedValue": "Administrative",
      "value": "Administrative"
    },
    "channels": "Operation",
    "eventName": {
      "localizedValue": "End request",
      "value": "EndRequest"
    },
    "eventTimestamp": "2021-02-05T09:33:42.920830+00:00",
    "level": "Informational",
    "operationName": {
      "localizedValue": "Deallocate Virtual Machine",
      "value": "Microsoft.Compute/virtualMachines/deallocate/action"
    },
    "properties": {
      "message": "Microsoft.Compute/virtualMachines/write",
      "responseBody": "<更新内容(json形式)>"
    "status": {
      "localizedValue": "Succeeded",
      "value": "Succeeded"
    }
  }
]
```

## 実行例(1)：過去7日間の停止履歴を取得

```shell
$ az monitor activity-log list --resource-id /subscriptions/633xxxxxxxxa81/resourceGroups/rg-xxxxxxxx1l4/providers/Microsoft.Compute/virtualMachines/xxxxxxxx \
  --query "[?operationName.value=='Microsoft.Compute/virtualMachines/deallocate/action']" \
  --offset 7d \
  | jq -r 'sort_by(.eventTimestamp)| .[] | [.operationName .localizedValue , .eventTimestamp , .caller , .status .value ] | @csv'
"Deallocate Virtual Machine","2021-02-03T10:00:42.118582+00:00","user_id","Started"
"Deallocate Virtual Machine","2021-02-03T10:00:42.313547+00:00","user_id","Accepted"
"Deallocate Virtual Machine","2021-02-03T10:00:54.005627+00:00","user_id","Succeeded"
"Deallocate Virtual Machine","2021-02-04T05:24:17.293567+00:00","user_id","Started"
"Deallocate Virtual Machine","2021-02-04T05:24:17.538572+00:00","user_id","Accepted"
"Deallocate Virtual Machine","2021-02-04T05:25:17.372672+00:00","user_id","Succeeded"
"Deallocate Virtual Machine","2021-02-04T09:07:50.841453+00:00","user_id","Started"
"Deallocate Virtual Machine","2021-02-04T09:07:51.451465+00:00","user_id","Accepted"
"Deallocate Virtual Machine","2021-02-04T09:08:39.316604+00:00","user_id","Succeeded"
"Deallocate Virtual Machine","2021-02-05T09:32:54.532282+00:00","user_id","Started"
"Deallocate Virtual Machine","2021-02-05T09:32:54.767286+00:00","user_id","Accepted"
"Deallocate Virtual Machine","2021-02-05T09:33:42.920830+00:00","user_id","Succeeded"
```

queryで停止の履歴、offsetで過去7日間を指定して検索しています。結果はデフォルトでは新しい順に取得されるので、古い順にソートしています。結果から、4回の停止操作が実行されて、全て成功しているのが確認できます。

## 実行例(2)：1日の起動履歴を取得

```shell
$ az monitor activity-log list --resource-id /subscriptions/633xxxxxxxxa81/resourceGroups/rg-xxxxxxxx1l4/providers/Microsoft.Compute/virtualMachines/xxxxxxxx \
  --query "[?operationName.value=='Microsoft.Compute/virtualMachines/start/action']" \
  --start-time 2021-02-04 \
  --offset 1d \
  | jq -r 'sort_by(.eventTimestamp)| .[] | [.operationName .localizedValue , .eventTimestamp , .caller , .status .value ] | @csv'
"Start Virtual Machine","2021-02-04T00:03:25.005497+00:00","user_id","Started"
"Start Virtual Machine","2021-02-04T00:03:25.295468+00:00","user_id","Accepted"
"Start Virtual Machine","2021-02-04T00:04:09.086478+00:00","user_id","Succeeded"
"Start Virtual Machine","2021-02-04T05:25:53.100257+00:00","user_id","Started"
"Start Virtual Machine","2021-02-04T05:25:53.740258+00:00","user_id","Accepted"
"Start Virtual Machine","2021-02-04T05:26:05.999168+00:00","user_id","Succeeded"
"Start Virtual Machine","2021-02-04T23:55:37.203207+00:00","user_id","Started"
"Start Virtual Machine","2021-02-04T23:55:37.503203+00:00","user_id","Accepted"
"Start Virtual Machine","2021-02-04T23:56:04.956479+00:00","user_id","Succeeded"
```

start-timeを指定し、2/4以降1日分のログを取得しています。3回起動操作が実行されて、全て成功しているのが確認できます。

## 実行例(3)：特定ユーザの操作履歴を取得

```shell
$ az monitor activity-log list --resource-id /subscriptions/633xxxxxxxxa81/resourceGroups/rg-xxxxxxxx1l4/providers/Microsoft.Compute/virtualMachines/xxxxxxxx \
  --query "[?caller=='user_id']" \
  --offset 90d \
  | jq -r 'sort_by(.eventTimestamp)| .[] | [.operationName .localizedValue , .eventTimestamp , .caller , .status .value ] | @csv'
"Create or Update Virtual Machine","2021-02-05T08:28:07.864183+00:00","user_id","Started"
"'auditIfNotExists' Policy action.","2021-02-05T08:28:08.084212+00:00","user_id","Started"
"Create or Update Virtual Machine","2021-02-05T08:28:08.164200+00:00","user_id","Accepted"
"Create or Update Virtual Machine","2021-02-05T08:28:08.825840+00:00","user_id","Succeeded"
"Create or Update Virtual Machine","2021-02-05T08:38:09.726146+00:00","user_id","Succeeded"
"'auditIfNotExists' Policy action.","2021-02-05T08:38:15.302087+00:00","user_id","Succeeded"
"'audit' Policy action.","2021-02-05T08:38:15.357087+00:00","user_id","Succeeded"
"'auditIfNotExists' Policy action.","2021-02-05T08:38:15.467089+00:00","user_id","Succeeded"
"'audit' Policy action.","2021-02-05T08:38:15.467089+00:00","user_id","Succeeded"
"'auditIfNotExists' Policy action.","2021-02-05T08:38:15.477086+00:00","user_id","Succeeded"
"'audit' Policy action.","2021-02-05T08:38:15.477086+00:00","user_id","Succeeded"
"'auditIfNotExists' Policy action.","2021-02-05T08:38:15.552089+00:00","user_id","Succeeded"
"'audit' Policy action.","2021-02-05T08:38:15.552089+00:00","user_id","Succeeded"
"'auditIfNotExists' Policy action.","2021-02-05T08:38:15.562094+00:00","user_id","Succeeded"
```

queryでユーザ名を指定し、過去90日に対象ユーザが行った操作を全て取得しています。audit, Policy actionの履歴がたくさん出てきますが、この例では2/5に全体として1件の操作が行われていただけでした。

操作内容は .properties.responseBody に記録されていますが、この内容自体もjsonであるため、ダブルコーテーションがエスケープされた文字列形式で取得されます。そこまでコマンド1つでパースするのは難しそうでした。

# その他

az monitorコマンドは比較的更新頻度が高いようで、Azure CLIが最新版でない場合にエラーが発生することがよくあります。今回はクライアントによっては'antlr4'モジュールのエラーが発生しましたが、Azure CLIを最新版にすると解消しました。

```shell
$ az monitor activity-log list --start-time 2018-07-01 --offset 7d
The command failed with an unexpected error. Here is the traceback:

No module named 'antlr4'
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/knack/cli.py", line 206, in invoke
    cmd_result = self.invocation.execute(args)
  File "/usr/lib/python3/dist-packages/azure/cli/core/commands/__init__.py", line 528, in execute
    self.commands_loader.load_arguments(command)
  File "/usr/lib/python3/dist-packages/azure/cli/core/__init__.py", line 300, in load_arguments
    loader.load_arguments(command)  # this adds entries to the argument registries
  File "/usr/lib/python3/dist-packages/azure/cli/command_modules/monitor/__init__.py", line 51, in load_arguments
    from azure.cli.command_modules.monitor._params import load_arguments
  File "/usr/lib/python3/dist-packages/azure/cli/command_modules/monitor/_params.py", line 12, in <module>
    from azure.cli.command_modules.monitor.actions import (
  File "/usr/lib/python3/dist-packages/azure/cli/command_modules/monitor/actions.py", line 7, in <module>
    import antlr4
ModuleNotFoundError: No module named 'antlr4'

To open an issue, please run: 'az feedback'
```

# 参考資料

- [az monitor activity-log | Microsoft Docs](https://docs.microsoft.com/en-us/cli/azure/monitor/activity-log?view=azure-cli-latest)
- [Query Azure CLI command output](https://docs.microsoft.com/ja-jp/cli/azure/query-azure-cli)
- [Azure CLI antlr4 error #12504](https://github.com/Azure/azure-cli/issues/12504)