基本編

KUSTO の基本となるフィルターやソート、日付に関するクエリを学習します。

はじめに

使い方

KQL を初めて使う方が KQL の書き方を身に着けるための演習用の問題集と回答、解説です。
実際の KQL を使う場面で頻繁に出現するコマンド(演算子、関数)を様々な角度からいろいろな使い方で実行することで、手でコマンドを覚えてもらうような利用方法を想定しています。

用語

KQL では他のプログラム言語と同じように、各言語要素に対して名前がつけられています。この名前を覚えることは必須ではありませんが、学習効率を高めるためには意識しておいたほうが良い要素です。単に全てを「コマンド」とひとくくりにするのではなく、現在自分はどの要素が使っているのかを意識しながら KQL を書くと仕組みへの理解が速やかに深まります。

  • 演算子、オペレーター: 意味は同じです。
  • テーブル演算子: 最も多く使われるオペレーターです。テーブルの入力を受け付け、テーブルを出力します。処理の最初やパイプラインの次に現れる要素はテーブル演算子です。
  • スカラー演算子: 主にテーブル オペレーターの中で使われるオペレーターです。数値演算子、論理演算子、文字列演算子などがあり、テーブルの各行の特定の列の値などデータ処理を行います。後述の関数と区別がつきにくいのですが、 between と not-between はスカラー演算子です。
  • 関数、ファンクション: 意味は同じで特定の処理結果を返します。入力値に対して処理を実施するものや、入力値を取らずに値を返すものがあります。スカラー演算子と似ているので注意してください。

リンク

  • Log Analytics DEMO workspace
    サンプル データが用意されているデモ ワークスペースです。このページの KQL はこのデータに対して実行することを想定しています。

  • Kusto Query Language
    Azure Data Explorer の KQL のリファレンスです。Log Analytics ではこのなかの一部が実装されています。

基本の KQL

目的の情報がどこにあるかを探す

Azure Monitor のログを検索するためにはワークスペースにどのようなテーブルがあり、各テーブルはどのような構造のレコードを格納しているかを知る必要があります。ここでは基本的なオペレーターを使用して、目的の情報を含んだテーブルを見つけたり、よく使われる テーブルを題材に、探し出したテーブルに含まれるデータの構造を調べる方法を学習します。

contoso を含むレコードの検索を行ってください

search "contoso"

解説 :
search オペレーターはテーブルを指定しない場合、ワークスペース上の全てのテーブルに対して検索を行います。
一般的な KQL ではテーブルを指定してデータを取得しますが、目的のデータがどのテーブルに含まれているかわからない場合や、そもそもデータがワークスペースに存在するかどうかわからない場合などはsearch オペレーターを使用することができます。
大量のデータを検索する場合パフォーマンス上不利になるため、定期的に実行されるクエリなどでの多用は避けてください。 また、Kusto の実行環境によっては使えません。例えば Log Analytics では使うことができますが、Azure Resource Graph では使うことができません。

contoso と retail を両方含むレコードの検索を行ってください

// Prefered
search "contoso" and "retail"

// NOT Prefered
// search "contoso" | search "retail"
// search "contoso" | where Name contains "retail"

解説 :
search オペレーターは多くの結果を生成する可能性があり、パフォーマンスに影響を与えます。複数の条件を指定する必要がある場合には、1つの search オペレーターの条件に含めるようにしてください。複数の search オペレーターをパイプしたり、search オペレーターの結果を where でフィルタするような処理は、中間処理のために前の search オペレーターによって大きな結果セットが生成されます。これに対して複数の条件を指定した1つの search オペレーターはそれよりも小さな結果セットを生成します。

大文字から始まる Contoso を含むレコードを表示してください

search kind=case_sensitive "Contoso"

解説 :
search オペレーターは既定で大文字小文字を区別せずに検索を行いますが、kind 引数で明示的に動作を指定することができます。case_sensitive は大文字小文字を区別する指定です。

Perf テーブルから contosohotels を含むレコードを表示してください

search in (Perf) "contosohotels"

// able to search piped Table
Perf
| search "contosohotels"

解説 :
search オペレーターはテーブルを指定して検索を行うことができます。in 引数の値で検索対象とするテーブルを指定します。search オペレーターはパイプから受け取ったテーブルに対して検索を行うこともできます。

SecurityEvent テーブルから contosohotels を含むレコードを表示してください

search in (SecurityEvent) "contosohotels"

// able to search piped Table
SecurityEvent
| search "contosohotels"

解説 :
この例は1つ前とほとんど同じです。同じ文字列を持つレコードが複数のテーブルに存在する可能性があることを理解するために用意しています。

Alert テーブルと SecurityAlert テーブルから contosohotels を含むレコードを表示してください

search in (Alert,SecurityAlert) "contosohotels"

// Other expression
// Need to combine multiple tables
Alert
| union SecurityAlert
| search "contosohotels"

解説 :
“in” 引数では複数のテーブルを指定することができます。この例では関連しそうな情報をもつテーブルをまたいで検索を行います。追加の例は KQL では複数のテーブルをパイプラインから渡すことができないため、予め複数のテーブルを “union” で接続しています。

