Forum Discussion

EntilZha's avatar
EntilZha
Iron Contributor
Dec 03, 2019

Using Microsoft Graph for Audit Logs

I'm trying to use Microsoft Graph to retrieve Windows Sign In logs from the previous day with the idea of creating reports  based on the data.

 

The issue I'm having:

  • Getting the exact same user information on each paging (@odata.nextLink) even though the skip token is different, except after the sixth skip the the URL within the odata.nextLink is repeating.

Any help in finding a resolution would be greatly appreciative 

 

 

Snapshot of my PowerShell script:

$Uri = "https://graph.microsoft.com/v1.0/auditLogs/signIns?`$filter=createdDateTime gt 2019-12-02T12:00Z and createdDateTime lt 2019-12-02T20:01Z and appDisplayName eq 'Windows Sign In' and deviceDetail/operatingSystem eq 'Windows'&orderby=createdDateTime desc"


#$Uri = "https://graph.microsoft.com/v1.0/auditLogs/signIns`?$filter=appDisplayName eq 'Windows Sign In' AND deviceDetail/operatingSystem eq 'Windows'&orderby=createdDateTime desc"

 

# Fetch all Signin Logs
$AuditLogRequest = Invoke-RestMethod -Uri $Uri -Headers $Header -Method Get -ContentType "application/json"

$AuditlogNextLink = $AuditLogRequest."@odata.nextLink"


while($AuditlogNextLink -ne $null)
{
$Header = @{Authorization = "$($Request.token_type) $($Request.access_token)"}
$AuditLogRequest = (Invoke-RestMethod -Uri $AuditlogNextLink –Headers $Header –Method Get -ContentType "application/json")


$AuditlogNextLink = $AuditLogRequest.'@odata.nextLink'
$AuditlogNextLink >> "D:\AzureADSignInError\nextLink.txt"
$AuditLogs += $AuditLogRequest.value

$LoginArray = New-Object System.Collections.Generic.List[System.Object]
Foreach($AuditLog in $AuditLogs)
{
$DisplayName = $AuditLog.userDisplayName
$EmailAddress = $AuditLog.userPrincipalName
$UserObjectID = $AuditLog.userID
$AppDisplayName = $AuditLog.appDisplayName
$CreatedDate = $AuditLog.createdDateTime

$DetailInfo = $AuditLog.deviceDetail
$DeviceName = $DetailInfo.displayname
$DeviceID = $DetailInfo.deviceId
$DeviceOS = $DetailInfo.operatingSystem
$DeviceTrustType = $DetailInfo.trustType

$StatusInfo = $AuditLog.status
$FailureReason = $StatusInfo.failureReason
$ErrorCode = $StatusInfo.errorCode
$AdditonalDetail = $StatusInfo.additionalDetails


If($AppDisplayName -eq "Windows Sign In")
{
[int]$Counter++
$AddDateTime = Get-date -UFormat $c
If($ErrorCode -eq "0")
{
$ArrayData = $DisplayName + "|" + $EmailAddress + "|" + $AppDisplayName + "|" + $DeviceOS + "|" + $DeviceTrustType + "|" + $CreatedDate + "|" + $AddDateTime
$Data = "$UserObjectID-$DeviceID"
$AddToLog = $LoginArray.Contains("$Data")

Write-host $AddToLog ".......................... $Data ..............................................................................................." -ForegroundColor DarkGray
Write-host $DisplayName " ... " $AppDisplayName "|"$DeviceOS "|" $DeviceTrustType "|" $DeviceName "|" $ErrorCode "|" $FailureReason "|" $CreatedDate "|" $AddDateTime "|" $Counter -ForegroundColor Green


If($AddToLog -eq $False)
{
#$LoginArray += $Data
$LoginArray.Add($Data)
Write-host $Data -ForegroundColor Magenta

$ExportData = $DisplayName + "|" + $EmailAddress + "|" + $UserObjectID + "|" + $AppDisplayName + "|" + $DeviceOS + "|" + $DeviceTrustType + "|" + $DeviceName + "|" + $DeviceID + "|" + $CreatedDate + "|" + $AddDateTime
Out-File -FilePath $LogFile -InputObject $ExportData -Encoding UTF8 -append

$Data = $Null
$AddToLog = $Null
}
}
ElseIf($ErrorCode -eq "50155")#50155 - Device authentication failed for this user 50057 - device failed authentication
{
[int]$Counter++
$errorreport = "$DeviceID ... $ErrorCode .... $FailureReason"
Write-host $errorreport "................................................................................Count: " $Counter -ForegroundColor Cyan
$errorreport >> "D:\AzureADSignInError\AzureSignInErrors.txt"
}
ElseIf($ErrorCode -eq "50057")#User account is disabled. The account has been disabled by an administrator.
{
[int]$Counter++
$errorreport = "$EmailAddress ... $ErrorCode .... $FailureReason"
Write-host $errorreport "................................................................................Count: " $Counter -ForegroundColor Cyan
$errorreport >> "D:\AzureADSignInError\AzureSignInErrors.txt"
}
Else
{
[int]$Counterr++
$errorreport = "$EmailAddress ... $DeviceName ... $ErrorCode .... $FailureReason"
Write-host $errorreport "................................................................................Count: " $Counter -ForegroundColor Cyan
$errorreport >> "D\AzureADSignInError:\AzureSignInErrors.txt"
}

}# End Windows SignIn If statment
}#End of Foreach Loop

} # End of While Loop

 

 

 

