{"id":54233,"date":"2009-03-09T21:20:00","date_gmt":"2009-03-09T21:20:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2009\/03\/09\/hey-scripting-guy-how-can-i-list-all-updates-that-have-been-added-to-a-computer\/"},"modified":"2009-03-09T21:20:00","modified_gmt":"2009-03-09T21:20:00","slug":"hey-scripting-guy-how-can-i-list-all-updates-that-have-been-added-to-a-computer","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/hey-scripting-guy-how-can-i-list-all-updates-that-have-been-added-to-a-computer\/","title":{"rendered":"Hey, Scripting Guy! How Can I List All Updates That Have Been Added to a Computer?"},"content":{"rendered":"<p><H2><IMG class=\"nearGraphic\" title=\"Hey, Scripting Guy! Question\" border=\"0\" alt=\"Hey, Scripting Guy! Question\" align=\"left\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/q-for-powertip.jpg\" width=\"34\" height=\"34\"> <\/H2>\n<P>Hey, Scripting Guy! We have Windows Update configured on all our workstations at work. Automatically, the workstations check for new updates, and download and install the updates that are required. This happens without user intervention, or from intervention on the part of our small and overworked IT department. Here is the problem. The <A href=\"http:\/\/encarta.msn.com\/dictionary_1861738302\/pointy-headed.html\" target=\"_blank\">pointy-headed boss<\/A> (PHB) came floating into our office late Friday afternoon and asked &#8220;How do you know all the workstations are up to date?&#8221; I said, because we have <A href=\"http:\/\/www.microsoft.com\/windows\/downloads\/windowsupdate\/learn\/windowsvista.mspx\" target=\"_blank\">Windows Update configured on all the workstations<\/A>.&#8221; He said, &#8220;Can you display a list of all the updates that have ever been applied to that computer?&#8221; I said, &#8220;D\u2019oh!&#8221; So, Scripting Guy, can you hook me up with a script to list all the updates that have been added to a computer?<BR><BR>&#8211; JU<\/P><IMG border=\"0\" alt=\"Spacer\" src=\"https:\/\/devblogs.microsoft.com\/scripting\/wp-content\/uploads\/sites\/29\/2019\/05\/spacer.gif\" width=\"5\" height=\"5\"><IMG class=\"nearGraphic\" title=\"Hey, Scripting Guy! Answer\" border=\"0\" alt=\"Hey, Scripting Guy! Answer\" align=\"left\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/a-for-powertip.jpg\" width=\"34\" height=\"34\"> \n<P>Hi JU,<\/P>\n<P>I just hate not having an answer when the PHB comes floating by. Usually, I just make something up, because I figure he will not really know the difference anyway. Luckily, Windows Update has an API we can use to obtain information regarding updates that have been applied to the system.<\/P>\n<TABLE id=\"E6C\" class=\"dataTable\" cellSpacing=\"0\" cellPadding=\"0\">\n<THEAD><\/THEAD>\n<TBODY>\n<TR class=\"record\" vAlign=\"top\">\n<TD>\n<P class=\"lastInCell\">This week we will be looking at using the Windows Update API to work with Windows Update. The Windows Update API is documented <A href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/aa387099.aspx\" target=\"_blank\">on MSDN<\/A>. There is also a good collection of VBScripts that use the Windows Update API in the <A href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/scripts\/default.mspx?mfr=true\" target=\"_blank\">Script Center Script Repository<\/A>. For information about manually configuring Windows Update on workstations, refer to <A href=\"http:\/\/www.microsoft.com\/windows\/downloads\/windowsupdate\/learn\/windowsvista.mspx\" target=\"_blank\">this page<\/A>. These scripts use Windows PowerShell. <A href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/hubs\/msh.mspx\" target=\"_blank\">The Windows PowerShell getting started page<\/A> has basic information for learning about Windows PowerShell and about downloading Windows PowerShell.<\/P><\/TD><\/TR><\/TBODY><\/TABLE>\n<DIV class=\"dataTableBottomMargin\"><\/DIV>\n<P>The script we wrote is called <B>Get-MicrosoftUpdates.ps1<\/B> and it is seen here (you can also see <A href=\"http:\/\/www.microsoft.com\/technet\/scriptcenter\/scripts\/sus\/client\/suclvb09.mspx\" target=\"_blank\">a VBScript version of today&#8217;s script<\/A>):.<\/P><PRE class=\"codeSample\">Function Get-MicrosoftUpdates\n{ \n  Param(\n        $NumberOfUpdates,\n        [switch]$all\n       )\n  $Session = New-Object -ComObject Microsoft.Update.Session\n  $Searcher = $Session.CreateUpdateSearcher()\n  if($all)\n    {\n      $HistoryCount = $Searcher.GetTotalHistoryCount()\n      $Searcher.QueryHistory(1,$HistoryCount)\n    }\n  Else { $Searcher.QueryHistory(1,$NumberOfUpdates) }\n} #end Get-MicrosoftUpdates<\/p>\n<p>Get-MicrosoftUpdates\n<\/PRE>\n<P>The first thing we do in the script is create a function named <B>Get-MicrosoftUpdates<\/B>. To create a function in Windows PowerShell, we use the <B>Function<\/B> keyword, assign a name, and open and close the script block by using two curly brackets. The basic pattern is seen here:<\/P><PRE class=\"codeSample\">Function FunctionName\n{\n# Function code goes here\n} #end of the function\n<\/PRE>\n<P>Here is the portion of our script that creates the function and assigns a name to it:<\/P><PRE class=\"codeSample\">Function Get-MicrosoftUpdates\n{\n<\/PRE>\n<P>Now we create two command parameters. To do this, we use the <B>Param<\/B> keyword. The pattern we use to include parameters in a function is the same pattern as the one we use for a script. We use the <B>Param<\/B> keyword, open a set of parentheses, separate each parameter with a comma, and then close the set of parentheses. This pattern is seen here:<\/P><PRE class=\"codeSample\">Param(Parametername, additionalParameterName)\n<\/PRE>\n<P>In this script we use two parameters. The first parameter is called <B>\u2013NumberOfUpdates<\/B>. The value supplied to this parameter when calling the function will be stored in the <B>$NumberOfUpdates<\/B> variable. The second parameter is a switched parameter named <B>\u2013all<\/B>. A switched parameter does not receive a value when it is used\u2014rather, it only has effect when it is present. When the <B>\u2013all<\/B> parameter is not present, the script does not return all the updates. The parameter section of the <B>Get-MicrosoftUpdates<\/B> function is seen here:<\/P><PRE class=\"codeSample\">Param(\n        $NumberOfUpdates,\n        [switch]$all\n       )\n<\/PRE>\n<P>It is time to create an instance of the <B>Microsoft Update Session<\/B> object (technically known as the <A href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/aa386854(VS.85).aspx\" target=\"_blank\">IUpdateSession interface<\/A>). The <B>Microsoft Update Session<\/B> object is a COM object, and can be created by using the <B>New-Object<\/B> cmdlet with the <B>\u2013ComObject<\/B> parameter. It is not necessary to place quotation marks around the program ID <B>Microsoft.Update.Session<\/B> when using the <B>New-Object<\/B> cmdlet. We store the returned object in the <B>$session<\/B> variable, as seen here:<\/P><PRE class=\"codeSample\">$Session = New-Object -ComObject Microsoft.Update.Session<\/PRE>\n<P>The <B>Session<\/B> object has a number of methods and properties available, which are listed in <B>Table 1<\/B>.<\/P>\n<TABLE id=\"EMG\" class=\"dataTable\" cellSpacing=\"0\" cellPadding=\"0\">\n<THEAD>\n<TR>\n<TD class=\"tableHeader\" colSpan=\"3\">Table 1 Members of the Microsoft Update Session object <\/TD><\/TR>\n<TR class=\"stdHeader\" vAlign=\"top\">\n<TD id=\"colEPG\">Name<\/TD>\n<TD id=\"colETG\">Type<\/TD>\n<TD id=\"colEXG\">Definition<\/TD><\/TR><\/THEAD>\n<TBODY>\n<TR class=\"record\" vAlign=\"top\">\n<TD>\n<P class=\"lastInCell\">CreateUpdateDownloader<\/P><\/TD>\n<TD>\n<P class=\"lastInCell\">Method<\/P><\/TD>\n<TD>\n<P class=\"lastInCell\">IUpdateDownloader CreateUpdateDownload<\/P><\/TD><\/TR>\n<TR class=\"evenRecord\" vAlign=\"top\">\n<TD>\n<P class=\"lastInCell\">CreateUpdateInstaller<\/P><\/TD>\n<TD>\n<P class=\"lastInCell\">Method<\/P><\/TD>\n<TD>\n<P class=\"lastInCell\">IUpdateInstaller CreateUpdateInstaller ()<\/P><\/TD><\/TR>\n<TR class=\"record\" vAlign=\"top\">\n<TD>\n<P class=\"lastInCell\">CreateUpdateSearcher<\/P><\/TD>\n<TD>\n<P class=\"lastInCell\">Method<\/P><\/TD>\n<TD>\n<P class=\"lastInCell\">IUpdateSearcher CreateUpdateSearcher ()<\/P><\/TD><\/TR>\n<TR class=\"evenRecord\" vAlign=\"top\">\n<TD>\n<P class=\"lastInCell\">CreateUpdateServiceManager<\/P><\/TD>\n<TD>\n<P class=\"lastInCell\">Method<\/P><\/TD>\n<TD>\n<P class=\"lastInCell\">IUpdateServiceManager2 CreateUpdateSer<\/P><\/TD><\/TR>\n<TR class=\"record\" vAlign=\"top\">\n<TD>\n<P class=\"lastInCell\">QueryHistory<\/P><\/TD>\n<TD>\n<P class=\"lastInCell\">Method<\/P><\/TD>\n<TD>\n<P class=\"lastInCell\">IUpdateHistoryEntryCollection QueryHis<\/P><\/TD><\/TR>\n<TR class=\"evenRecord\" vAlign=\"top\">\n<TD>\n<P class=\"lastInCell\">ClientApplicationID<\/P><\/TD>\n<TD>\n<P class=\"lastInCell\">Property<\/P><\/TD>\n<TD>\n<P class=\"lastInCell\">string ClientApplicationID () {get} {s<\/P><\/TD><\/TR>\n<TR class=\"record\" vAlign=\"top\">\n<TD>\n<P class=\"lastInCell\">ReadOnly<\/P><\/TD>\n<TD>\n<P class=\"lastInCell\">Property<\/P><\/TD>\n<TD>\n<P class=\"lastInCell\">bool ReadOnly () {get}<\/P><\/TD><\/TR>\n<TR class=\"evenRecord\" vAlign=\"top\">\n<TD>\n<P class=\"lastInCell\">UserLocale<\/P><\/TD>\n<TD>\n<P class=\"lastInCell\">Property<\/P><\/TD>\n<TD>\n<P class=\"lastInCell\">uint UserLocale () {get} {set}<\/P><\/TD><\/TR>\n<TR class=\"record\" vAlign=\"top\">\n<TD>\n<P class=\"lastInCell\">WebProxy<\/P><\/TD>\n<TD>\n<P class=\"lastInCell\">Property<\/P><\/TD>\n<TD>\n<P class=\"lastInCell\">IWebProxy WebProxy () {get} {set}<\/P><\/TD><\/TR><\/TBODY><\/TABLE>\n<DIV class=\"dataTableBottomMargin\"><\/DIV>\n<P>Now we need to create an <B>U<\/B><B>pdate<\/B><B>S<\/B><B>earcher<\/B> object. To create an <B>U<\/B><B>pdate <\/B><B>S<\/B><B>earcher<\/B> object, we use the <B>CreateUpdateSearcher<\/B> method from the <B>S<\/B><B>ession<\/B> object. We store the <B>Searcher<\/B> object in the variable <B>$Searcher<\/B> as seen here:<\/P><PRE class=\"codeSample\">$Searcher = $Session.CreateUpdateSearcher()<\/PRE>\n<P>Then we need to determine if the function was called with the <B>\u2013all<\/B> switch or not. If the function was called with the <B>\u2013all<\/B> switch, the <B>$all <\/B>variable will be present. If it was not used, the <B>$all<\/B> variable will not exist. We use the <B>if<\/B> statement to determine if the <B>$all<\/B> variable is present. An example of using the <B>if<\/B> statement to test for the presence of a variable is seen here:<\/P><PRE class=\"codeSample\">PS C:\\&gt; $all = $true\nPS C:\\&gt; if($all) {&#8216;$all was found&#8217;}\n$all was found\n<\/PRE>\n<P>If we want to see if the <B>$all<\/B> variable is not present, we can use the not <B>(!) <\/B>operator as shown here:<\/P><PRE class=\"codeSample\">PS C:\\&gt; $all = $false\nPS C:\\&gt; if(!$all) {&#8216;$all was not found&#8217;}\n$all was not found\n<\/PRE>\n<P>In our script, we use the <B>if<\/B> statement and look for the <B>$all<\/B> variable. If we find it, we use the <B>GetTotalHistoryCount<\/B> property from the <B>Searcher<\/B> object to retrieve the total number of updates that have been applied to the system. We need this information because the <B>QueryHistory<\/B> method of the <B>Searcher<\/B> object needs a starting point and an ending point. If we want to get all the updates, we start at one and go until we have reached the total number of updates applied to the system. This is seen here:<\/P><PRE class=\"codeSample\">if($all)\n    {\n      $HistoryCount = $Searcher.GetTotalHistoryCount()\n      $Searcher.QueryHistory(1,$HistoryCount)\n    }\n<\/PRE>\n<P>If the script was not run with the <B>\u2013all<\/B> switched parameter, we will be retrieving a specific number of updates from the system. The total number of updates to be retrieved is passed to the function via the <B>\u2013NumberOfUpdates<\/B> parameter and will be stored in the <B>$NumberOfUpdates<\/B> variable. This section of the script is seen here:<\/P><PRE class=\"codeSample\">Else { $Searcher.QueryHistory(1,$NumberOfUpdates) }\n} #end Get-MicrosoftUpdates\n<\/PRE>\n<P>To&nbsp;retrieve all the update information, you call the <B>Get-MicrosoftUpdates<\/B> function with the <B>\u2013all<\/B> parameter:<\/P><PRE class=\"codeSample\">Get-MicrosoftUpdates \u2013all<\/PRE>\n<P>When we call the function with the <B>\u2013all<\/B> parameter, we are treated with an output similar to the one seen here:<\/P><IMG border=\"0\" alt=\"Image of the output produced by the script\" src=\"http:\/\/img.microsoft.com\/library\/media\/1033\/technet\/images\/scriptcenter\/qanda\/hsg\/2009\/march\/hey0309\/hsg-3-9-9-1.jpg\" width=\"500\" height=\"409\"> \n<P>&nbsp;<\/P>\n<P>If we call the function with the <B>\u2013NumberOfUpdates<\/B> parameter set to 1, we retrieve the most recent update that was applied to the system. The syntax for this is seen here:<\/P><PRE class=\"codeSample\">Get-MicrosoftUpdates \u2013NumberOfUpdates 1<\/PRE>\n<P>When we do this, we are treated with the output seen here:<\/P><IMG border=\"0\" alt=\"Image of the output produced by the script\" src=\"http:\/\/img.microsoft.com\/library\/media\/1033\/technet\/images\/scriptcenter\/qanda\/hsg\/2009\/march\/hey0309\/hsg-3-9-9-2.jpg\" width=\"500\" height=\"248\"> \n<P>&nbsp;<\/P>\n<P>If we decide we would like to have a text file that contains all of the update information, we only need to use the redirection arrows as shown here when we call the function:<\/P><PRE class=\"codeSample\">Get-MicrosoftUpdates -all &gt;&gt; c:\\fso\\updates.txt ; notepad c:\\fso\\updates.txt<\/PRE>\n<P>Well, JU, that is all there is to querying the update history for Windows Update. If you were to compare the VBScript version and the Windows PowerShell version, you would notice we receive a nice bonus when using Windows PowerShell in that we do not need to use a <B>wscript.echo<\/B> kind of line for each of the properties in which we are interested. We used the extra lines of code to create a function and to allow the ability to choose how many updates to report. Hope you have a great day, and we will see you tomorrow as Windows Update Week continues. Until then, peace.<\/P>\n<P>&nbsp;<\/P>\n<P><B>Ed Wilson and Craig Liebendorfer, Scripting Guys<\/B><\/P><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Hey, Scripting Guy! We have Windows Update configured on all our workstations at work. Automatically, the workstations check for new updates, and download and install the updates that are required. This happens without user intervention, or from intervention on the part of our small and overworked IT department. Here is the problem. The pointy-headed boss [&hellip;]<\/p>\n","protected":false},"author":595,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[36,3,45,380],"class_list":["post-54233","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-client-side-management","tag-scripting-guy","tag-windows-powershell","tag-windows-update"],"acf":[],"blog_post_summary":"<p>Hey, Scripting Guy! We have Windows Update configured on all our workstations at work. Automatically, the workstations check for new updates, and download and install the updates that are required. This happens without user intervention, or from intervention on the part of our small and overworked IT department. Here is the problem. The pointy-headed boss [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/54233","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/595"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=54233"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/54233\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=54233"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=54233"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=54233"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}