Microsoft 365 Exchange Online PowerShell Connection Guide
Table of Contents
Part 1: Exchange Online PowerShell
Part 2: Microsoft Entra ID (Directory Services) ⚠️ CRITICAL
Part 3: Additional Services
Quick Start
Jump to Your Scenario:
- 🎯 Interactive admin tasks → Method 1
- 🤖 Automation scripts → Method 2: Certificate Auth
- ☁️ Azure Automation → Method 3: Managed Identity
- 👤 Managing users/groups → Microsoft Graph
Part 1: Exchange Online PowerShell
Summary
This guide covers all modern methods for connecting to Microsoft Exchange Online using the Exchange Online PowerShell V3 module. All legacy authentication methods, including Basic Authentication and Remote PowerShell (RPS), have been deprecated and are no longer supported.
Prerequisites
Before connecting to Exchange Online PowerShell, ensure the following requirements are met:
System Requirements
- PowerShell Version: Windows PowerShell 5.1 or PowerShell 7.4+ (recommended)
- Operating Systems: Windows, macOS, or Linux
- Network: TCP port 443 open to Microsoft 365 endpoints
- Modules: PowerShellGet and PackageManagement modules (required for REST API connections)
Administrative Requirements
- Exchange Online administrator credentials or appropriate RBAC role
- Account must be enabled for PowerShell access
- For MFA scenarios: Multi-factor authentication configured on the account
- For automation scenarios: Azure AD app registration with appropriate permissions
Execution Policy
Set your PowerShell execution policy to allow script execution:
Set-ExecutionPolicy RemoteSigned -Scope CurrentUser
Installing the Exchange Online PowerShell Module
Step 1: Check for Existing Module
First, verify if the module is already installed:
Get-Module -ListAvailable -Name ExchangeOnlineManagement
Step 2: Install or Update the Module
To install the latest version of the Exchange Online Management module:
Install-Module -Name ExchangeOnlineManagement -Force -AllowClobber
If the module is already installed, update it to the latest version:
Update-Module -Name ExchangeOnlineManagement
To install for the current user only (no administrator rights required):
Install-Module -Name ExchangeOnlineManagement -Scope CurrentUser -Force
Step 3: Verify Installation
Confirm the module is installed correctly:
Get-InstalledModule -Name ExchangeOnlineManagement
Expected Output:
Version Name Repository Description
------- ---- ---------- -----------
3.9.2 ExchangeOnlineManagement PSGallery Exchange Online PowerShell V3 Module
Step 4: Import the Module (Optional)
While the module loads automatically when you run connection commands, you can manually import it:
Import-Module ExchangeOnlineManagement
Connection Methods
Method 1: Interactive Authentication (with MFA Support)
This is the recommended method for manual administrative tasks. It supports multi-factor authentication and provides an interactive sign-in experience.
Basic Interactive Connection
Connect-ExchangeOnline -UserPrincipalName admin@365adviser.com
What happens:
- A browser window or authentication prompt appears
- Enter your password
- Complete MFA verification if enabled (Microsoft Authenticator app, SMS code, etc.)
- Upon success, you're connected to Exchange Online PowerShell
Interactive Connection Without Showing Banner
To suppress the connection banner message:
Connect-ExchangeOnline -UserPrincipalName admin@365adviser.com -ShowBanner:$false
Connection with Cmdlet Help Loading
To enable Get-Help for Exchange Online cmdlets (version 3.7.0 or later):
Connect-ExchangeOnline -UserPrincipalName admin@365adviser.com -LoadCmdletHelp
💡 Note: Starting with version 3.7.0, cmdlet help is not loaded by default to improve connection speed. Use
-LoadCmdletHelpwhen needed.
Complete Example Script
# Import the module
Import-Module ExchangeOnlineManagement
# Connect to Exchange Online
Connect-ExchangeOnline -UserPrincipalName admin@365adviser.com -ShowBanner:$false
# Verify connection
Get-ConnectionInformation
# Run Exchange Online commands
Get-Mailbox -ResultSize 10
# Disconnect when finished
Disconnect-ExchangeOnline -Confirm:$false
Method 2: Certificate-Based Authentication (for Automation)
Certificate-based authentication enables unattended scripts and automation scenarios without storing user credentials. This is the recommended approach for automated tasks, scheduled scripts, and CI/CD pipelines.
Prerequisites for Certificate-Based Authentication
- Microsoft Entra ID (Azure AD) app registration
- Self-signed or CA-issued certificate
- Exchange Administrator role assigned to the app
- API permissions configured
Step 1: Create a Self-Signed Certificate
Generate a self-signed certificate using PowerShell:
# Define certificate parameters
$CertificateName = "ExchangeOnlineAutomation"
$CertificateSubject = "CN=$CertificateName"
$ExpirationYears = 2
# Create the certificate
$Certificate = New-SelfSignedCertificate `
-Subject $CertificateSubject `
-CertStoreLocation "Cert:\CurrentUser\My" `
-KeyExportPolicy Exportable `
-KeySpec Signature `
-KeyLength 2048 `
-KeyAlgorithm RSA `
-HashAlgorithm SHA256 `
-NotAfter (Get-Date).AddYears($ExpirationYears)
# Display certificate thumbprint (save this for later)
Write-Host "Certificate Thumbprint: $($Certificate.Thumbprint)" -ForegroundColor Green
⚠️ Compatibility Note: If you encounter certificate authentication errors, create the certificate with the Microsoft RSA SChannel Cryptographic Provider:
$Certificate = New-SelfSignedCertificate `
-Subject $CertificateSubject `
-CertStoreLocation "Cert:\CurrentUser\My" `
-Provider "Microsoft RSA SChannel Cryptographic Provider" `
-KeyExportPolicy Exportable `
-KeySpec Signature `
-KeyLength 2048 `
-KeyAlgorithm RSA `
-HashAlgorithm SHA256 `
-NotAfter (Get-Date).AddYears($ExpirationYears)
Step 2: Export the Certificate
Export the certificate for upload to Azure AD:
# Export certificate to .cer format (public key only)
$CertificatePath = "C:\Certificates\ExchangeOnlineAutomation.cer"
Export-Certificate -Cert $Certificate -FilePath $CertificatePath
# Export certificate with private key to .pfx format (for backup or other machines)
$PfxPassword = ConvertTo-SecureString -String "SecurePassword123!" -AsPlainText -Force
$PfxPath = "C:\Certificates\ExchangeOnlineAutomation.pfx"
Export-PfxCertificate -Cert $Certificate -FilePath $PfxPath -Password $PfxPassword
🔐 Security Best Practice: Store the .pfx file securely (Azure Key Vault, encrypted storage) and never commit to source control.
Step 3: Register an Application in Microsoft Entra ID
Option A: Using Azure Portal
- Navigate to Azure Portal > Microsoft Entra ID > App registrations
- Click New registration
- Provide an application name (e.g., "Exchange Online PowerShell Automation")
- Select Accounts in this organizational directory only
- Click Register
- Copy and save the Application (client) ID and Directory (tenant) ID
Option B: Using PowerShell
# Install Microsoft Graph module if not already installed
Install-Module Microsoft.Graph.Applications -Scope CurrentUser -Force
# Connect to Microsoft Graph
Connect-MgGraph -Scopes "Application.ReadWrite.All"
# Create the application registration
$AppName = "Exchange Online PowerShell Automation"
$App = New-MgApplication -DisplayName $AppName
# Display the Application ID
Write-Host "Application (Client) ID: $($App.AppId)" -ForegroundColor Green
Write-Host "Object ID: $($App.Id)" -ForegroundColor Yellow
# Save for later use
$AppId = $App.AppId
Step 4: Upload Certificate to the Application
Option A: Using Azure Portal
- In your app registration, go to Certificates & secrets
- Click Upload certificate
- Upload the .cer file created in Step 2
- Note the certificate Thumbprint displayed
Option B: Using PowerShell
# Load the certificate
$Cert = Get-ChildItem Cert:\CurrentUser\My | Where-Object {$_.Subject -eq "CN=$CertificateName"}
# Get the certificate value
$CertValue = [System.Convert]::ToBase64String($Cert.GetRawCertData())
# Add certificate to application
$KeyCredential = @{
Type = "AsymmetricX509Cert"
Usage = "Verify"
Key = [System.Text.Encoding]::ASCII.GetBytes($CertValue)
}
Update-MgApplication -ApplicationId $App.Id -KeyCredentials @($KeyCredential)
Write-Host "Certificate uploaded successfully" -ForegroundColor Green
Step 5: Configure API Permissions
Option A: Using Azure Portal
- In your app registration, go to API permissions
- Click Add a permission
- Select APIs my organization uses
- Search for and select Office 365 Exchange Online (exact name)
- Select Application permissions
- Expand Exchange and select Exchange.ManageAsApp
- Click Add permissions
- Click Grant admin consent for [your organization]
- Click Yes to confirm
Option B: Using PowerShell
# Connect to Microsoft Graph with appropriate permissions
Connect-MgGraph -Scopes "AppRoleAssignment.ReadWrite.All", "Application.Read.All"
# Get the Office 365 Exchange Online service principal
$ExoServicePrincipal = Get-MgServicePrincipal -Filter "AppId eq '00000002-0000-0ff1-ce00-000000000000'"
# Get the Exchange.ManageAsApp role
$AppRole = $ExoServicePrincipal.AppRoles | Where-Object {$_.Value -eq "Exchange.ManageAsApp"}
# Get your application's service principal
$AppServicePrincipal = Get-MgServicePrincipal -Filter "AppId eq '$AppId'"
# Grant the API permission
New-MgServicePrincipalAppRoleAssignment `
-ServicePrincipalId $AppServicePrincipal.Id `
-PrincipalId $AppServicePrincipal.Id `
-AppRoleId $AppRole.Id `
-ResourceId $ExoServicePrincipal.Id
Write-Host "Exchange.ManageAsApp permission granted successfully" -ForegroundColor Green
Step 6: Assign Exchange Administrator Role
The application needs the Exchange Administrator role to perform administrative tasks.
Option A: Using Azure Portal
- Navigate to Microsoft Entra ID > Roles and administrators
- Search for and select Exchange Administrator
- Click Add assignments
- Search for your application name
- Select it and click Add
Option B: Using PowerShell
# Install Microsoft Graph module if not already installed
Install-Module Microsoft.Graph.Identity.DirectoryManagement -Scope CurrentUser -Force
# Connect to Microsoft Graph
Connect-MgGraph -Scopes "RoleManagement.ReadWrite.Directory"
# Get the service principal for your app
$AppId = "YOUR-APP-ID-HERE"
$ServicePrincipal = Get-MgServicePrincipal -Filter "AppId eq '$AppId'"
# Get the Exchange Administrator role definition
$RoleDefinition = Get-MgRoleManagementDirectoryRoleDefinition `
-Filter "DisplayName eq 'Exchange Administrator'"
# Check if role needs to be activated first
$Role = Get-MgDirectoryRole -Filter "RoleTemplateId eq '$($RoleDefinition.Id)'"
if (-not $Role) {
# Activate the role if not already activated
$Role = New-MgDirectoryRole -RoleTemplateId $RoleDefinition.Id
}
# Assign the role to the service principal
New-MgRoleManagementDirectoryRoleAssignment `
-DirectoryScopeId "/" `
-RoleDefinitionId $RoleDefinition.Id `
-PrincipalId $ServicePrincipal.Id
Write-Host "Exchange Administrator role assigned successfully" -ForegroundColor Green
Step 7: Connect Using Certificate
Method A: Using Certificate Thumbprint (Recommended)
# Define connection parameters
$AppId = "your-app-client-id"
$TenantId = "yourtenant.onmicrosoft.com" # or tenant GUID
$CertThumbprint = "your-cert-thumbprint"
# Connect to Exchange Online
Connect-ExchangeOnline -CertificateThumbprint $CertThumbprint `
-AppId $AppId `
-Organization $TenantId `
-ShowBanner:$false
# Verify connection
Get-ConnectionInformation
# Test with a command
Get-Mailbox -ResultSize 5
# Disconnect when finished
Disconnect-ExchangeOnline -Confirm:$false
Method B: Using Certificate Object
# Load certificate from store
$Cert = Get-ChildItem Cert:\CurrentUser\My\<YourCertThumbprint>
# Or find by subject
$Cert = Get-ChildItem Cert:\CurrentUser\My | Where-Object {$_.Subject -eq "CN=ExchangeOnlineAutomation"}
# Connect using certificate object
Connect-ExchangeOnline -Certificate $Cert `
-AppId $AppId `
-Organization $TenantId `
-ShowBanner:$false
Method C: Using Certificate File (PFX) - Not Recommended for Production
# Note: Requires managing certificate password - not ideal for automation
$CertPassword = Get-Credential -UserName "Certificate Password" -Message "Enter certificate password"
Connect-ExchangeOnline -CertificateFilePath "C:\Certs\ExchangeAutomation.pfx" `
-CertificatePassword $CertPassword.Password `
-AppId $AppId `
-Organization $TenantId
Complete Automation Script Example
<#
.SYNOPSIS
Exchange Online automation script with certificate-based authentication
.DESCRIPTION
Best practice template with error handling and logging
.NOTES
Author: Your Name
Date: January 2026
Version: 1.0
#>
# Import required module
Import-Module ExchangeOnlineManagement -ErrorAction Stop
# Define parameters
$AppId = "YOUR-APP-ID"
$CertificateThumbprint = "YOUR-THUMBPRINT"
$Organization = "contoso.onmicrosoft.com"
$LogFile = "C:\Logs\ExchangeScript_$(Get-Date -Format 'yyyyMMdd_HHmmss').log"
# Function for logging
function Write-Log {
param([string]$Message)
$Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$LogMessage = "[$Timestamp] $Message"
Write-Output $LogMessage
Add-Content -Path $LogFile -Value $LogMessage
}
try {
Write-Log "Script started"
# Connect
Write-Log "Connecting to Exchange Online..."
Connect-ExchangeOnline `
-AppId $AppId `
-CertificateThumbprint $CertificateThumbprint `
-Organization $Organization `
-ShowBanner:$false `
-ErrorAction Stop
Write-Log "Connected successfully"
# Perform operations
Write-Log "Retrieving mailbox information..."
$Mailboxes = Get-EXOMailbox -ResultSize 100 |
Select-Object DisplayName, PrimarySmtpAddress, WhenCreated
Write-Log "Retrieved $($Mailboxes.Count) mailboxes"
# Export results
$OutputFile = "C:\Reports\Mailboxes_$(Get-Date -Format 'yyyyMMdd').csv"
$Mailboxes | Export-Csv -Path $OutputFile -NoTypeInformation
Write-Log "Results exported to $OutputFile"
Write-Log "Operations completed successfully"
} catch {
Write-Log "ERROR: $_"
Write-Error $_
exit 1
} finally {
# Always disconnect
Write-Log "Disconnecting from Exchange Online..."
Disconnect-ExchangeOnline -Confirm:$false -ErrorAction SilentlyContinue
Write-Log "Script completed"
}
Method 3: Managed Identity (for Azure Automation)
Managed Identity provides the most secure authentication method for Azure-hosted resources by eliminating the need to manage certificates or secrets. This is the recommended approach for Azure Automation, Azure Functions, and Azure VMs.
Prerequisites
- Azure Automation account, Azure VM, Azure Function, or Azure VM Scale Set
- Exchange Online PowerShell V3 module (Version 3.0.0 or later)
- System-assigned or user-assigned managed identity enabled on the resource
Step 1: Enable Managed Identity on Azure Resource
For Azure Automation Account
Option A: Using Azure Portal
- Navigate to your Automation Account in Azure Portal
- Under Account Settings, select Identity
- Set System assigned status to On
- Click Save
- Note the Object (principal) ID
Option B: Using PowerShell
# Connect to Azure
Connect-AzAccount
# Define variables
$ResourceGroupName = "YourResourceGroup"
$AutomationAccountName = "YourAutomationAccount"
# Enable system-assigned managed identity
Set-AzAutomationAccount -Name $AutomationAccountName `
-ResourceGroupName $ResourceGroupName `
-AssignSystemIdentity
# Get the managed identity principal ID
$AutomationAccount = Get-AzAutomationAccount -Name $AutomationAccountName `
-ResourceGroupName $ResourceGroupName
$PrincipalId = $AutomationAccount.Identity.PrincipalId
Write-Host "Managed Identity enabled. Principal ID: $PrincipalId" -ForegroundColor Green
For Azure VM
# Enable system-assigned managed identity on VM
$VM = Get-AzVM -ResourceGroupName "YourResourceGroup" -Name "YourVMName"
Update-AzVM -ResourceGroupName "YourResourceGroup" -VM $VM -IdentityType SystemAssigned
Step 2: Install Exchange Online Module in Automation Account
# Define variables
$ResourceGroupName = "YourResourceGroup"
$AutomationAccountName = "YourAutomationAccount"
$ModuleName = "ExchangeOnlineManagement"
# Add module to Automation Account
New-AzAutomationModule `
-ResourceGroupName $ResourceGroupName `
-AutomationAccountName $AutomationAccountName `
-Name $ModuleName `
-ContentLinkUri "https://www.powershellgallery.com/api/v2/package/$ModuleName"
# Check module installation status
Get-AzAutomationModule -AutomationAccountName $AutomationAccountName `
-ResourceGroupName $ResourceGroupName `
-Name $ModuleName
⏱️ Note: Module installation can take 5-10 minutes. Wait for ProvisioningState to show Succeeded before proceeding.
Step 3: Grant Exchange.ManageAsApp Permission
# Install Microsoft Graph modules
Install-Module Microsoft.Graph.Authentication -Scope CurrentUser -Force
Install-Module Microsoft.Graph.Applications -Scope CurrentUser -Force
# Connect to Microsoft Graph
Connect-MgGraph -Scopes "AppRoleAssignment.ReadWrite.All", "Application.Read.All"
# Get the managed identity service principal
# For Automation Account:
$ManagedIdentity = Get-MgServicePrincipal -Filter "displayName eq '$AutomationAccountName'"
# For VM:
# $ManagedIdentity = Get-MgServicePrincipal -Filter "displayName eq 'YourVMName'"
# Get Exchange Online service principal
$ExoServicePrincipal = Get-MgServicePrincipal -Filter "AppId eq '00000002-0000-0ff1-ce00-000000000000'"
# Get the Exchange.ManageAsApp role ID (always the same)
$AppRoleId = "dc50a0fb-09a3-484d-be87-e023b12c6440"
# Grant the permission
New-MgServicePrincipalAppRoleAssignment `
-ServicePrincipalId $ManagedIdentity.Id `
-PrincipalId $ManagedIdentity.Id `
-AppRoleId $AppRoleId `
-ResourceId $ExoServicePrincipal.Id
Write-Host "Exchange.ManageAsApp permission granted" -ForegroundColor Green
Step 4: Assign Exchange Administrator Role
# Install required module
Install-Module Microsoft.Graph.Identity.DirectoryManagement -Scope CurrentUser -Force
# Connect with appropriate scope
Connect-MgGraph -Scopes "RoleManagement.ReadWrite.Directory"
# Get Exchange Administrator role
$RoleDefinition = Get-MgRoleManagementDirectoryRoleDefinition `
-Filter "DisplayName eq 'Exchange Administrator'"
# Check if role is activated
$Role = Get-MgDirectoryRole -Filter "RoleTemplateId eq '$($RoleDefinition.Id)'"
if (-not $Role) {
$Role = New-MgDirectoryRole -RoleTemplateId $RoleDefinition.Id
}
# Assign role to managed identity
New-MgRoleManagementDirectoryRoleAssignment `
-PrincipalId $ManagedIdentity.Id `
-RoleDefinitionId $RoleDefinition.Id `
-DirectoryScopeId "/"
Write-Host "Exchange Administrator role assigned" -ForegroundColor Green
⏱️ Important: Wait 5-10 minutes after role assignment for permissions to propagate.
Step 5: Create and Test Runbook
Create Runbook in Azure Portal
- Navigate to your Automation Account
- Go to Process Automation > Runbooks
- Click Create a runbook
- Name: "Exchange-DailyReport"
- Runbook type: PowerShell
- Runtime version: 7.2 (recommended)
- Click Create
Runbook Script for System-Assigned Managed Identity
<#
.SYNOPSIS
Exchange Online runbook using managed identity
.DESCRIPTION
Connects to Exchange Online using the Automation Account's managed identity
and performs reporting tasks
#>
# Define organization
$Organization = "contoso.onmicrosoft.com"
try {
Write-Output "Connecting to Exchange Online with Managed Identity..."
# Connect using managed identity
Connect-ExchangeOnline -ManagedIdentity -Organization $Organization -ErrorAction Stop
Write-Output "Connected successfully"
# Verify connection
$ConnectionInfo = Get-ConnectionInformation
Write-Output "Connection Details:"
Write-Output " Organization: $($ConnectionInfo.Organization)"
Write-Output " State: $($ConnectionInfo.State)"
# Get mailbox statistics
Write-Output "`nRetrieving mailbox information..."
$Mailboxes = Get-EXOMailbox -ResultSize 10 |
Select-Object DisplayName, PrimarySmtpAddress, @{
Name='SizeMB'
Expression={ (Get-EXOMailboxStatistics $_.Identity).TotalItemSize.Value.ToMB() }
}
Write-Output "`nMailbox Report:"
$Mailboxes | Format-Table -AutoSize
Write-Output "`nTotal mailboxes retrieved: $($Mailboxes.Count)"
} catch {
Write-Error "An error occurred: $_"
throw
} finally {
# Disconnect
Write-Output "`nDisconnecting from Exchange Online..."
Disconnect-ExchangeOnline -Confirm:$false -ErrorAction SilentlyContinue
Write-Output "Script completed"
}
Runbook Script for User-Assigned Managed Identity
# Define parameters
$Organization = "contoso.onmicrosoft.com"
$UserAssignedIdentityClientId = "your-user-assigned-identity-client-id"
try {
Write-Output "Connecting with User-Assigned Managed Identity..."
# Connect with user-assigned managed identity
Connect-ExchangeOnline -ManagedIdentity `
-Organization $Organization `
-ManagedIdentityAccountId $UserAssignedIdentityClientId `
-ErrorAction Stop
# Rest of script...
} catch {
Write-Error "An error occurred: $_"
throw
}
Test the Runbook
- Click Test pane in your runbook
- Click Start
- Monitor the output
- Verify successful connection and data retrieval
Advanced: Using Managed Identity from Azure VM
# Connect from Azure VM with managed identity
Connect-ExchangeOnline -ManagedIdentity -Organization "contoso.onmicrosoft.com"
# Verify connection
Get-ConnectionInformation
# Run commands
Get-Mailbox -ResultSize 10
# Disconnect
Disconnect-ExchangeOnline -Confirm:$false
Troubleshooting Managed Identity
Issue: "UnAuthorized" error when connecting
Solutions:
- Verify managed identity is enabled on the resource
- Confirm Exchange.ManageAsApp permission is granted
- Verify Exchange Administrator role is assigned
- Wait 5-10 minutes after role assignment
- Check Azure AD audit logs for permission changes
Issue: Module not found in runbook
Solutions:
- Verify module installation status in Automation Account
- Ensure module ProvisioningState is Succeeded
- Use PowerShell runtime version 7.2 or later
- Re-import the module if necessary
Method 4: Non-Interactive Authentication (Legacy)
⚠️ NOT RECOMMENDED: This method is less secure and should only be used in trusted lab environments. Certificate-based or managed identity authentication should be used instead for production automation.
This method allows connection without interactive prompts using stored credentials. Requirements:
- MFA must be disabled on the account
- Account must use password-based authentication
- Not suitable for production environments
Using Stored Credentials
# Prompt for credentials and store them
$UserCredential = Get-Credential
# Connect using stored credentials
Connect-ExchangeOnline -Credential $UserCredential -ShowBanner:$false
Using Encrypted Password (Still Not Recommended)
# Convert password to secure string
$Password = ConvertTo-SecureString -String 'YourPassword' -AsPlainText -Force
# Create credential object
$Credential = New-Object System.Management.Automation.PSCredential ('admin@365adviser.com', $Password)
# Connect to Exchange Online
Connect-ExchangeOnline -Credential $Credential -ShowBanner:$false
🔐 Security Warning: Never hardcode passwords in scripts or commit them to source control. Use Azure Key Vault or similar secure credential storage solutions for production environments.
Performance Optimization
Exchange Online PowerShell V3 includes optimized "Get-EXO*" cmdlets that provide significantly better performance for large-scale operations.
Standard vs. Optimized Cmdlets
# Standard cmdlet (slower for large datasets, returns all properties)
$Mailboxes = Get-Mailbox -ResultSize Unlimited
# Optimized EXO cmdlet (faster, returns focused property set)
$Mailboxes = Get-EXOMailbox -ResultSize Unlimited
# Using property sets for specific needs
$Mailboxes = Get-EXOMailbox -PropertySets All -ResultSize Unlimited
$Mailboxes = Get-EXOMailbox -PropertySets Minimum -ResultSize Unlimited
# Retrieve only specific properties needed
$Mailboxes = Get-EXOMailbox -Properties DisplayName, UserPrincipalName, ProhibitSendQuota -ResultSize Unlimited
Available Optimized Cmdlets
| Optimized Cmdlet | Standard Equivalent | Use Case |
|---|---|---|
| Get-EXOMailbox | Get-Mailbox | Large mailbox queries |
| Get-EXORecipient | Get-Recipient | Bulk recipient operations |
| Get-EXOCasMailbox | Get-CASMailbox | Client access settings |
| Get-EXOMailboxStatistics | Get-MailboxStatistics | Mailbox size/stats |
| Get-EXOMailboxFolderStatistics | Get-MailboxFolderStatistics | Folder-level stats |
| Get-EXOMailboxPermission | Get-MailboxPermission | Permission queries |
| Get-EXORecipientPermission | Get-RecipientPermission | Recipient permissions |
| Get-EXOMobileDeviceStatistics | Get-MobileDeviceStatistics | Mobile device info |
Performance Best Practices
- Use optimized cmdlets for operations retrieving 100+ objects
- Limit result sets with
-ResultSizeparameter - Request only needed properties using
-Propertiesparameter - Use property sets for common scenarios (Minimum, Archive, etc.)
- Batch operations in automation to reduce API calls
- Implement paging for very large datasets
Troubleshooting Common Issues
Issue 1: Module Import Errors
Problem: "The term 'Connect-ExchangeOnline' is not recognized"
Solution:
# Verify module is installed
Get-Module -ListAvailable -Name ExchangeOnlineManagement
# If not found, install it
Install-Module -Name ExchangeOnlineManagement -Force
# Import module manually
Import-Module ExchangeOnlineManagement -Force
# Verify cmdlets are available
Get-Command -Module ExchangeOnlineManagement
Issue 2: Authentication Failures
Problem: "Authorization_RequestDenied" or "Access Denied" errors
Solution:
# Verify account has Exchange admin rights
# Check role assignments in Microsoft Entra admin center
# For interactive: Ensure account can sign in to portal.office.com
# For app-only: Verify API permissions and admin consent
Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $ServicePrincipalId
# Check if Exchange Administrator role is assigned
Get-MgRoleManagementDirectoryRoleAssignment -Filter "principalId eq '$PrincipalId'"
Issue 3: Certificate Authentication Failures
Problem: "Certificate not found" or "Keyset does not exist"
Solution:
# Verify certificate is installed in correct store
Get-ChildItem -Path Cert:\CurrentUser\My | Where-Object {$_.Thumbprint -eq "YourThumbprint"}
# Check certificate has private key
$Cert = Get-ChildItem Cert:\CurrentUser\My\YourThumbprint
$Cert.HasPrivateKey # Should return True
# For scheduled tasks: Verify service account has access to certificate private key
# Run certlm.msc or certmgr.msc and check private key permissions
# Verify certificate hasn't expired
$Cert.NotAfter # Should be future date
Issue 4: Managed Identity Connection Failures
Problem: "UnAuthorized" when using -ManagedIdentity
Solution:
# Step 1: Verify managed identity is enabled
Get-AzAutomationAccount -Name $AutomationAccountName -ResourceGroupName $ResourceGroupName |
Select-Object Identity
# Step 2: Confirm Exchange.ManageAsApp permission is granted
$MI = Get-MgServicePrincipal -Filter "displayName eq '$AutomationAccountName'"
Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $MI.Id
# Step 3: Verify Exchange Administrator role is assigned
Get-MgRoleManagementDirectoryRoleAssignment -Filter "principalId eq '$($MI.Id)'"
# Step 4: Wait 5-10 minutes after role assignment for propagation
Issue 5: Connection Timeout
Problem: Connection hangs or times out
Solution:
# Verify network connectivity to Exchange Online
Test-NetConnection outlook.office365.com -Port 443
# Check for proxy requirements
# The module auto-detects system proxy settings
# Clear cached tokens
[Microsoft.Exchange.Management.ExoPowershellSnapin.ConnectionManager]::DisconnectAllConnections()
# Try connecting again
Connect-ExchangeOnline -UserPrincipalName admin@365adviser.com
Issue 6: "Too Many Connections" Error
Problem: "Cannot create more than 3 connections"
Solution:
# Check existing connections
Get-ConnectionInformation
# Disconnect all sessions
Disconnect-ExchangeOnline -Confirm:$false
# In automation scripts, always disconnect in finally block
try {
Connect-ExchangeOnline -CertificateThumbprint $Thumbprint -AppId $AppId -Organization $Org
# Your code here
} finally {
Disconnect-ExchangeOnline -Confirm:$false -ErrorAction SilentlyContinue
}
Issue 7: PowerShellGet/PackageManagement Errors
Problem: "REST API connections require PowerShellGet"
Solution:
# Update PowerShellGet and PackageManagement
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
Install-Module -Name PowerShellGet -Force -AllowClobber
Install-Module -Name PackageManagement -Force -AllowClobber
# Close and reopen PowerShell window
# Then try installing Exchange Online module again
Best Practices
Security Best Practices
- Use certificate-based authentication for all automation scenarios
- Enable MFA on all interactive admin accounts
- Apply principle of least privilege – assign only necessary permissions
- Rotate certificates regularly (recommend 1-2 year expiration)
- Store credentials securely using Azure Key Vault or similar solutions
- Never hardcode passwords in scripts or commit them to source control
- Use managed identities for Azure resources instead of service principals when possible
- Monitor and audit all Exchange Online PowerShell connections
- Implement conditional access policies for admin accounts
- Regularly review API permissions and role assignments
Performance Best Practices
- Use Get-EXO cmdlets* for large-scale data retrieval operations
- Limit result sets with
-ResultSizeparameter to avoid memory issues - Use property sets to retrieve only needed properties
- Disconnect sessions when finished to free resources
- Avoid
Get-Mailbox -ResultSize Unlimitedunless absolutely necessary - Batch operations in automation scenarios to reduce API calls
- Implement error handling with try-catch-finally blocks
- Use
-ShowBanner:$falseto reduce output in automated scripts - Leverage
-PropertySetsparameter for focused queries - Implement retry logic for transient failures
Scripting Best Practices Template
<#
.SYNOPSIS
Template for Exchange Online PowerShell scripts
.DESCRIPTION
Best practice template with error handling, logging, and proper cleanup
.PARAMETER ReportPath
Path where reports will be saved
.EXAMPLE
.\Exchange-Script.ps1 -ReportPath "C:\Reports"
.NOTES
Author: Your Name
Date: January 2026
Version: 1.0
Requires: ExchangeOnlineManagement module 3.9.2+
#>
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]$ReportPath
)
# Requires statement
#Requires -Modules ExchangeOnlineManagement
#Requires -Version 5.1
# Import required modules
Import-Module ExchangeOnlineManagement -ErrorAction Stop
# Define parameters
$AppId = $env:EXO_APP_ID # Store in environment variables or secure vault
$CertificateThumbprint = $env:EXO_CERT_THUMBPRINT
$Organization = "contoso.onmicrosoft.com"
$LogFile = Join-Path $ReportPath "ExchangeScript_$(Get-Date -Format 'yyyyMMdd_HHmmss').log"
# Ensure report directory exists
if (-not (Test-Path $ReportPath)) {
New-Item -Path $ReportPath -ItemType Directory -Force | Out-Null
}
# Logging function
function Write-Log {
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]$Message,
[Parameter(Mandatory=$false)]
[ValidateSet('Info','Warning','Error')]
[string]$Level = 'Info'
)
$Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
$LogMessage = "[$Timestamp] [$Level] $Message"
# Output to console with color
switch ($Level) {
'Warning' { Write-Warning $Message }
'Error' { Write-Error $Message }
default { Write-Output $LogMessage }
}
# Write to log file
Add-Content -Path $LogFile -Value $LogMessage
}
# Main script logic
try {
Write-Log "===== Script Started =====" -Level Info
Write-Log "Report Path: $ReportPath"
# Validate prerequisites
Write-Log "Validating prerequisites..."
if (-not $AppId -or -not $CertificateThumbprint) {
throw "Required environment variables not set"
}
# Connect to Exchange Online
Write-Log "Connecting to Exchange Online..."
Connect-ExchangeOnline `
-AppId $AppId `
-CertificateThumbprint $CertificateThumbprint `
-Organization $Organization `
-ShowBanner:$false `
-ErrorAction Stop
Write-Log "Connected successfully"
# Verify connection
$Connection = Get-ConnectionInformation
Write-Log "Connection State: $($Connection.State)"
Write-Log "Connected Organization: $($Connection.Organization)"
# Perform operations
Write-Log "Retrieving mailbox information..."
$Mailboxes = Get-EXOMailbox -ResultSize 100 -ErrorAction Stop |
Select-Object DisplayName, PrimarySmtpAddress, RecipientTypeDetails, WhenCreated
Write-Log "Retrieved $($Mailboxes.Count) mailboxes"
# Export results
$OutputFile = Join-Path $ReportPath "Mailboxes_$(Get-Date -Format 'yyyyMMdd_HHmmss').csv"
$Mailboxes | Export-Csv -Path $OutputFile -NoTypeInformation -ErrorAction Stop
Write-Log "Results exported to: $OutputFile"
Write-Log "Operations completed successfully" -Level Info
} catch {
Write-Log "FATAL ERROR: $_" -Level Error
Write-Log "Stack Trace: $($_.ScriptStackTrace)" -Level Error
throw # Re-throw to ensure non-zero exit code
} finally {
# Always disconnect and cleanup
Write-Log "Cleaning up..."
try {
Disconnect-ExchangeOnline -Confirm:$false -ErrorAction SilentlyContinue
Write-Log "Disconnected from Exchange Online"
} catch {
Write-Log "Error during disconnect: $_" -Level Warning
}
Write-Log "===== Script Completed ====="
}
Connection Status and Management
Check Current Connection
# View connection details
Get-ConnectionInformation
# Check connection state
$Connection = Get-ConnectionInformation
if ($Connection.State -eq 'Connected') {
Write-Host "Connected to: $($Connection.Organization)" -ForegroundColor Green
}
Disconnect from Exchange Online
# Disconnect with confirmation prompt
Disconnect-ExchangeOnline
# Disconnect without confirmation (for scripts)
Disconnect-ExchangeOnline -Confirm:$false
💡 Best Practice: Always disconnect in a
finallyblock to ensure cleanup even if errors occur.
Part 2: Microsoft Entra ID (Directory Services)
🚨 CRITICAL: MSOnline/AzureAD Module Retirement
⚠️ URGENT ACTION REQUIRED
MSOnline PowerShell stops working: April - May 2025
AzureAD PowerShell retired after: July 1, 2025If you're using
Connect-MsolServiceorConnect-AzureAD, you MUST migrate to Microsoft Graph PowerShell SDK immediately.
Retirement Timeline
| Date | Event |
|---|---|
| March 30, 2024 | Deprecation announced - no new features |
| Feb-Mar 2025 | Temporary outages for MSOnline (testing) |
| March 30, 2025 | End of support for both modules |
| April-May 2025 | MSOnline stops working for ALL tenants |
| July 1, 2025 | AzureAD retirement begins |
Why This Matters
If you don't migrate:
- ❌ Your automation scripts will fail
- ❌ User management tasks will break
- ❌ No support from Microsoft
- ❌ Security vulnerabilities won't be patched
What you need to do:
- ✅ Install Microsoft Graph PowerShell SDK
- ✅ Update all scripts using MSOnline/AzureAD cmdlets
- ✅ Test thoroughly in non-production
- ✅ Deploy updated scripts before May 2025
Microsoft Graph PowerShell SDK
Microsoft Graph PowerShell SDK is the official, modern replacement for MSOnline and AzureAD modules. It provides access to the entire Microsoft Graph API, including Microsoft Entra ID (Azure AD), Microsoft 365 services, and more.
Why Microsoft Graph?
✅ Unified API - Single endpoint for all Microsoft 365 services
✅ Modern Authentication - OAuth 2.0, certificate-based, managed identity
✅ Cross-Platform - Windows, macOS, Linux
✅ PowerShell 7 Support - Actively maintained
✅ Comprehensive - Access to 100+ Microsoft services
✅ Secure - Granular permission management
Installation
# Install the complete SDK (recommended for most users)
Install-Module Microsoft.Graph -Scope CurrentUser
# Or install specific sub-modules for targeted needs
Install-Module Microsoft.Graph.Users -Scope CurrentUser
Install-Module Microsoft.Graph.Groups -Scope CurrentUser
Install-Module Microsoft.Graph.Identity.DirectoryManagement -Scope CurrentUser
# Update existing installation
Update-Module Microsoft.Graph
# Verify installation
Get-InstalledModule Microsoft.Graph
Interactive Authentication
# Connect with delegated permissions (as signed-in user)
Connect-MgGraph -Scopes "User.Read.All", "Group.ReadWrite.All"
# Check connection context
Get-MgContext
# Example: Get users
Get-MgUser -Top 10 | Select-Object DisplayName, UserPrincipalName, JobTitle
# Example: Get groups
Get-MgGroup -Top 10 | Select-Object DisplayName, Description, GroupTypes
# Disconnect when finished
Disconnect-MgGraph
Certificate-Based Authentication (App-Only)
Perfect for automation and unattended scripts.
Step 1: Create Certificate
# Create self-signed certificate
$CertName = "GraphAutomation"
$Cert = New-SelfSignedCertificate `
-Subject "CN=$CertName" `
-CertStoreLocation "Cert:\CurrentUser\My" `
-KeyExportPolicy Exportable `
-KeySpec Signature `
-KeyLength 2048 `
-KeyAlgorithm RSA `
-HashAlgorithm SHA256 `
-NotAfter (Get-Date).AddYears(2)
# Export certificate
$CertPath = "C:\Certificates"
New-Item -Path $CertPath -ItemType Directory -Force
Export-Certificate -Cert $Cert -FilePath "$CertPath\GraphAutomation.cer"
# Note the thumbprint
$CertThumbprint = $Cert.Thumbprint
Write-Host "Certificate Thumbprint: $CertThumbprint" -ForegroundColor Green
Step 2: Register App and Upload Certificate
Via Azure Portal:
- Go to Microsoft Entra admin center (entra.microsoft.com)
- Navigate to App registrations → New registration
- Name: "Microsoft Graph PowerShell Automation"
- Account type: "Accounts in this organizational directory only"
- Click Register
- Note the Application (client) ID and Directory (tenant) ID
- Go to Certificates & secrets → Certificates → Upload certificate
- Upload the .cer file
- Note the certificate Thumbprint
Step 3: Grant API Permissions
- Go to API permissions → Add a permission
- Select Microsoft Graph → Application permissions
- Add required permissions:
User.Read.All- Read all user profilesGroup.ReadWrite.All- Read and write groupsDirectory.ReadWrite.All- Read and write directory data (use cautiously)
- Click Grant admin consent for [organization]
Step 4: Connect Using Certificate
# Define connection parameters
$ClientId = "your-app-client-id"
$TenantId = "your-tenant-id"
$CertThumbprint = "your-cert-thumbprint"
# Connect using certificate thumbprint
Connect-MgGraph -ClientId $ClientId `
-TenantId $TenantId `
-CertificateThumbprint $CertThumbprint
# Verify connection
Get-MgContext
# Example operations
Get-MgUser -ConsistencyLevel eventual -Count userCount
Get-MgGroup -Filter "groupTypes/any(c:c eq 'Unified')"
# Disconnect
Disconnect-MgGraph
Alternative: Using Certificate Object
# Load certificate from store
$Cert = Get-ChildItem Cert:\CurrentUser\My\$CertThumbprint
# Connect using certificate object
Connect-MgGraph -ClientId $ClientId `
-TenantId $TenantId `
-Certificate $Cert
Managed Identity Authentication
For Azure Automation, Azure Functions, and Azure VMs.
# System-assigned managed identity
Connect-MgGraph -Identity
# User-assigned managed identity
Connect-MgGraph -Identity -ClientId "user-assigned-identity-client-id"
Client Secret Authentication
# Create PSCredential with client secret
$ClientId = "your-app-client-id"
$ClientSecret = "your-client-secret"
$TenantId = "your-tenant-id"
$SecureSecret = ConvertTo-SecureString -String $ClientSecret -AsPlainText -Force
$ClientSecretCredential = New-Object System.Management.Automation.PSCredential($ClientId, $SecureSecret)
# Connect
Connect-MgGraph -TenantId $TenantId -ClientSecretCredential $ClientSecretCredential
🔐 Security Note: Client secrets should be stored securely (Azure Key Vault) and rotated regularly. Certificate-based authentication is more secure for production use.
Migration from MSOnline/AzureAD
Common Cmdlet Migration
| MSOnline/AzureAD Cmdlet | Microsoft Graph Cmdlet | Notes |
|---|---|---|
Connect-MsolService |
Connect-MgGraph |
Requires specifying scopes |
Connect-AzureAD |
Connect-MgGraph |
Requires specifying scopes |
Get-MsolUser |
Get-MgUser |
May need -ConsistencyLevel eventual |
Get-AzureADUser |
Get-MgUser |
May need -ConsistencyLevel eventual |
New-MsolUser |
New-MgUser |
Different parameter names |
New-AzureADUser |
New-MgUser |
Different parameter names |
Set-MsolUser |
Update-MgUser |
Uses -UserId parameter |
Set-AzureADUser |
Update-MgUser |
Uses -UserId parameter |
Remove-MsolUser |
Remove-MgUser |
Uses -UserId parameter |
Remove-AzureADUser |
Remove-MgUser |
Uses -UserId parameter |
Get-MsolGroup |
Get-MgGroup |
Different filtering syntax |
Get-AzureADGroup |
Get-MgGroup |
Different filtering syntax |
Get-MsolGroupMember |
Get-MgGroupMember |
Uses -GroupId parameter |
Get-AzureADGroupMember |
Get-MgGroupMember |
Uses -GroupId parameter |
Add-MsolGroupMember |
New-MgGroupMember |
Different syntax |
Add-AzureADGroupMember |
New-MgGroupMember |
Different syntax |
Get-MsolDomain |
Get-MgDomain |
Similar functionality |
Get-AzureADDomain |
Get-MgDomain |
Similar functionality |
Finding Equivalent Cmdlets
# Find Microsoft Graph command for specific functionality
Find-MgGraphCommand -Command Get-MgUser
# See all available Graph commands
Get-Command -Module Microsoft.Graph.*
# Get help for specific cmdlet
Get-Help Get-MgUser -Full
Migration Example: User Management
Old (MSOnline):
Connect-MsolService
$Users = Get-MsolUser -All
$Users | Where-Object {$_.IsLicensed -eq $true}
New (Microsoft Graph):
Connect-MgGraph -Scopes "User.Read.All"
$Users = Get-MgUser -All -Property DisplayName, UserPrincipalName, AssignedLicenses
$Users | Where-Object {$_.AssignedLicenses.Count -gt 0}
Old (AzureAD):
Connect-AzureAD
$Group = Get-AzureADGroup -Filter "DisplayName eq 'IT Admins'"
$Members = Get-AzureADGroupMember -ObjectId $Group.ObjectId
New (Microsoft Graph):
Connect-MgGraph -Scopes "Group.Read.All"
$Group = Get-MgGroup -Filter "DisplayName eq 'IT Admins'"
$Members = Get-MgGroupMember -GroupId $Group.Id
Advanced Filtering and Queries
Microsoft Graph requires different syntax for advanced queries:
# Connect with appropriate permissions
Connect-MgGraph -Scopes "User.Read.All"
# Count users (requires ConsistencyLevel)
Get-MgUser -ConsistencyLevel eventual -Count userCount
# Advanced filtering
Get-MgUser -ConsistencyLevel eventual `
-Filter "startsWith(DisplayName, 'Admin')" `
-OrderBy DisplayName
# Search (requires ConsistencyLevel)
Get-MgUser -ConsistencyLevel eventual `
-Search "DisplayName:John" `
-Count userCount
# Complex filter with multiple conditions
Get-MgUser -ConsistencyLevel eventual `
-Filter "accountEnabled eq true and userType eq 'Member'" `
-Select DisplayName, UserPrincipalName, Department
Complete Migration Script Example
<#
.SYNOPSIS
Migration example from MSOnline to Microsoft Graph
.DESCRIPTION
Shows how to migrate common user management tasks
#>
# Old way (MSOnline) - DEPRECATED
<#
Connect-MsolService
$Users = Get-MsolUser -All | Where-Object {$_.IsLicensed -eq $true}
foreach ($User in $Users) {
Set-MsolUser -UserPrincipalName $User.UserPrincipalName -Department "IT"
}
#>
# New way (Microsoft Graph)
try {
# Connect with required permissions
Connect-MgGraph -Scopes "User.ReadWrite.All" -ErrorAction Stop
# Get licensed users
Write-Host "Retrieving licensed users..."
$Users = Get-MgUser -All -Property DisplayName, UserPrincipalName, AssignedLicenses, Department |
Where-Object {$_.AssignedLicenses.Count -gt 0}
Write-Host "Found $($Users.Count) licensed users"
# Update users
foreach ($User in $Users) {
Write-Host "Updating: $($User.DisplayName)"
Update-MgUser -UserId $User.Id -Department "IT" -ErrorAction Stop
}
Write-Host "Migration completed successfully" -ForegroundColor Green
} catch {
Write-Error "Error during migration: $_"
} finally {
Disconnect-MgGraph
}
Part 3: Additional Services
Security & Compliance PowerShell
Connect to Security & Compliance PowerShell (Microsoft Purview) for eDiscovery, compliance, and data governance tasks.
Prerequisites
- Exchange Online PowerShell module (same module as Exchange Online)
- Security & Compliance admin role or appropriate permissions
Interactive Connection
# Connect to Security & Compliance
Connect-IPPSSession -UserPrincipalName admin@365adviser.com
# Verify connection
Get-ConnectionInformation
# Example commands
Get-ComplianceSearch
Get-RetentionCompliancePolicy
# Disconnect
Disconnect-IPPSSession
Certificate-Based Authentication
# Use same certificate setup as Exchange Online
Connect-IPPSSession -CertificateThumbprint $CertThumbprint `
-AppId $AppId `
-Organization $TenantId
# Note: REST API mode is default in version 3.2.0+
Managed Identity
# Connect from Azure Automation with managed identity
Connect-IPPSSession -ManagedIdentity -Organization "contoso.onmicrosoft.com"
🔍 Note: Version 3.9.0+ supports
-EnableSearchOnlySessionswitch for eDiscovery scenarios that connect to other M365 services.
REST API vs RPS Mode
# REST API mode (default, recommended)
Connect-IPPSSession -UserPrincipalName admin@365adviser.com
# Legacy RPS mode (deprecated in 3.9.2)
# Connect-IPPSSession -UserPrincipalName admin@365adviser.com -UseRPSSession
⚠️ Deprecation Notice: The
UseRPSSessionparameter has been deprecated in version 3.9.2. Always use REST API mode.
Hybrid Exchange (Optional)
For organizations with on-premises Exchange servers in hybrid configuration.
Connect to On-Premises Exchange
Using Exchange Management Shell
On the Exchange server, launch Exchange Management Shell - this automatically loads cmdlets.
# Verify Exchange snapin is loaded
Get-PSSnapin | Where-Object {$_.Name -like "*Exchange*"}
# Example commands
Get-Mailbox -ResultSize 10
Get-ExchangeServer
Remote PowerShell Connection
# Define connection parameters
$ExchangeServer = "exchangeserver.contoso.local"
$Credential = Get-Credential # Domain credentials
# Create remote session
$Session = New-PSSession -ConfigurationName Microsoft.Exchange `
-ConnectionUri "http://$ExchangeServer/PowerShell/" `
-Authentication Kerberos `
-Credential $Credential `
-ErrorAction Stop
# Import session
Import-PSSession $Session -DisableNameChecking -AllowClobber
# Verify connection
Get-ExchangeServer
# Run commands
Get-Mailbox -Server $ExchangeServer -ResultSize 10
# Disconnect when finished
Remove-PSSession $Session
Hybrid: Connect to Both Cloud and On-Premises
# Connect to Exchange Online with prefix
Connect-ExchangeOnline -UserPrincipalName admin@365adviser.com -Prefix Cloud
# Connect to on-premises with prefix
$OnPremSession = New-PSSession -ConfigurationName Microsoft.Exchange `
-ConnectionUri "http://exchangeserver.contoso.local/PowerShell/" `
-Authentication Kerberos `
-Credential (Get-Credential)
Import-PSSession $OnPremSession -DisableNameChecking -AllowClobber -Prefix OnPrem
# Now use both with prefixes
Get-CloudMailbox -Identity user@365adviser.com
Get-OnPremMailbox -Identity user@contoso.local
# Disconnect both
Remove-PSSession $OnPremSession
Disconnect-ExchangeOnline -Confirm:$false
Additional Resources
Microsoft Official Documentation
Exchange Online PowerShell:
Microsoft Graph PowerShell:
Module Retirement:
PowerShell Gallery
Community Resources
- Microsoft Tech Community - Exchange
- Microsoft Tech Community - Microsoft Entra
- Practical365
- Office 365 for IT Pros
Version History
Current Module Versions (January 2026):
- ExchangeOnlineManagement: 3.9.2 (Latest GA)
- Microsoft.Graph: 2.x (Latest GA)
Key Updates in ExchangeOnlineManagement V3:
- REST API-based cmdlets (no WinRM Basic Authentication required)
- Managed identity support for Azure resources
- Certificate-based authentication for automation
- Enhanced performance with optimized Get-EXO* cmdlets
- Cross-platform support (Windows, macOS, Linux)
- PowerShell 7.4+ compatibility
Recent Changes:
- v3.9.2 (January 2026): UseRpsSession parameter deprecated
- v3.9.0 (August 2025): Added EnableSearchOnlySession switch for Connect-IPPSSession
- v3.7.0 (2024): Web Account Manager (WAM) integration, LoadCmdletHelp parameter
Deprecated Features:
- Basic Authentication (permanently disabled October 2022)
- Remote PowerShell (RPS) protocol (blocked October 2023)
- MSOnline module (stops working April-May 2025)
- AzureAD module (retirement begins July 2025)
- New-PSSession method for Exchange Online
- UseRpsSession parameter (deprecated in 3.9.2)
Quick Reference Card
Most Common Commands
# Interactive Connection
Connect-ExchangeOnline -UserPrincipalName admin@365adviser.com
# Certificate-Based (Automation)
Connect-ExchangeOnline -CertificateThumbprint $Thumb -AppId $AppId -Organization $Org
# Managed Identity (Azure)
Connect-ExchangeOnline -ManagedIdentity -Organization $Org
# Microsoft Graph (Users/Groups)
Connect-MgGraph -Scopes "User.Read.All", "Group.ReadWrite.All"
# Check Connection
Get-ConnectionInformation
# Disconnect
Disconnect-ExchangeOnline -Confirm:$false
Disconnect-MgGraph
Useful One-Liners
# Get all mailboxes
Get-EXOMailbox -ResultSize Unlimited
# Get mailbox statistics
Get-EXOMailboxStatistics -Identity user@365adviser.com
# Get users with licenses
Get-MgUser -All -Property AssignedLicenses | Where-Object {$_.AssignedLicenses.Count -gt 0}
# Get all groups
Get-MgGroup -All
# Export mailbox list
Get-EXOMailbox -ResultSize Unlimited | Export-Csv C:\mailboxes.csv -NoTypeInformation
Share this article
Help others discover this content
Need Help Implementing This Solution?
Schedule a free 30-minute consultation to discuss your specific Microsoft 365 or Azure needs.
Schedule Free Consultation


