応用編

Azure のサービスでよく使われるクエリを学習します。

Network

ファイアウォールによって拒否された Azure ファイアウォール アプリケーション ルールの確認

let q1 = view() {
AzureDiagnostics
| where Category == "AzureFirewallApplicationRule" 
| where msg_s contains " to "
|parse kind = regex msg_s with Protocol @" request from " SourceIP ":" SourcePort @" to " TargetURL ":" targetPort @". Action: " Action @".\sN" Reason
};
let q2 = view() {
AzureDiagnostics
| where Category == "AzureFirewallApplicationRule" 
| where msg_s !contains " to "
|parse kind = regex msg_s with Protocol @" request from " SourceIP ":" SourcePort @". Action: " Action @". Reason:" Reason
};
union withsource="temptable" q1,q2 
| where SourceIP == "xx.xx.xx.xx"
| summarize count() by TargetURL, targetPort
//#| summarize count() by FQDN | order by count_ desc 
| order by count_ desc 

解説 :
ファイアウォールによって拒否された Azure ファイアウォール アプリケーション ルールの確認

特定の IP およびポートに対する Azure ファイアウォールの拒否接続 (ネットワーク ルール) を一覧表示する (必要に応じてクエリを変更する)

AzureDiagnostics
| where Category == "AzureFirewallNetworkRule"
| parse msg_s with Protocol " request from " SourceIP ":" SourcePortInt:int " to " TargetIP ":" TargetPortInt:int *
| parse msg_s with * ". Action: " Action1a
| parse msg_s with * "was " Action1b " to " NatDestination
| parse msg_s with Protocol2 " request from " SourceIP2 " to " TargetIP2 ". Action:" Action2
| extend SourcePort = tostring(SourcePortInt),TargetPort = tostring(TargetPortInt)
| extend Action = case(Action1a == "", case(Action1b == "",Action2,Action1b), Action1a),Protocol = case(Protocol == "", Protocol2, Protocol),SourceIP = case(SourceIP == "", SourceIP2, SourceIP),TargetIP = case(TargetIP == "", TargetIP2, TargetIP),SourcePort = case(SourcePort == "", "N/A", SourcePort),TargetPort = case(TargetPort == "", "N/A", TargetPort),NatDestination = case(NatDestination == "", "N/A", NatDestination)
| project TimeGenerated,Protocol, SourceIP,SourcePort,TargetIP,TargetPort,Action, NatDestination
| where SourceIP == "10.1.4.25" and TargetPort == 22 and Action == "Deny"
| order by TimeGenerated desc 

解説 :
特定の IP およびポートに対する Azure ファイアウォールの拒否接続 (ネットワーク ルール) を一覧表示する (必要に応じてクエリを変更する)

NSG フローログ - 送信元 IP に基づいてフローを表示する (必要に応じてクエリを変更する)

  AzureNetworkAnalytics_CL
  | where SubType_s == "FlowLog" 
  | where SrcIP_s == "ipaddress"
  | summarize by DestIP_s, DestPort_d

解説 :
NSG フローログ - 送信元 IP(ディスプレイ宛先ポートおよび IP)に基づいてフローを表示します。必要に応じて、IP アドレスを置き換えます。

NSG フローログ - ターゲット IP に基づいてフローを表示する (ディスプレイ ソース ポートと IP)

AzureNetworkAnalytics_CL
  | where SubType_s == "FlowLog" 
  | where DestIP_s == "ipaddress"
  | summarize by SrcIP_s, DestPort_d

解説 :
NSG フローログ - ターゲット IP に基づいてフローを表示する (ディスプレイ ソース ポートと IP)

バイト単位に基づいて上位 3 つの受信プロセスを調査する

VMConnection
| where Computer == "computername"
| where TimeGenerated between (ago(1d) .. ago(5m))
| summarize Received = sum(BytesReceived/1024) by ProcessName
| top 3 by Received desc

解説 :
バイト単位に基づいて上位 3 つの受信プロセスを調査する

送信バイト数に基づいて上位 3 つの送信プロセスを調査する

VMConnection
| where Computer == "computername"
| where TimeGenerated between (ago(1d) .. ago(5m))
| summarize Sent = sum(BytesSent/1024) by ProcessName
| top 3 by Sent desc

解説 :
送信バイト数に基づいて上位 3 つの送信プロセスを調査する

受信バイト数と送信バイト数に基づいて上位 3 つのプロセスを調査する

VMConnection
| where Computer == "computername"
| where TimeGenerated between (ago(1d) .. ago(5m))
| summarize Received = sum(BytesReceived/1024), Sent = sum(BytesSent/1024) by ProcessName
| extend TotalMb = Received + Sent
| extend SentMb = Sent
| extend ReceivedMb = Received
| project ProcessName, SentMb, ReceivedMb, TotalMb
| top 3 by TotalMb desc

解説 :
受信バイト数と送信バイト数に基づいて上位 3 つのプロセスを調査する

ネットワークの送信/秒と受信/秒、時間枠の30分平均を示すグラフ

Perf
| where Computer == "computername"
| where TimeGenerated between (ago(1d) .. ago(5m))
| where ObjectName == "Network Adapter"
| where InstanceName == "Microsoft Hyper-V Network Adapter _2"
| summarize avg(CounterValue) by CounterName, bin(TimeGenerated, 30min)
| order by TimeGenerated desc
| render timechart 

解説 :
ネットワークの送信/秒と受信/秒、時間枠の30分平均を示すグラフ

受信バイト数と送信バイト数に基づいて上位 3 つの通信パートナーを調査する

