Entra / Microsoft 365 · Applications
Get service principal sign-ins (Graph)
Extract and analyze service principal sign-in data from Entra ID using the Microsoft Graph API.
Connect & set up
Run these once per session. All scopes are read-only unless the script makes changes.
# Review required modules and connection steps before running.# Connect to Microsoft Graph or Exchange Online as needed for this script.
Run it
The main script. Copy it, or download the .ps1 and run it from your console.
param([string] $TenantId = "",[string] $AppId = "",[int] $LookbackDays = 7,[string] $StartDate = (Get-Date).AddDays(-$LookbackDays); $EndDate = (Get-Date -format s) + "Z,[string] $EndDate = (Get-Date))function Get-GraphData {# Based on https://danielchronlund.com/2018/11/19/fetch-data-from-microsoft-graph-with-powershell-paging-support/# GET data from Microsoft Graph.param ([parameter(Mandatory = $true)]$AccessToken,[parameter(Mandatory = $true)]$Uri)# Check if authentication was successful.if ($AccessToken) {$Headers = @{'Content-Type' = "application\json"'Authorization' = "Bearer $AccessToken"'ConsistencyLevel' = "eventual" }# Create an empty array to store the result.$QueryResults = @()# Invoke REST method and fetch data until there are no pages left.do {$Results = ""$StatusCode = ""do {try {$Results = Invoke-RestMethod -Headers $Headers -Uri $Uri -UseBasicParsing -Method "GET" -ContentType "application/json"$StatusCode = $Results.StatusCode} catch {$StatusCode = $_.Exception.Response.StatusCode.value__if ($StatusCode -eq 429) {Write-Warning "Got throttled by Microsoft. Sleeping for 45 seconds..."Start-Sleep -Seconds 45}else {Write-Error $_.Exception}}} while ($StatusCode -eq 429)if ($Results.value) {$QueryResults += $Results.value}else {$QueryResults += $Results}$uri = $Results.'@odata.nextlink'} until (!($uri))# Return the result.$QueryResults}else {Write-Error "No Access Token"}}# Define the values applicable for the application used to connect to the Graph - these variables vary from tenant to tenant and app to app$AppSecret = ''# Construct URI and body needed for authentication$uri = "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token"$body = @{client_id = $AppIdscope = "https://graph.microsoft.com/.default"client_secret = $AppSecretgrant_type = "client_credentials"}# Get OAuth 2.0 Token$tokenRequest = Invoke-WebRequest -Method Post -Uri $uri -ContentType "application/x-www-form-urlencoded" -Body $body -UseBasicParsing# Unpack Access Token$token = ($tokenRequest.Content | ConvertFrom-Json).access_token$Headers = @{'Content-Type' = "application\json"'Authorization' = "Bearer $Token"'ConsistencyLevel' = "eventual" }# Define variablesCLS;$Report = [System.Collections.Generic.List[Object]]::new();$CSVOutput = "C:\temp\SPSignInData.CSV"# Define start and end date for query. Add Z to each sortable date to make the Graph query happy# Build Uri for the Graph query$Uri = "https://graph.microsoft.com/beta/auditLogs/signIns?&`$filter=createdDateTime ge " + $StartDate + " and createdDateTime le " + $EndDate + " and signInEventTypes/any(z:z eq 'servicePrincipal')"# Execute the queryWrite-Host "Querying Azure AD for service principal sign-in records from" $StartDate "to" $EndDate[Array]$SpSignInData = Get-GraphData -Uri $Uri -AccessToken $TokenIf (!($SpSignInData)) { Write-Host "No service principal sign in data found - exiting" ; break }Write-Host "Processing" $SpSignInData.Count "sign-in records for service principals"# Process the information which came backForEach ($Sp in $SpSignInData) { # Process the records$StatusCode = "Success"; $StatusReason = $NullIf ($Sp.Status.ErrorCode -ne 0) {$StatusCode = $Sp.Status.ErrorCode$StatusReason = $Sp.Status.FailureReason }$ReportLine = [PSCustomObject][Ordered]@{Date = Get-Date($Sp.createdDateTime) -format gSPName = $Sp.servicePrincipalNameApp = $Sp.AppDisplayNameAppId = $Sp.AppIdLocation = $Sp.Location.CityState = $Sp.Location.StateipAddress = $Sp.IpAddressSpId = $Sp.ServicePrincipalIdResource = $Sp.ResourceDisplayNameStatus = $StatusCodeReason = $StatusReason}$Report.Add($ReportLine)} #End ForEach# Report what we've foundWrite-Host " "Write-Host "Summary of Service Principal sign-in activity"Write-Host "From" $StartDate "to" $EndDateWrite-Host "Output CSV file: " $CSVOutputWrite-Host ""$Report | Group SpName | Sort Count -Descending | Select Name, Count$Report | Export-CSV -NoTypeInformation $CSVOutput
Parameters
ParameterDefaultNotes
-TenantIdMicrosoft Entra tenant ID for app-only Graph authentication.-AppIdApplication (client) ID for the app registration used to connect.-LookbackDays7Number of days of service principal sign-in history to retrieve.-StartDate(Get-Date).AddDays(-7); $EndDate = (Get-Date -format s) + "ZStart of the reporting window.-EndDate(Get-Date)End of the reporting window.Attribution
Author
Office365itpros