Chapter 15Lesson 05~205 minutes

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.

PSResourceGetPackagesSupply chainRepositories

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,CommandType

Do 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

TaskPSResourceGet commandOlder PowerShellGet 2.x command you may encounter
DiscoverFind-PSResourceFind-Module / Find-Script
InstallInstall-PSResourceInstall-Module / Install-Script
Inventory installed packagesGet-InstalledPSResourceGet-InstalledModule
UpdateUpdate-PSResourceUpdate-Module
UninstallUninstall-PSResourceUninstall-Module
Repository configGet/Register/Set/Unregister-PSResourceRepositoryGet/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,ProjectUri

Repository 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' -Prerelease

Production 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,Priority

A 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 -WhatIf

The 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-Null

11. 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,Repository

The 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,Path

14. 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.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0 Send only Ethereum/ERC-20 compatible assets to this address.