Develop and distribute Powershell module

Develop and distribute Powershell module

In spite of the fact that in the Internet there tons of various guides and instructions on how to create your own Powershell module, after all, even the official documentation does not give full understanding, how to correctly create module with flexibility\scalability for easy addition of functions.

I spent several hours to learn and read some articles, and, as usual, came to my own approach, which seems to me adequately comfortable, elastic, and which I want to share with you all now.

Come up with the name

It's important :-). Later with this name you will live quite long, so think about the name, which will be pleasant to see for you everyday =).

In my case I decided to follow the approach: <customer_code\customer_name>.<project_code>.Powershell.

For the purposes of this guide, let's use the module name My.OwnModule.Powershell.

Create a prefix for function names

If you're developing/supporting/using multiple modules at a time (for example, for various companies or teams), sometimes you will use the same function in all modules (just because that's convenient). And, if you will start to use these modules, you will face some name conflicts during import. That's why there is a reason why you should use some unique prefix for functions names.

In our example I will use the MyOwn prefix. For example, standard name of famous cmdlet New-Item will transform to the New-MyOwnItem.

Install prerequisites

Except Powershell itself, you will require command line tool nuget. It's needed to pack the module into nuget-package and distribute it amongst other computers or users.

Create folders structure

Except of root folder, that will be named the same as the module, you will need the initial folder structure.

D:.
└───My.OwnModule.Powershell
    ├───images
    ├───private
    ├───public
    │   └───common
    └───_bin

  • images. There will be an icon file for a package, which in theory would be seen in some repository like Artifactory.
  • private. It's a folder for a scripts, that should be used for service needs only. For examples, to read file from the disk with error handling.
  • public. Here you will store script files with functions that will be accessible directly by module users. Here you can group your scripts in sub-folders for convenience only.
    • public -> common. This is an example of sub-folder. The directory is used exclusively for structure and grouping of files purposes. Anything inside public folder are going to be read recursively.
  • _bin. Nuget tool will put compiled packages here.

Create and populate necessary files

Surely you must create files, which, strictly speaking, convert a set of Powershell scripts into Powershell module.

D:.
│   LICENSE
│   PSScriptAnalyzerSettings.psd1
│   README.md

└───My.OwnModule.Powershell
    │   local-build.ps1
    │   local-install.ps1
    │   My.OwnModule.Powershell.nuspec
    │   My.OwnModule.Powershell.psd1
    │   My.OwnModule.Powershell.psm1
    │
    ├───images
    │       icon.png
    │
    ├───private
    │       _stub.ps1
    │
    ├───public
    │   └───common
    │           Read-MyOwnJsonFile.ps1
    │
    └───_bin

We're interested in files, which are located in the module root folder, private and public. Others - are the service files of the git-repository. Below you will find contents of each file with comments.

Writing service files of Powershell module

My.OwnModule.Powershell.nuspec

This is require for the nuget tool to generate the package.

<?xml version="1.0" encoding="utf-8"?>
<package >
  <metadata>
    <id>My.OwnModule.Powershell</id>
    <version>0.0.2</version>
    <authors>This email address is being protected from spambots. You need JavaScript enabled to view it.</authors>
    <requireLicenseAcceptance>false</requireLicenseAcceptance>
    <icon>images\icon.png</icon>
    <description>
      There is no any description available at this time. Sorry for that.
    </description>
    <releaseNotes>Initial release.</releaseNotes>
    <tags>PSEdition_Core PSEdition_Desktop Linux Mac PSModule PSIncludes_Cmdlet</tags>
  </metadata>
</package>

My.OwnModule.Powershell.psd1

This file - is a module manifest, a description of the module in the terms of Powershell itself. There are different details about the author, functions that module supports and so on.

Notice the line 11 and FunctionsToExport parameter. These are functions, which will be accessible to the users of your module. That's why, when adding new functions to the module, remember to add them here.

@{
    RootModule           = "My.OwnModule.Powershell.psm1"
    ModuleVersion        = "0.0.2"
    CompatiblePSEditions = "Desktop", "Core"
    GUID                 = "8f37ba7b-be77-4b6e-8ceb-d30c0c328674"
    Author               = "This email address is being protected from spambots. You need JavaScript enabled to view it."
    PowerShellVersion    = "7.0"

    ScriptsToProcess     = @();
       
    FunctionsToExport    = @(
        "Read-MyOwnJsonFile"
    );
    CmdletsToExport      = "*"
    VariablesToExport    = "*"
    AliasesToExport      = "*"
}

My.OwnModule.Powershell.psm1

