steele
steele

Reputation: 21

Powershell Script to output Installed Programs

I'd like to do this using the win32_product wmi class.

I need the script to simply count the number of products installed and also output the time taken to execute the script.

What I have atm doesn't seem to work correctly:

    $count = 0
    $products = get-wmiobject -class "Win32_Product"
    foreach ($product in $products) {
          if ($product.InstallState -eq 5) {
                count++
          }
    }
    write-host count

Upvotes: 2

Views: 2727

Answers (3)

Michael Sorens
Michael Sorens

Reputation: 36698

Beware! Using WMI's Win32_Product class, which the question and the previous 2 answers do, is not advised for this purpose.

In a nutshell: using Win32_Product is not an innocuous query because it has side effects. Quoting Microsoft, "[It]... initiates a consistency check of packages installed, verifying and repairing the install." (emphasis mine)

References:


So what is a better (safer) solution?

Marc Carter, writing the guest column in the Hey, Scripting Guy! blog above takes the first volley, providing a custom PowerShell function, back on my system it returned only half as many entries as the Win32_Product invocation. Also, it is a lot of code (3 dozen lines or so). In the comments to his post, however, knutkj offers this much shorter version that does the same thing:

Get-ChildItem -Path HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall |
   Get-ItemProperty |
   Sort-Object -Property DisplayName |
   Select-Object -Property DisplayName, DisplayVersion, InstallLocation

But it does, as I said, the same thing: not provide a full list. But it is a start.

Later in the comments Nick W reported that there are actually 3 registry paths of interest, though not all may be present on every system. Further, when looking at those 3 paths, one has to do some additional filtering.

Combining both of those, adding in a few more output fields, and making the code safe to run in strict mode, I arrived at this simple solution:

function Get-InstalledPrograms()
{
    $regLocations = (
        "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\", 
        "HKCU:\Software\Microsoft\Windows\CurrentVersion\Uninstall\",
        "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\"
        )

    Get-ChildItem ($regLocations | Where { Test-Path $_ } ) |
        Get-ItemProperty |
        Where {
            ( (Get-Member -InputObject $_ -Name DisplayName) -and $_.DisplayName -ne $Null) -and
            (!(Get-Member -InputObject $_ -Name SystemComponent) -or $_.SystemComponent -ne "1") -and 
            (!(Get-Member -InputObject $_ -Name ParentKeyName) -or $_.ParentKeyName -eq $Null)
            } |
        Sort DisplayName |
        Select DisplayName, DisplayVersion, Publisher, InstallLocation, InstallDate,  URLInfoAbout
}

Upvotes: 4

stej
stej

Reputation: 29449

Roman Kuzmin is right about the typo. Correction will solve almost everything.

To make it more powershellish, I would use

get-wmiobject -class "Win32_Product" | 
    ? { $_.InstallState -eq 5 } |
    measure-object  | 
    select -exp Count

And considering the time, you can wrap it into measure-command

measure-command { 
  $count = get-wmiobject -class "Win32_Product" | 
              ? { $_.InstallState -eq 5 } | 
              measure-object  |
              select -exp Count
  write-host Count: $count
}

Upvotes: 2

Marlon
Marlon

Reputation: 199

A bit late on this but the "more powershellish way":

$(Get-WmiObject -Class "Win32_Product" -Filter "InstallState=5").Count

Upvotes: 2

Related Questions