Skip to content

Instantly share code, notes, and snippets.

@PanosGreg
Created October 4, 2025 15:57
Show Gist options
  • Save PanosGreg/739d6501b10f2ada10eee030d27e33c7 to your computer and use it in GitHub Desktop.
Save PanosGreg/739d6501b10f2ada10eee030d27e33c7 to your computer and use it in GitHub Desktop.
Find the docker image from Docker Hub, for the current Windows Server OS
function Get-ImageFromHub {
<#
.SYNOPSIS
Find the docker image for the current Windows Server OS from Docker Hub.
Additionally it can also inspect the image and get its size (from all of its layers)
NOTE: This function does not download the docker image, just its metadata.
The -ShowSize switch requires the docker tool.
.EXAMPLE
Get-ImageFromHub -ShowSize
Find the image from Docker Hub along with its layers.
.EXAMPLE
$img = Get-ImageFromHub
docker pull $img
Find the image for the current OS and then pull it from docker hub
#>
[OutputType([string],[psobject])] # <-- default is [string], and [psobject] with ShowSize switch
[cmdletbinding()]
param (
[ValidateSet('Release','Insider')]
[string]$Channel = 'Release',
[ValidateSet('Base','Core','Nano')]
[string]$Type = 'Core',
[switch]$ShowSize
)
$list = @(
[pscustomobject] @{
Type = 'Base'
Chan = 'Release'
Img = 'mcr.microsoft.com/windows'
Tag = 'https://mcr.microsoft.com/v2/windows/tags/list'
Url = 'https://hub.docker.com/r/microsoft-windows'
}
[pscustomobject] @{
Type = 'Base'
Chan = 'Insider'
Img = 'mcr.microsoft.com/windows/insider'
Tag = 'https://mcr.microsoft.com/v2/windows/insider/tags/list'
Url = 'https://hub.docker.com/r/microsoft-windows-insider'
}
[pscustomobject] @{
Type = 'Core'
Chan = 'Release'
Img = 'mcr.microsoft.com/windows/servercore'
Tag = 'https://mcr.microsoft.com/v2/windows/servercore/tags/list'
Url = 'https://hub.docker.com/r/microsoft-windows-servercore'
}
[pscustomobject] @{
Type = 'Core'
Chan = 'Insider'
Img = 'mcr.microsoft.com/windows/servercore/insider'
Tag = 'https://mcr.microsoft.com/v2/windows/servercore/insider/tags/list'
Url = 'https://hub.docker.com/r/microsoft-windows-servercore-insider'
}
[pscustomobject] @{
Type = 'Nano'
Chan = 'Release'
Img = 'mcr.microsoft.com/windows/nanoserver'
Tag = 'https://mcr.microsoft.com/v2/windows/nanoserver/tags/list'
Url = 'https://hub.docker.com/r/microsoft-windows-nanoserver'
}
[pscustomobject] @{
Type = 'Nano'
Chan = 'Insider'
Img = 'mcr.microsoft.com/windows/nanoserver/insider'
Tag = 'https://mcr.microsoft.com/v2/windows/nanoserver/insider/tags/list'
Url = 'https://hub.docker.com/r/microsoft-nanoserver-insider'
}
)
$obj = $list | where {$_.Type -eq $Type -and $_.Chan -eq $Channel}
$ProgressPreference = 'SilentlyContinue' # <-- makes Invoke-RestMethod faster
# find appropriate version for your OS
try {
Write-Verbose "Find docker image from: $($obj.Url)"
$all = (Invoke-RestMethod $obj.Tag -Verbose:$false).tags
$ver = [System.Environment]::OSVersion.Version.ToString(3) # <-- ex. 10.0.20348
$reg = 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion'
$rev = (Get-ItemProperty $reg -Name 'UBR').UBR # <-- ex. 4106
$tag = '{0}.{1}' -f $ver,$rev
$t2 = $all | where {$_ -like "$tag*"} | select -First 1
$t3a = $all | where {$_ -match "^$ver\.\d+$"} | foreach {[version]$_} | Sort-Object
$less= $t3a | where {$_.Revision -lt $rev} | select -Last 1
$more= $t3a | where {$_.Revision -gt $rev} | select -First 1
$Blu = '{0}[38;2;{1};{2};{3}m' -f [char]27, 61, 148, 243 # Blue
$Ita = '{0}[3m' -f [char]27 # Italic
$Def = '{0}[0m' -f [char]27 # Default
if ([bool]$t2) {
$img = '{0}:{1}' -f $obj.img,$t2
}
elseif ([bool]$less -or [bool]$more){
$msg = $Blu + $Ita + "Could not find the exact tag $tag. Will fetch closest tags as fallback" + $Def
Write-Information $msg -InformationAction Continue
$img = @(
'{0}:{1}' -f $obj.img,$less.ToString()
'{0}:{1}' -f $obj.img,$more.ToString()
)
}
else {
$msg1 = "Could not find tag $tag in URL $($obj.Tag)"
$msg2 = "Make sure the Channel ($Channel) param aligns with your OS"
$msg3 = "Or that your OS version ($tag) actually has a docker image of $Type type"
$msg1,$msg2,$msg3 | foreach {Write-Warning $_}
return
}
}
catch {throw $_}
if ($ShowSize.IsPresent) {
try {
docker.exe -v *>$null
$HasDocker = $true
}
catch {
$HasDocker = $false
}
}
# helper function that shows human-readable sizes (which returns a string not a number)
function Get-PrettyCapacity ([uint64]$Size) {
$SzId = ('b', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB')
$Ord = [Math]::Floor( [Math]::Log($Size, 1000) )
$Num = $Size/([Math]::Pow(1000,$Ord))
$Flo = if ($Num -ge 100) {0} elseif ($Num -ge 10) {1} else {2}
$Rnd = [Math]::Round($Num,$Flo)
[String]($Rnd) + $SzId[$Ord]
}
if (-not $ShowSize.IsPresent) {
$out = $img
}
elseif ($ShowSize.IsPresent -and -not $HasDocker) {
$msg = $Blu + $Ita + 'Docker tool was not found, could not fetch the image size.' + $Def
Write-Information $msg -InformationAction Continue
$out = $img
}
elseif ($ShowSize -and $HasDocker) {
try {
$out = foreach ($image in $img) {
$man = docker manifest inspect --verbose $image 2>&1 # <-- the manifest from docker hub
if ($man -is [System.Management.Automation.ErrorRecord]) {throw $man}
$json = $man | ConvertFrom-Json -ErrorAction Stop
$Size = ($json.SchemaV2Manifest.layers | Measure-Object size -Sum).Sum
$i = 1 ; $Layers = [ordered]@{}
$json.SchemaV2Manifest.layers | foreach {$Layers.Add($i++,$_.size)}
[PSCustomObject] @{
Image = $image
Size = Get-PrettyCapacity $Size
SizeByte = $Size -as [uint64]
Layers = $Layers
}
} #foreach image
}
catch {throw $_}
} #elseif showsize
# return the results
Write-Output $out
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment