For Windows environments, WMIC (Windows Management Instrumentation Command-line) offers a built-in way to remotely query installed programs. The basic syntax:
wmic /node:"COMPUTERNAME" product get name,version
To authenticate with different credentials:
wmic /node:"COMPUTERNAME" /user:"DOMAIN\username" /password:"password" product get name,version
For more modern systems with PowerShell remoting enabled:
Invoke-Command -ComputerName REMOTEPC -ScriptBlock {
Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* |
Select-Object DisplayName, DisplayVersion
}
To run these commands discreetly without user notification:
Start-Process -WindowStyle Hidden -FilePath "wmic.exe" -ArgumentList '/node:"TARGETPC" product get name'
Create a text file with computer names (computers.txt) and run:
foreach ($computer in Get-Content .\computers.txt) {
Write-Host "Querying $computer" -ForegroundColor Yellow
wmic /node:$computer product get name | Out-File ".\$computer-software.txt"
}
To find specific applications across your network:
Get-Content .\computers.txt | ForEach-Object {
if (wmic /node:$_ product get name | Select-String "Adobe") {
"$_ has Adobe products installed" | Out-File results.txt -Append
}
}
- Use encrypted credentials with
Get-Credential
in PowerShell - Consider constrained endpoints for PowerShell remoting
- Document all remote access for compliance purposes
For Windows environments, WMIC (Windows Management Instrumentation Command-line) provides a lightweight way to inventory installed software across networked machines. The basic syntax for remote querying is:
wmic /node:"REMOTE_PC_NAME" product get name,version
To authenticate with domain credentials:
wmic /node:"REMOTE_PC_NAME" /user:"DOMAIN\username" /password:"password" product get name,version
For more flexibility, PowerShell's Get-WmiObject (or Get-CimInstance in newer versions) works better:
$cred = Get-Credential
Get-WmiObject -Class Win32_Product -ComputerName "REMOTE_PC_NAME" -Credential $cred |
Select-Object Name,Version
To run silently without user interaction, create a scheduled task:
$action = New-ScheduledTaskAction -Execute "powershell.exe" -Argument "-NoProfile -Command "Get-WmiObject Win32_Product | Select Name,Version | Export-Csv -Path 'C:\temp\software.csv' -NoTypeInformation""
$trigger = New-ScheduledTaskTrigger -At 3am -Daily
Register-ScheduledTask -TaskName "SoftwareInventory" -Action $action -Trigger $trigger -RunLevel Highest
For enterprise-scale queries, process multiple machines from a text file:
$computers = Get-Content "C:\temp\machines.txt"
$results = foreach ($computer in $computers) {
try {
Get-WmiObject -Class Win32_Product -ComputerName $computer -ErrorAction Stop |
Select-Object @{Name='Computer';Expression={$computer}},Name,Version
}
catch {
[PSCustomObject]@{
Computer = $computer
Name = "Access Denied"
Version = $_.Exception.Message
}
}
}
$results | Export-Csv -Path "C:\temp\network_software.csv" -NoTypeInformation
For more complete results (including non-MSI packages), query the registry:
Invoke-Command -ComputerName "REMOTE_PC_NAME" -ScriptBlock {
$paths = @(
'HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*',
'HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*'
)
Get-ItemProperty $paths |
Where-Object { $_.DisplayName } |
Select-Object DisplayName,DisplayVersion
}