3

I get an error intermittently when attempting to connect to Microsoft Power Apps using PowerShell. Does anyone have a suggestions for what needs to be updated to make the [Add-PowerAppsAccount][1] work reliably? You can see all the version numbers and the command process that leads to this error below.

By intermittent I mean that in a specific PowerShell session it will either work or not work, I haven't found a situation that within an active session it will work and not work, it remains consistent in the session (or at least that is what I've experienced so far.) I've found no consistency to when it works or doesn't in a new session. It'll work in a non-admin session and an admin session but then it won't work in a new non-admin or admin session. I've tried running Install-Module first and it'll sometimes work and sometimes it won't.

Name                           Value
----                           -----
PSVersion                      5.1.18362.1171
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.18362.1171
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1


PS C:\> Get-Module -Name Microsoft.PowerApps.Administration.PowerShell

ModuleType Version    Name                                ExportedCommands
---------- -------    ----                                ----------------
Script     2.0.108    Microsoft.PowerApps.Administrati... {Add-AdminPowerAppsSyncUser, Add-AllowedConsentPlans, Add-...


PS C:\> Get-Module -Name Microsoft.PowerApps.PowerShell                                                                                                
ModuleType Version    Name                                ExportedCommands
---------- -------    ----                                ----------------
Script     1.0.20     Microsoft.PowerApps.PowerShell      {Approve-FlowApprovalRequest, Deny-FlowApprovalRequest, Di...


PS C:\> $user ="someone@something.com"
PS C:\> $pass = ConvertTo-SecureString -String "mypassword" -AsPlainText -Force
PS C:\> Add-PowerAppsAccount -Username $user -Password $pass
New-Object : Cannot find an overload for "UserCredential" and the argument count: "2".
At C:\WindowsPowerShell\Modules\Microsoft.PowerApps.Administration.PowerShell\2.0.108\Mi
crosoft.PowerApps.AuthModule.psm1:143 char:23
+ ... redential = New-Object Microsoft.IdentityModel.Clients.ActiveDirector ...
+                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [New-Object], MethodException
    + FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand

PS C:\>
Nicknow
  • 7,064
  • 2
  • 17
  • 34

1 Answers1

3

tldr; I think you've got an assembly conflict, hard-code the assembly version.

Root Cause

At C:\WindowsPowerShell\Modules\Microsoft.PowerApps.Administration.PowerShell\2.0.108\Mi
crosoft.PowerApps.AuthModule.psm1:143 char:23

The error above references this line:

$credential = New-Object Microsoft.IdentityModel.Clients.ActiveDirectory.UserCredential($Username, $Password)

The documentation for Microsoft.IdentityModel.Clients.ActiveDirectory UserCredential class says the constructor expects 0 or 1 arguments... so how does this ever work with 2 arguments?


The module is using the bundled Microsoft.IdentityModel.Clients.ActiveDirectory.dll. We can load this and give it a go:

$path = ('C:\WindowsPowerShell\Modules\Microsoft.PowerApps.Administration.PowerShell\' +
    '2.0.108\Microsoft.IdentityModel.Clients.ActiveDirectory.dll')
[System.Reflection.Assembly]::LoadFrom($path)

$s = ConvertTo-SecureString "test" -AsPlainText -Force
New-Object Microsoft.IdentityModel.Clients.ActiveDirectory.UserCredential('test', $s)

Works no problem. We can find the assembly version by running:

[AppDomain]::CurrentDomain.GetAssemblies() |
    where {$_.FullName -match 'Microsoft.IdentityModel.Clients.ActiveDirectory'} |
    select FullName, Location, Format-List

This gives us the embedded assembly version (2.29.0.1078).
The latest Microsoft.IdentityModel.Clients.ActiveDirectory library version is 5.2.8.0.

I was able to reproduce your error by downloading and extracting the latest package:

# module not installed - downloaded and extracted using 7zip
$path = $env:USERPROFILE + '\Downloads\microsoft.identitymodel.clients.activedirectory.5.2.8' +
    '\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.dll'
[System.Reflection.Assembly]::LoadFrom($path)

$s = ConvertTo-SecureString "test" -AsPlainText -Force
New-Object Microsoft.IdentityModel.Clients.ActiveDirectory.UserCredential('test', $s)

# result
New-Object : Cannot find an overload for "UserCredential" and the argument count: "2".
At line:5 char:1
+ New-Object Microsoft.IdentityModel.Clients.ActiveDirectory.UserCreden ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [New-Object], MethodException
    + FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand

I don't know where your conflicting version is coming from. It could be part of a .NET install, or another package. You can run [AppDomain]::CurrentDomain.GetAssemblies()... from above to see which assemblies are loaded in a session where you get the error message.


Solution

Modify Add-PowerAppsAccount in Microsoft.PowerApps.PowerShell and Microsoft.PowerApps.Administration.PowerShell to use the full assembly-qualified type name:

# Line 143 in  
# C:\WindowsPowerShell\Modules\Microsoft.PowerApps.Administration.PowerShell\2.0.108\Microsoft.PowerApps.AuthModule.psm1
# and
# C:\WindowsPowerShell\Modules\Microsoft.PowerApps.PowerShell\1.0.2.0\Microsoft.PowerApps.AuthModule.psm1
# from
$credential = New-Object Microsoft.IdentityModel.Clients.ActiveDirectory.UserCredential($Username, $Password)
# to
$credential = New-Object -TypeName 'Microsoft.IdentityModel.Clients.ActiveDirectory.UserCredential,Microsoft.IdentityModel.Clients.ActiveDirectory,Version=2.29.0.1078, Culture=neutral, PublicKeyToken=31bf3856ad364e35' -ArgumentList ($Username, $Password)

Notes

  • Changing the code like above and nothing else is bad practice as you're probably aware - probably want to bump the module version. Or better yet, raise a bug - it looks like you already have an account on the right place :)
  • The change above fixes the issue for this specific call... it should really be made to all the UserCredential calls, and even then there might be other calls broken by changes to Microsoft.IdentityModel.Clients.ActiveDirectory
  • Remove the higher Microsoft.IdentityModel.Clients.ActiveDirectory version if you can do so safely (finding the dll location should help to make that call)
  • In my (brief) testing, I found the first assembly loaded tended to be the one used; loading 2.29.0.1079 = things worked even if loading 5.2.8.0 after, and vice versa.
    You might be able to change your code logic to consistently load 2.29.0.1079 first.
  • Maybe you could edit C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe.config to redirect assembly version so that 2.29.0.1079 is always used. (also 32-bit version)
  • Or maybe you can specify the app config during runtime (not tested)
  • Tried adding a full-qualified #requires, didn't work, I assume because it just checks if assembly present.
  • Tried binding redirect via ResolveEventHandler, could not get this to work.
G42
  • 9,230
  • 2
  • 15
  • 33