Use PowerShell to Find Missing Updates on WSUS Client Computers

Use PowerShell to Find Missing Updates on WSUS Client Computers

  • Comments 11
  • Likes

Summary: Learn how to use the computer target scope with Windows PowerShell to find WSUS client computers that are missing updates.

Microsoft Scripting Guy, Ed Wilson, is here. We are halfway through WSUS Week and some really good stuff from Boe Prox. You can see Boe’s biography in the Day 1 blog. In case you need to catch up with the series on Windows Software Update Services (WSUS), the following blogs will get you up to speed:

Day 1: Introduction to WSUS and PowerShell

Day 2: Use PowerShell to Perform Basic Administrative Tasks on WSUS

Day 3: Approve or Decline WSUS Updates by Using PowerShell

Now, here is Boe…

Over the past few days, I have been showing you some basic administration examples of what you can do by using Windows PowerShell to manage a WSUS server. During the course of some of these examples, I alluded to some methods that required a Computer Target Scope object. Well, today is the day that I get to show you how you can build a Computer Target Scope object and use that with some of the existing objects we have already created.

“What is the Computer Target Scope?” you ask? The Computer Target Scope is, as the name implies, a scope that that can be used to filter a list of clients. For more information about this class, see ComputerTargetScope Class on MSDN.

Creating the Computer Target Scope object

The first thing we need to do is create the scope object, which we can then use in some of the methods we have seen. To do this will require us to create the new object from the Microsoft.UpdateServices.Administration.ComputerTargetScope class.

$computerscope = New-Object Microsoft.UpdateServices.Administration.ComputerTargetScope

Simple enough to do, and we can choose to leave the default properties that are already set for this object if we want to.

Image of command output

So as a default object, this will reference all clients on the WSUS server. Fortunately, you can edit most of these properties to narrow down the clients that you want to use. Here is a list of the editable properties:

ExcludedInstallationStates

 

Gets or sets the installation states to exclude.

FromLastReportedStatusTime

 

Gets or sets the earliest reported status time.

FromLastSyncTime

 

Gets or sets the earliest last synchronization time to search for.

IncludedInstallationStates

 

Gets or sets the update installation states to search for.

IncludeDownstreamComputerTargets

 

Gets or sets whether or not clients of a downstream server, not clients of this server, should be included.

IncludeSubgroups

 

Gets or sets whether the ComputerTargetGroups property should include descendant groups.

NameIncludes

 

Gets or sets a name to search for.

OSFamily

 

Gets or sets the operating system family for which to search.

ToLastReportedStatusTime

 

Gets or sets the latest last reported status time to search for.

ToLastSyncTime

 

Gets or sets the latest last synchronization time to search for.

Working with the Computer Target Scope

Now, if you remember from the previous posts, there are some methods from the Update object (Microsoft.UpdateServices.Internal.BaseApi.Update) and from the WSUS object (Microsoft.UpdateServices.Internal.BaseApi.UpdateServer) that require a computer scope object to work.

Let’s take a look at some of these methods from the respective objects and see what we are able to pull.

[void][reflection.assembly]::LoadWithPartialName("Microsoft.UpdateServices.Administration")

#Connect to the WSUS Server and create the wsus object

$wsus = [Microsoft.UpdateServices.Administration.AdminProxy]::getUpdateServer('dc1',$False)

#Create a computer scope object

$computerscope = New-Object Microsoft.UpdateServices.Administration.ComputerTargetScope

#Find all clients using the computer target scope

$wsus.GetComputerTargets($computerscope)

Image of command output

By leaving the default properties, we will pull every client from the WSUS server.

Another method that we can use, which provides a report on the computers that match the scope filter is GetComputerStatus(). This method requires that you have a computer scope object and that you define a Microsoft.UpdateServices.Administration.UpdateSources object, which consists of All, Microsoft Update, or Other. Let’s give it a look.

$Wsus.GetComputerStatus($computerscope,[ Microsoft.UpdateServices.Administration.UpdateSources]::All)

Image of command output

You might be wondering why we are seeing mostly 0s here. If you look closely, you can see that only the areas where it mentions the computer targets are populated. This is by design for this object’s method because we only specified to get the status of the computers that match the scope filter.

Now, looking at the update object’s method, I can see a couple of methods that are available and require the computer scope.

GetSummary()

This will give us a summary of whether the clients that are specified in the scope require the update.

$updates = $wsus.SearchUpdates('Update for Windows Server 2003 (KB938759)')

$update = $updates[0]

$update.GetSummary($computerscope)

 

UnknownCount                : 0

NotApplicableCount          : 2

NotInstalledCount           : 0

DownloadedCount             : 1

InstalledCount              : 0

InstalledPendingRebootCount : 0

FailedCount                 : 0

IsSummedAcrossAllUpdates    : False

UpdateId                    : c2cdd066-7a03-4e7f-976c-139b5de943ed

ComputerTargetGroupId       : 00000000-0000-0000-0000-000000000000

ComputerTargetId            :

LastUpdated                 : 12/10/2011 7:08:29 AM

By looking at the data provided, I can see that only 1 client out of the 3 require this update and that it has already been downloaded to that client. This is a nice way of providing a report of a specific update’s status for clients. But the problem is…which client requires the update? I will show you how to use the GetUpdateInstallationInfoPerComputerTarget() method on the Update object.

$update.GetUpdateInstallationInfoPerComputerTarget($ComputerScope)

Image of command output

Although it does tell you which client ID requires the update, it’s not what I would call “humanly readable” by any means. But this is Windows PowerShell, after all, and we can certainly make this better for us to read.

$update.GetUpdateInstallationInfoPerComputerTarget($ComputerScope) |

 Select @{L='Client';E={$wsus.GetComputerTarget(([guid]$_.ComputerTargetId)).FulldomainName}},

@{L='TargetGroup';E={$wsus.GetComputerTargetGroup(([guid]$_.UpdateApprovalTargetGroupId)).Name}},

 @{L='Update';E={$wsus.GetUpdate(([guid]$_.UpdateId)).Title}},

 UpdateInstallationState,UpdateApprovalAction

Image of command output

Much better! Now we have something that not only tells us which client, but actually gives us the name of the client without giving us a GUID to try to guess with. I also translated the Target Group ID and the Update ID so that everything is much easier to read.

Well, that wraps it up for today. Tomorrow I will jump into working with the Update Scope object and performing queries that use that object.

~Boe

Boe, thank you for another great blog about using the WSUS object model. WSUS Week will continue tomorrow.

I invite you to follow me on Twitter and Facebook. If you have any questions, send email to me at scripter@microsoft.com, or post your questions on the Official Scripting Guys Forum. See you tomorrow. Until then, peace.

Ed Wilson, Microsoft Scripting Guy

