Kom igång med anpassade grafer i Microsoft Sentinel (förhandsversion)

Anpassade grafer i Microsoft Sentinel göra det möjligt för säkerhetsforskare och analytiker att skapa skräddarsydda grafrepresentationer av sina säkerhetsdata. Genom att skapa anpassade grafer kan du modellera specifika attackmönster, undersöka hot och köra avancerade grafalgoritmer för att upptäcka dolda relationer i din digitala miljö. Den här guiden vägleder dig genom stegen för att skapa och hantera anpassade grafer med hjälp av Jupyter Notebooks i Microsoft Sentinel Visual Studio Code-tillägget.

Den här artikeln fokuserar på att manuellt redigera anpassade grafer med hjälp av kod. En AI-driven upplevelse finns i AI-assisterad anpassad grafredigering i Microsoft Sentinel.

Förhandskrav

Följande krävs för att skapa anpassade grafer i Microsoft Sentinel:

Aktivera Microsoft Entra ID-anslutningsappen för att mata in de Microsoft Entra tillgångstabeller som används i den här artikelns exempelkod. Mer information finns i Inmatning av tillgångsdata i Microsoft Sentinel datasjö.

Behörigheter

Om du vill interagera med anpassade grafer behöver du följande XDR-behörigheter i Sentinel datasjö. I följande tabell visas behörighetskraven för vanliga grafåtgärder:

Graph-åtgärd Behörigheter som krävs
Modellera och skapa en notebook-graf Använd en anpassad Microsoft Defender XDR enhetlig RBAC-roll med databehörigheter (hantera) över Microsoft Sentinel datainsamling.
Spara ett diagram i klientorganisationen Använd någon av följande Microsoft Entra ID roller:
Säkerhetsoperatör
Säkerhetsadministratör
Global administratör
Fråga efter ett beständiga diagram Använd en anpassad Microsoft Defender XDR enhetlig RBAC-roll med behörigheter för grundläggande säkerhetsdata (läsbehörighet) över Microsoft Sentinel datainsamling.

Viktigt

Du måste ha behörighet att läsa de data som används i diagrammet. Om du inte har åtkomst till en specifik datauppsättning inkluderas inte dessa data i diagrammet. Om du vill skapa en graf får du inte begränsas av ett Sentinel omfång. En begränsad användare kan inte skapa en anpassad graf.

Microsoft Entra ID roller ger bred åtkomst till alla arbetsytor i datasjön. Mer information finns i Roller och behörigheter i Microsoft Sentinel.

Installera Visual Studio Code och Microsoft Sentinel-tillägget

Skapa anpassade grafer med hjälp av Jupyter Notebooks i Microsoft Sentinel Visual Studio Code-tillägget. Mer information finns i Installera Visual Studio Code och Microsoft Sentinel-tillägget

Skapa en anpassad graf

Utför följande steg för att skapa och arbeta med anpassade grafer:

  1. Modellera en anpassad graf
  2. Spara det anpassade diagrammet genom att schemalägga ett diagramjobb
  3. Visa och hantera anpassade grafer

Modellera en anpassad graf

Skapa en anpassad graf med hjälp av en Jupyter-anteckningsbok i Microsoft Sentinel Visual Studio Code-tillägget.

Följande steg vägleder dig genom att skapa din första anpassade graf med hjälp av en exempelanteckningsbok.

Konfigurera din notebook-fil och anslut till datasjön

  1. I Visual Studio Code med tillägget Microsoft Sentinel installerat väljer du ikonen Microsoft Sentinel i den vänstra menyn.

  2. Välj Logga in för att visa diagram

  3. En dialogruta visas med texten Tillägget "Microsoft Sentinel" vill logga in med Microsoft. Välj Tillåt för att logga in.

  4. Logga in med dina autentiseringsuppgifter.

  5. När du har loggat in väljer du + sedan Skapa ny anteckningsbok.

  6. Ge notebook-filen ett namn och spara den på en lämplig plats på din arbetsyta.

    En skärmbild av inloggningssidan för grafer i Visual Studio Code.

  7. Välj Välj kernel längst upp till höger i notebook-fönstret för att välja en Spark-beräkningspool.

  8. Välj Microsoft Sentinel och välj sedan någon av de tillgängliga Spark-poolerna.

    En skärmbild av sidan välj kernel i Visual Studio Code.

    Tips

    Du kan använda AI-prompter för att skapa en anpassad grafanteckningsbok. Mer information finns i AI-assisterad anpassad grafredigering i Microsoft Sentinel.

  9. Kör en cell till genom att välja triangelikonen för körningscellen till vänster om cellen. Första gången du kör en cell kan du uppmanas att välja en kernel om du inte redan har valt en.

    Första gången du kör en cell tar det ungefär fem minuter att starta Spark-sessionen.

    En skärmbild som visar körningen av den första cellen i Visual Studio Code.

