Managing Azure MFA Server with PowerShell

Do you have Azure MFA Server on-premises and want to manage it through PowerShell? Keep reading! If you’re using Azure MFA (completely in the Cloud), this is not for you. Sorry.

Many of our customers that have invested in our on-premises Azure MFA Server have complained about the lack of PowerShell support in the product. And I must agree; although our 2009 Common Engineering Criteria (CEC) (which today are nowhere to be found) more or less stated that all Microsoft products should have PowerShell support, Azure MFA Server lacks this support. Furthermore, no investments are made to add PowerShell support to the product as far as I know.

But there are still two distinct ways to manage your Azure MFA Servers with PowerShell. In this article, I will discuss both.

Using the Azure MFA Server Web SDK

Azure MFA Server comes with a Web SDK that you can install on any Azure MFA Server in the environment. It exposes a SOAP interface to many features and functions of Azure MFA Server. It can also be used to integrate third party solutions to support MFA. Using PowerShell, you can easily access and use this SOAP interface. If you haven’t setup the Web SDK already, here’s a great article on how to do it!

First, let’s declare where our SDK is installed, and provide a username and password. (The user needs to be in the PhoneFactor Admins group). You can also user certificate-based authentication for the user.

$MfaUrl = 'https://pfa.domain.com/MultiFactorAuthWebServiceSdk/'

$MfaUser = "domain\user"

$MfaPassword = "P@zzw0rd!"

Next thing we need to do, is convert our plain-text password to a Secure String and create a proxy to the SOAP interface. This proxy will allow us to execute all the methods available.

$MfaSecurePassword = ConvertTo-SecureString -AsPlainText -Force -String $MfaPassword

$Credentials = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $MfaUser, $MfaSecurePassword

$Proxy = New-WebServiceProxy -Uri $MfaUrl -Credential $Credentials

$Namespace = $Proxy.GetType().Namespace

The $Namespace variable is just a convenience, as we will see later.

After executing the code we have so far, let’s inspect the $Proxy variable. It’s the proxy to the SOAP interface, and all exposed methods are now available. You can check them out by piping the $Proxy to Get-Member:

$Proxy | Get-Member

You’ll see there’s a lot of stuff available. For this article, I am interested in adding a user in Azure MFA Server. So let’s dive in further to the Add User method;

$Proxy | Get-Member AddUser

In the output, we can see how we can call this method:

PS C:\> $Proxy | Get-Member AddUser

TypeName: Microsoft.PowerShell.Commands.NewWebserviceProxy.AutogeneratedTypes.WebServiceProxy1_MultiFactorAuthWebServiceSdk_.PfWsSdk

Name MemberType Definition

---- ---------- ----------