VMConnection
| where Computer == "computername"
| where TimeGenerated between (ago(1d) .. ago(5m))
| extend DNS = tostring(parse_json(RemoteDnsQuestions)[0])
| summarize Received = sum(BytesReceived/1024), Sent = sum(BytesSent/1024) by DestinationIp, DNS
| extend TotalMb = Received + Sent
| extend SentMb = Sent
| extend ReceivedMb = Received
| project DestinationIp, SentMb, ReceivedMb, TotalMb, DNS
| top 3 by TotalMb desc

解説 :
受信バイト数と送信バイト数に基づいて上位 3 つの通信パートナーを調査する

受信および送信の通信イベントが最も多い上位 10 台のコンピュータを調査する

VMConnection
| where TimeGenerated between (ago(1d) .. ago(5m))
| summarize Events = count() by Computer
| top 10 by Events desc

解説 :
受信および送信の通信イベントが最も多い上位 10 台のコンピュータを調査する

ほとんどの通信イベントで上位 10 個のポートを調査する

VMConnection
| where TimeGenerated between (ago(1d) .. ago(5m))
| summarize Events = count() by DestinationPort
| top 10 by Events desc

解説 :
ほとんどの通信イベントで上位 10 個のポートを調査する

受信バイト数と送信バイト数に基づいて上位 3 つの通信ポートを調査する

VMConnection
| where TimeGenerated between (ago(1d) .. ago(5m))
| summarize Received = sum(BytesReceived/1024), Sent = sum(BytesSent/1024) by DestinationPort
| extend TotalMb = Received + Sent
| extend SentMb = Sent
| extend ReceivedMb = Received
| project DestinationPort, SentMb, ReceivedMb, TotalMb
| top 3 by TotalMb desc

解説 :
受信バイト数と送信バイト数に基づいて上位 3 つの通信ポートを調査する

プロセス名に基づいて上位 3 つの通信ポートを調べる

VMConnection
| where TimeGenerated between (ago(1d) .. ago(5m))
| summarize Received = sum(BytesReceived/1024), Sent = sum(BytesSent/1024) by ProcessName
| extend TotalMb = Received + Sent
| extend SentMb = Sent
| extend ReceivedMb = Received
| project ProcessName, SentMb, ReceivedMb, TotalMb
| top 3 by TotalMb desc

解説 :
プロセス名に基づいて上位 3 つの通信ポートを調べる

Compute

コンピュータごとに最後のハートビートを取得する

Heartbeat 
| summarize LastHeartBeat = arg_max(TimeGenerated, *) by Computer

解説 :
Microsoft 監視エージェントがコンピューターに展開されている場合、1 分ごとにハートビートを送信します。このクエリを使用すると、各コンピュータから送信された最後のハートビートを確認できます。

5分間ハートビートを送っていないコンピュータを取得する

Heartbeat 
| summarize LastHeartBeat = arg_max(TimeGenerated, *) by Computer
| where LastHeartBeat < ago(5m)

解説 :
5分間ハートビートを送っていないコンピュータを取得する

過去 4 時間に集計されたすべてのコンピュータのディスク空き容量の割合を確認する

InsightsMetrics
| where TimeGenerated > ago(4h)
| where Namespace == "LogicalDisk"
| where Name == "FreeSpacePercentage"
| extend DiskInstance = tostring(parse_json(Tags).["vm.azm.ms/mountId"])
| summarize DiskFreeSpace = avg(Val) by bin(TimeGenerated, 4h), Computer, DiskInstance

解説 :
VM インサイトを使用する場合、すべてのパフォーマンス カウンターがインサイト メトリック テーブルに収集されます。このクエリは、過去 4 時間に集計されたすべてのディスクの空き領域の割合を示します。

すべてのコンピュータの更新管理の最新の状態を表示する

UpdateSummary 
| summarize arg_max(TimeGenerated, *) by Computer
| project Computer, LastUpdateApplied, OldestMissingSecurityUpdateInDays, WindowsUpdateSetting, SecurityUpdatesMissing, OtherUpdatesMissing, RestartPending

解説 :
更新の管理を使用する場合は、このクエリを使用して、各コンピュータの最新の状態を表示できます。

過去 7 日間の各更新ジョブの状態を表示します

UpdateRunProgress
| where TimeGenerated > ago(7d)
| summarize Failed = countif(InstallationStatus == "Failed"), FailedtoStart = countif(InstallationStatus == "FailedtoStart"), InProgress = countif(InstallationStatus == "InProgress"), Succeeded = countif(InstallationStatus == "Succeeded"), NotIncluded = countif(InstallationStatus == "NotIncluded"), NotStarted = countif(InstallationStatus == "NotStarted") by UpdateRunName

解説 :
このクエリを使用して、過去 7 日間の各更新ジョブの各ステータスの更新数を確認します。

Data

Azure SQL Database: SQL Database で出力可能なカテゴリを表示してください

AzureDiagnostics
| summarize by Category

解説 :
「DatabaseWaitStatistics」カテゴリでは指定した範囲期間内にデータベース内で発生した内部リソースの待機時間の詳細が確認できます。「QueryStoreWaitStatistics」カテゴリではクエリストア内に格納された情報全体からデータベース内で発生した内部リソースの待機時間の詳細が確認できます。「Errors」カテゴリでは発生したエラーを確認できます。

Azure SQL Database: リソース待機時間の詳細を円グラフで表示してください

AzureDiagnostics
| where Category == 'DatabaseWaitStatistics'
| summarize sum(delta_wait_time_ms_d) by wait_type_s
| sort by sum_delta_wait_time_ms_d desc
| render piechart 

解説 :
待機時間の多いリソースに着目することで、データベースのパフォーマンス改善の糸口を発見することができます。

Azure SQL Database: リソース待機回数の詳細を円グラフで表示してください

AzureDiagnostics
| where Category == 'DatabaseWaitStatistics'
| summarize sum(delta_waiting_tasks_count_d) by wait_type_s
| sort by sum_delta_waiting_tasks_count_d desc
| render piechart 

