PowerShell Modules: A change you should make for V2. (#2)
A few days back I wrote about PowerShell version 2’s ability to confirm whether it should be changing something. Since I was writing something which would some pretty drastic changes , supporting –WhatIf and –Confirm for almost no effort seems like a huge win.
The next thing I wanted to cover was modules. I’ve written some quite large function libraries in PowerShell V1 and I met a few problems which V2 solves by use of modules.
- Collaboration. One script with 100 functions isn’t easy to collaborate on. A module lets you load multiple script files as one, but different people can work on each.
- Loading of formatting and type extensions – these XML files can be loaded from scripts, but when they are loaded more than once things get untidy. Modules can load them along with the code.
- While we’re on code, modules allow the loading of a mixture of script files and DLLs in one go. Module DLLs don’t need to be registered as snap-ins do, so deployment is easier.
- Loading at all: an environment variable defines module paths, and you use
import-module NAME
, educating people on dot sourcing was a pain (and part of the reason for having a single monolithic file) - Discovery : finding all the functions in a script needed some creativity. Now you can just do
get-command –module Name
You can turn a script into a module simply by creating a manifest file, which is a text file with a .PSD extension. At it’s simplest it looks like this
@{ ModuleVersion = "1.0.0"
NestedModules = "Helper.ps1”}
But there is no reason why there should only be one nested module. So to collaborate with different people owning different functions, you just have a long list in the manifest. Here’s the (only slightly edited) manifest for a project which I’m just about to publish.
@{ ModuleVersion = "1.0.0"
NestedModules = "Firewall.ps1" , "Helper.ps1" , "Licensing.ps1",
"network.ps1" , "menu.ps1" , "Remote.ps1",
"WinConfig.ps1", "windowsUpdate.ps1", "WinFeatures.ps1" GUID = "{75c6f959-23a1-4673-8ee9-e61e21ff8381}"
Author = "James O'Neill"
CompanyName = "Microsoft Corporation"
Copyright = "© Microsoft Corporation 2009. All rights reserved."
PowerShellVersion = "2.0"
CLRVersion = "2.0"
FormatsToProcess = "Config.format.ps1xml" }
If my manifest file is named Configurator.psd
, all I need to do is create a configurator
sub-folder in one of the folders pointed to by the Environment variable PSModulePath
then I can just load it with import-module configurator.
Of course different people can be working on Firewall.ps1 and Licensing.ps1. Collaboration problem solved. I can get rid of the functions if I want to with remove-module configurator. And if I reload them the fact that I am loading a .format.ps1XML
file for the second time isn’t a problem as it would be when I load it from a script. No need to dot source, and the functions are discoverable with get-command –module configurator
.
As you can see there are quite a few things you can add to the manifest file over and above the basics – things like FormatsToProcess
and TypesToProcess
, so to make it easier to build the file there is a new-moduleManifest
cmdlet. There is plenty more to read about modules, but for starters look at this post of Oisin’s on Module Manifests.