Bulletproofing Exchange Hybrid Deployments : Permissions Checker (Part 1)


Exchange hybrid configurations can be a nightmare when they fail midway through setup due to insufficient permissions. rather than have a failure due to permissions issues its best to check them before you run the hybrid wizard tool, so I built a PowerShell script that comprehensively validates permissions before running the Exchange Hybrid Configuration Wizard. This isn't just another "check if you're an admin" script – it's a deep dive into the complex permission requirements across both on-premises Exchange and Exchange Online.

The Problem: Hybrid Configuration Wizard Failures

The Exchange Hybrid Configuration Wizard is notorious for failing partway through configuration when it encounters permission issues. Microsoft's documentation lists various role requirements, but the reality is more nuanced. Different organizations have different permission models, and what works in a lab environment often breaks in production due to:

  • Inconsistent role assignments between on-premises and cloud
  • Higher-level permissions (like Domain Admin) that aren't detected by simple role checks
  • Exchange Online cmdlet quirks that cause false negatives during permission validation
  • Conflicting PowerShell sessions when connecting to both environments simultaneously

Scripted : Permission Detection

I developed a PowerShell script that addresses these real-world challenges through multiple detection methods and intelligent fallback logic. The script doesn't just check for specific roles – it validates actual functional access using the same cmdlets the Hybrid Configuration Wizard will attempt to use.

On-Premises Exchange Permission Detection

Traditional approaches to checking Exchange permissions often fail because they only look for specific role assignments. In reality, administrators might have permissions through various paths:

Method 1: Cmdlet Execution Testing

Rather than querying role assignments, the script tests actual functionality:

$TestCommands = @(
    @{ Command = "Get-ExchangeServer"; Description = "Exchange Server Management" },
    @{ Command = "Get-OrganizationConfig"; Description = "Organization Configuration" },
    @{ Command = "Get-AcceptedDomain"; Description = "Domain Management" },
    @{ Command = "Get-SendConnector"; Description = "Send Connector Management" }
)

$PassedTests = 0
foreach ($Test in $TestCommands) {
    try {
        $null = Invoke-Expression "$($Test.Command) -ErrorAction Stop 
        | Select-Object -First 1"
        Write-LogAndHost "    ✓ $($Test.Description)" -Color "Green"
        $PassedTests++
    } catch {
        Write-LogAndHost "    ✗ $($Test.Description) - Access Denied" -Color "Red"
    }
}

This approach is far more reliable than role enumeration because it tests the actual permissions the Hybrid Configuration Wizard needs.

Method 2: Windows Security Group Analysis

Many Exchange administrators have permissions through Windows security groups rather than explicit Exchange role assignments:

# Get current user's groups
$UserGroups = @()
$WindowsIdentity = [System.Security.Principal.WindowsIdentity]::GetCurrent()
foreach ($Group in $WindowsIdentity.Groups) {
    try {
        $GroupName = $Group.Translate([System.Security.Principal.NTAccount]).Value
        $UserGroups += $GroupName
    } catch {
        # Skip groups that can't be translated
    }
}

# Check for high-level admin groups
$AdminGroups = @(
    "BUILTIN\Administrators",
    "*\Domain Admins",
    "*\Enterprise Admins",
    "*\Organization Management"
)

foreach ($AdminPattern in $AdminGroups) {
    $MatchingGroups = $UserGroups | Where-Object { $_ -like $AdminPattern }
    if ($MatchingGroups) {
        $FoundAdminGroup = $true
        $HasSufficientPermissions = $true
    }
}

This catches scenarios where administrators have sufficient permissions through group membership that wouldn't be detected by Exchange role queries.

Exchange Online: Navigating cmdlet Inconsistencies

Exchange Online presents unique challenges due to cmdlet inconsistencies and throttling behaviors that can cause false permission failures.

The SendConnector Problem

One major issue I encountered was using Get-SendConnector for Exchange Online testing. This cmdlet doesn't exist in EXO – the equivalent is Get-OutboundConnector. This kind of inconsistency between on-premises and cloud can cause scripts to incorrectly report permission failures.

# Correct EXO cmdlets for permission testing
$TestCommands = @(
    @{ Command = "Get-OrganizationConfig"; Description = "Organization Management" },
    @{ Command = "Get-AcceptedDomain"; Description = "Domain Management" },
    @{ Command = "Get-OutboundConnector"; Description = "Mail Flow Management" },  
    # Not Get-SendConnector!
    @{ Command = "Get-InboundConnector"; Description = "Connector Management" }
)

Handling EXO Throttling and Temporary Failures

Exchange Online can be temperamental with cmdlet execution due to throttling, caching, and regional variations. The script implements intelligent fallback testing:

