Who Created That Service Principal? Tracing It Back with Microsoft Graph
- Shannon

- Apr 12
- 4 min read
As with previous posts, all source code and a corresponding GitHub repository can be found here!
This is one of those questions that seems like it should have a straightforward answer, but it doesn't because what good fun would that be if there were a straightforward answer? I'm sure you've been here before: someone spots an enterprise application in Microsoft Entra ID, notices it has permissions or credentials attached, and naturally asks: who created this thing?
If you start digging through the service principal object in Microsoft Graph, you'll quickly notice something odd. You can pull properties like the display name, app ID, object ID, and creation timestamp, but there's no field that simply says "created by Shannon on Tuesday afternoon." That information lives somewhere else (of course it does, Microsoft!).
Here's the short version: the service principal object tells you when it was created, but the identity of the creator is recorded in the Microsoft Entra audit logs. To answer the full question, you need to correlate the service principal object with the audit log entry which captured the creation event. Microsoft Graph exposes both pieces, so you can absolutely track this down, as long as the audit logs are still available (that's the "gotcha").
Microsoft documents the service principal resource here: https://learn.microsoft.com/en-us/graph/api/resources/serviceprincipal
The directory audit log resource that records events like service principal creation lives here: https://learn.microsoft.com/en-us/graph/api/directoryaudit-list
The audit event you're looking for is called "Add service principal".
Step 1. Identify the service principal
Start by retrieving the service principal object. This script gives you the object ID and creation timestamp, both useful for correlation.
Connect-MgGraph -Scopes "Application.Read.All","AuditLog.Read.All"
$sp = Get-MgServicePrincipal -Filter "displayName eq ` 'MyServicePrincipal'" `
-Property ` Id,AppId,DisplayName,CreatedDateTime
$sp | Select-Object DisplayName, Id, AppId, CreatedDateTimeThe CreatedDateTime field tells you when the service principal appeared in the tenant. That timestamp doesn't reveal who created it, but it's useful context when you move into the audit logs.
Step 2. Search the audit logs for the creation event
Now pivot into the Entra directory audit logs and filter for the "Add service principal" activity. The event includes several useful fields:
activityDateTime
initiatedBy
targetResources
additionalDetails
The key is matching the audit record's targetResources field to the object ID you retrieved in Step 1.
$audits = Get-MgAuditLogDirectoryAudit -Filter "activityDisplayName eq 'Add service principal'"
$match = $audits | Where-Object {
$_.TargetResources.Id -contains $sp.Id
}
$match | Select Object `
ActivityDateTime, InitiatedBy, TargetResources, AdditionalDetailsWhen you find the matching record, the initiatedBy property reveals who or what triggered the creation. Sometimes it's a user account. Other times it's an application, a managed identity, or some other automation.
A more practical script
The following script pulls everything together and prints the likely creator along with useful context about the service principal.
Connect-MgGraph -Scopes "Application.Read.All","AuditLog.Read.All"
$servicePrincipalName = "MyServicePrincipal"
# Get the service principal
$sp = Get-MgServicePrincipal -Filter "displayName eq ` '$servicePrincipalName'" `
-Property Id,AppId,DisplayName,CreatedDateTime
if (-not $sp) {
Write-Host "Service principal not found."
return
}
Write-Host "Service Principal:"
Write-Host " Name: $($sp.DisplayName)"
Write-Host " Object ID: $($sp.Id)"
Write-Host " App ID: $($sp.AppId)"
Write-Host " Created: $($sp.CreatedDateTime)"
Write-Host ""
# Search for matching audit event
$audits = Get-MgAuditLogDirectoryAudit -Filter ` "activityDisplayName eq 'Add service principal'"
$match = $audits | Where-Object `
{
$_.TargetResources.Id -contains $sp.Id
} | Sort-Object ActivityDateTime -Descending | Select-Object ` -First 1
if (-not $match) {
Write-Host "No matching Add service principal audit event ` found."
Write-Host "This usually means the audit log aged out or `
was never retained long enough."
return
}
# Figure out who initiated it the service principal creation
$initiatedByUser = $match.InitiatedBy.User.UserPrincipalName
$initiatedByApp = $match.InitiatedBy.App.DisplayName
$initiator =
if ($initiatedByUser) { $initiatedByUser }
elseif ($initiatedByApp) { $initiatedByApp }
else { "Unknown" }
Write-Host "Creation Audit Event:"
Write-Host " Activity Time: $($match.ActivityDateTime)"
Write-Host " Initiated By: $initiator"
Write-Host " Logged By: $($match.LoggedByService)"
Write-Host " Category: $($match.Category)"
Write-Host ""
if ($match.AdditionalDetails) {
Write-Host "Additional Details:"
foreach ($detail in $match.AdditionalDetails) {
Write-Host " $($detail.Key): $($detail.Value)"
}
}The creator isn't always a person
One thing that regularly trips people up: the initiator isn't always a human. In modern tenants, service principals are frequently provisioned by automation, managed identities, Azure services, or orchestration platforms.
Microsoft has added audit log properties, including ServicePrincipalProvisioningType, AppOwnerOrganizationId, and several SKU-related fields, that help clarify why a service principal appeared in the tenant. These make it easier to distinguish between a user-driven creation, a service onboarding process, and Microsoft-managed provisioning.
More on those properties here: https://learn.microsoft.com/en-us/entra/identity/monitoring-health/understand-service-principal-creation-with-new-audit-log-properties
The retention problem comes next
There's one detail that can stop an investigation cold: Entra audit logs don't live forever.
Microsoft's current retention windows are:
Free tenants: 7 days
Entra P1 or P2: 30 days
If the service principal was created outside that window and the logs weren't exported, the creator may no longer be discoverable through Graph.
Retention details are documented here: https://learn.microsoft.com/en-us/entra/identity/monitoring-health/reference-reports-data-retention
This is exactly why many organizations stream Entra audit logs into Azure Monitor, Azure Blob Storage, Sentinel, or a SIEM. Once exported, retention can stretch to months or years, making investigations like this far more practical.
Final thoughts
Service principals have a way of quietly accumulating in most tenants. They represent integrations, automation identities, and SaaS applications that were enabled months or years ago, often without much documentation. Eventually someone asks where one came from and whether it's still needed.
Microsoft Graph gives you everything required to answer that question. The service principal object tells you when it was created; the audit logs tell you who or what created it. If both pieces are still available, correlating them is straightforward.
If the logs are gone, though, that mystery service principal may stay a mystery. That's usually the moment teams realize exporting Entra audit logs should have been part of the architecture from day one.




I liked this technical explanation because tracing a service principal shows how systems connect in the background. I once struggled with a similar cloud task, and I utilised help with assignment while managing deadlines at the same time. That gave me space to focus on understanding each step. Clear tracing makes complex systems easier to follow. Nice post