Search code examples
powershellarraylistinvoke-command

Powershell - Passing multiple arraylists to Invoke-Command block


I am trying to write a powershell script that will tell me if a computer in my network is on or off, and if it is on, if there is anyone logged in. Currently I have:

# Create some empty arraylists                                                               
$availablecomputers = New-Object System.Collections.ArrayList
$unavailablecomputers = New-Object System.Collections.ArrayList
$usersloggedon = New-Object System.Collections.ArrayList

#Check connectivity for each machine via Test-WSMan
foreach ($computer in $restartcomputerlist)
{
    try 
    {
    Test-WSMan -ComputerName $computer -ErrorAction Stop |out-null
    Invoke-Command `
    -ComputerName  $computer `
    -ScriptBlock `
    {
        if
        ((Get-WmiObject win32_computersystem).username -like "AD\*")
        {
            $args[0] += $computer
        }
        else 
        {
            $args[1] += $computer
        }
    } `
    -ArgumentList (,$usersloggedon), (,$availablecomputers)
    }
    catch 
    {
    $unavailablecomputers += $computer 
    }
}

So far, if the computer is not on, it works correctly. However, if it is on, $computer won't be added to $usersloggedon or $availablecomputers. Any help would be appreciated.


Solution

  • @Mathias is correct; variables you pass into the scriptblock are passed by value (serialized), not by reference, so you can't update them and change the original object.

    To return values from the scriptblock, use Write-Object or just simply "use" the value (Write-Object $env:COMPUTERNAME is the same as just doing $env:COMPUTERNAME).

    For your specific situation, consider returning an object that contains the information you want:

    $computers = @()
    
    #Check connectivity for each machine via Test-WSMan
    foreach ($computer in $restartcomputerlist)
    {
        try 
        {
        Test-WSMan -ComputerName $computer -ErrorAction Stop |out-null
        $computers += Invoke-Command -ComputerName $computer -ScriptBlock {
            $props = @{
                Name = $env:COMPUTERNAME
                Available = $true
                UsersLoggedOn = ((Get-WmiObject win32_computersystem).username -like "AD\*")
            }
            New-Object PSObject -Property $props
        }
        }
        catch 
        {
        $props = @{
            Name = $computer
            Available = $false
            UsersLoggedOn = $false
        }
        $computers += New-Object PSObject -Property $props 
        }
    }
    $computers # You can now use this with Select-Object, Sort-Object, Format-* etc.