We use both Azure MFA Server to secure our on-site resources, and Office365 MFA for our clients. To make sure we don’t have aggressors changing the MFA settings, or simply administrators forgetting to set-up MFA for clients we make sure that we alert on both.
The issue with monitoring the MFA server is that its a product Microsoft bought later on its in life. As such does not have a PowerShell module included, and it does not conform to the current Common Engineering Criteria.
To solve this, we add a DLL that exposes the functionality that we need, when they get a list of all users(starting with A, until Z).
Add-Type -Path "C:\Program Files\Multi-Factor Authentication Server\pfsvcclientclr.DLL" $problem = [PfSvcClientClr.ConstructResult]::miscError; $main = [PfSvcClientClr.PfSvcClient]::construct([PfSvcClientClr.ConstructTarget]::local, [ref] $problem); $DLLModule = $main.GetType().GetMethod("getInterface").MakeGenericMethod([PfSvcClientClr.IPfMasterComposite]).Invoke($main, $null); $users = $DLLModule.find_users_startsWith("a") foreach($username in $users){ if($DLLModule.get_user_disabledBehavior("$username") -eq "succeed") { $UserOutput += "$($username) is set to succeed authentication without MFA `n" } } if(!$UserOutput) { $UserOutput = "Healthy" }
Next we will monitor the multi-factor authentication on the Office365 side. For this we will use the MSOL module and your partner credentials you’ve generated using this blog. The script can be used both to get information for all clients, or a single client. To demonstrate I’ve added both
All Clients script:
$ApplicationId = "xxxxxx-xxxx-xxxx-xxxx-xxxxxxxx" $ApplicationSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx=" $RefreshToken = "VeryLongRefreshToken" $Tenantname = "YourTenant.onmicrosoft.com" $PasswordToSecureString = $ApplicationSecret | ConvertTo-SecureString -asPlainText -Force $credential = New-Object System.Management.Automation.PSCredential($ApplicationId,$PasswordToSecureString) $aadGraphToken = New-PartnerAccessToken -RefreshToken $refreshToken -Resource https://graph.windows.net -Credential $credential -TenantId $Tenantname $graphToken = New-PartnerAccessToken -RefreshToken $refreshToken -Resource https://graph.microsoft.com -Credential $credential -TenantId $Tenantname Connect-MsolService -AdGraphAccessToken $aadGraphToken.AccessToken -MsGraphAccessToken $graphToken.AccessToken $AllUSers = Get-MsolPartnerContract | Get-MsolUser -all | select DisplayName,UserPrincipalName,@{N="MFA Status"; E={ if( $_.StrongAuthenticationRequirements.State -ne $null) {$_.StrongAuthenticationRequirements.State} else { "Disabled"}}} foreach($User in $allusers | Where-Object{ $_.'MFA Status' -eq "Disabled"}){ $DisabledUsers += "$($User.UserPrincipalName) Has MFA disabled `n" } if(!$DisabledUsers){ $DisabledUsers = "Healthy"}
Single client script
$ApplicationId = "xxxx-xxxxx-xxxxx-xxxxx-xxxxxx" $ApplicationSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxxxx=" $RefreshToken = "VeryLongStringHere" $Tenantname = "YourTenant.onmicrosoft.com" $ClientTenantName = "TheClientsTenant.onmicrosoft.com" $PasswordToSecureString = $ApplicationSecret | ConvertTo-SecureString -asPlainText -Force $credential = New-Object System.Management.Automation.PSCredential($ApplicationId,$PasswordToSecureString) $aadGraphToken = New-PartnerAccessToken -RefreshToken $refreshToken -Resource https://graph.windows.net -Credential $credential -TenantId $Tenantname $graphToken = New-PartnerAccessToken -RefreshToken $refreshToken -Resource https://graph.microsoft.com -Credential $credential -TenantId $Tenantname Connect-MsolService -AdGraphAccessToken $aadGraphToken.AccessToken -MsGraphAccessToken $graphToken.AccessToken $AllUSers = Get-MsolPartnerContract -DomainName $ClientTenantName | Get-MsolUser -all | select DisplayName,UserPrincipalName,@{N="MFA Status"; E={ if( $_.StrongAuthenticationRequirements.State -ne $null) {$_.StrongAuthenticationRequirements.State} else { "Disabled"}}} foreach($User in $allusers | Where-Object{ $_.'MFA Status' -eq "Disabled"}){ $DisabledUsers += "$($User.UserPrincipalName) Has MFA disabled" } if(!$DisabledUsers){ $DisabledUsers = "Healthy"}
Kelvin Tegelaar
If you want to contact me directly you can find me on twitter here, or via email: Kelvin {at} limenetworks.nl
Latest posts by Kelvin Tegelaar (see all)
- Monitoring with PowerShell: Alerting on large Office 365 mailboxes - December 4, 2019
- Monitoring with PowerShell: Alerting on Shodan results - December 1, 2019
- Monitoring with PowerShell:Monitoring OneDrive status for current logged on user! - November 26, 2019
What is inside the dll? Where does it go?
I ran this in powershell on my pc and it failed with errors:
Add-Type -Path “C:\Program Files\Multi-Factor Authentication Server\pfsvcclientclr.DLL”
$problem = [PfSvcClientClr.ConstructResult]::miscError;
$main = [PfSvcClientClr.PfSvcClient]::construct([PfSvcClientClr.ConstructTarget]::local, [ref] $problem);
$DLLModule = $main.GetType().GetMethod(“getInterface”).MakeGenericMethod([PfSvcClientClr.IPfMasterComposite]).Invoke($main, $null);
$users = $DLLModule.find_users_startsWith(“a”)
foreach($username in $users){
if($DLLModule.get_user_disabledBehavior(“$username”) -eq “succeed”) { $UserOutput += “$($username) is set to succeed authentication without MFA `n” }
}
if(!$UserOutput) { $UserOutput = “Healthy” }
Thank you
That script is only if you use Azure Multi Factor Authentication Server. If you do have that installed and want to check the status of it, run the script and then check the contents of the $problem variable. It should help you check where the issue comes from.