The @data.nextlink Screenshots

 

https://graph.microsoft.com/v1.0/auditLogs/signIns?$filter=createdDateTime+gt+2019-12-02T12%3a00Z+and+createdDateTime+lt+2019-12-02T20%3a01Z+and+appDisplayName+eq+%27Windows+Sign+In%27+and+deviceDetail%2foperatingSystem+eq+%27Windows%27&orderby=createdDateTime+desc&$skiptoken=0000000000000000000000000009d3e_2000


https://graph.microsoft.com/v1.0/auditLogs/signIns?$filter=createdDateTime+gt+2019-12-02T12%3a00Z+and+createdDateTime+lt+2019-12-02T20%3a01Z+and+appDisplayName+eq+%27Windows+Sign+In%27+and+deviceDetail%2foperatingSystem+eq+%27Windows%27&orderby=createdDateTime+desc&$skiptoken=00000000000000000000000000039375_3000


https://graph.microsoft.com/v1.0/auditLogs/signIns?$filter=createdDateTime+gt+2019-12-02T12%3a00Z+and+createdDateTime+lt+2019-12-02T20%3a01Z+and+appDisplayName+eq+%27Windows+Sign+In%27+and+deviceDetail%2foperatingSystem+eq+%27Windows%27&orderby=createdDateTime+desc&$skiptoken=000000000000000000000000000f400_4000


https://graph.microsoft.com/v1.0/auditLogs/signIns?$filter=createdDateTime+gt+2019-12-02T12%3a00Z+and+createdDateTime+lt+2019-12-02T20%3a01Z+and+appDisplayName+eq+%27Windows+Sign+In%27+and+deviceDetail%2foperatingSystem+eq+%27Windows%27&orderby=createdDateTime+desc&$skiptoken=000000000000000000000000000e5b2c_5000


https://graph.microsoft.com/v1.0/auditLogs/signIns?$filter=createdDateTime+gt+2019-12-02T12%3a00Z+and+createdDateTime+lt+2019-12-02T20%3a01Z+and+appDisplayName+eq+%27Windows+Sign+In%27+and+deviceDetail%2foperatingSystem+eq+%27Windows%27&orderby=createdDateTime+desc&$skiptoken=0000000000000000000000000006b78_6000


https://graph.microsoft.com/v1.0/auditLogs/signIns?$filter=createdDateTime+gt+2019-12-02T12%3a00Z+and+createdDateTime+lt+2019-12-02T20%3a01Z+and+appDisplayName+eq+%27Windows+Sign+In%27+and+deviceDetail%2foperatingSystem+eq+%27Windows%27&orderby=createdDateTime+desc&$skiptoken=0000000000000000000000000000000_7000


https://graph.microsoft.com/v1.0/auditLogs/signIns?$filter=createdDateTime+gt+2019-12-02T12%3a00Z+and+createdDateTime+lt+2019-12-02T20%3a01Z+and+appDisplayName+eq+%27Windows+Sign+In%27+and+deviceDetail%2foperatingSystem+eq+%27Windows%27&orderby=createdDateTime+desc&$skiptoken=000000000000000000000000000b84b_8000


https://graph.microsoft.com/v1.0/auditLogs/signIns?$filter=createdDateTime+gt+2019-12-02T12%3a00Z+and+createdDateTime+lt+2019-12-02T20%3a01Z+and+appDisplayName+eq+%27Windows+Sign+In%27+and+deviceDetail%2foperatingSystem+eq+%27Windows%27&orderby=createdDateTime+desc&$skiptoken=00000000000000000000000000006d7_8000


