Client Management Suite

 View Only

Windows Update VBScript (v1.2) 

Feb 26, 2015 01:11 PM

Introduction

A few years ago, I posted to Symantec Connect our vbscript which triggers an audited Windows Update at the close of our computer build process.

http://www.symantec.com/connect/downloads/vbscript-windows-updates

The critical feature of this script was that it provided us a patching process which had a definitive start and end. This allowed us to streamline patching directly into our automated build process and continue with other tasks on completion in a timely fashion such as the sysprep and final image upload.

This script has served us well through the years, but for one issue; the windows executable that manages the installation of the updates in Windows Vista and beyond, TrustedInstall.exe, consumes an increasing amount of resource with each update it installs. The result is that when we've got a fresh build with well over a hundred updates queued up, the TrustedInstaller.exe process quickly grows in memory to a whopping 3GB,  permanently hogging the CPU, and grinding the machine to a near-halt. The update cycle will still eventually complete, but for us that can be over 8 hours due to this resource exhaustion. 

Turns out this issue was well documented by Marc Durdin, http://marc.durdin.net/2011/08/reproducing-the-trustedinstaller-exe-leak-2, where he clearly documents what appears to be trustedinstaller.exe's inability to clean-up after itself.

Until Microsoft fix the root cause here, the only way I currently know to tackle this is to feed the TrustedInstaller process  windows updates in manageable chunks. This keeps the process within reasonable resource boundaries, thus enabling a speedy install. This release (v1.2) of our vbscript to manage the update installation therefore contains a variable that allows you to fix the batch size.

 

v1.2 of our Windows Update Script

Today's download contains the script windowsupdate_v1.2.vbs. This script can be executed on Windows machines with the Windows Update Agent installed. To get the WindowsUpdateAgent, see Microsoft KB 949104 -How to obtain the latest version of the Windows Update Agent.

This script is based on a Microsoft script, with some changes by our Darren Collins here at Oxford to enable an entirely automated process suitable for Altiris deployments.

Key points are,

  1. Script will exit with code 98 if updates were installed
  2. Script will exit with code 99 if no updates found
  3. All activity logged to c:\Logs
  4. Batch size default is 30, (can be changed using the iMaxUpdates variable) 

For googlability, the script is also pasted below.

'v1.0 WindowsUpdates.vbs by Microsoft
'v1.1 Ammendment by D. Collins to increase logging
'v1.2 Ammendment by I. Atkin to introduce batching
'     ~~~ With 100 pending updates to install, TrustedInstaller.exe can consume 
'     ~~~ >3GB RAM and impede the updates process. So here limit the number of updates
'     ~~~ that can be queued and installed at any one time.


Dim I, I2, oSession, oSearcher, oSearchResult, oUpdate, oUpdatesToDownload, oUpdatesToInstall, oDownloader, oInstaller, oInstallationResult
Dim iMaxUpdates
Dim oFileSystem, oLog
dim oWinFolder
Dim blNoUpdates, qVal, oRunLog

blNoUpdates = False




iMaxUpdates=30

' ~~~ ---------------------------------------------
' ~~~ Create objects
' ~~~ ---------------------------------------------
Set oSession       = CreateObject("Microsoft.Update.Session")
Set oSearcher      = oSession.CreateupdateSearcher()

Set oFileSystem = CreateObject("Scripting.FileSystemObject")
Set oWinFolder = oFileSystem.GetSpecialFolder (0)
Set oLog = oWinFolder.CreateTextFile("SCTWindowsUpdates.log", True)
Set oRunLog = oFileSystem.OpenTextFile("C:\Logs\WindowsUpdates-All.log", 8, True)

Public Function DoWindowsUpdates

