User Rating: 5 / 5

Star ActiveStar ActiveStar ActiveStar ActiveStar Active
 

Applies to: PowerShell 5.1.14352.1002, 6.0.0.9, 6.0.0.10.  Updated 9/28/2016 to include Using statement.

With the advent of side-by-side module versions in Windows PowerShell 5.0, the lovely, but obscure ModuleSpecification object has become your new best friend. Use it to make sure that the commands and module that you use are the ones that you intend.

Using ModuleSpecification

Let’s start with an example. I want to get the Expand-Archive command in the Microsoft.PowerShell.Archive module. I can use Get-Command, of course, but when I surround the command name with wildcard characters to force a search, Get-Command returns this:

PS C:\ > Get-Command *Expand-Archive*

CommandType   Name             Version   Source
-----------   ----             -------   ------
Function      Expand-Archive   1.0.0.0   PowerShellLogging
Function      Expand-Archive   1.0.0.0   PowerShellLogging
Function      Expand-Archive   1.0.0.0   Microsoft.PowerShell.Archive
Function      Expand-Archive   0.8.0.0   Microsoft.PowerShell.Archive
Cmdlet        Expand-Archive   3.2.1.0   Pscx

[Tip: To detect name conflicts in your installed modules, use Group-Object.]

When I run ‘Get-Command Expand-Archive’ (without wildcard characters), PowerShell gets the command that actually runs when I type ‘Expand-Archive.’ That command is determined by command precedence and by the order in which PowerShell finds modules, which is, in turn, determined by the order of paths in the $PSModulePath environment variable. That’s complex enough on my own system, but if I’m running shared code in an arbitrary environment, I better make sure that I’m running the correct command.

To get the Expand-Archive command in the PSCX module, I could use a module-qualified name, such as:

Get-Command PSCX\Expand-Archive

 

But, I want to specify both the module name and the version. That’s where the ModuleSpecification object comes in. This command gets the Expand-Archive function in the 1.0.0.0 version of Microsoft.PowerShell.Archive.

Get-Command -Name Expand-Archive `
-FullyQualifiedModule @{ModuleName = 'Microsoft.PowerShell.Archive'; 
                       RequiredVersion = '1.0.0.0'}

 

To distinguish between two different modules with the same name and version, add a GUID value to get the right module.

Get-Command -Name Expand-Archive `
-FullyQualifiedModule @{ModuleName = 'PowerShellLogging'; 
                        RequiredVersion = '1.0.0.0'; 
                        GUID='abc0b34-02de-453a-9726-6bb7b716a63f'}

 

I can even use the invoke/call operator (&) to run a command in a specific version of a specific module.