foreach ($Test in $TestCommands) {
    $TestPassed = $false
    
    # Try primary command
    try {
        $null = Invoke-Expression "$($Test.Primary) -ErrorAction Stop 
        -WarningAction SilentlyContinue | Select-Object -First 1"
        $TestPassed = $true
    } catch {
        # Try fallback command
        try {
            $null = Invoke-Expression "$($Test.Fallback) -ErrorAction Stop 
        -WarningAction SilentlyContinue | Select-Object -First 1"
            $TestPassed = $true
        } catch {
            # Both failed - likely a real permission issue
        }
    }
}

Realistic Permission Thresholds

Rather than requiring 100% success on all cmdlet tests, the script uses realistic thresholds based on actual hybrid deployment requirements:

if ($PassedTests -eq $TotalTests) {
    # Full permissions - ideal scenario
    return $true
} elseif ($PassedTests -ge 3) {
    # 3/4 tests passed - sufficient for hybrid configuration
    return $true
} elseif ($PassedTests -ge 2) {
    # 2/4 tests passed - may work with limitations
    Update-OverallStatus "Yellow"
    return $true
} else {
    # Less than 2/4 - insufficient
    Update-OverallStatus "Red"
    return $false
}

This approach prevents false negatives while still catching genuine permission issues.

Avoiding PowerShell Session Conflicts

One of the biggest technical challenges was handling simultaneous connections to on-premises Exchange and Exchange Online. Both environments use similar cmdlet names, leading to conflicts and unpredictable behavior.

Sequential Connection Strategy

The script implements a sequential approach with proper session cleanup:

# After on-premises testing, clean up before EXO connection
Write-LogAndHost "Disconnecting from On-Premises Exchange to avoid conflicts..." 
-Color "Yellow"
try {
    Get-PSSession | Where-Object { $_.ConfigurationName -eq "Microsoft.Exchange" } |
     Remove-PSSession
    Remove-PSSnapin Microsoft.Exchange.Management.PowerShell.E2010 
    -ErrorAction SilentlyContinue
    Write-LogAndHost "✓ On-Premises Exchange session closed" -Color "Green"
} catch {
    Write-LogAndHost "⚠️ Could not cleanly disconnect from On-Premises Exchange"
 -Color "Yellow"
}

# Brief pause to ensure cleanup completion
Start-Sleep -Seconds 2

# Now safe to connect to Exchange Online
$OnlineConnected = Connect-ToExchangeOnline

This prevents the cmdlet name collisions that would otherwise cause unpredictable results.

User Experience and Output Design

The script provides color-coded output that immediately communicates the permission status:

  • Green: All required permissions present
  • Yellow: Some permissions missing but hybrid configuration may still work
  • Red: Insufficient permissions - hybrid configuration will likely fail

Comprehensive Logging

All output is simultaneously written to both console and log file:

function Write-LogAndHost {
    param(
        [string]$Message,
        [string]$Color = "White",
        [string]$Level = "INFO"
    )
    
    $Timestamp = Get-Date -Format "yyyy-MM-dd HH:mm:ss"
    $LogMessage = "[$Timestamp] [$Level] $Message"
    
    # Write to console with color
    Write-Host $Message -ForegroundColor $Color
    
    # Write to log file
    Add-Content -Path $LogFile -Value $LogMessage
}

This ensures administrators have both immediate feedback and a permanent record for compliance and troubleshooting.

Script : Runtime Modes

The script offers three operational modes to match different organizational needs:

Mode 1: On-Premises Only

Perfect for organizations that want to validate local Exchange permissions before attempting any hybrid configuration.

Mode 2: Exchange Online Only

Ideal for verifying cloud permissions without affecting on-premises systems, useful in environments with strict change control.

Mode 3: Sequential Full Validation

Comprehensive testing of both environments with proper session management to avoid conflicts, however you may get issues loading both Exchange and Exchange Online at the same time.

Installation and Usage

The script requires PowerShell 5.1 or later and must be run as Administrator. It automatically handles the installation of required modules:

#Requires -Version 5.1
#Requires -RunAsAdministrator

# The script will automatically install ExchangeOnlineManagement module if not present
if (!(Get-Module -ListAvailable -Name ExchangeOnlineManagement)) {
    Install-Module -Name ExchangeOnlineManagement -Force -AllowClobber -Scope CurrentUser
}

Visual Examples

When the script is run as an administrator you will get three options to check permissions as you can see below:


Lets choose Option 2 (Exchange Online) when the connects to the Exchange Online shell after a short pause it will confirm I have the required permissions to complete the Hybrid wizard actions.

  
Previous Post Next Post

نموذج الاتصال