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
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
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 } }
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?
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
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?
Yes, this interfaces directly with the Windows Update COM object on a client system. If you are looking for something that will query WSUS directly, I would suggest my module, PoshWSUS available at http://poshwsus.codeplex.com
Hi Boe, I run the script but returns me nothing, any advice?, thanks!
I forgot to say that I downloaded the script from http://gallery.technet.microsoft.com/scriptcenter/0dbfc125-b855-4058-87ec-930268f03285#content
is that correct?
How are you running the code? There is a function in the script that will need to be dot sourced before you are able to use it.
. .\Get-PendingUpdates.ps1
Get-PendingUpdates -Computername server1
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.
This is an issue where the windows firewall or another firewall is blocking inbound dynamic RPC ports. You will need to create an exception on your software firewall to allow the script to perform the audit on your remote machine. More information about this is at the bottom of the page in the following link: http://msdn.microsoft.com/en-us/library/windows/desktop/aa387288%28v=VS.85%29.aspx
Hope this helps…
Pingback: Computers Technology Gadgets & Web » 7 Pending Computer Sites » Computers Technology Gadgets & Web
Pingback: Patch Installation using PowerShell, VBScript and PSExec | Learn Powershell | Achieve More
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,
Awesome! Thanks for posting your solution here as well for others to use!
I was having this same error on a Windows 7 x64 box, then I ran PowerShell ISE as admin. Oddly I had to remap my home drive via “net use”, after launching the ISE as admin.
Did some more testing. Works in 2003 R2, but do not work on 2008 R2.
Yes its a server in a domain. Maybe its a firewall related issue as its a 2008 R2. Havent tested on a 2003 yet.
Maybe. I’d be curious to see if that is the case. Anything in the security logs? If not, then a firewall might be the culprit…
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.
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.
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”
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.
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.
Turn the firewall off.
Another fantastic script. Thanks Boe!
Dude … VERY nice find! Remotely accessing COM objects from PowerShell … oh the possibilities 🙂
Cheers,
Trevor