Thomas Lee's collection of random interesting items, views on things, mainly IT related, as well as
the occasional rant

Sunday, January 04, 2009

Modules in PowerShell V2

Introduction

As PowerShell has evolved, there are a number of things that have been needed to be added in order to make it truly enterprise ready. One of the key concepts added into Version 2 of PowerShell is that of a module. A module is some chunk of code that you can import into PowerShell and then use. Once imported, any cmdlets, scripts, or providers can be accessed. Installation of a module is now very simple – just use xcopy.

Modules came originally with CTP2 of PowerShell V2 and are greatly improved in the latest CTP (CTP3) issued late December 2008. Unfortunately, good documentation on how to use modules is scarce: there’s no detailed information in the release notes, no about_module help information, and all the module cmdlets lack auto-help (get-help just dumps raw syntax and offers nothing else in the way of help). Nevertheless, a bit of playing, extensive Googling, and lots of trial and error has yielded a better understanding. For such an important feature, MS has let the side down a bit. But I digress!

Modules vs Snap-ins

In Version 1, we had the Snap-in as the way to add functionality into PowerShell. Teams like Exchange used this to add their own Exchange cmdlets into Exchange 2007. You install a snap-in by copying the necessary files and then updating registry entries to point to those files. Once installed, you could use the Add-PSSnapIn cmdlet to add the functionality of the snap-in into your environment. With a snap-in, the installation routine had to be written (although it could be partly automated) and the user had to run commands to perform the installation. Modules simplify this greatly.

Modules are a replacement for Snap-ins, although V2 will still support V1 type snap-ins. Modules are a much much improved way of creating add-ins and are to be strongly preferred going forward. You can create a module either by using compiled code (as you did in V2), but also by writing them in PowerShell script. You can decorate modules with meta data, including Parameter descriptions, to enable them to be better integrated into a user’s PowerShell experience. By using the Auto-Help feature, the module is self documenting.

Creating a Module with Powershell CTP3

At it’s simplest, a module is just a PowerShell script, contained in a file with a .PSM1 extension. For it to be of any use, you must save this script in a folder below your modules folder which is where PowerShell looks for modules. PowerShell has an environment variable, PSMODULEPATH, which defines where modules are to be found. On my system, I can see this as follows:

As you can see, there are two folders on my system (one a per user, the other a per system). As you can also see, I’m running on an X64 OS (specifically Windows Server 2008 configured as a workstation!). I’m running PowerShell to create this post from within PowerShell Plus. Thus, you might see a different set of values on your system.

To illustrate PowerShell modules, here’s a module based on a script I posted to MDSN and to my PowerShell Scripts blog:

function DeviceInterface {

param ($value)

switch ($value) {

0 {"Other"}

1 {"Unknown"}

3 {"Serial"}

4 {"PS/2"}

5 {"Infrared"}

6 {"HP-HIL"}

7 {"Bus Mouse"}

8 {"ADP (Apple Desktop Bus)"}

160 {"Bus Mouse DB-9"}

161 {"Bus Mouse Micro-DIN"}

162 {"USB"}

}

}

function Handedness {

param ($value)

switch ($value) {

0 {"Unknown"}

1 {"Not Applicable"}

2 {"Right-Handed Operation"}

3 {"Left-Handed Operation"}

}

}

function PointingType {

param ($value)

switch ($value) {

1 {"Other"}

2 {"Unknown"}

3 {"Mouse"}

4 {"Track Ball"}

5 {"Track Point"}

6 {"Glide Point"}

7 {"Touch Pad"}

8 {"Touch Screen"}

9 {"Mouse - Optical Sensor"}

}

}

function get-MouseInfo {

# Now do script stuff

# Get Mouse information

$mouse = Get-WmiObject -Class Win32_PointingDevice

# Display details

"Mouse Information on System: {0}" -f $mouse.systemname

"Description : {0}" -f $mouse.Description

"Device ID : {0}" -f $mouse.DeviceID

"Device Interface : {0}" -f (Deviceinterface($mouse.DeviceInterface))

"Double Speed Threshold : {0}" -f $mouse.DoubleSpeedThreshold

"Handedness : {0}" -f (Handedness($mouse.handedness))

"Hardware Type : {0}" -f $mouse.Hardwaretype

"INF FIle Name : {0}" -f $mouse.InfFileName

"Inf Section : {0}" -f $mouse.InfSection

"Manufacturer : {0}" -f $mouse.Manufacturer

"Name : {0}" -f $mouse.Name

"Number of buttons : {0}" -f $mouse.NumberOfButtons

"PNP Device ID : {0}" -f $mouse.PNPDeviceID

"Pointing Type : {0}" -f (Pointingtype ($mouse.PointingType))

"Quad Speed Threshold : {0}" -f $mouse.QuadSpeedThreshold

"Resolution : {0}" -f $mouse.Resolution

"Sample Rate : {0}" -f $mouse.SampleRate

"Synch : {0}" -f $mouse.Synch

}

