Get Windows Update Status Information by Using PowerShell

Get Windows Update Status Information by Using PowerShell

  • Comments 10
  • Likes

Summary: Learn how to use the WSUS Update Scope with Windows PowerShell to get update status information for client computers.

Microsoft Scripting Guy, Ed Wilson, is here. What a week. Boe Prox has certainly been sharing quite a bit of Windows PowerShell goodness. In case you have missed them, here are links to the blog series thus far. You can also see Boe’s biography in the Day 1 blog.

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

Day 4: Use PowerShell to Find Missing Updates on WSUS Client Computers

Today we are moving from working with the Computer Target Scope and generating some cool reports to working with the Update Scope on the WSUS server. Much like yesterday, I will dive into some of the objects that we have worked with throughout the week and make use of some of the methods that require the Update Scope object to work properly.

If you are asking, “What is the Update Scope?”

…As the name implies, this is a scope that can be used to filter a list of updates on the WSUS server.

Creating the Update Scope object

Much as we did with the Computer Target Scope object, we need first to create the Update Scope object so we can then look at its properties and make adjustments, if needed. To create this object, we need to use the Microsoft.UpdateServices.Administration.UpdateScope class. For more information about this class, see UpdateScope Class on MSDN. The code that follows creates an instance of the UpdateScope.

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

OK, let’s take a look at these properties. The image that follows displays this information.

Image of command output

Here is a list of the editable properties for the Update Scope object:

ApprovedStates

 

Gets or sets the approval states to search for. An update will be included only if it matches at least one of the specified states. This value may be a combination of any number of values from ApprovedStates. Defaults to Any.

ExcludedInstallationStates

 

Gets or sets the installation states to exclude. An update will be included only if it does not have any computers in any of the specified states. This value may be a combination of any number of values from UpdateInstallationStates. Defaults to 0.

ExcludeOptionalUpdates

 

Gets or sets whether to exclude optional updates from the list.

FromArrivalDate

 

Gets or sets the minimum arrival date to search for. An update will be included only if its arrival date is greater than or equal to this value.

FromCreationDate

 

Gets or sets the minimum creation date to search for. An update will be included only if its creation date is greater than or equal to this value.

IncludedInstallationStates

 

Gets or sets the installation states to search for. An update will be included only if it has at least one computer in one of the specified states. This value may be a combination of any number of values from UpdateInstallationStates.

IsWsusInfrastructureUpdate

 

Gets or sets whether or not to filter for WSUS infrastructure updates. If set to true, only WSUS infrastructure updates will be included. If set to false, all updates are included. Defaults to false.

TextIncludes

 

Gets or sets the string to search for. An update will be included only if its Title, Description, Knowledge Base articles, or security bulletins contains this string.

TextNotIncludes

 

Gets or sets the string to exclude. An update will be not be included if its Title, Description, Knowledge Base articles, or security bulletins contains this string.

ToArrivalDate

 

Gets or sets the maximum arrival date to search for. An update will be included only if its arrival date is less than or equal to this value.

ToCreationDate

 

Gets or sets the maximum creation date to search for. An update will be included only if its creation date is less than or equal to this value.

UpdateApprovalActions

 

Gets or sets the update approval actions to search for. An update will be included only if it is approved to at least one computer target group for one of the specified approval actions. This value may be a combination of any number of values from UpdateApprovalActions. Defaults to All.

UpdateApprovalScope

 

Gets or sets the UpdateApprovalScope object that can be used to filter updates based on their approval properties.

UpdateSources

 

Gets or sets the update sources to search for. An update will be included only if its update source is included in this value. This value may be a combination of any number of values from UpdateSources.

UpdateTypes

 

Gets or sets the update types to search for. An update will be included only if its update type is included in this value.

Yes, that is a fairly large list of properties that we can modify to suit our filtering needs. For the sake of simplicity, I am only going to update ApprovedStates, IncludedInstallationStates, and FromArrivalTime to show how you can see all of the updates that are needed by all of the clients since the last patch Tuesday. The code that follows updates these three properties.

$updatescope.ApprovedStates = [Microsoft.UpdateServices.Administration.ApprovedStates]::NotApproved

$updatescope.IncludedInstallationStates = [Microsoft.UpdateServices.Administration.UpdateInstallationStates]::NotInstalled

$updatescope.FromArrivalDate = [datetime]"12/13/2011"

Note: For the purposes of this blog, the last patch Tuesday was December 13. I set the ApprovedStates property to NotApproved because I only want to see updates that are required by the clients that are not already approved for installation. I set the IncludedInstallationStates to NotInstalled. This will tell the filter to look for only updates that are required by the clients, but have not been installed yet. This works fine for us because these updates are new to us and because we only want those for the most recent patch Tuesday.