https://graph.microsoft.com/v1.0/auditLogs/signIns?$filter=createdDateTime+gt+2019-12-02T12%3a00Z+and+createdDateTime+lt+2019-12-02T20%3a01Z+and+appDisplayName+eq+%27Windows+Sign+In%27+and+deviceDetail%2foperatingSystem+eq+%27Windows%27&orderby=createdDateTime+desc&$skiptoken=00000000000000000000000000006d7_8000


https://graph.microsoft.com/v1.0/auditLogs/signIns?$filter=createdDateTime+gt+2019-12-02T12%3a00Z+and+createdDateTime+lt+2019-12-02T20%3a01Z+and+appDisplayName+eq+%27Windows+Sign+In%27+and+deviceDetail%2foperatingSystem+eq+%27Windows%27&orderby=createdDateTime+desc&$skiptoken=00000000000000000000000000006d7_8000


https://graph.microsoft.com/v1.0/auditLogs/signIns?$filter=createdDateTime+gt+2019-12-02T12%3a00Z+and+createdDateTime+lt+2019-12-02T20%3a01Z+and+appDisplayName+eq+%27Windows+Sign+In%27+and+deviceDetail%2foperatingSystem+eq+%27Windows%27&orderby=createdDateTime+desc&$skiptoken=00000000000000000000000000006d7_8000

 

  • Hi

    I use this script to loop over signinlogs, so this should be the same.
    could you test with this. If you can't adapt it to AuditLogs, feel free to reach out!

    $graphApiVersion = "v1.0"
    $User_resource = "auditLogs/signIns?top=1000"
    $uri = "https://graph.microsoft.com/$graphApiVersion/$User_resource"

    $signins = @()
    do{
    try {
    Write-Log "[INFO] - Getting all sign-in logs with uri - $uri"

    $data = (Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get)
    $signins += $data.Value

    Write-Log "[INFO] - Got all sign-in logs for $user"

    $uri = $data.'@odata.nextLink'
    }

    catch {
    $ex = $_.Exception
    $errorResponse = $ex.Response.GetResponseStream()
    $reader = New-Object System.IO.StreamReader($errorResponse)
    $reader.BaseStream.Position = 0
    $reader.DiscardBufferedData()
    $responseBody = $reader.ReadToEnd();
    Write-Log "[ERROR] - Getting sign-in Logs for $user"
    Write-Log "[ERROR] - Response content:`n$responseBody" -f Red
    Write-Log "[ERROR] - Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
    }
    }
    while($uri)
  • Thijs Lecomte's avatar
    Thijs Lecomte
    Bronze Contributor
    Hi

    I use this script to loop over signinlogs, so this should be the same.
    could you test with this. If you can't adapt it to AuditLogs, feel free to reach out!

    $graphApiVersion = "v1.0"
    $User_resource = "auditLogs/signIns?top=1000"
    $uri = "https://graph.microsoft.com/$graphApiVersion/$User_resource"

    $signins = @()
    do{
    try {
    Write-Log "[INFO] - Getting all sign-in logs with uri - $uri"

    $data = (Invoke-RestMethod -Uri $uri -Headers $authToken -Method Get)
    $signins += $data.Value

    Write-Log "[INFO] - Got all sign-in logs for $user"

    $uri = $data.'@odata.nextLink'
    }

    catch {
    $ex = $_.Exception
    $errorResponse = $ex.Response.GetResponseStream()
    $reader = New-Object System.IO.StreamReader($errorResponse)
    $reader.BaseStream.Position = 0
    $reader.DiscardBufferedData()
    $responseBody = $reader.ReadToEnd();
    Write-Log "[ERROR] - Getting sign-in Logs for $user"
    Write-Log "[ERROR] - Response content:`n$responseBody" -f Red
    Write-Log "[ERROR] - Request to $Uri failed with HTTP Status $($ex.Response.StatusCode) $($ex.Response.StatusDescription)"
    }
    }
    while($uri)
    • EntilZha's avatar
      EntilZha
      Iron Contributor

      Thijs Lecomte  thanks for responding.. 

      I changed my loop from While to Do...While and that helped with the issue with paging ... thank you.

       

      Due to the volume of audit logs and the time is takes to page through all the WindowsSignIn logs for 200+ users, I decided it would be quicker if I loop through each user and test to see if they log into their workstation. If they logged into their workstation I capture the most current information.

       

      Thank You Again,

      -Larry

       

       

      • jmakhija's avatar
        jmakhija
        Brass Contributor

        EntilZha 

        Hey Larry - I have a similar requirement to fetch the Windows Sign in Logs for all the users in Last 30days. Can you please share the script which worked for you ?

Resources