Find pending updates on local or remote computers

This is more of an upgrade to a vbscript I found to run on all of our servers at work. Originally I used the vbscript along with PSExec to remotely run the file on each remote machine which would then shoot out a log file on the server that would then be copied to a central repository and then compiled into one main log file that could be reviewed.  I admit I was really procrastinating on making this upgrade as I had other things to work on, but knew that this was going to happen eventually.  Fast forward to now and I have finally completed this upgrade.

The biggest key to this script is the use of the following line of code:

$updatesession =  [activator]::CreateInstance([type]::GetTypeFromProgID("Microsoft.Update.Session",$c)) 

This piece of code allows me to create the remote COM object on a remote computer that then allows me to perform the audit of patches that are available to install on that computer.  Unfortunately, this same trick does not work with the installation of the patches as remote installation via the COM object is forbidden.

The [activator] is best defined as:

Contains methods to create types of objects locally or remotely, or obtain references to existing remote objects. This class cannot be inherited.

I then use the CreateInstance() method of that class to then call the COM object using the [type] class with the GetTypeFromProgID() method to create the remote COM object of the Microsoft.Update.Session on the remote computer ($c).

While I am unable to install the updates, at least I can find out what, if any are waiting to be installed on a remote machine. The following line of code shows the syntax of what needs to be typed out and then the following output.

Get-PendingUpdates -Computer DC1,boe-laptop

 

Capture

This output can also be easily exported to a CSV file using Export-CSV. 

In between updating PoshWSUS to version 1.2 and some other side projects, I am also working on a GUI using WPF that uses this code in the backend to provide a nice user interface to work with for my work, but will also of course share this with everyone else as well when completed.

Code

Script Repository

PoshCode

Function Get-PendingUpdates { 
<#    
  .SYNOPSIS   
    Retrieves the updates waiting to be installed from WSUS   
  .DESCRIPTION   
    Retrieves the updates waiting to be installed from WSUS  
  .PARAMETER Computer 
    Computer or computers to find updates for.   
  .EXAMPLE   
   Get-PendingUpdates 
    
   Description 
   ----------- 
   Retrieves the updates that are available to install on the local system 
  .NOTES 
  Author: Boe Prox                                           
  Date Created: 05Mar2011                                           
#> 
      
#Requires -version 2.0   
[CmdletBinding( 
    DefaultParameterSetName = 'computer' 
    )] 
param( 
    [Parameter( 
        Mandatory = $False, 
        ParameterSetName = '', 
        ValueFromPipeline = $True)] 
        [string[]]$Computer               
    )     
Begin { 
    $scriptdir = { Split-Path $MyInvocation.ScriptName -Parent } 
    Write-Verbose "Location of function is: $(&$scriptdir)" 
    $psBoundParameters.GetEnumerator() | ForEach-Object { Write-Verbose "Parameter: $_" } 
    If (!$PSBoundParameters['computer']) { 
        Write-Verbose "No computer name given, using local computername" 
        [string[]]$computer = $Env:Computername 
        } 
    #Create container for Report 
    Write-Verbose "Creating report collection" 
    $report = @()     
    } 
Process { 
    ForEach ($c in $Computer) { 
        Write-Verbose "Computer: $($c)" 
        If (Test-Connection -ComputerName $c -Count 1 -Quiet) { 
            Try { 
            #Create Session COM object 
                Write-Verbose "Creating COM object for WSUS Session" 
                $updatesession =  [activator]::CreateInstance([type]::GetTypeFromProgID("Microsoft.Update.Session",$c)) 
                } 
            Catch { 
                Write-Warning "$($Error[0])" 
                Break 
                } 
 
            #Configure Session COM Object 
            Write-Verbose "Creating COM object for WSUS update Search" 
            $updatesearcher = $updatesession.CreateUpdateSearcher() 
 
            #Configure Searcher object to look for Updates awaiting installation 
            Write-Verbose "Searching for WSUS updates on client" 
            $searchresult = $updatesearcher.Search("IsInstalled=0")     
             
            #Verify if Updates need installed 
            Write-Verbose "Verifing that updates are available to install" 
            If ($searchresult.Updates.Count -gt 0) { 
                #Updates are waiting to be installed 
                Write-Verbose "Found $($searchresult.Updates.Count) update\s!" 
                #Cache the count to make the For loop run faster 
                $count = $searchresult.Updates.Count 
                 
                #Begin iterating through Updates available for installation 
                Write-Verbose "Iterating through list of updates" 
                For ($i=0; $i -lt $Count; $i++) { 
                    #Create object holding update 
                    $update = $searchresult.Updates.Item($i) 
                     
                    #Verify that update has been downloaded 
                    Write-Verbose "Checking to see that update has been downloaded" 
                    If ($update.IsDownLoaded -eq "True") {  
                        Write-Verbose "Auditing updates"   
                        $temp = "" | Select Computer, Title, KB,IsDownloaded 
                        $temp.Computer = $c 
                        $temp.Title = ($update.Title -split('\('))[0] 
                        $temp.KB = (($update.title -split('\('))[1] -split('\)'))[0] 
                        $temp.IsDownloaded = "True" 
                        $report += $temp                
                        } 
                    Else { 
                        Write-Verbose "Update has not been downloaded yet!" 
                        $temp = "" | Select Computer, Title, KB,IsDownloaded 
                        $temp.Computer = $c 
                        $temp.Title = ($update.Title -split('\('))[0] 
                        $temp.KB = (($update.title -split('\('))[1] -split('\)'))[0] 
                        $temp.IsDownloaded = "False" 
                        $report += $temp 
                        } 
                    } 
                 
                } 
            Else { 
                #Nothing to install at this time 
                Write-Verbose "No updates to install." 
                 
                #Create Temp collection for report 
                $temp = "" | Select Computer, Title, KB,IsDownloaded 
                $temp.Computer = $c 
                $temp.Title = "NA" 
                $temp.KB = "NA" 
                $temp.IsDownloaded = "NA" 
                $report += $temp 
                } 
            } 
        Else { 
            #Nothing to install at this time 
            Write-Warning "$($c): Offline" 
             
            #Create Temp collection for report 
            $temp = "" | Select Computer, Title, KB,IsDownloaded 
            $temp.Computer = $c 
            $temp.Title = "NA" 
            $temp.KB = "NA" 
            $temp.IsDownloaded = "NA" 
            $report += $temp             
            } 
        }  
    } 
End { 
    Write-Output $report 
    }     
}
This entry was posted in powershell, scripts, WSUS and tagged , , . Bookmark the permalink.

27 Responses to Find pending updates on local or remote computers

  1. Scott says:

    Love this script! But I am not a super advanced PS writer. I’d like to add one more column to the script for severity, i.e. Critical, Hotfix, Update, recommended etc. Can you help me?

  2. GW says:

    I have been looking for script like this one. It works great, on most of my servers except on a very few servers it hangs on the $searchresult = $updatesearcher.Search(“IsInstalled=0”). I waited and could not get out of it without killing the console. Any ideas what might be causing hang?

    Thanks,
    GW

  3. Fanny says:

    Is this powershell specifically for sessions between Microsoft and a WSUS client? How would it be modified to specify a new session between a WSUS server on the same domain?

  4. Hugo Acha says:

    Hi Boe, I run the script but returns me nothing, any advice?, thanks!

  5. pradeep says:

    Very interesting Script…!
    When run it on my local machine…it is working fine; when I try on remote machine…it giving following error. Also I am runing the Powershell as Administrator.

    Exception calling “CreateInstance” with “1” argument(s): “Creating an instance of the COM component with CLSID {4CB43D7F-7EEE-4906-8698-60DA1C38F2FE} from the IClassFactory failed due to the following error: 800706ba.”

    Can anyone suggest the solution…Appriciate your help.

  6. Pingback: Computers Technology Gadgets & Web » 7 Pending Computer Sites » Computers Technology Gadgets & Web

  7. Pingback: Patch Installation using PowerShell, VBScript and PSExec | Learn Powershell | Achieve More

  8. Hi Boe!

    I receive the same error on 2008R2 box inside a domain, with ports 135 and 445 listening on the remote server. I launched powershell as the domain admin account, so permissions shouldn’t be an issue. There was a post over at ComputerPerformance (http://www.computerperformance.co.uk/Logon/code/code_800706BA.htm) that mentioned firewalls as an issue.
    I turned off the firewall on a non-production machine and the function worked, you don’t happen to know what additional port needs opened do you?

    Thanks,

    • Additionally, I enabled the COM+ Remote Admin and Network Access, and the error message changed slightly from what Simon saw.

      WARNING: Exception calling “CreateInstance” with “1” argument(s): “Creating an instance of the COM component with CLSID {4CB43D7F-7EEE-4906-8698-60DA1C38F2FE} from the IClassFactory failed due to the following error: 800706ba.”

    • Found the solution for this error, by default it appears that RPC ephemeral ports are disabled on R2, this could be just 2008 in general. Created a rule to allow inbound RPC Dynamic Ports, once that rule was enabled the function worked perfectly. I noticed after the fact that there appears to be a DCOM rule that does the same thing, but after disabling my rule and enabling that rule the function failed. The only difference is that this the built-in has svchost as an allowed program/service, whereas my rule did not.
      This works for me, in production I have set the scope to be our administration network, but it could certainly be narrowed down to a single computer. I’d put the netsh in here, but I was lazy and used the Windows Firewall with Advanced Security snap-in.
      I’ve not researched any farther to see if this is potentially a GPO that we over-wrote in our domain, but as Simon was experiencing a similar issue with similar OS, I’d say this may indeed be a default.
      Really cool, function, hope other folks find this useful as well!

      Thanks,

  9. Simon says:

    Did some more testing. Works in 2003 R2, but do not work on 2008 R2.

  10. Simon says:

    Yes its a server in a domain. Maybe its a firewall related issue as its a 2008 R2. Havent tested on a 2003 yet.

  11. Simon says:

    I get this error when I try to run the script:
    WARNING: Exception calling “CreateInstance” with “1” argument(s): “Retrieving the COM class factory for remote component with CLSID {4CB43D7F-7EEE-4906-8698-60DA1C38F2FE} f
    rom machine machinename failed due to the following error: 80070005.

    • Boe Prox says:

      Hi Simon,
      The 8007005 error is Access Denied. Are you running the PowerShell console as an Administrator prior to running the function? If not, give that a shot and that should work for you.

      • Simon says:

        Yes I am. I open Powershell ISE with a admin account, run the script, and then uses the commandline in the bottom to write “Get-PendingUpdates -Computer server1”

        • Boe Prox says:

          Hmmm… That is interesting. I am assuming that the server you are running the command against is on a domain so your admin credentials can be passed to it. If it is a stand alone system, then there will be problems as I do not have an option for alternate credentials.

    • Greg says:

      I am seeing this same error as well when i run the script against a 2008 server. I am running the powershell console using the administrator account. Additionally, when the function does run to completion, the csv file is created as expected but it is invariably empty.

    • bithog says:

      Turn the firewall off.

  12. Chris Brown says:

    Another fantastic script. Thanks Boe!

  13. Dude … VERY nice find! Remotely accessing COM objects from PowerShell … oh the possibilities 🙂

    Cheers,
    Trevor

Leave a Reply to Trevor Sullivan Cancel reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s