AddUser Method bool AddUser(string username, Microsoft.PowerShell.Commands.NewWebserviceProxy.AutogeneratedTypes.WebServiceProxy1_MultiFactorAuthWebServiceSdk_.AddUserSettings addUserSettings, bool send...

Apparently, the method expects a parameter of type Microsoft.PowerShell.Commands.NewWebserviceProxy.AutogeneratedTypes.WebServiceProxy1_MultiFactorAuthWebServiceSdk_.
AddUserSettings
.

Let’s create an object of that type in PowerShell (here can see how the $Namespace variable saves us a ton of typing):

$AddUserSettings = New-Object ($Namespace + ".AddUserSettings");

After creating the object, we can set the properties:

$AddUserSettings.CountryCode = "31";

$AddUserSettings.EmailAddress = "user@domain.com";

$AddUserSettings.Enabled = $true;

$AddUserSettings.FirstName = "Firstname";

$AddUserSettings.LastName = "Lastname";

$AddUserSettings.Phone = "612345678";

When the object is completed, we need to create an object of type Microsoft.PowerShell.Commands.NewWebserviceProxy.AutogeneratedTypes.WebServiceProxy1_MultiFactorAuthWebServiceSdk_.Error (which is required in the method as a referenced parameter), and we can call the method:

$ErrorCode = New-Object ($Namespace + ".Error");

$MfaResult = $Proxy.AddUser("user@domain.com", $AddUserSettings, $true, $null, [ref]$ErrorCode);

That’s it! Now just check $MfaResult to see if we’re all good.

Here is what we created:


$MfaUrl = 'https://pfa.domain.com/MultiFactorAuthWebServiceSdk/'

$MfaUser = "domain\user"

$MfaPassword = "P@zzw0rd!"

$MfaSecurePassword = ConvertTo-SecureString -AsPlainText -Force -String $MfaPassword

$Credentials = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList $MfaUser, $MfaSecurePassword

$Proxy = New-WebServiceProxy -Uri $MfaUrl -Credential $Credentials

$Namespace = $Proxy.GetType().Namespace

$AddUserSettings = New-Object ($Namespace + ".AddUserSettings");

$AddUserSettings.CountryCode = "31";

$AddUserSettings.EmailAddress = "user@domain.com";

$AddUserSettings.Enabled = $true;

$AddUserSettings.FirstName = "Firstname";

$AddUserSettings.LastName = "Lastname";

$AddUserSettings.Phone = "612345678";

$ErrorCode = New-Object ($Namespace + ".Error");

$MfaResult = $Proxy.AddUser($User.UserPrincipalName, $AddUserSettings, $true, $null, [ref]$ErrorCode);

if ($MfaResult)

{

Write-Host "User added.";

}

else

{

Write-Error "Could not add user. Error Code: $($ErrorCode.Code), Error Description: $($ErrorCode.Description)";

}


So what about the good and the bad of this solution?

+ Supported.
+ Runs remotely.
- Limited functionality available through the SOAP interface.

Using the Azure MFA Server Client Runtime DLL

Okay, this is not for the faint of heart. Using the provided scripts and DLL referenced in this part of the article might be unsupported and could potentially break your Azure MFA Server if used improprely.

Both the Multi-Factor Authentication Server UI (MultiFactorAuthUI.exe) and the Azure MFA Server Web SDK use the same DLL’s to ‘talk’ to the Azure MFA Server; one of which is pfsvcclientclr.DLL. And hey, whatever they can do, we can do from PowerShell! Drawback is that the communcations channel that this DLL uses requires the code to run on the MFA Server itself. (That’s also why the UI and the Web SDK only run on the Azure MFA Server actually.)

Side note; If you want to use C# or something to manage your Azure MFA Server, you can reference this DLL as well. But there are additional DLL’s on the system that have nice wrappers around the hundreds of methods available through the pfsvcclientclr.dll. Perhaps sometimes I will write an article on that as well… (Do comment if you want me to.)

So, let’s first reference the DLL in our script;

Add-Type -Path "C:\Program Files\Multi-Factor Authentication Server\pfsvcclientclr.DLL"

After referencing the DLL, we can access the methods and properties the DLL exposes. Unfortunately, it’s not too easy to find out what to do with this DLL. So I’ll give you the first steps to start using the magic of the DLL:

$problem = [PfSvcClientClr.ConstructResult]::miscError;

$main = [PfSvcClientClr.PfSvcClient]::construct([PfSvcClientClr.ConstructTarget]::local, [ref] $problem);

$master = $main.GetType().GetMethod("getInterface").MakeGenericMethod([PfSvcClientClr.IPfMasterComposite]).Invoke($main, $null);

Should there be any problems loading this stuff, the $problem variable should have some information what went wrong.

From this point on, we have a $master variable which serves as our proxy to the MFA Server. Using the Get-Member function, we can see which methods are now available to us:

$master | Get-Member

Wow. That’s a lot of stuff! 1.647 Methods! Actually, anything you can do through the MultiFactorAuthUI can be done through this DLL. Let’s try to do something that cannot be done through the SOAP interface; let’s make a user a User Portal Administrator, and allow him to Add Users and Block/Unblock Users:

$master.set_user_isAdmin("user@domain.com", $true);

$master.set_user_adminAddUser("user@domain.com", $true);

$master.set_user_adminBlockUnblockUser("user@domain.com", $true);

That’s not too hard! So let’s put the username in a variable and see what we got;


Add-Type -Path "C:\Program Files\Multi-Factor Authentication Server\pfsvcclientclr.DLL"

$username = "user@domain.com";

$problem = [PfSvcClientClr.ConstructResult]::miscError;

$main = [PfSvcClientClr.PfSvcClient]::construct([PfSvcClientClr.ConstructTarget]::local, [ref] $problem);

$master = $main.GetType().GetMethod("getInterface").MakeGenericMethod([PfSvcClientClr.IPfMasterComposite]).Invoke($main, $null);

$master.set_user_isAdmin($username, $true);

$master.set_user_adminAddUser($username, $true);

$master.set_user_adminBlockUnblockUser($username, $true);


One of the drawbacks is that this solution only runs on MFA Servers. But using System Center Service Management Automation PowerShell Script Runbooks, this should not be a real problem.

So what about the good and the bad of this solution?

+ Unlimited functionality.
- Potentially Unsupported.
- Runs only locally on the MFA Server.

Conclusion

Using the two options provided, you should be able to do anything you want against your Azure MFA Servers using PowerShell. You decide what you want to use! There’s a lot of methods available to you, so spend some time figuring out what you can use. Keep in mind that the Web SDK is your best option to integrate solutions. For example if you want to use Username & One-Time Password authentication in an application. The DLL mentioned in this article is primarily for managing the environment, not for integration.

Don’t call me if anything goes wrong… Some if the methods mentioned in this article might be unsupported… But hey; you can manage your Azure MFA Server with PowerShell!