August 6th, 2018

PowerShell Standard Library: Build single module that works across Windows PowerShell and PowerShell Core

Steve Lee
Principal Software Engineer Manager

This is the first of a series of blog posts that will help you take advantage of a new NuGet package PowerShellStandard Library 5.1.0. This package allows developers to create modules that are portable between Windows PowerShell 5.1 and PowerShell Core 6.0. This means that you can create PowerShell modules that run on Windows, Linux, and macOS with a single binary!

The version of PowerShell Standard Library indicates the lowest version of PowerShell that it is compatible with. The community promise is that it is always forward compatible. So a module built against PowerShell Standard Library v3 is compatible with Windows PowerShell v3, v4, v5.1, PowerShell Core 6, and the upcoming PowerShell Core 6.1. Compatibility is achieved by providing a subset of the APIs common across all those versions of PowerShell. This reference assembly is the equivalent to a header file for C/C++ where it has the APIs defined, but no implementation. During runtime, the module would use the version of System.Management.Automation.dll that is used by the PowerShell host.

Creating a PowerShell Module

In this post, I’ll walk through the steps for creating a simple C# module with a single cmdlet. I will also be using the DotNet CLI tools for creating everything I need.

Installing the PowerShell Standard Module Template

First, we can leverage a new template that we published for DotNet CLI, but we need to install it first:

PS> dotnet new -i Microsoft.PowerShell.Standard.Module.Template
  Restoring packages for C:\Users\James\.templateengine\dotnetcli\v2.1.302\scratch\restore.csproj...
  Installing Microsoft.PowerShell.Standard.Module.Template 0.1.3.
  Generating MSBuild file C:\Users\James\.templateengine\dotnetcli\v2.1.302\scratch\obj\restore.csproj.nuget.g.props.
  Generating MSBuild file C:\Users\James\.templateengine\dotnetcli\v2.1.302\scratch\obj\restore.csproj.nuget.g.targets.
  Restore completed in 1.66 sec for C:\Users\James\.templateengine\dotnetcli\v2.1.302\scratch\restore.csproj.

Usage: new [options]

Options:
  -h, --help          Displays help for this command.
  -l, --list          Lists templates containing the specified name. If no name is specified, lists all templates.
  -n, --name          The name for the output being created. If no name is specified, the name of the current directory is used.
  -o, --output        Location to place the generated output.
  -i, --install       Installs a source or a template pack.
  -u, --uninstall     Uninstalls a source or a template pack.
  --nuget-source      Specifies a NuGet source to use during install.
  --type              Filters templates based on available types. Predefined values are "project", "item" or "other".
  --force             Forces content to be generated even if it would change existing files.
  -lang, --language   Filters templates based on language and specifies the language of the template to create.


Templates                                         Short Name         Language          Tags                             
----------------------------------------------------------------------------------------------------------------------------
Console Application                               console            [C#], F#, VB      Common/Console                   
Class library                                     classlib           [C#], F#, VB      Common/Library                   
PowerShell Standard Module                        psmodule           [C#]              Library/PowerShell/Module    
...

A new template called psmodule is now available making it easy to start a new C# based PowerShell module. Any issues, feedback, or suggestions for this template should be opened in the PowerShell Standard repo.

Creating a new project

We need to create a location for our new project and then use the template to create the project:

PS> mkdir myModule
Directory: C:\Users\James
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 8/3/2018 2:41 PM myModule
PS> cd myModule
PS C:\Users\James\myModule> dotnet new psmodule
The template "PowerShell Standard Module" was created successfully.

Processing post-creation actions...
Running 'dotnet restore' on C:\Users\James\myModule\myModule.csproj...
  Restoring packages for C:\Users\James\myModule\myModule.csproj...
  Installing PowerShellStandard.Library 5.1.0-preview-06.
  Generating MSBuild file C:\Users\James\myModule\obj\myModule.csproj.nuget.g.props.
  Generating MSBuild file C:\Users\James\myModule\obj\myModule.csproj.nuget.g.targets.
  Restore completed in 1.76 sec for C:\Users\James\myModule\myModule.csproj.

Restore succeeded.

You can see that the dotnet cli has created a source file and .csproj file for my project:

PS C:\Users\James\myModule> dir


    Directory: C:\Users\James\myModule


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
d-----         8/3/2018   1:48 PM                obj
-a----         8/3/2018   1:48 PM            376 myModule.csproj
-a----         8/3/2018   1:48 PM           1698 TestSampleCmdletCommand.cs

The sample from the template demonstrates a simple cmdlet with two parameters that outputs results with a custom class.

Building the module

Building the sample is code is easy with DotNet CLI:

PS C:\Users\James\myModule> dotnet build
Microsoft (R) Build Engine version 15.7.179.6572 for .NET Core
Copyright (C) Microsoft Corporation. All rights reserved.

  Restore completed in 76.85 ms for C:\Users\James\myModule\myModule.csproj.
  myModule -> C:\Users\James\myModule\bin\Debug\netstandard2.0\myModule.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:05.40

Testing the built module

To test this sample module, we just need to import it. We can check to see what it supports and try running it:

PS C:\Users\James\myModule> ipmo .\bin\Debug\netstandard2.0\myModule.dll
PS C:\Users\James\myModule> Test-SampleCmdlet -?

NAME
    Test-SampleCmdlet

SYNTAX
    Test-SampleCmdlet [-FavoriteNumber] <int> [[-FavoritePet] {Cat | Dog | Horse}] [<CommonParameters>]


ALIASES
    None


REMARKS
    None



PS C:\Users\James\myModule> Test-SampleCmdlet -FavoriteNumber 7 -FavoritePet Cat

FavoriteNumber FavoritePet
-------------- -----------
             7 Cat

This sample is pretty simple as it’s intended to just show how to get started on writing a PowerShell module from scratch. The important point is that using PowerShell Standard Library, this assembly can be used in both PowerShell Core 6 as well as Windows PowerShell. This sample will even work on Windows, Linux, or macOS without any changes.

In the next part of this series, I’ll cover other aspects of PowerShell module authoring such as module manifests and writing Pester tests.

James Truher Senior Software Engineer PowerShell Team

Category
PowerShell

Author

Steve Lee
Principal Software Engineer Manager

Principal Software Engineer Manager PowerShell 7, PowerShellGet, PSScriptAnalyzer, VSCode-PowerShell extension, PowerShellEditorServices, etc...

4 comments

Discussion is closed. Login to edit/delete existing comments.

  • Alexis Coles

    Yeah could you fix your code snippets, I can’t make any sense of any of them with all the markup in there.
    tried in chrome, edge and internet explorer, didn’t make a difference…

    • Zachary Alexander

      Fixed, thanks!

  • Andrew Stanton

    Those first few examples have some formatting issues. 

    • Zachary Alexander

      Ah. Yes. Sorry, we just migrated and there are a few hiccups. Filing a bug. 
      edit: fixed, thanks!