4 comments

MIM 2016: PowerShell Workflow and PowerShell v3

Published on Friday, August 21, 2015 in ,

One of the issues of running FIM 2010 R2 on Windows Server 2012 is calling PowerShell scripts from within FIM Portal Workflows (.NET). It seems the workflow code is running .NET 3.5 but uses PowerShell 2.0. When we started migrating our FIM 2010 to MIM 2016 (on Server 2012 R2) we ran into the same issues. This is the .NET code that has been running fine on Windows 2008 R2 for years without any issues:

RunspaceConfiguration config = RunspaceConfiguration.Create();
Runspace runspace = RunspaceFactory.CreateRunspace(config);
runspace.Open();
psh = PowerShell.Create();
psh.Runspace = runspace;
psh.AddCommand(this.PSCmdlet)

psh.Invoke();

And one the scripts that was executed contained code like this:

001
002
003
004
005
006
007
008

doSomething.ps1

#region Parameters
Param([string]$UserName,[string]$Department)
#endregion Parameters
Import-Module ActiveDirectory
Get-Aduser 
...

Now when porting that same logic to our MIM 2016 running on Windows Server 2016 we saw that our get-AD* cmdlets returned nothing. After some investigation we found the following error was triggered when running import-module Active Directory: The 'C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules\ActiveDirectory\ActiveDirectory.psd1' module cannot be imported because its manifest contains one or more members that are not valid. The valid manifest members are ('ModuleToProcess', 'NestedModules', 'GUID', 'Author', 'CompanyName', 'Copyright', 'ModuleVersion', 'Description', 'PowerShellVersion', 'PowerShellHostName', 'PowerShellHostVersion', 'CLRVersion', 'DotNetFrameworkVersion', 'ProcessorArchitecture', 'RequiredModules', 'TypesToProcess', 'FormatsToProcess', 'ScriptsToProcess', 'PrivateData', 'RequiredAssemblies', 'ModuleList', 'FileList', 'FunctionsToExport', 'VariablesToExport', 'AliasesToExport', 'CmdletsToExport'). Remove the members that are not valid ('HelpInfoUri'), then try to import the module again.

There are various topics online that cover this exact issue.

It seems some PowerShell modules are hardwired to require PowerShell v3. I came across the following suggestion a few times, but it scares me a bit as with my (limited?) knowledge of .NET It’s hard to estimate what impact this might have on FIM. The suggestion was to add the following to the Microsoft.ResourceManagement.Service.exe.config file.

001
002
003
004

<startup>
 <supportedRuntime version="v4.0"/>
 <supportedRuntime version="v2.0.50727"/>
</startup>

I found some approaches using a script that calls another script, but I wanted to avoid this. So I came up with the following approach to update the workflow itself:

PowerShellProcessInstance instance = new PowerShellProcessInstance(new Version(3, 0), null, null, false);
Runspace runspace = RunspaceFactory.CreateOutOfProcessRunspace(new TypeTable(new string[0]), instance);

Source:http://stackoverflow.com/questions/22383915/how-to-powershell-2-or-3-when-creating-runspace

The PowerShellProcessInstance is class that is available in System.Management.Automation. And that’s part of PowerShell itself. I tried various DLLS, but either they didn’t know the class or they resulted in the following error when building my .NET project:

The primary reference "System.Management.Automation" could not be resolved because it has an indirect dependency on the .NET Framework assembly "System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" which has a higher version "4.0.0.0" than the version "2.0.0.0" in the current target framework.    FODJ.FIM.Workflow.ActivityLibrary

My project is configured to build for .NET 3.5, but If I’m not mistaken .NET 3.5 use CLR 2.0. Whilst .net 4/4.5 use CLR 4.0 (see .NET Framework Versions and Dependencies). So I guess this route isn’t going to work after all. Back to the drawing board. As I only got a number of scripts to call like this, I decided to go back the wrapper script approach:

The script containing the logic to be executed:

001
002
003
004
005
006
007
008

doSomething.script.ps1

#region Parameters
Param([string]$UserName,[string]$Department)
#endregion Parameters
Import-Module ActiveDirectory
Get-Aduser 
...

As you can see I prepended .script to the .ps1 extension. And here’s my wrapper script. This is the one that is called from the FIM/MIM Workflow:

001
002
003
004
005
006

doSomething.ps1

Param
([string]$UserName,[string]$Department)
$script = $myinvocation.invocationName.replace(".ps1",".script.ps1")
powershell -version 3.0 -file $script -UserName $username -Department 
$Department

There are some things to note: the param line is just a copy past from the base script. And I just specify them as parameters again when calling the base script. I had been looking for a way to use unbound parameters, e.g. the calling workflow says –username … –department … and the wrapper script just passes it over. That would have allowed me to have a generic wrapper script. I got pretty close to getting it to work, but I kept running into issues. In the end I just decided to go for KISS.

Note: if you want to capture errors like the one I show from “import-module Active Directory”, just use the $error variable. You can use it like this. Saving it to disk is just one example. Typically you could integrate this with your logging function.

001
002
003

$error.Clear
Import-Module ActiveDirectory
$error | out-file c:\users\public\error.txt

Related Posts

4 Response to MIM 2016: PowerShell Workflow and PowerShell v3

02 March, 2016 04:45
This comment has been removed by the author.
02 March, 2016 04:46

This really helped. The basic idea is to call a script via a MIM workflow that in turn calls your powershell script and at the same time, runs it in version 3.0. The wrapper was a great workaround.

Thank you :)

04 March, 2016 06:33

Thanks for the thumbs up!

15 November, 2016 22:22

THANKS for the tip...made my life a lot easier.

Add Your Comment