Skip to main content

PowerShell Gallery is down - What now?

Intro

So the PowerShell Gallery is down, what now?

Here are some suggestions for proactive and reactive measures.

info

If you've made your business dependent on a free service with no SLA you can't blame anyone but yourself. Check the terms of use:

Why is it down?

PowerShell Gallery Status is available here*:

*PowerShell Gallery has been down for short periods with no notices or announcements from Microsoft in the past.

A NuGet v2 package repository with a web GUI and an API.

Other ways

Use pwsh.gallery

pwsh.gallery by Justin Grote is a NuGet v3 static API feed (aka a pile of files) hosted on an Azure Storage Account fronted by Cloudflare for caching. The packages are hosted on a Azure Storage Account directly. An Azure Container app preiodically queries the PowerShell Gallery for new packages and add them to the repository via Sleet.

If the PowerShell Gallery is ever 100% down, pwsh.gallery should up and be up to date to the point where PSG went down, and pwsh.gallery will pick up from the last sync timestamp when it comes back up to resume mirroring.

Example package URL:

Resources:

Example usage with PSResourceGet:

# Register pwsh.gallery as a PSResourceGet resource repository
Register-PSResourceRepository -Name 'pwsh.gallery' -Uri 'https://pwsh.gallery/index.json' `
-ApiVersion 3 -Priority 60

# Find a module with it
Find-PSResource -Repository 'pwsh.gallery' -Name 'Az.Accounts' | Format-List

Limitations:

Hit CDN directly with module and version

If only PowerShell Gallery itself is down, the CDN used to serve packages (*.nupkgs) might still be up.

  • Example URL: https://psg-prod-eastus.azureedge.net/packages/az.accounts.4.0.2.nupkg
    • Notice package name must be lower case.

AFAIK, PSResourceGet does not support installing packages from a URL, but if you can download the .nupkg file it can be installed locally like so:

# Register file system path as a PSResourceGet resource repository
Register-PSResourceRepository -Name 'Local' -Uri 'D/some/local/path' -ApiVersion 'local'

# Install latest available version of a package you've downloaded as `.nupkg`
Install-PSResource -Repository 'Local' -Name '<module_name>'

Limitations:

  • You must know each package and version you need, including dependencies.
  • No dependency resolution, but you could download and extract a .nupkg and check out metadata (<module_name>.psd1 for instance) within it.

Proxy or host your own repository

Limitations with a NuGet proxy:

  • It only downloads and caches a specific version of a specific package if it already has been asked for before PowerShell Gallery went down.

[In the future] Microsoft modules in MAR

Microsoft is working on getting their PowerShell modules to a public ACR (Azure Container Registry) instance called MAR (Microsoft Artifact Registry), also referred to as MCR (Microsoft Container Registry). PSResourceGet supports ACR as a repository.

Example usage:

FYI

Requires PSResourceGet >= v1.1.0

# Register MAR as a PSResourceGet resource repository
Register-PSResourceRepository -Name 'MAR' -Uri 'https://mcr.microsoft.com/' `
-ApiVersion 'ContainerRegistry' -Priority 60

# Find a module with it
Find-PSresource -Repository 'MAR' -Name 'Az.Resources' | Format-List

Example API usage:

# List available packages for PowerShell
(
Invoke-RestMethod -Method 'Get' -Uri 'https://mcr.microsoft.com/v2/_catalog'
).'repositories'.Where{
$_.StartsWith('psresource/')
} | Sort-Object

# Get available versions of Az.Resources
Invoke-RestMethod -Method 'Get' -Uri (
'https://mcr.microsoft.com/v2/psresource/az.resources/tags/list'
)

# Get metadata from the manifest of a specific version of Az.Resources
(
Invoke-RestMethod -Method 'Get' -Uri (
'https://mcr.microsoft.com/v2/psresource/az.resources/manifests/7.8.0'
) -Headers @{
'Accept' = [string] 'application/vnd.oci.image.manifest.v1+json'
}
).'layers'.'annotations'.'metadata' | ConvertFrom-Json

Module specific mirrors

AWS

Hosts .zip mirrors.

Az

Example PowerShell to get the latest .tar.gz from GitHub releases:

$(
[PSCustomObject[]](
Invoke-RestMethod -Method 'Get' -Uri 'https://api.github.com/repos/azure/azure-powershell/releases'
)
).ForEach{
[PSCustomObject]@{
'name' = [string] $_.'name'
'created_at' = [string] $_.'created_at'
'tar_download_link' = [string]($_.'assets'.'browser_download_url'.Where({$_.EndsWith('.tar.gz')},'First'))
}
}.Where{
$_.'name'.StartsWith('Az ',[System.StringComparison]::OrdinalIgnoreCase) -and
-not [string]::IsNullOrWhiteSpace($_.'tar_download_link')
} | Sort-Object -Property 'created_at' -Descending | Select-Object -First 1

Microsoft.Graph

PSResourceGet