mcpField
A
azure-kusto-graph

azure-kusto-graph

github.com/microsoftデータ・データベース1.6k751 回インストール今日更新
リポジトリ

azure-kusto-graph について

KQL のグラフ演算子で表形式データからグラフを構築し、クエリするスキルです。関係、経路、連結成分を分析する用途に向け、テンポラリーグラフとスナップショット付きの永続グラフモデルを扱います。

主な機能

  1. 01make-graph によるグラフ構築
  2. 02パターンと最短経路の検索
  3. 03連結成分の特定
  4. 04グラフのノードとエッジのテーブル出力
  5. 05永続モデルとスナップショットへの対応

活用シーン

  1. 01セキュリティイベントデータの関係の可視化
  2. 02エンティティ間の経路検索
  3. 03接続された IP やドメイン群の特定
  4. 04再利用可能なグラフモデルとスナップショットの保存

MCP 対応

KQL のグラフクエリを説明していますが、MCP 連携についての記載はありません。

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/microsoft/azure-skills --skill azure-kusto-graph

インストール後、Claude Code で/azure-kusto-graphを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
EXAMPLES.md
KUSTO_EXPLORER_LAUNCH.md
SCENARIOS.md

詳細

Kusto のグラフセマンティクス

KQL のグラフ演算子を使って、表形式のデータから一時的および永続的なグラフを構築します。このスキルは、自然言語をエッジ優先のグラフ構築パターンとグラフクエリ演算子に変換します。

使用するタイミング

次の場合にこのスキルを使用します。

  • 表形式のデータからグラフを構築したい(make-graph)
  • データ内のパターン、パス、関係を見つけたい
  • graph-match、graph-shortest-paths、graph-to-table、graph-mark-components について言及している
  • 永続的なグラフモデルまたはスナップショットを作成したい
  • 「グラフを構築して」「最短パスを見つけて」「連結成分を見つけて」「関係を表示して」と依頼している
  • 一時的なグラフと永続的なグラフの違いについて尋ねている

自然言語から KQL への変換ツールではありません。 入力には通常、グラフに変換したい結果を返す実行可能な KQL クエリと、目的とするグラフ構造の自然言語による説明が必要です。基本的な自然言語によるソース指定は、明らかな列を持つ既知のテーブルに直接対応する場合にのみサポートされます。一般的な自然言語から KQL への変換には、専用のクエリ生成スキル(別途利用可能)を使用してください。

関連スキル:

  • azure-kusto-irql -- グラフの表形式入力を生成する、組み合わせ可能なセキュリティクエリプリミティブ
  • azure-kusto-irql-graph -- Kusto Explorer で型情報やアイコンを豊富に備えたグラフを作成する、IRQL の Lift_To_Graph JSON マッピングシステム

エッジ優先のアプローチ

Kusto でグラフを構築する基本パターン:

1. Define your EDGES       -> src --> dest, with relationship type/properties
2. Define your NODE LOOKUPS -> display names, types, properties for each node ID
3. Union edge types         -> if you have multiple relationship types
4. Union node lookups       -> if you have multiple node types
5. Call make-graph          -> edges | make-graph Source --> Target with nodes on nodeId

これが make-graph の考え方です。エッジは、関心のある関係を表します。ノードは、ID に実体を与えるルックアップテーブルであり、表示名、種類、プロパティなどを含みます。

グラフ演算子のリファレンス

make-graph -- テーブルからグラフを構築する
Edges | make-graph SourceId --> TargetId with Nodes on NodeId
  • Edges:各行がエッジである表形式のソース
  • SourceId --> TargetId:ソースノードとターゲットノードの ID を含む列
  • with Nodes on NodeId:ID で結合する任意のノードプロパティテーブル
  • 複数のノードテーブルをサポートします:with Nodes1 on Id1, Nodes2 on Id2
  • エッジに存在するもののノードテーブルにないノードは、空のプロパティになります
graph-match -- パターンを検索する
G | graph-match (a)-[e]->(b) where <constraints> project <output>

パターン表記:

要素名前付き無名
ノード(n)()
エッジ(左から右)-[e]->-->
エッジ(右から左)<-[e]-<--
任意の方向-[e]---
可変長-[e*1..5]->-[*1..5]->

複数ホップのパターン:(a)-[e1]->(b)-[e2]->(c) スター型パターン:(a)--(center)--(b), (c)--(center)--(d) サイクルの制御:cycles = all | none | unique_edges(既定値:unique_edges)

graph-shortest-paths -- 最短パスを検索する
G | graph-shortest-paths (start)-[e*1..20]->(end)
      where start.name == "Alice" and end.name == "Server01"
      project Path = e, Length = array_length(e)
  • 可変長エッジが少なくとも 1 つ必要です
  • output = any(既定値。ペアごとに 1 つのパス)または output = all(同じ長さの最短パスをすべて取得)
  • 可変長エッジのプロパティは動的配列として返されます
graph-to-table -- グラフをテーブルにエクスポートする
G | graph-to-table nodes                                     // export nodes
G | graph-to-table edges                                     // export edges
G | graph-to-table nodes as N, edges as E                    // export both
G | graph-to-table nodes with_node_id=Id                     // include node hash ID
G | graph-to-table edges with_source_id=Src with_target_id=Tgt  // include edge endpoint IDs
graph-mark-components -- 連結成分を検索する
G | graph-mark-components with_component_id=ComponentId
  | graph-to-table nodes
  | summarize Members = make_list(name) by ComponentId

各ノードに ComponentId を割り当てます。同じ連結成分に属するノードには同じ ID が割り当てられます。

graph() 関数 -- 永続的なグラフをクエリする
graph("MyGraphModel")                              // latest snapshot
graph("MyGraphModel", "Snapshot_2025_01")           // specific snapshot
graph("MyGraphModel", true)                         // transient from model definition

一時的なグラフ

クエリ実行中に動的に作成されます。セットアップは不要です。アドホックな分析、調査、プロトタイピングに最適です。

テンプレート:基本的な 2 エンティティのグラフ
// 1. Define edges
let edges = <SourceTable>
    | summarize <aggregations> by SourceCol, TargetCol;
// 2. Define node lookups
let source_nodes = edges
    | distinct SourceCol
    | project nodeId = SourceCol, label = SourceCol, nodeType = "<SourceType>";
let target_nodes = edges
    | distinct TargetCol
    | project nodeId = TargetCol, label = TargetCol, nodeType = "<TargetType>";
let all_nodes = union source_nodes, target_nodes;
// 3. Build and query the graph
edges
| make-graph SourceCol --> TargetCol with all_nodes on nodeId
| graph-match (s)-[e]->(t)
    where <constraints>
    project Source = s.label, Target = t.label, <edge properties>
テンプレート:複数の関係を持つグラフ
// Multiple edge types -> union them with a common schema
let auth_edges = AuthEvents
    | project Source = username, Target = hostname, edgeType = "authenticates", ts = timestamp;
let net_edges = NetworkEvents
    | project Source = src_ip, Target = url, edgeType = "connects", ts = timestamp;
let all_edges = union auth_edges, net_edges;
// Node lookups from all sources
let user_nodes = Employees | project nodeId = username, label = name, nodeType = "User";
let host_nodes = AuthEvents | distinct hostname | project nodeId = hostname, label = hostname, nodeType = "Host";
let all_nodes = union user_nodes, host_nodes;
all_edges
| make-graph Source --> Target with all_nodes on nodeId

永続的なグラフ

大規模で再利用可能なグラフ向けです。データベースのメタデータに保存されます。履歴比較用のスナップショットをサポートします。

安全性: グラフモデルやスナップショットの作成または変更は、データベースを変更します。.create-or-alter graph_model または .make graph_snapshot を実行する前に、必ず正確なコマンドを表示し、ユーザーに確認してください。

ステップ 1:グラフモデルを作成する
.create-or-alter graph_model SecurityGraph
{
  "Schema": {
    "Nodes": {
      "User": {"name": "string", "role": "string"},
      "Host": {"hostname": "string"},
      "IP":   {"ip": "string"}
    },
    "Edges": {
      "AuthenticatesTo": {"timestamp": "datetime", "result": "string"},
      "ConnectsFrom":    {"timestamp": "datetime"}
    }
  },
  "Definition": {
    "Steps": [
      {
        "Kind": "AddNodes",
        "Query": "Employees | project name, role",
        "NodeIdColumn": "name",
        "Labels": ["User"]
      },
      {
        "Kind": "AddNodes",
        "Query": "AuthenticationEvents | distinct hostname | project hostname",
        "NodeIdColumn": "hostname",
        "Labels": ["Host"]
      },
      {
        "Kind": "AddEdges",
        "Query": "AuthenticationEvents | project username, hostname, timestamp, result",
        "SourceColumn": "username",
        "TargetColumn": "hostname",
        "Labels": ["AuthenticatesTo"]
      }
    ]
  }
}
ステップ 2:スナップショットを作成する
.make graph_snapshot SecurityGraph Snapshot_2025_07
ステップ 3:スナップショットをクエリする
graph("SecurityGraph")
| graph-match (user)-[auth]->(host)
    where user.role == "Admin" and auth.result == "Failed Login"
    project User = user.name, Host = host.hostname, Time = auth.timestamp
管理コマンド

安全性: 以下の制御コマンドはすべて、データベースオブジェクトを変更または削除します。.drop、.create-or-alter graph_model、.make graph_snapshot を自動的に実行してはいけません。実行前に、正確なコマンド、クラスター、データベース、対象オブジェクトを必ず提示し、ユーザーから明示的な確認を得てください。

.show graph_models                        // list all models
.show graph_model SecurityGraph           // show model details
.show graph_snapshots SecurityGraph       // list snapshots
.drop graph_snapshot SecurityGraph Snapshot_2025_07  // delete a snapshot (CONFIRM FIRST)
.drop graph_model SecurityGraph           // delete model and all snapshots (CONFIRM FIRST)

一時的なグラフと永続的なグラフ:どちらを使うか

要素一時的(make-graph)永続的(graph())
セットアップ不要 — クエリ内に直接記述モデルとスナップショットを作成
存続期間クエリの実行中のみデータベースのメタデータに保存
データの鮮度常に最新作成時点のスナップショット
規模クエリのメモリに制限されるエンタープライズ規模
再利用クエリごとに再構築ユーザーやクエリ間で共有
最適な用途アドホックな調査、プロトタイピング本番環境のワークフロー、ダッシュボード

セキュリティと脅威ハンティングの例

認証グラフ:誰が、どこから、何にログインしたか
let auth_edges = AuthenticationEvents
    | summarize
        logins = count(),
        fails = countif(result == "Failed Login")
      by src_ip, username, hostname;
let ip_nodes = auth_edges | distinct src_ip
    | project nodeId = src_ip, label = src_ip, nodeType = "IP";
let user_nodes = auth_edges | distinct username
    | project nodeId = username, label = username, nodeType = "User";
let host_nodes = auth_edges | distinct hostname
    | project nodeId = hostname, label = hostname, nodeType = "Host";
let all_nodes = union ip_nodes, user_nodes, host_nodes;
// IP -> User edges
let ip_user = auth_edges
    | project Source = src_ip, Target = username, logins, fails;
// User -> Host edges
let user_host = auth_edges
    | project Source = username, Target = hostname, logins, fails;
union ip_user, user_host
| make-graph Source --> Target with all_nodes on nodeId
| graph-match (ip)-[e1]->(user)-[e2]->(host)
    where e2.fails > 20
    project
        IP = ip.label,
        User = user.label,
        Host = host.label,
        Failures = e2.fails
| order by Failures desc
ラテラルムーブメントの検出:侵害されたホストを共有するユーザー
// Pattern: (user1)-[auth1]->(host)<-[auth2]-(user2)
// Two users both failing on the same host = possible credential spray
let edges = AuthenticationEvents
    | summarize fails = countif(result == "Failed Login"), logins = count()
      by username, hostname;
let nodes = union
    (edges | distinct username | project nodeId = username, nodeType = "User"),
    (edges | distinct hostname | project nodeId = hostname, nodeType = "Host");
