Jump to content
Sign in to follow this  
Tec

Remote WindowsUpdate

Recommended Posts

Tec

Hi all

I create a simple WindowsUpdate script to Update my Clients at work.

I use a WSUS Server too. But sometimes I want to update the client as quickly as possible.

It creates a vbs script on the client and a scheduled task to start the vbs script. View the Client Eventlog for update status.

I test this with Windows XP SP3.

#include <Date.au3>
#RequireAdmin
Opt('MustDeclareVars', 1)

Global $strComputer = InputBox("Force Windows Updates", "Enter Computername to force updates:", "", "", 250, 120)
    If @error = 1 OR $strComputer = "" Then 
        Exit
    EndIf
Global $strpassword = InputBox("Administrator Password", "Please enter " & $strComputer & "\Administrator password to create the scheduled task:", "", "*", 250, 130)
    If @error = 1 OR $strpassword = "" Then 
        Exit
    EndIf
Global $strPatchScript = "\\" & $strComputer & "\c$\patchscript.vbs"
Global $strJobfile = "\\"& $strComputer & "\admin$\tasks\WindowsUpdate.job"
Local $strUserName = $strComputer & "\Administrator"
Local $strExe = "\\" & $strComputer & "\admin$\system32\wscript.exe c:\patchscript.vbs "
Local $strNewTime = _RemoteDateTime()
If @OSLang = "0407" Then 
    Local $strSC = "EINMAL"
Else
    Local $strSC = "ONCE"
EndIf
Global $strCommand = "SCHTASKS /s " & $strComputer & " /Create /SC " & $strSC & " /TN WindowsUpdate /TR " & chr(34) & $strExe & chr(34) & " /ST "& $strNewTime & " /RU " & chr(34) & $strUserName & chr(34) & " /RP " & chr(34) & $strpassword & chr(34)


_PatchScript()
Exit
 

Func _RemoteDateTime()
    Local $strRmtDate, $strRmtTime, $strTime
    Dim $objWMI = ObjGet("winmgmts:\\" & $strComputer & "\root\CIMV2")
    If IsObj($objWMI) Then
        Dim $colItems = $objWMI.ExecQuery("SELECT * FROM Win32_Localtime")
        For $objItem In $colItems
            $strRmtDate = $objItem.Year & "/" &  $objItem.Month & "/" & $objItem.Day
            $strRmtTime = $objItem.Hour & ":" & $objItem.Minute & ":" & $objItem.Second
        Next

        $strRmtTime = _DateAdd("n", 2, $strRmtDate & " " & $strRmtTime)
        $strTime = _DateTimeFormat($strRmtTime, 5)
        Return String($strTime)
    Else
        MsgBox(16, "Error", "Failed to connect to WMI at !!!")
        Exit
    EndIf       
EndFunc


Func _PatchScript()
    Local $TextStream
    Local $strScript = "Dim strEventText" & @CRLF & _
        "strEventText = "& chr(34) & chr(34) & @CRLF & _
        "Set objShell = Wscript.CreateObject(" & chr(34) & "Wscript.Shell" & chr(34) & ")" & @CRLF & _
        "Set objSession = CreateObject(" & chr(34) & "Microsoft.Update.Session" & chr(34) & ")" & @CRLF & _
        "Set AutoUpdate = CreateObject(" & chr(34) & "Microsoft.Update.AutoUpdate" & chr(34) & ")" & @CRLF & _
        @CRLF & _
        "Set UpdateSearcher = objSession.CreateUpdateSearcher" & @CRLF & _
        "Set SearchResult = UpdateSearcher.Search(" & chr(34) & " IsAssigned=1 and IsHidden=0 and IsInstalled=0 and Type='Software'" & chr(34) & ")" & @CRLF & _
        @CRLF & _
        "Autoupdate.DetectNow()" & @CRLF & _
        @CRLF & _
        "If searchResult.Updates.Count = 0 Then" & @CRLF & _
        "   objShell.LogEvent 4, " & chr(34) & "No updates to install. Script terminated." & chr(34) & @CRLF & _
        "   DeleteSelf" & @CRLF & _
        "   WScript.Quit(0)" & @CRLF & _
        "End If " & @CRLF & _
        @CRLF & _
        "strEventText = " & chr(34) & "The following updates will be installed:" & chr(34) & " & vbCRLF" & @CRLF & _
        "Set updatesToDownload = CreateObject(" & chr(34) & "Microsoft.Update.UpdateColl" & chr(34) & ")" & @CRLF & _       
        "For i = 0 To SearchResult.Updates.Count-1" & @CRLF & _
        "   Set update = SearchResult.Updates.Item(I) " & @CRLF & _
        "   strEventText = strEventText & I + 1 & " & chr(34) & "> " & chr(34) & " & update.Title & vbCRLF" & @CRLF & _
        "   If Not update.EulaAccepted Then update.AcceptEula " & @CRLF & _
        "   updatesToDownload.Add(update)" & @CRLF & _
        "Next" & @CRLF & _  
        "objShell.LogEvent 4, strEventText" & @CRLF & _
        @CRLF & _
        "Set downloader = objSession.CreateUpdateDownloader()" & @CRLF & _
        "downloader.Updates = updatesToDownload" & @CRLF & _
        "downloader.Download()" & @CRLF & _
        @CRLF & _
        "Set UpdatesToInstall = CreateObject(" & chr(34) & "Microsoft.Update.UpdateColl" & chr(34) & ")" & @CRLF & _
        "For I = 0 To searchResult.Updates.Count-1" & @CRLF & _
        "   set update = searchResult.Updates.Item(I)" & @CRLF & _
        "   If update.IsDownloaded = true Then" & @CRLF & _
        "       UpdatesToInstall.Add(update)" & @CRLF & _
        "   End If" & @CRLF & _
        "Next" & @CRLF & _
        @CRLF & _
        "Set installer = objSession.CreateUpdateInstaller()" & @CRLF & _
        "installer.Updates = updatesToInstall" & @CRLF & _
        "installer.Install()" & @CRLF & _
        @CRLF & _
        "Set objSysInfo = CreateObject(" & chr(34) & "Microsoft.Update.SystemInfo" & chr(34) & ")" & @CRLF & _
        "If objSysInfo.RebootRequired Then" & @CRLF & _
        "   objShell.LogEvent 4, " & chr(34) & "Completed the installation of updates.  Restart required..." & chr(34) & @CRLF & _
        "   Set objWMIService = GetObject(" & chr(34) & " winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2"  & chr(34) & ")" & @CRLF & _
        "   Set objEnum = objWMIService.ExecQuery(" & chr(34) & " Select __relpath from win32_process where caption = 'explorer.exe'" & chr(34) & ")" & @CRLF & _
        "   If objEnum.Count = 0 then" & @CRLF & _
        "       objShell.LogEvent 4, " & chr(34) & "Force Restart. No User online..." & chr(34) & @CRLF & _
        "       Set OpSysSet = GetObject(" & chr(34) & "winmgmts:{(Shutdown)}//./root/cimv2" & chr(34) & ").ExecQuery(" & chr(34) & "select * from Win32_OperatingSystem where Primary=true" & chr(34) & ") " & @CRLF & _
        "       Const EWX_REBOOT = 2 " & @CRLF & _
        "       Const EWX_FORCE = 4" & @CRLF & _
        "       For each OpSys in OpSysSet" & @CRLF & _
        "           opSys.win32shutdown EWX_REBOOT + EWX_FORCE" & @CRLF & _
        "       Next" & @CRLF & _
        "   End If" & @CRLF & _
        "Else" & @CRLF & _
        "   objShell.LogEvent 4, " & chr(34) & "Completed the installation of updates.  No restart is required." & chr(34) & @CRLF & _
        "End If" & @CRLF & _    
        @CRLF & _
        "DeleteSelf" & @CRLF & _
        "WScript.Quit(0)" & @CRLF & _
        @CRLF & _
        "Sub DeleteSelf()" & @CRLF & _
        "   Dim objFSO" & @CRLF & _
        "   Set objFSO = CreateObject(" & chr(34) & "Scripting.FileSystemObject" & chr(34) & ")" & @CRLF & _
        "   objFSO.DeleteFile WScript.ScriptFullName" & @CRLF & _
        "   Set objFSO = Nothing" & @CRLF & _
        "End Sub"

    Local $fso = ObjCreate("Scripting.FileSystemObject")    
    If IsObj($fso) Then
        If $fso.FileExists($strPatchScript) Then 
            $fso.DeleteFile($strPatchScript, true)
        EndIf
    
        $TextStream = $fso.CreateTextFile($strPatchScript)
        $TextStream.Write($strScript) 
        $TextStream.Close   

        If $fso.FileExists($strJobFile) Then 
            $fso.DeleteFile($strJobFile, True)
        EndIf
    Else
        MsgBox(16, "Error", "Failed to create FileSystemObject !!!")
        Exit
    EndIf       

    Dim $wshShell = ObjCreate("WScript.Shell")
    Local $retval = $wshShell.Run($strCommand, 0, True)
    If $retval = 0 Then
        MsgBox(0, "Windows Update","The patch task was successfully created to start at " & $strNewTime)
    Else
        MsgBox(16,  "Error", "There were problems creating the patch task. ")
    EndIf
EndFunc

The client vbs script. You dont need this. Only for clear view.

Dim strEventText
strEventText = ""
Set objShell = Wscript.CreateObject("Wscript.Shell")
Set objSession = CreateObject("Microsoft.Update.Session")
Set AutoUpdate = CreateObject("Microsoft.Update.AutoUpdate")

Set UpdateSearcher = objSession.CreateUpdateSearcher
Set SearchResult = UpdateSearcher.Search(" IsAssigned=1 and IsHidden=0 and IsInstalled=0 and Type='Software'")

Autoupdate.DetectNow()

If searchResult.Updates.Count = 0 Then
    objShell.LogEvent 4, "No updates to install. Script terminated."
    DeleteSelf
    WScript.Quit(0)
End If 

strEventText = "The following updates will be installed:" & vbCRLF
Set updatesToDownload = CreateObject("Microsoft.Update.UpdateColl")
For i = 0 To SearchResult.Updates.Count-1
    Set update = SearchResult.Updates.Item(I) 
    strEventText = strEventText & I + 1 & "> " & update.Title & vbCRLF
    If Not update.EulaAccepted Then update.AcceptEula 
    updatesToDownload.Add(update)
Next
objShell.LogEvent 4, strEventText

Set downloader = objSession.CreateUpdateDownloader()
downloader.Updates = updatesToDownload
downloader.Download()

Set UpdatesToInstall = CreateObject("Microsoft.Update.UpdateColl")
For I = 0 To searchResult.Updates.Count-1
    set update = searchResult.Updates.Item(I)
    If update.IsDownloaded = true Then
        UpdatesToInstall.Add(update)
    End If
Next

Set installer = objSession.CreateUpdateInstaller()
installer.Updates = updatesToInstall
installer.Install()

Set objSysInfo = CreateObject("Microsoft.Update.SystemInfo")
If objSysInfo.RebootRequired Then
    objShell.LogEvent 4, "Completed the installation of updates.  Restart required..."
    Set objWMIService = GetObject(" winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
    Set objEnum = objWMIService.ExecQuery(" Select __relpath from win32_process where caption = 'explorer.exe'")
    If objEnum.Count = 0 then
        objShell.LogEvent 4, "Force Restart. No User online..."
        Set OpSysSet = GetObject("winmgmts:{(Shutdown)}//./root/cimv2").ExecQuery("select * from Win32_OperatingSystem where Primary=true") 
        Const EWX_REBOOT = 2 
        Const EWX_FORCE = 4
        For each OpSys in OpSysSet
            opSys.win32shutdown EWX_REBOOT + EWX_FORCE
        Next
    End If
Else
    objShell.LogEvent 4, "Completed the installation of updates.  No restart is required."
End If

DeleteSelf
WScript.Quit(0)

Sub DeleteSelf()
    Dim objFSO
    Set objFSO = CreateObject("Scripting.FileSystemObject")
    objFSO.DeleteFile WScript.ScriptFullName
    Set objFSO = Nothing
End Sub

Share this post


Link to post
Share on other sites
spudw2k

I did something like this too. I basically converted a vb script to autoit; we then push out the file remotely.

#include <misc.au3>
#include <Array.au3>
#NoTrayIcon

$oMyError = ObjEvent("AutoIt.Error","MyErrFunc") 

Opt("TrayMenuMode",1)
TraySetIcon("Shell32.dll",-47)
Global $reboot=0

TraySetToolTip("Automatic Updates - Checking for Needed Patches")
$patches = _CollectPatches()
If Not IsObj($patches) Then Exit

If $patches.Updates.Count > 0 Then
    TraySetToolTip("Automatic Updates - Found " & $patches.Updates.Count & _Iif($patches.Updates.Count > 1," Patches"," Patch"))
    _UpdatesDownloadAndInstall($patches)
Else
    TraySetToolTip("Automatic Updates - No Patches Needed.")
EndIf

Do
    sleep(10000)
Until $reboot=0


Func _CreateMSUpdateSession($strHost = @ComputerName)
    $objSession = ObjCreate("Microsoft.Update.Session",$strHost)
    If Not IsObj($objSession) Then Return 0
    Return $objSession
EndFunc

Func _CreateSearcher($objSession)
    If Not IsObj($objSession) Then Return -1
    Return $objSession.CreateUpdateSearcher
EndFunc

Func _CollectPatches()
    $objSearcher = _CreateSearcher(_CreateMSUpdateSession())
    $colNeeded = _GetNeededUpdates($objSearcher)
    $objSearcher = 0

    Return $colNeeded
EndFunc

Func _Force()
    
EndFunc

Func _GetNeededUpdates($objSearcher)
    If Not IsObj($objSearcher) Then Return -5
    $colNeeded = $objSearcher.Search("IsInstalled=0 and Type='Software'")
    Return $colNeeded
EndFunc

Func _UpdatesDownloadAndInstall($colNeeded)
    $objSearcher = _CreateMSUpdateSession()
    
    For $i = 0 To $colNeeded.Updates.Count-1
        $update = $colNeeded.Updates.Item($i)
        ;If $item = $update.Title Then
            TraySetToolTip("Automatic Updates - Downloading " & ($i+1) & " of " & $colNeeded.Updates.Count)
            $updatesToDownload = ObjCreate("Microsoft.Update.UpdateColl")
            $updatesToDownload.Add ($update)
            $DownloadSession = $objSearcher.CreateUpdateDownloader()
            $DownloadSession.Updates = $updatesToDownload
            $DownloadSession.Download
        ;EndIf
    Next
   
    For $i = 0 To $colNeeded.Updates.Count-1
        $update = $colNeeded.Updates.Item($i)
        ;If $item = $update.Title And $update.IsDownloaded Then
        If $update.IsDownloaded Then
            TraySetToolTip("Automatic Updates - Installing " & ($i+1) & " of " & $colNeeded.Updates.Count)
            $InstallSession = $objSearcher.CreateUpdateInstaller()
            $updatesToInstall = ObjCreate("Microsoft.Update.UpdateColl")
            $updatesToInstall.Add($update)
            $InstallSession.Updates = $updatesToInstall
            $installresult = $InstallSession.Install
            If $installresult.RebootRequired Then $reboot = 1
        EndIf
    Next
   
    If $reboot Then
        TraySetToolTip("Automatic Updates - Patching Complete. A Reboot is Required.")
    Else
        TraySetToolTip("Automatic Updates - Patching Complete.")
    EndIf
   
    $DownloadSession = 0
    $updatesToDownload = 0
    Return 0
EndFunc

Func MyErrFunc()
    Exit
Endfunc

Share this post


Link to post
Share on other sites
res2cpu

I work in a computer store where we build and sell more pc's a day then i care to count, i use the same kind of thing.

Its a bit annoying how Microsoft don't send out all updates to the wsus servers as it means we still have to eat bandwidth getting them upto date.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×