Introdução aos gráficos personalizados no Microsoft Sentinel (pré-visualização)

Os gráficos personalizados no Microsoft Sentinel permitem que investigadores e analistas de segurança criem representações de gráficos personalizadas dos respetivos dados de segurança. Ao criar gráficos personalizados, pode modelar padrões de ataque específicos, investigar ameaças e executar algoritmos de gráfico avançados para descobrir relações ocultas no seu ambiente digital. Este guia orienta-o ao longo dos passos para criar e gerir gráficos personalizados com os blocos de notas do Jupyter na extensão Microsoft Sentinel Visual Studio Code.

Este artigo centra-se na criação manual de gráficos personalizados com código. Para obter uma experiência orientada por IA, veja Criação de grafos personalizados assistidos por IA no Microsoft Sentinel.

Pré-requisitos

Os itens a seguir são necessários para criar gráficos personalizados no Microsoft Sentinel:

Ative o conector Microsoft Entra ID para ingerir as tabelas de recursos Microsoft Entra utilizadas no código de exemplo deste artigo. Para obter mais informações, veja Ingestão de dados de recursos no data lake do Microsoft Sentinel.

Permissões

Para interagir com gráficos personalizados, precisa das seguintes permissões XDR no Sentinel data lake. A tabela seguinte lista os requisitos de permissão para operações de grafos comuns:

Operação de gráfico Permissões obrigatórias
Modelar e criar um gráfico de bloco de notas Use uma função personalizada de RBAC unificado do Microsoft Defender XDR com permissões de dados (gerenciar) sobre a coleta de dados do Microsoft Sentinel.
Manter um gráfico no inquilino Use uma das seguintes funções do Microsoft Entra ID:
Operador de segurança
Administrador de segurança
Administrador global
Consultar um gráfico persistente Utilize uma função RBAC unificada Microsoft Defender XDR personalizada com permissões básicas de dados de segurança (leitura) através da recolha de dados Microsoft Sentinel.

Importante

Tem de ter permissões para ler os dados utilizados no gráfico. Se não tiver acesso a um conjunto de dados específico, esses dados não serão incluídos no gráfico. Para criar um gráfico, você não deve estar restrito por um escopo do Sentinel. Um usuário com escopo restrito não pode criar um gráfico personalizado.

As funções do Microsoft Entra ID fornecem amplo acesso a todos os espaços de trabalho em todo o data lake. Para obter mais informações, veja Funções e permissões no Microsoft Sentinel.

Instalar Visual Studio Code e a extensão Microsoft Sentinel

Crie gráficos personalizados com blocos de notas do Jupyter na extensão Microsoft Sentinel Visual Studio Code. Para obter mais informações, veja Instalar Visual Studio Code e a extensão Microsoft Sentinel

Criar um gráfico personalizado

Para criar e trabalhar com gráficos personalizados, conclua os seguintes passos:

  1. Modelar um gráfico personalizado
  2. Manter o gráfico personalizado ao agendar uma tarefa de gráfico
  3. Ver e gerir gráficos personalizados

Modelar um gráfico personalizado

Crie um gráfico personalizado com um bloco de notas do Jupyter na extensão Microsoft Sentinel Visual Studio Code.

Os passos seguintes explicam como criar o seu primeiro gráfico personalizado com um bloco de notas de exemplo.

Configure seu notebook e conecte-se ao data lake

Use as etapas a seguir para criar um notebook e conectar-se ao data lake Microsoft Sentinel.

  1. Em Visual Studio Code com a extensão Microsoft Sentinel instalada, selecione o ícone Microsoft Sentinel no menu esquerdo.

  2. Selecione Iniciar sessão para ver gráficos

  3. É apresentada uma caixa de diálogo com o texto A extensão "Microsoft Sentinel" quer iniciar sessão com a Microsoft. Selecione Permitir para iniciar sessão.

  4. Inicie sessão com as suas credenciais.

  5. Depois de iniciar sessão, selecione +Criar novo bloco de notas.

  6. Dê um nome ao ficheiro do bloco de notas e guarde-o numa localização adequada na área de trabalho.

    Uma captura de ecrã da página de início de sessão para gráficos no Visual Studio Code.

  7. Selecione Selecionar kernel no canto superior direito da janela do notebook para selecionar um pool de computação do Spark.

  8. Selecione Microsoft Sentinel e, em seguida, selecione qualquer um dos pools do Spark disponíveis.

    Uma captura de tela da página de seleção de kernel no Visual Studio Code.

    Dica

    Pode utilizar pedidos de IA para o ajudar a criar um bloco de notas de gráficos personalizado. Para obter mais informações, veja Criação de grafos personalizados assistidos por IA no Microsoft Sentinel.

  9. Execute uma célula selecionando o ícone de triângulo Executar célula à esquerda da célula. Na primeira vez que executar uma célula, poderá ser-lhe pedido que selecione um kernel se ainda não tiver selecionado um.

    A primeira vez que você executa uma célula, iniciar a sessão do Spark leva cerca de cinco minutos.

    Uma captura de ecrã a mostrar a execução da primeira célula no Visual Studio Code.

Criar um gráfico

O exemplo a seguir cria um grafo para percorrer as associações a grupos no Microsoft Entra e compreender as relações entre grupos aninhados. O código de exemplo ajuda você a começar com um caso de uso simples para conhecer o recurso de grafo personalizado e aproveitar o poder da travessia de grafos em suas investigações. Pode criar um gráfico a partir de qualquer tabela disponível no Sentinel data lake.

  1. Conecte-se ao seu espaço de trabalho e leia as tabelas de ativos do Entra para começar a criar o grafo.

    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. Prepare os DataFrames de nós e arestas necessários para construir o grafo

    # ============================================================
    # 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. Defina o esquema do seu grafo e vincule-o aos DataFrames de nós e arestas que você preparou anteriormente neste procedimento

    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. Validar o esquema do gráfico

    # Check the schema of the graph spec to ensure it's correct
    entra_group_graph_spec.show_schema()
    
  5. Criar o gráfico, incluindo preparar os dados e publicar o gráfico

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

    Observação

    Os gráficos criados durante sessões interativas do bloco de notas são removidos quando a sessão do bloco de notas é fechada. Para manter o gráfico para reutilização e partilha, consulte Manter o gráfico personalizado

Agora você criou um grafo no notebook.

Para mostrar uma representação visual do grafo, em uma nova célula cole e execute a consulta GQL (Linguagem de Consulta graph) de exemplo a seguir. Esta consulta percorre participações aninhadas em grupos do Microsoft Entra em até oito níveis de profundidade e exibe os relacionamentos resultantes:

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

Este código executa uma consulta GQL (Graph Query Language) de exemplo para recuperar todas as participações em grupos aninhados em até 8 níveis de profundidade. O gráfico resultante é exibido na saída.

Uma captura de ecrã a mostrar a visualização de um gráfico no Visual Studio Code.

Manter o gráfico personalizado

Depois de criar o código do gráfico no bloco de notas, pode executar o bloco de notas numa sessão interativa ou agendar uma tarefa de gráfico. Os gráficos criados durante a sessão interativa do bloco de notas são temporários e só estão disponíveis no contexto da sessão do bloco de notas. Para guardar o seu gráfico e partilhar com a sua equipa, agende uma tarefa de gráfico para reconstruir o seu gráfico com frequência. Assim que o gráfico for guardado, é acessível a partir de: a experiência de gráfico no portal do Microsoft Defender em Sentinel, Visual Studio Code Notebooks e Graph query APIs.

  1. No seu bloco de notas de gráficos, selecione Criar Tarefa Agendada e, em seguida, selecione Criar uma tarefa de gráfico.

    Captura de ecrã a mostrar o botão criar tarefa agendada num bloco de notas de gráficos.

  2. No formulário Criar tarefa de grafo, insira o Nome do grafo e a Descrição e verifique se o notebook de grafo correto está incluído no campo Caminho.

  3. Para criar o gráfico sem configurar uma agenda de atualização, selecione A pedido na secção Agenda e, em seguida, selecione Submeter para criar o gráfico.

    Observação

    Os gráficos criados com base na agenda a pedido têm uma retenção predefinida de 30 dias e são eliminados após a expiração.

  4. Para criar o gráfico onde os dados do gráfico são atualizados regularmente, selecione Agendado na secção Agenda .

    1. Selecione uma Frequência de repetição para a tarefa. Você pode escolher entre Por minuto, Por hora, Semanal, Diário ou Mensal.

    2. São apresentadas mais opções para configurar a agenda, consoante a frequência que selecionar. Por exemplo, dia da semana, hora do dia ou dia do mês.

    3. Selecione um horário de Iniciar em para que o agendamento comece a ser executado.

    4. Selecione um horário de Terminar em para que o agendamento pare de ser executado. Se não quiser definir uma hora de fim para a agenda, selecione Definir tarefa para ser executada indefinidamente. As datas e horas estão no seu fuso horário.

    5. Selecione Submeter para guardar a configuração da tarefa e publicar a tarefa. O processo de criação do grafo começa no seu locatário. Veja o gráfico recentemente criado e o respetivo status mais recente na extensão Sentinel.

    Uma captura de tela da página de criação de tarefa de gráfico.

