My company concerns security, request us-to-deploy the newest patches on our servers in time, even we have FIREWALL/ENCRYP tion internally.
With the number of servers increasing, there must is some servers can ' t be patched as expected, probably caused by sccm/ws US or incorrect configuration on server, plus 3rd party patch scanning software and presures from security team, support t Eam like me has to patches missing patches one by one. This makes me has to paste the MS number in Google, and find the correct KB, then download and install, turns out it's a Mess.
To make life easier, I do the script to automatic download patches from MS with scheduled time, the script would grab Cont Ents from Ms RSS and get MS numbers and links, it would loop Ms Links and grabs KBs, and download patches to local path.
Security Rss:https://technet.microsoft.com/en-us/security/rss/bulletin
Workflow:get contents of RSS, grab Ms numbers and links, Enum ms links and grab KBs--filter out some KB-& Gt Grab KB Details link, grab kb download links, download
The script, step by step,
$URL = ' https://technet.microsoft.com/en-us/security/rss/Bulletin ' $ExcludeProducts = ' lync| Itanium|for mac ' $IncludeProducts = ' server ' $ExcludePatches = '-ia64| windows6\.0|-rt-| servicebusserver ' $PatchStoreTo = '. \ '
Some variables defined,
$URL is the link of RSS;
$ExcludeProducts when get the contents of MS link, use regular expression to filter out unwanted product, for me is Lync a ND patches for Itanium CPU;
$IncludeProducts After product filter, I want to the filter again for KBs for "server";
$ExcludePatches is another filter, when final get patch link, I don ' t want patches for Itanium (why? Because some KB doesn ' t has enough details, so this filter added);
$PatchStoreTo is a path to store patches.
$WebClient = new-Object system.net.webclient$webclient.encoding = [System.text.encoding]::utf8
Create the WebClient object and set the encoding
Do{ $RSSContent = $WebClient. Downloadstring ($Url)}while( $ (if (!$?) { write-host ' Failed to get RSS '-foregroundcolor Red start-sleep-seconds $true }))
Get the contents of RSS, if failed, would report with red words and sleep minutes to do again.
([XML] $RSSContent). Rss.channel.Item | Sort-object link | %{...}
Convert RSS contents to XML type and then can easily retrieve data.
$MSRC _url = $_. Link write-host "Processing: [$MSRC _url]"-foregroundcolor Yellow $MSRC = ([regex]:: Match ($MSRC _url, ' (? i) ms\d+-\d+$'). Value Write-host "MS number: [$MSRC]"-foregroundcolor Green if (! ( Test-path-literalpath "$PatchStoreTo \ $MSRC")) { do {new-item-path "$PatchStoreTo \ $MSRC"- ItemType Directory | out-Null} while($ (if (!$?) {write-host ' Failed to create MSRC folder '-foregroundcolor Red start-sleep $true })}
Ms Link stores in $MSRC _url, and output to screens as color yellow, then use regular expression to grab MS number and store D in $MSRC, after that create a folder named as MS number to store patches.
Write-host "Trying to capture KBs from MSRC URLs"-foregroundcolor Yellow do { $MSContent = $null $MSContent = $WebClient. downloadstring ($MSRC _url) } while( $ (if (!$?) { write-host ' Failed to capture MSRC content '-foregroundcolor Red start-sleep $true })
Above codes is to grab MS link contents and store in $MSContent.
Ms Link is like https://technet.microsoft.com/en-us/library/security/MS14-063, Ms Contents is the source codes behind the Web page.
[Regex]::matches ($MSContent, ' (? i) <tr>[\s\s]+?<a href= "(http://www.microsoft.com/downloads/details.aspx \? Familyid=[\w\-]+?) " >[\s\s]+?\ ((\d{7}) \) | %{...}
The code is to grab KB information, like KB number, and KB link.
It would match contents like below screenshot, all characters in the Grah would be matched by the regular expression pattern .
Write-host "KB: [$ ($_. GROUPS[2]. Value)] "-nonewline-foregroundcolor Green if ($_. Value-imatch $ExcludeProducts) { write-host " ---excluded: [$ ($Matches [0])]"-Foregroundcolor Red } else { if ($_. Value-notmatch $IncludeProducts) { write-host " ---excluded:not match [$IncludeProducts]"- foregroundcolor Red return } $KBNumber = "kb$ ($_. GROUPS[2]. Value) "
Write-host "' Ndownload URL: [$ ($_. GROUPS[1]. Value)] "-foregroundcolor Gray
Above code excludes the KBs matched product names in $excludeProducts, and left KB filtered again by $IncludeProducts, fin Al passed KBs store in $KBNumber.
Do { $KBContent = $null $KBContent = $WebClient. downloadstring ($_. Groups[1]. Value) }while( $ (if (!$?) { write-host ' Failed to capture KB content '-foregroundcolor Red start-sleep $true })
Above code get contents from KB link and stores in $KBContent,
KB link looks like this in $MSContent: http://www.microsoft.com/downloads/details.aspx?familyid= 8a59fc6d-cbad-4905-842b-e5aa1fc6fedf
Access KB Link would automatic redirect to:http://www.microsoft.com/en-us/download/details.aspx?id=44400
Surely this is a automation behavior of Web server, I don ' t need to does anything in the script. Anyway the KB link page doesn ' t contain patch link, it just ask for confimation on languages and provide us some KB detail s, you can find screenshot followed.
As followed, I need to analysis KB contents, and find the page called "Confirmation.aspx". Actually I can see it if I move my cursor on the "Download" button.
$KBConfirm = ([Regex]::match ($KBContent, ' (? i) href= "(confirmation.aspx\?id=\d+))). Groups[1]. Value $KBConfirm = "http://www.microsoft.com/en-us/download/$KBConfirm" write-host "KB confirm URL: [$ Kbconfirm] "-foregroundcolor Gray do { $KBContent = $null $KBContent = $ Webclient.downloadstring ($KBConfirm) }while($ (if (!$?) {write-host ' Failed to capture KB download content '-foregroundcolor Red start-sleep $true })
Codes used to grab ' confirmation.aspx ' page link from KB contents, your may find the ' id ' behind ' confirmation.aspx ' is the Same like "details.aspx" in KB link, but just for Safey, I choose to grab "confirmation.aspx" page from KB content.
$KBLinks = @() $KBLinks = [Regex]::matches ($KBContent, ' (? i) <a href= "(http://download.microsoft.com/ download/.+?) ". +?>click here</span> ') | %{ $_. Groups[1]. Value } $KBLinks = @ ($KBLinks | Sort-object-Unique) write-host "The KB contains updates: [$ ($KBLinks. Count)]"-foregroundcolor Green
After I get the contents of ' confirmation.aspx ', I use regular expression to match patch links and does a unique sort for fi NAL results, now $KBLinks contains all patches belong to that KB.
Followed screenshot is the ' confirmation.aspx ', it contains all patches download link, I used regular expression again to Grab those links.
$KBLinks | %{ $FileName = $null $FileName = $_. Split ('/') [-1] if ($FileName-imatch $ExcludePatches) { write-host "Patch excluded: [$ ($Matches [ 0])] "-foregroundcolor Red return }
Now I had patch links in hand, the job was download, but I do another filter before the downloading, as I mentioned P reviously, sometimes KB contents don ' t has enough information, so-I use another filter to remove patches I don ' t Want by patch names.
if (Test-path-path $FilePath) { write-host ' File already exists, skip! '-foregroundcolor Gray } Else { do { $WebClient. DownloadFile ($_, $FilePath) }while($ (if (! $? {write-host ' Download file failed! '-foregroundcolor Red start-sleep-seconds $true}) }
Real download codes here, if patch already exists, script would skip it.
Last, the one screenshot when script running, and the full script followed.
Full script here,
$URL = ' https://technet.microsoft.com/en-us/security/rss/Bulletin ' $ExcludeProducts = ' lync| Itanium|forMac ' $IncludeProducts =' Server ' $ExcludePatches = '-ia64| windows6\.0|-rt-|Servicebusserver ' $PatchStoreTo = '. \' $WebClient = new-Object system.net.webclient$webclient.encoding = [System.Text.Encoding]::Utf8do{$RSSContent = $WebClient. Downloadstring ($URL)}while($ (if (!$?{write-host ' Failed to get RSS '-Foregroundcolor Red Start-sleep-seconds $true})) ([XML] $RSSContent). Rss.channel.Item | Sort-object link | %{$MSRC _url = $_. Link write-host "Processing: [$MSRC _url]"-Foregroundcolor Yellow $MSRC = ([Regex]::match ($MSRC _url, ' (? i) ms\d+-\d+$‘)). Value write-host "MS number: [$MSRC]"-Foregroundcolor Green if (!) ( Test-path-literalpath "$PatchStoreTo \ $MSRC")) {Do{New-item-path "$PatchStoreTo \ $MSRC"-itemtype Directory | out-Null} while($ (if (!$?{write-host ' Failed to create MSRC folder '-Foregroundcolor Red Start-sleep $true})} Write-host "Trying to capture KBs from MSRC URL"-Foregroundcolor Yellow Do{$MSContent = $null $MSContent = $WebClient. downloadstring ($MSRC _url)} while($ (if (!$?{write-host ' Failed to capture MSRC content '-Foregroundcolor Red Start-sleep $true})) [Regex]::matches ($MSContent, ' (? i) <tr>[\s\s]+?<a href= "(http://www.microsoft.com/downloads/details.aspx\? Familyid=[\w\-]+?) " >[\s\s]+?\ ((\d{7}) \) | %{write-host "KB: [$ ($_. GROUPS[2]. Value)] "-nonewline-Foregroundcolor Green if ($_. Value-imatch $ExcludeProducts{Write-host "---excluded: [$ ($Matches [0])]"-Foregroundcolor Red} else{if ($_. Value-notmatch $IncludeProducts{write-host "---excluded:not match [$IncludeProducts]"-Foregroundcolor Red return} $KBNumber = "kb$ ($_. GROUPS[2]. Value) "Write-host "' Ndownload URL: [$ ($_. GROUPS[1]. Value)] "-Foregroundcolor gray<# if (! ( Test-path-path "$MSRC \ $KBNumber")) {Do{new-item-name "$MSRC \ $KBNumber"-itemtype Directory | out-Null} while($ (if (!$?{write-host ' Failed to create KB folder '-Foregroundcolor Red Start-sleep $true}))}#> do{$KBContent = $null $KBContent = $WebClient. downloadstring ($_. Groups[1]. Value)}while($ (if (!$?{write-host ' Failed to capture KB content '-Foregroundcolor Red Start-sleep $true})) $KBConfirm = ([Regex]::match ($KBContent, ' (? i) href= "(confirmation.aspx\?id=\d+))). Groups[1]. Value $KBConfirm = "http://www.microsoft.com/en-us/download/$KBConfirm"Write-host "KB confirm URL: [$KBConfirm]"-Foregroundcolor Gray Do{$KBContent = $null $KBContent = $WebClient. Downloadstring ($KBConfirm)}while($ (if (!$?{write-host ' Failed to capture KB download content '-Foregroundcolor Red Start-sleep $true})) $KBLinks = @() $KBLinks = [Regex]::matches ($KBContent, ' (? i) <a href= "(http://download.microsoft.com/download/.+?). +?>click here</span> ') | %{ $_. Groups[1]. Value} $KBLinks = @ ($KBLinks | Sort-object- Unique) Write-host "The KB contains updates: [$ ($KBLinks. Count)]"- foregroundcolor Green $ Kblinks | % {$FileName = $null $FileName = $_. Split ('/') [-1 ] if ($FileName-imatch $ExcludePatches ) {write-host "Patch excluded: [$ ($Matches [0])]"- foregroundcolor Red return } $FilePath = $null $FilePath = "$MSRC \ $FileName" write-host "going to Downlo Ad file: [$FilePath] "- foregroundcolor Gray $FilePath =" $PatchStoreTo \ $FilePath "if (Test-path-path $FilePath span>) {write-host ' File already exists, skip! '- foregroundcolor Gray} else {do {$WebClient. Download File ($_, $FilePath )}while ($ (if (!$? ) {write-host ' Download file failed! '- foregroundcolor Red start-sleep-seconds $true })} } } }}
PS, about the proxy, WebClient class would use proxy settings on IE automatically, if want download patches via proxy, set IE to the right settings.
Use PowerShell script to download Windows patches monthly