Powershell : Scan for installed applications remotely on server

I have done a post like this before, linked to finding "java" versions but that was a simple requirement this one is a little more complex, and you always need to improve your skills when you do the same basic task again 💀

The goal this time is to report on the version of putty and winscp installed on a list of servers, the reason for this is older version of putty and winscp have a vulnerability there the private keys can be exploited if you have local access to the server.

While this risk is small if sounded like a good requirement for a script to check this on remote servers, so the usual approach for this is to look at the registry path > HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall which is good but this was not displaying winscp whatsoever, if you tried this command:

Get-WmiObject -Class Win32_Product

That will return all the software installed which is no good, but if you used the -like variable like this it would find putty:

Get-WmiObject -Class Win32_Product | where Name -like "putty*" | select Name, Version


However, if you use the same command for winscp it returned nothing

Get-WmiObject -Class Win32_Product | where Name -like "winscp*" | select Name, Version

This is where I dropped the WmiObject query and thought there must be a better way to do this than a WMI call, so I looked on the powershell galley for this task, as there is no point reinventing the wheel with coding and I can across this galley item

https://gallery.technet.microsoft.com/Get-Programs-Installed-on-0e93f152

When I look at the code for this I noticed that it scanned the HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall as well as this key path which contains all the 64bit applications HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\ and that is what I was missing, I was only looking at the location for 32bit applicationsm, which this code nicley addresses.

Script : Get-InstalledProgram

You can get that from the link above but the syntax for that command if you wish to query a remote computer looks like this for putty and winscp:

Get-InstalledProgram.ps1 -ProgramName "putty*" -ComputerName <server> | Select-Object DisplayName, DisplayVersion

Get-InstalledProgram.ps1 -ProgramName "winscp*" -ComputerName <server> | Select-Object DisplayName, DisplayVersion

When you run that it returns this, which is good news as we now have both putty and winscp reporting back with the relevant versions, as below:


Now, I can get back to scripting (or weaponize the original script) that command now I have a command that works and this is what I came up with that works well, this will take a list of servers in a file called "servers.txt" and run those two commands from earlier against them and output them to the screen

Script - Screen Output

# Read the list of server names from the text file
$servers = Get-Content "servers.txt"

# Loop through each server and execute the commands
foreach ($server in $servers) {
    Write-Host "Computer Name: $server"

# Execute the first command for WinSCP
$winscp = .\Get-InstalledProgram.ps1 -ProgramName "winscp*" -ComputerName $server | Select-Object DisplayName, DisplayVersion

# Execute the second command for Putty
$putty = .\Get-InstalledProgram.ps1 -ProgramName "putty*" -ComputerName $server | Select-Object DisplayName, DisplayVersion

# Combine data into a single table
$combinedTable = @($winscp, $putty) | Format-Table -AutoSize

# Output the table
$combinedTable

# Add a separator between outputs for each server
    Write-Host "---------------------------"
}

Script - CSV file output

If you wish this to go to a CSV file that is nicely formatted and easy to read and filter then you can use this script again same as the first but no "script visual" target here, all in the CSV file.

# Read the list of server names from the text file
$servers = Get-Content "servers.txt"

# Initialize an empty array to store combined data
$combinedData = @()

# Loop through each server and execute the commands
foreach ($server in $servers) {
    Write-Host "Processing server: $server"

    # Execute the first command for WinSCP
    $winscp = .\Get-InstalledProgram.ps1 -ProgramName "winscp*" -ComputerName $server | Select-Object DisplayName, DisplayVersion

    # Execute the second command for Putty
    $putty = .\Get-InstalledProgram.ps1 -ProgramName "putty*" -ComputerName $server | Select-Object DisplayName, DisplayVersion

    # Combine data into a single table
    $combinedData += New-Object PSObject -Property @{
        "ComputerName" = $server
        "WinSCP_DisplayName" = $winscp.DisplayName
        "WinSCP_DisplayVersion" = $winscp.DisplayVersion
        "Putty_DisplayName" = $putty.DisplayName
        "Putty_DisplayVersion" = $putty.DisplayVersion
    }

    Write-Host "Data processed for server: $server"
}

# Output combined data to CSV file
$combinedData | Export-Csv -Path "putty_scp_report.csv" -NoTypeInformation
Write-Host "Data exported to installed_programs.csv"

The CSV file you get is very easy to read and filter which makes this a beautiful but deadly edition (sorry stole that from Jurassic Park) to my script library.

I'll buy that for a dollar, scripting done for this mission (sorry stole from Robocop this time)

Previous Post Next Post

نموذج الاتصال