PowerShell 2.0 CTP3: Modules, in Practice.

There’s a bit of a flap going on now around the blogosphere as various vendors, eager to get onto the PowerShell train, are doing the bare minimum to get their product “powershellized.” No one has spent any time reading – or if they did, they weren’t successful in trying to understand it – the Microsoft Command Line Standard.

Modules as Namespaces

That’s right, modules are not as crazy sounding as they seem. They can be used quite simply to just group a load of functions together while allowing easy disambiguation should there be a name collision with another function or cmdlet. It wasn’t always this easy.

Namespaces in PowerShell v1.0

In PowerShell v1.0 it was possible to load two snap-ins that contained identically named commands. This causes PowerShell to spit out an error about ambiguous commands should you try to invoke one. The answer to this was to get the containing snap-in name, and prefix it to the cmdlet name using a backslash as a separator:

$o = Pscx\New-Object Collections.Generic.Dictionary –Of String, Int

As you can see, this lets us call the hypothetical PowerShell Community Extensions version of New-Object to create a generic type. If you want to call the original one, you would have to prefix it like this:

$o = Microsoft.PowerShell.Utility\New-Object Int[] 5

Blech - that’s a bit of a lengthy sentence, but the solution to this is to use an alias. Aliases are found before Cmdlets in the search path:

New-Alias New-Object Microsoft.PowerShell.Utility\New-Object
$o = New-Object Int[] 5 

The reason we need an alias here is because if you just typed the cmdlet without the snap-in prefix, PowerShell v1.0 complains that it doesn’t know which one you want. We forgot to add the mind reader snap-in!

How about functions in PowerShell v1.0? If you dot source a ps1 file that contains functions that already exist in the caller’s session, how do you disambiguate them? Oops! You can’t. They are overwritten. Ouch.

You cannot use the namespace\ prefix for functions in v1 - the support is just not there. However…

Namespaces in PowerShell v2.0

Things are a lot better in this version. Let’s say you have two groups of functions that you use in your business. One for your SharePoint farms, and another set for your Citrix farms. Let’s ignore the perfectly acceptable idea of prefixing the noun to differentiate for the moment and just imagine we have two ps1 files, containing ideally named function with approved verb, simple noun:

# sharepoint functions
function Get-Server {
   # ... gets sharepoint server
}
function Get-Farm {
   # ... gets sharepoint farm
}

And the second script:

# citrix functions
function Get-Server {
    # ... gets citrix server
}
function Get-Farm {
    # ... gets citrix farm
}

If you tried to load these up with via a dot source, one after the other, the functions from the second ps1 would overwrite the first lot. So this is where the wonder world of modules is entered. First step:

After renaming both ps1 files to use the psm1 extension instead, you have created modules of the simplest form. Ignoring the details of how this is deployed (there are several ways – documented on Microsoft and on many blogs, including this one), let's look at how it's loaded and used:

import-module citrix
# Now our functions are loaded. Invocation options:
# assuming no clash, invoke with simple names
get-farm bleh
# load sharepoint module (it also has get-farm)
import-module sharepoint
# ok, sharepoint functions are loaded too, so we have two get-farm commands
# and the last loaded wins, so citrix get-farm is inaccessible through simple syntax, but...
# let's refer specifically to the citrix module function
citrix\get-farm
# and for sharepoint:
sharepoint\get-farm
# and thirdly, if you don't want to use the module qualifier (or "namespace") then you ALSO have the
# choice of applying a prefix on import - this is the primary use case for quick interactive use:
import-module citrix -prefix ctx
# now you can call functions like:
get-ctxfarm
# or even citrix\get-ctxfarm if you so felt like it, but redundant.
# same for sharepoint
import-module sharepoint -prefix sp
get-spfarm

Make sense so far? Unfortunately, as mentioned earlier, in v1.0 the module qualifier (or "namespace") works ONLY for binary commands imported in a Snap-In DLL. Let’s see how that works with get-childitem, a command from one of the preloaded Snap-Ins in PowerShell:

gcm get-childitem | select pssnapin

outputs:

PSSnapIn
--------
Microsoft.PowerShell.Management

Ergo, we invoke it like:
Microsoft.PowerShell.Management\get-childitem
# ... outputs file listing ...

Module Aliasing

Some modules may have longer names that you would be comfortable to type all the time. Thankfully, there is a trick you can do that takes advantage of the fact that modules can be nested. Let’s say you have a module from a vendor that is called something like “Vendor.Division.Product” and it has a cmdlet in it called Get-Thing that happens to clash with another Get-Thing you have loaded. Normally to disambiguate, you have to type:

import-module vendor.division.product
Vendor.Division.Product\Get-Thing

…which is a bit annoying. Instead, wrap the initial import-module statement in a dynamic module where you supply an explicit name for it, and import that instead ;-)

new-module –name product { import-module vendor.division.product } | import-module
# you can now invoke the command like this:
product\get-thing

Any modules that are imported inside a module have their commands automatically exported as if they are part of the root module (unless you control visibility with export-modulemember). Cool, eh?

Have fun!

blog comments powered by Disqus

About the author

Irish, PowerShell MVP, .NET/ASP.NET/SharePoint Developer, Budding Architect. Developer. Montrealer. Opinionated. Montreal, Quebec.

Month List

Page List