Monitoring with PowerShell Chapter 3: Monitoring MFA-Server and Office365 MFA status

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"}
Follow me

Kelvin Tegelaar

I am a Microsoft Certified System Engineer working as the CTO of the Managed Services Provider Lime Networks B.V. in the Netherlands. I mostly enjoy automating business processes by deploying PowerShell solutions, but just have a large passion for Microsoft Technology in general.

If you want to contact me directly you can find me on twitter here, or via email: Kelvin {at} limenetworks.nl
Kelvin Tegelaar
Follow me

2 thoughts on “Monitoring with PowerShell Chapter 3: Monitoring MFA-Server and Office365 MFA status

  1. Sara

    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

    Reply
    1. Kelvin Tegelaar Post author

      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.

      Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.