[In preview] Generally Available: Azure NetApp Files migration assistant
June 24, 2026.NET July 2026!
June 24, 2026Microsoft Sentinel platform offers a growing list of tools and features, with graph being a cornerstone capability.
Sentinel graph is a relationship-first method for organizing and querying data within Microsoft Sentinel data lake. Activities amongst entities (users, devices, emails, IPs, applications, etc.) become a navigable structure that avoids a complex table structure. Rather than stitching together data and evidence via complex joins, users can follow multi-hop connections in order to understand insights such as blast radius, unseen pivots in malicious behavior, and investigative details that may not be as obvious within regular logs, all while visualizing these paths to assist in communicating evidence and findings.
This blog will walk through how to create custom graphs using GitHub Copilot chat experiences in Sentinel VS Code. And how to leverage out-of-the-box graph samples to build custom graphs addressing security outcomes. Custom graphs are available in public preview.
Prerequisites and Tooling
- Sentinel data lake enabled in the tenant, this is where the data for the graph will be stored.
- Users will need read/write permissions on Sentinel data lake data. And either security operator or security admin permissions to save a custom graph in the tenant.
- Visual Studio Code (VS Code) will need to be installed, as it is essential for building and saving graphs.
- The Jupyter notebook extension, Microsoft Sentinel extension, and GitHub Copilot extension will need to be installed from within VS Code. These are key pieces for configuring and managing graphs.
- (Optional) Microsoft Sentinel MCP server if using MCP tools like the data exploration tool.
Building a new custom graph
The starting point is within Visual Studio Code (VS Code), where the custom graph will be built via GitHub Copilot and the Sentinel graph authoring tool. Make sure to have a GitHub account logged in within VS Code, then start a chat with Copilot via View > Chat. This will open a chat window on the right side of the screen.
Determining security telemetry for investigation
If unsure about which tables are available within the environment or the columns to focus on for hunting/investigations, turn to the Sentinel MCP server. With the Sentinel MCP server, users can explore the threat landscape within their environment as well as see which data sources currently exist within the Sentinel data lake. This process can be done using natural language with Copilot to obtain the information needed to perform the task at hand.
“List the most important tables within my Microsoft Sentinel data lake environment that would build a blast radius for a compromised user account. List the best columns to use for this scenario. Format the response as a table”
The tables and columns that can be used are now known. The next step is to use these tables to construct a custom graph with help from GitHub Copilot. For this example, a blast radius graph will be built to assist in reviewing the impact of compromised accounts within the environment:
“List the top 5 compromised or targeted accounts within my environment. List which types of attacks are involved with those accounts. Summarize the information into a simple to read table”
Given this response, there are a few options for going forward:
- Return to the Microsoft Defender portal and attempt threat hunting/review this with other analysts
- Ask Copilot to provide threat hunting queries or perform incident investigations for the top users who are most targeted
- Build custom graphs to visualize threat data around the most targeted accounts
For this example, we will use option 3.
Building graph mappings with GitHub Copilot
To begin building a custom graph from scratch, a new prompt is submitted, this time tagging the Sentinel extension’s graph authoring tool. An example of the type of prompt to use is below:
“@Sentinel /graph-authoring I want to investigate the blast radius of a compromised user and what systems/ app/ devices that they accessed based on users authentication activity. Please use at least SignInLogs, NonInteractivelogs, DeviceLogon, Onprem AD logs, IdentityInfo, and AADRiskyUsers.
The graph should help investigate the following security outcomes:
- What is the user’s current risk level and risk score from Identity Protection?
- Which applications and resources did a user authenticate to?
- Are there sign-ins from risky IP addresses, Tor exit nodes, or anonymizers?
- Are there non-interactive sign-ins from unexpected locations or devices?
- Which machines did a user log on to locally/remotely (RDP)?
- Which user accounts have been active on a compromised device?
A few guidance for data ingestion:
Ensure to filter out any data that has NULL or empty values for key Nodes and Edges
Filter all data for last 14 days
Do not map json arrays as Keys in Nodes or Edges”
Note: To ensure that the graph that is written matches the desired scenario, it helps to provide outcomes or guidance to the graph authoring tool.
If a Juypter notebook is not already open within the VS Code, Copilot will build a new notebook based on the prompt given. Once Copilot is done, select a kernel to run the notebook. This can be done from the top right of the Notebook:
- Click on Select Kernel.
- Click on Microsoft Sentinel.
- Choose a pool option for the compute cluster.
- Once a pool is picked, click on the run button next to one of the code cells to boot up the compute pool (this can take up to 5 minutes)
- Once connected, users can either go through and click the run button next to the code cell to run the code or click the Run All button at the top of the Notebook.
For each cell in the Notebook:
Cell 2
This section of the notebook is for mporting the sentinel_graph library and configures Spark settings. This is essentially setting up the notebook environment for executing the rest of the code.
from sentinel_graph import notebook notebook.requires(sentinel_graph=”0.3.8″) spark.conf.set(“spark.sql.parquet.datetimeRebaseModeInRead”, “CORRECTED”)
Cell 3
This section is performing more Sentinel specific configurations by defining which Sentinel workspace to use, which timerange to use, which tables to use, etc. This is defining which data sources should be considered when building the graph.
from pyspark.sql import functions as F from sentinel_lake.providers import MicrosoftSentinelProvider lake_provider = MicrosoftSentinelProvider(spark=spark) LOG_ANALYTICS_WORKSPACE = “Woodgrove-LogAnalyiticsWorkspace” # Auto-detected from the Microsoft Sentinel extension TARGET_USER = “ram723@int.zava-private.com” # Time filter — 7 days for broader blast radius context time_filter = F.col(“TimeGenerated”) >= F.expr(“current_timestamp() – INTERVAL 7 DAYS”) # — IdentityInfo: user profile, roles, group memberships, risk — df_identity_info = ( lake_provider.read_table(“IdentityInfo”, LOG_ANALYTICS_WORKSPACE) .filter(time_filter) .filter(F.lower(F.col(“AccountUPN”)) == TARGET_USER.lower()) ) # — SigninLogs: interactive sign-ins to resources — df_signins = ( lake_provider.read_table(“SigninLogs”, LOG_ANALYTICS_WORKSPACE) .filter(time_filter) .filter( (F.lower(F.col(“UserPrincipalName”)) == TARGET_USER.lower()) & (F.col(“ResultType”) == “0”) # successful sign-ins ) )
Cell 4
This section is defining and building the nodes that will be used in the graph. The definitions include what events look like, which entities are involved, and how they are considered for each node type.
# 1. User node (the target user) user_nodes = ( df_identity_info .select( F.col(“AccountUPN”), F.col(“AccountDisplayName”), F.col(“RiskLevel”), F.col(“RiskState”), F.col(“AssignedRoles”), F.col(“GroupMembership”), F.col(“BlastRadius”), F.col(“Department”), F.col(“JobTitle”), F.col(“IsMFARegistered”), F.col(“IsAccountEnabled”) ) .distinct() .withColumn(“AccountUPN”, F.lower(F.col(“AccountUPN”))) )
Cell 5
This section is building out the schema for the graph. The schema for a graph is taking the columns and details from the tables in cell 3 while also tying them to the nodes and edges built in cell 4.
# Build nodes first builder = ( GraphSpecBuilder.start() # === NODES === .add_node(“User”) .from_dataframe(user_nodes) .with_columns(“AccountUPN”, “AccountDisplayName”, “RiskLevel”, “RiskState”, “AssignedRoles”, “GroupMembership”, “BlastRadius”, “Department”, “JobTitle”, “IsMFARegistered”, “IsAccountEnabled”, key=”AccountUPN”, display=”AccountUPN”) # Then add edges and finalise into a GraphSpec spec = ( builder # === EDGES === .add_edge(“AccessedInteractive”) .from_dataframe(edge_user_resource_interactive) .source(id_column=”UserUPN”, node_type=”User”) .target(id_column=”ResourceName”, node_type=”Resource”) .with_columns(“AppDisplayName”, “TimeGenerated”, “IPAddress”, “ConditionalAccessStatus”, “AccessType”, “EdgeKey”, key=”EdgeKey”, display=”AccessType”)
Cell 6
This cell will take the schema from cell 5 and will load it into the graph visual builder. This will give a sample of what the graphs made with this Notebook will look like. These samples are fully interactive and will give an example of how it will look within the Defender portal. For example:
Please note that the Authoring Agent may provide a different looking schema if following along with this example. The schema above is just meant to provide an example of what one will look like within a Notebook.
Cell 7
This cell is taking each of the following steps performed and is going to compile and build the graph based on the data from the Sentinel data lake. This may take a few minutes to perform.
With the custom graph built, the next step is to create a Graph Job to save the custom graph in the tenant for persistent use. If necessary, users can go back into the notebook to refine, expand, and improve the custom graph.
Publishing graph
Publishing a graph is the process of saving the graph in a tenant, allowing for the graph to be scheduled for recurring refreshes or as needed. This process saves the graph to the tenant and enables other SOC members to access this graph from within the Defender portal.
To publish a custom graph, this must go through a Graph Job. This option is available within the Notebook experience as a button near the top:
Clicking on the Create Scheduled Job button will open a new tab within VS Code with the jobs settings and the option to publish:
There are two types of job schedules:
- On Demand: Saves the custom graph to the tenant and will persist the custom graph for 30 days. After 30 days, the graph will be auto deleted.
- Scheduled: Saves the custom graph to the tenant and will rebuild with new security telemetry based on a user defined schedule.
Once everything is prepped, the custom graph can be published to the tenant by hitting the Submit button. Users can view and monitor the creation progress by finding the graph within the Sentinel extension navigation as it shows the graphs available for the environment:
Finding and selecting the custom graph will open up a new tab that shows details around the graph. This includes details around the name, creation status (creating, ready, etc), author, and publishing date.
Near the top, there are tabs for Job Details and Graph Query. These options allow the user to review the current Graph Job, make changes to the Graph Job, or query the graph within the notebook.
Querying the graph in Defender
Once the custom graph has been published and the creation status is Ready, users can query the new graph in the Defender Portal:
- Expand the Microsoft Sentinel navigation.
- Select Graphs.
- Either find the card with the graph title or search for it within the menu.
- Once found, click Query Graph to open it.
The graph will open in the schema view. The schema here is a visual representation of which nodes, edges, and relations are part of the graph. This is what was built in the notebook. To query it, a user can write GQL queries or use ones that are provided. For this example, a query provided in the Getting Started tab will be used. This is a generic query that will show everything in a graph:
// Visualize any graph MATCH (x)-[y]->(z) RETURN * LIMIT 100
More focused queries will yield more focused results. For example:
MATCH (n_user:User)-[e_ip:SignedInFrom]->(n_ip:IPAddress) MATCH (n_user)-[e_signin:InteractiveSignIn]->(n_app:Application) WHERE n_user.UserPrincipalName = ‘ENTERUSERNAMHERE’ AND n_ip.IPAddress = ‘IPADDRESSHERE’ RETURN n_user, e_ip, n_ip, e_signin, n_app
From here, a user can continue the hunt, remediate the concerns, escalate this for further attention and remediation, or refine the graph as needed.
Refining Graphs
Throughout the process, the custom graph may need to be updated for various reasons, including:
- The scope of the hunt/investigation has expanded due to new information or the hypothesis being updated based on findings
- The original hypothesis of the hunt was incorrect or needs to be changed
- Important nodes are missing from the graph and need to be added
To achieve this, return to VS Code and use the GitHub Copilot chat experience to add new telemetry, nodes, edges, or properties in the existing graph.
The below example illustrates adding Azure resources as new assets by prompting the Sentinel graph authoring tool and instructing it on what needs to be added.
Running the cells of the Notebook will yield an updated graph that includes the new changes:
Graph samples in the Sentinel VS Code extension
To help with learning, building, and using Sentinel graph, there are 5 graph samples included in the Sentinel extension within VS Code.
These can be found by clicking on the Sentinel extension and looking under Notebook Samples > Graphs. Each graph included contains a Jupyter notebook containing the graph schema and mappings, as well as graph queries which can be run against the graph.
These graphs ingest certain security telemetry and expect them to already exist within the Sentinel lake instance that is being used. If needed, the graph mapping can be updated to include/ exclude security telemetry as needed. These graph samples are also located within the Sentinel GitHub repository.
Let’s look at one of the sample graphs – Phishing Email Killchain to understand how it can help during a security investigation.
Using a graph: phishing email kill chain scenario
Phishing is the number one initial access vector, yet investigating a phishing campaign requires correlating data across multiple Sentinel tables: EmailEvents, EmailUrlInfo, UrlClickEvents, EmailAttachmentInfo, DeviceFileEvents, and DeviceProcessEvents. Each table uses a different join key (NetworkMessageId, AccountUpn, SHA256, DeviceName), and analysts must stitch results together manually across several Defender portals.
The core question every SOC analyst needs to answer is: “Who received the email, clicked the URL, downloaded the attachment, and executed it on their device?” In KQL, answering this requires 5+ sequential queries and 30–60 minutes of manual correlation. The Phishing Email Kill Chain graph fuses all of these tables into a single connected structure with 10 node types and 12 edge types, making it possible to answer that question in seconds with a single GQL traversal. SOC teams can create this graph in their tenant and start investigating phishing campaigns using graph-powered insights.
Investigation with the Phishing Email Killchain graph
Multi-hop traversal. The full kill chain from email to endpoint execution is a 4-hop path: Email → Attachment → Process → Device. In KQL, each hop is a separate join with a different key column. In the graph, it’s one MATCH clause.
Structural detection. Campaign topology is visible as the graph’s shape — senders fanning out to emails, emails fanning out to users, shared URLs converging into hubs. These patterns are structural properties requiring no aggregation queries.
Click-exposure overlay. The graph overlays email delivery and URL click paths in a single view. An analyst instantly sees which users received a phishing email AND clicked the embedded URL — no separate UrlClickEvents join needed.
Example queries
Below are three queries from the published phishing_email_killchain graph that demonstrate these capabilities. Each query is a single GQL statement that replaces multiple KQL joins.
Query 1: Full Kill Chain — Email to Endpoint
This query traces the complete attack path: phishing email → malicious attachment → process execution → endpoint device. In KQL, this requires joining 4 tables with different keys and temporal proximity filtering.
MATCH (e:Email)-[ha:HasAttachment]->(att:Attachment) -[tp:TriggeredProcess]->(p:Process)-[od:OnDevice]->(d:Device) RETURN e, ha, att, tp, p, od, d LIMIT 10
Figure 1: Two complete kill chains — Invoice_Q3.xlsm → EXCEL.EXE → DESKTOP-FIN01 and DocuSign_Contract.pdf.exe → cmd.exe → DESKTOP-SALES02. Each path is one traversal replacing 4+ KQL joins.
Query 2: Campaign Topology — Sender to Email to User to URL
This query visualizes the full campaign structure: which senders sent which emails, who received them, and what URLs were embedded. The graph’s fan-out shape immediately reveals the blast radius and shared infrastructure.
MATCH (s:Sender)-[se:Sent]->(e:Email)-[re:ReceivedEmail]->(u:User), (e)-[cu:ContainsUrl]->(url:Url) RETURN s, se, e, re, u, cu, url LIMIT 10
Figure 2: Campaign topology — 2 senders, 2 emails fanning out to 9 users and 2 URLs. The shared URL node (c0ntoso-share…) receiving edges from both emails reveals coordinated campaign infrastructure.
Query 3: URL Click Exposure — Who Clicked the Phishing Links
This query shows which emails contained URLs and which users clicked them. The Email → URL → User click chain is a single traversal that replaces joining EmailUrlInfo with UrlClickEvents.
MATCH (e:Email)-[cu:ContainsUrl]->(url:Url)<-[cl:ClickedUrl]-(u:User) RETURN e, cu, url, cl, u LIMIT 10
Figure 3: Click exposure — 3 users clicked phishing URLs from 3 different emails. Each cluster shows Email → URL → User, instantly identifying click-through victims.
These are just 3 examples of what is possible when using GQL on a graph. Users can author their own GQL queries to run on this graph to show other possibilities.
Additional graph samples
As mentioned, the Phishing Email Killchain graph is one of five graph samples that are available today for use within the VS Code Sentinel Extension. The remaining graphs are:
Behavioral Attack Chain
Ingests data from the SentinelBehaviorInfo, SentinelBehaviorEntities, AlertInfo, AlertEvidence, ThreatIntelIndicators, and BehaviorAnalytics tables to model the relationships between different detections, MITRE tactics/techniques, entities, and threat intel to high different traversals that are difficult to do with just KQL alone.
Databricks Outbound Exfiltration
Ingests data from the DatabricksNotebook, DatabricksSecrets, DatabricksDBFS, DatabricksClusters, DatabricksJobs, DatabricksSQLPermissions, IdentityInfo, AADUserRiskEvents, and BehaviorAnalytics tables to map Databricks notebook and cluster activities to the identities used in order to enable detections of unusual outbound data movement, privilege escalation, and data exfiltration patterns.
DNS C2 Beaconing
Ingests data from the DeviceNetworkEvents, DeviceInfo, and ThreatIntelIndicators to model DNS resolution patterns to detect C2 beaconing and other malicious patterns.
OAuth Privilege Escalation
Ingests data from the EntraServicePrincipals, AADRiskyServicePrincipals, and AADServicePrincipalSignInLogs tables to trace OAuth consent chains, credential abuse, and privilege escalation paths to identify hub users, over-permissions identities, and backdoor patterns that may exist.
Closing
This blog showcased an example of how a custom graph can be made with data within Microsoft Sentinel data lake and the help of GitHub Copilot, investigating a phishing email kill chain situation, and how to leverage the several graph templates that are provided in Sentinel.
Get started today by using one of the template graphs, building your own graph, or by checking out the public documentation for Sentinel graph.
Note: Custom graph API usage for creating graph and querying graph will be billed according to the Sentinel graph meter.
Public Documentation: https://learn.microsoft.com/azure/sentinel/datalake/sentinel-graph-overview
GQL Reference: Graph Query Language (GQL) reference for Microsoft Sentinel graph (Preview) | Microsoft Learn
Planning graph Costs: Plan costs and understand pricing and billing – Microsoft Sentinel | Microsoft Learn