Skip to content

Instantly share code, notes, and snippets.

@JustinGrote
Created April 17, 2025 21:56
Show Gist options
  • Save JustinGrote/1c7b1785becc8a91f4bbe2aeaf5efd61 to your computer and use it in GitHub Desktop.
Save JustinGrote/1c7b1785becc8a91f4bbe2aeaf5efd61 to your computer and use it in GitHub Desktop.
Exchange Online Commands in Parallel
#This function gathers the login context from the .NET type and shows how you can pass that into Foreach -Parallel in order to run in parallel without using an AppID/Secret
#You must be connected to Exchange Online PowerShell
<#
.SYNOPSIS
Prepares the environment to use EXO cmdlets in parallel
#>
function Get-ExoParallelContext {
$ErrorActionPreference = 'Stop'
$exoModule = Get-Module | Where-Object Name -Like 'tmpEXO_*'
if (-not $exoModule) {
Write-Error "No Exchange Online Login Context Found" -RecommendedAction "Connect to Exchange Online PowerShell using Connect-ExchangeOnline"
}
if ($exoModule.count -gt 1) {
Write-Error "Found multiple exchange login contexts" -RecommendedAction "Use Disconnect-ExchangeOnline and only connect to one tenant"
}
$exoContext = [Microsoft.Exchange.Management.ExoPowershellSnapin.ConnectionContextFactory]::GetAllConnectionContexts()
| Where-Object ModulePath -eq (Split-Path ($exoModule[0].Path))
if (-not $exoContext) {
throw 'Module exists but context does not. This is a bug'
}
$contextSplat = @{
#Yes, this is not a secure string, blame the Exchange team for exposing it, no point hiding it now
AccessToken = ($exoContext.PowerShellTokenInfo.AuthorizationHeader -split ' ')[-1]
CommandName = ($exoModule.ExportedCommands.Keys)
UserPrincipalName = $exoContext.PowerShellTokenInfo.UserPrincipalName
}
if ($exoModule.Organization) {
$contextSplat.Organization = $exoModule.Organization
}
return $contextSplat
}
function Get-MessageTraceDetailParallel {
[CmdletBinding()]
param(
[Parameter(Mandatory = $true)]
[string]$sender
)
$context = Get-ExoParallelContext
Get-MessageTracev2 -senderaddress $sender | ForEach-Object -ThrottleLimit 10 -Parallel {
$ErrorActionPreference = 'Stop'
$exoModule = Get-Module | Where-Object Name -Like 'tmpEXO_*'
if (-not $exoModule) { Connect-ExchangeOnline -ShowBanner:$false @USING:context }
$_ | Select message,@{N='TraceDetail';E={$_ | Get-MessageTraceDetail | Out-String}}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment