{"id":11791,"date":"2011-12-14T00:01:00","date_gmt":"2011-12-14T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2011\/12\/14\/use-powershell-to-find-and-uninstall-software\/"},"modified":"2011-12-14T00:01:00","modified_gmt":"2011-12-14T00:01:00","slug":"use-powershell-to-find-and-uninstall-software","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/use-powershell-to-find-and-uninstall-software\/","title":{"rendered":"Use PowerShell to Find and Uninstall Software"},"content":{"rendered":"<p><b>Summary<\/b>: Learn how to use Windows PowerShell to get software installation locations, and to uninstall software from remote computers.<\/p>\n<p><span><span><span><span><span><span><span><span><img decoding=\"async\" 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\" \/><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span>Hey, Scripting Guy! We have a dumb application that we have to use at work. The company has released a new version of this application, and I am trying to write a Windows PowerShell script to uninstall the old application&mdash;the problem is that I need to find the application first. I tried looking in the registry, but the install key is empty&hellip;figures. Like I said, this is a really dumb application. I read the <a href=\"http:\/\/blogs.technet.com\/b\/heyscriptingguy\/archive\/2011\/11\/13\/use-powershell-to-quickly-find-installed-software.aspx\" target=\"_blank\">guest blog written by Marc Carter<\/a> about problems using the <b>Win32_Product <\/b>WMI class, but it looks like I am going to be stuck using this anyway. The problem is that it is really slow. Is there any way to speed this thing up? I have to query over a thousand computers, and in our testing, this query takes nearly five minutes to complete&mdash;that would be three and a half days for only one query.<\/p>\n<p>&mdash;BT<\/p>\n<p><span><span><span><span><span><span><span><span><img decoding=\"async\" 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\" \/><\/span><\/span><\/span><\/span><\/span><\/span><\/span><\/span>&nbsp;Hello BT,<\/p>\n<p>Microsoft Scripting Guy, Ed Wilson, is here. The Scripting Wife and I were in Texas for the Corpus Christi Windows PowerShell User Group meeting when Marc Carter told me about the problem with the MSI installer reconfiguring applications when the <b>Win32_Product <\/b>WMI class is queried. I immediately encouraged him to write a guest blog about this issue.<\/p>\n<p>BT, there is a way to use the <b>Win32_Product <\/b>WMI class in a more efficient manner. It relies on using the [WMI] type accelerator, instead of doing a generic WMI query. The problem is that the [WMI] type accelerator returns a specific instance of a WMI class. To connect to a specific instance, I must use the <b>Key<\/b> property of a WMI class.<\/p>\n<p>I can use the <b>Get-WMIKey<\/b> function from my <a href=\"http:\/\/gallery.technet.microsoft.com\/scriptcenter\/WMI-Helper-Module-for-90e4f22e\" target=\"_blank\">HSGWMImoduleV6 module<\/a>. In the following code, I first import my HSGWMImoduleV6 module, and then I use the <b>Get-WMIKey<\/b> function to return the key to the <b>Win32_Product <\/b>WMI class. The commands and the output from the commands are shown here.<\/p>\n<p style=\"padding-left: 30px\">PS C:\\&gt; Import-Module hsg*6<\/p>\n<p style=\"padding-left: 30px\">PS C:\\&gt; Get-WmiKey win32_product<\/p>\n<p style=\"padding-left: 30px\">IdentifyingNumber<\/p>\n<p style=\"padding-left: 30px\">Name<\/p>\n<p style=\"padding-left: 30px\">Version<\/p>\n<p>The <b>Key<\/b> property for <b>Win32_Product<\/b> is a composite key comprised of <b>IdentifyingNumber<\/b>, <b>Name<\/b>, and <b>Version<\/b>. The easy way to get this information is to use the <b>Get-WmiObject<\/b> cmdlet to query for the information. I only need to do this once, and I will have the three pieces of information. A table is a nice way to display the information. In the code shown here, I use the <b>Get-WmiObject<\/b> cmdlet (<b>gwmi<\/b> is an alias) to return product information, and then I pipe the management objects to the <b>Format-Table<\/b> (<b>ft<\/b> is an alias) cmdlet for display.<\/p>\n<p>gwmi win32_product | ft name, version, ident*<\/p>\n<p>In the image that follows, I import the HSGWMIModuleV6 module, use the <b>Get-WMIKey<\/b> function to retrieve the <b>Key<\/b> property of the <b>Win32_Product <\/b>WMI class. I then use the <b>Get-WmiObject<\/b> cmdlet (<b>gwmi<\/b> is an alias) to query the <b>Win32_Product<\/b> WMI class, and I output the management objects to a table via the <b>Format-Table<\/b> (<b>ft<\/b> is alias) cmdlet. The following image displays the commands and the output from the commands.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/0160.HSG-12-14-11-01.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/0160.HSG-12-14-11-01.png\" alt=\"Image of command output\" title=\"Image of command output\" \/><\/a><\/p>\n<p>BT, you may ask, &ldquo;What about Marc Carter&rsquo;s warning about using the <b>Win32_Product <\/b>WMI class? &ldquo; Well as seen in the results from querying the event log, it is a concern. Shortly after querying the <b>Win32_Product <\/b>WMI<b> <\/b>class, I used the <b>Get-EventLog<\/b> cmdlet to query the application log for MSIInstaller events. The following image shows massive product reconfiguring going on.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/8446.HSG-12-14-11-02.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/8446.HSG-12-14-11-02.png\" alt=\"Image of command output\" title=\"Image of command output\" \/><\/a><\/p>\n<p>The query to return the three parts of the composite key only needs to run once; the values do not change. It is also possible to use the <b>Get-WmiObject<\/b> cmdlet and a <i>filter<\/i> to improve the performance of the command a little bit. The nice thing about this command is that it returns the information that is required by the [WMI] type accelerator. The command and associated output are shown here.<\/p>\n<p style=\"padding-left: 30px\">PS C:\\&gt; gwmi win32_product -filter &#8220;Name LIKE &#8216;%Silverlight%'&#8221;<\/p>\n<p style=\"padding-left: 30px\">IdentifyingNumber : {89F4137D-6C26-4A84-BDB8-2E5A4BB71E00}<\/p>\n<p style=\"padding-left: 30px\">Name&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : Microsoft Silverlight<\/p>\n<p style=\"padding-left: 30px\">Vendor&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : Microsoft Corporation<\/p>\n<p style=\"padding-left: 30px\">Version&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 4.0.60831.0<\/p>\n<p style=\"padding-left: 30px\">Caption&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : Microsoft Silverlight<\/p>\n<p>When I have the three pieces of information (the <b>IdentifyingNumber<\/b>, the <b>Name<\/b>, and the <b>Version<\/b>), it is time to create the key. This is where quite a bit of experimentation could be required. I have to use the back tick (grave) character to escape inside quotation marks. I have to escape the quotation mark and the opening curly bracket for the <b>IdentifyingNumber <\/b>property. I also have to escape the closing curly bracket and the closing quotation mark. I then have to escape the quotation marks that surround Microsoft Silverlight, in addition to the quotation marks for the <b>Version<\/b> property. There are also two quotation marks at the end of the <b>ClassKey<\/b>.<\/p>\n<p>Here is the key I derived for Microsoft Silverlight on my computer. (This is a single line command. A space between Microsoft and Silverlight exists, but other than that, there are no spaces).<\/p>\n<p style=\"padding-left: 30px\">$classKey=&#8221;IdentifyingNumber=`&#8221;`{89F4137D-6C26-4A84-BDB8-2E5A4BB71E00`}`&#8221;,Name=`&#8221;Microsoft Silverlight`&#8221;,version=`&#8221;4.0.60831.0`&#8221;&#8221;<\/p>\n<p>The reason for all the escaping in the <b>ClassKey<\/b>, is that WMI expects the quotation marks and the curly brackets in the key itself. To see what WMI expects to receive via the command, I use the Windows Management Instrumentation Tester (<b>WbemTest<\/b>) command, and I view the instances of the class. The following image illustrates the instances of <b>Win32_Product<\/b> on my computer.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/0246.HSG-12-14-11-03.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/0246.HSG-12-14-11-03.png\" alt=\"Image of query results\" title=\"Image of query results\" \/><\/a><\/p>\n<p>When I have the <b>ClassKey<\/b>, I can use the [WMI] type accelerator to connect to the specific software package (Microsoft Silverlight in this example). In fact, using the [WMI] type accelerator is very easy. Here is the command. (The command is [WMI], the class name, and the key).<\/p>\n<p style=\"padding-left: 30px\">[wmi]&#8221;Win32_Product.$classkey&#8221;<\/p>\n<p>I can also include the WMI namespace (really important if the class resides in a namespace other than the default root\\cimv2). In the command that follows, notice that there is a backslash that precedes the word <i>root. <\/i>One other thing to notice is that a colon separates the WMI namespace and the WMI class name.<\/p>\n<p style=\"padding-left: 30px\">[wmi]&#8221;\\root\\cimv2:Win32_Product.$classkey&#8221;<\/p>\n<p>If I need to connect to a WMI class on a remote computer, I use a double backslash and the name of the computer, then the WMI namespace, the WMI class, and the WMI <b>ClassKey<\/b>. The command that follows illustrates this.<\/p>\n<p style=\"padding-left: 30px\">[wmi]\\\\remotehost\\root\\cimv2:Win32_Product.$classkey<\/p>\n<p>In the image that follows, I illustrate the different ways of querying WMI for Microsoft Silverlight software. I then compare the speed of using the <b>Get-WmiObject<\/b> cmdlet against the speed of using the [WMI] type accelerator. As shown in the following image, the <b>Get-WmiObject<\/b> cmdlet, using the <i>filter <\/i>to find Microsoft Silverlight, takes over five seconds on my laptop. Using the [WMI] type accelerator takes less than one-half of a second. This is more than 10 times faster. &nbsp;<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/4578.HSG-12-14-11-04.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/4578.HSG-12-14-11-04.png\" alt=\"Image of results\" title=\"Image of results\" \/><\/a><\/p>\n<p>By the way, there was not much difference between using the <i>filter <\/i>to look for Microsoft Silverlight or using the <b>Where-Object<\/b>. In the following output, I use the <b>Measure-Object<\/b> cmdlet to determine the performance of using the <b>Where-Object<\/b> (the <b>?<\/b> is an alias for <b>Where-Object<\/b>).<\/p>\n<p style=\"padding-left: 30px\">PS C:\\&gt; measure-command {gwmi win32_product | ? {$_.name -match &#8216;silverlight&#8217;}}<\/p>\n<p style=\"padding-left: 30px\">Days&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 0<\/p>\n<p style=\"padding-left: 30px\">Hours&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 0<\/p>\n<p style=\"padding-left: 30px\">Minutes&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 0<\/p>\n<p style=\"padding-left: 30px\">Seconds&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 5<\/p>\n<p style=\"padding-left: 30px\">Milliseconds&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 311<\/p>\n<p style=\"padding-left: 30px\">Ticks&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 53112518<\/p>\n<p style=\"padding-left: 30px\">TotalDays&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 6.14728217592593E-05<\/p>\n<p style=\"padding-left: 30px\">TotalHours&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 0.00147534772222222<\/p>\n<p style=\"padding-left: 30px\">TotalMinutes&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 0.0885208633333333<\/p>\n<p style=\"padding-left: 30px\">TotalSeconds&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 5.3112518<\/p>\n<p style=\"padding-left: 30px\">TotalMilliseconds : 5311.2518<\/p>\n<p>If you suspect that the problem with the <i>filter <\/i>is that I used the <b>like<\/b> operator as opposed to the <b>equality<\/b> operator, that is not the case. Here are the results from using the <b>equality<\/b> operator.<\/p>\n<p style=\"padding-left: 30px\">PS C:\\&gt; measure-command {gwmi win32_product | ? {$_.name -match &#8216;silverlight&#8217;}}<\/p>\n<p style=\"padding-left: 30px\">Days&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 0<\/p>\n<p style=\"padding-left: 30px\">Hours&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 0<\/p>\n<p style=\"padding-left: 30px\">Minutes&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 0<\/p>\n<p style=\"padding-left: 30px\">Seconds&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 5<\/p>\n<p style=\"padding-left: 30px\">Milliseconds&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 311<\/p>\n<p style=\"padding-left: 30px\">Ticks&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 53112518<\/p>\n<p style=\"padding-left: 30px\">TotalDays&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 6.14728217592593E-05<\/p>\n<p style=\"padding-left: 30px\">TotalHours&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 0.00147534772222222<\/p>\n<p style=\"padding-left: 30px\">TotalMinutes&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : 0.0885208633333333<\/p>\n<p style=\"padding-left: 30px\">TotalSeconds&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;: 5.3112518<\/p>\n<p style=\"padding-left: 30px\">TotalMilliseconds : 5311.2518<\/p>\n<p>When using the [WMI] type accelerator, a complete instance of the WMI class instance returns. The properties and their associated values are shown in the following image. Notice two properties: the <b>__Path<\/b> (that is, double underscore <b>Path<\/b>) property is the key to the WMI class instance. The <b>InstallLocation<\/b><i> <\/i>property points to the location where the software installs.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/3343.HSG-12-14-11-05.png\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/3343.HSG-12-14-11-05.png\" alt=\"Image of results\" title=\"Image of results\" \/><\/a><\/p>\n<p>BT, you did not ask, but there is an <b>Uninstall<\/b><i> <\/i>method available from the <b>Win32_Product<\/b> WMI class. It appears only on instances of the class. Therefore, it is possible to uninstall software by using the command that is shown here. (If I want to uninstall from a large collection of servers, I use the <i>foreach <\/i>statement ($servers is an array of server names).<\/p>\n<p style=\"padding-left: 30px\">foreach($server in $servers)<\/p>\n<p style=\"padding-left: 30px\">{ ([wmi]&#8221;\\\\$server\\root\\cimv2:Win32_Product.$classKey&#8221;).uninstall() }<\/p>\n<p>BT, that is all there is to using the <b>Win32_Product<\/b> WMI class to detect or to uninstall software. Join me tomorrow when I will have a guest blog written by Raymond Mitchel as he talks about Windows PowerShell and SharePoint.<\/p>\n<p>I invite you to follow me on <a href=\"http:\/\/bit.ly\/scriptingguystwitter\" target=\"_blank\">Twitter<\/a> and <a href=\"http:\/\/bit.ly\/scriptingguysfacebook\" target=\"_blank\">Facebook<\/a>. If you have any questions, send email to me at <a href=\"mailto:scripter@microsoft.com\" target=\"_blank\">scripter@microsoft.com<\/a>, or post your questions on the <a href=\"http:\/\/bit.ly\/scriptingforum\" target=\"_blank\">Official Scripting Guys Forum<\/a>. See you tomorrow. Until then, peace.<\/p>\n<p><b>Ed Wilson, Microsoft Scripting Guy<\/b><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: Learn how to use Windows PowerShell to get software installation locations, and to uninstall software from remote computers. Hey, Scripting Guy! We have a dumb application that we have to use at work. The company has released a new version of this application, and I am trying to write a Windows PowerShell script to [&hellip;]<\/p>\n","protected":false},"author":596,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[237,16,3,4,45,6],"class_list":["post-11791","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-basic-computer-information","tag-desktop-management","tag-scripting-guy","tag-scripting-techniques","tag-windows-powershell","tag-wmi"],"acf":[],"blog_post_summary":"<p>Summary: Learn how to use Windows PowerShell to get software installation locations, and to uninstall software from remote computers. Hey, Scripting Guy! We have a dumb application that we have to use at work. The company has released a new version of this application, and I am trying to write a Windows PowerShell script to [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/11791","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\/596"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=11791"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/11791\/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=11791"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=11791"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=11791"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}