Checking for only one or many installed software using GUID

Afternoon everyone…

I found code to check a local system for the presence of a specific software using this powershell script and listing them in an array.

My problem is we use a custom MSI install/uninstaller in powershell which does not support arrays so if more than ONE GUID is found it borks the code so I have two options:

  1. See below the function… if I can code this “Get-InstalledSoftware” function only to detect the first instance of the GUID and not multiple that would allow me to then re-scan the machine for other instances later on in my code. Right now if it dumps an array of GUIDs causing my uninstaller to break so if the following function can be set to only detect one GUID that would help…
    function Get-InstalledSoftware {
    <#
    .SYNOPSIS
        Retrieves a list of all software installed
    .EXAMPLE
        Get-InstalledSoftware
        
        This example retrieves all software installed on the local computer
    .PARAMETER Name
        The software title you'd like to limit the query to.
    #>
    [OutputType([System.Management.Automation.PSObject])]
    [CmdletBinding()]
    param (
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string]$Name
    )
    $UninstallKeys = "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall", "HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
    $null = New-PSDrive -Name HKU -PSProvider Registry -Root Registry::HKEY_USERS
    $UninstallKeys += Get-ChildItem HKU: -ErrorAction SilentlyContinue | Where-Object { $_.Name -match 'S-\d-\d+-(\d+-){1,14}\d+$' } | ForEach-Object { "HKU:\$($_.PSChildName)\Software\Microsoft\Windows\CurrentVersion\Uninstall" }
    if (-not $UninstallKeys) {
        Write-Verbose -Message 'No software registry keys found'
    } else {
        foreach ($UninstallKey in $UninstallKeys) {
            if ($PSBoundParameters.ContainsKey('Name')) {
                $WhereBlock = { ($_.PSChildName -match '^{[A-Z0-9]{8}-([A-Z0-9]{4}-){3}[A-Z0-9]{12}}$') -and ($_.GetValue('DisplayName') -like "$Name*") }
            } else {
                $WhereBlock = { ($_.PSChildName -match '^{[A-Z0-9]{8}-([A-Z0-9]{4}-){3}[A-Z0-9]{12}}$') -and ($_.GetValue('DisplayName')) }
            }
            $gciParams = @{
                Path        = $UninstallKey
                ErrorAction = 'SilentlyContinue'
            }
            $selectProperties = @(
                @{n='GUID'; e={$_.PSChildName}}, 
                @{n='Name'; e={$_.GetValue('DisplayName')}},
                @{n='Version'; e={$_.GetValue('DisplayVersion')}}
            )
            Get-ChildItem @gciParams | Where $WhereBlock | Select-Object -Property $selectProperties
            }
        }
    }
  1. If not the above then I need to be able to set the command below “Execute-MSI -Action ‘Uninstall’ -Path $GUID” so that it supports an array as when when it finds $GUID its actually many GUIDs and, thus, breaks the MSI uninstall command. Is there an array one can use to support multiple Execute-MSIs? Here is the full code which calls from the function above.
        #Declare specific values for first scan.
        $GUIDObject = Get-InstalledSoftware "*Adobe Photoshop Elements*"
        $GUID = $GUIDObject.GUID
        Execute-MSI -Action 'Uninstall' -Path $GUID

Execute-MSI is a basic PS call using simple msiexec /i or /x and auto-pipes itself to a log file.

Thank you community!

e.

Without digging too deep into the code … why don’t you use PowerSehll to filter out doublets of GUIDs? There is Select-Object -Unique or Sort-Object -Unique or Group-Object or for PowerShell 7 there is even Get-Unique.

Thanks Olaf for the super fast response. Where would that command be laid down as right now once this runs off the above function…

        $GUIDObject = Get-InstalledSoftware "*Adobe Photoshop Elements*"

it dumps many GUIDs here: $GUID = $GUIDObject.GUID which then borks my Execute-MSI command as its not expecting that many GUIDs - it only supports one at a time. :frowning:

Sorry if that sounds rude but have you at least tried to read some of the help topics about these commands and tried to use them to figure out how they work?

… if that’s the issue you have you should use a loop to provide only one GUID at a time to your command

Thank you Olaf… sorry a bit new to this PS logic…

Think I may have nailed it for #1 so in the function the last line now looks as follows:

Get-ChildItem @gciParams | Where $WhereBlock | Select-Object -Property $selectProperties | Sort-Object -Unique

What this does is limit the function’s dump from many to the last one found so $GUID contains only one so my command goes through THEN, when I re-check the variables again down the command line with as follows:

        $GUIDObjectRecheck = Get-InstalledSoftware "*Adobe Photoshop Elements*"
        $GUIDRecheck = $GUIDObjectRecheck.GUID
        Execute-MSI -Action 'Uninstall' -Path $GUIDRecheck

It runs a re-scan of the function and I can simply grab each one individually as the others get removed with each re-check.

Found another method so I keep the function spitting out the GUID array… this works fantastic now. Clean and neat.

       foreach($GUID in $GUIDObject.GUID){
            Execute-MSI -Action 'Uninstall' -Path $GUID
        }

This now takes the original GUID array and sorts it individually and goes through all the array until done. If no array exists, it skips. \o/

Not trying to muddy the waters, but you may also want to include the 32bit reg hive to be a bit more thorough. What I do is gather both of them, get rid of the duplicates and process from there.

'SOFTWARE\\Wow6432Node\Microsoft\\Windows\\CurrentVersion\\Uninstall'

I believe its already in the function but hidden off the right side. :wink:

If you would read my answers you life could be easier :roll_eyes:

Ooops … sorry for missing that.

All fabulous, Olaf! Glad it was a fairly easy script fix. :slight_smile: Very much appreciate your time!