# Export just the last function.

Export-ModuleMember Get-Mouseinfo

# End of Script

Using Modules

First, I saved this file to MouseInfo.psm1, and then I stored that file under the folder MouseInfo contained in my per-user module folders (in specific, C:\Users\tfl\Documents\WindowsPowerShell\Modules\MouseInfo). Once this folder and the .PSM1 file are in place, you add this module into PowerShell by calling Import-Module, as follows:

As you can see, importing a module is very simple (once it’s in the right folder). And once you have imported your module, you can use the functions from the module that have been exported. In the MouseInfo module above, there are several helper functions to decrypt the values returned from WMI and which I (as the module’s author) do not wish to expose to a user of the module. The only exported function – in other words the only function that you can use from this module – is Get-MouseInfo. I explicitly export this function by using the Export-ModuleMember cmdlet in line 71 of the module.

Module Cmdlets

Modules are a key part of CTP3. To support modules, there are 7 module related cmdlets included with Powershell V2 CTP3, as follows:

New-Module – creates a new module from a script block.

Import-Module – imports a module from your Modules folder

Export-ModuleMember – notes the functions that a module exports and you can use once you import the module

Get-Module – gets information about modules

Remove-Module – removes a module

New-ModuleManifest – Helps create a new module manifest

Test-ModuleManifest – tests a module manifest

In addition, you can use the Get-Command cmdlet, specifying the module you want to get information from. For example:

I’ll try to blog more details on the module (and manifest) related cmdlets in due course.

Module Manifests

A module manifest is a specially constructed PowerShell script saved in a .PSD1 file. A module manifest is used to define precisely what is contained in a module. A manifest is an optional component, but the PowerShell team strongly advises use of a manifests to better document and describe a module. For more complex modules, e.g. more than just a single .psm1 file, a manifest is probably required.

A Module Manifest is really just a script that creates a hash table. This hash table contains the keys/values that PowerShell uses in managing your module. Since the manifest is pretty simple, it is very easy create one – just use your favourite script edtiro, and add in the values that you need. To make things even simpler, you can use the New-ModuleManifest cmdlet to create a manifest (and then tweak it using your favourite script editor).

Based on the MouseInfo module above, creating a new manifest using New-ModuleManifest is simple:

After a bit of reformatting for publication and tidiness, here’s what the Mouseinfo module manifest looks like:

# Module manifest for module 'Mouseinfo'

# Generated by: Thomas Lee

# Generated on: 1/3/2009

@{

# These modules will be processed when the module manifest is loaded.

NestedModules = ‘Mouseinfo.psm1’

# This GUID is used to uniquely identify this module.

GUID = '94979266-70b4-4243-bef8-6fd87529af69'

# The author of this module.

Author = 'Thomas Lee'

# The company or vendor for this module.

CompanyName = 'PS Partnership'

# The copyright statement for this module.

Copyright = '2009'

# The version of this module.

ModuleVersion = '1.0'

# A description of this module.

Description = 'Cool Module'

# The minimum version of PowerShell needed to use this module.

PowerShellVersion = '2.0'

# The CLR version required to use this module.

CLRVersion = '2.0'

# Functions to export from this manifest.

ExportedFunctions = 'Get-MouseInfo'

# Aliases to export from this manifest.

ExportedAliases = '*'

# Variables to export from this manifest.

ExportedVariables = '*'

# Cmdlets to export from this manifest.

ExportedCmdlets = '*'

# This is a list of other modules that must be loaded before this module.

RequiredModules = @()

# The script files (.ps1) that are loaded before this module.

ScriptsToProcess = @()

# The type files (.ps1xml) loaded by this module.

TypesToProcess = @()

# The format files (.ps1xml) loaded by this module.

FormatsToProcess = @()

# A list of assemblies that must be loaded before this module can work.

RequiredAssemblies = @()

# Lists additional items like icons, etc. that the module will use.

OtherItems = @()

# Module specific private data can be passed via this member.

PrivateData = ''

}

If I had included this manifest in my Mouseinfo module folder, after importing the Module, I’d be able to get better help information about the module (e.g. Get-Module would provide the description and the GUID created by New-ModuleManifest, etc). Manifests are an important aspect of PowerShell modules – I’ll cover them more in a future blog post.

References For More Information on Modules

As noted above, details on modules in CTP3 are hard to come by (and many are out of date) . Even the help text is pretty bare! Some blog posts that discuss modules and may be still be useful include:

With PowerShell V2 CTP3, you create and use modules to add functionality into PowerShell simply and easily. Modules are built using code, script or a combination. A module can be just one .PSM1 file, while more complex modules can be a combination of code, script, and other resources described in a module manifest. You can control what your users see when they import a module by using a module manifest and by using Export-ModuleMember cmdlet. To manage modules, you have a number of module related cmdlets to use. Finally, Module manifests are a key tool to describe modules.