On Error Resume Next

    ' ~~~ ---------------------------------------------
    ' ~~~ Search for updates
    ' ~~~ ---------------------------------------------
    oLog.WriteLine FormatDateTime (Now) & " Searching for updates..." & vbCRLF
    oLog.WriteLine FormatDateTime (Now) & " List of applicable items on the machine:"
    
    I2=0
    For I = 0 To oSearchResult.Updates.Count-1
        Set oUpdate = oSearchResult.Updates.Item(I)
        I2=I2+1
        oLog.WriteLine I + 1 & "> " & oUpdate.Title
        oRunLog.WriteLine Now() & " - " & I + 1 & " > " & oUpdate.Title
    Next
    
    If I2 = 0 Then
        oLog.WriteLine FormatDateTime (Now) & " There are no applicable updates."
                blNoUpdates = True
        Exit Function
    Else
        oRunLog.WriteLine Now() & " - Applicable updates: " & I2
    End If
    
    ' ~~~ ---------------------------------------------
    ' ~~~ Create collection of upates to download
    ' ~~~ ---------------------------------------------
    oLog.WriteLine vbCRLF & FormatDateTime (Now) & " Creating collection of updates to download:"
    Set oUpdatesToDownload = CreateObject("Microsoft.Update.UpdateColl")
    
        Dim iUpdatesCount
        iUpdatesCount=oSearchResult.Updates.Count
        if iUpdatesCount > iMaxUpdates then 
        iUpdatesCount=iMaxUpdates
        oRunLog.WriteLine Now() & " - Too many updates. To limit trustedinstaller overhead, reducing to " & iMaxUpdates
        End if

    For I = 0 to iUpdatesCount-1
        Set oUpdate = oSearchResult.Updates.Item(I)
        oLog.WriteLine I + 1 & "> adding: " & oUpdate.Title 
        oRunLog.WriteLine I + 1 & "> adding: " & oUpdate.Title 
        oUpdatesToDownload.Add(oUpdate)
    Next
    
    ' ~~~ ---------------------------------------------
    ' ~~~ Download updates
    ' ~~~ ---------------------------------------------
    oLog.WriteLine vbCRLF & FormatDateTime (Now) & " Downloading updates..."
    
    Set oDownloader = oSession.CreateUpdateDownloader() 
    oDownloader.Updates = oUpdatesToDownload
    oDownloader.Download()
    
    ' ~~~ ---------------------------------------------
    ' ~~~ Create a collection of downloaded updates to install
    ' ~~~ ---------------------------------------------
    oLog.WriteLine  vbCRLF & FormatDateTime (Now) & " Creating collection of downloaded updates to install:" 
    Set oUpdatesToInstall = CreateObject("Microsoft.Update.UpdateColl")
    
    For I = 0 To iUpdatesCount-1
        Set oUpdate = oSearchResult.Updates.Item(I)
        oLog.WriteLine I + 1 & "> adding:  " & oUpdate.Title 
        oUpdatesToInstall.Add(oUpdate)    
    Next
    
    ' ~~~ ---------------------------------------------
    ' ~~~ Install updates
    ' ~~~ ---------------------------------------------
    oLog.WriteLine FormatDateTime (Now) & " Installing updates..."
    Set oInstaller = oSession.CreateUpdateInstaller()
    oInstaller.Updates = oUpdatesToInstall
    oInstaller.ForceQuiet = true
    Set oInstallationResult = oInstaller.Install()
    
    ' ~~~ ---------------------------------------------
    ' ~~~ Output results of install
    ' ~~~ ---------------------------------------------
    oLog.WriteLine FormatDateTime (Now) & " Installation Result: " & oInstallationResult.ResultCode 
    oLog.WriteLine FormatDateTime (Now) & " Reboot Required: " & oInstallationResult.RebootRequired & vbCRLF 
    oLog.WriteLine FormatDateTime (Now) & " Listing of updates installed and individual installation results:" 
    
    For I = 0 to oUpdatesToInstall.Count - 1
        oLog.WriteLine I + 1 & "> " & oUpdatesToInstall.Item(i).Title & ": " & oInstallationResult.GetUpdateResult(i).ResultCode
    Next
    
End Function


On Error Resume Next

oRunLog.WriteLine Now() & " - Start: Windows Updates All script starting."

