Note
この機能は現在プレビューの段階です。 「Microsoft Azure プレビューの追加使用条件」を参照してください。
Microsoft Sentinel カスタム グラフ ソリューションは、データ レイク テーブルからのノードとエッジMicrosoft Sentinel定義し、グラフを構築し、ユーザーが Graph クエリ言語 (GQL) でそのグラフに対してクエリを実行できるようにするノートブックベースのソリューションです。
このガイドを使用して、カスタム グラフ ソリューションを構築、テスト、具体化、パッケージ化し、Microsoft Partner Centerの SaaS オファーとして発行し、顧客が Microsoft Security Store で見つけることができるようにします。
前提条件
カスタム グラフ ソリューションを作成するには、データ レイクにオンボードする必要があります。 詳細については、「Microsoft Sentinel Data Lake にオンボードする方法」を参照してください。
Permissions
次の表に、このガイドの各操作またはスコープに必要なアクセス許可を示します。
| Operation | 必要な役割 |
|---|---|
| Sentinel データ レイクへのオンボーディング | Microsoft Entra ID - セキュリティ管理者またはグローバル管理者 |
| Sentinel ワークスペースをDefenderポータルにオンボードする | サブスクリプションの所有者、またはサブスクリプション スコープのユーザー アクセス管理者と、サブスクリプションまたはリソース グループ スコープの Microsoft Sentinel 共同作成者 |
| Sentinel ワークスペースをデータ レイクにオンボードする | サブスクリプションまたはリソース グループ スコープでのサブスクリプション所有者または Microsoft Sentinel 共同作成者 |
| ノートブック グラフのモデル化と構築 | Microsoft Sentinel のデータ収集に対する data (manage) 権限を持つ、カスタムの Microsoft Defender XDR の統合 RBAC ロール |
| グラフを永続化 (具体化) する | セキュリティ オペレーター、セキュリティ管理者、またはグローバル管理者 |
| 永続化されたグラフのクエリを実行する | Microsoft Sentinel のデータ収集に対する security data basics (read) 権限を持つ、カスタムの Microsoft Defender XDR の統合 RBAC ロール |
| カスタム グラフ ソリューションの発行 | Microsoft Partner Center の Marketplace Publisher アカウント |
ツール
カスタム グラフ ソリューションをビルドして発行するには、次のツールが必要です。
- Visual Studio Code。
- Microsoft Sentinel の Visual Studio Code 用拡張機能 詳細については、「Visual Studio Code のインストール」および「Microsoft Sentinel extension」をご覧ください。
- Visual Studio Code の Jupyter 拡張機能。
- GitHub Copilot拡張機能とGitHub Copilotビジネスプランまたはエンタープライズプラン。
- ローカル マシンに Python 3.10 以降。
ヒント
テナントが KQL クエリ、ノートブック、または Security Copilot エージェント向けに既にオンボード済みである場合は、既存のデータ レイクを再利用してください。 テナントごとに 1 つのデータ レイクがあります。 Defender ポータルで、System>Settings>> に移動します。 状態が [プロビジョニング済み] の場合は、タスク 1 をスキップします。
成果物を使用して
次のワークフローでは、カスタム グラフ ソリューションをビルドして発行する手順の概要を示します。
- グラフがモデル化するテーブルにサンプル データを取り込みます。
- 単純なノートブックを使用して接続を検証します。
- カスタム グラフを作成します。
- グラフ ノートブックをテストします。
- グラフを具体化するようにグラフ ジョブをスケジュールします。
- Graph エクスプローラーからグラフのクエリと視覚化を行います。
- カスタム グラフ ソリューションをパッケージ化して発行します。
サンプル データを取り込む
このタスクは、このガイドのサンプル グラフで使用Microsoft Entra IDアセット テーブル (EntraUsers、EntraGroups、EntraServicePrincipals、EntraMembers) を取り込みます。
- Defender ポータルで、System>Settings>Microsoft Sentinel>データ コネクタに移動します。
- Entra アセット テーブルがデータ レイクに取り込まれるように、Microsoft Entra ID コネクタを有効にします。 詳細については、Microsoft Sentinelデータレイクの資産データをご覧ください。
- データ レイク エクスプローラーに次のテーブルが表示されたことを確認します。
EntraUsersEntraGroupsEntraServicePrincipalsEntraMembers
- これらのテーブルは 、[システム テーブル ] ワークスペースに格納されます。
- オプション: シナリオで異なるデータが使用されている場合は、後の手順で他のコネクタを取り込み、テーブル名を置き換えます。
Note
データ レイク エクスプローラーにテーブルが表示されない場合は、グラフの作成にテーブルを使用することはできません。また、AI アシスタンスでは参照されません。
単純なノートブックを使用して接続を検証する
グラフを作成する前に、最小限のノートブックを作成して実行し、データ レイク接続を確認します。
Visual Studio Codeを開き、Data Lake にアクセスできるのと同じアカウントでMicrosoft Sentinel拡張機能にサインインします。
Visual Studio Codeで、[Sentinel 拡張機能] の [グラフ] で [新しいノートブックの作成] を選択します。
hello-world-show-tables.ipynbなど、ノートブックを保存します。カーネルを選択 を選択し、Microsoft Sentinel を選択して、使用可能な Spark プールを選択します。
次のセルを順番に追加します。
セル 1 - マークダウン:
# Hello World: Show Tables
A minimal connectivity test for the Microsoft Sentinel data lake.
If every cell below runs without error, your environment is ready
to author custom graphs.
Steps:
1. Connect to the data lake with the Microsoft Sentinel provider
2. Point at your workspace
3. Read a table and show a few rows
セル 2 - コード(ワークスペースへの接続と設定):
from sentinel_lake.providers import MicrosoftSentinelProvider
# The provider gives the notebook access to data lake tables.
lake_provider = MicrosoftSentinelProvider(spark=spark)
# >>> ADD YOUR WORKSPACE HERE <<<
# Replace with the Log Analytics workspace that holds your tables.
# Entra asset tables (EntraUsers, EntraGroups, ...) live in "System tables".
LOG_ANALYTICS_WORKSPACE = "System tables"
print(f"Using workspace: {LOG_ANALYTICS_WORKSPACE}")
セル 3 - マークダウン:
## Read a table and show rows
Reading a known table is the fastest way to confirm the extension
can reach the lake with your permissions. Swap EntraGroups for any
table that exists in your workspace.
セル 4 - コード (テーブルの読み取りと行の表示):
# Read a table from the data lake into a Spark DataFrame.
df_groups = lake_provider.read_table("EntraGroups", LOG_ANALYTICS_WORKSPACE)
# Show a sample. .df exposes the underlying Spark DataFrame.
df_groups.df.select("id", "displayName", "mailEnabled").show(20, truncate=False)
セル 5 - コード (オプションのサニティ カウント):
# Confirm the read returned data and inspect the schema.
print("Row count:", df_groups.df.count())
df_groups.df.printSchema()
- すべてのセルを上から下に実行します。
- 赤いトレースバックなしですべてのセルが完了し、テーブル プレビュー データが表示され、行数が 0 以外であることを確認します。
Note
読み取りが失敗した場合、または行が返されない場合は、[ サンプル データの取り込み ] セクションのコネクタが最初のスナップショットを完了し、 LOG_ANALYTICS_WORKSPACE が実際のワークスペースと一致し、アカウントに読み取りアクセス権があることを確認します。 Defender ポータルで KQL を使用してテーブルの内容を確認し、データが存在することを確認します。 詳細については、「Microsoft Sentinel Data Lake で KQL クエリを実行する」を参照してください。
カスタム グラフを作成する
次のいずれかの方法で作成:
- AI 支援グラフの作成。
- 手動で作成します。
AI 支援グラフの作成
AI 支援を使用してグラフを作成するには、次の手順に従います。
Visual Studio Codeで、GitHub Copilot Chatを開きます。
グラフ作成ヘルパー
@sentinel /graph-authoringでプロンプトを開始し、グラフをプレーンな言語で記述します。@sentinel /graph-authoring Create a graph that maps Entra groups to their member users, groups, and service principals using EntraGroups, EntraUsers, EntraServicePrincipals, and EntraMembers from the "System tables" workspace.Copilotでは、グラフ作成ライフサイクルに対して次のセルが生成されます。
ライフサイクル ステージ 生成された出力 環境のセットアップ 必要なパッケージと接続情報を確認します データの読み込み Sentinel データ レイクから名前付きテーブルを読み取ります データの変換 ノードとエッジ データを準備します グラフ スキーマ ノードとエッジを定義します スキーマの検証 グラフ定義を検証します グラフの構築 セッションのグラフを具体化します グラフ クエリ サンプルの GQL クエリを実行し、結果を視覚化する 次のようなプロンプトでノートブックを調整し続けます。
ゴール プロンプト リレーションシップの追加 @sentinel Add an edge from User to IPAddressデータをフィルター処理する @sentinel Filter the data to show only failed sign-ins時間範囲を変更する @sentinel Change the time range to the last 7 daysビルド エラーを修正する @sentinel Fix the error in the graph build stepコードを理解する @sentinel Explain how edge keys are defined編集せずに API を検索する What parameters does build_graph_with_data() accept? #Sentinel
ヒント
ノートブックのセルを作成または変更するには、 @sentinel /graph-authoring を使用します。 API の説明またはサンプル クエリが必要な場合は、Copilotに質問し、Sentinel 固有の回答の#Sentinelを含めます。
手動で作成
グラフを手動で作成するには、次の手順に従います。
資産テーブルを接続して読み取る。
from pyspark.sql import functions as F from sentinel_lake.providers import MicrosoftSentinelProvider lake_provider = MicrosoftSentinelProvider(spark=spark) # Entra asset tables live in the "System tables" workspace. # If your data is elsewhere, update this and ensure the tables exist. LOG_ANALYTICS_WORKSPACE = "System tables" # Use the latest snapshot of EntraUsers as the point-in-time for all tables. snapshot_time = ( lake_provider.read_table("EntraUsers", LOG_ANALYTICS_WORKSPACE) .df.agg(F.max("_SnapshotTime").alias("max_snapshot")) .collect()[0]["max_snapshot"] .strftime("%Y-%m-%dT%H:%M:%SZ") ) print(f"Using snapshot_time: {snapshot_time}") snapshot_filter = (F.col("_SnapshotTime") == F.lit(snapshot_time).cast("timestamp")) # Edges: group contains user / group / servicePrincipal df_members = ( lake_provider.read_table("EntraMembers", LOG_ANALYTICS_WORKSPACE) .filter( snapshot_filter & (F.col("sourceType") == "group") & (F.col("targetType").isin("user", "group", "servicePrincipal")) ) ) # Nodes df_groups = ( lake_provider.read_table("EntraGroups", LOG_ANALYTICS_WORKSPACE) .filter(snapshot_filter) .select("id", "displayName", "mailEnabled") ) df_users = ( lake_provider.read_table("EntraUsers", LOG_ANALYTICS_WORKSPACE) .filter(snapshot_filter) .select("id", "accountEnabled", "displayName", "department", "userPrincipalName", "usageLocation") )ノードとエッジの DataFrame を準備します。
# NODES user_nodes = df_users.df.select( "id", "displayName", "accountEnabled", "department", "userPrincipalName", "usageLocation") group_nodes = df_groups.df.select("id", "displayName", "mailEnabled") # EDGES edge_group_contains_user = ( df_members.df.filter(F.col("targetType") == "user") .select(F.col("sourceId").alias("SourceGroupId"), F.col("targetId").alias("TargetUserId")) .distinct() .withColumn("EdgeKey", F.concat_ws("_", F.col("SourceGroupId"), F.col("TargetUserId"))) ) edge_group_contains_group = ( df_members.df.filter(F.col("targetType") == "group") .select(F.col("sourceId").alias("SourceGroupId"), F.col("targetId").alias("TargetGroupId")) .distinct() .withColumn("EdgeKey", F.concat_ws("_", F.col("SourceGroupId"), F.col("TargetGroupId"))) )グラフ スキーマを定義し、DataFrames をバインドします。
from sentinel_graph import GraphSpecBuilder, Graph entra_group_graph_spec = ( GraphSpecBuilder.start() # === NODES === .add_node("EntraUser").from_dataframe(user_nodes) .with_columns("id", "displayName", "accountEnabled", "department", "userPrincipalName", "usageLocation", key="id", display="displayName") .add_node("EntraGroup").from_dataframe(group_nodes) .with_columns("id", "displayName", "mailEnabled", key="id", display="displayName") # === EDGES === .add_edge("ContainsUser").from_dataframe(edge_group_contains_user) .source(id_column="SourceGroupId", node_type="EntraGroup") .target(id_column="TargetUserId", node_type="EntraUser") .with_columns("SourceGroupId", "TargetUserId", "EdgeKey", key="EdgeKey", display="EdgeKey") .add_edge("ContainsGroup").from_dataframe(edge_group_contains_group) .source(id_column="SourceGroupId", node_type="EntraGroup") .target(id_column="TargetGroupId", node_type="EntraGroup") .with_columns("SourceGroupId", "TargetGroupId", "EdgeKey", key="EdgeKey", display="EdgeKey") ).done() # Validate the schema before building. entra_group_graph_spec.show_schema()グラフを作成します。
# Build = prepare data + publish for the session. entra_group_graph = Graph.build(entra_group_graph_spec) print(entra_group_graph.build_status.status) # "published" or "prepared"Note
Graph.build()データを準備し、1 回の呼び出しで発行します。 発行前に準備されたノードとエッジを検査するには、Graph.prepare(spec)を呼び出し、graph.nodesとgraph.edgesを確認してから、graph.publish()を呼び出します。GQL クエリを実行します。
# Find nested group relationships up to 8 levels deep. # Update the Entra Group name that you want to traverse from query_nested_groups = """ MATCH p=(g1:EntraGroup)-[cg]->{1,8}(g2) WHERE g1.displayName = 'tmplevel3' RETURN * """ entra_group_graph.query(query_nested_groups).show()
グラフ ノートブックをテストする
ノートブックをテストして、予想されるグラフ出力が生成されることを確認します。
各セルが正常に完了したことを確認します。
小さなプールで変換セルの実行時間が 5 分を超える場合は、スケールアップする前にスナップショット フィルターと列プロジェクションを強化します。
show_schema()が期待されるノードとエッジを返すことを確認します。サンプルの GQL クエリを実行し、視覚化と表形式の出力を確認します。
エッジケースを検証する:
- 空の入力。
- スキーマ ドリフト。
- テーブルへのアクセス権がありません。
ヒント
各コード セクションの上にマークダウン セルを追加して、目的、入力、出力、生成されたノードまたはエッジを文書化します。
グラフジョブをスケジュールしてグラフを具体化する
対話型セッションで作成されたグラフはエフェメラルです。 グラフを保持して更新するようにグラフ ジョブをスケジュールします。 詳細については、「Microsoft Sentinelグラフ ジョブのスケジュールと管理」を参照してください。
グラフ の名前 と 説明を入力し、ノートブックのパスを確認します。
スケジュールを選択します。
- オンデマンド: 既定の 30 日間のリテンション期間を使用して 1 回ビルドします。
- スケジュール: 繰り返し周期と開始/終了時刻を選択します。
送信を選択します。
Sentinel 拡張機能でグラフを開き、 ジョブの詳細を監視します。
必要に応じて 、[今すぐ実行 ] を使用してスケジュール外で実行します。
QueuedからIn ProgressからReadyへの状態の進行状況を確認します。
Important
送信されたジョブは、ローカル ノートブックから切り離されます。 ローカル .ipynb を編集しても、スケジュールされたジョブは更新されません。 ジョブ ノートブックを更新するには、[グラフ] パネルからジョブ ノートブックをダウンロードし、編集してから、 ジョブの編集>Submit を使用します。
Graph エクスプローラーからクエリと視覚化を行う
グラフ ジョブの状態がReadyされたら、Defender ポータル、ノートブック、および REST API でクエリを実行して視覚化します。
- Defender ポータルの [Microsoft Sentinel] でグラフ エクスペリエンスを開き、具体化されたグラフを選択します。
- GQL クエリの実行、スキーマの検査、ビジュアル出力と表形式の出力の切り替え、次ホップの走査を行います。
- 次の GQL クイック リファレンスを使用します。
| パターンまたは句 | Example |
|---|---|
| ラベル付きノード | (g:EntraGroup) |
| 型指定された有向エッジ | -[c:ContainsUser]-> |
| 可変長パス (1 ~ 8 ホップ) | (a)-[e]->{1,8}(b) |
| Filter | WHERE g.displayName = 'Finance Admins' |
| プロジェクト、並べ替え、制限 | RETURN g.displayName ORDER BY g.displayName LIMIT 10 |
| ラベル付き式 | (:EntraUser \| EntraServicePrincipal) |
Note
すべての GQL クエリは、 MATCHで始まります。 エンジンは UTC で動作します。 datetime リテラルには ZONED_DATETIME(...) を使用します。
INSERT と CREATE はサポートされません。 グラフは読み取り専用です。
ノートブックからクエリを実行します。
from sentinel_graph import Graph # Attach to an already-materialized graph by name. graph = Graph.get("entra_group_membership") graph.query("MATCH (g:EntraGroup)-[c:ContainsUser]->(u:EntraUser) RETURN g, c, u").show() # Purpose-built security algorithms are available too: graph.blast_radius(source_property_value="Finance Admins", min_hop_count=1).show()Graph REST API を使用してクエリを実行します。
GET https://api.securityplatform.microsoft.com/graphs/graph-instances?graphTypes=Custom Authorization: Bearer <access_token>POST https://api.securityplatform.microsoft.com/graphs/graph-instances/{graphName}/query Authorization: Bearer <access_token> Content-Type: application/json { "query": "MATCH (g:EntraGroup)-[c:ContainsUser]->(u) RETURN g, c, u LIMIT 100", "responseFormats": ["Table", "Graph"], "queryLanguage": "GQL" }
responseFormats は、出力図形を制御します。
"Table" は行を返し、 "Graph" はノードとエッジを返し、 ["Table", "Graph"] は両方を返します。
詳細については、「 カスタム グラフの Graph REST API」を参照してください。
グラフ ソリューションをパッケージ化して発行する
グラフをテストして具体化したら、顧客へのデプロイ用にパッケージ化します。 パッケージ化の詳細な手順については、Microsoft Sentinelグラフおよびノートブック ソリューションのパッケージ化と発行に関するページを参照してください。
Troubleshooting
次の表を使用して、症状別の一般的な問題を診断します。
グラフの作成
| 症状 | 考えられる原因と修正 |
|---|---|
MicrosoftSentinelProvider または sentinel_graph が見つかりません |
拡張機能にもう一度サインインし、Microsoft Sentinelカーネルが選択されていることを確認します。 |
| "Spark セッションを開始しています" でセルがハングする | 最初の起動には 3 ~ 5 分かかる場合があります。 6 分を超える場合は、プールの容量を確認して再試行します。 |
show_schema() が空であるか、 Graph.build() が失敗する |
キー列と表示列が存在し、null 以外であることを確認し、ソースとターゲットの node_type 値がノードのエイリアスと一致していることを確認します。 |
| GQL クエリは行を返さない | 選択したスナップショットにフィルター値が存在することを確認し、スナップショットのフィルター処理を確認します。 |
publish() アクセス許可が拒否されて失敗する |
セキュリティ オペレーター、セキュリティ管理者、またはグローバル管理者ロールがあることを確認します。 |
AI 支援型の作成
| 症状 | 考えられる原因と修正 |
|---|---|
@sentinel /graph-authoring 何もしない |
GitHub Copilotと Jupyter 拡張機能がインストールされアクティブであり、Copilot Business プランまたは Enterprise プランがあることを確認します。 |
| 生成されたコードが不足しているテーブルを参照する | コネクタのセットアップを確認し、テーブルが Data Lake Explorer に表示されていることを確認します。 |
ジョブのスケジューリング
| 症状 | 考えられる原因と修正 |
|---|---|
| ローカルノートブックの編集内容がジョブに反映されない | Graphs パネルからジョブ ノートブックをダウンロードし、編集してから、 ジョブの編集>Submit を使用します。 |
| 具体化されたグラフが消えました | オンデマンド グラフのリテンション期間は 30 日間です。 定期的なジョブをスケジュールするか、新しいビルドを実行します。 |
関連するコンテンツ
- Microsoft Sentinelの概要のカスタム グラフ
- カスタム グラフを使い始める
- AI 支援カスタム グラフの作成
- Microsoft Sentinel のグラフ プロバイダー リファレンス
- Sentinel カスタム グラフの GQL リファレンス
- カスタム グラフ用の Graph REST API
- Microsoft Sentinel データ レイクにオンボードする
- Microsoft Sentinel Data Lake でノートブックを実行する
- Security Store で Security Copilot エージェントまたは分析ソリューションを公開する
- Microsoft パートナー センター