Published in Articles on by Michiel van Oosterhout ~ 7 min read

As we've seen in the previous article, PowerShell package providers can discover, install, and manage packages. But the package provider abstraction does not provide a way to publish packages. This is left to the individual package providers to implement. The PowerShellGet package provider exports additional cmdlets to prepare package manifests and publish packages. One type of package it can publish is a script package.

Packages in Hangar 51
Packages in Hangar 51 Indiana Jones and the Raiders of the Lost Ark, Paramount Pictures

Authoring a script manifest

A PowerShell script package is created from a single PowerShell script (.ps1) file containing code and a manifest. The manifest is required to package the script, and it takes the form of two specially formatted comment blocks with metadata properties:

<#PSScriptInfo

.VERSION 1.0.0

.GUID a2b72a79-5013-4d1e-9c63-1345ac2269a0

.AUTHOR Michiel van Oosterhout

#>

<#
.DESCRIPTION 
An example of script file info
#>

The manifest can be validated using Test-ScriptFileInfo, which returns a PSCustomObject with note properties for all the metadata properties. The following metadata properties are supported, but only the ones used in the example above are required.

  • .AUTHOR
  • .COMPANYNAME
  • .COPYRIGHT
  • .DESCRIPTION
  • .EXTERNALMODULEDEPENDENCIES
  • .EXTERNALSCRIPTDEPENDENCIES
  • .GUID
  • .ICONURI
  • .LICENSEURI
  • .PRIVATEDATA
  • .PROJECTURI
  • .RELEASENOTES
  • .REQUIREDMODULES
  • .REQUIREDSCRIPTS
  • .TAGS
  • .VERSION

Test-ScriptFileInfo adds these additional note properties to the object it returns:

DefinedCommands
All the functions and workflows defined in the script
DefinedFunctions
All the functions defined in the script
DefinedWorkflows
All the workflows defined in the script
Name
The script's file name without the extension
Path
The script's path
ScriptBase
The script's directory

The manifest can be updated in-place using Update-ScriptFileInfo. This is useful when you need to update the .VERSION metadata property in an automated release pipeline.

Creating a PowerShell script package

A PowerShell script package is created and published by invoking Publish-Script. This will validate the manifest, create a NuGet package (.nupkg) file, and upload the package to the package source referred to by the -Repository parameter.

Publish-Script -Path ./Example.ps1 -Repository Example

Executing this this command creates a file named Example.1.0.0.nupkg in the local package source location. On Windows creating and publishing a NuGet package requires NuGet.exe, which can be installed via an interactive prompt when Publish-Script is invoked.

On Windows PowerShell 5.1:

NuGet.exe is required to continue

PowerShellGet requires NuGet.exe to publish an item to the NuGet-based repositories. NuGet.exe must be available in %ProgramData%\Microsoft\Windows\PowerShell\PowerShellGet\ or %LOCALAPPDATA%\Microsoft\Windows\PowerShell\PowerShellGet\, or under one of the paths specified in PATH environment variable value. NuGet.exe can be downloaded from https://nuget.org/nuget.exe. Do you want PowerShellGet to install NuGet.exe now?

(This will install version 2.8 of NuGet.exe to the path specified in the prompt.)

On PowerShell 7.2:

NuGet.exe is required to continue

This version of PowerShellGet requires minimum version '4.1.0' of NuGet.exe to publish an item to the NuGet-based repositories. NuGet.exe must be available in %ProgramData%\Microsoft\Windows\PowerShell\PowerShellGet\ or %LOCALAPPDATA%\Microsoft\Windows\PowerShell\PowerShellGet\, or under one of the paths specified in PATH environment variable value. NuGet.exe can be downloaded from https://aka.ms/psget-nugetexe. For more information, see https://aka.ms/installing-powershellget. Do you want PowerShellGet to install the latest version of NuGet.exe now?

(This will install version 6.2 of NuGet.exe to the path specified in the prompt.)

On Linux and macOS creating and publishing a NuGet package requires the dotnet executable, as indicated by this prompt when Publish-Script is invoked:

