Warren F bio photo

Warren F

Systems Engineer with a penchant for PowerShell, science, cooking, information security, family, cookies, and the Oxford comma.

Connect

@pscookiemonster LinkedIn Github Stackoverflow TechNet RSS Feed My old blog

My GitHub Repos

AppVReporting BuildHelpers Citrix.NetScaler Git-Presentation InfoBlox Invoke-Parallel PowerShell PSDepend PSDeploy PSDiskPart PSExcel PSHTMLTable PSRabbitMQ PSSlack PSSQLite PSStash RabbitMqTools SecretServer

Overview

Rambling

A while back, someone mentioned it might be fun to document the PowerShell-module-writing-process. This has been done before, but I figure it would be fun to post my own process, from initial idea through publication on the official PowerShellGallery.com site.

I recently discussed the August Scripting Games puzzle on PowerShell.org, which involved querying a web API. It turns out this is a very common need, and many of the modules we write abstract out these APIs into handy PowerShell functions and modules.

We’re going to make the assumption you know what a module is, and that you have some experience writing PowerShell functions. If not, be sure to spend some time learning PowerShell before continuing here!

This post will cover my typical formula for writing a module, using the Stack Exchange API as an example. Feel free to browse the PSStackExchange code on your own.

Why Modules?

Advanced functions will take you far with PowerShell. If you aren’t writing functions today, be sure to start encapsulating your code in these re-usable tools. But… they have their limits. Here are a few reasons you might bundle your advanced functions in a module:

  • Simplify code organization
  • Group related functions together
  • Share state between functions, but not with the user
  • Re-use “helper functions” that you don’t want exposed to the user
  • Improve discoverability: Find-Module MyModule or Get-Command -Module MyModule
  • Simplify distribution: Install-Module MyModule

In our example, we will organize a set of Stack Exchange functions into one module.

This Seems Complicated!

Doing this from scratch might take you a little time. Thankfully, once you write a module or two, you can quickly get started by copying it and tweaking a few files. Don’t be scared off by the length of this post; writing your own modules is well worth spending a few minutes to pick up the basics!

The Ingredients

There are many ways to create a module, from slapping a .psm1 extension onto a file, to compiling a fully fledged binary module from C#. We’ll take a common middle ground here, and use the following ingredients:

  • A Module Manifest. This is a .psd1 file that describes your module. PSStackExchange.psd1
  • A Root Module. In our case, a script module .psm1 file. This is just PowerShell code to run when importing the module. PSStackExchange.psm1
  • Exported (Public) Functions. These are the advanced functions an end user can run from our module. For example, Get-SEQuestion.ps1 or Get-SEObject.ps1
  • Private Functions. These are optional “helper functions” that we want to use in our exported functions, that the end user shouldn’t see. For example, Add-ObjectDetail.ps1 or Join-Parts.ps1
  • Formats. These are optional format.ps1xml formats to help decorate your output, often specified in the module manifest ‘FormatsToProcess’. PSStackExchange.Format.ps1xml
  • Readme. If you’re using GitHub or another common code repository, the Readme.md is a handy front page for your project, written using simple Markdown rather than HTML
  • AppVeyor config. If you’re using a supported version control solution, AppVeyor enables simple and free continuous integration and delivery for open source projects. AppVeyor.yml

We have our ingredients, let’s look at a recipe for a module!

The Recipe

We’re going to do this in a few quick steps:

  • Create a GitHub repository
  • Create the module and scaffolding around it
  • Hook up AppVeyor and publish the module

This might take a few minutes the first time you run through it, but you can borrow and tweak this same scaffolding for each module you write. In fact, you might find or write helper PowerShell modules and tools that simplify this process.

Let’s get to work!

Following the Recipe

There’s no real order to this; depending on what you do or don’t incorporate, don’t feel like you need to follow this to the letter.

Create a GitHub Repository.

This should be pretty straightforward. If you haven’t used GitHub before, the following might help:

All we need to do is:

  • Create an account on GitHub, download GitHub for Windows
  • Create a new repository (We’ll call it PSStackExchange, and pick the MIT license)
  • Clone PSStackExchange using GitHub for Windows

Let’s move on to the most important bit, the module itself.

Create the Module and Scaffolding Around It

Here’s how I typically organize my modules. We’ll use PSStackExchange as an example, substitute this out for your own module!

  • PSStackExchange\
    • en-US\ (or locales of choice)
      • about_PSStackExchange.help.txt
    • Private
      • Join-Parts.ps1
      • Get-SEData.ps1
    • Public
      • Get-SEObject.ps1
      • Search-SEQuestion.ps1
    • lib\ (Not used in this module)
      • Some.Library.dll
    • bin\ (Not used in this module)
      • SomeDependency.exe
    • PSStackExchange.Format.ps1xml
    • PSStackExchange.psd1
    • PSStackExchange.psm1

If we’re going to be adding our project to GitHub or a similar code repository, we add a little more scaffolding:

  • Repository Root
    • PSStackExchange\ (Module folder described above)
    • Tests
      • PSStackExchange.Tests.ps1
      • Appveyor.Pester.ps1
    • README.md
    • AppVeyor.yml

I ran through the following code to get started. Typically I’ll just copy the scaffolding from another module, create a new GUID in the psd1, and tweak other module specific references.

In our case, we have a few Stack Exchange advanced functions that hopefully follow a few best practices, some private helper functions that we don’t want the user to see, and a few other files to cover testing and usability.

In PSStackExchange.psm1 we load our public and private functions. If a module is a work-in-progress, I’ll usually export $Public.Basename to avoid hard coding functions to export in the psd1. Once a module is released, I try to add the public functions to the psd1.

If you’re writing a module, you should consider writing Pester tests for it. It’s quite comforting to have a suite of tests that run automatically after each change you push, rather than assuming the code you write was correct, or attempting to manually test your code after each change. Give it a shot! We include a few superficial tests in PSStackExchange.Tests.ps1.

Lastly, we include some usability features. We add an about_PSStackExchange help topic, we decorate our output with the PSStackExchange.Format.ps1xml file, and we add some notes on how to install and use the module in the README.md.

We’re good to go! Let’s look at how we can publish this module for others to use and improve.

Hook up AppVeyor and Publish the Module

The content of our module is ready to publish. Before we publish this, we’ll enable continuous integration with some handy automated testing through AppVeyor.

First, we set up our project in AppVeyor by adding appveyor.yml to the repository, and adding the GitHub project to our AppVeyor account. We abstract out the calls to Pester in AppVeyor.Pester.ps1, using some ideas from here.

Next, we push the changes we’ve made on our computer up to GitHub. Our code is now published, and AppVeyor will start running a build.

Lastly, we want to publish our module in the PowerShell Gallery, giving end users with PowerShell 5 a simple way to find and install your module. We could hook this up to automatically run in AppVeyor, but that’s a topic for later.

  • Sign on to PowerShellGallery.com with your Microsoft account
  • Get your API key (find it here)
  • Publish your module!

Our module is now live on PowerShell Gallery!

How I Write Modules, Summarized

Whew! That was a long post. Thankfully, most of this stuff can be re-used in each module you write. Let’s review the steps:

  • Create a GitHub repository
  • Create the module and scaffolding around it
  • Hook up AppVeyor and publish the module

The first and last step take a minute or two each. The module and scaffolding around it can be copied and tweaked, which should only take a few minutes. Most of your time will be spent writing the advanced functions for the module.

PSStackExchange

The module is published and ready to use! I’m on another computer with PowerShell 5, I can get up and running with a few lines of code:

Here’s some output from the examples:

Get-SEObject

Search-SEQuestion

Wrapping Up

That’s about it! If you aren’t writing modules already, you should definitely consider it. Looking for further reading? Here are a few references that might come in handy:

Side Note for Vendors

Writing PSStackExchange reminded me how important it is for vendors of enterprise products to provide PowerShell modules that wrap their product’s API. Despite a nice API and decent documentation, writing a feature-poor PowerShell module for this was just as painful as wrapping the Infoblox API.

Vendors: If your competition provides a PowerShell module and you do not, there’s a good chance I’ll push for your competitor’s product. This is a major value-add if you do it right and follow PowerShell conventions.

Cheers!

EDIT July 2016: Updated links to PSStackExchange to link to a specific point in time. The current version of this project may see updates to illustrate things like PSDeploy.