解説 :
待機回数の多いリソースに着目することで、データベースのパフォーマンス改善の糸口を発見することができます。

Azure SQL Database: クエリストア内に格納されたリソース待機時間情報の詳細を円グラフで表示してください

AzureDiagnostics
| where Category == 'QueryStoreWaitStatistics'
| summarize sum(total_query_wait_time_ms_d) by wait_category_s
| sort by sum_total_query_wait_time_ms_d desc
| render piechart 

解説 :
待機時間の多いリソースに着目することで、データベースのパフォーマンス改善の糸口を発見することができます。クエリストア内にはデフォルトで1ヵ月分の情報が格納されるため、より広い範囲での確認が可能です。

Azure SQL Database: SQL Database で発生したエラーを表示してください

AzureDiagnostics
| where Category == 'Errors'
| project TimeGenerated, error_number_d, Severity, state_d, Message

解説 :
データベースで発生したエラーを把握することは顕在化した障害の解決や、潜在的な障害を未然に防止することにつながります。

Azure SQL Database: SQL Database で発生したエラー数を時系列に折れ線グラフで表示してください

AzureDiagnostics
| where Category == 'Errors'
| summarize count() by TimeGenerated
| render timechart  

解説 :
データベースで発生したエラーの回数を把握することは顕在化した障害の解決や、潜在的な障害を未然に防止することにつながります。

Azure SQL Managed Instance: リソース使用状況を表示してください

AzureDiagnostics
| where Category == "ResourceUsageStats"

解説 :
Category オペレーターに “ResourceUsageStats” を指定することで、Azure SQL Managed Instance の様々なリソースの使用状況を確認することができます。

Azure SQL Managed Instance: 平均 CPU 使用率、読み込みバイト数、書き込みバイト数を表示してください

AzureDiagnostics
| where Category == "ResourceUsageStats"
| project avg_cpu_percent_s, io_bytes_read_s, io_bytes_written_s

解説 :
秒ごとの CPU 使用率は avg_cpu_percent_s 列、秒ごとの読み取りバイト数は io_bytes_read_s 列、秒ごとの書き込みバイト数は io_bytes_written 列で確認することが可能です。

Azure SQL Managed Instance: 平均 CPU 使用率、読み込みバイト数、書き込みバイト数の 5 分ごとの平均値を、時刻で並べ替えて昇順に表示してください

AzureDiagnostics
| where Category == "ResourceUsageStats" 
| sort by  asc 
| summarize avg(todouble(avg_cpu_percent_s)), avg(todouble(io_bytes_read_s)), avg(todouble(io_bytes_written_s)) by bin(TimeGenerated, 5m)

解説 :
avg_cpu_percent_s 列、io_bytes_read_s 列および io_bytes_written 列は文字列型なので、平均値を求めるために数値型に変換する必要があります。また、時系列で表示するために TimeGenerated で並べ替えを行います。

Azure SQL Managed Instance: 平均 CPU 使用率、読み込みバイト数、書き込みバイト数の 5 分ごとの平均値を、時刻で昇順に並べ替えて折れ線グラフで表示してください

AzureDiagnostics
| where Category == "ResourceUsageStats" 
| sort by TimeGenerated asc 
| summarize avg(todouble(avg_cpu_percent_s)), avg(todouble(io_bytes_read_s)), avg(todouble(io_bytes_written_s)) by bin(TimeGenerated, 5m)
| render timechart

解説 :
render オペレーターを timechart キーワードとともに使用して折れ線グラフを表示します。

Azure Synapse Analytics Dedicated SQL Pool: 過去 30 日の期間内で、実行の時間の長いクエリ上位 20 件を表示してください (クエリ解析 ステップ 1)

AzureDiagnostics
| where Category == 'ExecRequests'
| where StatementType_s !in ('Batch','Execute')
| summarize Session_ID=any(SessionId_s),Request_ID=any(RequestId_s),Submit_Time=max(SubmitTime_t),Start_Time=max(StartTime_t),End_Time=max(EndTime_t),Command=any(Command_s),Status=min(Status_s), Statement_Type=any(StatementType_s),Resource_class=any(ResourceClass_s) by RequestId_s
| summarize ElapsedTime_min=anyif((End_Time - Start_Time)/1m,Start_Time > ago(30d)),Session_ID=any(Session_ID), Submit_Time=any(Submit_Time) ,Start_Time=any(Start_Time), End_Time=any(End_Time),Command=any(Command),Status=any(Status),Statement_Type=any(Statement_Type),Resource_class=any(Resource_class) by Request_ID
| order by ElapsedTime_min desc
| limit 20

解説 :
Dedicated SQL Poolの ExecRequests カテゴリでは、クエリの実行時間を確認することができます。クエリの実行時間/状態によっては、複数行に渡って一つのクエリの情報が表示されるため、summarize オペレータを使用して必要な情報だけを表示するようにします。処理時間はクエリ終了時間(End_Time)と開始時間(Start_Time)の差から計算しています。より長い期間の確認が必要な場合は ago オペレーターの引数の値を増やしてください (サンプルでは 30d (30日)としています)。また、表示件数を増やす場合は limit オペレーターの値を増やしてください。Request_ID 列の値はステップ 2で使用します。

Azure Synapse Analytics Dedicated SQL Pool: 分散クエリの各ステップで処理されたデータ件数を表示してください (クエリ解析 ステップ 2)

AzureDiagnostics
| where Category  == 'RequestSteps'
| where RequestId_s == 'QIDnnnnnn' //Put your QueryID here
| where Status_s != 'Running'
| summarize max(StartTime_t),max(EndTime_t),max(RequestId_s),max(OperationType_s),max(RowCount_d),max(Command_s),max(Status_s) by StepIndex_d
| order by StepIndex_d asc

