Azure Resource Graph (ARG) に馴染んでみる

こんにちは🎄

この記事は、[Microsoft Azure Tech Advent Calendar 2022](https://qiita.com/advent-calendar/2022/microsoft-azure-tech) 20日目の記事です。
この記事では、Azure リソースの管理に便利な Azure Resource Graph について利用シーンを踏まえ紹介してみます。

### Azure Resource Graph とは

Azure Resource Graphは、Azure AD テナント全体に対して、
様々リソースに対し、Kusto Query Language (KQL) で探索できるサービスで、
2019 年に一般提供されました。

ただし、KQL の記法が全て使えるわけでない状況 (変数を定義する let は未対応) です。

検索方法としては、Azure Resource Graph エクスプローラや、
各種 SDK 、Azure CLI / Azure PowerShell が利用できるようになっています。

また、利用に際して料金はかかりませんが、
決められた時間に使用回数の制限 (クォータ) が設けられており、５秒毎、最大15回のクエリの送信が可能です。

このクォータ情報は、クエリ送信した際のレスポンスヘッダーに含まれ、
例えば、 Azure CLI を利用される場合には、"--debug" オプションにて確認可能です。

```bash
$ az graph query -q 'resourcechanges | project properties.changeAttributes.timestamp, properties.changeType, properties.targetResourceId, properties.targetResourceType, properties.changes | limit 5' --debug
…
cli.azure.cli.core.sdk.policies: Response status: 200
cli.azure.cli.core.sdk.policies: Response headers:
…
cli.azure.cli.core.sdk.policies:     'x-ms-ratelimit-remaining-tenant-resource-requests': '14'
cli.azure.cli.core.sdk.policies:     'x-ms-user-quota-remaining': '14'
cli.azure.cli.core.sdk.policies:     'x-ms-user-quota-resets-after': '00:00:05'
cli.azure.cli.core.sdk.policies:     'x-ms-resource-graph-request-duration': '0:00:00:01.2570864'
….

```


出典; [スロットルされた要求に関するガイダンス - Azure Resource Graph | Microsoft Learn](https://learn.microsoft.com/ja-jp/azure/governance/resource-graph/concepts/guidance-for-throttled-requests)

以下、IaaS に偏った内容となっておりますが、
代表的な3つの利用シーンについて紹介できればと思います。

* リソースを探索する
* リソースの変更を確認する
* 正常性を確認する

また、以降のクエリには、サブスクリプションを絞りこむために、
```| where subscriptionId == "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"``` と指定しているので、
試してみる際には、ご自身のサブスクリプション ID にて置き換えてください。

### リソースを探索する

利用シーンとしては、無用な課金を抑えたいという場合があるかと思います。

Azure VM であれば、使われていない (どの仮想マシンにもアタッチされていない) ディスクを以下のクエリにて確認できます。 

```sql
Resources
| where type has "microsoft.compute/disks"
| where subscriptionId == "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
| extend diskState = tostring(properties.diskState), allProperties = pack_all()
| where diskState == 'Unattached'
| project id, diskState, resourceGroup, location, allProperties
```

![resource1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28260/f88ebf43-6d10-ca4d-159c-308933edc256.png)


また、パブリック IP アドレスもついつい消し忘れてしまいがちです。そのようなパブリック IP アドレスを以下のクエリで探してみます。
むむ... 22 件。まさにコスト掛かっているところが分かりました。

```sql
Resources
| where type == "microsoft.network/publicipaddresses"
| where subscriptionId == "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
| extend allProperties = pack_all()
| where properties.ipConfiguration == "" and properties.natGateway == ""
| project id, name, resourceGroup, location, sku.name, allProperties
```

![resource2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28260/09e774a3-e482-8329-f4c8-218bc27a8f0f.png)


さらに管理ディスクの種別を調べ、検証用途など性能をあまり必要としない場合に、
SKU を変えるということも検討されるかと思います。

まずは、今現時点の管理ディスクでどの程度の割合なのか、以下のクエリで把握してみます。 
(Premium SSD v2 ディスクを考慮していないので、これは別途要検討..)

```sql
Resources
| where type =~ 'Microsoft.Compute/disks'
| where subscriptionId == "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
| extend diskSku=case(sku.name =~ "Standard_LRS", "Standard HDD", sku.name =~ "StandardSSD_LRS", "Standard SSD", sku.name =~ "Premium_LRS", "Premium SSD", sku.name), allProperties = pack_all()
| project diskSku, sku.name, sku.tier, allProperties
| summarize count() by diskSku
```

![resource3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28260/2de0088d-0b45-df18-b308-9b597e747bab.png)

既定でテーブル表示のところ、[グラフ] を押して見るともう少し視覚的に分かりやすい表示も可能です。

![resource4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28260/b8662f17-5641-e66b-e998-97f44033b09e.png)


同様に、仮想マシンサイズについて見てみましょう。

```sql
Resources
| where type == 'microsoft.compute/virtualmachines'
| where subscriptionId == "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
| summarize count() by VMSize = tostring(properties.hardwareProfile.vmSize)
```

![resource5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28260/3423dcdc-bfac-91e8-f697-0e2deebb0db3.png)


あとは、仮想マシンの電源状態によっては、コンピュートリソース分の課金が発生するので、
どの程度の仮想マシンが停止 (割り当て解除)されているか、確認してみます。

```sql
Resources
| where type == 'microsoft.compute/virtualmachines'
| where subscriptionId == "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
| summarize count() by PowerState = tostring(properties.extended.instanceView.powerState.code)
```

![resource6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28260/8fa14153-b9ad-a801-fb91-3df5cc56d9b8.png)


出典; [状態と課金状態 - Azure Virtual Machines | Microsoft Learn](https://learn.microsoft.com/ja-jp/azure/virtual-machines/states-billing)

クエリの結果は、ダッシュボードにピン止めすることができるので、
必要に応じて、共有しておくと便利です。

![dashboard2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28260/ee6c6080-9766-9fa0-9ea8-e142dd299b10.png)

![dashboard.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28260/25cc5084-516c-9d3d-76ce-20bb555f2b66.png)

### リソースの変更を確認する

次に Azure リソースの変更履歴を確認してみます。
Resourcechanges テーブルにて、Azure リソースに対して実行された最大7日前までのCRUD操作が取得できます。

例; 特定の日時 (以下は、2022-12-17 00:30 UTC) 以降の変更操作を取得

```sql
resourcechanges
| where subscriptionId == 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
| extend changeTime = todatetime(properties.changeAttributes.timestamp), targetResourceId = tostring(properties.targetResourceId),
changeType = tostring(properties.changeType), correlationId = properties.changeAttributes.correlationId, 
changedProperties = properties.changes, changeCount = properties.changeAttributes.changesCount
| where changeTime > datetime("2022-12-17T00:30:00.000Z")
| order by changeTime desc
| project changeTime, targetResourceId, changeType, correlationId, changeCount, changedProperties
```

例; Azure リソースの削除操作を確認

```sql
resourcechanges
| where subscriptionId == 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
| extend changeTime = todatetime(properties.changeAttributes.timestamp), targetResourceId = tostring(properties.targetResourceId),
changeType = tostring(properties.changeType), correlationId = properties.changeAttributes.correlationId, allProperties = pack_all()
| where changeType == "Delete"
| order by changeTime desc
| project changeTime, resourceGroup, targetResourceId, changeType, correlationId, allProperties
```

ただ誰が操作されたか確認されたい場合では、操作者 (caller) のプロパティが含まれていないので、
アクティビティログとの突合せが必要になります (以下の例では、日時とcorrelation ID で対象アクティビティログを確認しています)。

![resourcechange1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28260/e7f2d89f-f2e7-c506-99ed-4a1303ecd545.png)

![resourcechange2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28260/c7fad96f-f924-2d48-a136-b99e16f30e9f.png)



出典; [Azure アクティビティ ログのイベント スキーマ - Azure Monitor | Microsoft Learn](https://learn.microsoft.com/ja-jp/azure/azure-monitor/essentials/activity-log-schema#administrative-category)

### 正常性を確認する

Azure では正常について各リソースの正常性と、サービス正常性の２つがあります。

出典; [Azure Service Health とは - Azure Service Health | Microsoft Learn](https://learn.microsoft.com/ja-jp/azure/service-health/overview)

#### リソース正常性

Azure Resource Graph から確認できるリソース正常性の情報については、
下記にある通り、現時点で仮想マシンのみとなっています。

出典; [Project Flash で Azure 仮想マシンの可用性の監視を進化させる | Azure のブログと更新プログラム | Microsoft Azure
](https://azure.microsoft.com/ja-jp/blog/advancing-azure-virtual-machine-availability-monitoring-with-project-flash/)

>現在、Azure Resource Graph の Health Resources テーブルに VM の可用性状態 (使用可能、使用不可、不明) が出力されており、お客様は、大規模データセットを同時にふるい分けるための複雑な Kusto Query Language (KQL) クエリを実行することができます。この機能は、VM 可用性の過去の変化を追跡したり、カスタム ダッシュボードを構築したり、複数のテーブルにまたがる多数のリソース プロパティを詳細に調査したりするのに便利です。

また、実行してみると直近１日のリソース正常性に変化があった場合のみレコードが取得できる状況になります。

```sql
HealthResources
| where type =~ 'microsoft.resourcehealth/availabilitystatuses'
| where subscriptionId == 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
| where tostring(properties.targetResourceType) =~ 'microsoft.compute/virtualmachines'
| extend allProperties = pack_all()
| project targetResourceId = tolower(tostring(properties.targetResourceId)), previousAvailabilityState = properties.previousAvailabilityState, currentAvailabilityState = properties.availabilityState, occurredTimeUTC = properties.occurredTime, allProperties
| order by ['targetResourceId'] asc
```

![resourcehealth.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28260/68e36fdd-924b-5735-7db1-1d519c7bfe3e.png)


また、'microsoft.resourcehealth/resourceannotations' の情報と併せると、リソース正常性の変化の要因 (ユーザー操作 / Azure 基盤起因) が分かるので、その結果と併せて確認してみます。

```sql
HealthResources
| where type =~ 'microsoft.resourcehealth/availabilitystatuses'
| where subscriptionId == 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
| project targetResourceId = tolower(tostring(properties.targetResourceId)), previousAvailabilityState = properties.previousAvailabilityState, currentAvailabilityState = properties.availabilityState, occurredTimeUTC = properties.occurredTime
| join kind=leftouter (
    HealthResources
    | where type =~ 'microsoft.resourcehealth/resourceannotations'
    | where subscriptionId == 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
    | project Id = tolower(tostring(properties.targetResourceId)), properties.summary, properties.context, properties.reason)
    on $left.targetResourceId == $right.Id
| project-away Id
```

![resourcehealth2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28260/2f21b0b2-1448-ef37-4928-d05f0babb88e.png)


#### サービス正常性

サービス正常性について履歴も含め取得することが可能です。
例えば、サービスの問題 (eventType が ServiceIssue) の場合は、以下のように取得できます。

```sql
ServiceHealthResources
| where type =~ 'Microsoft.ResourceHealth/events'
| where subscriptionId == 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
| extend eventType = tostring(properties.EventType), status = properties.Status, description = properties.Title, trackingId = properties.TrackingId, summary = properties.Summary, priority = properties.Priority, impactStartTime = todatetime(tolong(properties.ImpactStartTime)), impactMitigationTime = todatetime(tolong(properties.ImpactMitigationTime)), allProperties = pack_all()
| where eventType == "ServiceIssue"
| project trackingId, eventType, status, priority, impactStartTime, impactMitigationTime, summary, allProperties
```

![servicehealth1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28260/42b7a131-acd6-941d-d64b-b76f5a08e7cb.png)


計画メンテナンス (eventType が PlannedMaintenance) の場合のクエリはこちらで取得できます。

```sql
ServiceHealthResources
| where type =~ 'Microsoft.ResourceHealth/events'
| where subscriptionId == 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
| extend eventType = tostring(properties.EventType), status = properties.Status, description = properties.Title, trackingId = properties.TrackingId, summary = properties.Summary, priority = properties.Priority, impactStartTime = todatetime(tolong(properties.ImpactStartTime)), impactMitigationTime = todatetime(tolong(properties.ImpactMitigationTime)), allProperties = pack_all()
| where eventType == "PlannedMaintenance"
| project trackingId, eventType, status, priority, impactStartTime, impactMitigationTime, summary, allProperties
```

![servicehealth2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28260/7ccc510b-dba8-5a96-d004-a88760acecb8.png)


紹介できるレコードが無かったのですが、正常性やセキュリティに関する勧告も取得できるかと思います。

出典; [Service Health クラシック エクスペリエンスの概要 - Azure Service Health | Microsoft Learn](https://learn.microsoft.com/ja-jp/azure/service-health/service-health-overview#service-health-events)


### 今後

仮想マシンの可用性監視を強化していこうというProject Flash というプロジェクトが進められており、その１つのサービスとして ARG は位置づけられています。
この記事で興味を持ってもらえれば幸いです。

出典; [New Project Flash Update: Advancing Azure Virtual Machine availability monitoring | Azure のブログと更新プログラム | Microsoft Azure](https://azure.microsoft.com/ja-jp/blog/advancing-azure-virtual-machine-availability-monitoring-with-project-flash-update/)

---
明日は同じチームの @atakada がAKSのメモリ周りのトラブルシューティングについてご紹介されるみたいです。
お楽しみに🎁