PowerShell Remoting Question - Help

I have 3300 AD computers that I want to get computer info from. I am going to use Invoke-Command and the code in a scriptblock. I can run this fine but here is my question. How can you still use the parallel part of remoting and only run it on computers that are online so I am not running invoke-command against a bunch of offline machines. Or does it matter. I can’t ping them becasue thhat’s one at a time and by the time I build a list the online state might have changed. I also want to catch results in a CSV file and then when I run it again parse the AD computers against the CSV file so I don’t run it on computers that I have already collected info on. How do you do this. Do you just run it against all of the computers online or not and just catch errors in a variable. please help this is driving my OCD crazy and I can’t come up with a good solution.

As you probably notice by yourself doing such a task properly is quite a complex task.

I’d actually recommend NOT to do it with PowerShell remoting. I’d expect in an environment of this size you probably have a software deployment solution in place. If this software deployment solution does not already come with inventory functions I’d eventually use it to deliver the PowerShell code to the computers and do whatever you have to do locally.

If that’s not suitable you can even use GPOs to run scripts.

You could set up a share where all computers drop their inventory. Later you simply collect all individual inventory files from that share and combine them in a CSV file oder data base or whatever you like.

Now we can try to answer your actual questions:

I’d recommend to add the online/offline check into your scriptblock.

Simple. You query the AD and the CSV and compare them before you run the scripts for the next chunk of computers. :wink:
You can use

for this purpose.

Thank you for your suggestions I do have PDQ deploy but not the inventory part so instead of running it through PowerShell I could do what you suggested and have it right files to a share or append information to one file on a network share. If you were going to run it through PowerShell and you still wanted to keep the parallel part of remoting how would you ping a list of computers that large you said to add it into the script block I’m not sure what you mean by that could you give me an example if you have one thank you again for your help

I don’t have one, sorry. But you said …

Instead of checking the online status of all computers in advance you include this check in the script block you already have.

As Olaf says you can check the status of each computer just before running the actual Invoke-Command cmdlet.
Here’s a snippet I use in one of my scripts for checking servers in the AD:

foreach ($Server in $Servers) {
  # Test if the server is online and has WinRM enabled.
  $pingtest = Test-Connection -ComputerName $Server.Name -Quiet -Count 1 -ErrorAction SilentlyContinue
  $winrmtest = Test-WSMan -ComputerName $Server.Name -ErrorAction SilentlyContinue

  if ($pingtest -and $winrmtest) {
    Write-Output "$($Server) is online and WinRM is active!"
  }
  else {
    Write-Warning "$($Server) is offline or WinRM is not enabled!"
  }

What you want to do is to stick the Invoke-Command in the IF-block, in the ELSE-block you can log that the computer didn’t respond in a separate log or csv-file. That way you should be able to just run the Invoke-Command against that subset at a later time.

You can of course also log successes after completion of the Invoke-Command, and just exclude successful runs from the next runtime… It’s a matter of preference.

And if you want to get even more granular you can split the IF-ELSE block into IF-ELSEIF-ELSE if you want to log if a computer responds to ping, but fails the WinRM test so you can verify that WinRM is actually enabled on the computer in question.

@w7-65a Just to point an item here, Invoke-Command can be used to execute multiple targets at same time when rans as a job using -Job switch. By default it throttles to 32 computers which can be controlled using -ThrottleLimit parameter.

see below example.
Invoke-Command (Microsoft.PowerShell.Core) - PowerShell | Microsoft Docs

I would like to clarify for all that Invoke-Command can run concurrently without the use of -Job. Again it defaults to ThrottleLimit of 32. Putting Invoke-Command in a try/catch has never worked the way I would hope and expected, so I typically just silence the error output while collecting the errors using -ErrorAction and -ErrorVariable parameters. Here is an example of that with comments for what some may think is odd.

# this example just gets enabled systems with OS like *server*, use whatever list you have
$serverlist = Get-ADComputer -Filter "Enabled -eq '$true' -and operatingsystem -like '*server*'"

# if it doesn't connect in 10 seconds, it's probably not going to. Adjust for your needs
$sessionoption = New-PSSessionOption -OpenTimeout 10

# hashtable for the parameters/arguments and splat to the command for readability
$pssparams = @{
    ComputerName  = $serverlist.name
    SessionOption = $sessionoption
    ErrorAction   = 'SilentlyContinue'
    ErrorVariable = 'errs'
}

# create ps session for each
$sessionlist = New-PSSession @pssparams 

# extract computernames from $errs if any
# -OutVariable always produces an arraylist which we can append to as shown in the next batch
# remove $null assignment if you want to see the output while it's running
$null = $errs | ForEach-Object {
    [PSCustomObject]@{
        ComputerName = $_.TargetObject.ConnectionInfo.ComputerName
    }
} -OutVariable haderror

# again splat for readability
$icmparams = @{
    Session       = $sessionlist
    ScriptBlock   = {Get-ComputerInfo}
    ThrottleLimit = $sessionlist.Count
    ErrorAction   = 'SilentlyContinue'
    ErrorVariable = 'errs'
}

$output = Invoke-Command @icmparams

# extract computernames of those that ended in error
# append to $haderror with the plus sign
$null = $errs | ForEach-Object {
    [PSCustomObject]@{
        ComputerName = $_.OriginInfo.PSComputerName
    }
} -OutVariable +haderror

# show the computers that failed
Write-Host "These $($haderror.computername.count) machines encountered an error" -ForegroundColor Yellow
Write-Host ($haderror.computername -join ', ') -ForegroundColor Yellow

# build list of machines that did not complete or had an error
# Remove the error portion if you want to reattempt on these
$notcomplete = $serverlist | Where-Object {$_.name -NotIn $output.pscomputername -or $_.name -notin $haderror.computername}

$haderror | Export-Csv -Path path\to\ended_in_error.csv -NoTypeInformation
$output | Export-Csv -Path path\to\Successful_output.csv -NoTypeInformation
$notcomplete | Export-Csv -Path path\to\still_needed.csv -NoTypeInformation

Please note that I set the throttlelimit dynamically to the amount of sessions, you may want/need to adjust this. You don’t want to set off any alarms.

1 Like