runspacepool - disconnect exchange online

Olivier GOURET 1 Reputation point
2021-02-18T10:10:26.85+00:00

hello

I work with a large tenant (100k mailbox), so i'm trying different methods to speed up my scripts.
Currently i'm doing a get-mailboxstatistics + get-mailboxpermission on all mailbox, and it takes a VERY long time, more than 24 hours.

All my code is ok, except one point : I'm using the new module "Exchange online v2", and i can't figure out how to call the function "disconnect-exchangeonline" when the runspacepool is closed !

Here is a sample code :

$curScriptPath = Split-Path -parent $MyInvocation.MyCommand.Definition

$ScriptBlock = {
Param($num)

"num=$num"

$p=Get-mailbox XXXXXXXX -erroraction stop |select primarysmtpaddress
"primarysmtpaddress=$($p.primarysmtpaddress)"

$totalitemsize=Get-o365mailboxstatistics XXXXXXXX -erroraction stop |select totalitemsize
"totalitemsize=$($totalitemsize.totalitemsize)"
}


$RunspaceCollection = @()

#Connect O365 using module Exchange Online v2 (i had to include all this in another file ".ps1", i did not figured out how to add it "dynamically")
$initialSessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault2()
$initialSessionState.Variables.Add((New-Object -TypeName System.Management.Automation.Runspaces.SessionStateVariableEntry -ArgumentList "CommandName", @("Get-Mailbox", "Get-MailboxStatistics"), $null) )
$initialSessionState.StartupScripts.Add("$curScriptPath\Connect-O365.ps1")

#Create runspace pool
$runspacepool = [runspacefactory]::CreateRunspacePool(1, 3, $initialSessionState, $Host)
$RunspacePool.Open()

0..10 | % {
    $Powershell = [PowerShell]::Create().AddScript($ScriptBlock).AddArgument($_)

    #Specify runspace to use
    $Powershell.RunspacePool = $RunspacePool

    #Create Runspace collection
    [Collections.Arraylist]$RunspaceCollection += New-Object -TypeName PSObject -Property @{
        Runspace = $PowerShell.BeginInvoke()
        PowerShell = $PowerShell  
    }
}

#Get return of each call
While($RunspaceCollection){
    Foreach($Runspace in $RunspaceCollection.ToArray()){
        If($Runspace.Runspace.IsCompleted){
            $Runspace.PowerShell.EndInvoke($Runspace.Runspace)
            $Runspace.PowerShell.Dispose()
            $RunspaceCollection.Remove($Runspace)
        }
    }
}
$RunspacePool.Close()
$RunspacePool.Dispose()
Exchange Server Development
Exchange Server Development
Exchange Server: A family of Microsoft client/server messaging and collaboration software.Development: The process of researching, productizing, and refining new or existing technologies.
511 questions
0 comments No comments
{count} votes

1 answer

Sort by: Most helpful
  1. Olivier GOURET 1 Reputation point
    2021-06-16T13:15:47.227+00:00

    Update : I found some sort of a "solution", but i'm not sure it's completely OK as now I don't use this code anymore, i use the "EXO" cmdlets from the module "Exchange Online Powershell v2", and it seems reliable.
    The solution I think of is to add some "fake" items to the runspacecollection to deal with the "disconnect", here is the updated pseudo-code (i could not identify my updates, the main part is the addition of a parameter "disconnect" to the scriptBlock).

    $curScriptPath = Split-Path -parent $MyInvocation.MyCommand.Definition
    
    $ScriptBlock = {
        Param($num,$disconnect)
    
        if ($disconnect) {
            Disconnect-ExchangeOnline -Confirm:$false
        }
        else {
            num=$num"
    
            $p=Get-mailbox XXXXXXXX -erroraction stop |select primarysmtpaddress
            "primarysmtpaddress=$($p.primarysmtpaddress)"
    
            $totalitemsize=Get-o365mailboxstatistics XXXXXXXX -erroraction stop |select totalitemsize
            "totalitemsize=$($totalitemsize.totalitemsize)"
        }
    }
    
    
    $RunspaceCollection = @()
    

    Connect O365 using module Exchange Online v2 (i had to include all this in another file ".ps1", i did not figured out how to add it "dynamically")

    $initialSessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault2()
    $initialSessionState.Variables.Add((New-Object -TypeName System.Management.Automation.Runspaces.SessionStateVariableEntry -ArgumentList "CommandName", @("Get-Mailbox", "Get-MailboxStatistics"), $null) )
    $initialSessionState.StartupScripts.Add("$curScriptPath\Connect-O365.ps1")
    

    Create runspace pool

    $maxThreads = 3
    $runspacepool = [runspacefactory]::CreateRunspacePool(1, $maxThreads, $initialSessionState, $Host)
    $RunspacePool.Open()
    
    0..10 | % {
        $Powershell = [PowerShell]::Create().AddScript($ScriptBlock).AddArgument($_)
    

    Specify runspace to use

        $Powershell.RunspacePool = $RunspacePool
    

    Create Runspace collection

        [Collections.Arraylist]$RunspaceCollection += New-Object -TypeName PSObject -Property @{
            Runspace = $PowerShell.BeginInvoke()
            PowerShell = $PowerShell  
        }
    }
    

    add some "fake" items to the runspacecollection to deal with the "disconnect", 1 per thread

    $disconnect=$true
    1..$maxThreads | % {
        $Powershell = [PowerShell]::Create().AddScript($ScriptBlock).AddArgument($_).AddArgument($disconnect)
        $Powershell.RunspacePool = $RunspacePool    #Specify runspace pool to use
    

    Add runspace in Runspace collection

        [Collections.Arraylist]$RunspaceCollection += New-Object -TypeName PSObject -Property @{
            Runspace = $PowerShell.BeginInvoke()
            PowerShell = $PowerShell
        }
    }
    

    Get return of each call

    While($RunspaceCollection){
        Foreach($Runspace in $RunspaceCollection.ToArray()){
            If($Runspace.Runspace.IsCompleted){
            $Runspace.PowerShell.EndInvoke($Runspace.Runspace)
            $Runspace.PowerShell.Dispose()
            $RunspaceCollection.Remove($Runspace)
            }
        }
    }
    $RunspacePool.Close()
    $RunspacePool.Dispose()
    
    0 comments No comments