解説 :
Dedicated SQL Poolの RequestSteps カテゴリでは、分散クエリの各ステップの情報を取得することができます。一番長く時間がかかっているステップを確認することで、対処方法を検討することができます。解析対象としたいクエリの Request_ID の値 (クエリ解析 ステップ 1にて取得)を、クエリ内で指定してください。

Azure Synapse Analytics Dedicated SQL Pool: 過去 30 日の期間内で、処理件数の多いクエリステップ上位 20 件を表示してください (クエリ解析 ステップ 3)

AzureDiagnostics
| where Category  == 'RequestSteps'
| extend elapsedTime = EndTime_t - StartTime_t
| extend elapsedTime_min = elapsedTime/1m
| where EndTime_t >= ago(30d)
| where StartTime_t >= ago(30d)
| order by RowCount_d desc
| project RequestId_s,OperationType_s, RowCount_d,elapsedTime_min , StartTime_t, EndTime_t, Status_s
| limit 20

解説 :
Dedicated SQL Poolの RequestSteps カテゴリでは、分散クエリの各ステップの情報を取得することができます。一番長く時間がかかっているクエリステップを確認することで、対処が必要なクエリの発見が可能です。より長い期間の確認が必要な場合は ago オペレーターの引数の値を増やしてください (サンプルでは 30d (30日)としています)。また、表示件数を増やす場合は limit オペレーターの値を増やしてください。Request_ID 列の値はステップ 4で使用します。

Azure Synapse Analytics Dedicated SQL Pool: 処理件数が多い分散クエリステップのクエリ全体の処理時間を表示してください (クエリ解析 ステップ 4)

AzureDiagnostics
| where Category  == 'RequestSteps'
| where RequestId_s == 'QIDnnnnn' //Insert your QID here
| where Status_s != 'Running'
| summarize max(StartTime_t),max(EndTime_t),max(RequestId_s),max(OperationType_s),max(RowCount_d),max(Command_s),max(Status_s) by StepIndex_d
| order by StepIndex_d asc

解説 :
Dedicated SQL Poolの ExecRequests カテゴリでは、クエリ処理状況を確認することができます。解析対象としたいクエリの Request_ID の値 (クエリ解析 ステップ 2にて取得)を、クエリ内で指定してください。

AVD

AVD の診断ログが格納されているテーブルを一覧で表示

search * 
| where TimeGenerated > ago(24h)
| distinct $table
| where $table startswith "WVD"

解説 :
AVD に関連する診断ログは用途毎に別々のテーブルに出力されます。ここでは AVD に関連するテーブルの一覧(ログが 24 時間以内に出力されたものに限る)を表示しています。 AVD の診断ログについては以下のドキュメントを参照ください。
https://docs.microsoft.com/ja-jp/azure/virtual-desktop/diagnostics-log-analytics

過去 24 時間のアクティブなセッション数の推移を 5 分刻みでグラフ化

WVDAgentHealthStatus
| where TimeGenerated > ago(24h)
| summarize TotalActiveSessions=sum(toint(ActiveSessions))/10 by bin(TimeGenerated, 5m)
| render timechart 

解説 :
AVD Agent は 30 秒おきにホスト上のアクティブなセッション数などのステータス情報を ‘WVDAgentHealthStatus’ テーブルに送信しています。 このクエリはアクティブなセッションの数を sum() 関数により集計してグラフ化しています。

過去 24 時間のインアクティブなセッション数の推移を 5 分刻みでグラフ化

WVDAgentHealthStatus
| where TimeGenerated > ago(24h)
| summarize TotalInactiveSessions=sum(toint(InactiveSessions))/10 by bin(TimeGenerated, 5m)
| render timechart 

解説 :
インアクティブ(ユーザーが x ボタンで閉じるなどしてアクティブではないがセッションが残っているもの)なセッション数を sum() 関数により集計してグラフ化しています。

セッションホスト毎のアクティブとインアクティブなセッション数を表示

WVDAgentHealthStatus
| where TimeGenerated > ago(10m)
| summarize arg_max(TimeGenerated,*) by SessionHostName
| project SessionHostName, ActiveSessions, InactiveSessions, TimeGenerated

解説 :
現時点(過去 10 分以内)で正常稼働しているセッションホストから、アクティブ/インアクティブなセッション数をホスト毎に表示

過去 24 時間以内に発生した AVD コントロールプレーンに起因する可能性のあるエラーを表示

WVDErrors 
| where TimeGenerated > ago(24h) 
| where ServiceError == "true" 

解説 :
ServiceError カラムが true となっているエラーのみをフィルター表示しています。これらのエラーはマイクロソフト サポートへの問い合わせが必要な問題が発生していることを示しています

過去 24 時間以内に発生したエラーの数を種類ごとに集計

WVDErrors 
| where TimeGenerated > ago(24h) 
| where ServiceError == "false" 
| summarize NumOfErrors=count() by ActivityType, Source, CodeSymbolic
| sort by Source

Description :

過去 24 時間以内に発生したエラーの数をユーザー毎に集計

WVDErrors 
| where TimeGenerated > ago(24h) 
| where ServiceError == "false" 
| summarize NumOfErrors=count() by UserName

Description :

過去 24 時間以内に <User UPN> に指定したユーザーで発生したエラーの一覧を表示

WVDErrors 
| where TimeGenerated > ago(24h) 
| where ServiceError == "false" 
| where UserName == "<User UPN>"

Description :

過去 24 時間におけるホストプール毎の接続元 IP アドレスを表示

WVDConnections
| where TimeGenerated > ago(24h)
| extend Multi=split(_ResourceId, "/")
| extend HostPool = tostring(Multi[8])
| summarize count() by ClientSideIPAddress, HostPool

Description :

過去 24 時間の <Host Pool> で指定したホストプールにおけるユーザー毎の接続時間を表示

WVDConnections  
| where TimeGenerated > ago(24h)  
| extend Multi=split(_ResourceId, "/")
| extend HostPool = tostring(Multi[8])
| where HostPool == "<Host Pool>"
| where State == "Connected"  
| project CorrelationId, UserName, StartTime=TimeGenerated
| join kind=inner(WVDConnections  
| where State == "Completed"  
| project EndTime = TimeGenerated, CorrelationId) on CorrelationId  
| project Duration = EndTime - StartTime, UserName
| summarize ConnectionTimeTotal = sum(Duration) by UserName

解説 :
‘WVDConnections’ テーブルにはユーザーによる接続開始や切断のログが記録されます。 このクエリではセッションホストに接続してから切断するまでの時間を集計して、ユーザー毎の接続時間の累計値を算出しています。 同じセッションにおける接続と切断のログは ‘CorrelationId’ が一致するため、join により接続と切断のログを一つのレコードに纏めています。

過去 1 週間の <Host Pool> で指定したホストプールにおけるユーザー毎の接続時間の推移を表示

WVDConnections
| where TimeGenerated > ago(7d)
| extend Multi=split(_ResourceId, "/")
| extend HostPool = tostring(Multi[8])
| where HostPool == "<Host Pool>"
| where State == "Connected"
| project CorrelationId, UserName, StartTime = TimeGenerated  
| join kind=inner(WVDConnections  
| where State == "Completed"  
| project EndTime = TimeGenerated, CorrelationId) on CorrelationId  
| project DurationInMinutes = (EndTime - StartTime)/1m, UserName, EndTime
| summarize ConnectionTimeInMinutes=sum(DurationInMinutes) by bin(EndTime, 24h), UserName
| render timechart

解説 :
上に書いたクエリーを拡張して、ユーザー毎の接続時間の日次推移をグラフ化しています。 datetime 型同士で引き算をすると timespan 型になりますが、この型のままではグラフ化できないため、11 行目で ‘1m’ で割ることで分単位のスカラー値に変換しています。

過去 24 時間におけるセッションホストとクライアント デバイス間の応答性(レイテンシ)をホストプール毎に表示

WVDConnectionNetworkData
| where TimeGenerated > ago(24h)
| extend Multi = split(_ResourceId, "/")
| extend HostPool = tostring(Multi[8])
| summarize avg(EstRoundTripTimeInMs) by bin(TimeGenerated, 10m), HostPool
| render timechart 

解説 :
‘WVDConnectionNetworkData’ テーブルにはセッションホスト接続時の応答性(レイテンシ)の情報が記録されます。 このクエリでは過去 24 時間におけるホストプール毎のレイテンシの推移を、10 分毎の平均値として表示しています。

過去 24 時間におけるセッションホストとクライアント デバイス間の応答性(レイテンシ)を <User UPN> で指定したユーザーについて表示

WVDConnectionNetworkData
| where TimeGenerated > ago(24h)
| join kind=inner (WVDConnections
| distinct CorrelationId, UserName) on CorrelationId
| where UserName == "<User UPN>"
| summarize avg(EstRoundTripTimeInMs) by bin(TimeGenerated, 10m)
| render timechart 

解説 :
このクエリでは過去 24 時間の特定のユーザーにおけるレイテンシの推移を、10 分毎の平均値として表示しています。

Application Insights

過去12時間のリクエストを集計してください

requests
| where timestamp > ago(12h) 

解説 :
requests テーブルは、Application Insights で収集したHTTPリクエストのテーブルです。

過去12時間の失敗したリクエストを集計してください

requests
| where timestamp > ago(12h) 
| where success == false

解説 :
requests テーブルの中には、sucess という boolean 型の行があります。 successfalse に設定することによって失敗したリクエストを集計することができます。

リクエストのパフォーマンスを平均、50、95、99パーセンタイルで集計してください

requests
| summarize RequestsCount=sum(itemCount), AverageDuration=avg(duration), percentiles(duration, 50, 95, 99) by operation_Name

解説 :
requests テーブルの中には、duration という 返答にかかった時間を表すデータがあります。 avg を使えば平均を、percentiles を使えばパーセンタイルでの集計が可能です。

リクエストからアクセス元の国トップ10を表示してください

requests
| summarize CountByCountry=count() by client_CountryOrRegion
| top 10 by CountByCountry
| render piechart

Description :
NA

ページビューのデータを表示してください

pageViews

解説 :
ページビューを集計するためには、pageViews のテーブルを使います。

ブラウザから参照されるページビューのデータを表示してください

pageViews
| where client_type == 'Browser'

解説 :
pageViews テーブルの中に client_type のデータがあります。where 句を使い client_type を Browser に絞ることでブラウザから参照されるデータを抽出できます。

ブラウザから参照されるページビューのデータを使い、処理にかかった時間を集計し、トップ10とその平均値を表示してください。

pageViews
| where notempty(duration) and client_Type == 'Browser'
| extend total_duration=duration*itemCount
| summarize avg_duration=(sum(total_duration)/sum(itemCount)) by operation_Name
| top 10 by avg_duration desc

解説 :
where 句で duration が空でないものでかつブラウザからのアクセスのものを区切り、extend 句で処理時間の合計値として total_duration を計算します。 sumamrize 句で アクセスディレクトリのデータである operation_Name 事に平均処理時間を出します。最後に top 句を使い平均処理時間のトップ10を出し desc で並べかえます。

Security

同一のユーザーに対する連続的なログオンの失敗を検出する

let threshold = 10;
let unit = 10m;
SigninLogs
| where ResultType !in ("0", "50125", "50140")
| summarize loginfailure = count() by UserPrincipalName, bin(TimeGenerated,unit)
| where loginfailure > threshold 

解説 :

このクエリはブルートフォース攻撃を検出するためのサンプルです。
SigninLogs テーブルから unit に設定した時間内に threshold で設定した回数を超えるサインインの失敗を検出します。
ResultType にサインインの結果が含まれ、0 はサインインが成功し問題がないことを表します。50125 と 50140 はそれぞれパスワードがリセットされた場合とサインインの記憶を設定した場合に記録されるイベントで、頻繁に発生する可能性のある失敗ではないイベントです。

SigninLogs は Azure AD に対するユーザーなどのサインインを記録します。このログは Azure AD の診断設定から有効化することができ、有効化には Azure AD Premium P1 または P2 のライセンスが必要です。 これらのコードは将来変更される可能性がありますが、の詳細は Azure AD 認証と承認のエラー コード に記載がある他、https://login.microsoftonline.com/error でコードに対応するメッセージを確認することができます。

異なるユーザーに対する連続的なログオンの失敗を検出する

let threshold = 20;
let unit = 10m;
SigninLogs
| where ResultType !in ("0", "50125", "50140")
| summarize loginfailure = dcount(UserPrincipalName) by  bin(TimeGenerated,unit)
| where loginfailure > threshold 

解説 :
このクエリはパスワード スプレー攻撃を検出するためのサンプルです。 ブルート フォースは特定のユーザーに複数のパスワードを試行する攻撃ですが、アカウントのロックなどで回避・検出が行われやすいため、パスワードスプレーではパスワードをよく使われやすいものに固定し、ユーザーを変えながらサインインを試行します。 このクエリでは unit で指定した時間内に記録されたユニークなユーザーによるサインインの失敗を数え、閾値を越えたものを検出します。

Application Gateway で過去に記録されたエラーの数が閾値より多いクライアントの IP アドレスを表示する

let threshold = 10;
let lookback = 10m;
AzureDiagnostics
| where TimeGenerated > ago(lookback)
| where Category == "ApplicationGatewayAccessLog"
| where httpStatus_d >= 300
| summarize ErrCount= count() by clientIP_s,userAgent_s
| where ErrCount > threshold

解説 :
ApplicationGateway は複数のログを生成します。一つはクライアントからアプリケーションのアクセスがあったことを記録する ApplicationGatewayAccessLog カテゴリで、IP アドレスやユーザーエージェント文字列、アクセス先のアプリケーションの URI とアクセス結果の HTTP ステータスなどが記録されています。このログは AzureDiagnostics の ApplicationGatewayAccessLog カテゴリに記録されます。
偵察のフェーズでは HTTP のステータスコードでエラーが記録されることが多いため、ステータスコードがエラー (300) 以上のレコードをフィルタします。予め設定された閾値を上回るエラーを記録した IP アドレスを検出することで偵察と思われるアクセスを発見します。 このクエリはバックエンドのサービスに障害が発生しているようなケースを誤検出する可能性があり、また拠点から複数のユーザーが1つのゲートウェイを経由してインターネットアクセスを行うようなケースでも、同一 IP アドレスからの発信元とみなされるためうまく動作しない可能性があります。緩和策として IP アドレスの他にも UserAgent_s でユーザーエージェント文字列によりアクセス元を区別するようにしていますが、十分ではありません。

Application Gateway で複数のアプリケーションに対してアクセスエラーとなっているクライアント IP アドレスを表示する

let threshold = 5;
let lookback = 10m;
AzureDiagnostics
| where TimeGenerated > ago(lookback)
| where Category == "ApplicationGatewayAccessLog"
| where httpStatus_d >= 300
| summarize UriCount = dcount(requestUri_s) by clientIP_s
| where UriCount > threshold

解説 :
偵察フェーズではパスの総当たりや脆弱性のあるアプリケーションを確認するために複数のアプリケーションのパスに対してアクセスを行うことがあります。この結果単位時間内に複数の requestUri_s に対してエラーが発生します。 dcount は指定された列に対してユニークな数をカウントする関数で、このクエリでは clientIP_s に対してユニークな requestUri_s の数、つまり特定の clientIP_s からアクセスが試行されたアプリケーションの数が集計されます。 レコード httpStatus_d がエラーになるものにフィルタされているため、ある IP からアクセスに失敗したアプリケーションの数を閾値と比較して攻撃を検出することができます。
実際の攻撃では発信元 IP が複数に分散している場合もあるので注意が必要です。

Application Gateway で複数回のアクセス エラーの後にアプリケーションのアクセスに成功したクライアントの IP アドレスを表示する

let lookback = 10m;
let threshold = 10;
let SuccessIPs = AzureDiagnostics
| where TimeGenerated > ago(lookback)
| where Category == "ApplicationGatewayAccessLog"
| where httpStatus_d between (200 .. 299) 
| summarize AccessTime = max(TimeGenerated) by clientIP_s;
AzureDiagnostics
| where TimeGenerated > ago(lookback)
| where httpStatus_d >= 300
| join kind=inner SuccessIPs on clientIP_s 
| where TimeGenerated < AccessTime
| summarize ErrBeforeSuccess = count() by clientIP_s
| where ErrBeforeSuccess > threshold

解説 :
偵察フェーズではエラーが発生することが多いですが、エラーが続いたあとに HTTP のアクセス成功コードがかえる場合、アプリケーションの不正なアクセスに成功している可能性があります。 このクエリでは ApplicationGatewayAccessLog から IP アドレスごとにアクセスに成功した最後の時刻のデータセットを作成し、元の ApplicationGatewayAccessLog からエラーだけを抽出したデータセットと join します。 アクセスに成功した時刻が AccessTime 列に含まれるため、TimeGenerated がこれより小さい(古い)レコードを数え、アクセス成功前に閾値を超える回数エラーが発生していたことを検出します。

Web Application Firewall で検出した攻撃を表示する

AzureDiagnostics
| where Category== "ApplicationGatewayFirewallLog"
| project TimeGenerated,clientIp_s,hostname_s,requestUri_s,ruleSetVersion_s,ruleId_s,Message,details_message_s,details_data_s

解説 :
WAF が有効な場合 ApplicationGatewayFirewallLog カテゴリに WAF のルールによる検出結果を記録することができます。このクエリでは WAF のルール検出の際によく使う項目を表示しています。

AzureActivity で過去1か月に発生していなかった新しい管理操作を検出する

let past1month = AzureActivity
| where TimeGenerated between (ago(30d) .. startofday(now()))
| where CategoryValue == "Administrative"
| distinct  OperationNameValue;
AzureActivity
| where TimeGenerated >  startofday(now())
| where CategoryValue == "Administrative"
| where OperationNameValue !in (past1month)
| project TimeGenerated, OperationNameValue, OperationName, ResourceGroup, ResourceProviderValue, Caller

解説 :
Administrative テーブルは Administrative に管理操作を記録します。システムの変更が発生していないときには管理操作は一定のものが記録されますが、急に記録されていない新しい操作が記録された場合には、新しく変更作業が発生したか、セキュリティ侵害の兆候の可能性があります。

他のコンピューターでは起動していないプロセスを持つコンピューターを表示する

VMProcess 
| summarize UniqueProcesses = dcount(Computer) by ExecutableName
| where UniqueProcesses == 1
| join (
VMProcess
| project Computer, ExecutableName, ExecutablePath
) on ExecutableName
| distinct ExecutableName, Computer, ExecutablePath
| order by Computer, ExecutableName

解説 :
サーバー ワークロードでは同じ役割のコンピューターでは同じプロセスが起動することになるため、ユニークなプロセスが起動している場合には何か特殊な操作を行っている可能性があります。 これは何らかの変更作業に伴うものである可能性もありますし、セキュリティ侵害を示唆するものである可能性もあります。

Secuire Score の1週間の変化を確認する

SecureScores 
| where TimeGenerated > ago(7d)
| extend SecureScorePercentage = PercentageScore*100
| summarize AverageScore = avg(SecureScorePercentage) by bin(TimeGenerated, 1d)

解説 :
セキュリティ スコアは無償で利用することができる Microsoft Defender for Cloud のポスチャ マネジメントの機能です。 Azure Policy を使用して、マイクロソフトの推奨設定が行われているかをチェックします。 ワークロードの追加や更新プログラムのリリースにより日々変化するため、90% 以上を維持するようにメンテナンスしましょう。

ApplicationGateway のバックエンド プールのリソースからのデータ送信を検出する

let backend = AzureDiagnostics
| where Category == "ApplicationGatewayAccessLog"
| where serverRouted_s <> ""
| distinct serverRouted_s;
AzureDiagnostics
| where Category == "AzureFirewallNetworkRule" or Category == "AzureFirewallApplicationRule"
| parse msg_s with *  " request from " SourceIP ":" SourcePort " to " Dest ":" DestPort "." *
| project TimeGenerated,SourceIP,SourcePort,Dest,DestPort
| where SourceIP in (backend)

解説 :
アプリケーションゲートウェイのバックエンドに対する通信は Web Applicaiton Firewall によって件さされますが、検出されなかった攻撃のペイロードはバックエンドが受け取る可能性があります。 ペイロードは様々な動作をすることが考えられますが、インターネット上の C&C サーバーと通信を行うためにアウトバウンドのデータ通信を発生させるものがあります。
このクエリでは Azure Firewall が使われていて、全てのインターネット向け通信は Azure Firewall にルーティングされるように構成されている環境を想定しています。 pplicationGateway のバックエンド サーバーの IP アドレスは serverRouted_s に記録されるため、serverRouted_s のリストを作成します。 Azure Firewall では IP ベースのネットワークルール、URL ベースのアプリケーション ルールともに送信元アドレスが記録されるため、送信元アドレスが予め作成したバックエンドのリストと一致するものがデータ送信を行おうとしたバックエンドです。

コンピューターに対して対話的ログインを行ったユーザーを表示する

SecurityEvent 
| where EventID == 4624 and (LogonType == 2 or LogonType == 10) and AccountType == "User"
| summarize count() by Computer, Account

解説 :
一般的に運用中のサーバーには、変更管理の期間を除き対話的ログオンが発生することはありません。 SecurityEvent は Windows のセキュリティ ログが記録されますが対話的ログインのイベントを検出することで簡単なセキュリティ監視を実装することができます。 SecurityEvent を取得するためには Defender for Cloud の有償 SKU が必要です。

脅威インテリジェンスに含まれる IP アドレスへの送信アクセスを検出する

let maliciousIPs = 
ThreatIntelligenceIndicator
| where NetworkIP <> "" and Active == true and ExpirationDateTime > now()
| distinct NetworkIP;
AzureDiagnostics
| where Category == "AzureFirewallNetworkRule"
| parse msg_s with *  " request from " SourceIP ":" SourcePort " to " DestIP ":" DestPort "." *
| where DestIP  in (maliciousIPs)

解説 :
このクエリを実行するためには Azure Sentinel が必要です。Azure Sentinel では様々なデータソースからログを連携するためのデータコネクタがあり、脅威インテリジェンス(信頼できないIP、URL、ファイルハッシュなど)を取り込むことができます。 この例では脅威インテリジェンスから得られたアクティブな信頼できない IP アドレスに対して Azure Firewall のログを照会し、あやしい外部アクセスがあるかどうかを確認しています。

Resource Graph

View subscriptions with no Key Vaults