First, I am going to show you the Administration Console and look at all updates that are required by the clients on the network since patch Tuesday. This is shown in the image that follows.

Image of menu

Note the number of updates (30) and some of the titles listed here. Although not all of the updates are listed in the image, I will show you a couple of methods that we can use to pull the exact same information by using Windows PowerShell!

GetUpdateCount()

This method, as you can probably tell, will allow us to view the number of updates that are returned by using the previously configured Update Scope.

$wsus.GetUpdateCount($updatescope)

Image of computer output

Look at that, 30 updates—just as if it showed on the console.

I am going to expand on this slightly by using another method similar to one I showed you yesterday for the Computer Target Scope.

GetUpdateStatus()

This will return a little more information about the updates, as you will see. Besides supplying the Update Scope object, I need to supply a Boolean value for whether to include DownStream Computers.

$wsus.GetUpdateStatue($updatescope,$False)

Image of computer output

That is pretty cool, but we want to see those updates as well. Sure enough, we can use the GetUpdates() method and supply our Update Scope object to pull this information.

$wsus.GetUpdates($updatescope) | Select Title

Image of computer output

And there you have it! You can compare the screenshots and you will see that the titles in the console match the titles from our query.

Now I will show you how to view the update approvals by using the Update Scope object and the GetUpdateApprovals() method. To make this work and to provide some useful information, I will make a couple of adjustments to the existing Update Scope. This revision is shown here.

$updatescope.FromArrivalDate = [datetime]"10/01/2011"

$updatescope.ApprovedStates = [Microsoft.UpdateServices.Administration.ApprovedStates]::LatestRevisionApproved

Now we can use this scope in the method and pull some information. To do this, I use the GetUpdateApprovals method, and pass it the Update Scope. This technique is shown in the code that follows.

$wsus.GetUpdateApprovals($updatescope)

The result from the previous command is shown in the following image.

Image of computer output

Let us clean this up a little so it is more readable and not just a bunch of GUIDs. The code that follows produces an easier to read output.

$wsus.GetUpdateApprovals($updatescope) | Select @{L='ComputerTargetGroup';E={$_.GetComputerTargetGroup().Name}},

@{L='UpdateTitle';E={($wsus.GetUpdate([guid]$_.UpdateId.UpdateId.Guid)).Title}},GoLiveTime,AdministratorName,Deadline

The result of the previous code is shown here.

Image of computer output

The last two things I will show you in this blog are a couple of methods that require the Update Scope object and the Computer Target Scope object to work properly. They provide some nice reporting features similar to what you would see in the Administration Console.

GetSummariesPerComputerTarget()

This method gets per-computer summaries for each of the specified computers, summed across all of the specified updates.

Let’s see this in action, where I use the following code to gather this information.

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

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

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

    Format-Table @{L='ComputerTarget';E={($wsus.GetComputerTarget([guid]$_.ComputerTargetId)).FullDomainName}},

        @{L='NeededCount';E={($_.DownloadedCount + $_.NotInstalledCount)}},DownloadedCount,NotApplicableCount,NotInstalledCount,InstalledCount,FailedCount

The output from the previous code produces a nice table, as shown here.

Image of computer output

Let’s see how this compares to what you would see in the console. One thing you do not have available in the console is the DownloadedCount, which our previous code nicely displayed.

Image of menu

Lastly, let’s look at the GetSummariesPerUpdate() method to get per-update summaries for each of the specified updates, summed across all of the specified computers. The following code uses this method.

$updatescope.ApprovedStates = [Microsoft.UpdateServices.Administration.ApprovedStates]::NotApproved

$updatescope.IncludedInstallationStates = [Microsoft.UpdateServices.Administration.UpdateInstallationStates]::NotInstalled

$updatescope.FromArrivalDate = [datetime]"12/13/2011"   

$wsus.GetSummariesPerUpdate($updatescope,$computerscope) |

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

        @{L='NeededCount';E={($_.DownloadedCount + $_.NotInstalledCount)}},DownloadedCount,NotApplicableCount,NotInstalledCount,InstalledCount,FailedCount

Note: I changed the Update Scope to what I had at the beginning of this blog. This is so I can more accurately compare the output here with what is in the Administration Console.

The output from the previous code is shown here.

Image of computer output

Now compare the previous image, with the console (shown here), and you will see that they match up as planned.

Image of menu

So there you have it. We spent the week starting out with a WSUS server installation, then doing some basic administration, and we finished by providing some reports using Windows PowerShell—and we haven’t even begun to scratch the surface of what we can do. But working with WSUS doesn’t end here! This weekend I will talk about update 2.0 to my WSUS module, PoshWSUS, which you can use to more easily manage and maintain a WSUS server.

~Boe