Skapa ett diagram

I följande exempel skapas ett diagram för att bläddra Microsoft Entra gruppmedlemskap och förstå kapslade grupprelationer. Exempelkoden hjälper dig att komma igång med ett enkelt användningsfall för att lära dig den anpassade graffunktionen och utnyttja kraften i grafbläddering för dina undersökningar. Du kan skapa ett diagram från valfri tabell som är tillgänglig i Sentinel datasjö.

  1. Anslut till din arbetsyta och läs Entra tillgångstabeller för att börja skapa diagrammet.

    from pyspark.sql import functions as F
    from sentinel_lake.providers import MicrosoftSentinelProvider
    
    lake_provider = MicrosoftSentinelProvider(spark=spark)
    
    # Use the "System tables" workspace which contains the Entra* Assets tables
    # If you are data is in a different workspace, update this variable accordingly and ensure the tables are present
    LOG_ANALYTICS_WORKSPACE = "System tables"
    
    # Dynamically get the latest snapshot time from EntraUsers
    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"))
    
    # Load EntraMembers - 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"))
        )
    )
    
    # Load EntraGroups - nodes
    df_groups = (
        lake_provider.read_table("EntraGroups", LOG_ANALYTICS_WORKSPACE)
        .filter(snapshot_filter)
        .select("id", "displayName", "mailEnabled")
    )
    
    # Load EntraUsers - nodes
    df_users = (
        lake_provider.read_table("EntraUsers", LOG_ANALYTICS_WORKSPACE)
        .filter(snapshot_filter)
        .select("id", "accountEnabled", "displayName", "department",
                "lastPasswordChangeDateTime", "userPrincipalName", "usageLocation")
    )
    
    # Load EntraServicePrincipals - nodes
    df_service_principals = (
        lake_provider.read_table("EntraServicePrincipals", LOG_ANALYTICS_WORKSPACE)
        .filter(snapshot_filter)
        .select("accountEnabled", "id", "displayName", "servicePrincipalType",
                "tenantId", "organizationId")
    )
    
    # Fix for Spark 3.x Parquet datetime rebase issue. Required when reading Parquet files
    # written by Spark 2.x which used the Julian calendar, whereas Spark 3.x uses Proleptic
    # Gregorian. Without these settings, timestamp columns (e.g. lastPasswordChangeDateTime)
    # may throw errors or return incorrect values. Safe to remove if all data was written by
    # Spark 3.x (typical for current Fabric/Sentinel environments).
    spark.conf.set("spark.sql.parquet.datetimeRebaseModeInRead", "CORRECTED")
    spark.conf.set("spark.sql.parquet.datetimeRebaseModeInWrite", "CORRECTED")
    spark.conf.set("spark.sql.parquet.int96RebaseModeInRead", "CORRECTED")
    spark.conf.set("spark.sql.parquet.int96RebaseModeInWrite", "CORRECTED")
    
  2. Förbereda noden och gränsdataramarna som krävs för att skapa grafen

    # ============================================================
    # NODE PREPARATION
    # ============================================================
    
    # EntraUser nodes - keyed by user id
    user_nodes = (
        df_users.df
        .select(
            F.col("id"),
            F.col("displayName"),
            F.col("accountEnabled"),
            F.col("department"),
            F.col("lastPasswordChangeDateTime"),
            F.col("userPrincipalName"),
            F.col("usageLocation")
        )
    )
    
    # EntraGroup nodes - keyed by group id
    group_nodes = (
        df_groups.df
        .select(
            F.col("id"),
            F.col("displayName"),
            F.col("mailEnabled")
        )
    )
    
    # EntraServicePrincipal nodes - keyed by SP id
    sp_nodes = (
        df_service_principals.df
        .select(
            F.col("id"),
            F.col("displayName"),
            F.col("accountEnabled"),
            F.col("servicePrincipalType"),
            F.col("tenantId"),
            F.col("organizationId")
        )
    )
    
    # ============================================================
    # EDGE PREPARATION
    # ============================================================
    
    # Edge: EntraGroup --Contains--> EntraUser
    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: EntraGroup --Contains--> EntraGroup (nested groups)
    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")))
    )
    
    # Edge: EntraGroup --Contains--> EntraServicePrincipal
    edge_group_contains_sp = (
        df_members.df
        .filter(F.col("targetType") == "servicePrincipal")
        .select(
            F.col("sourceId").alias("SourceGroupId"),
            F.col("targetId").alias("TargetSPId")
        )
        .distinct()
        .withColumn("EdgeKey", F.concat_ws("_", F.col("SourceGroupId"), F.col("TargetSPId")))
    )
    
  3. Definiera grafschemat och bind till dataramarna som skapades i föregående steg

    from sentinel_graph import GraphSpecBuilder, Graph
    
    # Define the graph schema 
    
    entra_group_graph_spec = (
        GraphSpecBuilder.start()
    
        # === NODES ===
    
        .add_node("EntraUser")
        .from_dataframe(user_nodes)  # Native Spark DataFrame (from .df + .select + .distinct)
        .with_columns(
            "id", "displayName", "accountEnabled",
            "department", "lastPasswordChangeDateTime", "userPrincipalName", "usageLocation",
            key="id", display="displayName"
        )
    
        .add_node("EntraGroup")
        .from_dataframe(group_nodes)  # Native Spark DataFrame
        .with_columns(
            "id", "displayName", "mailEnabled",
            key="id", display="displayName"
        )
    
        .add_node("EntraServicePrincipal")
        .from_dataframe(sp_nodes)  # Native Spark DataFrame
        .with_columns(
            "id", "displayName", "accountEnabled",
            "servicePrincipalType", "tenantId", "organizationId",
            key="id", display="displayName"
        )
    
        # === EDGES ===
    
        # EntraGroup --ContainsUser--> EntraUser
        .add_edge("ContainsUser")
        .from_dataframe(edge_group_contains_user)  # Native Spark DataFrame
        .source(id_column="SourceGroupId", node_type="EntraGroup")
        .target(id_column="TargetUserId", node_type="EntraUser")
        .with_columns("SourceGroupId", "TargetUserId", "EdgeKey",
                      key="EdgeKey", display="EdgeKey")
    
        # EntraGroup --ContainsGroup--> EntraGroup (nested groups)
        .add_edge("ContainsGroup")
        .from_dataframe(edge_group_contains_group)  # Native Spark DataFrame
        .source(id_column="SourceGroupId", node_type="EntraGroup")
        .target(id_column="TargetGroupId", node_type="EntraGroup")
        .with_columns("SourceGroupId", "TargetGroupId", "EdgeKey",
                      key="EdgeKey", display="EdgeKey")
    
        # EntraGroup --ContainsServicePrincipal--> EntraServicePrincipal
        .add_edge("ContainsServicePrincipal")
        .from_dataframe(edge_group_contains_sp)  # Native Spark DataFrame
        .source(id_column="SourceGroupId", node_type="EntraGroup")
        .target(id_column="TargetSPId", node_type="EntraServicePrincipal")
        .with_columns("SourceGroupId", "TargetSPId", "EdgeKey",
                      key="EdgeKey", display="EdgeKey")
    
    ).done()
    
  4. Verifiera grafschemat

    # Check the schema of the graph spec to ensure it's correct
    entra_group_graph_spec.show_schema()
    
  5. Skapa grafen, inklusive att förbereda data och publicera grafen

    # Build the graph from the spec - this will validate the spec and prepare it for querying
    # Alter options is to use Graph.prepare() to prepare the graph nodes and edges in the lake
    # and then use Graph.publish() to create the graph. You would typically call prepare() and publish()
    # seperately to understand the cost of Graph API calls that are triggeterd by Graph.publish()
    # see https://learn.microsoft.com/azure/sentinel/billing?tabs=simplified%2Ccommitment-tiers
    intra_group_graph = Graph.build(entra_group_graph_spec)
    

    Obs!

    Diagram som skapas under interaktiva notebook-sessioner tas bort när notebook-sessionen stängs. Information om hur du sparar diagrammet för återanvändning och delning finns i Spara din anpassade graf

Nu har du skapat ett diagram i notebook-filen.

Om du vill visa en visuell representation av diagrammet klistrar du in och kör följande kod i en ny cell:

# Query 1: Find nested group relationships nexting 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 *
"""
intra_group_graph.query(query_nested_groups).show()

Den här koden kör ett exempel på en GQL-fråga (Graph Query Language) för att hämta alla kapslade gruppmedlemskap upp till 8 nivåer djupt Det resulterande diagrammet visualiseras i utdata

En skärmbild som visar visualiseringen av ett diagram i Visual Studio Code.

Spara din anpassade graf

När du har skapat grafkoden i notebook-filen kan du köra notebook-filen i en interaktiv session eller schemalägga ett diagramjobb. Diagram som skapas under den interaktiva notebook-sessionen är tillfälliga och är endast tillgängliga i kontexten för notebook-sessionen. Om du vill spara grafen och dela med ditt team schemalägger du ett grafjobb för att återskapa grafen ofta. När grafen har sparats är den tillgänglig från: grafupplevelsen i Microsoft Defender-portalen under Sentinel, Visual Studio Code Notebooks och Graph-fråge-API:er.

  1. I din diagramanteckningsbok väljer du Skapa schemalagt jobb och sedan Skapa ett diagramjobb.

    En skärmbild som visar knappen Skapa schemalagt jobb i en grafanteckningsbok.

  2. I formuläret Skapa diagramjobb anger du Graph-namnet och Beskrivning och kontrollerar att rätt diagramanteckningsbok ingår i Sökväg.

  3. Om du vill skapa grafen utan att konfigurera ett uppdateringsschema väljer du På begäran i avsnittet Schema och väljer sedan Skicka för att skapa diagrammet.

    Obs!

    Grafer som skapas med schema på begäran har standardkvarhållning på 30 dagar och tas bort vid förfallodatum.

  4. Om du vill skapa grafen där grafdata uppdateras regelbundet väljer du Schemalagd i avsnittet Schema .

    1. Välj en Upprepningsfrekvens för jobbet. Du kan välja mellan Per minut, Varje timme, Varje vecka, Varje dag eller Varje månad.

    2. Fler alternativ visas för att konfigurera schemat, beroende på vilken frekvens du väljer. Till exempel veckodag, tid på dagen eller dag i månaden.

    3. Välj en Start i tid för att schemat ska börja köras.

    4. Välj ett Slut i tid för att schemat ska sluta köras. Om du inte vill ange en sluttid för schemat väljer du Ange jobb som ska köras på obestämd tid. Datum och tider finns i tidszonen.

    5. Välj Skicka för att spara jobbkonfigurationen och publicera jobbet. Grafens byggprocess startar i din klientorganisation. Visa det nyligen skapade diagrammet och dess senaste status i Sentinel-tillägget.

    En skärmbild av jobbsidan skapa diagram.

Visa och hantera anpassade grafer

När du har skapat ett grafjobb kan du visa och hantera diagrammet i din klientorganisation från Microsoft Sentinel-tillägget i Visual Studio Code.

  1. I listan med grafer väljer du den materialiserade grafen för att visa dess information.

  2. Välj fliken Jobbinformation för att visa status för grafjobbet, inklusive senaste körningstid, nästa körningstid och eventuella fel som påträffades under byggprocessen.

  3. Välj Kör nu för att manuellt utlösa en grafversion utanför de schemalagda tiderna. Statusen ändras till I kö och sedan "Pågår" medan grafen skapas.

    En skärmbild som visar fliken diagramjobbsinformation i Visual Studio Code.

  4. När grafversionen är klar uppdateras statusen till Klar. Välj fliken Diagraminformation för att visa information om diagrammet.

    En skärmbild av diagraminformationsfliken.

  5. Nu kan du fråga och visualisera grafen från grafvisualiseringen i Microsoft Sentinel i Defender-portalen. Mer information finns i Visualisera grafer i Microsoft Sentinel graf (förhandsversion).