Publish-Script: For publish operations, dotnet command version 2.0.0 or newer is required to interact with the NuGet-based repositories. Please ensure that dotnet command version 2.0.0 or newer is installed and available under one of the paths specified in PATH environment variable value. You can also install the dotnet command by following the instructions specified at https://aka.ms/dotnet-install-script.

PowerShell script package metadata

A PowerShell script package is a NuGet package (.nupkg) file, which can be opened on Windows using NuGet Package Explorer or online at https://nuget.info. The script file is located in the root of the package, and most metadata properties in the script's manifest will have been mapped to the corresponding NuGet package metadata. In addition, the following rules apply:

  • .COMPANYNAME maps to Owners;
  • .REQUIREDSCRIPTS maps to Dependencies;
  • .REQUIREDSCRIPTS is a space- or comma-separated list of string values;
  • .REQUIREDSCRIPTS values consist of a name, optionally followed by a colon and a version;
  • .REQUIREDSCRIPTS values cannot contain a version with a prerelease label;
  • .REQUIREDSCRIPTS must be available in the package source;
  • .VERSION can contain a prerelease label since PowerShellGet 1.6;
  • .VERSION prerelease labels are not semver 1.0.0 compatible 2;
  • the PSScript tag is added automatically to Tags;
  • the PSIncludes_Function tag is added automatically to Tags if the script declares a function;
  • the PSIncludes_Workflow tag is added automatically to Tags if the script declares a workflow;
  • a PSCommand_ tag is added automatically to Tags for each function and workflow;
  • a PSFunction_ tag is added automatically to Tags for each function;
  • a PSWorkflow_ tag is added automatically to Tags for each workflow.

Consuming a PowerShell script package

A PowerShell script package can be installed like any other package. Alternatively the script contained in the script package can be saved to a specific location.

Installing the package

Once published the PowerShell script package can be discovered and installed using standard PowerShell package management commands:

Find-Package -Name Example -ProviderName PowerShellGet
Install-Package -Name Example -ProviderName PowerShellGet

Unfortunately these commands cannot find packages published to a local package source. Fortunately the PowerShellGet package provider exports additional cmdlets specifically to find and install PowerShell script packages, and these cmdlets can be used to find script packages published to a local PowerShellGet repository.

Find-Script -Name Example -Repository example
Install-Script -Name Example -Repository example
Get-InstalledScript -Name Example

PowerShellGet will prompt to add the directory where scripts are installed to the PATH environment variable:

PATH Environment Variable Change

Your system has not been configured with a default script installation path yet, which means you can only run a script by specifying the full path to the script file. This action places the script into the folder %USERPROFILE%\Documents\WindowsPowerShell\Scripts, and adds that folder to your PATH environment variable. Do you want to add the script installation path %USERPROFILE%\Documents\WindowsPowerShell\Scripts to the PATH environment variable?

You can suppress this prompt with the -NoPathUpdate switch.

(If the script manifest declares any .EXTERNALMODULEDEPENDENCIES or .EXTERNALSCRIPTDEPENDENCIES then a warning will be given if those dependencies are not satisfied.)

Once installed, the script can be used. If the script is a procedural script, it should be executed (e.g. Example.ps1). If the script declares one or more functions, it should be dot sourced (e.g. . Example.ps1) before those function(s) can be invoked. If the directory where scripts are installed has not been added to the PATH environment variable, then its location must be resolved using (Get-InstalledScript -Name Example).InstalledLocation.

Saving the script

As an alternative to installing a PowerShell script package, the script contained in it can simply be saved from the repository to an arbitrary location, and executed or dot sourced from there: Save-Script -Name Example -Repository Example -Path ....

Summary

A PowerShell script is one of the smallest units of distribution available in PowerShell package management, made even more so by the fact that the code and the package manifest are combined in a single file. The package format is based on NuGet with limited support for semantic versioning. Consuming PowerShell scripts distributed this way is further simplified by the option to save the script directly from its package source to a local directory.


  1. A local package source can be registered by invoking Register-PackageSource -Trusted -Provider PowerShellGet -Name Example -Location ...↩︎

  2. The label is validated using $validCharacters = "^[a-zA-Z0-9]+$"↩︎