edges
| make-graph username --> hostname with nodes on nodeId
| graph-match (u1)-[e1]->(h)<-[e2]-(u2)
    where u1.nodeId != u2.nodeId and e1.fails > 10 and e2.fails > 10
    project
        User1 = u1.nodeId, User2 = u2.nodeId,
        SharedHost = h.nodeId,
        User1Fails = e1.fails, User2Fails = e2.fails
| distinct User1, SharedHost, User2, User1Fails, User2Fails
| order by User1Fails + User2Fails desc
最短攻撃経路
let edges = SecurityEvents
    | project Source = source_entity, Target = target_entity, action, timestamp;
let nodes = union
    (edges | distinct Source | project nodeId = Source),
    (edges | distinct Target | project nodeId = Target);
edges
| make-graph Source --> Target with nodes on nodeId
| graph-shortest-paths (start)-[e*1..10]->(end)
    where start.nodeId == "ExternalIP_1.2.3.4" and end.nodeId == "DatabaseServer"
    project
        PathLength = array_length(e),
        Actions = e.action,
        Hops = e.Target
連結成分:孤立したクラスターを見つける
let edges = NetworkFlows
    | project Source = src_ip, Target = dst_ip;
let nodes = union
    (edges | distinct Source | project nodeId = Source),
    (edges | distinct Target | project nodeId = Target);
edges
| make-graph Source --> Target with nodes on nodeId
| graph-mark-components with_component_id = ComponentId
| graph-to-table nodes
| summarize Members = make_list(nodeId), Size = count() by ComponentId
| order by Size desc
Kusto Explorer で可視化する

クエリを make-graph で終了し(graph-match にはパイプしません)、Kusto Explorer の対話型グラフ可視化ウィンドウを開きます。

edges
| make-graph Source --> Target with all_nodes on nodeId
// <- stop here. Kusto Explorer renders the graph visually.

ダッシュボードやエクスポート用にテーブルへ戻すには、graph-match | project または graph-to-table にパイプします。

IRQL との併用

セキュリティデータを扱う場合は、データソースとして azure-kusto-irql スキルの IRQL セレクター(Get_*)の使用を検討してください。IRQL を使うと、生のテーブル名や列のマッピングを覚えなくても、統一されたスキーマを利用できます。アイコンやノードの折りたたみによる豊富な可視化には、azure-kusto-irql-graph スキルの Lift_To_Graph がより迅速な方法です。

アプローチ最適な用途
生の make-graph(このスキル)完全な制御、永続モデル、最短経路、連結成分、カスタムスキーマ
Lift_To_Graph(azure-kusto-irql-graph)Kusto Explorer でのアイコン付きの迅速な可視化、ノードの折りたたみ
IRQL Get_* -> make-graphIRQL の統一スキーマを入力として使い、その後、生のグラフ演算子で分析
IRQL Get_* -> Lift_To_Graph -> Graph_Render_View質問からビジュアルグラフを得るまでの最速の方法

注: Lift_To_Graph、Graph_Render_View、Graph_Fold_By_Property は組み込み演算子ではなく、保存関数です。kc7001 のサンプルクラスターには事前にデプロイされていますが、ほかのクラスターではデプロイが必要な場合があります。関数の定義とデプロイ手順については、azure-kusto-irql-graph/references/DEPLOY_IRQL_FUNCTIONS.md を参照してください。

例:IRQL セレクター -> make-graph -> 最短経路

データの取得は IRQL が担当し、グラフ分析は make-graph が担当します。認証イベントを経由して、外部 IP からメールサーバーまでの最短経路を見つけます。

// IRQL provides unified columns (ClientIp, Hostname, Username, Result)
let auth = Get_Event_Authentication_All
    | where Result == "Failed Login";
let edges = auth
    | summarize Failures = count() by ClientIp, Hostname;
let nodes = union
    (edges | distinct ClientIp | project nodeId = ClientIp, nodeType = "IP"),
    (edges | distinct Hostname | project nodeId = Hostname, nodeType = "Host");
edges
| make-graph ClientIp --> Hostname with nodes on nodeId
| graph-shortest-paths (src)-[e*1..5]->(dest)
    where src.nodeType == "IP" and dest.nodeId == "MAIL-SERVER01"
    project
        SourceIP = src.nodeId,
        PathLength = array_length(e),
        Hops = e.Hostname