This file is a script, that executes when you run (or the system runs) Import-Module. It's a main file in the module. Generally speaking, this file represent the module itself (meaning, you can create the Powershell module without the psd1 file, but not recommended, of course).

#Requires -Version 7.0


$public  = @( Get-ChildItem -Path $PSScriptRoot\public\*.ps1 -Recurse; );
$private = @( Get-ChildItem -Path $PSScriptRoot\private\*.ps1 -Recurse; );


# Dot source the files
foreach ($import in @($public + $private))
{
    try
    {
        Write-Verbose "Importing '$($import.FullName)'.";
        . $import.FullName;
    }
    catch
    {
        Write-Error -Message "Failed to import function $($import.fullname): $_";
    }
}


Export-ModuleMember -Function $public.Basename;

_stub.ps1

I put this file-stub, so that script from the psm1 file is able to normally read folder without possible problems. But in general, it is completely empty.

Read-MyOwnJsonFile.ps1

This is a file of the cmdlet, that we're providing with our module. Notice its name - Read-MyOwnJsonFile, I mean here that I'm using prefix for the functions names.

local-build.ps1

This file is not directly part of the Powershell module. It's a script that I'm using to compile the module into a nuget-package, so that I later put it somewhere into repository. Also this script is incrementing module version by 1.

# Config
$moduleName = "My.OwnModule.Powershell";


# Clear old packages
# Get-ChildItem .\$moduleName\_bin | Remove-Item -Force -Verbose;


#region Get current package version and increment Patch
$version = Select-String -Path .\$moduleName\$moduleName.nuspec -Pattern "<version>(\d)\.(\d)\.(\d{1,})</version>";
[int]$majorVersion = $version | Select-Object @{name="version"; expression={$_.Matches.Groups[1].Value}} | select-object -ExpandProperty version;
[int]$minorVersion = $version | Select-Object @{name="version"; expression={$_.Matches.Groups[2].Value}} | select-object -ExpandProperty version;
[int]$patchVersion = $version | Select-Object @{name="version"; expression={$_.Matches.Groups[3].Value}} | select-object -ExpandProperty version;
$patchVersion++;
$nuspecVersionString = "<version>$majorVersion.$minorVersion.$patchVersion</version>";
$psd1VersionString = "ModuleVersion        = `"$majorVersion.$minorVersion.$patchVersion`"";
#endregion /Get current package version and increment Patch


#region Update package version
$a = Get-Content -Path ".\$moduleName\$moduleName.nuspec" | %{$_ -replace "<version>(\d)\.(\d)\.(\d{1,})<\/version>", $nuspecVersionString };
$a | Out-File ".\$moduleName\$moduleName.nuspec" -Verbose;

$a = Get-Content -Path ".\$moduleName\$moduleName.psd1" | %{$_ -replace "ModuleVersion        = `"\d\.\d.\d{1,}`"", $psd1VersionString };
$a | Out-File ".\$moduleName\$moduleName.psd1" -Verbose;
#endregion /Update package version


nuget pack ".\$moduleName\$moduleName.nuspec" -OutputDirectory .\$moduleName\_bin -Properties NoWarn=NU5111,NU5110,NU5100
$package = Get-ChildItem .\$moduleName\_bin\*.nupkg | Sort-Object -Property CreationTime | Select-Object -Last 1;

$package

local-install.ps1

Accordingly, this custom script is for installing the module.

$moduleName = "My.OwnModule.Powershell";
$repoName = "My.OwnModule.Powershell.Repository";
$repoPath = "D:\Private\My.OwnModule.Powershell\My.OwnModule.Powershell\_bin\";

Register-PSRepository -Name $repoName -SourceLocation $repoPath -InstallationPolicy Trusted -ScriptSourceLocation $repoPath;

Install-Module -Name $moduleName -Repository $repoName -Verbose;

Get-Module $moduleName -ListAvailable;

Packaging the Powershell module

Let's try to run local-build.ps1 and see what happens.

Develop and distribute Powershell module

Great! Nuget-package is ready and pushed to the folder My.OwnModule.Powershell\_bin\My.OwnModule.Powershell.0.0.3.nupkg. Now let's try to install the module from the package and use it.

Installation Powershell module from the nuget package

Develop and distribute Powershell module

Marvelous, the module is ready to work!

Verbose is saying that version 0.0.5 already has been installed and so on. That's because I've tried to lower the version manually instead of increasing. Don't pay attention to this.

GitHub repository

You can find all these files in my GitHub repository: https://github.com/vicioussn/My.OwnModule.Powershell.

powershell (en)

  • Hits: 1794
Add comment

Related Articles