Search code examples
powershellpowershell-remotingrunspace

New-PSSession Parellel Execution


We have two PSSessions that need to be established and imported into the current session before our script can continue. Both steps require about 10 - 15 seconds each for a total of 20 - 30 seconds when run in series.

Is it possible to run New-PSSession in a separate runspace and then somehow import that established session into the parent process?

For example change from this:

New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri ("https://$($service)/PowerShell/") -Credential $Credential -Authentication Basic -AllowRedirection -ErrorAction Stop

New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "https://outlook.office365.com/powershell-liveid/" -Credential $Credential -Authentication Basic -AllowRedirection -ErrorAction Stop

To possibly something like this (warning this doesn't work):

$credential = Get-Credential

$scriptblock = 
{
       param ([string]$Credential)

       $session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri "https://outlook.office365.com/powershell-liveid/" -Credential $Credential -Authentication Basic -AllowRedirection -ErrorAction Stop
       return $session
}


$shell = [PowerShell]::Create().AddScript($scriptblock).AddParameter($credential)

$job = $shell.BeginInvoke()
$result = $shell.EndInvoke($job)

Import-PSSession $result

The ultimate goal is to make this take less time, the idea being if we use New-PSSession in parallel it completes in 10 - 15 seconds instead of 20 - 30 seconds in series. I'd be happy with any answer that accomplishes this, it doesn't need to be using runspaces.

EDIT: Added goals


Solution

  • Credit goes to @ShankarShastri for pointing us in the right direction. The New-PSSession commandlet supports taking an array of URIs or ComputerNames as input. I had servers to test against instead of URIs, but take a look at this:

    $cred = Get-Credential DOMAIN\user
    
    $servers = 
    "server1.domain.company.com",
    "server2.domain.company.com",
    "server3.domain.company.com",
    "server4.domain.company.com",
    "server5.domain.company.com",
    "server6.domain.company.com",
    "server7.domain.company.com"
    
    (Measure-Command {
        foreach($s in $servers) { $temp = New-PSSession -ComputerName $s -Authentication Negotiate -Credential $cred }
    }).TotalSeconds
    
    # 2.987739
    
    (Measure-Command {
        $s1, $s2, $s3, $s4, $s5, $s6, $s7 = New-PSSession -ComputerName $servers -Authentication Negotiate -Credential $cred
    }).TotalSeconds
    
    # 0.5793281
    

    That shows New-PSSession run 7 times vs running New-PSSession once and supplying 7 ComputerNames. The difference is about 6 times faster, which implies the connections are made asynchronously.

    So in your case, you can probably accomplish what you want by running something like:

    $sessions1, $sessions2 = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri ("https://$($service)/PowerShell/"),"https://outlook.office365.com/powershell-liveid/" -Credential $Credential -Authentication Basic -AllowRedirection -ErrorAction Stop