Preview Warning: This module is made available in a Preview version. Given the complexity involved, changes are expected before the first "stable" release, given that it is impossible to catch all eventualities and bugs out of the box.
Welcome to the PSFramework.NuGet
module, a project to provide sane package management.
Its purpose is to make it a simple, convenient experience to install and deploy PowerShell modules and the tools we need to do it.
Feature-Set:
- Simply install modules, using any kind of Package Management module and repository available
- Bootstrap offline computer for package management
- Manage repositories by policy / configuration
- Simplify credential use
- Install modules to remote computers via PSRemoting
- Customize Module Scopes for Installation
Example use
For example, when you wanted to install a module, depending on which version you had available, you would originally call:
- PowerShellGet v1-2:
Install-Module
- PowerShellGet v3+ (Microsoft.PowerShell.PSResourceGet):
Install-PSResource
With the PSFramework.NuGet
module, in both scenarios you would instead use:
Install-PSFModule 'MyModule'
But now you can also do something like this:
Install-PSFModule 'MyModule' -ComputerName server1, server2
Or this:
$sessions = New-PSSession -VMName server1, server2, -Credential $cred
Install-PSFModule 'MyModule' -ComputerName $sessions
To install this module, run ...
Install-Module PSFramework.NuGet -Scope CurrentUser
Simple Installation:
Install-PSFModule 'MyModule'
Deploy via remoting (only local computer needs repository access):
Install-PSFModule 'MyModule' -ComputerName server1, server2
Deploy via already existing remoting session:
$sessions = New-PSSession -VMName server1, server2, -Credential $cred
Install-PSFModule 'MyModule' -ComputerName $sessions
Even on PowerShellGet V2 or older, this will now work:
Install-PSFModule 'MyModule' -SkipDependenciesCheck
Install a module from PowerShell 7, that is available on both WinPS & PS7:
Install-PSFModule 'MyModule' -Scope AllUsersWinPS
One of the classic problems:
To use Install-Module
you first need to either bootstrap the preinstalled version or update to the latest version of PowerShellGet.
However, the bootstrap requires internet (problematic on an offline server) and the module update required PowerShellGet to already work - the classic Chicken-Egg problem.
So lets fix this:
# Bootstrap Binaries for old Versions
Install-PSFPowerShellGet -Type V2Binaries -ComputerName $sessions
# Install Latest V2
Install-PSFPowerShellGet -Type V2Latest -ComputerName $sessions
# Install Latest V3
Install-PSFPowerShellGet -Type V3Latest -ComputerName $sessions
None of these require internet access, so long as PSFramework.NuGet
is available.
On that note, it is equally possible to distribute PSFramework.NuGet
at scale, but that requires a PowerShell repository to be available.
# Install PSFramework.NuGet on all machines
Install-PSFModule -Name PSFramework.NuGet -ComputerName $sessions
Sometimes we might want to deploy non-Module files & folders using the same mechanism as modules.
For example, a module that provides templating might want to offer commands such as Install-Template
, Publish-Template
or Find-Template
.
This is also provided for by this module:
# Publish files as a Resource Module
Publish-PSFResourceModule -Name MyModule.Template.MyFunction -Version 1.1.0 -Path .\MyFunction\* -Repository PSGallery -ApiKey $key
# Download and extract the files
Save-PSFResourceModule -Name MyModule.Template.MyFunction -Path .
Install-Module and Install-PSResource both offer two scopes to select where to install to:
AllUsers
and CurrentUser
.
These same options exist with Install-PSFModule
but ... with the remoting capabilities and non-Windows OSes, a more flexible system became necessary.
For example, here is how AllUsers
is implemented in PSFramework.NuGet
:
$code = {
if ($PSVersionTable.PSVersion.Major -le 5) {
return "$([Environment]::GetFolderPath("ProgramFiles"))\WindowsPowerShell\Modules"
}
if ($IsWindows) {
return "$([Environment]::GetFolderPath("ProgramFiles"))\PowerShell\Modules"
}
'/usr/local/share/powershell/Modules'
}
$scopeParam = @{
Name = 'AllUsers'
ScriptBlock = $code
Description = 'Default path for modules visible to all users.'
}
Register-PSFModuleScope @scopeParam
This way, even while installing to multiple remote systems in parallel (Install-PSFModule
multithreads using Runspaces), each computer will have it installed to the correct location, Windows or not.
Static Paths
Of course, a static scope can be defined as well:
Register-PSFModuleScope -Name Personal -Path C:\code\Modules -Description 'Personal local modules, not redirected to OneDrive documents'
Install-PSFModule -Name EntraAuth -Scope Personal
And to avoid having to do that again each time you start PowerShell - and to avoid having to put it into your $profile and force the module import into your console start - you can make PowerShell remember your choice:
Register-PSFModuleScope -Name Personal -Path C:\code\Modules -Description 'Personal local modules, not redirected to OneDrive documents' -Persist
Overriding defaults
This same can be used to override the default scopes if desired.
By default, when not specifying a scope or ComputerName, it will use the CurrentUser
scope, no matter how that scope is configured.
# This too can be persisted
Register-PSFModuleScope -Name CurrentUser -Path C:\code\Modules -Description 'Personal local modules, not redirected to OneDrive documents' -Persist
# Will now install to C:\code\Modules
Install-PSFModule -Name EntraAuth
Working with private repositories can be a bit of an annoyance. Especially when you need to remember to always provide credentials with each request.
So, let's make this pain go away:
# For the current session
Set-PSFRepository -Name AzDevOps -Credential $patCred
# Remember it going forward
Set-PSFRepository -Name AzDevOps -Credential $patCred -Persist
Deploy repositories by policy
Another pain is teaching each and every computer on where to get their modules. Fortunately you can deploy them by using the PSFramework Configuration System. There are several options to that, whether deploying a configuration file to the expected location, a registry key or even environment variables.
Example configuration Set for the repository-name "AzDevOps":
PSFramework.NuGet.Repositories.AzDevOps.Uri: <url>
PSFramework.NuGet.Repositories.AzDevOps.Priority: 40
PSFramework.NuGet.Repositories.AzDevOps.Type: Any
PSFramework.NuGet.Repositories.AzDevOps.Trusted: 1
Or an example deployed to HKLM:
Key: HKLM:\SOFTWARE\Microsoft\WindowsPowerShell\PSFramework\Config\Default
Values:
Name | Type | Value |
---|---|---|
PSFramework.NuGet.Repositories.AzDevOps.Uri | REG_SZ | String: |
PSFramework.NuGet.Repositories.AzDevOps.Priority | REG_SZ | Int:40 |
PSFramework.NuGet.Repositories.AzDevOps.Type | REG_SZ | String:Any |
PSFramework.NuGet.Repositories.AzDevOps.Trusted | REG_SZ | Int:1 |