Scripts/Get-MFAStatus.ps1
function Get-MFA { <# .SYNOPSIS Retrieves the MFA status for all users. Script inspired by: https://activedirectorypro.com/mfa-status-powershell/ .DESCRIPTION Retrieves the MFA status for all users. .PARAMETER OutputDir OutputDir is the parameter specifying the output directory. Default: Output\MFA .PARAMETER Encoding Encoding is the parameter specifying the encoding of the CSV output file. Default: UTF8 .EXAMPLE Get-MFA Retrieves the MFA status for all users. .EXAMPLE Get-MFA Retrieves the MFA status for all users. .EXAMPLE Get-MFA -Encoding utf32 Retrieves the MFA status for all users and exports the output to a CSV file with UTF-32 encoding. .EXAMPLE Get-MFA -OutputDir C:\Windows\Temp Retrieves the MFA status for all users and saves the output to the C:\Windows\Temp folder. #> [CmdletBinding()] param( [string]$OutputDir = "Output\MFA", [string]$Encoding = "UTF8", [string[]]$UserIds ) $requiredScopes = @("UserAuthenticationMethod.Read.All","User.Read.All") $graphAuth = Get-GraphAuthType -RequiredScopes $RequiredScopes if (!(test-path $OutputDir)) { New-Item -ItemType Directory -Force -Name $OutputDir > $null write-logFile -Message "[INFO] Creating the following directory: $OutputDir" } else { if (Test-Path -Path $OutputDir) { write-LogFile -Message "[INFO] Custom directory set to: $OutputDir" } else { write-Error "[Error] Custom directory invalid: $OutputDir exiting script" -ErrorAction Stop write-LogFile -Message "[Error] Custom directory invalid: $OutputDir exiting script" } } Write-LogFile -Message "[INFO] Running Get-MFA" -Color "Green" Write-LogFile -Message "[INFO] Identifying all the authentication methods utilized within the environment" -Color "Green" $results = @() $allUsers = @() if ($UserIds) { foreach ($userId in $UserIds) { $userUri = "https://graph.microsoft.com/v1.0/users/$userId" try { $user = Invoke-MgGraphRequest -Uri $userUri -Method Get -OutputType PSObject $allUsers += $user } catch { Write-LogFile -Message "[WARNING] User with ID $userId not found" -Color "Yellow" } } } else { $nextLink = "https://graph.microsoft.com/v1.0/users" do { $response = Invoke-MgGraphRequest -Uri $nextLink -Method Get -OutputType PSObject $allUsers += $response.value $nextLink = $response.'@odata.nextLink' } while ($nextLink) } $MFAEmail = 0 $MFAfido2 = 0 $MFAapp = 0 $MFAphone = 0 $MFAsoftwareoath = 0 $MFAhellobusiness = 0 $MFAstatusAmount = 0 foreach ($user in $allUsers) { $userPrinc = $user.userPrincipalName $myObject = [PSCustomObject]@{ user = $userPrinc MFAstatus = "Disabled" # Default to 'Disabled' email = $false fido2 = $false app = $false password = $false phone = $false softwareoath = $false hellobusiness = $false temporaryAccessPass = $false certificateBasedAuthConfiguration = $false } try { $contentUri = "https://graph.microsoft.com/v1.0/users/$($user.id)/authentication/methods" $MFAData = Invoke-MgGraphRequest -Uri $contentUri -Method Get -OutputType PSObject if ($MFAData -and $MFAData.value) { ForEach ($method in $MFAData.value) { $odataType = $method.'@odata.type' Switch ($odataType) { "#microsoft.graph.emailAuthenticationMethod" { $myObject.email = $true $myObject.MFAstatus = "Enabled" } "#microsoft.graph.fido2AuthenticationMethod" { $myObject.fido2 = $true $myObject.MFAstatus = "Enabled" } "#microsoft.graph.microsoftAuthenticatorAuthenticationMethod" { $myObject.app = $true $myObject.MFAstatus = "Enabled" } "#microsoft.graph.passwordAuthenticationMethod" { $myObject.password = $true if ($myObject.MFAstatus -ne "Enabled") { $myObject.MFAstatus = "Disabled" } } "#microsoft.graph.phoneAuthenticationMethod" { $myObject.phone = $true $myObject.MFAstatus = "Enabled" } "#microsoft.graph.softwareOathAuthenticationMethod" { $myObject.softwareoath = $true $myObject.MFAstatus = "Enabled" } "#microsoft.graph.windowsHelloForBusinessAuthenticationMethod" { $myObject.hellobusiness = $true $myObject.MFAstatus = "Enabled" } "#microsoft.graph.temporaryAccessPassAuthenticationMethod" { $myObject.temporaryAccessPass = $true $myObject.MFAstatus = "Enabled" } "#microsoft.graph.certificateBasedAuthConfiguration" { $myObject.certificateBasedAuthConfiguration = $true $myObject.MFAstatus = "Enabled" } Default { Write-Output "Unknown method type: $odataType for user $userPrinc" } } } } else { Write-LogFile -Message "[WARNING] No MFA data found for user $userPrinc" -Color "Yellow" } } catch { Write-LogFile -Message "[ERROR] Error while retrieving the MFA status for $userPrinc | Error: $_ " -Color "Red" } if ($myObject.MFAstatus -eq "Enabled") { $MFAstatusAmount++ } $results += $myObject } $date = Get-Date -Format "yyyyMMddHHmm" $filePath = "$OutputDir\$($date)-MFA-AuthenticationMethods.csv" $results | Export-Csv -Path $filePath -NoTypeInformation -Encoding $Encoding Write-LogFile -Message "[INFO] Output written to $filePath" -Color "Green" $MFAEmail = (Import-Csv -Path "$filePath" -Delimiter "," | Where-Object { $_.email -eq $true } | Measure-Object).Count $MFAfido2 = (Import-Csv -Path "$filePath" -Delimiter "," | Where-Object { $_.fido2 -eq $true } | Measure-Object).Count $MFAapp = (Import-Csv -Path "$filePath" -Delimiter "," | Where-Object { $_.app -eq $true } | Measure-Object).Count $MFAphone = (Import-Csv -Path "$filePath" -Delimiter "," | Where-Object { $_.phone -eq $true } | Measure-Object).Count $MFAsoftwareoath = (Import-Csv -Path "$filePath" -Delimiter "," | Where-Object { $_.softwareoath -eq $true } | Measure-Object).Count $MFAhellobusiness = (Import-Csv -Path "$filePath" -Delimiter "," | Where-Object { $_.hellobusiness -eq $true } | Measure-Object).Count $MFAstatusAmount = (Import-Csv -Path "$filePath" -Delimiter "," | Where-Object { $_.MFAstatus -eq "Enabled" } | Measure-Object).Count $temporaryAccessPassAuthenticationMethod = (Import-Csv -Path "$filePath" -Delimiter "," | Where-Object { $_.temporaryAccessPass -eq $true } | Measure-Object).Count $certificateBasedAuthConfiguration = (Import-Csv -Path "$filePath" -Delimiter "," | Where-Object { $_.certificateBasedAuthConfiguration -eq $true } | Measure-Object).Count $totalUsers = $allUsers.Count Write-Host "$MFAstatusAmount out of $totalUsers users have MFA enabled:" Write-Host " - $MFAEmail x Email" Write-Host " - $MFAfido2 x Fido2" Write-Host " - $MFAapp x Microsoft Authenticator App" Write-Host " - $MFAphone x Phone" Write-Host " - $MFAsoftwareoath x SoftwareOAuth" Write-Host " - $MFAhellobusiness x HelloBusiness" Write-Host " - $temporaryAccessPassAuthenticationMethod x Temporary Access Pass (TAP)" Write-Host " - $certificateBasedAuthConfiguration x Certificate Based Auth Configuration" write-host "" Write-logFile -Message "[INFO] Retrieving the user registration details" $results = @() $filePath = "$OutputDir\$($date)-MFA-UserRegistrationDetails.csv" $nextLink = "https://graph.microsoft.com/v1.0/reports/authenticationMethods/userRegistrationDetails" do { $response = Invoke-MgGraphRequest -Uri $nextLink -Method Get -OutputType PSObject $userDetails = $response.value $nextLink = $response.'@odata.nextLink' ForEach ($detail in $userDetails) { if (!$UserIds -or $UserIds -contains $detail.userPrincipalName) { $myObject = [PSCustomObject]@{} $detail.PSObject.Properties | ForEach-Object { $myObject | Add-Member -Type NoteProperty -Name $_.Name -Value $_.Value } $results += $myObject } } } while ($nextLink) $results | Export-Csv -Path $filePath -NoTypeInformation -Encoding $Encoding Write-LogFile -Message "[INFO] Output written to $filePath" -Color "Green" } |