This Powershell script list all the installed application on both 32 and 64 bit applications, particularly useful for people managing both 32-bit and 64-bit applications. This code also contains an exclusion array where you can exclude list of program that you don’t want to show.
- The function allows -ComputerName parameter so you can connect to any machines
- Application architecture detection (32-bit or 64-bit) using Win32_processor
- Output is Powershell console and GridView
Function call:- The computer name is mandatory to retrive the software list.
Get-InstalledApplication -Computername <Computername>
Code:
Function Get-InstalledApplication { Param( [Parameter(Mandatory=$true)] [string[]]$Computername) #Registry Hives $Object =@() $excludeArray = ("Security Update for Windows", "Update for Windows", "Update for Microsoft .NET", "Security Update for Microsoft", "Hotfix for Windows", "Hotfix for Microsoft .NET Framework", "Hotfix for Microsoft Visual Studio 2007 Tools", "Hotfix") [long]$HIVE_HKROOT = 2147483648 [long]$HIVE_HKCU = 2147483649 [long]$HIVE_HKLM = 2147483650 [long]$HIVE_HKU = 2147483651 [long]$HIVE_HKCC = 2147483653 [long]$HIVE_HKDD = 2147483654 Foreach($EachServer in $Computername){ $Query = Get-WmiObject -ComputerName $Computername -query "Select AddressWidth, DataWidth,Architecture from Win32_Processor" foreach ($i in $Query) { If($i.AddressWidth -eq 64){ $OSArch='64-bit' } Else{ $OSArch='32-bit' } } Switch ($OSArch) { "64-bit"{ $RegProv = GWMI -Namespace "root\Default" -list -computername $EachServer| where{$_.Name -eq "StdRegProv"} $Hive = $HIVE_HKLM $RegKey_64BitApps_64BitOS = "Software\Microsoft\Windows\CurrentVersion\Uninstall" $RegKey_32BitApps_64BitOS = "Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall" $RegKey_32BitApps_32BitOS = "Software\Microsoft\Windows\CurrentVersion\Uninstall" ############################################################################# # Get SubKey names $SubKeys = $RegProv.EnumKey($HIVE, $RegKey_64BitApps_64BitOS) # Make Sure No Error when Reading Registry if ($SubKeys.ReturnValue -eq 0) { # Loop Trhough All Returned SubKEys ForEach ($Name in $SubKeys.sNames) { $SubKey = "$RegKey_64BitApps_64BitOS\$Name" $ValueName = "DisplayName" $ValuesReturned = $RegProv.GetStringValue($Hive, $SubKey, $ValueName) $AppName = $ValuesReturned.sValue $Version = ($RegProv.GetStringValue($Hive, $SubKey, "DisplayVersion")).sValue $Publisher = ($RegProv.GetStringValue($Hive, $SubKey, "Publisher")).sValue $donotwrite = $false if($AppName.length -gt "0"){ Foreach($exclude in $excludeArray) { if($AppName.StartsWith($exclude) -eq $TRUE) { $donotwrite = $true break } } if ($donotwrite -eq $false) { $Object += New-Object PSObject -Property @{ Appication = $AppName; Architecture = "64-BIT"; ServerName = $EachServer; Version = $Version; Publisher= $Publisher; } } } }} ############################################################################# $SubKeys = $RegProv.EnumKey($HIVE, $RegKey_32BitApps_64BitOS) # Make Sure No Error when Reading Registry if ($SubKeys.ReturnValue -eq 0) { # Loop Through All Returned SubKEys ForEach ($Name in $SubKeys.sNames) { $SubKey = "$RegKey_32BitApps_64BitOS\$Name" $ValueName = "DisplayName" $ValuesReturned = $RegProv.GetStringValue($Hive, $SubKey, $ValueName) $AppName = $ValuesReturned.sValue $Version = ($RegProv.GetStringValue($Hive, $SubKey, "DisplayVersion")).sValue $Publisher = ($RegProv.GetStringValue($Hive, $SubKey, "Publisher")).sValue $donotwrite = $false if($AppName.length -gt "0"){ Foreach($exclude in $excludeArray) { if($AppName.StartsWith($exclude) -eq $TRUE) { $donotwrite = $true break } } if ($donotwrite -eq $false) { $Object += New-Object PSObject -Property @{ Appication = $AppName; Architecture = "32-BIT"; ServerName = $EachServer; Version = $Version; Publisher= $Publisher; } } } } } } #End of 64 Bit ###################################################################################### ########################################################################################### "32-bit"{ $RegProv = GWMI -Namespace "root\Default" -list -computername $EachServer| where{$_.Name -eq "StdRegProv"} $Hive = $HIVE_HKLM $RegKey_32BitApps_32BitOS = "Software\Microsoft\Windows\CurrentVersion\Uninstall" ############################################################################# # Get SubKey names $SubKeys = $RegProv.EnumKey($HIVE, $RegKey_32BitApps_32BitOS) # Make Sure No Error when Reading Registry if ($SubKeys.ReturnValue -eq 0) { # Loop Through All Returned SubKEys ForEach ($Name in $SubKeys.sNames) { $SubKey = "$RegKey_32BitApps_32BitOS\$Name" $ValueName = "DisplayName" $ValuesReturned = $RegProv.GetStringValue($Hive, $SubKey, $ValueName) $AppName = $ValuesReturned.sValue $Version = ($RegProv.GetStringValue($Hive, $SubKey, "DisplayVersion")).sValue $Publisher = ($RegProv.GetStringValue($Hive, $SubKey, "Publisher")).sValue if($AppName.length -gt "0"){ $Object += New-Object PSObject -Property @{ Appication = $AppName; Architecture = "32-BIT"; ServerName = $EachServer; Version = $Version; Publisher= $Publisher; } } }} }#End of 32 bit } # End of Switch } #$AppsReport $column1 = @{expression="ServerName"; width=15; label="Name"; alignment="left"} $column2 = @{expression="Architecture"; width=10; label="32/64 Bit"; alignment="left"} $column3 = @{expression="Appication"; width=80; label="Appication"; alignment="left"} $column4 = @{expression="Version"; width=15; label="Version"; alignment="left"} $column5 = @{expression="Publisher"; width=30; label="Publisher"; alignment="left"} "#"*80 "Installed Software Application Report" "Numner of Installed Application count : $($object.count)" "Generated $(get-date)" "Generated from $(gc env:computername)" "#"*80 $object |Format-Table $column1, $column2, $column3 ,$column4, $column5 $object|Out-GridView }
Output:-
Happy Learning
This is exactly what I’m looking for, thank you so much! I can’t believe no one has thought to do this until now. How difficult would it be to read multiple computer names from a text file and compile into a single list?
LikeLike
Thanks for reading my space.
The request will not much time. How do you want to have the output? Text file?
-Prashanth
LikeLike
It was simple enough to use get-childitem for a text file and call the script for each individual server. Oh one small item, I did notice that when scanning 32-bit machines, the script doesn’t check against the $excludeArray! But that was easy to fix. Also you might replacing the variable appication with application. I love this script, made my job so much easier. It belongs here: http://gallery.technet.microsoft.com/scriptcenter as well!
LikeLike
Ooops, I meant to say Get-Content, not Get-ChildItem.
LikeLike
Fantastic script! Is there a way to output this as a CSV instead of GridView? Thanks!
LikeLike
Hi Jackson,
You can refer my other post where i wrote data to csv file.
Please refer and let me know if you find any difficulty in implementing the same
–Prashanth
LikeLike
Hi Prashanth,
Good job you have done. It can help beginners with some ready-to-use code.
Few questions though…
(1) What is it with code duplication (actually, triplication) and the ginormous switch statement? Isn’t it against any code guideline and good practice?
(2) Another thing is explicit comparison against $true or $false. It is debatable in many languages, on the readability grounds; but I think such comparison is definitely unwanted in powershell, because there it certainly increases chances of bugs, because boolean can be $null and will fail comparison against both $true and $false. Next, if you happened to misspell one of the constants, it would be hard to catch that…
I think that your
if ($donotwrite -eq $false)
would be better written as
if (!$donotwrite)
or
if (-not $donotwrite)
if you prefer verbosity.
(3) would almost the same (minus architecture and excluding by arbitrary selected list) be done by the below one-liner?
gp HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*, HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* | Select DisplayName, DisplayVersion, Publisher, InstallDate |ogv
With great respect,
Dmitrii
LikeLike
I really appreciate the time you took to describe the flaws or bad practices that needs to be overcome while writing the code. I’ll try to incorporate your advise going forward. Much appreciate. Thanks for reading and sharing your knowledge.
LikeLike
You’re welcome.
If you ever decide on incorporating, do yourself a favor and use -contains instead of looping through array 😉
Love your T-SQL articles 🙂
LikeLike
Sure, I will do. Thanks for your valuable suggestion:-)
LikeLike
Man this script is a BLESSING!!! Thank you so much. New to powershell. Can you suggest some books / tools to get more advanced. Thank you!!!!
LikeLike
Hi Prashanth,
can you tell me how to use the script? When trying to run it, nothing happens. No error messages, and only the command prompt clears.
LikeLike
Hi Stan,
I think you have created the function but you are not calling the function.
First you need create a function
and then call the function like below
PS:\>Get-InstalledApplication -Computername ABCD
Let me know the steps, if you doing anything different.
Prashanth
LikeLike
Hi Prashanth,
thank you for your answer. I did exactly as you advised and got the result – a huge table of the installed software. After that, another question came. May I somehow filter unwanted results (in my case anything which contains “Update for Microsoft Office” and “Update for Microsoft Visio”)?
I tried to add these strings under $excludeArray in the beginning of the script, but that didn’t work: all the updates are still shown.
LikeLike
Hi Stan,
I think the name is not right.
Can you try to give correct application name?
This is the logic for validation
Foreach($exclude in $excludeArray)
{
if($AppName.StartsWith($exclude) -eq $TRUE)
{
$donotwrite = $true
break
}
}
Prashanth
LikeLike
I copied and pasted the name. I don’t think it might be incorrect in this case. According to the logic for validation it should begin with these words but may not be the full name, which is variable (it shows KB# after each update). I tried to leave only word “Update” but it still shows all output nevertheless.
LikeLike
Hi Stan,
Can you send me a screen shot to powershellsql@gmail.com?
I will take a look at it?
–Prashanth
LikeLike
Prashanth,
thank you so much for your time. My PowerShell knowledge is quite limited, this was the reason for such behavior. Obviously I missed very simple thing which is probably apparent for anyone more experienced. I accidentally closed my PowerShell window and after reopening it and doing Import-Module again, my changes came into force and the script now filters things which I have added in it.
LikeLike
Hi Stan,
Did you recompile the function after adding the application to an exclude list of a function?
I excluded many application.
–Prashanth
LikeLike
Now I know that this is called “recompile function” 🙂
LikeLike
I’m glad that you figured it out and its serving your purpose.
Happy Learning:-)
Thanks for reading my space.
Prashanth
LikeLike
Thank you!
Regards,
Stan
LikeLike
Hi Prashanth,
How can you change the sorting?
In the Out-gridview
I see publisher – version – servername – architecture – application
I want first collumn to be architecture – application – version – publisher and remove servername.
Can that be done?
Thanks
LikeLike
Hello Pau, Data formatting is not supported in Data grid view. You can drag and place it as per your requirement. Did you see there is an output to the console.
Prashanth
LikeLike
I manage to do it Prashant.
But I’m having an issue. Our environnent only have 64OS.
So when I’m doing the query.
It’s showing every application in double which doesn’t reflect whats installed on the computer.
LikeLike