Today I’m going to show you a new way to build a PowerShell module: I think it’s the fastest, easiest way to turn a few PowerShell script files into a reusable, shareable module.
Here’s the “too long; didn’t read” version: I’ve written a function that will allow you to specify a list of scripts and collect them into a module. Get the script from the end of this post, and you can run this command to build a module named “MyUtilities” within the current directory, moving the ps1 scripts into the new sub-directory:
Or you can run this command to create the “MyUtilities” module in your regular modules folder from all the ps1 or psm1 scripts in this directory or any sub-directories, overwriting any existing module:
If you want greater precision, you can pipe the files in. This command will let you collect only the ps1 and psm1 files in the current directory, but recursively collect ALL files from sub-directories (like language data files, or nested modules), and generate the module containing them.
Once you’ve created a script module using New-ScriptModule, you can run this command to update it with the files that are currently in it’s folder tree, incrementing the version number:
Note: none of these commands move the scripts, they just copy them. However, you can add the -Move switch to move them instead.
It’s important to note that the scripts you put into a module need to define functions (they can’t be pure scripts that you run by file name, or else they’ll execute when you import the module).
What follows is an explanation of what this function does (and steps for doing it by hand). The first step, of course, is to create a “MyUtilities” folder in your PSModulePath, and copy all of the script files into it. Then we can get started with a simple Root Module.
The Root Module (ModuleToProcess)
The root module is the main script or assembly that’s going to be executed/loaded when the module is imported. In our example case, it’s the “MyUtilities.psm1” file (the name matches the folder and ends in psm1). Basically, copy the line below into a new file and save it as “MyUtilities.psm1”:
That simple version just imports every ps1 file in the module folder into the module itself (it’s just like dot-sourcing them, but cleaner to write). In the end, I decided that I wanted to have the list of scripts that would be imported hard-coded in the root module (which can be signed), to make the whole thing harder to tamper with, so in the script below you’ll see I’m building a slightly more complicated root module.
The Module Manifest
I want you to end up with a good reusable and redistributable module, so you should definitely create a module manifest. You don’t really have to set any properties in the manifest except the RootModule and the version (but that defaults to 1.0, so you don’t have to worry about it at first), but I recommend you also pass at least:
Authorparameter (it defaults to your
$Env:UserNamewhich is usually not a full name).
CompanyName(otherwise it comes out “Unknown”, you could at least set it to your blog url).
FileList(which will matter for packaging someday).
Here’s an example of how to use New-ModuleManifest to create one:
$Files = Get-ChildItem $Module\*.* -recurse | Resolve-Path -Relative
New-ModuleManifest -Path "$Module\MyUtilities.psd1" -RootModule "MyUtilities.psm1" -FileList $Files -Author "Joel Bennett" -Functions "*-*" -Aliases "*" -Cmdlets $null -Variables "MyUtilities*" -Types "*Types.ps1xml" -Format "*Formats.ps1xml"
That’s it. We’re done. You can try now try:
Import-Module MyUtilities and
Get-Command -Module MyUtilities to see the results!
Some additional information, and the script.
I want to add some detail here about the conventions I follow which are enforced or encouraged by this function and the modules and manifests that it produces, but as I’ve gone on long enough, I will save that for another post.
I do want to state one caveat: although the function below produces much more complete modules than the few lines above, it’s still basically a beta — it’s only been tested by me (and a couple of people from IRC, thanks guys), so it’s possible that everything is not perfect. That said:
- It supports piping files in to generate the module from, as well as specifying a pattern to match files (and even the -recurse switch).
- It handles nested psm1 modules as well as ps1 scripts, and treats *.Types.ps1xml and *.Formats.ps1xml files appropriately.
- It sets reasonable defaults for the export filters of functions, aliases and variables.
- It supports upgrading modules, updating the file list and incrementing the version number, but won’t overwrite your customizations.
The current version generates a default module which lists specifically the files to import, and then pushes location to the module folder before importing the scripts, one at a time, writing progress messages and error messages as appropriate.
This function is part of the larger PoshCode Packaging module that I am working on. Hopefully you’ll find it an easy way to put a bunch of scripts into a module. It should work in PowerShell 2-4 (although I haven’t been able to test on 2 yet), but let me know if you have any problems (get it from poshcode).