Back to script library
Entra / Microsoft 365 · Users & guests

Report non-MFA sign-ins

A script to show how to use the Microsoft Graph PowerShell SDK to report on non-MFA sign-ins in Microsoft 365.

Connect & set up

Run these once per session. All scopes are read-only unless the script makes changes.

Connect-MgGraph -Scopes "AuditLog.Read.All"

Run it

The main script. Copy it, or download the .ps1 and run it from your console.

param(
[string] $StartDate = (Get-Date $Item.CreatedDateTime).toString('yyyy-MM-ddT00:00:00Z'),
[string] $EndDate = (Get-Date $Item.CreatedDateTime).toString('yyyy-MM-ddT23:59:59Z')
)
Connect-MgGraph -Scopes "AuditLog.Read.All"
$Uri = 'https://graph.microsoft.com/beta/reports/authenticationMethods/userMfaSignInSummary'
[array]$Data = Invoke-MgGraphRequest -Uri $Uri -Method Get -OutputType PSObject | Select-Object -ExpandProperty Value
If ($Data) {
Write-Output "Processing MFA summary sign-in data"
} Else {
Write-Output "No non-MFA sign-ins found" -ForegroundColor Green
Break
}
$Report = [System.Collections.Generic.List[Object]]::new()
ForEach ($Item in $Data) {
$NonMFAUsers = @()
$NonMFAApps = @()
If ($Item.singleFactorSignIns -gt 0) {
Write-Host ("Checking sign-in records from {0} to {1}" -f $StartDate, $EndDate)
[array]$SignInRecords = Get-MgBetaAuditLogSignIn -Filter "createdDateTime gt $StartDate and createdDateTime lt $EndDate and AuthenticationRequirement eq 'singleFactorAuthentication' and status/errorCode eq 0" -Sort "createdDateTime DESC"
If ($SignInRecords.Count -gt 0) {
Write-Host ("Found {0} non-MFA sign-in records for {1}" -f $SignInRecords.Count, (Get-Date $Item.CreatedDateTime -format 'dd-MMM-yyyy'))
[array]$SignInRecords = $SignInRecords | Sort-Object CreatedDate
[array]$NonMFAUsers = $SignInRecords | Group-Object UserPrincipalName -NoElement | Select-Object -ExpandProperty Name
[array]$NonMFAApps = $SignInRecords | Group-Object AppDisplayName -NoElement | Select-Object -ExpandProperty Name
} Else {
Write-Host "No non-MFA sign-in records found" -ForegroundColor Green
}
}
$ReportLine = [PSCustomObject][Ordered]@{
Date = Get-Date $Item.CreatedDateTime -format 'dd-MMM-yyyy'
'Total Signins' = $Item.TotalSignIns
'Non MFA' = $Item.singleFactorSignIns
'MFA Signins' = $Item.multiFactorSignIns
'Non MFA Percentage' = "{0:P2}" -f ($Item.singleFactorSignIns / $Item.TotalSignIns)
'Non MFA Users' = $NonMFAUsers -Join ", "
'Non MFA Apps' = $NonMFAApps -Join ", "
}
$Report.Add($ReportLine)
}
$Report | Out-GridView -Title "Non-MFA Sign-In Report"
Write-Host "Generating an Excel or CSV report for non-MFA sign-ins"
# Generate reports
If (Get-Module ImportExcel -ListAvailable) {
$ExcelGenerated = $True
$ExcelTitle = ("Non-MFA sign-ins for period {0} to {1}" -f $StartDate, $EndDate)
Import-Module ImportExcel -ErrorAction SilentlyContinue
$OutputXLSXFile = ((New-Object -ComObject Shell.Application).Namespace('shell:Downloads').Self.Path) + "\NonMFASignIns.xlsx"
If (Test-Path $OutputXLSXFile) {
Remove-Item $OutputXLSXFile -ErrorAction SilentlyContinue
}
$Report | Export-Excel -Path $OutputXLSXFile -WorksheetName "Non-MFA Sign-Ins" -Title $ExcelTitle -TitleBold -TableName "NonMFASignIns" -AutoSize
} Else {
$OutputCSVFile = ((New-Object -ComObject Shell.Application).Namespace('shell:Downloads').Self.Path) + "\NonMFASignIns.csv"
$Report | Export-Csv -Path $OutputCSVFile -NoTypeInformation -Encoding Utf8
}
If ($ExcelGenerated) {
Write-Host ("An Excel worksheet containing the report data is available in {0}" -f $OutputXLSXFile)
} Else {
Write-Host ("A CSV file containing the report data is available in {0}" -f $OutputCSVFile)
}

Parameters

ParameterDefaultNotes
-StartDate(Get-Date $Item.CreatedDateTime).toString('yyyy-MM-ddT00:00:00Z')Start of the reporting window.
-EndDate(Get-Date $Item.CreatedDateTime).toString('yyyy-MM-ddT23:59:59Z')End of the reporting window.
Attribution