1

I have some experience with PowerShell, and usually Google or searching forums like these yields the answers when I have questions - but not this time.

I'm trying to collect the number of .log files in a directory on a remote server, then I'd like to store the location (drive letter and folder path) and the count in an array list for later. So far everything is working as I'd expect, but I'm running into trouble adding my PSCustomObjects to the array list. I'm not sure if it's because I'm executing on a remote server or if something else is causing the problem. Here is my code:

$server = Read-Host -Prompt 'Please enter the server name'
[System.Collections.ArrayList]$returnObj = @()

Invoke-Command -ComputerName $server {
    $drives = Get-PSDrive -PSProvider FileSystem |
              Where-Object {$_.Description -like "ExVol*"} |
              Select-Object Root

    foreach ($d in $drives) {
        Set-Location -Path $d.Root
        $folders = Get-ChildItem -Path $d.Root |
                   Where-Object {$_.Name -like "*.log"} |
                   Select-Object Name
        foreach ($f in $folders) {
            $count = (Get-ChildItem -Path $f.Name).Count

            $obj = [PSCustomObject]@{
                LogFolder = $d.Root.Trim() + $f.Name
                LogFileCount = $count
            }
            Write-Host $obj
            $returnObj.Add($obj | Select-Object DatabaseFolder,LogFileCount)
        }
    }
}
$returnObj

In this format I get a syntax error on the line

$returnObj.Add($obj | Select-Object DatabaseFolder,LogFileCount)

If I change the above line to $returnObj.Add($obj) I avoid the syntax error, but instead I get an error saying I cannot call a method on a null valued expression.

I've tried creating the ArrayList inside the Invoke-Command and I've tried using New-Object instead of PSCustomObject to no avail.

Ansgar Wiechers
  • 193,178
  • 25
  • 254
  • 328
Shaurya R
  • 13
  • 3

2 Answers2

1

I think your mixing stuff a bit up, this will do:

$returnObj = Invoke-Command -ComputerName $server {
    $drives = Get-PSDrive -PSProvider FileSystem |
              Where-Object {$_.Description -like "ExVol*"} |
              Select-Object Root

    foreach ($d in $drives) {
        Set-Location -Path $d.Root

        $folders = Get-ChildItem -Path $d.Root |
                   Where-Object {$_.Name -like "*.log"} |
                   Select-Object Name

        foreach ($f in $folders) {
            $count = (Get-ChildItem -Path $f.Name).Count

            [PSCustomObject]@{
                LogFolder = $d.Root.Trim() + $f.Name
                LogFileCount = $count
            }
        }
    }

}
$returnObj

The problem is this line:

[System.Collections.ArrayList]$returnObj = @()

is declared outside of the Invoke-Command -ScriptBlock. This means it's not available within the session on the remote machine, and as such can not be used there.

On a side note, you cannot fill an array like you fill a Hashtable with data.

  • Arrays are filled like $MyArray = @(); $MyArray += 'MyValue'
  • Hashtables like $MyHash=@{}; $MyHash.SomeKey = 'SomeValue' or as you indicated $MyHash.Add('SomeKey', 'SomeValue')
  • ArrayLists are filled like [System.Collections.ArrayList]$MyArrayList = @(); $MyArrayList.Add('SomeValue')

I hope this makes it a bit more clear. The return values can always be catched before the Invoke-Command or even before a simple foreach (). For example $result = 0..3 | ForEach-Object {$_} is perfectly valid too.

DarkLite1
  • 13,637
  • 40
  • 117
  • 214
  • This is exactly what I was looking for. I added a Format-table to the final line to get the output in the format I needed. I think this is where my lack of experience shows because I don't fully understand how the objects are being stored for each directory. If you can provide any insight I would appreciate it. Either way, this is exactly what I needed and thank you for your help! – Shaurya R Aug 07 '18 at 13:56
  • You're welcome :) A golden rule would be to always work with objects in the code and only in the end format the data (for output to a file, e-mail, or a quick read on the display...) – DarkLite1 Aug 07 '18 at 13:59
0

You need to actually return your object from the remote system to your local system since you cannot use your $returnObj within the remote session.

As an example:

$returnValue = Invoke-Command -ComputerName $server {

    $obj = [PSCustomObject]@{
        LogFolder = $d.Root.Trim() + $f.Name
        LogFileCount = $count
    }

    #return the object via the pipline
    $obj 
}

$returnObj.Add($returnValue | Select-Object DatabaseFolder,LogFileCount)

The above example is lacking of proper error handling, therefore you would get an error if the remote system is not reachable but it's a start.

TobyU
  • 3,718
  • 2
  • 21
  • 32