Many years ago I wrote a post on passing functions into Start-Job (Calling a PowerShell function in a Start-Job script block when it’s defined in the same script). Over the years I’ve had a number of emails from people asking about how to use it to fix their own situation. In many of these cases there’s been a simpler way to achieve what they wanted to do.
Sometimes a non obvious solution using PowerShell basics can be much simpler. Patrick posted up that he was having problems running a command against a list of servers and wanting to be able to skip those that time out. Using an injected function for this was a bit of overkill. I had a premade solution I use, which relies on Invoke-Command and is simpler:
$session = New-PSSession -ComputerName Server1, server2,server3 -ErrorAction SilentlyContinue $ServiceName = 'MSSQL`$' $timeout = 10 $remote = Invoke-Command -Session $session -ScriptBlock {Get-Service -Name *$using:servicename*} -AsJob $remote | Wait-Job -Timeout $timeout $output = $remote | Receive-Job $output | ForEach {"$($_.Name) on $($_.PsComputerName) is $($_.Status)"}
We setup new PsSessions using New-PsSession
, I set ErrorAction
to SilentlyContinue
just in case a host isn’t available for some reason (if I was being good I’d try/catch here).
As we’re just using PS standard functionality here with Get-Service
there’s no need to build a a new function, we can just call this directly here. By calling Invoke-Command
against a session pointed at numerous hosts we can PowerShell handle all the connection management here and just assume the command will be ran against each host. if we were running against a lot of hosts then we would want to look into using the -ThrottleLimit
parameter to limit the number of concurrent hosts we’re hitting. The one little trick here is using the using
scope modifier here so PS pulls in the variable defined in our main scope (gory details on scoping here
As we called Get-Service
with the -AsJob
switch we can now treat it as a job, so we can use the PS jobs cmdlets to manage it. The first thing we want to do is skip those jobs that are taking longer than out specified timeout value (in this case 10 seconds). So we pass out Invoke-Command
job into Wait-Job
with a Timeout parameter. PS will now keep an eye on each job and drop those exceeding our timeout limit.
Once we’ve gotten all the jobs that met our timelimit we grab the ouput using Receive-Job
, and then just process it like any other PowerShell object
As we’ve removed a lot of complexity it’s much easier to revise or reuse the framework at a later date.
Patrick
Thank you for posting that solution! I’ll see if I can work that in. I’m managing slightly over 700 SQL servers globally so I’ll have to try adjusting the process a bit for that.
admin
In that case investigate the ThrottleLimit parameter on Invoke-Command to limit the concurrent connections or you’ll try to open all 700 sessions at once!