Boe, this is great stuff. Thank you for sharing with us. 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
  • <p>Thank you for the great write ups on powershell and WSUS. &nbsp;I have one question that has me scratching my head. &nbsp;Attempting to create a update scope with only certain classifications using this code:</p> <p>$wsus.GetUpdateClassifications() |? {$_.Title -eq &quot;Critical Updates&quot; -OR $_.Title -eq &quot;Security Updates&quot; -OR $_.Title -eq &quot;Updates&quot; -OR $_.Title -eq &quot;Update Rollups&quot;}</p> <p>And I get the error: &quot;Classifications&quot; is a ReadOnly property. &nbsp;However looking at description on msdn it says it &quot;Gets or Sets&quot; so I was assuming you could set it like any of the other properties? &nbsp;Am I missing something here?</p> <p>Thanks!</p>

  • <p>I left out a pretty important part... here is the full line of code :)</p> <p>$updatescope.Classifications = $wsus.GetUpdateClassifications() |? {$_.Title -eq &quot;Critical Updates&quot; -OR $_.Title -eq &quot;Security Updates&quot; -OR $_.Title -eq &quot;Updates&quot; -OR $_.Title -eq &quot;Update Rollups&quot;} </p>

  • <p>@JasonF The Classifications property is actual a Read-Only value. While the MSDN page says it is Get or Set, if you look at the actual property in the object, it only has Get.</p> <p>(($updatescope | gm -Type Property) | ? {$_.name -eq &#39;Classifications&#39;}).Definition</p> <p>Microsoft.UpdateServices.Administration.UpdateClassificationCollection Classifications {get;}</p>

  • <p>Great stuff, love the series and the forum! I am n00B but *think* I&#39;m catching up pretty quick - quick enough to tell (IMHO) that there&#39;s no easy way to go from the screen output above from the code...</p> <p>$wsus.GetSummariesPerUpdate($updatescope,$computerscope) | </p> <p>Format-Table @{L=&#39;UpdateTitle&#39;;E={($wsus.GetUpdate([guid]$_.UpdateId)).Title}},</p> <p>@{L=&#39;NeededCount&#39;;E={($_.DownloadedCount + $_.NotInstalledCount)}},DownloadedCount,NotApplicableCount,NotInstalledCount,InstalledCount,FailedCount</p> <p>...to a .csv or other file format for reporting (I&#39;m trying to put this together for a scripted email to send raw patch status data to IT Security). I&#39;ve tried a few things, but am really getting trouble putting needed counts per-kb into a csv file. Any suggestions on how to attack?</p>

  • <p>@ T.Willy Based on your comment (and you can correct me if I am wrong), it sounds like you are trying to pipe the results of the Format-Table into a CSV file which will not work because PowerShell applies its own custom formatting to the existing object which results in garbage being sent to the file.</p> <p>Instead of using that, you can switch Format-Table into Select-Object and it will work great with Export-Csv to send to a spreadsheet.</p> <p>$wsus.GetSummariesPerUpdate($updatescope,$computerscope) | </p> <p>Select-Object @{L=&#39;UpdateTitle&#39;;E={($wsus.GetUpdate([guid]$_.UpdateId)).Title}},</p> <p>@{L=&#39;NeededCount&#39;;E={($_.DownloadedCount + $_.NotInstalledCount)}},DownloadedCount,NotApplicableCount,NotInstalledCount,InstalledCount,FailedCount |</p> <p>Export-Csv -NoTypeInformation &#39;WsusReport.csv&#39;</p> <p>Let me know if this helps...</p>

  • <p>Thank You for the information. I&#39;m working on a script to gather information from WSUS server. It should look at a specific computer group &quot;A&quot; and show how many failed updates each of them have. But is there a way to also show those failed updates (names) for each computer in computer group&quot;A&quot;?</p> <p>The script so far:</p> <p>[void][reflection.assembly]::LoadWithPartialName(&quot;Microsoft.UpdateServices.Administration&quot;) | Out-Null</p> <p>$ServerName = &quot;xxx&quot;</p> <p>$wsus = [Microsoft.UpdateServices.Administration.AdminProxy]::getUpdateServer($ServerName,$False)</p> <p>$computerscope = New-Object Microsoft.UpdateServices.Administration.ComputerTargetScope </p> <p>$computerscope.IncludedInstallationStates = [Microsoft.UpdateServices.Administration.UpdateInstallationStates]::Failed</p> <p>$updatescope = New-Object Microsoft.UpdateServices.Administration.UpdateScope </p> <p>$updatescope.IncludedInstallationStates = [Microsoft.UpdateServices.Administration.UpdateInstallationStates]::Failed</p> <p>$pcgroup = @($wsus.GetComputerTargets($computerscope) | Where {$_.ComputerTargetGroupIds -like &#39;529963a3-28ac-4317-abbf-52d4fcc4006f&#39;}) | Select -expand Id</p> <p>$wsus.GetSummariesPerComputerTarget($updatescope,$computerscope) | Where {</p> <p>$pcgroup -Contains $_.ComputerTargetID</p> <p>} | ForEach {</p> <p>New-Object PSObject -Property @{</p> <p>ComputerTarget = ($wsus.GetComputerTarget([guid]$_.ComputerTargetId)).FullDomainName</p> <p>NeededCount = ($_.DownloadedCount + $_.NotInstalledCount)</p> <p>DownloadedCount = $_.DownloadedCount</p> <p>NotApplicableCount = $_.NotApplicableCount</p> <p>NotInstalledCount = $_.NotInstalledCount</p> <p>InstalledCount = $_.InstalledCount</p> <p>FailedCount = $_.FailedCount</p> <p>}</p> <p>}</p>

  • <p>@Tonyc89</p> <p>Here is the approach I would take to list out all of the failed updates with each computer (Look at the FailedUpdates property I created in my custom object):</p> <p>[reflection.assembly]::LoadWithPartialName(&quot;Microsoft.UpdateServices.Administration&quot;) | Out-Null</p> <p>$ServerName = &quot;xxx&quot;</p> <p>$wsus = [Microsoft.UpdateServices.Administration.AdminProxy]::getUpdateServer($ServerName,$False)</p> <p>$computerscope = New-Object Microsoft.UpdateServices.Administration.ComputerTargetScope </p> <p>$computerscope.IncludedInstallationStates = [Microsoft.UpdateServices.Administration.UpdateInstallationStates]::Failed</p> <p>$updatescope = New-Object Microsoft.UpdateServices.Administration.UpdateScope </p> <p>$updatescope.IncludedInstallationStates = [Microsoft.UpdateServices.Administration.UpdateInstallationStates]::Failed</p> <p>#Create Forward and Reverse lookup</p> <p>$groups = @{}</p> <p>$wsus.GetComputerTargetGroups() | ForEach {$groups[$_.Name]=$_.id;$groups[$_.ID]=$_.name}</p> <p>$pcgroup = @($wsus.GetComputerTargets($computerscope) | Where {$_.ComputerTargetGroupIds -eq $groups[&#39;A&#39;]}) | Select -expand Id</p> <p>$wsus.GetSummariesPerComputerTarget($updatescope,$computerscope) | Where {</p> <p> &nbsp; &nbsp;$pcgroup -Contains $_.ComputerTargetID</p> <p>} | ForEach {</p> <p> &nbsp; &nbsp;$ComputerTarget = ($wsus.GetComputerTarget([guid]$_.ComputerTargetId))</p> <p> &nbsp; &nbsp;New-Object PSObject -Property @{</p> <p> &nbsp; &nbsp; &nbsp; &nbsp;ComputerTarget = $ComputerTarget.FullDomainName</p> <p> &nbsp; &nbsp; &nbsp; &nbsp;NeededCount = ($_.DownloadedCount + $_.NotInstalledCount)</p> <p> &nbsp; &nbsp; &nbsp; &nbsp;DownloadedCount = $_.DownloadedCount</p> <p> &nbsp; &nbsp; &nbsp; &nbsp;NotApplicableCount = $_.NotApplicableCount</p> <p> &nbsp; &nbsp; &nbsp; &nbsp;NotInstalledCount = $_.NotInstalledCount</p> <p> &nbsp; &nbsp; &nbsp; &nbsp;InstalledCount = $_.InstalledCount</p> <p> &nbsp; &nbsp; &nbsp; &nbsp;FailedCount = $_.FailedCount</p> <p> &nbsp; &nbsp; &nbsp; &nbsp;FailedUpdates = $ComputerTarget.GetUpdateInstallationInfoPerUpdate($updatescope).GetUpdate().Title</p> <p> &nbsp; &nbsp;}</p> <p>}</p>

  • <p>@Tonyc89</p> <p>If running PowerShell V2, just update the FailedUpdates property to this:</p> <p>FailedUpdates = $ComputerTarget.GetUpdateInstallationInfoPerUpdate($updatescope) | ForEach {$_.GetUpdate().Title}</p>

  • <p>Thank You! This is exactly what i needed.</p>

  • @Boe, Thanks...any ideas how to format the output into table... tried couple of echos, plus the format table, but cannot seem to put this New-Object PSObject -Property into table....Format-Table @{L='ComputerTarget';E={($wsus.GetComputerTarget([guid]$_.ComputerTargetId)).FullDomainName}}, @{L='NeededCount';E={($_.DownloadedCount &#43; $_.NotInstalledCount)}},DownloadedCount,NotApplicableCount,NotInstalledCount,InstalledCount,FailedCount