Windows Update for OS Builders – Part 2

This is the second post in the series, the first one can be found here:

In this post we shall look as implementing a detection and download script for Windows Update. This code is the VBScript version of this script.

The PowerShell implementation will be available later in the series. However, if you are not using this for Windows 10 or Windows 2012 R2 or above, I’d recommend using this version due to the backward compatibility that VBScript brings.

VBScript has been working in all versions of Windows since Windows 2000 (Even Windows NT supported it as a add-on). This universality makes it a great candidate for tooling in creating builds. Indeed, anyone that has ever used and version of MDT will realised that a huge amount of the code is just VBScript.

Anyway, lets set the scene before we dump the code on you.

This code in it’s current form can just be run from the command line like this:-

cscript WSUSDownload.vbs

This is the most basic usage and it will download the latest copy of WSUSSCN2.CAB (If it hasn’t already bee downloaded). It then performs a detection using this definition file and creates a folder called “Updates” into which the detected updates will be downloaded into a sub-folder for each download. I’ll come to what it looks like and how to use it later on.

The other althernative way to use this, one born of necessity, is to detect the patches required, but instead of downloading them, a file is created containing the download details. This file can then be transferred to somewhere else where the same script can read this file and create the folder full of updates. This has been essential when I’ve been working in secure locations with limited access to the internet. It means you can detect patches required on a secure locked down machine, but download the patches using a machine with a free internet connection. Probably doesn’t help most people, but if you work for a Bank or the Government you’ll recognise the problem.

To create the export file:

cscript WSUSDownload.vbs /e MyFile.txt

To download the patches now defined in MyFile.txt:

cscript WSUSDownload.vbs /i MyFile.txt

This method requires that you have already downloaded the WSUSSCN2.CAB file to the same folder as the script (It’s a secure environment remember, you probably can’t download the definition file)

NOTE: There is a section at the top of the script to define you proxy server details if you use one.

Anyway, time for some code. Be warned, read it, understand it and amend it to your needs. If you don’t undestand it, don’t use it as I won’t be there to hold you hands. The same is true for any code downloaded from the internet, it’s a dark and dangerous place.