[重要]ワークスペースにあるテーブルの一覧を表示してください

search *
| distinct $table

解説 :

KQL では通常テーブルを指定して検索を行うため、ワークスペースにどのようなテーブルがあるかを知る必要があります。 search オペレーターの検索結果には所属するテーブルを表す $table カラムが含まれるため、distinct オペレーターで一意の値を抽出することで、ワークスペースに含まれている全てのテーブルを一覧することができます。

存在するテーブルは UI から確認するのは非常に時間がかかるため、簡単にテーブル名だけを確認する方法としてこの例を挙げています。

AzureDiagnostics テーブルのレコードを 10 個表示してください

AzureDiagnostics
| take 10

// limit and take is same 
//AzureDiagnostics
//| limit 10

解説 :
take オペレーターは素早くレコードの中身を確認したいときに使います。take は引数の数を上限としてレコードを取り出します。AzureDiagnostics は Azure 診断ログを保存していて非常に多くのレコードを含んでいるます。単純に AzureDiagnostics のテーブルを表示した場合、非常に長い実行時間が必要ですが take で結果を少数のレコードに絞ることですぐに結果を確認することができます。limit オペレーターも take と同じ動作をします。
take は毎回同じ結果が返るわけではありません。たとえば時刻順に最新のデータが取得されるわけではなく、データセットが変わらない場合でも前回実行時と結果が異なる場合があります。

AzureDiagnostics テーブルの Category の一覧を表示してください

AzureDiagnostics
|distinct Category

解説 :
AzureDiagnostics テーブルに含まれる診断ログには複数のリソースからのログから成り立っています。一般的に数十種類のリソースのログが集約されているため、どのようなリソースのログが含まれているかを把握することが重要です。
Category 列にはログを生成したリソースやログの種類のヒントが含まれているため、distinct オペレーターで Category の値を一覧にすることで、目的のログの有無を判断することが容易になります。

AzureDiagnostics テーブルから ResourceType と Category の組み合わせの一覧を表示してください

AzureDiagnostics
|distinct ResourceType,Category

解説 :
AzureDiagnostics の ResourceType にはログを生成したリソースの型が含まれるため、Category と組み合わせて distinct することで、どのリソースがどのカテゴリのログを生成しているか、わかりやすく表示することができます。

AzureDiagnostics テーブルから ApplicationGatewayAccessLog を表示してください<

AzureDiagnostics
|where Category == "ApplicationGatewayAccessLog"

解説 :
where オペレーターは与えられた条件によってレコードを選択するフィルタです。AzureDiagnostics テーブルのログは種類ごとに異なる Category 列の値を持つため、Category 列の値でフィルタすることで、目的のリソースや目的の種類のログだけを選ぶことができます。

AzureDiagnostics テーブルから ApplicationGatewayAccessLog をレコードの新しい順に表示してください

AzureDiagnostics
|where Category == "ApplicationGatewayAccessLog"
|sort by TimeGenerated desc 

解説 :
フィルタされたログはクエリの実行者が期待する順序で並んでいるとは限りません。特に時系列に従って並べられていることを期待されることが多いですが、そうでない場合が多くあります。
sort オペレーターは指定した列の値によってレコードをソートします。既定では降順でソートを行います。この例では desc 引数を指定して明示的に降順に並び変えていますが、省略することもできます。

AzureDiagnostics テーブルから ApplicationGatewayAccessLog をレコードの古い順に表示してください

AzureDiagnostics
|where Category == "ApplicationGatewayAccessLog"
|sort by TimeGenerated asc 

解説 :
sort オペレーターで昇順にソートする場合には asc 引数を指定します。

AzureDiagnostics テーブルのから ApplicationGatewayAccessLog をレコードの新しい順に 10 件表示してください

AzureDiagnostics
|where Category == "ApplicationGatewayAccessLog"
|top 10 by TimeGenerated desc 

解説 :
ソートされた結果からいくつかのレコードを取り出したい場合には top オペレーターを使用します。top は by 引数に指定された列の値でレコードをソートします。sort と同じく既定では降順にソートを行い、この例では明示的に desc を指定しています。

AzureDiagnostics テーブルのから ApplicationGatewayAccessLog をレコードの古い順に 10 件表示してください

AzureDiagnostics
|where Category == "ApplicationGatewayAccessLog"
|top 10 by TimeGenerated asc 

解説 :
top オペレーターも sort と同様に asc 引数で昇順のソートを行うことができます。

AzureMetrics テーブルのレコードを 10 個表示してください

AzureMetrics
| take 10

解説 :
AzureMetrics テーブルは Azure リソースのメトリックを保持するテーブルです。リソースの診断設定でメトリックを有効にした場合、このテーブルに情報が保存されます。 Metric は Windows でいうところのパフォーマンス カウンターのようなもので、リソースの数値的な側面を計測するために利用します。

AzureMetrics テーブルの MetricName の一覧を表示してください

AzureMetrics
| distinct MetricName

解説 :
AzureMetrics テーブルには様々なリソースからメトリックが送られます。各メトリックは MetricName というフィールドを持ち、あるレコードがどのメトリックのデータであるかを見分けることができます。

AzureMetrics テーブルの ResourceProvider と MetricName の組み合わせの一覧を表示してください

AzureMetrics
| distinct ResourceProvider,MetricName

解説 :
メトリックの種類はかなり多いので、一覧する際には生成元となっているリソース プロバイダーの情報も利用すると見やすくなります。 distinct オペレーターで ResourceProvider と MetricName の一意な組み合わせを表示すると、各リソースプロバイダ―が生成するメトリックを整理することができます。

AzureMetrics テーブルから Percentage CPU メトリックだけを表示してください

  AzureMetrics
  | where MetricName == "Percentage CPU"

解説 :
特定のメトリックを表示するためには where オペレーターで任意の MetricName を指定します。

AzureMetrics テーブルからコンピューター CH1-SQLVM12 の Percentage CPU を表示してください

AzureMetrics
| where Resource == "CH1-SQLVM12" and MetricName == "Percentage CPU"

解説 :
where オペレーターは複数の条件でレコードをフィルタすることができます。リソース名とメトリックを使用することで、特定のリソースの任意のメトリックだけを表示することができます。

AzureActivity テーブルのレコードを 10 個表示してください

AzureActivity
| take 10

解説 :
この例はデモ ワークスペースでは動作しないかもしれませんので、お手持ちの Log Analytics ワークスペースでお試しください。 [参考:Log Analytics ワークスペースに送信する]Log Analytics ワークスペースに送信する(https://docs.microsoft.com/azure/azure-monitor/essentials/activity-log#send-to-log-analytics-workspace) AzureActivity テーブルはリソースの変更や仮想マシンの起動といった、サブスクリプションに対する操作が記録されます。 変更作業の確認や、不必要なリソースに変更が行われていないかなどを調べることができます。

AzureActivity テーブルで ResourceProviderValue と _ResourceId と OperationNameValue の組み合わせの一覧を表示してください

AzureActivity
| distinct ResourceProviderValue,_ResourceId, OperationNameValue

解説 :
_ResourceId はリソースの一意の識別子、ResourceProviderValue はリソース プロバイダーを表します。このクエリにより、各リソースにどのような操作が行われたかをプロバイダごとに整理して表示することができます。

AzureActivity テーブルから操作を行ったユーザーとIP アドレスの一覧を表示する

  AzureActivity
  | distinct Caller,CallerIpAddress

解説 :
Caller は実行を行ったユーザー、CallerIpAddress はその際の操作元の IP アドレスです。意図しないユーザーや場所からの操作がないかどうかの確認に使うことができます。

AzureActivity テーブルで Administrative カテゴリの操作を行ったユーザーの一覧

AzureActivity
| where CategoryValue == "Administrative"   
| distinct Caller

解説 :
管理作業は CategoryValue に Administrative という値を含みます。

AzureActivity テーブルから管理作業に失敗したユーザーと IP アドレス、その操作対象となったリソースの一覧を表示してください

AzureActivity
| where ActivityStatusValue == "Failure" and CategoryValue == "Administrative"
| distinct Caller,CallerIpAddress, _ResourceId

解説 :
操作が失敗したエントリは ActivityStatusValue が Failure になります。このクエリにより管理業務を試みて失敗したユーザーを表示することができます。 失敗の理由は様々ですが、例えば CallerIpAddress が普段と異なる場合にはセキュリティ侵害を示唆している可能性があります。

参考ドキュメント

日付を指定・書式化する

Azure Monitor のログには、標準で定義されている列があり、データソースにより生成される時刻を示す TimeGenerated 列 によってログレコードの時刻が確認できます。TimeGenerated 列を使用することで、時刻を基にしたフィルターやレコードの件数を確認できます。

現在の日付を UTC 時間で表示してください

print now()

解説 :
現在の時刻は、now() 関数を使用すると取得できます。now() 関数を含む日付や時刻に関する関数が返す値は、datetime型となり Kusto では UTC 時間で表されます。datetime型のデータは、テーブルの時刻列(TimeGenerated)等で使用されており、フィルターやソートなどに使えます。

特定の日時(“2022年1月1日 12:00”)を UTC 時間で表示してください

print datetime("2022-01-01 12:00")

解説 :
datetime関数を使うと文字列を指定して日付や時刻を取得できます。datetime 関数の戻り値はdatetime型として取得できます。

特定の日時(“2022年1月1日 12:00”)を UTC+9 時間で表示してください

print datetime("2022-01-01 12:00") + 9h

解説 :
datetime 関数で返される datetime 型は UTC 時間で扱われます。タイムゾーンを意識した日付を扱う場合、ユーザーがタイムゾーンを指定する必要があります。UTC 時間を他のタイムゾーンに変更するには、その時間をdatetime型の値に足します(もしくは引きます)。datetime型に対する足し引きは、timespan型の値を使います。このクエリのように、9h(9時間)や30m(30分)のような表記を使用します。

現在の日時の 30 分前の日時を UTC+9 時間で表示してください

print datetime("now") + 9h - 30m

解説 :
datetime 型に対する足し引きを使うことで、過去や未来の時間を指定できます。datetime型に対する足し引きは、timespan型の値が使えます。

UTC 時間で特定の期間(“2022年1月1日"の 1 日のみ)のレコードを検索してください

Alert
| where TimeGenerated between (datetime("2022-01-01 00:00:00")..1day)

解説 :
トラブルシューティング等で Azure の各サービスが出力するログを確認する際、特定の 1 日のログを確認したいことがあります。TimeGenerated 列に対して、between関数とdatetime関数を組み合わせて使用することで時間の範囲を限定できます。

UTC 時間で特定の期間(“2022年1月1日 00時00分00秒"から"2022年1月2日 23時59分59秒"まで)のレコードを検索してください

Alert
| where TimeGenerated between (datetime("2022-01-01 00:00:00")..datetime("2022-01-02 23:59:59"))

解説 :
between関数の範囲の始まりと終わりに、datetime関数を使用するとdatetime関数に指定した日時を範囲とすることができます。

UTC 時間で特定の期間(“2022年1月1日 12時00分"から"2022年1月2日 12時59分"まで)のレコードを検索してください

Alert
| where TimeGenerated between (datetime("2022-01-01 12:00")..datetime("2022-01-02 12:59"))

解説 :
between関数の範囲の始まりと終わりに、datetime関数を使用するとdatetime関数に指定した日時を範囲とすることができます。datetime関数の引数にはさまざまな日付表現を指定できます。

UTC 時間で今日(0時0分から今の時刻まで)のレコードを検索してください

Alert
| where TimeGenerated > startofday(now())

解説 :
TimeGenerated型に対して比較演算子を使うことで、さまざまな時間の範囲の表現ができます。startofday関数は引数に指定された日付の開始日時を表すdatetime型の値を返します。つまり、“2022-01-01 12:34” は、“2022-01-01 0:00:00.000” を表します。

UTC 時間で今週(日曜日から今の時刻まで)のレコードを検索してください

Alert
| where TimeGenerated > startofweek(now())

解説 :
TimeGenerated型に対して比較演算子を使うことで、さまざまな時間の範囲の表現ができます。startofweek関数は引数に指定された日付の週の開始日時を表すdatetime型の値を返します。つまり、“2022-01-01 12:34” は、“2021-12-26 0:00:00.000” を表します。

3日前から現時点までのレコードを検索してください

Alert
| where TimeGenerated > ago(3day)

解説 :
ago 関数を使用すると現在の時刻から指定した日時前を表すdatetime型の値を返します。つまり、ago(3d) は、現在の時刻から3日前の時刻を表します。日付以外にも、1m1hなどの時間単位を指定できます。

過去30分間のレコードを検索してください

Alert
| where TimeGenerated > ago(30m)

解説 :
大きな時間範囲でクエリを実行すると実行時間が長くなります。Azure Firewall の診断ログや OS のパフォーマンスログ等大量にログが発生し得るテーブルを対象にする場合は、可能な限り時間範囲を絞った上でクエリを実行することを心がけてください。

過去1.5時間のレコードを検索してください

Alert
| where TimeGenerated > ago(1.5h)

解説 :
timespan型はさまざまな表現が出来ます。このクエリのように小数点を表すこともできます。

2日前から3日前までのレコードを検索してください

Alert
| where TimeGenerated between (now(-3d)..1day)

解説 :
now関数には、引数でtimespan型のオフセットを指定できます。between関数と組み合わせるとさまざまな時間範囲の表現ができます。このクエリは now(-3d)ago(3d) としても同じ結果になります。

ローカルのタイムゾーン(UTC+9h)で今日のレコードを検索してください

Alert
| where TimeGenerated > startofday(now()) - 9h

解説 :
TimeGeneratedstartofday関数は、UTC時間で表現されます。従って、ローカル時間でフィルターする場合はタイムゾーンを考慮する必要があります。

レコードの時刻と現在時刻の差をカラムとして追加してください

Alert
| where TimeGenerated > ago(30m)
| extend timeAgo = now() - TimeGenerated 

解説 :
アクセスログ等が含まれるテーブルにおいて、レコードの時刻と現在時刻の差を確認したいことがあります。extend と時刻関数を使用すると新たにカラムを追加し、時刻差を出力できます。

1週間分(日曜日から今の時刻まで)のレコードを1日単位でグループ化し、1日当たりのレコード件数を表示してください

AzureDiagnostics
| where TimeGenerated > startofweek(now())
| summarize count() by startofday(TimeGenerated)

解説 :
summarize関数は時刻関数と組み合わされることがよくあります。アクセスログを集計する際等に利用すると便利です。

過去30分のレコードを1分単位でグループ化し、1分当たりのレコード件数を表示してください

AzureDiagnostics
| where TimeGenerated >= ago(30m)
| summarize count() by bin(TimeGenerated, 1m)

解説 :
summarize関数とbin関数、時刻関数を組み合わせると日付に対してより柔軟な集計が出来ます。bin関数はfloor関数(床関数)のエイリアスです。

参考ドキュメント

データを整える

Azure Monitor に保存されるログは生成元のリソースに応じて多様なフィールドをもち、多様な分析を行うことができます。一方、全てのフィールドを表示してしまう、テーブルに非常に多くの列が表示され、結果のデータセットは大きく、見づらくなってしまいます。ここではデータから必要なフィールドだけを選び出したり、フィールドの名前を変更して分析しやすくしたりなど、データを整型する方法について学習します。

Heartbeat テーブルから過去 5 分以内にハートビートのあったコンピューターを表示してください、結果には Computer 列と TimeGenerated 列だけを含めてください

Heartbeat 
| where TimeGenerated > ago (5m)
| project Computer, TimeGenerated

解説 :
Heartbeat は Log Analytics エージェントによって送信される健全性の状態が記録されるテーブルです。 このテーブルを確認することで、特定の時刻のコンピューターの簡単な死活監視を行うことができます。
project 演算子は結果セットの出力を特定の列に絞ることができます。このクエリでは出力結果を Computer 列と TimeGenerated 列に限定しています。出力される列の順番は project に渡された引数の順番になり、元のテーブルの順序ではなくなります。
Heartbeat 列はこの他にコンピューターの OS の情報や国などの地理情報も含んでいます。

ハートビートを表示する際に、Computer 列を VM に、 TimeGenerated 列を TimeStamp という名前に変更してください

Heartbeat 
| where TimeGenerated > ago (5m)
| project VM=Computer, TimeStamp=TimeGenerated

解説 :
project 演算子は出力列を選択する際に、列の名前を任意の文字列に変更することができます。 これにより出力列をわかりやすい名前に変更することができるほか、join など列名を参照するようなオペレーターをより簡単に扱うことができます。

列名を変更する際にスペースを使った列名を指定してください

Heartbeat 
| where TimeGenerated > ago (5m)
| project ['Virtual Machine']=Computer, ['Time Stamp']=TimeGenerated

解説 :
見やすさのために列名にスペースを使いたいという場合がありますが、単純に ‘変更後 列名’ = 変更前の列名 のように文字列をクォートで囲むだけではうまくいきません。
括弧 [] で文字列を囲むことで、変更後の列名にスペース入が入った文字列を使用することができます。

式の利用 - TimeStamp に TimeGenerated に 5 時間を加算した値を設定してください

Heartbeat 
| where TimeGenerated > ago (5m)
| project VM=toupper(Computer), TimeStamp=datetime_add("Hour",5,TimeGenerated)

// simplified expression
Heartbeat 
| where TimeGenerated > ago (5m)
| project VM=toupper(Computer), TimeStamp= TimeGenerated + 5h

解説 :
project 演算子は列を選択する際に計算式を適用することができます。このクエリでは datetime_add 関数を使用して、TimeGenerated に 5時間を加えたものを新しい列の値として設定しています。

ハートビートを表示する際に VMUUID、MG、ManagementGroupName、SourceComputerId 列を出力しないようにしてください

Heartbeat 
| where TimeGenerated > ago (5m)
| project-away VMUUID, MG, ManagementGroupName, SourceComputerId

解説 :
project 演算子は出力する列を選択しますが、project-away 演算子は出力しない列を選択します。出力から取り除きたい列が少ない場合にはこの演算子のほうが効率的かもしれません。

Resource という文字列から始まる列を出力から取り除いてください

Heartbeat 
| where TimeGenerated > ago (5m)
| project-away Resource*

解説 :
project-away 演算子は特定のパターンを含む列を除外することができます。ワイルドカード文字を使用して、例えば ABC* を project-away に渡すと 「ABC から始まる列は除く」という意味になります。

project-keep 演算子を使用して Resource という文字列から始まる列だけを出力してください

Heartbeat 
| where TimeGenerated > ago (5m)
| project-keep Resource*

解説 :
project-keep 演算子は project と同じように列の選択を行います。結果の列の順序はテーブルの素の順序に従うという点が project との大きな違いです。

project-reorder 演算子を使用して列を並び替えてください

Heartbeat 
| where TimeGenerated > ago (5m)
| project-reorder * desc

解説 :
出力結果の列は project-reorder 演算子で並び替えることができます。この例では全ての列を降順に並び替えています。

project-rename 演算子を使用して列名を変更してください

Heartbeat 
| where TimeGenerated > ago (5m)
| project-rename VM=Compute

解説 :
project-rename 演算子は列名を変更することができます。project でも列名の変更はできますが、project-rename は他の列のフィルタリングには影響を与えずに、特定の列の名前だけを変更することができます。

計算結果を持つ新しい列を追加してください

Heartbeat
| extend
    Age = now() - TimeGenerated

解説 :
extend 演算子は計算列を作成し、結果セットに追加します。この例では現在時刻とログが生成された時刻の差分を計算した新しい列を追加しています。

新しい列を追加し、5 分より新しいレコードには✔️を、古いレコードには❌を設定してください

Heartbeat
| summarize LastReported=now()-max(TimeGenerated) by Computer,TimeGenerated
| extend RecentHeartBeat=iff(TimeGenerated > ago(5m),'✔️ ', '❌ ')

解説 :
iif 関数は最初の引数が true であれば 2番目の引数を、false であれば 3 番目の引数の結果を返します。2番目や3番目の引数には計算式を持つこともでき、条件に応じてより複雑な処理を行うこともできます。

データを並び替え・集約する

ログは特定の目的のために並び替えを行ったり、データの特定の側面にフォーカスするために集計をする必要があります。ここでは summarize 演算子と、集計関数を使用して、ログやメトリックに含まれる情報を整理する方法について学習します。

summarize 演算子を使用して、InsightsMetrics テーブルに収集されているパフォーマンス カウンターの数をコンピューターごとに集計してください

InsightsMetrics
| summarize count() by Computer, Namespace, Name

解説 :
summarize 演算子は入力されたテーブルを様々な形に集計することができます。集計の種類は引数の集計関数で指定することができ、count() は単純にレコードの数を数える集計関数です。 集計の単位は by に続く列名で指定することができ、この例では Computer と Namespace の組み合わせに対して集計を行っています。
InsightsMetrics は (従来の Log Analytics エージェントによって収集される Perf ではなく) VM Insights によって収集されるパフォーマンス カウンターを格納するためのテーブルです。

コンピューターごとに何種類のパフォーマンス カウンターがあるかを集計してください

InsightsMetrics 
| summarize dcount(Name) by Computer

解説 :
dcount() 関数は与えられた列の中で、ユニークな値が何個あるかを計算する集計関数です。この例ではコンピューターから収集されるパフォーマンス カンターの名前ですが、ユーザーがログインしている場所の数、コンピューターが接続したことのある IP の数など、様々な集計に使うことができます。

UtilizationPercentage の平均値をコンピューターごとに集計してください

InsightsMetrics
| where Name == "UtilizationPercentage"
| summarize avg(Val) by Computer

解説 :
summarize 演算子に与える集計関数を変えることで集計方法が変わります。avg() 関数に対象とする列名を与えることで、列の値の平均値を計算することができます。

UtilizationPercentage の最大値をコンピューターごとに集計してください

InsightsMetrics
| where Name == "UtilizationPercentage"
| summarize max(Val) by Computer

解説 :
max() 関数に対象とする列名を与えることで、列の値の最大値を計算することができます。

UtilizationPercentage の最小値をコンピューターごとに集計してください

InsightsMetrics
| where Name == "UtilizationPercentage"
| summarize min(Val) by Computer

解説 :
min() 関数に対象とする列名を与えることで、列の値の最小値を計算することができます。

UtilizationPercentage の平均値の 1 時間ごとの推移をコンピューターごとに集計してください

InsightsMetrics
| where Name == "UtilizationPercentage"
| summarize avg(Val) by Computer, bin(TimeGenerated, 1h)

解説 :
summarize 演算子の集計は複数の列に対して行うことができます。この例は非常によく使われるパターンで、注目したいリソースと時間に対して集計を行うことで、特定のリソースのふるまいを時系列で集計することができます。 単純に TimeGenerated を使った場合には単位が細かすぎてしまうので、bin() 関数を使って丸めるのが一般的です。例では 1 時間単位に丸めていますが、10 分 (10m) 、100 秒 (100s) といった丸め方もできます。

容量の少ないディスクを 10 個表示してください

InsightsMetrics 
| where Namespace == "LogicalDisk" and Name == "FreeSpacePercentage"
| extend DiskInstance = tostring(parse_json(Tags).["vm.azm.ms/mountId"])
| summarize FreeDiskSpace = avg(Val) by Computer, DiskInstance
| order by FreeDiskSpace asc 
| take 10

解説 :
ディスクの空き容量を表すパフォーマンス カウンターの名前は FreeSpacePercentage です。この例では集計した時間帯の FreeSpacePercentage の平均値が最も小さいディスクを 10 個選択しています。
InsightsMetrics テーブルではボリュームのラベルは直接値としては列に現れず、JSON データとして Tags 列に含まれています。 このため parse_json 関数を使用して Tags 列を解析して取得したボリュームのラベルの値を extend 演算子により新たな列の値として設定しています。

空き容量が閾値 (20 %) 以下のロジックディスクを表示してください

let threshold = 20;
InsightsMetrics 
| where Namespace == "LogicalDisk" and Name == "FreeSpacePercentage"
| extend DiskInstance = tostring(parse_json(Tags).["vm.azm.ms/mountId"])
| summarize FreeDiskSpace = avg(Val) by Computer, DiskInstance
| where FreeDiskSpace < threshold

解説 :
空き容量が一定の値以下のディスクを表示しています。この例では閾値となる数値を threshold という名前の変数に格納しています。
変数を使用することで変更、再利用が容易で、わかりやすいクエリにすることができます。

Count the number of events collected per computer

Event 
| summarize count() by Computer

解説 :
コンピュータごとに収集したイベント数をカウントする

コンピュータごとに収集されたイベント数を時系列でカウントする

Event 
| summarize count() by bin(TimeGenerated, 1h), Computer

解説 :
コンピュータごとに収集したイベント数を1時間のタイムバケットに集約してカウントする

コンピュータの収集イベントトップ10を表示

Event 
| summarize count() by Computer
| top 10 by Computer

解説 :
コンピュータごとに収集したイベント数をカウントし、イベントを収集している上位10台のコンピュータを表示します。

全マシンのトッププロセスを表示

Perf 
| where ObjectName == "Process" and CounterName == "% Processor Time"
| summarize AverageValue = avg(CounterValue) by InstanceName
| top 10 by InstanceName
| order by AverageValue

解説 :
全サーバーにおけるプロセスの % Processor をまとめ、上位10位までをリストアップしてください。

参考ドキュメント

可視化する

Azure Monitor ではクエリで取得したデータセットのテーブルを見やすく表示することができます。ここでは render 演算子を使用して、データセットを棒グラフや折れ線グラフとして可視化する方法を学習します。

Show event count as a chart

SecurityEvent 
| summarize EventCount = count() by EventID
| top 10 by EventCount
| render columnchart 

Description :
Use the render operator to draw a chart of the top 10 Security Events

Show event count distribution over time

SecurityEvent 
| summarize EventCount = count() by bin(TimeGenerated, 1h), Computer
| render timechart 

Description :
Use the render operator to draw a chart of the security event distribution over time, per computer

Show CPU Utilisation over time

InsightsMetrics
| where Namespace == "Processor" and Name == "UtilizationPercentage"
| summarize AverageValue = avg(Val) by bin(TimeGenerated, 1h), Computer
| render timechart with (ycolumns = Computer, series= AverageValue)

Description :
Use the render operator to draw a chart of the processor utilisation over time with aggregations into 1 hour averages, broken down by computer

Show Memory Utilisation over time

InsightsMetrics
| where Namespace == "Memory" and Name == "AvailableMB"
| extend memorySizeMB = toint(parse_json(Tags).["vm.azm.ms/memorySizeMB"])
| extend PercentageUsed = 100-(Val/memorySizeMB)*100
| summarize AverageValue = avg(PercentageUsed) by bin(TimeGenerated, 30m), Computer
| render timechart

Description :
Use the render operator to draw a chart of the percentage used memory over time with aggregations into 30 minute averages, broken down by computer. The memory used percentage is a calculated column, as only available memory in MB and total memory in MB is presented

Show Pie graph of sources collected in Azure Diagnostics

AzureDiagnostics 
| summarize count() by ResourceProvider
| render piechart 

Description :
Use the render operator to draw a chart of the different sources collected into Azure Diagnostics

Show Pie graph of current missing updates in Update Management

Update
| where UpdateState == "Needed" and OSType!="Linux"
| summarize  arg_max(TimeGenerated, *) by Title
| where (Classification == "Security Updates" or Classification == "Critical Updates" or Classification == "Definition Updates" or Classification == "Others")
| summarize MissingUpdates = count() by Classification
| order by MissingUpdates desc
| render piechart 

Description :
Use the render operator to draw a chart of the missing update count in Update Management

Show Area Chart of Logic Apps Run Latency

AzureMetrics 
| where ResourceProvider == "MICROSOFT.LOGIC"
| where MetricName == "RunLatency"
| summarize AverageLatency = avg(Average) by bin(TimeGenerated, 10m), Resource
| render areachart

Description :
Use the render operator to draw a chart of run latency for logic apps

参考ドキュメント

テーブルを結合する

数のテーブルに分散した特定のリソースの情報を分析したいようなケースではテーブルを結合することができます。よく似た(あるいは全く同じ性質の)テーブル同士を結合したデータセットに対して分析を行ったり、あるいはリソースへの追加となる情報を持つテーブルから特定の情報を選び出し、データをエンリッチすることができます。ここではテーブルを結合するための演算子を学習します。

Join VMComputer and InsightMetrics to get last heartbeat per computer

VMComputer
| project Computer, OperatingSystemFullName, _ResourceId
| join (
InsightsMetrics 
| where Name == "Heartbeat"
| summarize LastHeartbeat = arg_max(TimeGenerated, *) by _ResourceId
| project LastHeartbeat, _ResourceId
) on _ResourceId
| project-away _ResourceId1

Description :
Use the join operator to join two tables to see last heartbeat per computer

Join two queries from the SecurityEvents table together to determine logon duration

SecurityEvent 
| where EventID == 4624		// sign-in events
| project Computer, Account, TargetLogonId, LogonTime=TimeGenerated
| join kind= inner (
SecurityEvent 
| where EventID == 4634		// sign-out events
| project TargetLogonId, LogoffTime=TimeGenerated
    ) on TargetLogonId
| extend Duration = LogoffTime-LogonTime
| project-away TargetLogonId1 
| top 10 by Duration desc

Description :
Use the join operator to join two queries from the SecurityEvents table together to determine logon duration

Join Heartbeat table with Update Management table to view update status

Update
| where OSType!="Linux" and Optional==false
| summarize hint.strategy=partitioned arg_max(TimeGenerated, *) by Computer,SourceComputerId,UpdateID
| where UpdateState=~"Needed" and Approved!=false
| join (UpdateSummary 
| summarize arg_max(TimeGenerated, *) by Computer, OsVersion, RestartPending
| project Computer, OsVersion, RestartPending
)
on Computer
| join ( Heartbeat
| summarize LastHeartBeat = arg_max(TimeGenerated, *) by Computer
| project LastHeartBeat, Computer
) on Computer
| summarize arg_max(TimeGenerated, *), SecurityUpdates = countif(Classification == "Security Updates"), CriticalUpdates = countif(Classification == "Critical Updates"), DefinitionUpdates = countif(Classification == "Definition Updates"), ServicePacks = countif(Classification == "Service Packs"), Others = countif(Classification != "Security Updates" and Classification != "Critical Updates" and Classification != "Definition Updates" and Classification != "Service Packs") by Computer, OsVersion, RestartPending, ResourceId, LastHeartBeat
| project Computer, ResourceId, OsVersion, RestartPending, LastHeartBeat, SecurityUpdates, CriticalUpdates, DefinitionUpdates, ServicePacks, Others

Description :
Use the join operator to join Heartbeat table to Update Management table to view update status, last heartbeat and reboot status

Join Heartbeat table with syslog table to get count of syslog events

Heartbeat 
| summarize arg_max(TimeGenerated, *) by Computer, _ResourceId
| join (
Syslog
| summarize SyslogCount = count() by Computer, _ResourceId
) on _ResourceId
| project _ResourceId, Computer, ComputerIP, OSName, ResourceGroup, SubscriptionId, SyslogCount

Description :
Use the join operator to join Heartbeat table to syslog table to get a count of events per computer

参考ドキュメント

データをフォーマットする

Azure Monitor に保存されるログは、全部が分析のために最適な形になっているわけではありません。具体的にはログの生成元が XML や JSON、あるいは構造化されていないただのテキストデータを生成していて、そのデータは複数のフィールドとして加工されずにどこか一つのフィールドに格納されていることがあります。ここでは、そのようなフィールドを扱いやすく加工するための方法について学習します。

リソースID を解析する

AzureDiagnostics
| where ResourceType == "SERVERS/DATABASES"
| parse ResourceId with * "/RESOURCEGROUPS/" ResourceGroupName "/" * "/DATABASES/" Databasename
| project TimeGenerated, Category, OperationName, ResourceGroupName, Databasename 

解説 :
parse オペレーターを使用して、リソース ID フィールドからリソースグループとリソー ス名を抽出します。

syslogの文字列を解析する

Syslog 
| where Facility == "authpriv"
| parse SyslogMessage with * "(" session "):" * "user" UserName

解説 :
ログの文字列エントリから一貫して文字列値を抽出するには、parse コマンドを使用します。

クエリから特定の列を返す

VMComputer 
| summarize arg_max(TimeGenerated, *) by Computer
| extend IPAddress = tostring(Ipv4Addresses[0])
| extend DNSNAme = tostring(DnsNames[0])
| project Computer, BootTime, IPAddress, DNSNAme, OperatingSystemFullName, PhysicalMemoryMB, Cpus, CpuSpeed, AzureResourceGroup, AzureSubscriptionId

解説 :
project 演算子を使用して、クエリの列を特定することができます。

クエリ結果から特定のカラムを除外する

SecurityEvent
| summarize count() by EventID, Activity
| project-away EventID

解説 :
クエリ結果から特定のカラムを除外するには、project-away 演算子を使用します。

参考ドキュメント

その他

ここでは複雑なクエリを書く際に頻繁に使われる記法や、必要とされる頻度は多くないものの特定のな問題を効率よく解決するための KQL を紹介します。

色々な分析の中で

サービスタグに含まれる IP プレフィックスの一覧を取得する

let serviceName= "VirtualDesktop";
externaldata(changeNumber: string, cloud: string, values: dynamic)["https://download.microsoft.com/download/7/1/D/71D86715-5596-4529-9B13-DA13A5DE5B63/ServiceTags_Public_20220228.json"]  with(format='multijson')
| mv-expand values
| where values contains serviceName
| mv-expand values.properties.addressPrefixes
| project addressPrefixes= tostring( values_properties_addressPrefixes)

解説 :

Azure のサービスで使われる IP アドレスはサービスタグとして公開されていて、インターネット上から参照することができます。
externaldata オペレーターは外部のストレージ アカウントから CSV や JSON などで記述されたデータを取得します。このサンプルでは直接インターネット上からファイルを取得していますが、SAS を使用してあアクセスが制御されているストレージ アカウントからデータを取得することができます。

注意 - ファイル名 ServiceTags_Public_YYYYMMDD.json は IP リストの更新に伴い変更されるため、次のページから常に新しいファイルを参照する必要があります。
Azure IP Ranges and Service Tags – Public Cloud

KQL で参照するファイルは自身で管理するストレージアカウントに配置し、リストが更新されるたびに自動的にストレージ アカウントのファイルが更新されるような仕組みを考えてください。
Using external data sources to enrich network logs using Azure storage and KQL

日付や文字列などの計算結果をクイックに確認する

print now()

print now(9h)

print ago(9h)

print substring("hello KQL world",6,3)

print  1 + 2 + 3

print iif(true, 100 , 200)

print iif(false, 100 , 200)

解説 :
print オペレーターはスカラー式(複数の行を持つテーブルを作らない計算)を実行します。時刻や文字列の計算を長いクエリに含める際に、予め部分的に計算結果を確認しておきたいような場合に便利です。

参考ドキュメント

最終更新 March 21, 2022: update _index.md (#78) (9019733)