Your comment has been posted.   Close
Thank you, your comment requires moderation so it may take a while to appear.   Close
Leave a Comment
  • Hi Boe,

    Thumbs up for the great blogs!

    Is it possible that the computerscope only contains servers? What command would get that result?

    Tnx

  • Hi Davy,

    Glad you have enjoyed my series on WSUS! Unfortunately, the ComputerScope does not lend itself very well to identifying only servers in WSUS, you would have better luck with using a generic search and filter by OSDescription.

    $wsus.GetComputerTargets() | Where {$_.OSDescription -like "*server*"}

    Or if you have Target Groups with a name containing server, for example:

    $wsus.GetComputerTargetGroups() | Where {$_.name -Like '*Server*'} | ForEach {$_.GetComputerTargets()}

    Hope this helps...

  • Thanks for your reply.

    What I wanted to achive is that I could get a list of our servers that have updates waiting and the number of updates.

    But I can't seem to get it done.

    Any tips?

  • This can be done by first using my previous post here to filter for only servers and collecting the GUID of each computer (ID property). Then, using the article where I talk about using the GetSummariesPerComputerTarget() method, we can generate the update summary and begin filtering out non servers by checking it against our collection of servers. This should give you what you are looking for.

    #Load assemblies

    [void][system.reflection.assembly]::LoadWithPartialName('Microsoft.UpdateServices.Administration')

    #Connect to WSUS

    $Global:wsus = [Microsoft.UpdateServices.Administration.AdminProxy]::getUpdateServer('j23',$False)

    #Create Scope objects

    $computerscope = New-Object Microsoft.UpdateServices.Administration.ComputerTargetScope

    $updatescope = New-Object Microsoft.UpdateServices.Administration.UpdateScope

    #Gather only servers

    $ServersId = @($wsus.GetComputerTargets($computerscope) | Where {

       $_.OSDescription -like "*Server*"

    } | Select -expand Id)

    #Get Update Summary

    $wsus.GetSummariesPerComputerTarget($updatescope,$computerscope) | Where {

       #Filter out non servers

       $ServersId -Contains $_.ComputerTargetID

    } | ForEach {

       New-Object PSObject -Property @{

           ComputerTarget = ($wsus.GetComputerTarget([guid]$_.ComputerTargetId)).FullDomainName

           NeededCount = ($_.DownloadedCount + $_.NotInstalledCount)

           DownloadedCount = $_.DownloadedCount

           NotApplicableCount = $_.NotApplicableCount

           NotInstalledCount = $_.NotInstalledCount

           InstalledCount = $_.InstalledCount

           FailedCount = $_.FailedCount

       }

    }

    Hope this helps...

  • Hi, First of all - Great job. It's really nice to see things arranged and explained. Second, there is a report I would like to get from my WSUS. Unfortunately I couldn't find it in WSUS console. I hope that you could help me. I want to perform on a regular basis a report about clients that have failed to report to the server. Currently I saw a lot of computers that didn't report, and we are talking for arround 300 PCs in different OUs in one domain. I just need information about hostnames, IPs, and how many days. Is it possible to be achieved?

  • @kicho That can be accomplished using the following code: #Load assemblies [void][system.reflection.assembly]::LoadWithPartialName('Microsoft.UpdateServices.Administration') #Connect to WSUS $wsus = [Microsoft.UpdateServices.Administration.AdminProxy]::getUpdateServer('',$False) $computerscope = New-Object Microsoft.UpdateServices.Administration.ComputerTargetScope #Threshold here is to report anything over 30 days old, but can be adjusted to fit your requirements $computerscope.ToLastReportedStatusTime = (Get-Date).AddDays(-30) $Wsus.GetComputerTargets($computerscope) | ForEach { If ($_.LastReportedStatusTime -eq '1/1/0001 12:00:00 AM') { $status = 'NeverReported' } Else { $status = (New-TimeSpan -Start $_.LastReportedStatusTime -End (Get-Date)).Days } [pscustomobject]@{ Computername = $_.FullDomainName IP = $_.IPAddress LastReportedStatus = $_.LastReportedStatusTime DaysOld = $status } }

  • Hi All, thanks Boe for those explanation, there are very usefull!
    I'm trying to change ComputerTargetGroups of "Computer Target Scope" object but i can't.
    I would like to filter to a different target group, like "unassigned computers", is that possible?

    Thanks!

    Boby

  • This is great! I need some help. I need a report to see how many computers have had internet explorer 10 installed with the list of computernames. Is that possible? Can you tell me how?

    Thank you

  • @Boby
    As far as I know, this is not possible.

  • Wouldn't that be awesome though? Thanks for your response.

  • @Boby

    Figures as soon as I say that it cannot be done, I find out how to do it.

    $ComputerScope=New-Object Microsoft.UpdateServices.Administration.ComputerTargetScope
    $Wsus = [Microsoft.UpdateServices.Administration.AdminProxy]::GetUpdateServer($Computername,$UseSSL,$Port)
    $TargetGroupHash = @{}
    $wsus.GetComputerTargetGroups() | ForEach {
    $TargetGroupHash[$_.Name] = $_
    }
    $ComputerScope.ComputerTargetGroups.Add($TargetGroupHash['Unassigned Computers'])