Skip to content

Instantly share code, notes, and snippets.

@TylerJWhit
Last active December 7, 2019 16:02
Show Gist options
  • Save TylerJWhit/f596c307cf87540842281a8a20149f9a to your computer and use it in GitHub Desktop.
Save TylerJWhit/f596c307cf87540842281a8a20149f9a to your computer and use it in GitHub Desktop.
List all applications in Windows
# Version: 0.5
# Date: 2019-01-30
# File Name: Get-InstalledApps
# Author: TylerJWhit
# Notes:
# The following commands may be of help:
#
# Run against every computer in domain.
# Get-ADComputer -Filter * | Select-Object -ExpandProperty Name | Get-InstalledApps
#
# Run against CSV (replace path with location of file and ensure A1 says 'computername':
# Get-InstalledApps -computers (Import-Csv -Path C:\Computers.csv | Select-Object -ExpandProperty computername)
#
# Run against computers listed in command:
# Get-InstalledApps -computers HOSTNAMEA,HOSTNAMEB
function Get-InstalledApps {
Param (
[CmdletBinding()]
[Parameter(ValueFromPipeline=$true)]
[Alias('name')] # Helps with 'Select-Object -ExpandProperty Name'
[string[]]$computers = $env:COMPUTERNAME
)
process {
foreach($computer in $computers){
write-verbose -verbose -message "`nStarting scan on $computer"
Invoke-Command -Computername $computer -ErrorAction SilentlyContinue -ErrorVariable InvokeError -Scriptblock {
$installPaths = @('HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall','HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall')
Get-ChildItem -Path $installPaths | Get-ItemProperty | Sort-Object -Property DisplayName | Select-Object -Property DisplayName, DisplayVersion, Publisher, UninstallString, Version
}
if ($invokeerror){
Write-Warning "Could not communicate with $computer"
} # if ($invokeerror)
} # foreach($computer in $computers)
} # process
} # function Get-InstalledApps
@ArtisanByteCrafter
Copy link

ArtisanByteCrafter commented Jan 29, 2019

A few things:

Invoke-Command -Cn $computername

I would suggest don't use aliases (-Cn) in scripts (Save that for ad-hoc shell interaction). It tends to lead to confusion, especially if others will be reviewing your work. Along those lines, alse define -ScriptBlock instead of just {}

Invoke-Command -ComputerName $computername -ScriptBlock { }


write-verbose -verbose -message "`nStarting scan on computer "
write-verbose -verbose -message "$computername."

These can be condensed into a single line, no line break needed. Write-Verbose -Message "Starting scan on $computername"


Convert $Computers to a parameter, and give it a default value (or make it mandatory). This will default to the current computer.

param (
    [string[]]$Computers = $env:COMPUTERNAME
)

Foreach ($Computer in $Computers) {
    # rest of code
}

You can still import your csv if you want. -Computers (Import-Csv -Path C:\path\to\file.csv)


Consider using -Count 1 for Test-Connection to limit number of pings from 4 to 1. This should improve pipeline processing time considerably.

Here is the dramatic difference between the two: (TotalSeconds)

Without -Count 1

measure-command {
    if (test-connection -ComputerName $env:computername) {}
}

TotalSeconds      : 3.0513824

With -Count 1

measure-command {
    if (test-connection -ComputerName $env:computername -count 1) {}
}

TotalSeconds      : 0.0199772

@TylerJWhit
Copy link
Author

Need to be able to parse this output : Get-ADComputer -Filter * | Select Name | Get-InstalledApps
Get-InstalledApps -Computers (Import-Csv -Path C:/path/to/file.csv) is also not parsing the info correctly.

@ArtisanByteCrafter
Copy link

ArtisanByteCrafter commented Jan 30, 2019

Question 1:
Get-ADComputer -Filter * | Select Name

That gets the Name object but what you want is the actual value of that object. Change it to Get-ADComputer -Filter * | Select-Object -ExpandProperty Name

Your pipeline is expecting a raw string format, not a NoteProperty.

Last, consider adding -Filter {(enabled -eq $True)} to Get-ADComputer to filter out disabled machines.

In order for Get-InstalledApps to be at the end of the pipeline, it has to be set to accept pipeline input. This is done with cmdletbinding and [Parameter(ValueFromPipeline=$true)] parameters. Read up on value by pipeline here

It looks like you've already done that, so once you fix the part that is passing a valid computername down the pipeline, that part should work.

Question 2:
We'd need to know the headers in the csv file in order to know the exact import code. Assuming header1 is "hostname"

Get-InstalledApps -Computers (Import-Csv -Path C:\path\to\file.csv | Select-Object hostname)

@TylerJWhit
Copy link
Author

The Select Name statement works with the Alias, but it's only trying the last entry piped to it. The Import-Csv keeps parsing the name like this: @{computername=hostname}

@TylerJWhit
Copy link
Author

Get-InstalledApps -computers (Import-Csv -Path C:\computername.csv | Select-Object -ExpandProperty computername) fixed the issue. I was missing -ExpandProperty

@TylerJWhit
Copy link
Author

My last issue is iteration of the Piped values instead of simply running against the last value.

@TylerJWhit
Copy link
Author

Last piece of the puzzle: missing the process bracket.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment