Published: 08 Mar 2010
By: Manning Publications

This article is taken from the book Windows PowerShell in Action, Second Edition. The author provides an overview of the PowerShell modules, including their roles and terminology. He shows how modules allow packaging collections of PowerShell resources into shareable, reusable units. The author dives into the methods of writing Powershell scripts and converting a script into a module.

Contents [hide]

About the book

This is a sample chapter of the book Windows PowerShell in Action, Second Edition. It has been published with the exclusive permission of Manning.


Written by: Bruce Payette
Pages: 700
Publisher: Manning
ISBN: 9781935182139

Get 30% discount

DotNetSlacker readers can get 30% off the full print book or ebook at www.manning.com using the promo code dns30 at checkout.

Introduction

Before explaining how to turn a script into a module, let's have a quick overview of modules, a new feature in PowerShell version 2. We'll start with an example of just how successful a module system can be. The preeminent example of a network of community-created modules is Perl's CPAN. CPAN stands for Comprehensive Perl Archive Network and is an enormous collection of reusable scripts and modules created by the community that can be easily and effectively searched for existing solutions. This repository can be explored at http:/www.cpan.org and it currently claims the following statistics:

2009-06-21 online since 1995-10-26

5600 MB 230 mirrors

7451 authors 15999 modules

This is an incredible (and enviable) resource for Perl developers. Clearly, such a thing would be of tremendous value to PowerShell.

The traditional shell-language approach to code reuse is to organize the code into functions and scripts and use dot-sourcing to load "libraries" of reusable script code. PowerShell modules provide a more manageable, production-oriented way to package code, and, as is the usual case with PowerShell, modules build on features we've already learned. For example, a PowerShell script module is simply a PowerShell script with a special extension (.psm1) loaded in a special way. We'll cover all of these details further in this article but, first, we need to understand the problem domains the PowerShell module system was designed to address.

Module roles in PowerShell

Modules serve three roles in PowerShell. These roles are listed in table 1.

Table 1: The roles modules play in PowerShell

Role

Description

Configuring the environment

The first role is configuration – packaging a set of functions to configure the environment. This is what you usually use dot-sourcing but modules allow you to do this in a more controlled way.

Code reuse

The second major role for modules is to facilitate the creation of reusable libraries. This is the traditional role of modules in a programming language.

Composing solutions

The final role is the most unusual one. Modules can be used to create solutions – essentially, a domain-specific application. PowerShell modules have the unique characteristic of being nested. In most programming languages, when one module loads another, all of the loaded modules are globally visible. In PowerShell, modules nest. If the user loads module A and module A loads module B, then all the user sees is Module A (at least by default). In fact, sometimes all you'll do in a module is import some other modules and republish a subset of those modules members.

Next, we'll discuss the basic information needed to use PowerShell modules.

Module Basics

The first thing to know is that the module features in PowerShell are exposed through cmdlets, not language keywords. For example, we can get a list of the module commands using the Get-Command command as follows:

Note that in the command name pattern we used wildcards because there are a couple of different types of module cmdlets. These cmdlets and their descriptions are shown in table 2.

Table 2: The cmdlets used for working with modules.

Module CmdLet

Description

Get-Module pattern

Gets a list of the modules currently loaded in memory.

Import-Module

Loads a module into memory and imports the public commands from that module.

Remove-Module

Removes a module from memory and removes the imported members.

Export-ModuleMember

Used to specify the members of a module to export to the user of the module.

New-ModuleManifest

Used to create a new metadata file for a module directory.

Test-ModuleManifest

Run a series of tests on a module manifest validating its contents.

New-Module

Create a new dynamic module.

We can also use the Get-Help command to search for a list of all of the help topics for modules available in the on-line help.

As expected, we see all of the cmdlets listed, but there is also an about_modules help topic that describes modules and how they work in PowerShell. You can use this online help as a quick reference when working in a PowerShell session.

Module concepts and terminology

Before we get too far into modules, there are a number of concepts and definitions we should cover. Along with the names of the cmdlets, table 2 introduced some new terms – "module member" and "module manifest" – and reintroduced a couple of familiar terms – import and export – used in the context of modules. These terms and their definitions are shown in table 3.

Table 3: A glossary of module terminology

Term

Description

Module Member

A module member is any function, variable, or alias defined inside a script. Modules can control which members are visible outside the module by using the Export-ModuleMember cmdlet.

Module Manifest

A module manifest is a PowerShell data file that contains information about the module and controls how the module gets loaded.

Module Type

Just as PowerShell commands can be implemented by different mechanisms like functions and cmdlets, so modules also have a variety of implementation types. PowerShell has three module types – script, binary, and manifest modules.

Nested Module

One module can load another, either procedurally by calling Import-Module or by adding the desired module to the NestedModules element in the module manifest for that module.

Root Module

The root module is the main module file loaded when a module is imported. It's called the root module because it may have associated nested modules.

