Sign-Ins: Recent Conditional Access Policy Failures

Retrieving recent Conditional Access Policy sign-in failures.

Failure: The sign-in satisfied the user and application condition of at least one Conditional Access policy and grant controls are either not satisfied or set to block access.

For a user delegated sign-in (to read CA Policy) they must be also be one of the following:

  • Global Administrator
  • Global Reader
  • Security Administrator
  • Security Reader
  • Conditional Access Administrator

PowerShell

Connect-MgGraph -Scopes @('AuditLog.Read.All', 'Directory.Read.All')
$EXCLUDE_MFA_CHALLENGES = $true
$caPolicySignInFailures = @{}
$since = (Get-MgAuditLogSignIn -Top 1).CreatedDateTime
$sinceAsStr = $since.ToString('yyyy-MM-ddTHH:mm:ssZ')
$fileOutputSuffix = $since.ToLocalTime().ToString('yyyy-MM-ddTHH-mm-ss')
$filter = "conditionalAccessStatus eq 'failure'"
$filter += " and isInteractive eq true and createdDateTime gt $sinceAsStr"
while ($true) {
    $params = @{
        'All'      = $true;
        'Filter'   = $filter;
        'PageSize' = '999';
    }
    $signIns = Get-MgAuditLogSignIn @params
    Clear-Host
    # Graph appears to not respect seconds in the filter, so a further check here is used.
    $signIns = $signIns | Where-Object { $_.CreatedDateTime -gt $since }
    $signIns = $signIns | Sort-Object CreatedDateTime
    if ($signIns.Count -ne 0 ) {
        $since = ($signIns | Select-Object -Last 1).CreatedDateTime
        $sinceAsStr = $since.ToString('yyyy-MM-ddTHH:mm:ssZ')
    }
    foreach ($signIn in $signIns) {
        $failedPolicies = $signIn.AppliedConditionalAccessPolicies `
        | Where-Object { $_.Result -eq 'failure' }
        foreach ($failedPolicy in $failedPolicies) {
            $signInDetail = [PSCustomObject]@{
                UserPrincipalName     = $signIn.UserPrincipalName
                AppDisplayName        = $signIn.AppDisplayName
                CreatedDateTime       = $signIn.CreatedDateTime
                ErrorCode             = $signIn.Status.ErrorCode
                FailureReason         = $signIn.Status.FailureReason
                AdditionalDetails     = $signIn.Status.AdditionalDetails
                DeviceBrowser         = $signIn.DeviceDetail.Browser
                DeviceId              = $signIn.DeviceDetail.DeviceId
                DeviceDisplayName     = $signIn.DeviceDetail.DisplayName
                DeviceIsCompliant     = $signIn.DeviceDetail.IsCompliant
                DeviceIsManaged       = $signIn.DeviceDetail.IsManaged
                DeviceOperatingSystem = $signIn.DeviceDetail.OperatingSystem
                DeviceTrustType       = $signIn.DeviceDetail.TrustType
            }
            # 50072 UserStrongAuthEnrollmentRequiredInterrupt
            # 50074 UserStrongAuthClientAuthNRequiredInterrupt
            # 50076 UserStrongAuthClientAuthNRequired
            # 500121 The user didn't complete the MFA prompt.
            if ($EXCLUDE_MFA_CHALLENGES -and $signIn.Status.ErrorCode -in @('50074','50076')) {
            }
            else {
                if ($caPolicySignInFailures.ContainsKey($failedPolicy.Id)) {
                    $item = $caPolicySignInFailures[$failedPolicy.Id]
                    $item.FailureCount += 1
                    $item.FailureSignIns += $signInDetail
        
                    $sanitisedFilename = $failedPolicy.DisplayName.Replace('/', '')
                    $outFile = "./$sanitisedFilename-$fileOutputSuffix.csv"
                    $signInDetail `
                    | ConvertTo-Csv -NoTypeInformation `
                    | Select-Object -Skip 1 `
                    | Out-File $outFile -Append
                }
                else {
                    $detail = [PSCustomObject]@{
                        PolicyName     = $failedPolicy.DisplayName
                        PolicyId       = $failedPolicy.Id
                        FailureCount   = 1
                        FailureSignIns = @($signInDetail)
                    }
                    $caPolicySignInFailures.Add($failedPolicy.Id, $detail)
        
                    $sanitisedFilename = $failedPolicy.DisplayName.Replace('/', '')
                    $outFile = "./$sanitisedFilename-$fileOutputSuffix.csv"
                    $signInDetail `
                    | ConvertTo-Csv -NoTypeInformation `
                    | Out-File $outFile
                }
            }
        }
    }
    $caPolicySignInFailures.GetEnumerator() `
    | Select-Object -ExpandProperty Value `
    | Select-Object PolicyName, FailureCount | Format-Table
    Write-Host -ForegroundColor Yellow "Refreshing in 5 seconds. To stop press CTRL + C"
    Start-Sleep -Seconds 5
}

Dependencies

Microsoft Graph SDK for PowerShell

Install-Module Microsoft.Graph -AllowClobber -Force

Connect-MgGraph

Using the Microsoft Graph Command Line Tools Enterprise Application:

Connect-MgGraph -Scopes @('')

Using an existing Access Token:

Connect-MgGraph -AccessToken (ConvertTo-SecureString 'ey..' -AsPlainText -Force)

Using an Application Registration (Platform: Mobile and desktop applications, redirect http://localhost):

Connect-MgGraph -ClientId 'abc..' -TenantId 'abc..'

Using a ClientId and Secret (Password):

$tenantId = ''
$clientId = ''
$secret = ConvertTo-SecureString '' -AsPlainText -Force
$secretCredential = New-Object System.Management.Automation.PSCredential ($clientId, $secret)
$params = @{
    'SecretCredential' = $secretCredential
    'TenantId'         = $tenantId
}
Connect-MgGraph @params