Posts Tagged ‘RequiredModules’

Shay Levy, ScriptFanatic, wrote about the PowerShellHostVersion on his blog this morning, explaining how it is not a field you should use in your module manifests.

Of course, there’s an exception: if your module is dependent on a specific host. For instance, if you’ve written a module for PoshConsole which exploits the WpfHost display features or the BgHost hotkeys feature … or if you’ve written ISEPack based on the menu and script-editor features of PowerShell ISE), then it makes sense to specify the PowerShellHostName and the PowerShellHostVersion together. However, if you aren’t taking a dependency on a specific host … you should definitely never specify PowerShellHostVersion by itself.

RequiredModules

I’ve got another one that I think you should not use. PowerShell has several ways of specifying prerequisites for your modules, such as assemblies that should be loaded, modules that must be nested, etc. In every case except RequiredModules, PowerShell will actually import those things for you.

That is, if you specify that you RequiredAssemblies = “System.Windows.Forms” it will automatically load it. If you specify NestedModules = “BitsTransfer” it will automatically load that. But if you specify RequiredModules = “BitsTransfer” you’re sore out of luck.

Not only does PowerShell not load them for you, it doesn’t give you all the information you need to load them when it fails. For example, consider that you have a module called “ReallyRequired” and one called “TestModule” which requires ReallyRequired. It’s metadata file is very simple, it looks like this:

@{
    Author="Joel Bennett"
    ModuleToProcess="TestModule.psm1"; ModuleVersion="1.0.0.0"
    RequiredModules=@{ModuleName="ReallyRequired";GUID="84b5ab08-3620-4f72-bffd-44d2a6bb506d"; ModuleVersion="2.5.0.0"
}

Before we try to import it, we might try to check which modules it requires, and since it doesn’t appear to require any, we’ll go ahead and import it:

[1]: get-module -list TestModule | Select RequiredModule

RequiredModules                                                                                          
---------------                                                                                          
{}

[2]: Import-Module TestModule

Import-Module : The required module 'ReallyRequired' is not loaded. Load the module or remove the module from 'RequiredModules' in the file 'C:\Users\Joel\Documents\WindowsPowerShell\Modules\TestModule\TestModule.psd1'.

Apparently, there’s just no way to tell which module(s) you need to preload (or even whether there are any) until you get an error message. You might think you could just write a script to test for that error and then run Import-Module ReallyRequired … but what you don’t know is that after you load your copy of the ReallyRequired module:

[3]: ImportModule TestModule

Import-Module : The required module 'ReallyRequired' with version '2.5.0.0' is not loaded. Load the module or remove the module from 'RequiredModules' in the file 'C:\Users\Joel\Documents\WindowsPowerShell\Modules\TestModule\TestModule.psd1'.

If you’re like me, right now you’re tearing out your hair wondering why the error message didn’t tell you that in the first place. Then you have to go off and try to find a newer version of your RequiredModules. And that’s not even the crazy thing! The crazy thing is, if you find the wrong one, you could still get a message about the GUID not matching next. 8-O

So yeah, RequiredModules is really unlikely to be helpful right now. You’re better off just putting an Import-Module ReallyRequired -version 2.5 -ErrorAction Stop at the top of you script module (although that won’t help you if you’re writing a binary cmdlet module).

Some good news

This is just one of the things that the new PoshCode is being designed to address, so I’ll be blogging more about this shortly, but for now, you can use this function to list RequiredModules:

function Get-RequiredModules {
Param($Path)
$dataSource = Get-Content (Resolve-Path $Path) -delim [char]0

## We are ONLY going to "Tokenize" this to make sure there isn't an extra "}" which could lead to an exploit:
    $null = [Management.Automation.PSParser]::Tokenize("", [ref]$null)  # work around a PowerShell BUG
    $ParseErrors = New-Object "System.Collections.ObjectModel.Collection[System.Management.Automation.PSParseError]"
    $global:tokens = [Management.Automation.PSParser]::Tokenize($dataSource, [ref]$ParseErrors)
    if($ParseErrors.Count -gt 0) {
        $ParseErrors | %{ throw "$($_.Message)`n$File at line:$($_.Token.StartLine) char:$($_.Token.Start)" }
    }
$dataSource = $dataSource -replace "`n# SIG #","`n # SIG #"
return (Invoke-Expression "DATA {`n $dataSource `n}").RequiredModules
}
Reblog this post [with Zemanta]
Search My Content