Imported Member

An imported module member is a function, variable, or alias imported from another module.

Exported Member

An exported member is a module member that has been marked for export. In other words, it's marked to be visible to the caller when the module is imported. Of course, if module foo imports module bar as a nested member, the exported members of bar become the imported members in foo.

Now let's introduce another core module concept.

Modules are single instance objects

An important characteristic of modules is that there is only ever one instance of the module in memory. If a second request is made to load the module, the fact that the module is already loaded will be caught and the module will not be reprocessed (at least as long as the module versions match). There are a couple of reasons for this behavior. Modules can depend on other modules so an application may end up referencing a module multiple times, and we don't want to be reloading all the time because it slows things down. The other reason is that we want to allow for private static resources – bits of data that are reused by the functions exported from a module and aren't discarded, as is normally the case. For example, say we have a module that establishes a connection to a remote computer when the module is loaded. This connection will be used by all of the functions exported from that module. If the functions had to reestablish the connection every time they were called, that would be extremely inefficient. By storing the connection in the module, it will persist across the function calls.

There are two basic types of modules: script modules, which are written using the PowerShell language, and binary modules, written in a compiled language. Both types of modules are simply files on disk.

Now, onto our dish de jour, writing script modules and turning scripts into modules.

Writing script modules

In looking at how to write script modules, we'll also be covering how script modules work in more detail. Let's start by explaining what a script module is. A script module is just a file that contains PowerShell script text with a .psm1 extension instead of a .ps1 extension. In other words, a PowerShell script module is just a script with a different extension.

Execution Policy and Modules

Since a script module is a form of script, it obeys execution policy just like a script. So, before you can load a script module, you'll need to change the execution policy to be remote signed as a minimum.

Is it really as simple as that? Well, almost. Let's walk through an example where we convert a script into a module and see what changes during the process.

A quick review of scripts

We're going to write a short script to work with in this conversion exercise. This script is indented to implement a simple counter. We get the next number from the counter by calling Get-Count and we reset the sequence using the Reset-Count command. Here's what the script looks like:

As we can see, this script defines the two functions we mentioned – Get-Count and Reset-Count. It also defines a number of other things that aren't part of the specification – a helper function setIncrement and two script-level variables – $count and $increment. These variables hold the "state" of the counter. Obviously, just running the script won't be very useful as the commands are defined at the script scope and are removed when the script exits. Instead, we'll dot-source the script to load the script members into our environment.

which creates the elements without showing anything (which is what you want a library to do in most cases.) Let's manually verify that we got what we intended:

The functions are there so let's try them out. We'll start with Get-Count.

Each call to Get-Count returns the next number in the sequence. Now let's use the Reset-Count command.

and call Get-Count to verify that the count has been reset.

OK, great! But what about the other "private" members in the script? Using Get-Command we see that the setIncrement function is also visible.

And we can even call it directly.

Since this function was supposed to be a private implementation detail, the fact that it's publically visible isn't desirable. Likewise, we can also get at the state variables we created.

The problem with this is pretty clear – $count is not a very unique name so the chance of it colliding with a similarly named variable from another script is pretty high. This lack of isolation between scripts makes using dot-sourcing a very fragile way to build libraries.

Finally, let's try and "remove" this script, emulating what we've been doing with Remove-Module. This turns out to be quite complicated. We end up having to write a command that looks like this:

This is necessary because there is no implicit grouping of all of the members created by a script.

Author's Note

It's not actually true that there isn't a way to find out which functions came from a particular file. Another change in PowerShell V2 was to attach the path to the file where a function was defined to the scriptblock of the function. For the counter example we've been discussing, the path might look like:

PS (23) > ${function:Get-Count}.File

C:\wpia_v2\text\chapter09\code\counter.ps1

PS (24) >

This File property helps to figure out where things came from in your environment when you have to debug it. For example, all of the files that were defined in your profile will have the path to your profile in it; functions that were defined in the system profile will have the system profile path, and so forth. Of course this only fixes part of the problem – managing functions. It doesn't deal with variables and aliases.

At this point, it's clear that, while it's possible to build libraries using dot-sourcing, there are a number of problems with this approach. Private implementation details "leak" into the public namespace and the members of a dot-sourced script lose any sort of grouping that allows you to manage them as a whole. Let's turn this script into a module and see how that fixes the problem.

Turning a script into a module

Now let's turn the counter script into a module. We do this simply by changing the extension on the module from .ps1 to .psm1 (where the 'm' rather obviously stands for module.) Let's do this:

(We're using the force parameter here simply to make the example work all the time.) Now we'll try loading the renamed file. Figure 1 shows what you'll probably see when you do this.

Figure 1: This is what happens when we try to directly run a module file. The module file is opened up in the editor associated with the .psm1 extension. Modules aren't commands and can't simply be run. They need to be imported using the Import-Module cmdlet.

This is what happens when we try to directly run a module file. The module file is opened up in the editor associated with the .psm1 extension. Modules aren't commands and can't simply be run. They need to be imported using the Import-Module cmdlet.

The module was not run. The default action is open the file in the editor associated with the extension. This is because module files aren't commands and can't just be run. Instead, we us the Import-Module command to load this module:

Now that we've finally loaded a module, we can try the Get-Module command and see something useful:

Again we'll use the Format-List cmdlet to see the object in more detail.

An important thing to notice is that the Path property stores the full path to the location where the module was loaded. The module type is "script" and the version is 0.0 – the default for a script module. The most important thing to notice are the export lists. We see all of the functions defined in the script module are being exported but no variables are. To verify this, let's use Get-Command to look for all of the functions defined by the script:

We can immediately see one of the benefits of using modules – we can work with sets of related elements as a unit. Now that we've loaded the functions, we have to run them to make sure they work:

and, as before, we see that Get-Count returns the next element in the sequence. Now let's check on the variables used by Get-Count - these were a big problem when we dotted the script.

and neither of them exist. Let's try assigning a value to $count and see if it makes a difference.

As desired, it has no effect on Get-Count. Let's try Reset-Count and verify that it works.

and it does. Now let's look at another issue we had to deal with when using script libraries - how to remove the imported elements. With modules, you can simply remove the module

This will remove the module from the session and remove all imported members so if we try to run Get-Count now, we get an error.

Check out my book to learn how to get more fine-grained control over the things that modules export.

Summary

In this article, we introduced PowerShell modules – a new feature in PowerShell version 2 – and showcased turning scripts into modules. Modules allow us to package collections of PowerShell resources into shareable, reusable units. Using this feature, we can start to build our library of reusable modules in a manageable way.

Looking at the big picture, the important points about using and authoring modules are that:

  1. Modules are manipulated, managed, and imported using cmdlets in PowerShell. Unlike many languages, no special syntax needed. Modules are discovered, both in memory and on disk, using the Get-Module cmdlet. They are loaded with Import-Module and removed from memory with Remove-Module. These three cmdlets are all you need to know if you just want to use modules on your system.
  2. PowerShell uses the $ENV:PSModulePath environment variable to search the file system for modules to load when an unqualified module name is specified. Alternatively, a fully-qualified path name can be used to load a module directly without going through the search process.
  3. There are two basic types of modules: script modules which are written using the PowerShell language and binary modules written in a compiled language. Both types of modules are simply files on disk. No registration process is needed to make a module available for use - we just need to be able to read the file somehow.
  4. Script modules are another form of script with a .psm1 extension. Because they are a type of script, they obey the execution policy setting just like regular scripts.
  5. Script modules execute in their own isolated environment called the module context. A script module also has access to the global environment which is shared across all modules.
Get 30% discount

DotNetSlacker readers can get 30% off the full print book or ebook at www.manning.com using the promo code dns30 at checkout.

<<  Previous Article Continue reading and see our next or previous articles Next Article >>

About Manning Publications

Manning Publication publishes computer books for professionals--programmers, system administrators, designers, architects, managers and others. Our focus is on computing titles at professional levels. We care about the quality of our books. We work with our authors to coax out of them the best writi...

This author has published 33 articles on DotNetSlackers. View other articles or the complete profile here.

Other articles in this category


Android for .NET Developers - Location and Maps
In Windows Phone and iOS getting the current position of the device in terms of latitude and longitu...
Android for .NET Developers - Using Web Views
In this article, I'll show a native app that contains a web-based view. The great news is that HTML ...
Android for .NET Developers - Building a Twitter Client
In this article, I'll discuss the features and capabilities required by an Android application to ta...
Developing a Hello World Java Application and Deploying it in Windows Azure - Part II
In this article we will see the steps involved in deploying the WAR created in the first part of thi...
Ref and Out (The Inside Story)
Knowing the power of ref and out, a developer will certainly make full use of this feature of parame...

You might also be interested in the following related blog posts


Why is ASP.NET encoding &s in script URLs? A tale of looking at entirely the wrong place for a cause to a non-existing bug. read more
Twilight: A Silverlight Twitter Badge read more
Copy Pictures To Folders By Date Taken with Powershell read more
New Release: BGI SCORM LMS Portal v2.5.0 - New Certification Manager with WYSIWYG Certificate Designer - DotNetNuke Integrated read more
Anatomy of a Subtle JSON Vulnerability read more
Utilizing the Microsoft AJAX Framework and ClientAPI to Develop Rich Modules: Part III read more
Utilizing the Microsoft AJAX Framework and ClientAPI to Develop Rich Modules: Part II read more
Creating a Setup Project for IIS Extensions using Visual Studio 2008 read more
Two features from .Net 3.5 SP1 that will benefit DotNetNuke module developers read more
New feature in Subtext 2.0: CSS and JS optimization for skins and mobile support read more
Top
 
 
 

Please login to rate or to leave a comment.