PSResourceGet, PowerShell Gallery, Private Repositories, and Supply-Chain Safety
Manage PowerShell packages with the current PSResourceGet workflow, including version pinning, local/private repositories, install/update/uninstall lifecycle, legacy PowerShellGet recognition, and supply-chain controls.
Learning objectives
- Identify Microsoft.PowerShell.PSResourceGet as the modern package-management module and verify its installed version.
- Use Find/Install/Get-Installed/Update/Uninstall-PSResource concepts coherently.
- Design version and prerelease policies rather than blindly following latest.
- Treat repository registration, provenance, dependencies, and credentials as supply-chain boundaries.
- Recognize legacy PowerShellGet/Install-Module syntax without making it the modern default.
- Build and clean up a local file-repository package lab with no paid service.
1. Distribution adds a supply-chain boundary
A module that works on your laptop is not yet an organizational dependency. Teams need to discover packages, select versions, install them into known scopes, update them deliberately, remove them, and know which repository supplied the bits.
That acquisition path is a supply chain: publisher → repository → package manager → local installation → imported code. Every step can affect integrity, provenance, and operational reproducibility.
2. PSResourceGet is the modern package-management path
As of this chapter's generation date, Microsoft documents Microsoft.PowerShell.PSResourceGet 1.2.0 as the current stable release. It is the new package-management solution for PowerShell and is installed with PowerShell 7.4 or later. The module replaces the older PowerShellGet/PackageManagement workflow for modern automation.
Get-Module -ListAvailable Microsoft.PowerShell.PSResourceGet |
Sort-Object Version -Descending |
Select-Object Name,Version,Path
Get-Command -Module Microsoft.PowerShell.PSResourceGet |
Sort-Object Name |
Select-Object Name,CommandTypeDo not hard-code the assumption that every machine has exactly the same PSResourceGet version. Inspect the installed version and state your minimum requirement when your workflow uses a newer feature.
3. Learn the lifecycle verbs as one coherent workflow
| Task | PSResourceGet command | Older PowerShellGet 2.x command you may encounter |
|---|---|---|
| Discover | Find-PSResource | Find-Module / Find-Script |
| Install | Install-PSResource | Install-Module / Install-Script |
| Inventory installed packages | Get-InstalledPSResource | Get-InstalledModule |
| Update | Update-PSResource | Update-Module |
| Uninstall | Uninstall-PSResource | Uninstall-Module |
| Repository config | Get/Register/Set/Unregister-PSResourceRepository | Get/Register/Set/Unregister-PSRepository |
Windows PowerShell 5.1 still commonly exposes old PowerShellGet examples, so you need to recognize them. Do not copy old package-manager bootstrapping into a PowerShell 7.6 workflow without checking current guidance.
4. Discovery is read-only; use it to inspect before installing
Find-PSResource returns package metadata from registered repositories. Exact package names are especially important for local file repositories, where wildcard name searches are not supported.
Get-PSResourceRepository |
Select-Object Name,Uri,Trusted,Priority
# Optional internet-connected, read-only inspection:
# Find-PSResource -Name Pester -Repository PSGallery |
# Select-Object Name,Version,Prerelease,Repository,Author,ProjectUriRepository metadata is evidence, not an automatic trust decision. Before installation, verify expected name, publisher/project, version, repository, and your organization's allowlist/policy.
5. Version constraints turn “latest” into an explicit decision
PSResourceGet uses NuGet-style version syntax. An exact version produces reproducible installation; a bounded range permits compatible updates. Which policy is correct depends on how you test and promote dependencies.
# Illustrative patterns. They do not install anything.
# Exact release:
# Find-PSResource -Name 'Contoso.Tools' -Version '2.4.1'
# Compatible range: >=2.4.0 and <3.0.0
# Find-PSResource -Name 'Contoso.Tools' -Version '[2.4.0,3.0.0)'
# Include prerelease builds only when you intend to evaluate them:
# Find-PSResource -Name 'Contoso.Tools' -PrereleaseProduction CI usually benefits from lock-like or explicitly promoted versions. “Always install newest” lets upstream changes enter your pipeline at an unrelated time.
6. Repositories are configuration and trust boundaries
PSResourceGet can work with NuGet repositories and local file stores. Registered repositories have names, URIs, priorities, and a trusted/untrusted setting. In stable PSResourceGet 1.2.0, the PowerShell Gallery registration is not something you should silently mark trusted just to suppress prompts.
Get-PSResourceRepository |
Sort-Object Priority,Name |
Select-Object Name,Uri,Trusted,PriorityA private repository can provide internal package review, retention, promotion, and access control. It does not eliminate the need to review source, signing policy, dependency provenance, and who is allowed to publish.
7. Install and update are filesystem changes with version consequences
Install-PSResource places a resource into an installation scope. It does not automatically load the new module into the current session. Update-PSResource installs a newer version side-by-side; existing loaded command definitions can therefore remain stale until you import the intended version or start a new session.
$installPreview = @{
Name = 'Contoso.Tools'
Version = '2.4.1'
Repository = 'CorpApproved'
Scope = 'CurrentUser'
WhatIf = $true
}
# Install-PSResource @installPreview
# Update-PSResource -Name 'Contoso.Tools' -Scope CurrentUser -WhatIf
# Uninstall-PSResource -Name 'Contoso.Tools' -Version '2.4.1' -Scope CurrentUser -WhatIfThe important operational point is that package lifecycle and session import lifecycle are separate.
8. Trust is more than the repository name
Package provenance asks where the package came from and how strongly you can connect those bits to an expected publisher/source. Supply-chain controls can include an approved repository list, version pinning, offline/source review, package checksums, Authenticode policy where applicable, package signing, CI verification, and controlled promotion from external to internal repositories.
A checksum proves that two byte sequences match an expected digest; it does not by itself prove who authored the package. A signature is only as trustworthy as the key/certificate verification and publisher policy behind it.
9. Dependencies expand the review surface
Installing one module can install dependencies. Review not only the top-level package but also its dependency graph and version constraints. Stable PSResourceGet 1.2.0 can search/include dependencies, but repository protocol capabilities differ; design your internal promotion workflow around what your chosen repository actually supports.
# Read-only discovery pattern when a repository is available:
# Find-PSResource -Name 'Contoso.Tools' -Repository 'CorpApproved' -IncludeDependencies
# For an exact environment, record the resolved package set as data
# and review/promote that set together.10. Lab setup: create a local package without any paid service
This lab builds a tiny disposable module, compresses it into a local NuGet package, registers a temporary file-based PSResourceGet repository, installs to CurrentUser, verifies it, uninstalls it, and removes the repository registration. It does not require administrator/root privileges.
Import-Module Microsoft.PowerShell.PSResourceGet -MinimumVersion 1.2.0 -ErrorAction Stop
$labRoot = Join-Path ([IO.Path]::GetTempPath()) ('psresource-ch15-' + [guid]::NewGuid().ToString('N'))
$sourceRoot = Join-Path $labRoot 'DevOpsAcademy.Ch15Lab'
$repoRoot = Join-Path $labRoot 'repository'
$repoName = 'AcademyLocal-' + ([guid]::NewGuid().ToString('N').Substring(0,8))
New-Item -ItemType Directory -Path $sourceRoot,$repoRoot -Force | Out-Null
@'
function Get-Ch15PackageProof {
[CmdletBinding()] param()
[pscustomobject]@{
Module='DevOpsAcademy.Ch15Lab'
Version='1.0.0'
CheckedUtc=[datetime]::UtcNow
}
}
Export-ModuleMember -Function Get-Ch15PackageProof
'@ | Set-Content -LiteralPath (Join-Path $sourceRoot 'DevOpsAcademy.Ch15Lab.psm1') -Encoding utf8
$manifestParams = @{
Path = Join-Path $sourceRoot 'DevOpsAcademy.Ch15Lab.psd1'
RootModule = 'DevOpsAcademy.Ch15Lab.psm1'
ModuleVersion = '1.0.0'
Guid = [guid]::NewGuid()
Author = 'DevOps Academy Lab'
Description = 'Disposable local PSResourceGet training package.'
FunctionsToExport = @('Get-Ch15PackageProof')
}
New-ModuleManifest @manifestParams
Test-ModuleManifest -Path $manifestParams.Path -ErrorAction Stop | Out-Null11. Package and register the local repository
Compress-PSResource -Path $sourceRoot -DestinationPath $repoRoot -ErrorAction Stop
$repoUri = [uri]::new((Resolve-Path -LiteralPath $repoRoot).Path).AbsoluteUri
$repoParams = @{
Name = $repoName
Uri = $repoUri
Trusted = $true
ApiVersion = 'Local'
}
Register-PSResourceRepository @repoParams
Find-PSResource -Name 'DevOpsAcademy.Ch15Lab' -Repository $repoName |
Select-Object Name,Version,RepositoryThe repository is marked trusted only because the lab just created every package in that private temporary folder. Do not apply that trust decision automatically to internet or shared repositories.
12. Install, verify, preview update, and clean up
$alreadyInstalled = Get-InstalledPSResource -Name 'DevOpsAcademy.Ch15Lab' -ErrorAction SilentlyContinue
if ($alreadyInstalled) {
throw 'The training module name is already installed. Choose a different lab module name before continuing.'
}
try {
$installParams = @{
Name = 'DevOpsAcademy.Ch15Lab'
Version = '1.0.0'
Repository = $repoName
Scope = 'CurrentUser'
PassThru = $true
}
Install-PSResource @installParams
Get-InstalledPSResource -Name 'DevOpsAcademy.Ch15Lab' |
Select-Object Name,Version,Repository
Import-Module DevOpsAcademy.Ch15Lab -Force
Get-Ch15PackageProof
$updateParams = @{
Name = 'DevOpsAcademy.Ch15Lab'
Repository = $repoName
Scope = 'CurrentUser'
WhatIf = $true
}
Update-PSResource @updateParams
}
finally {
Remove-Module DevOpsAcademy.Ch15Lab -ErrorAction SilentlyContinue
Uninstall-PSResource -Name 'DevOpsAcademy.Ch15Lab' -Version '1.0.0' -Scope CurrentUser -ErrorAction SilentlyContinue
Unregister-PSResourceRepository -Name $repoName -ErrorAction SilentlyContinue
Remove-Item -LiteralPath $labRoot -Recurse -Force -ErrorAction SilentlyContinue
}If your machine already has a module with the same lab name, choose a different unique name before running the install step. The course uses a deliberately specific training name to minimize that chance.
13. Recognize legacy PowerShellGet without making it the default
You will still encounter Find-Module, Install-Module, Update-Module, and Uninstall-Module in existing scripts, especially Windows PowerShell 5.1 environments. Current Microsoft guidance calls PSResourceGet the new package-management solution for PowerShell; use the older workflow only when the target environment requires it.
# Identify which package-manager generations exist on the current machine.
Get-Module -ListAvailable PowerShellGet,Microsoft.PowerShell.PSResourceGet |
Sort-Object Name,Version -Descending |
Select-Object Name,Version,Path14. Supply-chain review checklist
- Use approved repositories and inspect their configured URI/trust/priority.
- Pin or bound versions according to your release policy.
- Review top-level package and dependencies, not only the command you intend to call.
- Prefer internal promotion/allowlists for privileged production automation.
- Verify signatures/checksums according to organizational policy; understand what each proves.
- Never place repository API keys/tokens in source code, command history, or verbose logs.
- Test new versions before promoting them and keep rollback/recovery artifacts.
- After an update, verify which version is actually imported in the running session.
15. Knowledge check
Question 1. What is the current stable PSResourceGet release used as this lesson baseline?
Question 2. What modern command replaces Install-Module for the general module/script package lifecycle?
Question 3. Why can updating an installed module leave the current session using old behavior?
Question 4. Does marking a repository Trusted prove every package in it is safe?
Question 5. Why use a local file repository in the lab?
16. Chapter summary and next bridge
Chapter 15 connected four layers of reusable PowerShell engineering. Module discovery and PSModulePath explain how commands become visible. Script modules create a private implementation scope and public API. Manifests add identity, versions, compatibility, dependencies, and metadata. Classes/.NET provide stronger domain or interoperability tools when lightweight functions and objects are not enough. PSResourceGet then turns the module into a versioned supply-chain artifact.
The next chapter focuses on the security consequences of all these capabilities: execution policy and signing, credentials/secrets, untrusted input, audit logging, constrained language, JEA, and least privilege.
17. Authoritative references
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this address.