Ver e gerir gráficos personalizados

Depois de criar uma tarefa de gráfico, pode ver e gerir o gráfico no seu inquilino a partir da extensão Microsoft Sentinel no Visual Studio Code.

  1. Na lista de gráficos, selecione o gráfico materializado para ver os respetivos detalhes.

  2. Selecione o separador Detalhes da Tarefa para ver o status da tarefa de gráfico, incluindo a hora da última execução, a hora da próxima execução e quaisquer erros encontrados durante o processo de compilação.

  3. Selecione Executar Agora para acionar manualmente uma compilação de gráficos fora das horas agendadas. O Status muda para Na fila e, em seguida, "Em andamento" enquanto o gráfico está sendo criado.

    Uma captura de tela mostrando a guia de detalhes do trabalho de grafo no Visual Studio Code.

  4. Quando a compilação do gráfico estiver concluída, o Estado é atualizado para Pronto. Selecione o separador Detalhes do Graph para ver informações sobre o gráfico.

    Uma captura de tela da aba de detalhes do gráfico.

  5. Agora você pode consultar e visualizar seu grafo na visualização de grafo do Microsoft Sentinel no portal do Microsoft Defender. Para obter mais informações, veja Visualizar gráficos no gráfico Microsoft Sentinel (pré-visualização).

Atualizar grafos personalizados

Depois de atualizar o código do grafo do notebook, execute o notebook localmente para validar suas alterações antes de atualizar um trabalho de grafo.

Editar detalhes do grafo personalizado existente

Use esse fluxo de trabalho para editar um trabalho de grafo existente.

  1. No painel do Microsoft Sentinel Graph no Visual Studio Code, selecione Editar trabalho do grafo para o grafo que você deseja atualizar e, em seguida, acesse Nome do grafo>Detalhes do trabalho.

  2. Atualize os campos que você deseja alterar:

    • Nome do grafo (opcional). Ao atualizar o nome do grafo, um novo grafo é criado no seu tenant. O grafo existente permanece inalterado.
    • Descrição (opcional). Ao atualizar a descrição, a descrição do grafo atual é substituída e o grafo é reconstruído.
    • Configuração do cluster (opcional). Atualizar a configuração do cluster altera o tamanho da computação e recria o grafo.
    • Frequência de trabalho (opcional). Atualizar a frequência do trabalho altera o agendamento recorrente e recria o grafo.

Uma captura de tela que mostra o fluxo de trabalho do trabalho

Sobrescrever um gráfico personalizado existente

Use esse fluxo de trabalho quando quiser recriar um grafo e substituir um grafo existente com o mesmo nome.

  1. Abra o bloco de anotações atualizado.

  2. Selecione Criar Trabalho Agendado na experiência do bloco de anotações do Sentinel e, em seguida, selecione Criar um trabalho de grafo.

  3. No formulário Criar grafo , forneça um nome de grafo.

    • Se você fornecer um novo nome de grafo, um novo grafo será criado no locatário, e o grafo existente permanecerá disponível.
    • Se você reutilizar um nome de grafo existente para locatário, o grafo existente será substituído após a confirmação. Nomes de grafo são exclusivos, não há suporte para nomes de grafo duplicados.
  4. Verifique se Caminho se refere ao notebook atualizado.

  5. Atualize a Descrição do grafo.

  6. Selecione a configuração do cluster e a frequência de trabalho, semelhante à criação de um novo grafo.

  7. Atualize as informações de Agendamento , se necessário.

  8. Atualize o campo Descrição novamente conforme necessário.

  9. Selecione Enviar para recriar o grafo no seu tenant.

Uma captura de tela mostrando o formulário Criar grafo usado para substituir um grafo personalizado existente no Visual Studio Code.