$myCommand = Get-Command -Name Expand-Archive `
-FullyQualifiedModule @{ModuleName = 'Microsoft.PowerShell.Archive'; 
                        RequiredVersion = '1.0.0.0'}
 
& $myCommand -Path .\Myzip.zip -DestinationPath .\Unzipped

 

Where can I use ModuleSpecification?

One of the most important places to use a ModuleSpecification object is the value of the #Requires -Module parameter.

#Requires -Module @{ModuleName='Pester'; ModuleVersion='3.4.0'}

 

The Using module statement, which imports modules and any PowerShell classes defined in the modules, takes a ModuleSpecification object. The Using statement was introduced in PowerShell 5.0.

using module @{ModuleName='TestClasses'; RequiredVersion='2.1.25'}

 

Also, several cmdlets and functions in PowerShell 5.0 have parameters that take a ModuleSpecification object. They're indicated by a parameter name that begins with FullyQualified.

PS C:\ > .\Get-ParameterType.ps1 -ParameterType ModuleSpecification

CmdletName         Parameter
----------         ---------
Export-PSSession   FullyQualifiedModule
Get-Command        FullyQualifiedModule
Get-Module         FullyQualifiedName
Import-Module      FullyQualifiedName
Import-PSSession   FullyQualifiedModule
Remove-Module      FullyQualifiedName
Save-Help          FullyQualifiedModule
Update-Help        FullyQualifiedModule

(See Get-ParameterType.ps1 on GitHub.)

Import-Module has a FullyQualifiedName parameter that takes a ModuleSpecification object. It also has version parameters that have the same effect, except for the GUID.

PS C:\> (Get-Command Import-Module).ParameterSets.Parameters | where Name -like "*Version" | Sort Name | Select -Property Name, ParameterType -Unique

Name             ParameterType
----             -------------
MaximumVersion   System.String
MinimumVersion   System.Version
RequiredVersion  System.Version

Also, several commands, including the functions in the PowerShellGet module, have version parameters, even though they don’t have parameter that takes a ModuleSpecification object.

Find-Module -Name Pester -RequiredVersion 3.4.1

 

Be aware that Version and ModuleVersion parameters often behave like MinimumVersion, not RequiredVersion. To verify for any given command, check the help.

PS C:\> C:\ps-test\Get-ParameterName.ps1 -ParameterName "*Version"

CmdletName          Parameter      Type
----------          ---------      ----
Find-DscResource    MinimumVersion System.Version
Find-Module         MinimumVersion System.Version
Find-Script         MinimumVersion System.Version
Get-InstalledModule MinimumVersion System.Version
...

Syntax of a ModuleSpecification Object

You can create a ModuleSpecification object from a hash table using the specified keys. PowerShell converts the hash table to a ModuleSpecification object.

Here are the hash table keys:

  • ModuleName <string> (required): Specifies one module name. Enter one name string. Wildcard characters are not suppored.

Select one of the following keys (required):

  • ModuleVersion <String or System.Version>: Specifies the minimum acceptable version.
    @{ModuleName = 'Pester'; ModuleVersion = '3.4.0'}
  • MaximumVersion <String or System.Version>: Specifies the maximum acceptable version.
    @{ModuleName = 'Pester'; MaximumVersion = '3.3.10'}
  • RequiredVersion <String or System.Version>: Specifies the required version.
    @{ModuleName = 'Pester'; RequiredVersion = '3.3.9'}

The GUID key is optional:

  • GUID <String or System.Guid>: Specifies the module GUID.
    @{ModuleName = 'Pester'; ModuleVersion = '3.4.0'; GUID='a699dea5-2c73-4616-a270-1f7abb777e71'}

 

Unfortunately, examining the Microsoft.PowerShell.Commands.ModuleSpecification class separate from its PowerShell implementation is not very helpful.

For example, you can create a ModuleSpecification object with no arguments or with a Name argument, but if you do, all of the other fields are blank, because they are read-only (get, not set).

PS C:\> $ms = [Microsoft.PowerShell.Commands.ModuleSpecification]::New('PSScriptAnalyzer')

PS C:\> $ms | Get-Member

TypeName: Microsoft.PowerShell.Commands.ModuleSpecification

Name             MemberType Definition
----             ---------- ----------
Equals           Method     bool Equals(System.Object obj)
GetHashCode      Method     int GetHashCode()
GetType          Method     type GetType()
ToString         Method     string ToString()
Guid             Property   System.Nullable[guid] Guid {get;}
MaximumVersion   Property   string MaximumVersion {get;}
Name             Property   string Name {get;}
RequiredVersion  Property   version RequiredVersion {get;}
Version Property version    Version {get;}

PS C:\ps-test> $ms.Version = '1.0.0.0'
'Version' is a ReadOnly property.
At line:1 char:1
+ $ms.Version = '1.0.0.0'
+ ~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : PropertyAssignmentException

And, the property names don’t match the required hash table key names.

PS C:\> $ms

Name : PSScriptAnalyzer
Guid :
Version :
MaximumVersion :
RequiredVersion :

So, forget the class and use the rules to create a hash table.

How does it work?

One word of caution. When the core commands use the ModuleSpecification object parameters, they don’t always return what you expect.

For example, in PowerShell 5.1.14352.1002, when you specify a minimum version, Import-Module doesn’t look for the earliest qualified version. Instead, it imports the first instance of that module that it encounters with a version greater than or equal to the minimum.

Also, Get-Command cannot get a command in a non-default version of a module unless that version is already imported into the current session.

Get-Command cannot get a commans in a non-default version of a module unless that version is already imported

We’ll look at these behaviors in a separate article.

June Blender is a technology evangelist at SAPIEN Technologies, Inc. and a Windows PowerShell MVP. You can reach her at This email address is being protected from spambots. You need JavaScript enabled to view it. or follow her on Twitter at @juneb_get_help.

If you have questions about our products, please post in our support forum.
For licensed customers, use the forum associated with your product in our Product Support Forums for Registered Customers.
For users of trial versions, please post in our Trial Software Questions forum.
Copyright © 2017 SAPIEN Technologies, Inc.