Thursday, August 28, 2014

Command line Windows Update

Update: If you are behind a proxy, here is some additional code to add right after creating the update session:
'updateSession.WebProxy.AutoDetect = true 'try this first. It doesn't work so well in some environments if no authentication windows appears (*cough* Windows 8 *cough*)

strProxy = "proxy name or address:proxy port"  'ex: 1234:999
strProxyUser = "your username"
strProxyPass = "your password" 


I am working behind a "secured" web proxy that sometimes skips a beat. As a result there are days in which I cannot install Window Updates, the normal Windows update application just fails (with Error Code: 0x80246002) and I am left angry and powerless. Well, there are options. First of all, none of the "solutions" offered by Microsoft seem to work. The most promising one (which may apply to you, but it did not apply to me) was that you may have corrupted files in the Download folder for Windows updates. As a result you need to:
  • Stop the Windows Update service issuing the command line command: net stop wuauserv or by going to Control Panel, Services and manually stopping it.
  • Go to the download folder parent found at %systemroot%\SoftwareDistribution (cd %systemroot%\SoftwareDistribution) and rename the Download folder (ren Download Download.old)
  • Start the Windows Update service issuing the command line command: net start wuauserv or by going to Control Panel, Services and manually starting it.

So my solution was to use a script that downloads and installs the Windows updates from the command line and I found this link: Searching, Downloading, and Installing Updates that pretty much provided the solution I was looking for. There are two issues with the script. The first is that it prompts you to accept any EULA that the updates may present. The second is that it downloads all updates, regardless of severity. So I am publishing here the script that I am using who fixes these two problems: EULA is automatically accepted and only Important and Critical updates are downloaded and installed:
Set updateSession = CreateObject("Microsoft.Update.Session")
updateSession.ClientApplicationID = "Siderite :) Sample Script"

Set updateSearcher = updateSession.CreateUpdateSearcher()

WScript.Echo "Searching for updates..." & vbCRLF

Set searchResult = _
updateSearcher.Search("IsInstalled=0 and Type='Software' and IsHidden=0")

WScript.Echo "List of applicable items on the machine:"

For I = 0 To searchResult.Updates.Count-1
    Set update = searchResult.Updates.Item(I)
    WScript.Echo I + 1 & "> " & update.Title

If searchResult.Updates.Count = 0 Then
    WScript.Echo "There are no applicable updates."
End If

WScript.Echo vbCRLF & "Creating collection of updates to download:"

Set updatesToDownload = CreateObject("Microsoft.Update.UpdateColl")

For I = 0 to searchResult.Updates.Count-1
    Set update = searchResult.Updates.Item(I)

    addThisUpdate = false
    If update.InstallationBehavior.CanRequestUserInput = true Then
        WScript.Echo I + 1 & "> skipping: " & update.Title & _
        " because it requires user input"
        If update.EulaAccepted = false Then
            WScript.Echo I + 1 & ">     Accept EULA " & update.Title 
            addThisUpdate = true
            'WScript.Echo I + 1 & "> note: " & update.Title & " has a license agreement that must be accepted:"
            'WScript.Echo update.EulaText
            'WScript.Echo "Do you accept this license agreement? (Y/N)"
            'strInput = WScript.StdIn.Readline
            'If (strInput = "Y" or strInput = "y") Then
            '    update.AcceptEula()
            '    addThisUpdate = true
            '    WScript.Echo I + 1 & "> skipping: " & update.Title & _
            '    " because the license agreement was declined"
            'End If
            addThisUpdate = true
        End If
    End If

    If addThisUpdate AND (update.MsrcSeverity = "Important" OR update.MsrcSeverity = "Critical") Then
        'wscript.echo ("This item is " & update.MsrcSeverity & " and will be processed!")
        'comment these lines to make it download everything
        wscript.echo (update.Title & " has severity [" & update.MsrcSeverity & "] and will NOT be processed!")
    End If

    If addThisUpdate = true Then
        wscript.echo(I + 1 & "> adding: (" & update.MsrcSeverity & ") " & update.Title)
    End If

If updatesToDownload.Count = 0 Then
    WScript.Echo "All applicable updates were skipped."
End If
WScript.Echo vbCRLF & "Downloading updates..."

Set downloader = updateSession.CreateUpdateDownloader() 
downloader.Updates = updatesToDownload

Set updatesToInstall = CreateObject("Microsoft.Update.UpdateColl")

rebootMayBeRequired = false

WScript.Echo vbCRLF & "Successfully downloaded updates:"

For I = 0 To searchResult.Updates.Count-1
    set update = searchResult.Updates.Item(I)
    If update.IsDownloaded = true Then
        WScript.Echo I + 1 & "> " & update.Title 
        If update.InstallationBehavior.RebootBehavior > 0 Then
            rebootMayBeRequired = true
        End If
    End If

If updatesToInstall.Count = 0 Then
    WScript.Echo "No updates were successfully downloaded."
End If

If rebootMayBeRequired = true Then
    WScript.Echo vbCRLF & "These updates may require a reboot."
End If

WScript.Echo  vbCRLF & "Would you like to install updates now? (Y/N)"
strInput = WScript.StdIn.Readline

If (strInput = "Y" or strInput = "y") Then
    WScript.Echo "Installing updates..."
    Set installer = updateSession.CreateUpdateInstaller()
    installer.Updates = updatesToInstall
    Set installationResult = installer.Install()
    'Output results of install
    WScript.Echo "Installation Result: " & _
    WScript.Echo "Reboot Required: " & _ 
    installationResult.RebootRequired & vbCRLF 
    WScript.Echo "Listing of updates installed " & _
    "and individual installation results:" 
    For I = 0 to updatesToInstall.Count - 1
        WScript.Echo I + 1 & "> " & _
        updatesToInstall.Item(i).Title & _
        ": " & installationResult.GetUpdateResult(i).ResultCode   
End If

Save the code above in a file called Update.vbs and then creating a batch file that looks like this:
start "Command line Windows update" cscript Update.vbs

Run the script and you will get the .vbs executed in a command line window that will also wait for pressing Enter at the end of execution so you can see the result.

For other solutions that are more system admin oriented, follow this link which provides you with a lot of possibilities, some in PowerShell, for example.

Also, I didn't find a way to install the updates without the Windows annoyance that asks me to reboot the computer popping up. If you know how to do that, I would be grateful.