AADGraphActivityLogs Just Went GA. Your SOC Has a Net-New Detection Surface for AADInternals, ROADtools, and Rogue AI Agents.
Microsoft quietly added something to Azure Monitor that closes one of the longest-running visibility gaps in the Entra ID stack.
The AADGraphActivityLogs table is now generally available in Log Analytics. It captures every legacy Azure AD Graph API call hitting your tenant. If you’ve been running Sentinel detections against MicrosoftGraphActivityLogs and feeling good about your coverage, I have bad news. You’ve been watching the front door while attackers walked through a side entrance you didn’t know existed.
This post breaks down what changed, why this matters more than anyone is saying, and the KQL detections you can drop into Sentinel today.
What Actually Changed
Microsoft Entra emits two distinct activity log streams. They cover different APIs.
MicrosoftGraphActivityLogs covers requests to graph.microsoft.com. This is the modern API. It went GA back in April 2024 and most teams running well-tuned Sentinel deployments already have it wired up.
AADGraphActivityLogs covers requests to graph.windows.net. This is the legacy Azure AD Graph API. Microsoft has been retiring it for years. Azure AD Graph was originally targeted for retirement on June 30, 2025. Apps configured for extended access lost the ability to call it starting in early September 2025.
Here is the part nobody is talking about: the API still works for some apps. And every offensive tool worth knowing about still uses it.
Log Source API Endpoint Status Default Coverage MicrosoftGraphActivityLogs graph.microsoft.com GA April 2024 Most well-tuned SOCs AADGraphActivityLogs graph.windows.net GA (recent) Almost nobody
The schema is comparable to MicrosoftGraphActivityLogs. You get UserAgent, CallerIpAddress, RequestUri, RequestMethod, ResponseStatusCode, AppId, ServicePrincipalId, UserId, IdentityProvider, TokenIssuedAt, and the full request context. Enough to build real detections.
Requires Microsoft Entra ID P1 or P2. Configure via Diagnostic Settings on Entra ID, same workflow as MicrosoftGraphActivityLogs.
Why This Is a Net-New Detection Surface
Here is why I keep using the phrase “net-new.” For years, security teams who tracked offensive tooling like AADInternals, ROADtools, and Ping Castle hit the same wall: these tools talked to graph.windows.net, not graph.microsoft.com. Defenders watching MicrosoftGraphActivityLogs saw nothing. The activity simply did not appear in the modern log stream.
That gap is now closed. If AADInternals enumerates your tenant, you can see it. If a red team runs ROADrecon, you can see it. If a service principal that should have been migrated years ago is still hitting Azure AD Graph, you can see it.
This matters for three audiences:
SOC analysts get visibility into a class of attack tooling that was previously invisible. AADInternals is in MITRE ATT&CK as S0677. ROADtools is in ATT&CK as S0684. Both have been associated with named threat groups. You should not have been running blind on this.
Identity engineers get an inventory tool. Every legacy app or service principal still calling Azure AD Graph shows up. This is your migration backlog, made visible.
CISOs get a forcing function. If something is still calling Azure AD Graph in your tenant in 2026, it is either an unmigrated legacy app, a vendor that ignored Microsoft’s notices, or an attacker. All three deserve a conversation.
The AI Angle Nobody Is Connecting
This is the part I want CISOs paying attention to.
AI agents acting on behalf of users authenticate through the same identity plane as everything else. Microsoft’s broader direction with Agent ID, Agent 365, and the agent governance stack is pushing toward AI agents being first-class identities. Most modern agent platforms talk to graph.microsoft.com. Good.
But here is the threat model. An attacker does not care which API your agent platform was supposed to use. If they can get a token with Azure AD Graph permissions, they can use the legacy API to enumerate, exfiltrate, and persist. AADGraphActivityLogs is your only signal that this happened.
Three AI-specific patterns worth detecting:
Agent service principals calling Azure AD Graph. A modern agent should never need the legacy API. If your Copilot Studio agent or custom-built agent has a service principal hitting
graph.windows.net, that is a misconfiguration at best and a compromised identity at worst.Token replay against Azure AD Graph from a non-standard user agent. AI tooling is creating a flood of automated traffic. Attackers using stolen tokens are blending into that traffic. Anomalous user agent strings against the legacy API are a high-fidelity signal because legitimate AI traffic should not be there.
High-volume enumeration patterns from agent identities. Reconnaissance tools query users, groups, applications, and roles in characteristic patterns. These patterns look identical whether the actor is AADInternals or an attacker who hijacked an AI agent’s token.
The detections below cover all three.
KQL Detection Pack
Drop these into Sentinel as analytic rules or hunting queries. Tune the thresholds for your environment before flipping to alert mode.
1. AADInternals Default User Agent
AADInternals ships with a default user agent string. Sophisticated operators change it. Most red teamers and opportunistic attackers do not.
AADGraphActivityLogs
| where TimeGenerated > ago(1d)
| where UserAgent has_any ("AADInternals", "aad-internals")
| project TimeGenerated, UserId, ServicePrincipalId, AppId, CallerIpAddress, UserAgent, RequestUri, RequestMethod, ResponseStatusCode
| order by TimeGenerated desc
2. ROADtools / ROADrecon Enumeration Pattern
ROADtools uses the Python requests library or its own client. The fingerprint is high-volume reads against directory objects from a single principal in a short window.
AADGraphActivityLogs
| where TimeGenerated > ago(1h)
| where RequestUri has_any ("/users", "/groups", "/applications", "/servicePrincipals", "/directoryRoles")
| where RequestMethod == "GET"
| summarize
RequestCount = count(),
UniqueEndpoints = dcount(RequestUri),
SampleUserAgents = make_set(UserAgent, 10)
by UserId, ServicePrincipalId, AppId, CallerIpAddress, bin(TimeGenerated, 5m)
| where RequestCount > 100 and UniqueEndpoints >= 4
| order by RequestCount desc
3. Any Service Principal Still Calling Azure AD Graph
This is the migration-backlog query. Every result is either a legacy app that needs migrating or something worse.
AADGraphActivityLogs
| where TimeGenerated > ago(7d)
| where ResponseStatusCode < 400
| summarize
CallCount = count(),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated),
UniqueOperations = dcount(OperationName),
SampleEndpoints = make_set(RequestUri, 5)
by AppId, ServicePrincipalId
| order by CallCount desc
4. Coverage-Gap Detection (Modern API vs Legacy API)
This is the one I am most excited about. Identify principals that hit MicrosoftGraphActivityLogs AND AADGraphActivityLogs. Anything appearing in legacy-only space is suspect.
let LegacyCallers =
AADGraphActivityLogs
| where TimeGenerated > ago(7d)
| summarize LegacyCalls = count() by AppId, ServicePrincipalId;
let ModernCallers =
MicrosoftGraphActivityLogs
| where TimeGenerated > ago(7d)
| summarize ModernCalls = count() by AppId, ServicePrincipalId;
LegacyCallers
| join kind=leftouter ModernCallers on AppId, ServicePrincipalId
| extend ModernCalls = coalesce(ModernCalls, 0)
| where ModernCalls == 0
| project AppId, ServicePrincipalId, LegacyCalls, ModernCalls
| order by LegacyCalls desc
A principal that ONLY shows in AADGraphActivityLogs and never in MicrosoftGraphActivityLogs is using the legacy API exclusively. That is a strong signal of either an unmigrated legacy integration or offensive tooling.
5. AI Agent Identity Calling Azure AD Graph
Replace the AppId list with your environment’s known agent service principals (Copilot Studio agents, custom Foundry agents, third-party agents).
let AgentAppIds = dynamic([
"<copilot-studio-agent-app-id>",
"<foundry-agent-app-id>",
"<custom-agent-app-id>"
]);
AADGraphActivityLogs
| where TimeGenerated > ago(1d)
| where AppId in (AgentAppIds) or ServicePrincipalId in (AgentAppIds)
| project TimeGenerated, AppId, ServicePrincipalId, UserId, CallerIpAddress, UserAgent, RequestUri, ResponseStatusCode
| order by TimeGenerated desc
If this returns ANY rows, an agent identity called the legacy API. That should never happen by design. Investigate every hit.
6. Token Replay Heuristic on Azure AD Graph
Same TokenIssuedAt timestamp used from multiple geographies or IPs is a classic token-theft signal.
AADGraphActivityLogs
| where TimeGenerated > ago(24h)
| where isnotempty(TokenIssuedAt)
| summarize
UniqueIPs = dcount(CallerIpAddress),
IPList = make_set(CallerIpAddress, 10),
UserAgentList = make_set(UserAgent, 5),
CallCount = count()
by UserId, AppId, TokenIssuedAt
| where UniqueIPs > 1
| order by UniqueIPs desc
Where Microsoft Is Heading With This
Microsoft is closing the legacy-API blind spot on purpose. The trajectory is obvious if you look at how Entra, Defender for Identity, and Sentinel have been changing release after release.
Identity is becoming the primary attack surface in cloud environments. Microsoft’s response is to instrument every API call against Entra in the same way EDR instruments every process on an endpoint. AADGraphActivityLogs is the last big visibility gap to close. MicrosoftGraphActivityLogs covered the modern surface. Now the legacy surface is covered too.
Expect this data to start flowing into Defender XDR’s IdentityLogonEvents and related tables in the coming releases. Expect Microsoft to publish their own analytic rules. Expect Security Copilot to start surfacing legacy-API anomalies in the unified investigation experience.
The teams that move first capture two advantages. They get coverage today. And they get baseline data that will make their detections meaningful when Microsoft’s own rules ship and produce noise without context.
What to Do This Week
Three actions, in order:
Enable the diagnostic setting on Entra ID and route
AADGraphActivityLogsto your Sentinel workspace. You need Entra ID P1 or P2 and Security Administrator or Global Administrator to configure it.Run the migration-backlog query (#3 above) and see what is still calling Azure AD Graph in your tenant. Tag every result. Vendor app, internal legacy integration, or unknown.
Deploy the AADInternals and ROADtools detections (#1 and #2) as analytic rules in alert mode. False positive rate should be near zero. If they fire, you have an incident or an authorized red team engagement you didn’t know about.
The detection rules above are running in my dev tenant. I will be submitting them to the Azure-Sentinel community repo as a coordinated detection pack with documentation. Watch the GitHub for the PR.
If your team is sitting on a pile of identity-attack visibility gaps and you want a hand prioritizing what to build first, that is what I do every day. Reach out.
Caleb McDowell is a Microsoft Security evangelist and highly certified practitioner. He runs a Microsoft Security Services practice helping organizations deploy and operationalize the Microsoft E5 security stack. Follow on LinkedIn or subscribe at calebamcdowell.substack.com.

