Continuously deploying my PowerShell Modules

Page content

Continuously building and deploying new versions of my modules to internal repositories (or PowerShell Gallery) is something I really like. That way I can just push my code and let the automation handle the rest. Here are some learnings I’ve made.

Deploy on tag

Firstly, I usually only trigger a deploy-workflow on tags, this way I can manually decide when to do a release and what version it will have without relying on any complex methods. My modules are usually quite small and we’re rarely more than a handful of people working on each one of them so I’m trying to keep things as simple as possible.

Creating a tag with git is really simple, I just do this:

git tag -a 1.4.2 -m 'Release 1.4.2'

I annotate each tag with my desired version, then by using gitversion I can parse the semantic version from my tag and I’ll always get the correct version. Gitversion also keeps track of build-numbers so I can build and test on each commit (even if I won’t deploy the result to a repository). You can read more about gitversion here: https://gitversion.readthedocs.io/en/latest/

Build my module

When working on my module I like to keep my code divided into files different files. First of all I’m usually having two subfolders, Private and Public. Each one contains ps1-files with private or public functions. Within these folder I usually create one ps1 for each set or group of similar functions. This is just how I’ve gotten used to work and you might have another way of doing things and that’s fine. However I’ve found that having many files sometimes makes the loading-time of my module really slow so I’ve come up with a way to combine all files into one large psm1-file upon build. I just get the content of all of them and output that to .\bin\MyModule\MyModule.psm1. This is an example of my module folder before build:

ModuleStructure

I also often forget to export my public functions. In the beginning I had one ps1 file per function and used the filename to find functions to export, but this gave me way to many files to work with so instead I now use AST to parse functions from all ps1-files in my Public folder instead. This way I can add an Export-ModuleMember with the correct parameters at the end of my psm1 file.

A snippet of the code I use to combine all files in the public folder to a scriptblock and parse function and alias names is this:

$PublicScriptBlock = [ScriptBlock]::Create((Get-ChildItem -Path $PublicFolder | Get-Content | Out-String))
$PublicFunctions = $PublicScriptBlock.Ast.FindAll({ $args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst]},$false).Name
$PublicAlias = $PublicScriptBlock.Ast.FindAll({ $args[0] -is [System.Management.Automation.Language.ParamBlockAst] },$true).Attributes.Where{$_.TypeName.FullName -eq 'alias'}.PositionalArguments.Value

Update Module Manifest

Since I use gitversion, my new version is found by this command:

$gitversion = gitversion | ConvertFrom-Json
if($gitversion.BuildMetaData) {
$ModuleVersion = $gitversion.MajorMinorPatch + '.' + $gitversion.BuildMetaData
}
else {
$ModuleVersion = $gitversion.MajorMinorPatch
}

If my latest commit is a tag named 1.4.2, this will be my new version. However if I add three commits after that tag, my new version will be 1.4.2.3.

Next up I need to update my ModuleManifest. For this, Update-ModuleManifest is great! If you don’t have it get it by installing the module PowerShellGet from the gallery (https://www.powershellgallery.com/packages/PowerShellGet). If you already have it, make sure you are using the latest version.

Update-ModuleManifest -Path '.\bin\MyModule\MyModule.psd1' -ModuleVersion $ModuleVersion