TL;DR – Programmatically get the latest Windows 10 Cumulative Updates!
Got a request from someone on an internal e-mail Distribution list recently, asking how to find out the latest Windows 10 (or Windows Server 2016) Cumulative Update.
Normally you can find these updates by going to this Microsoft KB article, then finding the right Operating System Version. Then you use the KB article number to go to Windows Update, and find the correct download link, then download the file.
I wanted to update this for another project, so I took it as a challenge to code in PowerShell.
New Tool
For this tool, I placed the source code up on GitHub.com, in a new gist. A gist is just a file that can be edited, version controlled, and shared out publicly on the GitHub.com site.
Given a Windows Version (build number), and a couple other search strings, will programmatically determine what the correct download links are for this Windows 10 (or Windows Server 2016).Â
The output can also be piped into BITS so you can just download locally.
Example
Get the links for the latest Windows 10 Version 1703 Updates:

Additionally we can also download the files using the BITS command Start-BITSTransfer

Source
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<# | |
.SYNOPSIS | |
Get the latest Cumulative update for Windows | |
.DESCRIPTION | |
This script will return the list of Cumulative updates for Windows 10 and Windows Server 2016 from the Microsoft Update Catalog. | |
.NOTES | |
Copyright Keith Garner (KeithGa@DeploymentLive.com), All rights reserved. | |
.LINK | |
https://support.microsoft.com/en-us/help/4000823 | |
.EXAMPLE | |
Get the latest Cumulative Update for Windows 10 x64 | |
.\Get-LatestUpdate.ps1 | |
.PARAMETER Build | |
Windows 10 Build Number used to filter avaible Downloads | |
10240 – Windows 10 Version 1507 | |
10586 – Windows 10 Version 1511 | |
14393 – Windows 10 Version 1607 and Windows Server 2016 | |
15063 – Windows 10 Version 1703 | |
16299 – WIndows 10 Version 1709 | |
.PARAMETER Filter | |
Specify a specific search filter to change the target update behaviour. The default will only Cumulative updates for x86 and x64. | |
If Mulitple Filters are specified, only string that match *ALL* filters will be selected. | |
Cumulative – Download Cumulative updates. | |
Delta – Download Delta updates. | |
x86 – Download x86 | |
x64 – Download x64 | |
.EXAMPLE | |
Get the latest Cumulative Update for Windows 10 x86 | |
.\Get-LatestUpdate.ps1 -Filter 'Cumulative','x86' | |
.EXAMPLE | |
Get the latest Cumulative Update for Windows Server 2016 | |
.\Get-LatestUpdate.ps1 -Filter 'Cumulative','x64' -Build 14393 | |
.EXAMPLE | |
Get the latest Cumulative Updates for Windows 10 (both x86 and x64) and download to the %TEMP% directory. | |
.\Get-LatestUpdate.ps1 | Start-BitsTransfer -Destination $env:Temp | |
#> | |
[CmdletBinding()] | |
Param( | |
[Parameter(Mandatory=$False, HelpMessage="JSON source for the update KB articles.")] | |
[string] $StartKB = 'https://support.microsoft.com/app/content/api/content/asset/en-us/4000816', | |
[Parameter(Mandatory=$False, HelpMessage="Windows build number.")] | |
[ValidateSet('16299','15063','14393','10586','10240')] | |
[string] $BUild = '16299', | |
[Parameter(Mandatory=$False, HelpMessage="Windows update Catalog Search Filter.")] | |
[ValidateSet('x64','x86','Cumulative','Delta',$null)] | |
[string[]] $Filter = @( "Cumulative" ) | |
) | |
#region Support Routine | |
Function Select-LatestUpdate { | |
[CmdletBinding(SupportsShouldProcess=$True)] | |
Param( | |
[Parameter(Mandatory=$true, ValueFromPipeline=$true)] | |
$Updates | |
) | |
Begin { | |
$MaxObject = $null | |
$MaxValue = [version]::new("0.0") | |
} | |
Process { | |
ForEach ( $Update in $updates ) { | |
Select-String –InputObject $Update –AllMatches –Pattern "(\d+\.)?(\d+\.)?(\d+\.)?(\*|\d+)" | | |
ForEach-Object { $_.matches.value } | | |
ForEach-Object { $_ -as [version] } | | |
ForEach-Object { | |
if ( $_ -gt $MaxValue ) { $MaxObject = $Update; $MaxValue = $_ } | |
} | |
} | |
} | |
End { | |
$MaxObject | Write-Output | |
} | |
} | |
#endregion | |
#region Find the KB Article Number | |
Write-Verbose "Downloading $StartKB to retrieve the list of updates." | |
$kbID = Invoke-WebRequest –Uri $StartKB | | |
Select-Object –ExpandProperty Content | | |
ConvertFrom-Json | | |
Select-Object –ExpandProperty Links | | |
Where-Object level -eq 2 | | |
Where-Object text -match $BUild | | |
Select-LatestUpdate | | |
Select-Object –First 1 | |
#endregion | |
#region get the download link from Windows Update | |
Write-Verbose "Found ID: KB$($kbID.articleID)" | |
$kbObj = Invoke-WebRequest –Uri "http://www.catalog.update.microsoft.com/Search.aspx?q=KB$($KBID.articleID)" | |
$Available_KBIDs = $kbObj.InputFields | | |
Where-Object { $_.type -eq 'Button' -and $_.Value -eq 'Download' } | | |
Select-Object –ExpandProperty ID | |
$Available_KBIDs | out-string | write-verbose | |
$kbGUIDs = $kbObj.Links | | |
Where-Object ID -match '_link' | | |
Where-Object { $_.OuterHTML -match ( "(?=.*" + ( $Filter -join ")(?=.*" ) + ")" ) } | | |
ForEach-Object { $_.id.replace('_link','') } | | |
Where-Object { $_ -in $Available_KBIDs } | |
foreach ( $kbGUID in $kbGUIDs ) | |
{ | |
Write-Verbose "`t`tDownload $kbGUID" | |
$Post = @{ size = 0; updateID = $kbGUID; uidInfo = $kbGUID } | ConvertTo-Json –Compress | |
$PostBody = @{ updateIDs = "[$Post]" } | |
Invoke-WebRequest –Uri 'http://www.catalog.update.microsoft.com/DownloadDialog.aspx' –Method Post –Body $postBody | | |
Select-Object –ExpandProperty Content | | |
Select-String –AllMatches –Pattern "(http[s]?\://download\.windowsupdate\.com\/[^\'\""]*)" | | |
Select-Object –Unique | | |
ForEach-Object { [PSCustomObject] @{ Source = $_.matches.value } } # Output for BITS | |
} | |
#endregion |
Nice tool!
I added this to the build parameter so you don’t have to type your version if you run it for the local machine.
[Parameter(Mandatory=$False, HelpMessage=”Windows build number.”)]
[ValidateSet(‘15063′,’14393′,’10586′,’10240’)]
[string] $BUild = ([environment]::OSVersion.Version).Build,
True, however, the most common scenario for this script is for downloads to patch offline OS images.
If you want to patch the current online OS, why not just run Windows Update?
Thanks for this. Do you have a version for Win7?
Microsoft does not produce Cumulative Updates for Windows 7, so no.
I was referring to the quality rollups (i.e. 2017-07 Security Monthly Quality Rollup for Windows 7 for x64-based Systems (KB4025341)) since they are cumulative.
Sorry, support for Windows 7 is not a priority.
Windows 10 is interesting because we can take the Cumulative package and apply it offline, we can’t do that with Windows 7.
For windows 7, I recommend WSUS or ZTI_WindowsUpdate.wsf.
Did the API change? I just found your script but the URI used for $StartKB just gives an “API not found” message.
Thanks for sharing!
Yes Microsoft changed the API, and I have updated the Source on GitHub (should auto-embed the correct version above..)
Thanks for the update, by the way. Can you provide info on how you found this URL? I’m looking to setup something similar for 2012 R2 since it also uses cumulative updates now but haven’t been able to find a similar API location. I found the Microsoft Security Updates API but haven’t had a chance to work with it. Plugging a different URL and build number into your script would be a fast solution. 🙂
Thanks, I don’t recall how I found this, I think I got started from a blog post elsewhere ( I don’t recall where), and then did screen scraping to find the correct download links.
I am getting the following error:
PS C:\Users\*\Desktop\Updates> .\Get-LatestUpdate.ps1 -Filter ‘Cumulative’,’x64′ -Build 14393
Method invocation failed because [System.Version] does not contain a method named ‘new’.
At C:\Users\*\Desktop\Updates\Get-LatestUpdate.ps1:81 char:9
+ $MaxValue = [version]::new(“0.0”)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (:) [], RuntimeException
+ FullyQualifiedErrorId : MethodNotFound
Weird, [version]::new(“0.0”) should work in powershell, specifically, what version are you running? $PSVersionTable should give a hint.
Local Machine:
Name Value
—- —–
PSVersion 5.1.15063.0
PSEdition Desktop
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0…}
BuildVersion 10.0.15063.0
CLRVersion 4.0.30319.42000
WSManStackVersion 3.0
PSRemotingProtocolVersion 2.3
SerializationVersion 1.1.0.1
Deployment Server:
Name Value
—- —–
PSVersion 4.0
WSManStackVersion 3.0
SerializationVersion 1.1.0.1
CLRVersion 4.0.30319.42000
BuildVersion 6.3.9600.18728
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0}
PSRemotingProtocolVersion 2.2
Got the same error on both machines.
should this be updated to incude 1709? Under Windows 10 Build Number do you need to include 16299?
Updated
Can you provide info on how you found Microsoft API for Creating JSON file for the Microsoft Cumulative article?
I am looking for creating similar JSON file for Microsoft Security Essentials Update.
Not sure what you are asking for? The code to find/download files from the Microsoft Catalog are provided in the article.
If you are asking how I figured it out? The answer is 3 years of Computer Science, and 20+ years of experience working on Windows programming.
Thanks for the update.
My question is how json source file is created for the cumulative updates.
; JSON source for the update KB ‘https://support.microsoft.com/app/content/api/content/asset/en-us/4000816’
When above link is clicked it downloads json file from support Microsoft site.
But I am not able to find a similar API location for Microsoft security essentials.
When testing on my machine, it only shows cumulative updates from august? it is now November. What do you suspect is wrong? It does not matter what build string I use. It shows:
KB4343909 2018-08 Cumulative Update for Windows 10 Version 1803 for x64-based Systems (KB4343909)
The methods I document here in the blog may not work anymore as Microsoft has changed the KB article format.
I recently updated my code on the issue, you can find it here:
https://github.com/keithga/DeploySharedLibrary/tree/master/DeployShared/Update
Thanks, it works now.