Writing PowerShell Modules in F#

This article will give you step-by-step instructions to author PowerShell Cmdlets and modules in F#.

Image for post
Image for post
My crude combination of the F# and PowerShell logos

PowerShell Cmdlets are an awesome way to write command line tools, especially within Microsoft’s .NET ecosystem. Generally, developing PSModules (the established means of packaging related PowerShell functionalities) is presented as a task that can either be done in C# or the PowerShell scripting language. However, a PowerShell binary module is just a .NET assembly, and F# also compiles to a .NET assembly just like C#. So how do we do it?

Well, before we begin I should note this tutorial is meant to be accessible to any platform with PowerShell Core, and the .NET Core SDK installed. Since both of those tools work on most platforms, this tutorial applies to development on Windows, macOS, and Linux (I’ve tested on both macOS and Windows).

Quick Start

Copy-and-Paste Along

# Create a new solution
dotnet new sln -o DotnetCoreProj
# Add a FSharp class library to that solution
cd DotnetCoreProj
dotnet new classlib -lang 'F#' -o src/MyPSModule
dotnet sln add src/MyPSModule/MyPSModule.fsproj

You will see that several files and directories were automatically created after running the dotnet new command. The file we will focus on later is Library.fs. For now we will focus on finishing setting up the F# project so that we can actually build a PSModule.

cd src/MyPSModule
dotnet add package PowerShellStandard.Library

Here we add the PowerShellStandard.Library package reference to the new F# project. This references the necessary assemblies to build and run PSModules. And that’s all you need to do! The next step is adding our source code. Remember Library.fs? Replace its contents with the following:

This source code is super cool and must be discussed later. For now we will finish out building and running the Cmdlet.

You build the project with dotnet build, which will create a MyPSModule.dll under src/MyPSModule/bin/Debug/netstandard2.0/, however, importing this may not work! This is because an F# assembly needs to reference FSharp.Core.dll in order to work. Thankfully, dotnet publishwill include this necessary assembly. All together now:

dotnet build
dotnet publish
# Lets RUN
Import-Module ./bin/Debug/netstandard2.0/publish/MyPSModule.dll
Get-Foo -Name Bar

The result:

Foo is Bar

NOTE: after running Import-Module on the assembly, you may find it difficult to run dotnet build again. Depending on your environment, replacing the assembly with the newly compiled assembly may not be possible because the file might be locked by the PowerShell instance that has loaded it.

The Code (again)

The best way to understand the code in Library.fs is to see it translated into C#. I give you Library.cs.

Assuming some knowledge of either C# or F#, you can see that this example is almost a direct translation. The namespace and import syntax of each language maps directly aside from being somewhat simpler in F#.

Besides that, a type in F# can map directly to a C# class and the [Foo] decorator syntax in C# just gets additional alligator brackets [<Foo>] in F#. This is incredibly useful when writing Cmdlets, as it is used as a means of defining Cmdlet parameter attributes as well as writing the Cmdlet class definition.

The first piece that is likely to be the least familiar to most is the use of x. on lines 12 and 13. This is a feature of F# that allows a "member function" to specify a unique name of the underlying object on a per-function basis, these are called Self Identifiers. F# can also make use of auto properties just like C# by using the with keyword, which, in my opinion, is somewhat unclear but is widely used in C#.

It’s worth noting that a direct translation such as this will inherently introduce an instance of an object with mutable state, and is therefore not purely functional, but F# was never a purely functional language to begin with. Even so, if you treat a PSCmdlet object as just the entrypoint to your processing, you should be able to carry on as if it was purely functional.

This should provide enough to get things started for writing new PSModules and PSCmdlets in only F#, which I think is super cool. Functional programming rocks!

Written by

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store