Skip to content

Instantly share code, notes, and snippets.

@SMSAgentSoftware
Last active February 23, 2024 20:58
Show Gist options
  • Star 15 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save SMSAgentSoftware/79fb091a4b7806378fc0daa826dbfb47 to your computer and use it in GitHub Desktop.
Save SMSAgentSoftware/79fb091a4b7806378fc0daa826dbfb47 to your computer and use it in GitHub Desktop.
Gets the current software update level of a Windows 10/11 workstation and compares with the latest available updates. Can also list all available updates for the current build.
[CmdletBinding()]
Param(
[switch]$ListAllAvailable,
[switch]$ExcludePreview,
[switch]$ExcludeOutofBand
)
$ProgressPreference = 'SilentlyContinue'
Function Get-MyWindowsVersion {
[CmdletBinding()]
Param
(
$ComputerName = $env:COMPUTERNAME
)
$Table = New-Object System.Data.DataTable
$Table.Columns.AddRange(@("ComputerName","Windows edition","Version","Build number"))
$ProductName = Get-CimInstance -ClassName Win32_OperatingSystem -Property Caption | Select -ExpandProperty Caption
Try
{
$DisplayVersion = (Get-ItemPropertyValue 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -Name DisplayVersion -ErrorAction Stop)
}
Catch
{
$DisplayVersion = "N/A"
}
$CurrentBuild = Get-ItemPropertyValue 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -Name CurrentBuild
$UBR = Get-ItemPropertyValue 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -Name UBR
$OSVersion = $CurrentBuild + "." + $UBR
$TempTable = New-Object System.Data.DataTable
$TempTable.Columns.AddRange(@("ComputerName","Windows edition","Version","Build number"))
[void]$TempTable.Rows.Add($env:COMPUTERNAME,$ProductName,$DisplayVersion,$OSVersion)
Return $TempTable
}
Function Convert-ParsedArray {
Param($Array)
$ArrayList = New-Object System.Collections.ArrayList
foreach ($item in $Array)
{
[void]$ArrayList.Add([PSCustomObject]@{
Update = $item.outerHTML.Split('>')[1].Replace('</a','').Replace('&#x2014;',' - ')
KB = "KB" + $item.href.Split('/')[-1]
InfoURL = "https://support.microsoft.com" + $item.href
OSBuild = [int]$item.outerHTML.Split('.')[-1].Split(')')[0] # Just for sorting
})
}
Return $ArrayList
}
# Get Windows info
$CurrentWindowsVersion = Get-MyWindowsVersion -ErrorAction Stop
# Set the correct URL for W11 or W10
If ($CurrentWindowsVersion.'Build number' -like "2*")
{
$URI = "https://aka.ms/Windows11UpdateHistory"
}
If ($CurrentWindowsVersion.'Build number' -like "1*")
{
$URI = "https://support.microsoft.com/en-gb/topic/windows-10-update-history-7dd3071a-3906-fa2c-c342-f7f86728a6e3"
}
# Retrieve the web pages
If ($PSVersionTable.PSVersion.Major -ge 6)
{
$Response = Invoke-WebRequest -Uri $URI -ErrorAction Stop
}
else
{
$Response = Invoke-WebRequest -Uri $URI -UseBasicParsing -ErrorAction Stop
}
# Pull the version data from the HTML
If (!($Response.Links))
{ throw "Response was not parsed as HTML"}
$VersionDataRaw = $Response.Links | where {$_.outerHTML -match "supLeftNavLink" -and $_.outerHTML -match "KB"}
# Pull all the update info if requested
If ($ListAllAvailable)
{
If ($ExcludePreview -and $ExcludeOutofBand)
{
$AllAvailable = $VersionDataRaw | where {$_.outerHTML -match $CurrentWindowsVersion.'Build number'.Split('.')[0] -and $_.outerHTML -notmatch "Preview" -and $_.outerHTML -notmatch "Out-of-band"}
}
ElseIf ($ExcludePreview)
{
$AllAvailable = $VersionDataRaw | where {$_.outerHTML -match $CurrentWindowsVersion.'Build number'.Split('.')[0] -and $_.outerHTML -notmatch "Preview"}
}
ElseIf ($ExcludeOutofBand)
{
$AllAvailable = $VersionDataRaw | where {$_.outerHTML -match $CurrentWindowsVersion.'Build number'.Split('.')[0] -and $_.outerHTML -notmatch "Out-of-band"}
}
Else
{
$AllAvailable = $VersionDataRaw | where {$_.outerHTML -match $CurrentWindowsVersion.'Build number'.Split('.')[0]}
}
$UniqueList = (Convert-ParsedArray -Array $AllAvailable) | Select -Property * -Unique | Sort OSBuild -Descending
$Table = New-Object System.Data.DataTable
[void]$Table.Columns.AddRange(@('Update','KB','InfoURL'))
foreach ($Update in $UniqueList)
{
[void]$Table.Rows.Add(
$Update.Update,
$Update.KB,
$Update.InfoURL
)
}
Return $Table
}
# Get the latest patch info
$CurrentPatch = $VersionDataRaw | where {$_.outerHTML -match $CurrentWindowsVersion.'Build number'} | Select -First 1
If ($ExcludePreview -and $ExcludeOutofBand)
{
$LatestAvailablePatch = $VersionDataRaw | where {$_.outerHTML -match $CurrentWindowsVersion.'Build number'.Split('.')[0] -and $_.outerHTML -notmatch "Out-of-band" -and $_.outerHTML -notmatch "Preview"} | Select -First 1
}
ElseIf ($ExcludePreview)
{
$LatestAvailablePatch = $VersionDataRaw | where {$_.outerHTML -match $CurrentWindowsVersion.'Build number'.Split('.')[0] -and $_.outerHTML -notmatch "Preview"} | Select -First 1
}
ElseIf ($ExcludeOutofBand)
{
$LatestAvailablePatch = $VersionDataRaw | where {$_.outerHTML -match $CurrentWindowsVersion.'Build number'.Split('.')[0] -and $_.outerHTML -notmatch "Out-of-band"} | Select -First 1
}
Else
{
$LatestAvailablePatch = $VersionDataRaw | where {$_.outerHTML -match $CurrentWindowsVersion.'Build number'.Split('.')[0]} | Select -First 1
}
# Add the data to a table
$Table = New-Object System.Data.DataTable
[void]$Table.Columns.AddRange(@('OSVersion','OSEdition','OSBuild','CurrentInstalledUpdate','CurrentInstalledUpdateKB','CurrentInstalledUpdateInfoURL','LatestAvailableUpdate','LastestAvailableUpdateKB','LastestAvailableUpdateInfoURL'))
[void]$Table.Rows.Add(
$CurrentWindowsVersion.Version,
$CurrentWindowsVersion.'Windows edition',
$CurrentWindowsVersion.'Build number',
$CurrentPatch.outerHTML.Split('>')[1].Replace('</a','').Replace('&#x2014;',' - '),
"KB" + $CurrentPatch.href.Split('/')[-1],
"https://support.microsoft.com" + $CurrentPatch.href,
$LatestAvailablePatch.outerHTML.Split('>')[1].Replace('</a','').Replace('&#x2014;',' - '),
"KB" + $LatestAvailablePatch.href.Split('/')[-1],
"https://support.microsoft.com" + $LatestAvailablePatch.href
)
Return $Table
@AxelLimousin
Copy link

AxelLimousin commented Jul 25, 2021

Hi Trevor,
Could be useful to keep only the date on the Update property : Update = ([DateTime]($item.outerHTML.Split('>')[1]).Split('&#x')[0]).ToShortDateString().

@bezik46
Copy link

bezik46 commented May 15, 2023

It would be great if BIOS version could be added

@ejespino1127
Copy link

Hi, I got an error when running the cmdlet:

PS C:\Users\sicopadmin\workspaces\powershell> .\Get-CurrentPatchInfo -ExcludePreview -ExcludeOutofBand
InvalidOperation: C:\Users\sicopadmin\workspaces\powershell\Get-CurrentPatchInfo.ps1:121
Line |
121 | [void]$Table.Rows.Add(
| ~~~~~~~~~~~~~~~~~~~~~~
| You cannot call a method on a null-valued expression.
PS C:\Users\sicopadmin\workspaces\powershell>

Any advice on how to fix this error from happen again?

@Naveen-Kanakaraj
Copy link

Hi, I got an error when running the cmdlet:

PS C:\Users\sicopadmin\workspaces\powershell> .\Get-CurrentPatchInfo -ExcludePreview -ExcludeOutofBand InvalidOperation: C:\Users\sicopadmin\workspaces\powershell\Get-CurrentPatchInfo.ps1:121 Line | 121 | [void]$Table.Rows.Add( | ~~~~~~~~~~~~~~~~~~~~~~ | You cannot call a method on a null-valued expression. PS C:\Users\sicopadmin\workspaces\powershell>

Any advice on how to fix this error from happen again?

That is because its not a windows 10 probably
here is my fix, replace these lines with line number 8 and it will work (only for windows 11 and 10)

$Winver=Get-ComputerInfo | Select OsName

If($winver.'OsName' -like "Microsoft Windows 11*"){$URI = "https://aka.ms/Windows11UpdateHistory" <#Windows 11 release history#> }
Elseif($winver.'OsName' -like "Microsoft Windows 10*"){$URI = "https://aka.ms/WindowsUpdateHistory" <#Windows 10 release history#> }

@Romascopa
Copy link

So if $CurrentPatch is $null/empty, what does this mean?

@Naveen-Kanakaraj
Copy link

Naveen-Kanakaraj commented Feb 2, 2024

As per my understanding...

if the Device build version (example 22621.3085) does not match the list of builds in the website "https://aka.ms/WindowsUpdateHistory" (check the left pane) there is no record of the build to match it against a patch, so we get a null value error

one more update
for window 11 device you have to use this URL: "https://aka.ms/WindowsUpdateHistory"
and for windows 10 change it to: https://support.microsoft.com/en-us/topic/windows-10-update-history-8127c2c6-6edf-4fdf-8b9f-0f7be1ef3562

$Winver=Get-ComputerInfo | Select OsName

If($winver.'OsName' -like "Microsoft Windows 11*"){$URI = "https://aka.ms/WindowsUpdateHistory" <#Windows 11 release history#> }
Elseif($winver.'OsName' -like "Microsoft Windows 10*"){$URI = "https://support.microsoft.com/en-us/topic/windows-10-update-history-8127c2c6-6edf-4fdf-8b9f-0f7be1ef3562" <#Windows 10 release history#> }

@SMSAgentSoftware
Copy link
Author

Thanks for the feedback and comments!

I have updated the code with the following changes:

  • Added the correct URLs for update history which are now different between W10/11
  • Pull OSEdition from WMI instead of registry (required for Windows 11)
  • Dropped ReleaseId for DisplayVersion for OSVersion (ie 23H2 etc)
  • Improved the extraction of the OSBuild in the parsed array to allow for proper sorting
  • Fixed issue where only 1 update might be returned when using the -ListAvailable switch

@Romascopa
Copy link

Romascopa commented Feb 2, 2024 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment