3

i want to have an overview of any software which is installed on a Windows system (or at least all software that is registering itself in Windows). With Powershell i am able to extract the data into a gridview and after filtering there into a csv file. For that i am using the following code:

### Extract x64 registered programs. Excluding KB updates

$data = dir HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall |
   where {$_.name -notmatch '(\.)?KB\d+'}  -pv p |
   Get-ItemProperty |
     Where {$_.displayname -notmatch "KB\d{5,}"} |
   Select @{Name="Path";Expression={$p.name}},Displayname,DisplayVersion,InstallDate,UninstallString
;

### Extract and append to $data the x86 registered programs. Excluding KB updates

$data += dir HKLM:\SOFTWARE\Wow6432Node\Micsosoft\Windows\CurrentVersion\Uninstall |
   where {$_.name -notmatch '(\.)?KB\d+'}  -pv p |
   Get-ItemProperty |
     Where {$_.displayname -notmatch "KB\d{5,}"} |
   Select @{Name="Path";Expression={$p.name}},DisplayName,DisplayVersion,InstallDate,UninstallString
;

### Acces $data and output to a gridview and further to csv.

$data |
   select-Object DisplayName,DisplayVersion,InstallDate,UninstallString |
   sort-object -Property DisplayName |
   out-gridview -PassTHru |
   export-csv -delimiter "," -Path C:\temp\software.csv

An example output looks like this:

DisplayName                         DisplayVersion  InstallDate UninstallString    
7-Zip 9.20 (x64 edition)            9.20.00.0       20190827    MsiExec.exe /I{23170F69-40C1-2702-0920-000001000000}
QGIS 3.4.13 'Madeira'               03.04.2013                  C:\Program Files\QGIS 3.4\uninstall.exe
Microsoft Office Access ...         14.0.7015.      20190827    MsiExec.exe /X{90140000-0015-0407-0000-0000000FF1CE}
Realtek Card Reader 10.0.10125....  20190827                    "C:\Program Files (x86)\InstallShield Installation Information\{5BC2B5AB-80DE-4E83-B8CF-426902051D0A}\setup.exe" -runfromtemp  -removeonly

For further processing i want to append a column where the GUID - if it is existing - is extracted from the UninstallString. Example:

DisplayName                         DisplayVersion  InstallDate GUID                                  UninstallString
7-Zip 9.20 (x64 edition)            9.20.00.0       20190827    23170F69-40C1-2702-0920-000001000000  MsiExec.exe /I{23170F69-40C1-2702-0920-000001000000}
QGIS 3.4.13 'Madeira'               03.04.2013                                                        C:\Program Files\QGIS 3.4\uninstall.exe
Microsoft Office Access ...         14.0.7015.      20190827    90140000-0015-0407-0000-0000000FF1CE  MsiExec.exe /X{90140000-0015-0407-0000-0000000FF1CE}
Realtek Card Reader 10.0.10125....  20190827                    5BC2B5AB-80DE-4E83-B8CF-426902051D0A  "C:\Program Files (x86)\InstallShield Installation Information\{5BC2B5AB-80DE-4E83-B8CF-426902051D0A}\setup.exe" -runfromtemp  -removeonly

I know that i have to do it somehow with a RegEx code but i am not able to proceed further. May someone is able to help me out? Thank you

Sascha
  • 45
  • 7

2 Answers2

2

You can extract the GUID from the UninstallString using a regex -match and add that as a calculated property to the Select-Object:

$reGuid = '\{?(([0-9a-f]){8}-([0-9a-f]){4}-([0-9a-f]){4}-([0-9a-f]){4}-([0-9a-f]){12})\}?'

$data | Select-Object DisplayName,DisplayVersion,InstallDate,
                      @{Name = 'GUID'; Expression = { if ($_.UninstallString -match $reGuid) {$matches[1]}} },
                      UninstallString |
        Sort-Object -Property DisplayName |
        Out-GridView -PassThru |
        Export-Csv -Delimiter "," -Path 'C:\temp\software.csv' -NoTypeInformation

Regex details:

\{                    Match the character “{” literally
   ?                  Between zero and one times, as many times as possible, giving back as needed (greedy)
(                     Match the regular expression below and capture its match into backreference number 1
   (                  Match the regular expression below and capture its match into backreference number 2
      [0-9a-f]        Match a single character present in the list below
                      A character in the range between “0” and “9”
                      A character in the range between “a” and “f”
   ){8}               Exactly 8 times
   -                  Match the character “-” literally
   (                  Match the regular expression below and capture its match into backreference number 3
      [0-9a-f]        Match a single character present in the list below
                      A character in the range between “0” and “9”
                      A character in the range between “a” and “f”
   ){4}               Exactly 4 times
   -                  Match the character “-” literally
   (                  Match the regular expression below and capture its match into backreference number 4
      [0-9a-f]        Match a single character present in the list below
                      A character in the range between “0” and “9”
                      A character in the range between “a” and “f”
   ){4}               Exactly 4 times
   -                  Match the character “-” literally
   (                  Match the regular expression below and capture its match into backreference number 5
      [0-9a-f]        Match a single character present in the list below
                      A character in the range between “0” and “9”
                      A character in the range between “a” and “f”
   ){4}               Exactly 4 times
   -                  Match the character “-” literally
   (                  Match the regular expression below and capture its match into backreference number 6
      [0-9a-f]        Match a single character present in the list below
                      A character in the range between “0” and “9”
                      A character in the range between “a” and “f”
   ){12}              Exactly 12 times
)                  
\}                    Match the character “}” literally
   ?                  Between zero and one times, as many times as possible, giving back as needed (greedy)
Theo
  • 57,719
  • 8
  • 24
  • 41
  • Dear Theo, thanks a lot for the RegEx infos! NOw i have the extracted GUIDs in a new column. But, i also have the full path, when there was no any GUID found. As an example: `DisplayName DisplayVersion InstallDate GUID UninstallString Mozilla Thunderbird 68.4.2 (x64 de) 68.4.2 C:\Program Files\Mozilla Thunderbird\uninstall\helper.exe C:\Program Files\Mozilla Thunderbird\uninstall\helper.exe ` – Sascha Feb 13 '20 at 17:24
  • @Sascha I see what you mean. I have changed the code now to use `-match`. – Theo Feb 13 '20 at 18:40
  • thanks again for help, but match is just placing a true or false into the column. I need the GUID when the registry can deliver it and, if not, just a blank entry / NA / false... I tried some operators now but i think there has to be another test in forehand. something like "if match = TRUE, then write GUID. If FALSE, then write "false") – Sascha Feb 13 '20 at 22:06
  • thanks again, i tried your answer before you edited them and i missed the "new" `{$matches[1]}}`in the end. Now everything is perfect as i need it ;) – Sascha Feb 14 '20 at 08:05
0

You might prefer using get-package:

get-package | select -first 5

Name                           Version          Source                           ProviderName
----                           -------          ------                           ------------
Windows Driver Package - Ci... 02/14/2014 6....                                  Programs
Wolfram Extras 11.0 (5570611)  11.0.0                                            Programs
ArcGIS Desktop Background G... 10.5.6491                                         Programs
ArcGIS Desktop Background G... 10.5.6491        C:\Program Files (x86)\ArcGIS... msi
Conexant HD Audio              8.65.186.3                                        Programs
get-package | % { $_.metadata['uninstallstring'] }

By the way, if you have Netbeans installed, it will crash get-itemproperty.

js2010
  • 23,033
  • 6
  • 64
  • 66