Creating a PowerShell script that can install itself as module
Powershell advanced functions are a lightweight, yet pretty powerful way to extend the set of commands available in a Powershell sessions. Advanced functions look and feel almost exactly like proper cmdlets, but they are written in Powershell and therefore quick to develop.
By default, advanced functions are ephemeral though: If you run a script containing an advanced function, that function is going to be available for the rest of the Powershell session – after that, it is gone. To make an advanced function available permanently – like a cmdlet – you have to wrap it in a Powershell module, and install that module.
A module consists of at least two files:
- a script module, which is a Powershell script containing the advanced functions. Unlike a regular Powershell script, it has to use the file suffix
.psm1
. - a module manifest (
*.psd1
) that defines some metadata for the module.
If you have a NuGet repository, you can publish your module there to make it easily installable across your environment. But what if you do not have a NuGet repository you could publish the module to, but still want to make it easily installable for other users?
Installing without Nuget
Installing a module entails taking the .psm1 and .psd1 file and copying them to a folder underneath [ProgramFiles]\WindowsPowerShell\Modules\
(to become visible globally) or [MyDocuments]\WindowsPowerShell\Modules
(for the current user). That process is easy enough, but already adds a good amount of friction:
- Comprising multiple files, the module is not as easy to download or copy as a plain script file anymore and you might have to package the files in a Zip archive.
- Asking users to copy the files to the right folder is rarely an option, so you might have to create an extra installer script or package to take care of this.
As it turns out, there is a way to have your cake and eat it too – to take advantage of advanced functions and modules while still being able to distribute everything as a single script file. The idea is to create a Powershell script that, when run, installs itself permanently as a PowerShell module.
See powershell-install-as-module/Install-Template.ps1 on GitHub for an example of such a script.
The script can be run in three modes. If you dot-source it without arguments, the script will make the advanced functions available to the current session:
Name Value
---- -----
ModuleVersion 1.0
FunctionsToExport {Invoke-TemplateTest}
RootModule JP.TemplateModule.psm1
Advanced functions added to current session.
Use -Install to add functions permanenently.
PSInvoke-TemplateTest
This is a test function
If you run .\Install-Template.ps1 -Install CurrentUser
, the script generates a .psm1
and .psd1
file off of itself and saves them to [MyDocuments]\WindowsPowerShell\Modules\JP.TemplateModule
. This causes the advanced functions to permanently become available for the current user:
Name Value
---- -----
ModuleVersion 1.0
FunctionsToExport {Invoke-TemplateTest}
RootModule JP.TemplateModule.psm1
Advanced functions added to current session.
Advanced functions installed to C:\Users\jpassing\Documents\WindowsPowerShell\Modules\
In a new session:
PS> Invoke-TemplateTest
This is a test function
Finally, if you run .\Install-Template.ps1 -Install LocalMachine
, the script generates a .psm1
and .psd1
file off of itself and saves them to [ProgramFiles]\WindowsPowerShell\Modules\JP.TemplateModule
, causing the advanced functions to become visible to everyone.