2

I have a function called Get-InstalledApps that connects to all computers listed with the -computers parameter, which accepts input via the pipeline.

There are two problems when piping computer names to the function:

(a) I have a CSV file I can pass to it but it parses the value like this: @{computername=HOSTNAME} instead of just HOSTNAME.

(b) When piping from Get-ADComputer -Filter * instead, it's only grabbing the last computer name passed.

Here's my function:

function Get-InstalledApps {

    Param (

    [CmdletBinding()]

    [Parameter(ValueFromPipeline=$true)]

    [Alias('name')]

    [string[]]$computers = $env:COMPUTERNAME

    )

    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"

        }

    }

}

Update: This issue has been resolved. Here is the gist for those who want it:

https://gist.github.com/TylerJWhit/f596c307cf87540842281a8a20149f9a

mklement0
  • 382,024
  • 64
  • 607
  • 775

1 Answers1

0

Re (a):

If your function sees @{computername=HOSTNAME} instead of just HOSTNAME, the implication is that you're doing something like:

 Import-Csv ... | Select-Object ComputerName | Get-InstalledApps

instead of the required:

 Import-Csv ... | Select-Object -ExpandProperty ComputerName | Get-InstalledApps

Note the -ExpandProperty switch, which is required in order to extract a single property value from the input; without it, you'll get an object with that property - see this answer for more.


Re (b):

In order to accept object-by-object pipeline input, your function must have a process { ... } block:

function Get-InstalledApps {
  Param (
    [CmdletBinding()]
    [Parameter(ValueFromPipeline=$true)]
    [Alias('name')]
    [string[]]$computers = $env:COMPUTERNAME
  )

  process {  # This ensures that the following code is called for each input object.
    foreach ($computer in $computers){
      # ...
    }
  }

}

See Get-Help about_Functions_Advanced.

Note that if you only ever expect your function to receive the list of computer names via the pipeline (... | Get-InstalledApps) as opposed to by parameter (Get-InstalledApps -Computers ...), you can declare the -Computers parameter as just [string] (not as an array), which obviates the need for the foreach loop inside the process { ... } block.
For a discussion of this difference, see this GitHub issue.

mklement0
  • 382,024
  • 64
  • 607
  • 775