비고
이 기능은 현재 미리 보기로 제공됩니다. Microsoft Azure 미리 보기에 대한 보충 사용 약관을 참조하세요.
Microsoft Sentinel 사용자 지정 그래프 솔루션은 Microsoft Sentinel 데이터 레이크 테이블의 노드와 가장자리를 정의하고, 그래프를 빌드하며, 사용자가 GQL(그래프 쿼리 언어)을 사용하여 해당 그래프를 쿼리할 수 있도록 하는 Notebook 기반 솔루션입니다.
이 가이드를 사용하여 고객이 Microsoft Security Store에서 검색할 수 있도록 사용자 지정 그래프 솔루션을 Microsoft Partner Center SaaS 제품으로 빌드, 테스트, 구체화, 패키지 및 게시합니다.
사전 요구 사항
사용자 지정 그래프 솔루션을 만들려면 데이터 레이크에 온보딩되어야 합니다. 자세한 내용은 Microsoft Sentinel 데이터 레이크에 온보딩하는 방법을 참조하세요.
Permissions
다음 표에서는 이 가이드의 각 작업 또는 범위에 필요한 권한을 나열합니다.
| Operation | 필수 역할 |
|---|---|
| Sentinel 데이터 레이크에 온보딩 | Microsoft Entra ID - 보안 관리자 또는 전역 관리자 |
| Defender 포털에 Sentinel 작업 영역 온보딩 | 구독 소유자 또는 구독 범위의 사용자 액세스 관리자 및 구독 또는 리소스 그룹 범위의 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 게시자 계정 |
도구
사용자 지정 그래프 솔루션을 빌드하고 게시하려면 다음 도구가 필요합니다.
- Visual Studio Code.
- Visual Studio Code 대한 Microsoft Sentinel 확장입니다. 자세한 내용은 Visual Studio Code 설치와 Microsoft Sentinel 확장 기능을 참조하세요.
- Visual Studio Code용 Jupyter 확장입니다.
- GitHub Copilot 확장 및 GitHub Copilot Business 또는 Enterprise 플랜.
- 로컬 컴퓨터에 설치된 Python 3.10 이상.
팁 (조언)
테넌트가 KQL 쿼리, notebook 또는 Security Copilot 에이전트용으로 이미 온보딩된 경우 기존 데이터 레이크를 재사용하세요. 테넌트당 하나의 데이터 레이크가 있습니다. Defender 포털에서 시스템>설정>Microsoft Sentinel>데이트 레이크로 이동합니다. 상태가 프로비전된 경우 작업 1을 건너뜁니다.
빌드 워크플로
다음 워크플로에서는 사용자 지정 그래프 솔루션을 빌드하고 게시하는 단계를 간략하게 설명합니다.
- 그래프가 모델링하는 테이블에 샘플 데이터를 수집합니다.
- 간단한 Notebook을 사용하여 연결의 유효성을 검사합니다.
- 사용자 지정 그래프를 작성합니다.
- 그래프 노트북을 테스트합니다.
- 그래프 작업을 예약하여 그래프를 구체화합니다.
- Graph 탐색기에서 그래프를 쿼리하고 시각화합니다.
- 사용자 지정 그래프 솔루션을 패키지하고 게시합니다.
샘플 데이터 수집
이 작업은 이 가이드의 EntraUsersEntraGroupsEntraServicePrincipalsEntraMembers샘플 그래프에서 사용하는 Microsoft Entra ID 자산 테이블을 수집합니다.
- Defender 포털에서 시스템>설정>Microsoft Sentinel>데이타 커넥터로 이동합니다.
- Entra 자산 테이블이 데이터 레이크로 수집되도록 Microsoft Entra ID 커넥터를 사용하도록 설정합니다. 자세한 내용은 Microsoft Sentinel 데이터 레이크의 자산 데이터를 참조하세요.
- 이러한 테이블이 데이터 레이크 탐색기에 표시되는지 확인합니다.
EntraUsersEntraGroupsEntraServicePrincipalsEntraMembers
- 이러한 테이블은 시스템 테이블 작업 영역에 배치됩니다.
- 선택 사항: 시나리오에서 다른 데이터를 사용하는 경우 다른 커넥터를 수집하고 이후 단계에서 테이블 이름을 대체합니다.
비고
데이터 레이크 탐색기에 테이블이 표시되지 않으면 그래프 작성에 사용할 수 없으며 AI 지원에서 이를 참조하지 않습니다.
간단한 Notebook을 사용하여 연결 유효성 검사
그래프 작성 전에 데이터 레이크 연결을 확인하는 최소 Notebook을 만들고 실행합니다.
Visual Studio Code 열고 데이터 레이크에 대한 액세스 권한이 있는 동일한 계정으로 Microsoft Sentinel 확장에 로그인합니다.
Visual Studio Code Sentinel 확장-그래프 아래에서 새 전자 필기장 만들기를 선택합니다.
노트북을 저장합니다. 예:
hello-world-show-tables.ipynb커널 선택을 선택하고, Microsoft Sentinel을 선택한 다음, 사용 가능한 Spark 풀을 선택합니다.
다음 셀을 순서대로 추가합니다.
셀 1 - Markdown:
# 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 - Markdown:
## 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이 아닌지 확인합니다.
비고
읽기가 실패하거나 행을 반환하지 않는 경우 수집 샘플 데이터 섹션의 커넥터가 첫 번째 스냅샷을 완료하고 실제 LOG_ANALYTICS_WORKSPACE 작업 영역과 일치하며 계정에 읽기 권한이 있는지 확인합니다. Defender 포털에서 KQL을 사용하여 테이블 내용을 확인하여 데이터가 있는지 확인합니다. 자세한 내용은 Microsoft Sentinel 데이터 레이크에서 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 쿼리를 실행하고 결과를 시각화합니다. 다음과 같은 프롬프트로 Notebook을 계속 다듬어 가세요.
목표 프롬프트 관계 추가 @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") )노드 및 에지 데이터 프레임을 준비합니다.
# 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"))) )그래프 스키마를 정의하고 DataFrame을 바인딩합니다.
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"비고
Graph.build()는 데이터를 준비하고 한 번의 호출로 게시합니다. 게시 전에 준비된 노드와 에지를 검사하려면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()
그래프 노트북 테스트
Notebook을 테스트하여 예상 그래프 출력을 생성하는지 확인합니다.
각 셀이 성공적으로 완료되었는지 확인합니다.
작은 풀에서 변환 셀이 5분 이상 실행되는 경우 확장하기 전에 스냅샷 필터 및 열 프로젝션을 강화합니다.
show_schema()예상된 노드와 에지를 반환하는지 확인합니다.샘플 GQL 쿼리를 실행하고 시각화 및 테이블 형식 출력을 확인합니다.
경계 사례를 검증합니다:
- 입력이 비어 있습니다.
- 스키마 드리프트.
- 테이블 액세스가 없습니다.
팁 (조언)
각 코드 섹션 위에 markdown 셀을 추가하여 용도, 입력, 출력 및 생성된 노드 또는 가장자리를 문서화합니다.
그래프 작업을 예약하여 그래프를 실체화합니다.
대화형 세션에서 만든 그래프는 임시입니다. 그래프 작업을 예약하여 그래프를 유지 및 새로 고칩니다. 자세한 내용은 Microsoft Sentinel 그래프 작업 예약 및 관리를 참조하세요.
그래프 이름 및 설명을 입력하고 Notebook 경로를 확인합니다.
일정을 선택합니다.
- 주문형: 기본 30일 보존을 사용하여 한 번 빌드합니다.
- 예약됨: 반복 주기 및 시작/종료 시간을 선택합니다.
제출을 선택합니다.
Sentinel 확장에서 그래프를 열고 작업 세부 정보를 모니터링합니다.
지금 실행을 사용하여 필요에 따라 외부 일정을 실행합니다.
상태가
Queued에서In Progress를 거쳐Ready로 진행되는지 확인합니다.
중요합니다
제출된 작업은 로컬 노트북과 분리되어 독립적으로 실행됩니다. 로컬 .ipynb 을 편집해도 예약된 작업이 업데이트되지 않습니다. 작업 전자 필기장을 업데이트하려면 그래프 패널에서 다운로드하고 편집한 다음 작업>제출 편집을 사용합니다.
Graph 탐색기에서 쿼리 및 시각화
그래프 작업 상태가 Ready된 후 Defender 포털, Notebook 및 REST API에서 쿼리 및 시각화합니다.
- Defender 포털에서 Microsoft Sentinel 아래의 그래프 환경을 열고 구체화된 그래프를 선택합니다.
- GQL 쿼리를 실행하고, 스키마를 검사하고, 시각적 및 테이블 형식 출력을 전환하고, 다음 홉을 트래버스합니다.
- 이 GQL 빠른 참조를 사용하세요:
| 패턴 또는 절 | 예시 |
|---|---|
| 레이블이 있는 노드 | (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) |
비고
모든 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() 권한이 거부된 상태에서 실패 |
보안 운영자, 보안 관리자 또는 전역 관리자 역할이 있는지 확인합니다. |
인공지능 지원 저작
| 증상 | 원인 및 수정 가능성 |
|---|---|
@sentinel /graph-authoring 아무 것도 수행하지 않습니다. |
GitHub Copilot 및 Jupyter 확장이 설치되고 활성화되어 있고 Copilot Business 또는 Enterprise 계획이 있는지 확인합니다. |
| 생성된 코드가 누락된 테이블을 참조합니다. | 커넥터 설정을 확인하고 테이블이 데이터 레이크 탐색기에 표시되는지 확인합니다. |
업무 일정 예약
| 증상 | 원인 및 수정 가능성 |
|---|---|
| 로컬 전자 필기장 편집이 작업에 반영되지 않음 | 그래프 패널에서 작업 노트북을 다운로드하여 편집한 다음 작업 편집>제출을 사용합니다. |
| 구체화된 그래프가 사라졌습니다. | 주문형 그래프의 보존 기간은 30일입니다. 되풀이 작업을 예약하거나 새 빌드를 실행합니다. |
관련 콘텐츠
- Microsoft Sentinel 개요의 사용자 지정 그래프
- 사용자 지정 그래프 시작
- AI 지원 사용자 지정 그래프 작성
- Microsoft Sentinel 그래프 공급자 참조
- Sentinel 사용자 지정 그래프에 대한 GQL 참조
- 사용자 지정 그래프에 대한 그래프 REST API
- Microsoft Sentinel 데이터 레이크로 온보드
- Microsoft Sentinel Data Lake에서 Notebook 실행
- Security Store에 Security Copilot 에이전트 또는 분석 솔루션 게시
- Microsoft Partner Center