Prise en main des graphiques personnalisés dans Microsoft Sentinel (préversion)

Les graphiques personnalisés dans Microsoft Sentinel permettent aux chercheurs et aux analystes en sécurité de créer des représentations graphiques personnalisées de leurs données de sécurité. En créant des graphiques personnalisés, vous pouvez modéliser des modèles d’attaque spécifiques, examiner les menaces et exécuter des algorithmes de graphe avancés pour découvrir les relations cachées au sein de votre environnement numérique. Ce guide vous guide tout au long des étapes de création et de gestion de graphiques personnalisés à l’aide de notebooks Jupyter dans l’extension Microsoft Sentinel Visual Studio Code.

Cet article se concentre sur la création manuelle de graphiques personnalisés à l’aide de code. Pour une expérience pilotée par l’IA, consultez Création de graphiques personnalisés assistés par IA dans Microsoft Sentinel.

Prérequis

Les éléments suivants sont nécessaires pour créer des graphiques personnalisés dans Microsoft Sentinel :

Activez le connecteur Microsoft Entra ID pour ingérer les tables de ressources Microsoft Entra utilisées dans l’exemple de code de cet article. Pour plus d’informations, consultez Ingestion de données de ressources dans le lac de données Microsoft Sentinel.

Autorisations

Pour interagir avec des graphiques personnalisés, vous avez besoin des autorisations XDR suivantes dans Sentinel lac de données. Le tableau suivant répertorie les exigences d’autorisation pour les opérations de graphe courantes :

Opération sur le graphe Autorisations requises
Modéliser et créer un graphe de bloc-notes Utilisez un rôle RBAC unifié Microsoft Defender XDR personnalisé avec des autorisations de données (gestion) pour la collecte de données Microsoft Sentinel.
Enregistrez un graphique au sein du tenant Utilisez l’un des rôles Microsoft Entra ID suivants :
Opérateur de sécurité
Administrateur de sécurité
Administrateur général
Interroger un graphe persistant Utilisez un rôle RBAC unifié Microsoft Defender XDR personnalisé disposant de permissions de base en lecture sur les données de sécurité pour la collecte de données Microsoft Sentinel.

Important

Vous devez disposer des autorisations nécessaires pour lire les données utilisées dans le graphique. Si vous n’avez pas accès à un jeu de données spécifique, ces données ne seront pas incluses dans le graphique. Pour créer un graphique, vous ne devez pas être limité par une étendue Sentinel. Un utilisateur délimité n’est pas en mesure de créer un graphe personnalisé.

Les rôles Microsoft Entra ID fournissent un accès étendu dans tous les espaces de travail du lac de données. Pour plus d’informations, consultez Rôles et autorisations dans Microsoft Sentinel.

Installer Visual Studio Code et l’extension Microsoft Sentinel

Créez des graphiques personnalisés à l’aide de notebooks Jupyter dans l’extension Visual Studio Code de Microsoft Sentinel. Pour plus d’informations, consultez Installer Visual Studio Code et l’extension Microsoft Sentinel

Créer un graphe personnalisé

Pour créer et utiliser des graphiques personnalisés, procédez comme suit :

  1. Modéliser un graphe personnalisé
  2. Conserver le graphe personnalisé en planifiant un travail de graphe
  3. Afficher et gérer des graphiques personnalisés

Modéliser un graphe personnalisé

Créez un graphique personnalisé à l’aide d’un notebook Jupyter dans l’extension Microsoft Sentinel Visual Studio Code.

Les étapes suivantes vous guident tout au long de la création de votre premier graphe personnalisé à l’aide d’un exemple de notebook.

Configurer votre notebook et se connecter au lac de données

Procédez comme suit pour créer un bloc-notes et vous connecter au lac de données Microsoft Sentinel.

  1. Dans Visual Studio Code avec l’extension Microsoft Sentinel installée, sélectionnez l’icône Microsoft Sentinel dans le menu de gauche.

  2. Sélectionnez Se connecter pour afficher les graphiques

  3. Une boîte de dialogue s’affiche avec le texte L’extension « Microsoft Sentinel » souhaite se connecter à l’aide de Microsoft. Sélectionnez Autoriser pour vous connecter.

  4. Connectez-vous avec vos informations d’identification.

  5. Après vous être connecté, sélectionnez +Créer un bloc-notes.

  6. Nommez le fichier de bloc-notes et enregistrez-le à un emplacement approprié dans votre espace de travail.

    Capture d’écran de la page de connexion pour les graphiques dans Visual Studio Code.

  7. Sélectionnez Sélectionner un noyau en haut à droite de la fenêtre du notebook pour sélectionner un pool de calcul Spark.

  8. Sélectionnez Microsoft Sentinel, puis sélectionnez l’un des pools Spark disponibles.

    Capture d’écran de la page de sélection du noyau dans Visual Studio Code.

    Conseil

    Vous pouvez utiliser des instructions d’IA pour vous aider à créer un notebook de graphes personnalisé. Pour plus d’informations, consultez Création de graphiques personnalisés assistés par IA dans Microsoft Sentinel.

  9. Exécutez une cellule en cliquant sur l’icône triangulaire d’exécution située à gauche de celle-ci. La première fois que vous exécutez une cellule, vous pouvez être invité à sélectionner un noyau si vous n’en avez pas déjà sélectionné un.

    La première fois que vous exécutez une cellule, le démarrage de la session Spark prend environ cinq minutes.

    Capture d’écran montrant l’exécution de la première cellule dans Visual Studio Code.

