ee1242
September 12, 2020, 11:48am
1
Every month when the latest Microsoft OS updates are released I have to go to the Microsoft Update Catalog website ( https://www.catalog.update.microsoft.com/Home.aspx ), and do the following:
Type in the month and the OS version of the updates that I want to download (e.g. 2020-09 2012 R2 ).
Copy the names of each of the updates and create folders with these names on my local computer
Download the updates into the folder names matching the updates
I have to repeat this process to manually download updates for Server 2008R2, Server 2012R2, Server 2016, and Server 2019.
This has to be done since these updates don’t always appear within the SCCM Software Center console and because it always takes the SCCM team around a month to approve the updates after they have been released from Microsoft. I always need to install the latest updates whenever I patch servers.
Once these updates have been downloaded I put these updates into a folder on a NAS so that these updates can be copied over to servers that need them.
So I’m hoping that a PowerShell script can be written where I can provide the month and OS version (e.g. 2020-09 2012 R2 ) that will then:
Create the folder names corresponding to the updates in the target folder
Download the updates into the folder names matching the updates
Well hope no more because it’s absolutely possible! If you hit a snag come back and show what’s not working and plenty of us will be happy to help. Happy scripting!
ee1242
September 12, 2020, 1:26pm
3
Can you provide me with the PowerShell script that will do this?
nitbha
September 13, 2020, 1:55am
4
ee124209
You can refer below scripts which will help you to achieve your task.
Script is to download the updates based on the KB article
https://gallery.technet.microsoft.com/42-POWERSHELL-TO-UPDATES-7071ab32
How convert html into PowerShell object
https://sysjam.wordpress.com/2016/01/08/consuming-html-tables-with-powershell/
Thanks,
Nitesh
Hi sorry for hijacking the thread, but I´ve been trying to get this to work now for a while now but there still is something I´m not getting. I´ve managed to get it to download the KB´s from the txt file but I can´t get it to filter it will always download everything under the KB# so both x86 and x64.
I´ve been trying webscraping to narrow down how til filter it out and got it somewhat to work when I did it in a small script but when I tried to implement it to the script it wouldn´t work only says “No such article exist”
Can someone please point to me what I´m doing wrong…
In line 7 there is the issue if I comment out the things after # the script runs but downloads all the KB´s if uncommented it says there is no KB´s
foreach ($KB in $KB_LIST)
{
$COMPLETE_LINKS=$LINKS=$ONEURL=$KB_ROOT_FOLDER= $null
$ONEURL="https://www.catalog.update.microsoft.com/Search.aspx?q=$KB"
Write-Host ""
write-host "Connecting to URL $ONEURL." -ForegroundColor Gray
$COMPLETE_LINKS=(Invoke-WebRequest -Uri $ONEURL -UseBasicParsing).Links | Where-Object id -like '*_link' | Select-Object @{Name = 'GUID';Expression = { $_.Id -replace '_link', '' }} #, @{Name = '*'; Expression = {if( $_.innerText -match 'x64') {$Matches[0].ToLower()}}}, innertext | Where-Object {$_.innertext -match $OS_FILTER}
if($null -ne $COMPLETE_LINKS)
{
#Filtering based on architecure
if($PLATFORM_FILTER -eq 'x86')
{
$LINKS = $COMPLETE_LINKS | Where-Object { $_.platform -eq 'x86' -or $_.platform -eq $null}
}
elseif($PLATFORM_FILTER -eq 'x64')
{
$LINKS = $COMPLETE_LINKS | Where-Object { $_.platform -eq 'x64'}
}
elseif($PLATFORM_FILTER -eq 'ARM64')
{
$LINKS = $COMPLETE_LINKS | Where-Object { $_.platform -eq 'ARM64'}
}
write-host "List of patches found:" -ForegroundColor Green
write-host $($LINKS.innerText) -ForegroundColor Gray
Write-Host ""
Proper indentation as well as a decent IDE will help you see when you’re missing not only one, but two closing curly braces. You are also not showing how you populate your KB list.
foreach ($KB in $KB_LIST)
{
$COMPLETE_LINKS=$LINKS=$ONEURL=$KB_ROOT_FOLDER= $null
$ONEURL="https://www.catalog.update.microsoft.com/Search.aspx?q=$KB"
Write-Host ""
write-host "Connecting to URL $ONEURL." -ForegroundColor Gray
$COMPLETE_LINKS=(Invoke-WebRequest -Uri $ONEURL -UseBasicParsing).Links | Where-Object id -like '*_link' | Select-Object @{Name = 'GUID';Expression = { $_.Id -replace '_link', '' }} #, @{Name = '*'; Expression = {if( $_.innerText -match 'x64') {$Matches[0].ToLower()}}}, innertext | Where-Object {$_.innertext -match $OS_FILTER}
if($null -ne $COMPLETE_LINKS)
{
#Filtering based on architecure
if($PLATFORM_FILTER -eq 'x86')
{
$LINKS = $COMPLETE_LINKS | Where-Object { $_.platform -eq 'x86' -or $_.platform -eq $null}
}
elseif($PLATFORM_FILTER -eq 'x64')
{
$LINKS = $COMPLETE_LINKS | Where-Object { $_.platform -eq 'x64'}
}
elseif($PLATFORM_FILTER -eq 'ARM64')
{
$LINKS = $COMPLETE_LINKS | Where-Object { $_.platform -eq 'ARM64'}
}
}
}
write-host "List of patches found:" -ForegroundColor Green
write-host $($LINKS.innerText) -ForegroundColor Gray
Write-Host ""
Hi Doug and thanks for the reply. Yeah sorry I didn´t post the whole script since it´s quite large but I guess you need all find out the problem. It´s basically the same script as Evila posted the link to but just with some tweeks I´ve made to get it to start do something.
The KB are stored in a .txt file for now just on C:\Temp while testing I´ve just been using this KB for testing KB4576630
Here is all the code
function Get-UpdateURL($GUID)
{
foreach ($oneGUID in $GUID)
{
$POST_BODY = @{ updateIDs = "[{$( $POST_BODY_TEMPLATE -f $oneGUID )}]" }
if (( Invoke-WebRequest -Uri $UPDATE_CATALOG_DOWNLOAD_LINK -UseBasicParsing -Method Post -Body $POST_BODY).Content -match $UPDATE_URL_PATTERN)
{
$Matches[0]
}
else
{
$null
}
}
}
#Variable declaration
$UPDATE_CATALOG_DOWNLOAD_LINK='https://www.catalog.update.microsoft.com/DownloadDialog.aspx'
$UPDATE_URL_PATTERN = "https?://download\.windowsupdate\.com\/[^ \'\""]+"
$POST_BODY_TEMPLATE = '"updateID": "{0}"'
$OS_FILTER=' Windows Server 2012 R2 '
$PLATFORM_FILTER='x64' #X86,ARM64
#$OSBUILD_FILTER='1809'
$ParentPath="$env:USERPROFILE"
$KB_LIST=Get-Content 'c:\Temp\KBlist.txt'
$PATCH_REPORT="C:\Temp\Patch_Download_Report.html"
if(test-path $PATCH_REPORT)
{
Remove-Item $PATCH_REPORT -Force -ErrorAction SilentlyContinue
}
$DOWNLOAD_RESULT=@()
#--CSS formatting
$HEADER_FORMAT=@'
<style type="text/css">
h1, h5,h2, th { text-align: left; font-family: Segoe UI;font-size: 13px;}
h4 { text-align: left; font-family: Segoe UI;font-size: 12px;}
table { margin: left; font-family: Segoe UI; box-shadow: 10px 10px 5px #888; border: thin ridge grey; }
th { background: #0046c3; color: #fff; max-width: 400px; padding: 5px 10px; font-size: 12px;}
td { font-size: 11px; padding: 5px 20px; color: #000; }
tr { background: #b8d1f3; }
tr:nth-child(even) { background: #dae5f4; }
tr:nth-child(odd) { background: #b8d1f3; }
</style>
'@
Clear-Host
foreach ($KB in $KB_LIST)
{
$COMPLETE_LINKS=$LINKS=$ONEURL=$KB_ROOT_FOLDER= $null
$ONEURL="https://www.catalog.update.microsoft.com/Search.aspx?q=$KB"
Write-Host ""
write-host "Connecting to URL $ONEURL." -ForegroundColor Gray
$COMPLETE_LINKS=(Invoke-WebRequest -Uri $ONEURL -UseBasicParsing).Links | Where-Object id -like '*_link' | Select-Object @{Name = 'GUID';Expression = { $_.Id -replace '_link', '' }}#, @{Name = 'platform';Expression = {if( $_.innerText -match '\b(x86|x64|arm64)\b') {$Matches[1].ToLower()}}}, innertext | Where-Object {$_.innertext -match $OS_FILTER}
if($null -ne $COMPLETE_LINKS)
{
#Filtering based on architecure
if($PLATFORM_FILTER -eq 'x86')
{
$LINKS = $COMPLETE_LINKS | Where-Object { $_.platform -eq 'x86' -or $_.platform -eq $null}
}
elseif($PLATFORM_FILTER -eq 'x64')
{
$LINKS = $COMPLETE_LINKS | Where-Object { $_.platform -eq 'x64'}
}
elseif($PLATFORM_FILTER -eq 'ARM64')
{
$LINKS = $COMPLETE_LINKS | Where-Object { $_.platform -eq 'ARM64'}
}
write-host "List of patches found:" -ForegroundColor Green
write-host $($LINKS.innerText) -ForegroundColor Gray
Write-Host ""
#Creating folder for each specific KB article
$KB_ROOT_FOLDER="$ParentPath\$KB"
if(Test-Path $KB_ROOT_FOLDER)
{
Remove-Item $KB_ROOT_FOLDER -Force -ErrorAction SilentlyContinue
}
New-Item -ItemType Directory -Path $KB_ROOT_FOLDER -ErrorAction SilentlyContinue | Out-Null
if($null -ne $LINKS)
{
foreach ($ONELINK in $Links)
{
if($null -ne $ONELINK)
{
Write-Host "Processing link $($ONELINK.innerText)"
$DOWNLOAD_LINK = Get-UpdateURL $ONELINK.GUID
if($null -ne $DOWNLOAD_LINK)
{
$DOWNLOAD_FOLDER_NAME=$ONELINK.innerText.TrimEnd(" ")
$DOWNLOAD_FILE_NAME=($DOWNLOAD_LINK -split "/")[-1]
Write-Host "Downloading update $DOWNLOAD_FILE_NAME...."
New-Item -ItemType Directory -Path "$KB_ROOT_FOLDER\$DOWNLOAD_FOLDER_NAME" | Out-Null
$DOWNLOAD_PATH="$KB_ROOT_FOLDER\$DOWNLOAD_FOLDER_NAME\$DOWNLOAD_FILE_NAME"
try
{
Invoke-WebRequest $DOWNLOAD_LINK -OutFile $DOWNLOAD_PATH
Write-Host "Download completed for update $DOWNLOAD_FILE_NAME" -ForegroundColor Green
$KB_STATUS=[PSCustomObject]@{
'KB Article' = $KB
'Downloaded' = 'YES'
'DownloadPath' = $DOWNLOAD_PATH
'URL' = $DOWNLOAD_LINK
'Patch Name'=$DOWNLOAD_FILE_NAME
}
$DOWNLOAD_RESULT+=$KB_STATUS
$KB_STATUS=$null
}
catch
{
Write-Host "Exception occured during download." -Foreground Red
$KB_STATUS=[PSCustomObject]@{
'KB Article' = $KB
'Downloaded' = 'Failed'
'DownloadPath' = $DOWNLOAD_PATH
'URL' = $DOWNLOAD_LINK
'Patch Name'=$DOWNLOAD_FILE_NAME
}
$DOWNLOAD_RESULT+=$KB_STATUS
$KB_STATUS=$null
}
}
else
{
Write-Host "Source URL for patch $ONELINK does not exist" -ForegroundColor Red
$KB_STATUS=[PSCustomObject]@{
'KB Article' = $KB
'Downloaded' = "Source URL for patch $ONELINK does not exist"
'DownloadPath' = "Does not Exist"
'URL' = "Does not Exist"
'Patch Name'="Does not Exist"
}
$DOWNLOAD_RESULT+=$KB_STATUS
$KB_STATUS=$null
}
}
$ONELINK=$DOWNLOAD_FOLDER_NAME=$DOWNLOAD_FILE_NAME=$DOWNLOAD_LINK=$DOWNLOAD_PATH=$null
}
}
else
{
write-host "No patch exists for the selected Filter type." -ForegroundColor Red
$KB_STATUS=[PSCustomObject]@{
'KB Article' = $KB
'Downloaded' = "No patch exists for the selected Filter type."
'DownloadPath' = ""
'URL' = ""
'Patch Name'=""
}
$DOWNLOAD_RESULT+=$KB_STATUS
$KB_STATUS=$null
}
}
else
{
write-host "No such kb article exists" -ForegroundColor Red
$KB_STATUS=[PSCustomObject]@{
'KB Article' = $KB
'Downloaded' = "No such KB article exist."
'DownloadPath' = ""
'URL' = ""
'Patch Name'=""
}
$DOWNLOAD_RESULT+=$KB_STATUS
$KB_STATUS=$null
}
}
Anyone got clue why it´s not working ?