사용자 지정 그래프 플랫폼 솔루션 개발

비고

이 기능은 현재 미리 보기로 제공됩니다. 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 게시자 계정

도구

사용자 지정 그래프 솔루션을 빌드하고 게시하려면 다음 도구가 필요합니다.

팁 (조언)

테넌트가 KQL 쿼리, notebook 또는 Security Copilot 에이전트용으로 이미 온보딩된 경우 기존 데이터 레이크를 재사용하세요. 테넌트당 하나의 데이터 레이크가 있습니다. Defender 포털에서 시스템>설정>Microsoft Sentinel>데이트 레이크로 이동합니다. 상태가 프로비전된 경우 작업 1을 건너뜁니다.

빌드 워크플로

다음 워크플로에서는 사용자 지정 그래프 솔루션을 빌드하고 게시하는 단계를 간략하게 설명합니다.

  1. 그래프가 모델링하는 테이블에 샘플 데이터를 수집합니다.
  2. 간단한 Notebook을 사용하여 연결의 유효성을 검사합니다.
  3. 사용자 지정 그래프를 작성합니다.
  4. 그래프 노트북을 테스트합니다.
  5. 그래프 작업을 예약하여 그래프를 구체화합니다.
  6. Graph 탐색기에서 그래프를 쿼리하고 시각화합니다.
  7. 사용자 지정 그래프 솔루션을 패키지하고 게시합니다.

샘플 데이터 수집

이 작업은 이 가이드의 EntraUsersEntraGroupsEntraServicePrincipalsEntraMembers샘플 그래프에서 사용하는 Microsoft Entra ID 자산 테이블을 수집합니다.

  1. Defender 포털에서 시스템>설정>Microsoft Sentinel>데이타 커넥터로 이동합니다.
  2. Entra 자산 테이블이 데이터 레이크로 수집되도록 Microsoft Entra ID 커넥터를 사용하도록 설정합니다. 자세한 내용은 Microsoft Sentinel 데이터 레이크의 자산 데이터를 참조하세요.
  3. 이러한 테이블이 데이터 레이크 탐색기에 표시되는지 확인합니다.
    • EntraUsers
    • EntraGroups
    • EntraServicePrincipals
    • EntraMembers
  4. 이러한 테이블은 시스템 테이블 작업 영역에 배치됩니다.
  5. 선택 사항: 시나리오에서 다른 데이터를 사용하는 경우 다른 커넥터를 수집하고 이후 단계에서 테이블 이름을 대체합니다.

비고

데이터 레이크 탐색기에 테이블이 표시되지 않으면 그래프 작성에 사용할 수 없으며 AI 지원에서 이를 참조하지 않습니다.

간단한 Notebook을 사용하여 연결 유효성 검사

그래프 작성 전에 데이터 레이크 연결을 확인하는 최소 Notebook을 만들고 실행합니다.

  1. Visual Studio Code 열고 데이터 레이크에 대한 액세스 권한이 있는 동일한 계정으로 Microsoft Sentinel 확장에 로그인합니다.

  2. Visual Studio Code Sentinel 확장-그래프 아래에서 새 전자 필기장 만들기를 선택합니다. VS Code에서 새 Notebook 만들기 옵션을 보여 주는 스크린샷

  3. 노트북을 저장합니다. 예: hello-world-show-tables.ipynb

  4. 커널 선택을 선택하고, Microsoft Sentinel을 선택한 다음, 사용 가능한 Spark 풀을 선택합니다. VS Code의 커널 선택 옵션을 보여 주는 스크린샷

  5. 다음 셀을 순서대로 추가합니다.

셀 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()
  1. 모든 셀을 위쪽에서 아래쪽으로 실행합니다.
  2. 빨간색 추적 없이 모든 셀이 완료되고, 테이블 미리 보기 데이터가 표시되고, 행 수가 0이 아닌지 확인합니다.

비고

읽기가 실패하거나 행을 반환하지 않는 경우 수집 샘플 데이터 섹션의 커넥터가 첫 번째 스냅샷을 완료하고 실제 LOG_ANALYTICS_WORKSPACE 작업 영역과 일치하며 계정에 읽기 권한이 있는지 확인합니다. Defender 포털에서 KQL을 사용하여 테이블 내용을 확인하여 데이터가 있는지 확인합니다. 자세한 내용은 Microsoft Sentinel 데이터 레이크에서 KQL 쿼리 실행을 참조하세요.

사용자 지정 그래프 작성

다음 방법 중 하나를 사용하여 작성하세요.

  • AI 지원 그래프 작성.
  • 손으로 작성합니다.

AI 지원 그래프 작성

