How Module Command Discovery Works in PSv3

As you probably know, you can use Import-Module to manually load additional PowerShell modules and get more cmdlets/functions.

Beginning in PS V3, though, there is an autoloading mechanism. You no longer need to manually load modules. Instead, PowerShell “knows” the cmdlets/functions that reside in all the modules, offers them in its code completion even if the modules haven’t been loaded yet, and once you use one of these commands, it automatically loads the module.

Reason enough to have a deeper look into how this magic works – and why it may be wrong sometimes.

Where are my modules?

Modules can be located anywhere, and you can use Import-Module with a full path or even an UNC path to load modules from whatever location you want. (you cannot load most binary modules across a network though, i.e. from a mapped drive, etc., due to security restrictions in .NET). For PowerShell to automatically pick up modules and their content, modules must be stored in one of the folders found in the environment variable $env:psmodulepath:

$env:PSModulePath-split‘;’

So once a module is stored here, PowerShell can automatically find it and lists the modules when you run:

Get-Module-ListAvailable

Autodetection of cmdlets/functions found in a module is turned on by default. You can manually turn it off

$PSModuleAutoloadingPreference=‘none’

If you do that, though, you now have to manually import any module via Import-Module before you can use any of its cmdlets/functions.

You should be careful with changing autoloading behavior because in PowerShell V3, even a lot of internal PowerShell cmdlets are loaded from additional modules, so when you turn this off, you may no longer be able to use some of the built-in PowerShell cmdlets until you import the appropriate PowerShell module.

How PowerShell detects module content

For PSv3 to be able to do module autoloading, it needs to know the commands contained in a script module. This is not a problem in most commercial binary modules because these define the cmdlets they export in their manifest (*.psd1-file). For script modules that you may have created yourself with pure PowerShell means, your script modules must expose the functions contained. Autoloading works if the script module exposes its functions in one of these ways:

In its .psm1 file, it explicitly loads additional .ps1 files (so it uses $psscriptroot and calls the scripts dot-sourced). PSv3 module autoloading apparently parses these files and looks for functions.

Or, in its .psm1 file it uses Export-ModuleMember to explicitly declare the exported functions

Secret Command Cache

Autoloading also automagically works if the functions are not declared, though. You just need to ?Import-Module? the script module once manually. Once imported one time, PowerShell remembers the functions imported by the module, and the next time you launch PowerShell, it knows the functions and automatically loads the module if needed.

Apparently, PSv3 uses an additional cache that gets filled each time you run Import-Module manually.

Outdated Caches

I had never heard of this cache before, and the cache explains the confusing behavior when script modules are changed after the cache was created – so when you expand or change a module that you used before.

In this case, PSv3 still autocompletes functions that may no longer exist in the module, and fails to autocomplete newly added functions.

So in PowerShell 3.0, as a module author, when you change your module, make sure you explicitly and manually load it at least once with Import-Module. This will update the cache.

Or, you can use the new and widely unknown parameter -Refresh to rebuild the cache like this: