Mitch A
Mitch A

Reputation: 2098

powershell array results truncated for unknown reason

I've hacked together a script that lists all AD computer objects and enriches with some other information: Namely, is the machine pingable and what are the members of its local admin group. Each of these data elements are retrieved in an independent pipeline function.

For reasons unknown, the array of objects I'm pipelining is being truncated. Instead of the 883 computers returned from the GetAdComputer cmdlet, I'm left with only ~230 computers in the final CSV.

I've narrowed down the problem to the command that retrieves local admins via PS remoting. I'm sure there are machines for which this command fails so it's likely an exception is being thrown at some point. I assume such an exception may terminate the loop and lop off array items? I tried wrapping the Invoke-Command in a try/catch but that didn't resolve the issue. Still missing array items.

I'm sure this is a noob oversight. I don't use PS often enough to keep the language semantics straight. Any help is appreciated.

Import-Module active*
 

$now=(Get-Date).ToString("yyyyMMddTHHmmss")
Get-ADComputer -Filter   'Name -like "prod-idfi*"' -Properties LastLogonDate, Description |select LastLogonDate,DNSHostName,DistinguishedName,Description   |foreach{
 
  
    $alive = Test-Connection -CN $_.DNSHostName  -Count 1 -BufferSize 16 -Quiet

 
    return [PSCustomObject]@{
    PSTypeName = "Computer"
    Id=$null
    Name = $_.DNSHostName
    IsAlive = If($alive) {1} Else {0}
    DistinguishedName = $_.DistinguishedName
    Description = $_.Description
    LastLogonDate = $_.LastLogonDate
    LocalAdmins=''
   
    }
 
    

}|foreach{
    
  $computer = $_
  if ($computer.IsAlive){
    
  try{
          $localAdmins = Invoke-Command -ComputerName $_.Name {([ADSI]"WinNT://./Administrators").psbase.Invoke('Members') | % { ([ADSI]$_).InvokeGet('AdsPath')}} 
          $computer.LocalAdmins= "$($localAdmins -join ",")" 
          }
  catch {
      Write-Host ("something bad happened")
  }
        
  }
  $computer
    
} |Export-Csv ".\ComputerInventory_$now.csv" -NoTypeInformation

Upvotes: 1

Views: 150

Answers (1)

Theo
Theo

Reputation: 61013

You do not need to pipe to a second ForEach-Object, since all can be done inside the first loop. Also, the Select-Object can be left out.

In order to enter the catch block also on non-terminating errors, you need to add parameter -ErrorAction Stop to the Invoke-Command.

Your code rewritten

Import-Module Active*

$now = (Get-Date).ToString("yyyyMMddTHHmmss")
$localGroup = 'Administrators'
Get-ADComputer -Filter "Name -like 'prod-idfi*'" -Properties LastLogonDate, Description |
    ForEach-Object {
        $alive = Test-Connection -ComputerName $_.DNSHostName -Count 1 -BufferSize 16 -Quiet
        $localAdmins = if ($alive) {
            try {
                Invoke-Command -ScriptBlock {
                    ([ADSI]"WinNT://./$localGroup").psbase.Invoke('Members') | 
                     ForEach-Object { ([ADSI]$_).InvokeGet('AdsPath') -join ', '}
                } -ErrorAction Stop
            }
            catch {
                Write-Warning "Could not retrieve members of the local '$localGroup' group"
            }
        }
        else { '' }
        [PSCustomObject]@{
            PSTypeName        = "Computer"
            Id                = $null
            Name              = $_.DNSHostName
            IsAlive           = [int]$alive
            DistinguishedName = $_.DistinguishedName
            Description       = $_.Description
            LastLogonDate     = $_.LastLogonDate
            LocalAdmins       = $localAdmins
        }
    } | Export-Csv -Path ".\ComputerInventory_$now.csv" -NoTypeInformation

Upvotes: 1

Related Questions