Créer un graphique

L’exemple suivant crée un graphe pour parcourir les appartenances aux groupes dans Microsoft Entra et comprendre les relations entre groupes imbriqués. Ce code d’exemple vous permet de démarrer à l’aide d’un cas d’usage simple pour vous familiariser avec la fonctionnalité de graphe personnalisé et exploiter la puissance de la traversée de graphe dans le cadre de vos investigations. Vous pouvez créer un graphique à partir de n’importe quelle table disponible dans le lac de données Sentinel.

  1. Connectez-vous à votre espace de travail et lisez les tables de ressources Entra pour commencer à créer le graphe.

    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. Préparer les dataframes de nœud et de périphérie nécessaires à la création du graphe

    # ============================================================
    # 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. Définissez votre schéma de graphe et liez-vous aux dataFrames de nœud et de périphérie que vous avez préparés précédemment dans cette procédure

    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. Valider le schéma de graphe

    # Check the schema of the graph spec to ensure it's correct
    entra_group_graph_spec.show_schema()
    
  5. Générer le graphe, y compris la préparation des données et la publication du graphe

    # 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)
    

    Remarque

    Les graphiques créés pendant les sessions interactives de notebook sont supprimés lorsque la session de notebook est fermée. Pour conserver le graphe à des fins de réutilisation et de partage, consultez Conserver votre graphe personnalisé

Vous avez maintenant créé un graphique dans le notebook.

L’exemple Python suivant exécute une requête Graph Query Language (GQL) sur le graphe que vous venez de créer pour explorer les relations de groupes imbriqués. Utilisez des requêtes comme celle-ci après la création de graphes pour traverser les hiérarchies de membres et découvrir des chemins d’accès indirects. Pour essayer, collez le code dans une nouvelle cellule de carnet et exécutez-le. Cette requête d’exemple parcourt des membres imbriqués de groupes Microsoft Entra jusqu’à huit niveaux de profondeur et affiche les relations résultantes :

# 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()

Ce code exécute un exemple de requête GQL (Graph Query Language) pour récupérer toutes les appartenances de groupe imbriquées jusqu’à 8 niveaux de profondeur. Le graphe obtenu est visualisé dans la sortie

Capture d’écran montrant la visualisation d’un graphique dans Visual Studio Code.

Conserver votre graphe personnalisé

Une fois que vous avez créé le code du graphe dans le notebook, vous pouvez exécuter le bloc-notes dans une session interactive ou planifier un travail de graphe. Les graphiques créés pendant la session de bloc-notes interactive sont temporaires et sont disponibles uniquement dans le contexte de la session de bloc-notes. Pour enregistrer votre graphique et le partager avec votre équipe, planifiez un travail de graphe pour reconstruire votre graphe fréquemment. Une fois enregistré, le graphe devient accessible depuis l’interface de graphe du portail Microsoft Defender sous Sentinel, les notebooks Visual Studio Code ainsi que les API de requête de graphe.

  1. Dans votre bloc-notes de graphique, sélectionnez Créer un travail planifié, puis Créer un travail de graphe.

    Capture d’écran montrant le bouton Créer un travail planifié dans un bloc-notes de graphique.

  2. Dans le formulaire Créer une tâche de graphe, entrez le nom du graphe et la description, puis vérifiez que le notebook de graphe approprié est inclus dans Chemin d’accès.

  3. Pour générer le graphe sans configurer une planification d’actualisation, sélectionnez À la demande dans la section Planification , puis envoyer pour créer le graphe.

    Remarque

    Les graphiques créés à l’aide de la planification à la demande ont une rétention par défaut de 30 jours et sont supprimés à l’expiration.

  4. Pour générer le graphe où les données du graphique sont actualisées régulièrement, sélectionnez Planifié dans la section Planification .

    1. Sélectionnez une fréquence de répétition pour le travail. Vous pouvez choisir parmi À la minute, À l’heure, Hebdomadaire, Quotidien ou Mensuel.

    2. D’autres options s’affichent pour configurer la planification, en fonction de la fréquence que vous sélectionnez. Par exemple, le jour de la semaine, l’heure de la journée ou le jour du mois.

    3. Sélectionnez une heure de début pour démarrer l’exécution de la planification.

    4. Sélectionnez une Date de fin pour que la planification s’arrête. Si vous ne souhaitez pas définir d’heure de fin pour la planification, sélectionnez Définir le travail à exécuter indéfiniment. Les dates et heures se trouvent dans votre fuseau horaire.

    5. Sélectionnez Envoyer pour enregistrer la configuration du travail et publier le travail. Le processus de création du graphe est lancé dans votre tenant. Affichez le graphique nouvellement créé et sa dernière status dans l’extension Sentinel.

    Capture d’écran de la page de création d’un travail de graphe.

Afficher et gérer des graphiques personnalisés

Après avoir créé une tâche de graphe, vous pouvez afficher et gérer le graphe au sein de votre locataire depuis l’extension Microsoft Sentinel dans Visual Studio Code.

  1. Dans la liste des graphiques, sélectionnez votre graphe matérialisé pour afficher ses détails.

  2. Sélectionnez l’onglet Détails du travail pour afficher les status du travail de graphe, y compris l’heure de la dernière exécution, l’heure de la prochaine exécution et les erreurs rencontrées pendant le processus de génération.

  3. Sélectionnez Exécuter maintenant pour déclencher manuellement une génération de graphique en dehors des heures planifiées. L’état passe à En file d’attente, puis « En cours » pendant la génération du graphique.

    Capture d’écran montrant l’onglet Détails du travail de graphique dans Visual Studio Code.

  4. Une fois la génération du graphe terminée, l’état passe à Prêt. Sélectionnez l’onglet Détails du graphique pour afficher des informations sur le graphique.

    Capture d’écran de l’onglet Détails du graphique.

  5. Vous pouvez désormais interroger et visualiser votre graphe depuis la visualisation du graphe dans Microsoft Sentinel, dans le portail Defender. Pour plus d’informations, consultez Visualiser les graphiques dans le graphe Microsoft Sentinel (préversion).

Mettre à jour des graphiques personnalisés

Après avoir mis à jour le code du graphe du bloc-notes, exécutez le bloc-notes localement pour valider vos modifications avant de mettre à jour un travail de graphe.

Modifier les détails existants du graphe personnalisé

Utilisez ce flux de travail pour modifier un travail de graphe existant.

  1. Dans le panneau Graph de Microsoft Sentinel de Visual Studio Code, sélectionnez Modifier le travail du graphe pour le graphe que vous souhaitez mettre à jour, puis accédez à Nom du graphe>Détails du travail.

  2. Mettez à jour les champs à modifier :

    • Nom du graphe (facultatif). La mise à jour du nom du graphe crée un nouveau graphe dans votre locataire. Le graphique existant reste inchangé.
    • Description (facultatif). La mise à jour de la description remplace la description actuelle du graphique et reconstruit le graphique.
    • Configuration du cluster (facultatif). La mise à jour de la configuration du cluster modifie la taille de calcul et reconstruit le graphique.
    • Fréquence du travail (facultatif). La mise à jour de la fréquence du travail modifie la planification périodique et reconstruit le graphique.

Capture d’écran représentant le workflow « Modifier la tâche de graphe » dans le volet Graph de Microsoft Sentinel dans Visual Studio Code.

Remplacer un graphe personnalisé existant

Utilisez ce flux de travail lorsque vous souhaitez recréer un graphique et remplacer un graphique existant portant le même nom.

  1. Ouvrez le bloc-notes mis à jour.

  2. Sélectionnez Créer un travail planifié dans l’expérience de notebook Sentinel, puis sélectionnez Créer un travail de graphe.

  3. Dans le formulaire Créer un graphe , fournissez un nom de graphique.

    • Si vous fournissez un nouveau nom de graphique, un nouveau graphique est créé dans le locataire et le graphique existant reste disponible.
    • Si vous réutilisez un nom de graphe existant pour le locataire, le graphique existant est remplacé après confirmation. Les noms de graphe doivent être uniques ; les doublons de noms de graphe ne sont pas pris en charge.
  4. Vérifiez que path référence votre bloc-notes mis à jour.

  5. Mettez à jour la description du graphique.

  6. Sélectionnez la configuration du cluster et la fréquence de travail, comme pour créer un graphique.

  7. Mettez à jour les informations de planification , si nécessaire.

  8. Mettez à jour le champ Description en fonction des besoins.

  9. Sélectionnez Soumettre pour recréer le graphe dans votre tenant.

Capture d’écran montrant le formulaire Créer un graphique utilisé pour remplacer un graphique personnalisé existant dans Visual Studio Code.