Option Explicit
'Windows Updates Handler Script
'Author: Paul Prior
'Company: Consulting Services Limited.
'Compatibility: Windows XP, WIndows 2003, WIndows Vista, Windows 7, Windows 2008, Windows 2008 R2, Windows 2012, Windows 2012 R2
'Additional Notes: Works for Windows 10 and Windows 2016 Preview but is sub-optimal as pkgmgr.exe is depricated. See the PowerShell version.
'Feel free to modify and reuse. Attribution would be appreciated.
'As always, test, test and test again. Don't blame me if your house burns down after using this script. Like any internet script, only use it once you understand what it does.
Const ForWriting = 2
Const ForReading = 1
Dim ProxyName : ProxyName = "" ' Change if using a proxy server to something likre this: Dim ProxyName : ProxyName = ""
Dim objFSO : Set objFSO = CreateObject("Scripting.FileSystemObject")
Dim objHTTP: Set objHTTP = CreateObject("WinHttp.WinHttpRequest.5.1")
Dim Shell : Set Shell = CreateObject("WScript.Shell")
Dim strWorkingDirectory : strWorkingDirectory = objFSO.GetParentFolderName(WScript.ScriptFullName)
If Instr(1, WScript.FullName, "CScript", vbTextCompare) = 0 Then
	Shell.Run "cscript //NoLogo """ & WScript.ScriptFullName & """", 1, False
End If

Dim Mode : Mode = "DetectAndDownload"
Dim FileName : FileName = ""
Dim objFile
Dim Args : Set Args = Wscript.Arguments
If Args.Count > 0 Then
	If InStr(UCase(Args(0)),"/I") > 0 Then
		Mode = "Import"
	    FileName = Args(1)
	    Mode = "Export"
	    FileName = Args(1)
	End If
End If

If InStr(FileName,"\") = 0 Then
	FileName = strWorkingDirectory & "\" & FileName
End If

If Mode <> "Import" Then
	If Not objFSO.FileExists(strWorkingDirectory & "\WSUSSCN2.CAB") Then
		WScript.echo "Downloading WSUSSCN2.CAB definition file..."
		HTTPDownload "", strWorkingDirectory & "\WSUSSCN2.CAB"
	End If
End If

Dim objUpdateSession
Dim objUpdateServiceManager
Dim objUpdateService
Dim objUpdateSearcher
On Error Resume Next
Set objUpdateSession = CreateObject("Microsoft.Update.Session")
Set objUpdateServiceManager = CreateObject("Microsoft.Update.ServiceManager")
Set objUpdateService = objUpdateServiceManager.AddScanPackageService("Offline Sync Service", strWorkingDirectory & "\WSUSSCN2.CAB")
Set objUpdateSearcher = objUpdateSession.CreateUpdateSearcher()
On Error Goto 0

If Mode="Export" Then
	Set objFile = objFSO.CreateTextFile(FileName, True)
End If

If Mode="Import" Then
	Set objFile = objFSO.OpenTextFile(FileName, ForReading)
End If

If Mode="Import" Then
	Dim ThisLine : ThisLine = ""
	Do Until objFile.AtEndOfStream
		ThisLine = objFile.ReadLine
		WScript.Echo ThisLine
		On Error Resume Next
		objFSO.CreateFolder(objFSO.GetParentFolderName(Split(ThisLine, vbTab)(1)))
		On Error Resume Next
		HTTPDownload Split(ThisLine, vbTab)(0), Split(ThisLine, vbTab)(1)
	Wscript.Echo "Searching for updates..." & vbCRLF
	objUpdateSearcher.ServerSelection = 3 ' ssOthers
	objUpdateSearcher.ServiceID = objUpdateService.ServiceID
	Dim objSearchResult : Set objSearchResult = objUpdateSearcher.Search("IsInstalled=0")
	Dim objUpdates : Set objUpdates = objSearchResult.Updates
	If objSearchResult.Updates.Count = 0 Then
		WScript.Echo "There are no applicable updates."
	End If
	Wscript.Echo "Offline Scan Results:" & vbCRLF
	Dim I
	For I = 0 to objSearchResult.Updates.Count-1
		Dim objUpdate : Set objUpdate = objSearchResult.Updates.Item(I)
		WScript.Echo I + 1 & "> " & objUpdate.Title
		On Error Resume Next
		objFSO.CreateFolder("Updates\" & objUpdate.Title)
		On Error Goto 0
		If objUpdate.BundledUpdates.Count > 0 Then
			Dim j
			For j = 0 To objUpdate.BundledUpdates.Count - 1
				Dim k
				For k = 0 To objUpdate.BundledUpdates(j).DownloadContents.Count - 1
					Dim DownloadFileName : DownloadFileName = Mid(objUpdate.BundledUpdates(j).DownloadContents(k).DownloadUrl, InstrRev(objUpdate.BundledUpdates(j).DownloadContents(k).DownloadUrl, "/") + 1)
					Dim Extension : Extension = Mid(DownloadFileName, InStrRev(DownloadFileName, "."))
					DownloadFileName = Left(DownloadFileName, InStrRev(DownloadFileName, "_")-1)
					DownloadFileName = "Updates\" & objUpdate.Title & "\" & DownloadFileName & Extension
					HTTPDownload objUpdate.BundledUpdates(j).DownloadContents(k).DownloadUrl, DownloadFileName
		End If
	On Error Resume Next
End If


Sub HTTPDownload(URL, strFile)
	Select Case Mode
		Case "Export"
			objFile.WriteLine(URL & vbTab & strFile)
		Case Else
			If ProxyName <> "" Then
				objHTTP.SetProxy 2, ProxyName
			End If
			objHTTP.Option(6) = True
			objHTTP.Open "GET", URL, False
			Dim ADODB : Set ADODB = CreateObject("ADODB.Stream")
			ADODB.Type = 1
			ADODB.Write objHTTP.ResponseBody 
			ADODB.SaveToFile strFile
	End Select
End Sub

Once you patches are downloaded, they look like this:



You’re probably wondering what you do with them to install them. If you are running XP or 2003 they are mostly in .EXE file formats, if you have Windows 7/2008 R2, you’ll see there are .EXE and mostly .CAB files. Windows 10 appears to only download .CAB files, but there could be the odd .EXE file. Those of you used to installing patches manually are probably used to the .MSU files, well what you have here is the equivalent of an expanded .MSU file. In the next episode of this series I’ll cover what we are going to do with these and provide another script that’ll install them for you.

Until next time, be safe, and don’t break stuff you can’t fix.

Leave a comment

Your email address will not be published. Required fields are marked *