ResourceContainers 
| where type=='microsoft.resources/subscriptions'
| parse id with "/subscriptions/" SubscriptionID
| project subscriptionId, SubscriptionName = name
| join kind=leftouter (
Resources
| where type == 'microsoft.keyvault/vaults'
| project id, name, subscriptionId
) on subscriptionId
| join kind= leftouter (
Resources
| where type == 'microsoft.keyvault/vaults'
| summarize ResourceCount = count() by subscriptionId
) on subscriptionId
| extend RCount = iff(isnull(ResourceCount), 0, ResourceCount)
| project-away ResourceCount, subscriptionId1, subscriptionId2
| extend HealthStatus = iff(RCount == 1, "Not Compliant", "Compliant")

Description :
You can use this query in Azure Resource Graph to identify any subscriptions that do not contain KeyVaults.

View status of Azure Monitor Agents for all virtual machines

resources
| where type == "microsoft.compute/virtualmachines"
| project ComputerName = name, subscriptionId
| join kind=leftouter  (
resources
| where type == "microsoft.compute/virtualmachines/extensions"
| where name in ("AzureMonitorLinuxAgent", "AzureMonitorWindowsAgent")
| parse id with * "/virtualMachines/" ComputerName "/extensions/" *
| extend AMAStatus = properties.provisioningState,
    WorkspaceID = tostring(properties.settings.workspaceId)
| project AMA = name, ComputerName, AMAStatus
) on ComputerName
| join kind=leftouter  (
resources
| where type == "microsoft.compute/virtualmachines/extensions"
| where name in ("MicrosoftMonitoringAgent", "OmsAgentForLinux")
| parse id with * "/virtualMachines/" ComputerName "/extensions/" *
| extend MMAStatus = properties.provisioningState,
    WorkspaceID = tostring(properties.settings.workspaceId)
| project MMA = name, ComputerName, MMAStatus, WorkspaceID
) on ComputerName
| join kind=leftouter  (
resources
| where type == "microsoft.operationalinsights/workspaces"
| extend WorkspaceID = tostring(properties.customerId)
| project WorkspaceName = name, WorkspaceID
) on WorkspaceID
| project-away WorkspaceID1, ComputerName1, ComputerName2
| extend ["Both Agents"] = iff(isnotempty(AMA) and isnotempty(MMA), "True", "False")

Description :
You can use this query in Azure Resource Graph to identify which virtual machines have both the Azure Monitor Agent and the Microsoft Monitoring Agent deployed, and which workspace the MMA reports to.

View Orphan Managed disks

Resources
| where type =~ 'microsoft.compute/Disks'
| where managedBy == ""
| project name, id

Description :
You can use this query in Azure Resource Graph to identify any orphaned disks.

View Orphaned NICs

resources
| where type == "microsoft.network/networkinterfaces"
| where isnull (properties.virtualMachine) and isnull (properties.privateEndpoint)

Description :
This query can be used in Azure Resource Graph to identify any detached NIC

View Orphaned NSGs

resources
| where type =~ 'microsoft.network/networksecuritygroups' and isnull(properties.networkInterfaces) and isnull(properties.subnets)
| project name, resourceGroup

Description :
This query can be used in Azure Resource Graph to identify any used Network Security Group

View Orphaned Route Tables

resources
| where type == "microsoft.network/routetables" and isnull(properties.subnets)

Description :
This query can be used in Azure Resource Graph to identify any unused route table

Show Storage accounts with public endpoints

resources
| where type == "microsoft.storage/storageaccounts" and properties.allowBlobPublicAccess == 'true'

Description :
This query can be used in Azure Resource Graph to identify any storage account with public endpoints

Show Storage accounts with non-encrypted transfers enabled

  resources
  | where type == "microsoft.storage/storageaccounts" and properties.allowBlobPublicAccess == 'true'        

Description :
This query can be used in Azure Resource Graph to identify any storage accounts without an https requirement

VNets: Peering Configuration

resources
| where ['type'] == 'microsoft.network/virtualnetworks'
| mv-expand peerings=properties.virtualNetworkPeerings
| project VNetName=name, peeringName=peerings.name, peeringTarget=split(peerings.properties.remoteVirtualNetwork.id,"/",8)[0], peeringState=peerings.properties.peeringState,useRemoteGateways=peerings.properties.useRemoteGateways,
allowGatewayTransit=peerings.properties.allowGatewayTransit

Description :
Use this query to review the peering configuration of all Virtual Networks in scope

Subnet details (NSG/UDR)

resources
| where ['type'] == 'microsoft.network/virtualnetworks'
//| project dynProperties=todynamic(properties)
| mv-expand subnetNames=properties.subnets
| project VNetName=name, Subnet=subnetNames.name,AddressPrefix=subnetNames.properties.addressPrefix,
RouteTable=split(subnetNames.properties.routeTable.id,"/",8)[0],
NSG=split(subnetNames.properties.networkSecurityGroup.id,"/",8)[0],type="microsoft.network/subnets"

Description :
This query shows details about the subnets, Addresses, NSGs, UDRs.

Display count of VMs by Azure location

Resources | where type == "microsoft.compute/virtualmachines"
| summarize Total=count() by Region=location

Description :
Use this query to display a count of VMs by Azure location

Display a count of VMs by OS Platform

resources
| where ['type'] == 'microsoft.compute/virtualmachines'
| project osType=properties.storageProfile.osDisk.osType
| summarize count() by tostring(osType)

Description :
Use this query to display a count of VMs by OS Platform

<ASG> で指定したアプリケーション セキュリティ グループに紐づくネットワーク インターフェースを表示する

Resources 
| where type == "microsoft.network/networkinterfaces"
| where isnotnull (properties.ipConfigurations[0].properties.applicationSecurityGroups)
| project subscriptionId, resourceGroup, name, ASGs=properties.ipConfigurations[0].properties.applicationSecurityGroups
| mv-expand ASGs
| project subscriptionId, resourceGroup, name, ASG=ASGs.id
| where ASG endswith "<ASG>"

Description :
NA

最終更新 June 13, 2022: Copy from HackOn repository (2a0a62f)