PowerShellHostVersion – WTF?

Superstar Shay Levy recently wrote a blog article, Module Manifest Gotcha .  He was exploring the various options that the Module Manifest provides and saw what he thought was a problem with using the PowerShellHostVersion key.

PowerShellHostVersion key

Specifies the minimum version of the Windows PowerShell host program that works with the module.

The “gotcha” here is that there are LOTS of PowerShell Hosts:  PowerShell.exe,  Powershell_ISE.exe, PowerGUI, Sapien’s PrimalScript, PowerShellPlus and others (apologizes in advance to those I forgot to mention).   Shay created a manifest with PowerShellHostVersion = “2.0” and it worked fine with PowerShell.exe but barfed when he imported it into PowerShell_ISE.exe (because that is Version 1.0).

Say concluded:

“To be on the safe side, do not set a value for ‘PowerShellHostVersion’.”

That is almost, but not quite, right.  To understand why we added this seemingly useless feature, you need to take a look at the other keys in the manifest ( HERE ).  There are about 15 keys you can specify there and in general,  you can safely ignore most of them and everything will work fine.  Most of these keys are put there to allow people that have specific requirements and would generate weird errors or unpredictable behavior if their requirements were not met. 

Let’s take an example.  PowerShell_ISE is our new Integrated Scripting Environment that we shipped with PowerShell V2.  We are super excited with PowerShell_ISE but the fact of the matter is that it is a pretty lean tool without many features.  Each of the tools I mentioned above blow the doors of PowerShell_ISE in terms of features (you should check them all out – they are great).  We knew that there would be features that people needed that we were not delivering so we provided an extensibility mechanism, the PowerShell ISE Scripting Object Model.  People do all sorts of wonderful things with this.  For instance, James Brundage created ISE_Pack which is part of the (Free) Windows 7 Resource kit PowerShell Pack.  This delivers a bunch of cool functions that only work with PowerShell_ISE (because they script against the PowerShell ISE Scripting Object). 

So what what happens when you try to import this module into plain old PowerShell.exe where they won’t work because it doesn’t have the PowerShell scripting model:

PS> Import-Module isepack

errrrr….. hmmm….. ooooookaaaaaay….. well that’s embarrassing! 

I just loaded a module that has exactly 0% chance of working in this environment and got no error message.  Let’s see what happens if I try to do one of the functions:

PS> New-ScriptModuleFromCurrentLocation
Split-Path : Cannot bind argument to parameter ‘Path’ because it is null.
At C:\Users\jsnover\Documents\WindowsPowerShell\Modules\isepack\New-ScriptModuleFromCurrentLocation.ps1:16 char:27
+     $location = Split-Path <<<<  $psise.CurrentFile.FullPath
    + CategoryInfo          : InvalidData: (:) [Split-Path], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.SplitPathCo

Split-Path : Cannot bind argument to parameter ‘Path’ because it is null.
At C:\Users\jsnover\Documents\WindowsPowerShell\Modules\isepack\New-ScriptModuleFromCurrentLocation.ps1:17 char:31
+     $locationName = Split-Path <<<<  $location -Leaf
    + CategoryInfo          : InvalidData: (:) [Split-Path], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.SplitPathCo

Join-Path : Cannot bind argument to parameter ‘Path’ because it is null.
At C:\Users\jsnover\Documents\WindowsPowerShell\Modules\isepack\New-ScriptModuleFromCurrentLocation.ps1:22 char:28
+     $modulePath = Join-Path <<<<  $location “$locationName.psm1”
    + CategoryInfo          : InvalidData: (:) [Join-Path], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.JoinPathCom

Test-Path : Cannot bind argument to parameter ‘Path’ because it is null.
At C:\Users\jsnover\Documents\WindowsPowerShell\Modules\isepack\New-ScriptModuleFromCurrentLocation.ps1:23 char:18
+     if (Test-Path <<<<  -ErrorAction SilentlyContinue $modulePath) {
    + CategoryInfo          : InvalidData: (:) [Test-Path], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationErrorNullNotAllowed,Microsoft.PowerShell.Commands.TestPathCom

Exception calling “WriteAllText” with “2” argument(s): “Empty path name is not legal.”
At C:\Users\jsnover\Documents\WindowsPowerShell\Modules\isepack\New-ScriptModuleFromCurrentLocation.ps1:26 char:28
+     [IO.File]::WriteAllText <<<< ($modulePath, $text)
    + CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : DotNetMethodException

You cannot call a method on a null-valued expression.
At C:\Users\jsnover\Documents\WindowsPowerShell\Modules\isepack\New-ScriptModuleFromCurrentLocation.ps1:27 char:42
+     $psise.CurrentPowerShellTab.Files.Add <<<< ($modulePath)
    + CategoryInfo          : InvalidOperation: (Add:String) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull


Ok – now I want to puke. 

What James has done is to highlight the “Formality Range” aspect of PowerShell.  When you are writing code for yourself, it can be as informal as you like.  PowerShell doesn’t get in you way with lots of extra steps or hurdles – you can GO GO GO.  But  … when you want to share it with the world, PowerShell provides incremental features to be more formal and thus more friendly to a wide range of usage scenarios. 

What James should have done is to go into his manifest and change:

    # Name of the Windows PowerShell host required by this module
    PowerShellHostName = ”


    # Name of the Windows PowerShell host required by this module
    PowerShellHostName = ‘Windows PowerShell ISE Host’

Once you do that and try to import the module for Powershell.exe (the console host), you get this error:

PS> Import-Module isepack
Import-Module : The name of the current PowerShell host is: ‘ConsoleHost’. The module ‘C:\Users\jsnover\Documents\Windo
wsPowerShell\Modules\isepack\isepack.psd1′ requires the following PowerShell host: ‘Windows PowerShell ISE Host’.
At line:1 char:5
+ ipmo <<<<  isepack -Force
    + CategoryInfo          : ResourceUnavailable: (C:\Users\jsnove…ck\isepack.psd1:String) [Import-Module], Invalid
    + FullyQualifiedErrorId : Modules_InvalidPowerShellHostName,Microsoft.PowerShell.Commands.ImportModuleCommand

Now that is an error message that even I can understand!

So where are we going with all of this? 

As I mentioned the first version of Powershell_ISE is pretty lean but we might go add more features in a future version.  Let’s imagine that James releases a new version of ISEPACK with a ton of new features that use the new PowerShell_ISE features.  Since he added PowerShellHostName = ‘Windows PowerShell ISE Host’, we know that it will only load in the PowerShell_ISE but what happens if someone tries to load it into V1 of Powershell_ISE?  We are going to be back to the same horrible experience of it loading OK only to fail in some random manner when I try to use one of it’s features. 

But wait wait! 
Wouldn’t it be great if James could specify that his new Module ONLY worked with V3 of PowerShell_ISE? 
Wouldn’t it be great if there was a way to add something like:

    # Name of the Windows PowerShell host required by this module
    PowerShellHostName = ‘Windows PowerShell ISE Host’

    # Minimum version of the Windows PowerShell host required by this module
    PowerShellHostVersion = ‘3.0’

Then if you loaded it into V3, it would work and if you tried to  load it into V1, it would give you a nice error message. 

PS> Import-Module ISEPACK
Import-Module : The current PowerShell host is: ‘Windows PowerShell ISE Host’ (version 2.0). The module ‘C:\Users\jsnover\Documents\WindowsPo
werShell\Modules\ISEPACK\ISEPACK.psd1′ requires a minimum PowerShell host version of ‘3.0’ to execute.
At line:1 char:14
+ Import-Module <<<<  ISEPACK -force
    + CategoryInfo          : ResourceUnavailable: (C:\Users\jsnove…CK\ISEPACK.psd1:String) [Import-Module], InvalidOperationException
    + FullyQualifiedErrorId : Modules_InsufficientPowerShellHostVersion,Microsoft.PowerShell.Commands.ImportModuleCommand

And now you know why we added something silly like PowerShellVersionHost to manifests. 
It’s almost like we are thinking this stuff out.  🙂

Shay- thanks for bringing the topic up.  It gave me an opportunity to explain what is going on and it clearly demonstrates that we need to beef up our documentation.  We delivered SO MANY new functions in V2, that we have a lot of documentation to write.

Experiment!  Enjoy!  Engage!

Jeffrey Snover [MSFT]
Distinguished Engineer
Visit the Windows PowerShell Team blog at:    http://blogs.msdn.com/PowerShell
Visit the Windows PowerShell ScriptCenter at:  http://www.microsoft.com/technet/scriptcenter/hubs/msh.mspx