Src/Private/Connect-ExoSession.ps1
|
function Connect-ExoSession { <# .SYNOPSIS Establishes authenticated connections to Exchange Online for reporting. .DESCRIPTION Connects to: 1. Exchange Online PowerShell (EXO v3 module) - for mailbox, transport, EOP cmdlets 2. Security & Compliance PowerShell (IPPSSession) - for Get-ProtectionAlert, Get-CompliancePolicy, Get-RetentionCompliancePolicy, Get-LabelPolicy, etc. 3. Microsoft Graph - for supplementary tenant identity data Required Roles (any one of): - Global Administrator - Global Reader - Exchange Administrator + Security Reader - Security Administrator - Compliance Administrator .NOTES Version: 0.1.1 Author: Pai Wei Sing .EXAMPLE Connect-ExoSession -UserPrincipalName 'admin@contoso.onmicrosoft.com' #> [CmdletBinding()] param ( [Parameter(Mandatory)] [string]$UserPrincipalName ) if (-not (Test-UserPrincipalName -UserPrincipalName $UserPrincipalName)) { $errorMsg = "Invalid User Principal Name format: '$UserPrincipalName'. Expected format: user@domain.com" Write-TranscriptLog $errorMsg 'ERROR' 'AUTH' throw $errorMsg } Write-TranscriptLog "Starting connection to Exchange Online: $UserPrincipalName" 'INFO' 'AUTH' #region Exchange Online PowerShell $ExistingExo = $null try { $ExistingExo = Get-ConnectionInformation -ErrorAction SilentlyContinue } catch { } if ($ExistingExo) { Write-TranscriptLog "Reusing existing Exchange Online session" 'SUCCESS' 'AUTH' Write-Host " - Exchange Online: existing session reused." -ForegroundColor Cyan } else { Write-Host " - Connecting to Exchange Online..." Write-TranscriptLog "Connecting to Exchange Online PowerShell (EXO v3)" 'INFO' 'AUTH' Invoke-WithRetry -ScriptBlock { Connect-ExchangeOnline -UserPrincipalName $UserPrincipalName -ShowBanner:$false -ErrorAction Stop } -OperationName 'Connect to Exchange Online' Write-Host " - Exchange Online connected successfully." -ForegroundColor Green Write-TranscriptLog "Exchange Online connection established" 'SUCCESS' 'AUTH' } #endregion #region Security & Compliance PowerShell (IPPSSession) # Required for: Get-ProtectionAlert, Get-CompliancePolicy, Get-RetentionCompliancePolicy, # Get-LabelPolicy, Get-DlpCompliancePolicy and other Purview/Defender cmdlets. # Uses the same UPN/interactive auth as Exchange Online. # If this fails we warn but do not abort — EXO-only sections will still work. $script:IPPSConnected = $false try { # Check if IPPSSession is already active by testing a compliance cmdlet $null = Get-ProtectionAlert -ResultSize 1 -ErrorAction Stop $script:IPPSConnected = $true Write-Host " - Security & Compliance (IPPSSession): existing session detected." -ForegroundColor Cyan Write-TranscriptLog "Reusing existing IPPSSession" 'SUCCESS' 'AUTH' } catch { # Not connected yet — attempt Connect-IPPSSession try { Write-Host " - Connecting to Security and Compliance PowerShell (IPPSSession)..." Write-TranscriptLog "Connecting to IPPSSession for compliance cmdlets" 'INFO' 'AUTH' Invoke-WithRetry -ScriptBlock { Connect-IPPSSession -UserPrincipalName $UserPrincipalName -ShowBanner:$false -ErrorAction Stop } -OperationName 'Connect to IPPSSession (Security & Compliance)' $script:IPPSConnected = $true Write-Host " - Security and Compliance PowerShell connected successfully." -ForegroundColor Green Write-TranscriptLog "IPPSSession established successfully" 'SUCCESS' 'AUTH' } catch { $script:IPPSConnected = $false Write-Host " - Security and Compliance PowerShell connection failed (alert policies and compliance data will be skipped): $($_.Exception.Message)" -ForegroundColor Yellow Write-TranscriptLog "IPPSSession connection failed: $($_.Exception.Message)" 'WARNING' 'AUTH' } } #endregion #region Microsoft Graph (for supplementary data) $ExistingGraph = $null try { $ExistingGraph = Get-MgContext -ErrorAction SilentlyContinue } catch { } if ($ExistingGraph -and $ExistingGraph.TenantId) { Write-TranscriptLog "Reusing existing Microsoft Graph session (TenantId: $($ExistingGraph.TenantId))" 'SUCCESS' 'AUTH' } else { Write-Host " - Connecting to Microsoft Graph (supplementary)..." $GraphScopes = @( 'Organization.Read.All' 'Domain.Read.All' 'Policy.Read.All' 'Reports.Read.All' ) try { Invoke-WithRetry -ScriptBlock { Connect-MgGraph -Scopes $GraphScopes -ErrorAction Stop } -OperationName 'Connect to Microsoft Graph (EXO supplementary)' Write-Host " - Microsoft Graph connected successfully." -ForegroundColor Green } catch { Write-Warning " - Microsoft Graph connection failed (some data may be unavailable): $($_.Exception.Message)" } } #endregion } function Disconnect-ExoSession { <# .SYNOPSIS Cleanly disconnects Exchange Online, IPPSSession, and Microsoft Graph sessions. #> [CmdletBinding()] param() Write-TranscriptLog "Disconnecting Exchange Online, IPPSSession, and Graph sessions" 'INFO' 'AUTH' try { $null = Disconnect-ExchangeOnline -Confirm:$false -ErrorAction SilentlyContinue Write-TranscriptLog "Exchange Online session disconnected" 'SUCCESS' 'AUTH' } catch { Write-TranscriptLog "Exchange Online disconnect warning: $($_.Exception.Message)" 'WARNING' 'AUTH' } # Disconnect IPPSSession if it was established if ($script:IPPSConnected) { try { $null = Disconnect-ExchangeOnline -Confirm:$false -ErrorAction SilentlyContinue Write-TranscriptLog "IPPSSession disconnected" 'SUCCESS' 'AUTH' } catch { Write-TranscriptLog "IPPSSession disconnect warning: $($_.Exception.Message)" 'WARNING' 'AUTH' } } try { $GraphCtx = Get-MgContext -ErrorAction SilentlyContinue if ($GraphCtx) { $null = Disconnect-MgGraph -ErrorAction SilentlyContinue Write-TranscriptLog "Microsoft Graph session disconnected" 'SUCCESS' 'AUTH' } } catch { Write-TranscriptLog "Graph disconnect warning: $($_.Exception.Message)" 'WARNING' 'AUTH' } Write-Host " - Session disconnected." -ForegroundColor Green } |