Speeding Up PowerShell Remoting

by Aug 20, 2020

PowerShell remoting is insanely powerful: with Invoke-Command, you can send arbitrary PowerShell code to one or many remote machines and execute it there in parallel.

On Windows Servers, PowerShell remoting is typically enabled, so all you need are Administrator privileges. Here is a simple example:

 
PS> Invoke-Command -ScriptBlock { "I am running on $env:computername!" } -ComputerName server1 -Credential domain\adminuser 
 

This tip is not about setting up PowerShell remoting so we assume the above call does work for you. Let’s instead focus on one of the most significant bottlenecks of PowerShell remoting, and how you can work around it.

Here is a code that accesses a remote system and dumps the DLLs from the Windows folder. A stopwatch is used to measure how long this takes:

# change this to the computer you want to access
$computername = 'server123'

# ask for a credential that has admin privileges on the target side
$cred = Get-Credential -Message "Log on as Administrator to $computername!" 

# check how long it takes to retrieve information
$stopwatch = [System.Diagnostics.Stopwatch]::new()
$stopwatch.Start()

$result = Invoke-Command -ScriptBlock {
   Get-ChildItem -Path c:\windows\system32 -Filter *.dll 
} -ComputerName $computername -Credential $cred

$stopwatch.Stop()
$stopwatch.Elapsed 

Surprisingly, this code can run for a very long time. When we tried it against our own local machine, it took 95 seconds. Returning information from Invoke-Command may be extremely slow because objects need to be serialized as XML to cross process boundaries and re-hydrated when they travel back to the caller.

To speed up remoting, keep this in mind and return only as little information as possible. Often, the amount of information can be easily reduced.

For example, if you really needed a list of all DLL files in the Windows folder, you most likely just need a few properties like path and size. By adding a Select-Object and specifying the properties you really need, the same code that took 95 seconds now runs in less than one second:

# change this to the computer you want to access
$computername = 'server123'

# ask for a credential that has admin privileges on the target side
$cred = Get-Credential -Message "Log on as Administrator to $computername!"

# check how long it takes to retrieve information
$stopwatch = [System.Diagnostics.Stopwatch]::new()
$stopwatch.Start()

$result = Invoke-Command -ScriptBlock {
   Get-ChildItem -Path c:\windows\system32 -Filter *.dll | 
   # REDUCE DATA BY SPECIFYING THE PROPERTIES YOU REALLY NEED!
   Select-Object -Property FullName, LastWriteTime
} -ComputerName $computername -Credential $cred

$stopwatch.Stop()
$stopwatch.Elapsed 


Twitter This Tip! ReTweet this Tip!