oLog.WriteLine FormatDateTime (Now) & " Downloading and Installing All Available Software Updates"
Set oSearchResult  = oSearcher.Search("IsAssigned=1 and IsHidden=0 and IsInstalled=0 and Type='Software'")
Call DoWindowsUpdates
oLog.WriteLine vbCRLF & vbCRLF

oLog.WriteLine FormatDateTime (Now) & " Updates Complete"

oLog.Close

If blNoUpdates = True Then
    oRunLog.WriteLine Now() & " - No Updates Found."
    qVal = 99
Else
    oRunLog.WriteLine Now() & " - Updates found and installed.  See %WinDir%\WindowsUpdate.log for details."
    qVal = 98
End If

oRunLog.WriteLine Now() & " - End: Windows Updates All script Complete."
If qVal = 0 Then WScript.Quit(Err.Number)
WScript.Quit(qVal)

 

Running the Script

You can run this script a multitude of ways to get your Windows Updates installed in a nicely logged and auditable fashion.

The way we utilise this script is using an Altiris DS6.9 job. This job keeps calling itself in a loop with reboots until the script yields a return code of 99 (which indicated no more updates left to install).

Below is a screen grab of this job (a .bin file for Altiris import can for this is also attached to this download) which shows how we accomplish this using five 'Run Script' tasks.

DS Job.png

 

The 2nd task is the one which contains this script, and it will halt progress when the 99 return code is encountered,

DS Job2.png

The last task simply re-calls the job, giving us the loop. 

Let's now look at some log outputs for the scenario of updates found/not found.

 

Log Extract for Update Found and Installed Scenario

Here is an example of a log entry from the log file, C:\Logs\WindowsUpdates-ALL.log for when a patch is found,

14/09/2012 09:19:43 - Start: Windows Updates All script starting.

14/09/2012 09:21:14 - 1 > Update for Office File Validation 2010 (KB2553065), 32-bit Edition

14/09/2012 09:21:14 - Applicable updates: 1

14/09/2012 09:21:55 - Updates found and installed.  See %WinDir%\WindowsUpdate.log for details.

14/09/2012 09:21:55 - End: Windows Updates All script Complete.
 
In this case, the script also exits with return code 98. In your setup, you'd probably want to initiate a reboot and run the script again on finding this return code. This script works on XP and Windows 7. 
 
 

Log Extract for No Updates Found Scenario

Here is an example of a log entry from the log file, C:\Logs\WindowsUpdates-ALL.log for when no updates are found,

14/09/2012 09:26:10 - Start: Windows Updates All script starting.

14/09/2012 09:26:17 - No Updates Found.

14/09/2012 09:26:17 - End: Windows Updates All script Complete.

In this case the script exits with return code 99. In your setup, you'd probably want to initiate a reboot and then simply continue with your task chain.

 

Gotchas

It's very important to have jobs preceding this script which ensure you have a suitable version of the MSI service and Windows Update agent installed.

Next, from time to time, you'll find that certain updates never get installed properly and result in you never getting a 99 return code to say all is now fine. If you find this happens for you, consider moving the problem update into it's own task. I've seen this for example with the Microsoft Office File Validation Add-in (to resolve we just added the OFV.EXE install into our MS Office installation job directly) and with Microsoft's Malicious Software Removal Update (which again we have a job to download and install automatically).

Finally, please note that this script will download updates from the Microsoft Updates server as configured on the client computer. In many organisations, the settings for the local WSUS server come down from GPO for your local server, and are not present when image building (before domain join). In this situation you have to be aware that these updates will therefore come down from Microsoft (unless you reg hack for otherwise). 

Kind Regards,

Ian./

 

Statistics
0 Favorited
0 Views
1 Files
0 Shares
0 Downloads
Attachment(s)
zip file
WindowsUpdateScript_v1.2.zip   3 KB   1 version
Uploaded - Feb 25, 2020

Tags and Keywords

Related Entries and Links

No Related Resource entered.