2

Is there any way I can utilize this piece of code with Test-NetConnection -ComputerName $_ -Port 5985 instead of Test-Connection -ComputerName $_ -Count 1 -AsJob ? I can't utilize -AsJob with the Test-NetConnection cmdlet. Are there any workarounds?

Here is my code:

$online = @()
$pc = Get-Content C:\servers.txt 
$pc | ForEach-Object { 
    Test-Connection -ComputerName $_ -Count 1 -AsJob 
} | Get-Job | Receive-Job -Wait | 
Select-Object @{Name='ComputerName';Expression={$_.Address}},@{Name='Reachable';Expression={
    if ($_.StatusCode -eq 0) { 
        $true 
    } else { 
        $false 
    }
}} | ForEach-Object {
    if ($_.Reachable -eq $true) {
        $online += $_.ComputerName
    }
}
$online | ft -AutoSize

servers.txt just basically has hostnames of all the machines on the network.

Santiago Squarzon
  • 41,465
  • 5
  • 14
  • 37
unixpipe
  • 75
  • 1
  • 9
  • What version of PowerShell are you running? This can be quite easy with `ForEach-Object -Parallel` if running PowerShell 7+, this function might help otherwise https://github.com/santisq/PS-NetScanners/blob/main/Test-TCPConnectionAsync.ps1 – Santiago Squarzon Apr 11 '23 at 21:20
  • 5.1.19041.2673. 5. We can't upgrade all of the powershells in the environment. the key here is `online` will contain all the servers that are up so that I can then interact with those systems at once. This is why a job is key. – unixpipe Apr 11 '23 at 21:31
  • What about installing modules? This can be also easy using `Start-ThreadJob` and you can install it with `Install-Module ThreadJob -Scope CurrentUser` – Santiago Squarzon Apr 11 '23 at 21:32
  • It would be best to stick with the defaults if possible – unixpipe Apr 11 '23 at 21:39

1 Answers1

1

With vanilla Windows PowerShell 5.1 the best you can get is using a RunspacePool, the code is not easy to follow and for a good reason there are many modules available to handle multithreading in PowerShell. The most recommended one would be ThreadJob.
This answer offers a "copy-pastable" version of a function that can handle multithreading from pipeline similar to ForEach-Object -Parallel but compatible with Windows PowerShell 5.1.

$ProgressPreference = 'Ignore'
$maxThreads = 32 # How many jobs can run at the same time?

$pool = [runspacefactory]::CreateRunspacePool(1, $maxThreads,
    [initialsessionstate]::CreateDefault2(), $Host)

$pool.Open()

$jobs = [System.Collections.Generic.List[hashtable]]::new()

Get-Content C:\servers.txt | ForEach-Object {
    $instance = [powershell]::Create().AddScript({
        param($computer)

        [pscustomobject]@{
            Computer = $computer
            Port     = 5985
            PortOpen = Test-NetConnection $computer -Port 5985 -InformationLevel Quiet
        }
    }).AddParameters(@{ computer = $_ })

    $instance.RunspacePool = $pool

    $jobs.Add(@{
        Instance = $instance
        Async    = $instance.BeginInvoke()
    })
}

$result = while($jobs) {
    $job = $jobs[[System.Threading.WaitHandle]::WaitAny($jobs.Async.AsyncWaitHandle)]
    $job.Instance.EndInvoke($job.Async)
    $job.Instance.Dispose()
    $null = $jobs.Remove($job)
}

$pool.Dispose()

$result | Where-Object PortOpen # filter only for computers with this port open
mklement0
  • 382,024
  • 64
  • 607
  • 775
Santiago Squarzon
  • 41,465
  • 5
  • 14
  • 37
  • Does this mean it can run this on 32 systems at the same time as a maximum? Also is there anyway, like in my previous question, to just get all systems with the host name into an `online` array? – unixpipe Apr 11 '23 at 22:35
  • @unixpipe that number can be tweaked for as many as you want or as many as your computer can handle and regarding the hosts online I believe I've already provided this in the answer `$result | Where-Object PortOpen` you just need to expand the `Computer` property from there to get an array of values – Santiago Squarzon Apr 11 '23 at 22:39
  • Thanks. I was able to get that. The issue is, I may not know how many systems are actually up. There could be 4000 or even 5000. Do I just set a max value to 5000 or is that simply not possible with this method? – unixpipe Apr 11 '23 at 22:41
  • @unixpipe no, `$maxThreads = 32` doesn't affect the results in any way. This variable is there to tell the runspace pool how many threads can run at the same time nothing else. `32` is a decent number, if you go up too much on that number you can crash your PowerShell session. I would leave it with that value – Santiago Squarzon Apr 11 '23 at 22:43
  • @unixpipe had a typo, my bad, see the update (missing `$instance.RunspacePool = $pool` ) – Santiago Squarzon Apr 11 '23 at 23:17
  • I was going to accept the answer just now because it worked even before you mentioned the missing statement but can you please elaborate on why it worked without it and would it still work without it? – unixpipe Apr 11 '23 at 23:30
  • @unixpipe it works because the runspace pool is not mandatory to run code in a separate thread, all you need is a powershell instance (`[powershell]::Create()....`) but without the pool all instances would be running at the same time. The pool is a thread managing mechanism, it will tell the threads when they can "start running" – Santiago Squarzon Apr 11 '23 at 23:37