Using a PowerShell Requires Statement to Efficiently Load PowerCLI Modules

Since the release of PowerShell version 3, the ability to automatically load modules has been an exciting feature for those using the console. Prior to this, getting access to the cmdlets and functions buried inside of a module (or snappin) needed to be explicitly imported. However, not all modules will support this feature. It’s up to the module owner to determine which module commands are exported. To see this, I’ll use the Pester module as an example.

Get-Module -ListAvailable Pester
ModuleType Version    Name                                ExportedCommands
---------- -------    ----                                ----------------
Script     3.4.3      Pester                              {Describe, Context, It, Should...}

You can see a few of the ExportedCommands are shown. To see all of them, you could try a Format-List or just pick apart the hashtable itself.

(Get-Module -ListAvailable Pester).ExportedCommands
Key                           Value                        
---                           -----                        
Describe                      Describe                     
Context                       Context                      
It                            It                           
Should                        Should                       
Mock                          Mock                         
Assert-MockCalled             Assert-MockCalled            
Assert-VerifiableMocks        Assert-VerifiableMocks       
New-Fixture                   New-Fixture                  
Get-TestDriveItem             Get-TestDriveItem
etc..

This is why I can type Invoke-Pester from anywhere in the console session and it will autocomplete via Intellisense and subsequently load the Pester module when called. The neat thing is that loading a module isn’t just limited to calling a command. You could also use, for example, the Get-Help cmdlet as shown below. Notice how the first use of Get-Module does not show the Pester module loaded, but after requesting the help file, the Pester module is now loaded. Fairly snazzy.

PS> Get-Module
ModuleType Version    Name                                ExportedCommands                                                                                                                                                                           
---------- -------    ----                                ----------------                                                                                                                                                                           
Script     1.0.0.0    ISE                                 {Get-IseSnippet, Import-IseSnippet, New-IseSnippet}                                                                                                                                        
Binary     2.6.2.2    ISESteroids                         {Add-SteroidsContextMenuCommand, Add-SteroidsEditorTabCommand, Add-SteroidsLicense, Add-SteroidsTextChange...}                                                                             
Manifest   3.1.0.0    Microsoft.PowerShell.Management     {Add-Computer, Add-Content, Checkpoint-Computer, Clear-Content...}                                                                                                                         
Manifest   3.0.0.0    Microsoft.PowerShell.Security       {ConvertFrom-SecureString, ConvertTo-SecureString, Get-Acl, Get-AuthenticodeSignature...}                                                                                                  
Manifest   3.1.0.0    Microsoft.PowerShell.Utility        {Add-Member, Add-Type, Clear-Variable, Compare-Object...}                                                                                                                                  

PS> Get-Help Invoke-Pester
NAME
    Invoke-Pester
<snip>

PS> Get-Module
ModuleType Version    Name                                ExportedCommands                                                                                                                                                                           
---------- -------    ----                                ----------------                                                                                                                                                                           
Binary     1.0.0.0    CimCmdlets                          {Export-BinaryMiLog, Get-CimAssociatedInstance, Get-CimClass, Get-CimInstance...}                                                                                                          
Script     1.0.0.0    ISE                                 {Get-IseSnippet, Import-IseSnippet, New-IseSnippet}                                                                                                                                        
Binary     2.6.2.2    ISESteroids                         {Add-SteroidsContextMenuCommand, Add-SteroidsEditorTabCommand, Add-SteroidsLicense, Add-SteroidsTextChange...}                                                                             
Manifest   3.1.0.0    Microsoft.PowerShell.Management     {Add-Computer, Add-Content, Checkpoint-Computer, Clear-Content...}                                                                                                                         
Manifest   3.0.0.0    Microsoft.PowerShell.Security       {ConvertFrom-SecureString, ConvertTo-SecureString, Get-Acl, Get-AuthenticodeSignature...}                                                                                                  
Manifest   3.1.0.0    Microsoft.PowerShell.Utility        {Add-Member, Add-Type, Clear-Variable, Compare-Object...}                                                                                                                                  
Script     3.4.3      Pester                              {AfterAll, AfterEach, Assert-MockCalled, Assert-VerifiableMocks...}

VMware’s PowerCLI Modules

As stated earlier, not all modules support this. One example is the VMware PowerCLI 6.5 module. I bring this up as I’ve had a number of folks report this behavior to me on various forums and comments threads. My guess is that as VMware continues to swap over from a rigid snappin architecture to a more open module architecture this will be remediated. However, for now there are hardly any commands being exported by any of their modules.

PS> Get-Module -ListAvailable "VMware*"
    Directory: C:\Program Files (x86)\VMware\Infrastructure\PowerCLI\Modules
ModuleType Version    Name                                ExportedCommands
---------- -------    ----                                ----------------
Binary     6.0.0.0    VMware.DeployAutomation
Binary     6.0.0.0    VMware.ImageBuilder
Binary     6.5.0.4... VMware.VimAutomation.Cis.Core
Binary     6.5.0.4... VMware.VimAutomation.Cloud
Manifest   6.5.0.4... VMware.VimAutomation.Common
Binary     6.5.0.2... VMware.VimAutomation.Core           HookGetViewAutoCompleter
Binary     6.0.0.0    VMware.VimAutomation.HA
Binary     7.0.2.4... VMware.VimAutomation.HorizonView
Binary     6.5.0.4... VMware.VimAutomation.License
Binary     6.5.0.4... VMware.VimAutomation.PCloud
Manifest   6.5.0.4... VMware.VimAutomation.Sdk            Get-PSVersion
Binary     6.5.0.4... VMware.VimAutomation.Storage
Binary     6.5.0.4... VMware.VimAutomation.Vds
Binary     6.5.0.4... VMware.VimAutomation.vROps
Binary     6.0.0.0    VMware.VumAutomation

If you’ve ever tried entering Connect-VIServer without first implicitly loading the module, you’ll know the pain of seeing the “The term ‘Connect-VIServer’ is not recognized as the name of a cmdlet, function, script file, or operable program” error. I’m assuming there’s just something not quite right with the export code in their module at this time. Of course, you can always implicitly load the module in your code. Kyle Ruddy describes the process on the VMware PowerCLI blog along with a sample script to update your existing scripts. Kudos for sharing!

The other approach is to use a requires statement.

The Requires Statement

A practice that I highly advise making habit is using a requires statement at the top of your scripts and functions. It’s a handy way to define the requirements necessary for code to execute. Here’s some examples of what can be examined:

#Requires -Version <N>[.<n>]   
#Requires -PSSnapin <PSSnapin-Name> [-Version <N>[.<n>]]  
#Requires -Modules { <Module-Name> | <Hashtable> }   
#Requires -ShellId <ShellId>  
#Requires -RunAsAdministrator

For this blog post, I’ve written a tiny sample script to show how to require the VMware module before code will execute without having to lay in extra logic or potentially stumble upon a module that’s already loaded and throw an error.

#requires -Modules VMware.VimAutomation.Core
Write-Host "Loaded!"

What an exciting sample script! Its job in life is to load the VMware.VimAutomation.Core module and then write the word “Loaded!” onto the console. Here’s what happens:

PS> Get-Module
ModuleType Version    Name                                ExportedCommands
---------- -------    ----                                ----------------
Manifest   3.1.0.0    Microsoft.PowerShell.Management     {Add-Computer, Add-Content, Checkpoint-Computer, Clear-Con
Manifest   3.0.0.0    Microsoft.PowerShell.Security       {ConvertFrom-SecureString, ConvertTo-SecureString, Get-Acl
Manifest   3.1.0.0    Microsoft.PowerShell.Utility        {Add-Member, Add-Type, Clear-Variable, Compare-Object...}
Script     1.2        PSReadline                          {Get-PSReadlineKeyHandler, Get-PSReadlineOption, Remove-PS

PS> .\SampleRequires.ps1
Loaded!
PS> Get-Module
ModuleType Version    Name                                ExportedCommands
---------- -------    ----                                ----------------
Script     0.0        Initialize-VMware_VimAutomation_Cis
Manifest   3.1.0.0    Microsoft.PowerShell.Management     {Add-Computer, Add-Content, Checkpoint-Computer, Clear-Con
Manifest   3.0.0.0    Microsoft.PowerShell.Security       {ConvertFrom-SecureString, ConvertTo-SecureString, Get-Acl
Manifest   3.1.0.0    Microsoft.PowerShell.Utility        {Add-Member, Add-Type, Clear-Variable, Compare-Object...}
Script     1.2        PSReadline                          {Get-PSReadlineKeyHandler, Get-PSReadlineOption, Remove-PS
Binary     6.5.0.4... VMware.VimAutomation.Cis.Core       {Connect-CisServer, Disconnect-CisServer, Get-CisService}
Manifest   6.5.0.4... VMware.VimAutomation.Common
Binary     6.5.0.2... VMware.VimAutomation.Core           {Add-PassthroughDevice, Add-VirtualSwitchPhysicalNetworkAd
Manifest   6.5.0.4... VMware.VimAutomation.Sdk            Get-PSVersion

If the module doesn’t exist, you’ll instead get an error. To show this, I’ve changed the requirement to find a non-existent module named “SpongeBob.”

.\SampleRequires.ps1 : The script 'SampleRequires.ps1' cannot be run because the following modules that are specified by the "#requires" statements of the script are missing: SpongeBob.

In the case of PowerCLI, this will work for versions 6.0 and later due to the conversion from a pssnapin to a partial module (in 6.0) and full module (in 6.5). Enjoy!