例:IRQL セレクター -> make-graph -> 連結成分

相互に接続された IP とドメインのクラスター(潜在的な C2 インフラストラクチャ)を見つけます。

let dns = Get_Dns_All;
let edges = dns | project Source = ClientIp, Target = Domain;
let nodes = union
    (edges | distinct Source | project nodeId = Source, nodeType = "IP"),
    (edges | distinct Target | project nodeId = Target, nodeType = "Domain");
edges
| make-graph Source --> Target with nodes on nodeId
| graph-mark-components with_component_id = ComponentId
| graph-to-table nodes
| summarize
    IPs = make_set_if(nodeId, nodeType == "IP"),
    Domains = make_set_if(nodeId, nodeType == "Domain"),
    Size = count()
  by ComponentId
| where Size > 3
| order by Size desc
例:IRQL と make-graph の統合

IRQL セレクターと make-graph を組み合わせた複数ソースの調査グラフ、および Lift_To_Graph を使ったビジュアルグラフの例については、references/EXAMPLES.md を参照してください。

実践的な使用シナリオ

以下を含む詳しい実例については、references/SCENARIOS.md を参照してください。

  • 到達可能性分析(重要な資産への最短経路)
  • ネットワークセグメンテーションの検証(連結成分)
  • 侵害されたアカウントの影響範囲(可変長パスの照合)
  • SOC チーム向けの永続グラフモデル(graph_model + スナップショット)

使用する MCP ツール

ツール用途
kusto_querymake-graph、graph-match、管理コマンドを含む KQL クエリを実行
kusto_table_schema_getエッジ/ノードのプロジェクションを作成する前にテーブル列を確認
kusto_cluster_list利用可能な ADX クラスターを一覧表示
kusto_database_listクラスター内のデータベースを一覧表示

Kusto Explorer でのクエリの起動(Windows のみ)

任意の便利機能です。 デフォルトのワークフローでは、KQL をチャットに出力し、ユーザーが手動で Kusto Explorer または VS Code Kusto 拡張機能にコピーします。自動起動はオプトインの場合のみです。

デフォルト:チャットに KQL を出力

Step 1(接続)と Step 2(クエリ)を明確に示し、完全な KQL を必ず出力します。

// Step 1: Connect to your cluster (skip if already connected)
// Example: uncomment to connect to the KC7 training cluster
// #connect cluster('kc7001.eastus.kusto.windows.net').database('ValdyTimes')
// Or replace with your own cluster:
// #connect cluster('<YOUR_CLUSTER>').database('<YOUR_DATABASE>')

// Step 2: Run the query below
<KQL_QUERY ending at make-graph>

続けてその直下に、| graph-to-table nodes as N, edges as E を追加した ADX Web Explorer バージョンを出力します。ADX Web Explorer では make-graph を直接レンダリングできないためです。

// ADX Web Explorer version (tabular output):
<SAME_QUERY>
| graph-to-table nodes as N, edges as E

これにより、ユーザーが何も変更しなくても、Kusto Explorer(グラフの可視化)と ADX Web Explorer(表形式の結果)の両方で出力を利用できます。

任意:保存して起動

ユーザーから Kusto Explorer でクエリを保存または開くよう求められた場合は、references/KUSTO_EXPLORER_LAUNCH.md の手順に従います。主なルールは次のとおりです。

  • ファイルへの書き込みや実行ファイルの起動前に、必ず ask_user で確認します
  • 開く前にユーザーが確認できるよう、必ずファイルの内容をチャットに表示します
  • シェルのインターポレーションやヒア文字列は使用せず、Set-Content/Add-Content でファイルを書き込みます
  • クエリをブラウザーの URL にエンコードしません
  • macOS/Linux では、.kql ファイルを保存し、VS Code Kusto 拡張機能または ADX Web Explorer を提案します

make-graph の可視化(グラフウィンドウ)では、クエリは make-graph で終わる必要があります。graph-match にはパイプしないでください。Kusto Explorer がグラフの可視化ウィンドウを開くのは、出力がテーブルではなくグラフオブジェクトの場合のみです。

コメント