AI 지원을 사용하여 그래프를 작성하려면 다음 단계를 수행합니다.

  1. Visual Studio Code에서 GitHub Copilot Chat을 엽니다.

  2. 그래프 작성 도우미 @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.
    

    VS Code에서 AI 지원 그래프 작성을 보여 주는 스크린샷

    Copilot 그래프 작성 수명 주기에 대해 다음 셀을 생성합니다.

    수명 주기 단계 생성된 출력
    환경 설정 필요한 패키지 및 연결 정보를 확인합니다.
    데이터 로드 Sentinel 데이터 레이크에서 이름이 지정된 테이블을 읽습니다.
    데이터 변환 노드 및 에지 데이터 준비
    그래프 스키마 노드 및 에지 정의
    스키마 유효성 검사 그래프 정의의 유효성을 검사합니다.
    그래프 빌드 세션에 대한 그래프를 구체화합니다.
    그래프 쿼리 샘플 GQL 쿼리를 실행하고 결과를 시각화합니다.
  3. 다음과 같은 프롬프트로 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를 포함하세요.

직접 작성

그래프를 수동으로 작성하려면 다음 단계를 수행합니다.

  1. 자산 테이블을 연결하고 읽습니다.

    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")
    )
    
  2. 노드 및 에지 데이터 프레임을 준비합니다.

    # 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")))
    )
    
  3. 그래프 스키마를 정의하고 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()
    
  4. 그래프를 작성합니다.

    # 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()을(를) 호출합니다.

  5. 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을 테스트하여 예상 그래프 출력을 생성하는지 확인합니다.

  1. 커널을 다시 시작하고 모든 셀을 위에서 아래로 실행합니다. 커널 다시 시작 단추를 보여 주는 스크린샷

  2. 각 셀이 성공적으로 완료되었는지 확인합니다.

  3. 작은 풀에서 변환 셀이 5분 이상 실행되는 경우 확장하기 전에 스냅샷 필터 및 열 프로젝션을 강화합니다.

  4. show_schema() 예상된 노드와 에지를 반환하는지 확인합니다.

  5. 샘플 GQL 쿼리를 실행하고 시각화 및 테이블 형식 출력을 확인합니다.

  6. 경계 사례를 검증합니다:

    • 입력이 비어 있습니다.
    • 스키마 드리프트.
    • 테이블 액세스가 없습니다.

팁 (조언)

각 코드 섹션 위에 markdown 셀을 추가하여 용도, 입력, 출력 및 생성된 노드 또는 가장자리를 문서화합니다.

그래프 작업을 예약하여 그래프를 실체화합니다.

대화형 세션에서 만든 그래프는 임시입니다. 그래프 작업을 예약하여 그래프를 유지 및 새로 고칩니다. 자세한 내용은 Microsoft Sentinel 그래프 작업 예약 및 관리를 참조하세요.

  1. 그래프 노트북에서 예약된 작업 만들기>그래프 작업 만들기를 선택합니다. 그래프 작업 만들기 단추를 보여 주는 스크린샷.

  2. 그래프 이름 및 설명을 입력하고 Notebook 경로를 확인합니다.

  3. 일정을 선택합니다.

    • 주문형: 기본 30일 보존을 사용하여 한 번 빌드합니다.
    • 예약됨: 반복 주기 및 시작/종료 시간을 선택합니다.
  4. 제출을 선택합니다.

  5. Sentinel 확장에서 그래프를 열고 작업 세부 정보를 모니터링합니다.

  6. 지금 실행을 사용하여 필요에 따라 외부 일정을 실행합니다.

  7. 상태가 Queued에서 In Progress를 거쳐 Ready로 진행되는지 확인합니다.

중요합니다

제출된 작업은 로컬 노트북과 분리되어 독립적으로 실행됩니다. 로컬 .ipynb 을 편집해도 예약된 작업이 업데이트되지 않습니다. 작업 전자 필기장을 업데이트하려면 그래프 패널에서 다운로드하고 편집한 다음 작업>제출 편집을 사용합니다.

Graph 탐색기에서 쿼리 및 시각화

그래프 작업 상태가 Ready된 후 Defender 포털, Notebook 및 REST API에서 쿼리 및 시각화합니다.

  1. Defender 포털에서 Microsoft Sentinel 아래의 그래프 환경을 열고 구체화된 그래프를 선택합니다.
  2. GQL 쿼리를 실행하고, 스키마를 검사하고, 시각적 및 테이블 형식 출력을 전환하고, 다음 홉을 트래버스합니다.
  3. 이 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 는 지원되지 않습니다. 그래프는 읽기 전용입니다.

  1. 노트북에서 쿼리하기.

    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()
    
  2. 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일입니다. 되풀이 작업을 예약하거나 새 빌드를 실행합니다.