Public/Permissions/Invoke-365TuneTestExchange.ps1

function Invoke-365TuneTestExchange {
    <#
    .SYNOPSIS
        Tests whether Exchange Online permissions are correctly assigned to the 365TUNE Enterprise App.

    .DESCRIPTION
        Checks that the 365TUNE Service Principal is registered in Exchange Online and has the
        View-Only Configuration management role assigned. Read-only - makes no changes.

    .EXAMPLE
        Invoke-365TuneTestExchange

    .NOTES
        Author : Metawise Consulting LLC
        Module : 365TUNE
        Version : 2.2.7
    #>


    [CmdletBinding()]
    param(
        [switch]$SkipAuth
    )

    $displayNameProd = "365TUNE - Security and Compliance"
    $displayNameBeta = "365TUNE - Security and Compliance - Beta"
    $roleName        = "View-Only Configuration"

    Write-Host "`n======================================================" -ForegroundColor Cyan
    Write-Host " 365TUNE - Test Exchange Online Permissions" -ForegroundColor Cyan
    Write-Host "======================================================`n" -ForegroundColor Cyan

    # Step 1 - Modules
    Write-Host "[1/4] Checking required modules..." -ForegroundColor Cyan
    foreach ($module in @("Az.Accounts", "ExchangeOnlineManagement")) {
        if (-not (Get-Module -ListAvailable -Name $module)) {
            Install-Module -Name $module -Force -Scope CurrentUser
        }
    }
    Import-Module Az.Accounts, ExchangeOnlineManagement
    Write-Host " [OK] Modules ready." -ForegroundColor Green

    # Step 2 - Authenticate
    Write-Host "`n[2/4] Authenticating..." -ForegroundColor Cyan
    if (-not $SkipAuth) {
        $inCloudShell = ($env:ACC_CLOUD -eq "PROD") -or
                        ($env:POWERSHELL_DISTRIBUTION_CHANNEL -like "*CloudShell*") -or
                        ($env:AZUREPS_HOST_ENVIRONMENT -like "*cloud-shell*")
        if ($inCloudShell) {
            Connect-AzAccount -Identity -WarningAction SilentlyContinue | Out-Null
        } else {
            Disconnect-AzAccount -ErrorAction SilentlyContinue | Out-Null
            Connect-AzAccount -WarningAction SilentlyContinue | Out-Null
        }
    }
    $context = Get-AzContext
    if (-not $context) { throw "Not authenticated." }
    Write-Host " Tenant : $($context.Tenant.Id)" -ForegroundColor Gray
    Write-Host " Account : $($context.Account.Id)" -ForegroundColor Gray
    Write-Host " [OK] Authenticated." -ForegroundColor Green

    # Step 3 - Connect Exchange
    Write-Host "`n[3/4] Connecting to Exchange Online..." -ForegroundColor Cyan
    Connect-ExchangeOnline -ShowBanner:$false
    Start-Sleep -Milliseconds 500
    Write-Host " [OK] Connected." -ForegroundColor Green

    # Step 4 - Check
    Write-Host "`n[4/4] Checking Exchange permissions..." -ForegroundColor Cyan

    $spRegistered = $null
    try {
        $spRegistered = Get-ServicePrincipal -ErrorAction SilentlyContinue |
            Where-Object { $_.DisplayName -eq $displayNameProd -or $_.DisplayName -eq $displayNameBeta } |
            Select-Object -First 1
    } catch {}

    $roleAssigned = $null
    if ($spRegistered) {
        try {
            $roleAssigned = Get-ManagementRoleAssignment -ErrorAction SilentlyContinue |
                Where-Object { $_.RoleAssigneeName -eq $spRegistered.DisplayName -and $_.Role -eq $roleName } |
                Select-Object -First 1
        } catch {}
    }

    Disconnect-ExchangeOnline -Confirm:$false -ErrorAction SilentlyContinue
    Start-Sleep -Milliseconds 500

    Write-Host ""
    if ($spRegistered) {
        Write-Host " [OK] Service Principal registered in Exchange ASSIGNED" -ForegroundColor Green
        Write-Host " Display Name : $($spRegistered.DisplayName)"
    } else {
        Write-Host " [FAIL] Service Principal registered in Exchange MISSING" -ForegroundColor Red
    }

    if ($roleAssigned) {
        Write-Host " [OK] $roleName role ASSIGNED" -ForegroundColor Green
    } else {
        Write-Host " [FAIL] $roleName role MISSING" -ForegroundColor Red
    }

    $allOk = $spRegistered -and $roleAssigned

    Write-Host ""
    Write-Host "======================================================" -ForegroundColor Cyan
    if ($allOk) {
        Write-Host " Exchange permissions OK. [OK]" -ForegroundColor Green
    } else {
        Write-Host " Exchange permissions INCOMPLETE. Run Invoke-365TuneConnectExchange." -ForegroundColor Red
    }
    Write-Host "======================================================`n" -ForegroundColor Cyan

    return $allOk
}