# Checking for only one or many installed software using GUID

**URL:** <https://forums.powershell.org/t/checking-for-only-one-or-many-installed-software-using-guid/18611>\
**Category:** PowerShell Help\
**Created:** [February 17, 2022, 6:56pm UTC](https://forums.powershell.org/t/checking-for-only-one-or-many-installed-software-using-guid/18611 "2022-02-17T18:56:51Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![Thudo](https://avatars.discourse-cdn.com/v4/letter/t/a587f6/32.png) [@Thudo](https://forums.powershell.org/u/Thudo)\
**Post date:** [February 17, 2022, 6:56pm UTC](https://forums.powershell.org/t/checking-for-only-one-or-many-installed-software-using-guid/18611/1 "2022-02-17T18:56:51Z")

</div>

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…

```auto
    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.

```auto
        #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.

---

<div class="post-metadata">

**Author:** ![Olaf](https://sea1.discourse-cdn.com/flex019/user_avatar/forums.powershell.org/olaf/32/992_2.png) [@Olaf](https://forums.powershell.org/u/Olaf)\
**Post date:** [February 17, 2022, 7:02pm UTC](https://forums.powershell.org/t/checking-for-only-one-or-many-installed-software-using-guid/18611/2 "2022-02-17T19:02:14Z")

</div>

> [@Thudo](#):
>
> 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…

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`.

---

<div class="post-metadata">

**Author:** ![Thudo](https://avatars.discourse-cdn.com/v4/letter/t/a587f6/32.png) [@Thudo](https://forums.powershell.org/u/Thudo)\
**Post date:** [February 17, 2022, 7:05pm UTC](https://forums.powershell.org/t/checking-for-only-one-or-many-installed-software-using-guid/18611/3 "2022-02-17T19:05:57Z")

</div>

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

```auto
        $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. ☹

---

<div class="post-metadata">

**Author:** ![Olaf](https://sea1.discourse-cdn.com/flex019/user_avatar/forums.powershell.org/olaf/32/992_2.png) [@Olaf](https://forums.powershell.org/u/Olaf)\
**Post date:** [February 17, 2022, 7:08pm UTC](https://forums.powershell.org/t/checking-for-only-one-or-many-installed-software-using-guid/18611/4 "2022-02-17T19:08:50Z")

</div>

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?

---

<div class="post-metadata">

**Author:** ![Olaf](https://sea1.discourse-cdn.com/flex019/user_avatar/forums.powershell.org/olaf/32/992_2.png) [@Olaf](https://forums.powershell.org/u/Olaf)\
**Post date:** [February 17, 2022, 7:10pm UTC](https://forums.powershell.org/t/checking-for-only-one-or-many-installed-software-using-guid/18611/5 "2022-02-17T19:10:23Z")

</div>

> [@Thudo](#):
>
> it only supports one at a time.

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

---

<div class="post-metadata">

**Author:** ![Thudo](https://avatars.discourse-cdn.com/v4/letter/t/a587f6/32.png) [@Thudo](https://forums.powershell.org/u/Thudo)\
**Post date:** [February 17, 2022, 7:45pm UTC](https://forums.powershell.org/t/checking-for-only-one-or-many-installed-software-using-guid/18611/6 "2022-02-17T19:45:13Z")

</div>

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:

```auto
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:

```auto
        $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.

---

<div class="post-metadata">

**Author:** ![Thudo](https://avatars.discourse-cdn.com/v4/letter/t/a587f6/32.png) [@Thudo](https://forums.powershell.org/u/Thudo)\
**Post date:** [February 18, 2022, 1:50am UTC](https://forums.powershell.org/t/checking-for-only-one-or-many-installed-software-using-guid/18611/7 "2022-02-18T01:50:30Z")

</div>

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

```auto
       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/

---

<div class="post-metadata">

**Author:** ![tonyd](https://sea1.discourse-cdn.com/flex019/user_avatar/forums.powershell.org/tonyd/32/1041_2.png) [@tonyd](https://forums.powershell.org/u/tonyd)\
**Post date:** [February 18, 2022, 2:11am UTC](https://forums.powershell.org/t/checking-for-only-one-or-many-installed-software-using-guid/18611/8 "2022-02-18T02:11:45Z")

</div>

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.

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

```

---

<div class="post-metadata">

**Author:** ![Thudo](https://avatars.discourse-cdn.com/v4/letter/t/a587f6/32.png) [@Thudo](https://forums.powershell.org/u/Thudo)\
**Post date:** [February 18, 2022, 5:12am UTC](https://forums.powershell.org/t/checking-for-only-one-or-many-installed-software-using-guid/18611/9 "2022-02-18T05:12:36Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![Olaf](https://sea1.discourse-cdn.com/flex019/user_avatar/forums.powershell.org/olaf/32/992_2.png) [@Olaf](https://forums.powershell.org/u/Olaf)\
**Post date:** [February 18, 2022, 7:29am UTC](https://forums.powershell.org/t/checking-for-only-one-or-many-installed-software-using-guid/18611/10 "2022-02-18T07:29:19Z")

</div>

> [@Thudo](#):
>
> Found another method so I keep the function spitting out the GUID array

> [@Olaf](#):
>
> … if that’s the issue you have you should use a loop to provide only one GUID at a time to your command

If you would read my answers you life could be easier 🙄

---

<div class="post-metadata">

**Author:** ![tonyd](https://sea1.discourse-cdn.com/flex019/user_avatar/forums.powershell.org/tonyd/32/1041_2.png) [@tonyd](https://forums.powershell.org/u/tonyd)\
**Post date:** [February 18, 2022, 2:04pm UTC](https://forums.powershell.org/t/checking-for-only-one-or-many-installed-software-using-guid/18611/11 "2022-02-18T14:04:05Z")

</div>

Ooops … sorry for missing that.

---

<div class="post-metadata">

**Author:** ![Thudo](https://avatars.discourse-cdn.com/v4/letter/t/a587f6/32.png) [@Thudo](https://forums.powershell.org/u/Thudo)\
**Post date:** [February 18, 2022, 3:00pm UTC](https://forums.powershell.org/t/checking-for-only-one-or-many-installed-software-using-guid/18611/12 "2022-02-18T15:00:35Z")

</div>

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

---

<div class="post-metadata">

**Author:** ![dotnVo](https://avatars.discourse-cdn.com/v4/letter/d/4af34b/32.png) [@dotnVo](https://forums.powershell.org/u/dotnVo)\
**Post date:** [May 16, 2024, 8:19pm UTC](https://forums.powershell.org/t/checking-for-only-one-or-many-installed-software-using-guid/18611/13 "2024-05-16T20:19:32Z")

</div>


