Public/Connect-O365Exchange.ps1
function Connect-O365Exchange { <# .SYNOPSIS Establishes a connection to Exchange Online with proper error handling and verification. .DESCRIPTION This function provides a streamlined way to connect to Exchange Online PowerShell. It checks for existing connections, handles authentication, and verifies the connection is working properly. Includes support for different authentication methods and automatic module installation if needed. .PARAMETER UserPrincipalName The User Principal Name (UPN) to use for authentication. If not specified, will prompt for credentials or use modern authentication. .PARAMETER UseModernAuth Forces the use of modern authentication (OAuth2) instead of basic authentication. This is the default and recommended authentication method. .PARAMETER Organization The organization name (tenant) to connect to. Example: "contoso" for contoso.onmicrosoft.com .PARAMETER ShowProgress Displays detailed progress information during connection process. .PARAMETER Force Forces a new connection even if already connected to Exchange Online. Useful for switching between different tenants or accounts. .EXAMPLE Connect-O365Exchange Connects to Exchange Online using modern authentication with interactive login. .EXAMPLE Connect-O365Exchange -UserPrincipalName "admin@contoso.com" Connects using a specific user account. .EXAMPLE Connect-O365Exchange -Organization "contoso" -ShowProgress Connects to a specific organization with detailed progress information. .EXAMPLE Connect-O365Exchange -Force Forces a new connection even if already connected. .OUTPUTS System.Boolean Returns $true if connection was successful, $false otherwise. .NOTES Requires ExchangeOnlineManagement module version 2.0.5 or later. Uses modern authentication by default for enhanced security. Automatically installs the required module if not present (with user consent). .LINK https://docs.microsoft.com/en-us/powershell/exchange/connect-to-exchange-online-powershell #> [CmdletBinding()] [OutputType([System.Boolean])] param( [Parameter( HelpMessage = "User Principal Name for authentication" )] [ValidatePattern("^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$")] [string]$UserPrincipalName, [Parameter( HelpMessage = "Use modern authentication (OAuth2)" )] [switch]$UseModernAuth, [Parameter( HelpMessage = "Organization/tenant name to connect to" )] [ValidateNotNullOrEmpty()] [string]$Organization, [Parameter( HelpMessage = "Show detailed progress during connection" )] [switch]$ShowProgress, [Parameter( HelpMessage = "Force new connection even if already connected" )] [switch]$Force ) begin { Write-Verbose "Starting Exchange Online connection process" # Check if already connected (unless Force is specified) if (-not $Force) { try { $existingConnection = Get-ConnectionInformation -ErrorAction SilentlyContinue if ($existingConnection) { Write-Host "Already connected to Exchange Online:" -ForegroundColor Green Write-Host " Organization: $($existingConnection.Name)" -ForegroundColor Cyan Write-Host " User: $($existingConnection.UserPrincipalName)" -ForegroundColor Cyan Write-Host " Connection State: $($existingConnection.State)" -ForegroundColor Cyan # Test the connection is working try { $null = Get-OrganizationConfig -ErrorAction Stop Write-Host " Connection Status: Active and functional" -ForegroundColor Green return $true } catch { Write-Warning "Existing connection appears to be inactive. Attempting to reconnect..." } } } catch { Write-Verbose "No existing connection found or connection check failed" } } # Function to check and install required module function EnsureExchangeOnlineModule { if ($ShowProgress) { Write-Host "Checking for ExchangeOnlineManagement module..." -ForegroundColor Yellow } $module = Get-Module -Name ExchangeOnlineManagement -ListAvailable | Sort-Object Version -Descending | Select-Object -First 1 if (-not $module) { Write-Host "ExchangeOnlineManagement module not found." -ForegroundColor Yellow $installChoice = Read-Host "Would you like to install it now? (Y/N)" if ($installChoice -match "^[Yy]") { try { Write-Host "Installing ExchangeOnlineManagement module..." -ForegroundColor Yellow Install-Module -Name ExchangeOnlineManagement -Scope CurrentUser -Force -AllowClobber Write-Host "Module installed successfully." -ForegroundColor Green } catch { Write-Error "Failed to install ExchangeOnlineManagement module: $($_.Exception.Message)" return $false } } else { Write-Error "ExchangeOnlineManagement module is required. Please install it manually: Install-Module -Name ExchangeOnlineManagement" return $false } } else { if ($ShowProgress) { Write-Host "Found ExchangeOnlineManagement module version: $($module.Version)" -ForegroundColor Green } # Check if version is recent enough if ($module.Version -lt [Version]"2.0.5") { Write-Warning "ExchangeOnlineManagement module version $($module.Version) is outdated. Consider updating: Update-Module -Name ExchangeOnlineManagement" } } # Import the module try { Import-Module ExchangeOnlineManagement -Force if ($ShowProgress) { Write-Host "ExchangeOnlineManagement module imported successfully." -ForegroundColor Green } return $true } catch { Write-Error "Failed to import ExchangeOnlineManagement module: $($_.Exception.Message)" return $false } } # Ensure module is available if (-not (EnsureExchangeOnlineModule)) { return $false } } process { try { if ($ShowProgress) { Write-Host "Initiating Exchange Online connection..." -ForegroundColor Yellow } # Build connection parameters $connectionParams = @{ ShowBanner = $false WarningAction = 'SilentlyContinue' } # Add UPN if specified if ($UserPrincipalName) { $connectionParams['UserPrincipalName'] = $UserPrincipalName if ($ShowProgress) { Write-Host "Using specified user: $UserPrincipalName" -ForegroundColor Cyan } } # Add organization if specified if ($Organization) { $connectionParams['Organization'] = $Organization if ($ShowProgress) { Write-Host "Connecting to organization: $Organization" -ForegroundColor Cyan } } # Disconnect existing connections if Force is specified if ($Force) { try { Disconnect-ExchangeOnline -Confirm:$false -WarningAction SilentlyContinue if ($ShowProgress) { Write-Host "Disconnected from existing Exchange Online sessions." -ForegroundColor Yellow } } catch { Write-Verbose "No existing connections to disconnect or disconnect failed" } } # Attempt connection if ($ShowProgress) { Write-Host "Authenticating..." -ForegroundColor Yellow } Connect-ExchangeOnline @connectionParams # Verify connection if ($ShowProgress) { Write-Host "Verifying connection..." -ForegroundColor Yellow } $connectionInfo = Get-ConnectionInformation -ErrorAction Stop if ($connectionInfo) { # Test that we can actually run Exchange Online commands $null = Get-OrganizationConfig -ErrorAction Stop Write-Host "Successfully connected to Exchange Online!" -ForegroundColor Green Write-Host " Organization: $($connectionInfo.Name)" -ForegroundColor Cyan Write-Host " User: $($connectionInfo.UserPrincipalName)" -ForegroundColor Cyan Write-Host " Token Expiry: $($connectionInfo.TokenExpiryTimeUTC) UTC" -ForegroundColor Cyan return $true } else { throw "Connection established but verification failed" } } catch { Write-Error "Failed to connect to Exchange Online: $($_.Exception.Message)" # Provide helpful troubleshooting information Write-Host "`nTroubleshooting tips:" -ForegroundColor Yellow Write-Host "1. Ensure you have the correct permissions for Exchange Online" -ForegroundColor White Write-Host "2. Check your internet connection" -ForegroundColor White Write-Host "3. Verify your credentials are correct" -ForegroundColor White Write-Host "4. If using MFA, ensure you can receive authentication prompts" -ForegroundColor White Write-Host "5. Try running: Update-Module ExchangeOnlineManagement" -ForegroundColor White return $false } } } function Test-O365ExchangeConnection { <# .SYNOPSIS Tests the current Exchange Online connection status and functionality. .DESCRIPTION This function verifies that there is an active Exchange Online connection and that it is functioning properly by attempting to run a basic command. Provides detailed connection information and troubleshooting guidance. .PARAMETER Detailed Returns detailed connection information including token expiry and permissions. .PARAMETER Quiet Suppresses output and only returns true/false result. .EXAMPLE Test-O365ExchangeConnection Tests the connection and displays basic status information. .EXAMPLE Test-O365ExchangeConnection -Detailed Tests the connection and displays detailed information. .EXAMPLE if (Test-O365ExchangeConnection -Quiet) { "Connected" } else { "Not Connected" } Uses the function in conditional logic. .OUTPUTS System.Boolean Returns $true if connected and functional, $false otherwise. .NOTES This function is called internally by other functions in the module but can also be used standalone for connection verification. #> [CmdletBinding()] [OutputType([System.Boolean])] param( [Parameter( HelpMessage = "Return detailed connection information" )] [switch]$Detailed, [Parameter( HelpMessage = "Suppress output, return only boolean result" )] [switch]$Quiet ) try { # Check if we have any connection information $connectionInfo = Get-ConnectionInformation -ErrorAction Stop if (-not $connectionInfo) { if (-not $Quiet) { Write-Host "No Exchange Online connection found." -ForegroundColor Red Write-Host "Run Connect-O365Exchange to establish a connection." -ForegroundColor Yellow } return $false } # Test that the connection actually works $null = Get-OrganizationConfig -ErrorAction Stop if (-not $Quiet) { Write-Host "Exchange Online connection is active and functional." -ForegroundColor Green if ($Detailed) { Write-Host "`nConnection Details:" -ForegroundColor Cyan Write-Host " Organization: $($connectionInfo.Name)" -ForegroundColor White Write-Host " User: $($connectionInfo.UserPrincipalName)" -ForegroundColor White Write-Host " Connection ID: $($connectionInfo.ConnectionId)" -ForegroundColor White Write-Host " State: $($connectionInfo.State)" -ForegroundColor White Write-Host " Token Expiry: $($connectionInfo.TokenExpiryTimeUTC) UTC" -ForegroundColor White Write-Host " App ID: $($connectionInfo.AppId)" -ForegroundColor White Write-Host " Tenant ID: $($connectionInfo.TenantId)" -ForegroundColor White # Check token expiry if ($connectionInfo.TokenExpiryTimeUTC) { $timeUntilExpiry = $connectionInfo.TokenExpiryTimeUTC - (Get-Date).ToUniversalTime() if ($timeUntilExpiry.TotalMinutes -lt 30) { Write-Host " Warning: Token expires in $([math]::Round($timeUntilExpiry.TotalMinutes, 1)) minutes" -ForegroundColor Yellow } } } } return $true } catch { if (-not $Quiet) { Write-Host "Exchange Online connection test failed: $($_.Exception.Message)" -ForegroundColor Red Write-Host "Run Connect-O365Exchange to establish a connection." -ForegroundColor Yellow } return $false } } function Disconnect-O365Exchange { <# .SYNOPSIS Safely disconnects from Exchange Online and cleans up sessions. .DESCRIPTION This function properly disconnects from Exchange Online, cleaning up any active sessions and providing confirmation of disconnection. Includes error handling for cases where no connection exists. .PARAMETER Force Disconnects without confirmation prompt. .PARAMETER Quiet Suppresses output messages. .EXAMPLE Disconnect-O365Exchange Disconnects from Exchange Online with confirmation. .EXAMPLE Disconnect-O365Exchange -Force Disconnects immediately without confirmation. .OUTPUTS System.Boolean Returns $true if disconnection was successful, $false otherwise. .NOTES Always run this function when finished with Exchange Online operations to properly clean up sessions and free resources. #> [CmdletBinding()] [OutputType([System.Boolean])] param( [Parameter( HelpMessage = "Disconnect without confirmation" )] [switch]$Force, [Parameter( HelpMessage = "Suppress output messages" )] [switch]$Quiet ) try { # Check if we have an active connection $connectionInfo = Get-ConnectionInformation -ErrorAction SilentlyContinue if (-not $connectionInfo) { if (-not $Quiet) { Write-Host "No active Exchange Online connection found." -ForegroundColor Yellow } return $true } if (-not $Quiet) { Write-Host "Disconnecting from Exchange Online..." -ForegroundColor Yellow if (-not $Force) { Write-Host "Organization: $($connectionInfo.Name)" -ForegroundColor Cyan Write-Host "User: $($connectionInfo.UserPrincipalName)" -ForegroundColor Cyan } } # Disconnect with appropriate parameters $disconnectParams = @{ WarningAction = 'SilentlyContinue' } if ($Force) { $disconnectParams['Confirm'] = $false } Disconnect-ExchangeOnline @disconnectParams if (-not $Quiet) { Write-Host "Successfully disconnected from Exchange Online." -ForegroundColor Green } return $true } catch { if (-not $Quiet) { Write-Error "Failed to disconnect from Exchange Online: $($_.Exception.Message)" } return $false } } |