MedhaCloud-ExchangeDiagnostics.psm1

#Requires -Version 5.1
<#
.SYNOPSIS
    MedhaCloud Exchange Server Diagnostics Module

.DESCRIPTION
    Comprehensive diagnostic toolkit for Exchange Server administration.
    Includes health checks, mail flow analysis, DAG monitoring, and more.

    Professional Support: https://medhacloud.com/professional-services/server-support/exchange-server-support

.NOTES
    Author: MedhaCloud
    Company: MedhaCloud (https://medhacloud.com)
    LinkedIn: https://linkedin.com/company/medhacloud
    Version: 1.0.0
#>


# Module variables
$script:ModuleVersion = '1.0.0'
$script:SupportUrl = 'https://medhacloud.com/professional-services/server-support/exchange-server-support'

function Test-ExchangeServerHealth {
    <#
    .SYNOPSIS
        Performs comprehensive Exchange Server health check.

    .DESCRIPTION
        Validates Exchange Server components including services, databases,
        mail flow, certificates, and connectivity.

        For professional Exchange support, visit:
        https://medhacloud.com/professional-services/server-support/exchange-server-support

    .PARAMETER ServerName
        The Exchange server to check. Defaults to local server.

    .PARAMETER Detailed
        Include detailed component analysis.

    .EXAMPLE
        Test-ExchangeServerHealth

    .EXAMPLE
        Test-ExchangeServerHealth -ServerName "EX01" -Detailed

    .LINK
        https://medhacloud.com/professional-services/server-support/exchange-server-support
    #>

    [CmdletBinding()]
    param(
        [Parameter(Position = 0)]
        [string]$ServerName = $env:COMPUTERNAME,

        [Parameter()]
        [switch]$Detailed
    )

    $results = [PSCustomObject]@{
        ServerName = $ServerName
        Timestamp = Get-Date
        OverallStatus = 'Unknown'
        Services = @()
        Databases = @()
        Certificates = @()
        MailFlow = $null
        SupportUrl = $script:SupportUrl
    }

    Write-Host "MedhaCloud Exchange Health Check v$script:ModuleVersion" -ForegroundColor Cyan
    Write-Host "Server: $ServerName" -ForegroundColor Gray
    Write-Host "================================================" -ForegroundColor Gray

    # Check Exchange services
    Write-Host "`n[1/5] Checking Exchange Services..." -ForegroundColor Yellow
    $exchangeServices = @(
        'MSExchangeADTopology',
        'MSExchangeIS',
        'MSExchangeMailboxAssistants',
        'MSExchangeTransport',
        'MSExchangeFrontEndTransport',
        'MSExchangeMailboxReplication'
    )

    foreach ($svc in $exchangeServices) {
        try {
            $service = Get-Service -Name $svc -ComputerName $ServerName -ErrorAction SilentlyContinue
            if ($service) {
                $status = if ($service.Status -eq 'Running') { 'OK' } else { 'Warning' }
                $results.Services += [PSCustomObject]@{
                    Name = $svc
                    Status = $service.Status
                    Health = $status
                }
                $color = if ($status -eq 'OK') { 'Green' } else { 'Red' }
                Write-Host " $svc : $($service.Status)" -ForegroundColor $color
            }
        }
        catch {
            $results.Services += [PSCustomObject]@{
                Name = $svc
                Status = 'Unknown'
                Health = 'Error'
            }
        }
    }

    # Check databases
    Write-Host "`n[2/5] Checking Mailbox Databases..." -ForegroundColor Yellow
    try {
        if (Get-Command Get-MailboxDatabase -ErrorAction SilentlyContinue) {
            $databases = Get-MailboxDatabase -Server $ServerName -Status -ErrorAction SilentlyContinue
            foreach ($db in $databases) {
                $dbStatus = if ($db.Mounted) { 'Mounted' } else { 'Dismounted' }
                $health = if ($db.Mounted) { 'OK' } else { 'Critical' }
                $results.Databases += [PSCustomObject]@{
                    Name = $db.Name
                    Status = $dbStatus
                    Health = $health
                    Size = $db.DatabaseSize
                }
                $color = if ($health -eq 'OK') { 'Green' } else { 'Red' }
                Write-Host " $($db.Name) : $dbStatus" -ForegroundColor $color
            }
        }
        else {
            Write-Host " Exchange cmdlets not available. Run from Exchange Management Shell." -ForegroundColor Yellow
        }
    }
    catch {
        Write-Host " Unable to query databases: $($_.Exception.Message)" -ForegroundColor Red
    }

    # Check certificates
    Write-Host "`n[3/5] Checking Exchange Certificates..." -ForegroundColor Yellow
    try {
        if (Get-Command Get-ExchangeCertificate -ErrorAction SilentlyContinue) {
            $certs = Get-ExchangeCertificate -Server $ServerName -ErrorAction SilentlyContinue
            foreach ($cert in $certs | Where-Object { $_.Services -ne 'None' }) {
                $daysToExpiry = ($cert.NotAfter - (Get-Date)).Days
                $health = if ($daysToExpiry -gt 30) { 'OK' } elseif ($daysToExpiry -gt 0) { 'Warning' } else { 'Expired' }
                $results.Certificates += [PSCustomObject]@{
                    Subject = $cert.Subject
                    Thumbprint = $cert.Thumbprint
                    ExpiresIn = "$daysToExpiry days"
                    Health = $health
                }
                $color = switch ($health) { 'OK' { 'Green' } 'Warning' { 'Yellow' } default { 'Red' } }
                Write-Host " $($cert.Subject.Split(',')[0]) : $daysToExpiry days until expiry" -ForegroundColor $color
            }
        }
    }
    catch {
        Write-Host " Unable to query certificates" -ForegroundColor Yellow
    }

    # Check mail queues
    Write-Host "`n[4/5] Checking Mail Queues..." -ForegroundColor Yellow
    try {
        if (Get-Command Get-Queue -ErrorAction SilentlyContinue) {
            $queues = Get-Queue -Server $ServerName -ErrorAction SilentlyContinue
            $totalMessages = ($queues | Measure-Object -Property MessageCount -Sum).Sum
            $health = if ($totalMessages -lt 100) { 'OK' } elseif ($totalMessages -lt 500) { 'Warning' } else { 'Critical' }
            $results.MailFlow = [PSCustomObject]@{
                TotalQueuedMessages = $totalMessages
                QueueCount = $queues.Count
                Health = $health
            }
            $color = switch ($health) { 'OK' { 'Green' } 'Warning' { 'Yellow' } default { 'Red' } }
            Write-Host " Total queued messages: $totalMessages" -ForegroundColor $color
        }
    }
    catch {
        Write-Host " Unable to query mail queues" -ForegroundColor Yellow
    }

    # Determine overall status
    Write-Host "`n[5/5] Calculating Overall Health..." -ForegroundColor Yellow
    $criticalIssues = ($results.Services | Where-Object { $_.Health -eq 'Critical' }).Count +
                      ($results.Databases | Where-Object { $_.Health -eq 'Critical' }).Count +
                      ($results.Certificates | Where-Object { $_.Health -eq 'Expired' }).Count

    $warningIssues = ($results.Services | Where-Object { $_.Health -eq 'Warning' }).Count +
                     ($results.Certificates | Where-Object { $_.Health -eq 'Warning' }).Count

    if ($criticalIssues -gt 0) {
        $results.OverallStatus = 'Critical'
    }
    elseif ($warningIssues -gt 0) {
        $results.OverallStatus = 'Warning'
    }
    else {
        $results.OverallStatus = 'Healthy'
    }

    Write-Host "`n================================================" -ForegroundColor Gray
    $statusColor = switch ($results.OverallStatus) { 'Healthy' { 'Green' } 'Warning' { 'Yellow' } default { 'Red' } }
    Write-Host "Overall Status: $($results.OverallStatus)" -ForegroundColor $statusColor
    Write-Host "`nNeed professional Exchange support?" -ForegroundColor Cyan
    Write-Host $script:SupportUrl -ForegroundColor White
    Write-Host "LinkedIn: https://linkedin.com/company/medhacloud" -ForegroundColor Gray

    return $results
}

function Get-MailFlowStatus {
    <#
    .SYNOPSIS
        Analyzes Exchange mail flow and queue status.

    .DESCRIPTION
        Provides detailed mail flow diagnostics including queue analysis,
        message tracking, and transport issues.

        Professional Support: https://medhacloud.com/professional-services/server-support/exchange-server-support

    .PARAMETER ServerName
        Target Exchange server.

    .PARAMETER Hours
        Hours of mail flow history to analyze.

    .LINK
        https://medhacloud.com/professional-services/server-support/exchange-server-support
    #>

    [CmdletBinding()]
    param(
        [string]$ServerName = $env:COMPUTERNAME,
        [int]$Hours = 24
    )

    Write-Host "MedhaCloud Mail Flow Analysis v$script:ModuleVersion" -ForegroundColor Cyan
    Write-Host "Analyzing last $Hours hours of mail flow..." -ForegroundColor Gray

    $results = [PSCustomObject]@{
        ServerName = $ServerName
        AnalysisPeriod = "$Hours hours"
        Queues = @()
        MessageStats = $null
        Issues = @()
        SupportUrl = $script:SupportUrl
    }

    try {
        if (Get-Command Get-Queue -ErrorAction SilentlyContinue) {
            $queues = Get-Queue -Server $ServerName
            foreach ($queue in $queues) {
                $results.Queues += [PSCustomObject]@{
                    Identity = $queue.Identity
                    DeliveryType = $queue.DeliveryType
                    Status = $queue.Status
                    MessageCount = $queue.MessageCount
                    NextHopDomain = $queue.NextHopDomain
                }

                if ($queue.MessageCount -gt 50) {
                    $results.Issues += "Queue $($queue.Identity) has $($queue.MessageCount) messages"
                }
            }
        }

        Write-Host "`nQueue Summary:" -ForegroundColor Yellow
        $results.Queues | Format-Table -AutoSize

        if ($results.Issues.Count -gt 0) {
            Write-Host "`nPotential Issues Detected:" -ForegroundColor Red
            $results.Issues | ForEach-Object { Write-Host " - $_" -ForegroundColor Yellow }
        }
        else {
            Write-Host "`nNo mail flow issues detected." -ForegroundColor Green
        }
    }
    catch {
        Write-Host "Error analyzing mail flow: $($_.Exception.Message)" -ForegroundColor Red
    }

    Write-Host "`nProfessional Exchange Support: $script:SupportUrl" -ForegroundColor Cyan
    return $results
}

function Test-DAGReplication {
    <#
    .SYNOPSIS
        Tests Database Availability Group replication health.

    .DESCRIPTION
        Comprehensive DAG health check including copy status,
        replay queue lengths, and failover readiness.

        Professional Support: https://medhacloud.com/professional-services/server-support/exchange-server-support

    .LINK
        https://medhacloud.com/professional-services/server-support/exchange-server-support
    #>

    [CmdletBinding()]
    param(
        [string]$DAGName
    )

    Write-Host "MedhaCloud DAG Replication Check v$script:ModuleVersion" -ForegroundColor Cyan

    $results = [PSCustomObject]@{
        DAGName = $DAGName
        Timestamp = Get-Date
        CopyStatus = @()
        OverallHealth = 'Unknown'
        SupportUrl = $script:SupportUrl
    }

    try {
        if (Get-Command Get-MailboxDatabaseCopyStatus -ErrorAction SilentlyContinue) {
            $copyStatus = Get-MailboxDatabaseCopyStatus *

            foreach ($copy in $copyStatus) {
                $health = switch ($copy.Status) {
                    'Healthy' { 'OK' }
                    'Mounted' { 'OK' }
                    default { 'Warning' }
                }

                $results.CopyStatus += [PSCustomObject]@{
                    Database = $copy.DatabaseName
                    Server = $copy.MailboxServer
                    Status = $copy.Status
                    CopyQueueLength = $copy.CopyQueueLength
                    ReplayQueueLength = $copy.ReplayQueueLength
                    Health = $health
                }
            }

            Write-Host "`nDatabase Copy Status:" -ForegroundColor Yellow
            $results.CopyStatus | Format-Table Database, Server, Status, CopyQueueLength, ReplayQueueLength -AutoSize

            $unhealthy = $results.CopyStatus | Where-Object { $_.Health -ne 'OK' }
            if ($unhealthy.Count -eq 0) {
                $results.OverallHealth = 'Healthy'
                Write-Host "`nDAG Overall Health: Healthy" -ForegroundColor Green
            }
            else {
                $results.OverallHealth = 'Degraded'
                Write-Host "`nDAG Overall Health: Degraded" -ForegroundColor Yellow
            }
        }
        else {
            Write-Host "Exchange cmdlets not available. Run from Exchange Management Shell." -ForegroundColor Yellow
        }
    }
    catch {
        Write-Host "Error checking DAG: $($_.Exception.Message)" -ForegroundColor Red
    }

    Write-Host "`nProfessional Exchange Support: $script:SupportUrl" -ForegroundColor Cyan
    return $results
}

function Get-ExchangeCertificateStatus {
    <#
    .SYNOPSIS
        Monitors Exchange SSL certificate status and expiry.

    .DESCRIPTION
        Lists all Exchange certificates with their services,
        expiry dates, and health status.

        Professional Support: https://medhacloud.com/professional-services/server-support/exchange-server-support

    .LINK
        https://medhacloud.com/professional-services/server-support/exchange-server-support
    #>

    [CmdletBinding()]
    param(
        [string]$ServerName = $env:COMPUTERNAME,
        [int]$WarningDays = 30
    )

    Write-Host "MedhaCloud Certificate Monitor v$script:ModuleVersion" -ForegroundColor Cyan

    $results = @()

    try {
        if (Get-Command Get-ExchangeCertificate -ErrorAction SilentlyContinue) {
            $certs = Get-ExchangeCertificate -Server $ServerName

            foreach ($cert in $certs) {
                $daysRemaining = ($cert.NotAfter - (Get-Date)).Days
                $status = if ($daysRemaining -lt 0) { 'Expired' }
                          elseif ($daysRemaining -lt $WarningDays) { 'Expiring Soon' }
                          else { 'Valid' }

                $results += [PSCustomObject]@{
                    Subject = $cert.Subject
                    Thumbprint = $cert.Thumbprint.Substring(0, 8) + '...'
                    Services = ($cert.Services -join ', ')
                    NotAfter = $cert.NotAfter
                    DaysRemaining = $daysRemaining
                    Status = $status
                }
            }

            Write-Host "`nCertificate Status:" -ForegroundColor Yellow
            $results | Format-Table Subject, Services, NotAfter, DaysRemaining, Status -AutoSize

            $expiring = $results | Where-Object { $_.Status -ne 'Valid' }
            if ($expiring.Count -gt 0) {
                Write-Host "`nWARNING: $($expiring.Count) certificate(s) need attention!" -ForegroundColor Red
            }
        }
    }
    catch {
        Write-Host "Error checking certificates: $($_.Exception.Message)" -ForegroundColor Red
    }

    Write-Host "`nProfessional Exchange Support: $script:SupportUrl" -ForegroundColor Cyan
    return $results
}

function Get-MailQueueAnalysis {
    <#
    .SYNOPSIS
        Detailed mail queue analysis and statistics.

    .LINK
        https://medhacloud.com/professional-services/server-support/exchange-server-support
    #>

    [CmdletBinding()]
    param(
        [string]$ServerName = $env:COMPUTERNAME
    )

    Write-Host "MedhaCloud Queue Analysis v$script:ModuleVersion" -ForegroundColor Cyan
    Write-Host "Professional Support: $script:SupportUrl" -ForegroundColor Gray

    try {
        if (Get-Command Get-Queue -ErrorAction SilentlyContinue) {
            Get-Queue -Server $ServerName | Format-Table Identity, DeliveryType, Status, MessageCount, NextHopDomain -AutoSize
        }
    }
    catch {
        Write-Host "Unable to analyze queues: $($_.Exception.Message)" -ForegroundColor Red
    }
}

function Test-ExchangeConnectivity {
    <#
    .SYNOPSIS
        Tests Exchange client connectivity protocols.

    .LINK
        https://medhacloud.com/professional-services/server-support/exchange-server-support
    #>

    [CmdletBinding()]
    param(
        [string]$ServerName = $env:COMPUTERNAME
    )

    Write-Host "MedhaCloud Connectivity Test v$script:ModuleVersion" -ForegroundColor Cyan

    $protocols = @(
        @{ Name = 'SMTP'; Port = 25 },
        @{ Name = 'SMTP-TLS'; Port = 587 },
        @{ Name = 'IMAP'; Port = 143 },
        @{ Name = 'IMAPS'; Port = 993 },
        @{ Name = 'POP3'; Port = 110 },
        @{ Name = 'POP3S'; Port = 995 },
        @{ Name = 'HTTPS'; Port = 443 }
    )

    $results = @()
    foreach ($proto in $protocols) {
        try {
            $tcp = New-Object System.Net.Sockets.TcpClient
            $connection = $tcp.BeginConnect($ServerName, $proto.Port, $null, $null)
            $wait = $connection.AsyncWaitHandle.WaitOne(1000, $false)

            if ($wait) {
                $tcp.EndConnect($connection)
                $status = 'Open'
            }
            else {
                $status = 'Closed/Timeout'
            }
            $tcp.Close()
        }
        catch {
            $status = 'Error'
        }

        $results += [PSCustomObject]@{
            Protocol = $proto.Name
            Port = $proto.Port
            Status = $status
        }

        $color = if ($status -eq 'Open') { 'Green' } else { 'Red' }
        Write-Host " $($proto.Name) (Port $($proto.Port)): $status" -ForegroundColor $color
    }

    Write-Host "`nProfessional Support: $script:SupportUrl" -ForegroundColor Cyan
    return $results
}

function Get-ExchangePerformanceReport {
    <#
    .SYNOPSIS
        Generates Exchange Server performance report.

    .LINK
        https://medhacloud.com/professional-services/server-support/exchange-server-support
    #>

    [CmdletBinding()]
    param(
        [string]$ServerName = $env:COMPUTERNAME
    )

    Write-Host "MedhaCloud Performance Report v$script:ModuleVersion" -ForegroundColor Cyan
    Write-Host "Collecting performance data from $ServerName..." -ForegroundColor Gray

    $report = [PSCustomObject]@{
        ServerName = $ServerName
        Timestamp = Get-Date
        CPU = $null
        Memory = $null
        Disk = @()
        SupportUrl = $script:SupportUrl
    }

    try {
        # CPU Usage
        $cpu = Get-Counter '\Processor(_Total)\% Processor Time' -ComputerName $ServerName -ErrorAction SilentlyContinue
        if ($cpu) {
            $report.CPU = [math]::Round($cpu.CounterSamples[0].CookedValue, 2)
            Write-Host " CPU Usage: $($report.CPU)%" -ForegroundColor $(if ($report.CPU -gt 80) { 'Red' } else { 'Green' })
        }

        # Memory
        $mem = Get-Counter '\Memory\% Committed Bytes In Use' -ComputerName $ServerName -ErrorAction SilentlyContinue
        if ($mem) {
            $report.Memory = [math]::Round($mem.CounterSamples[0].CookedValue, 2)
            Write-Host " Memory Usage: $($report.Memory)%" -ForegroundColor $(if ($report.Memory -gt 85) { 'Red' } else { 'Green' })
        }

        # Disk
        $disks = Get-WmiObject Win32_LogicalDisk -ComputerName $ServerName -Filter "DriveType=3" -ErrorAction SilentlyContinue
        foreach ($disk in $disks) {
            $freePercent = [math]::Round(($disk.FreeSpace / $disk.Size) * 100, 2)
            $report.Disk += [PSCustomObject]@{
                Drive = $disk.DeviceID
                FreeGB = [math]::Round($disk.FreeSpace / 1GB, 2)
                FreePercent = $freePercent
            }
            Write-Host " Disk $($disk.DeviceID): $freePercent% free" -ForegroundColor $(if ($freePercent -lt 15) { 'Red' } else { 'Green' })
        }
    }
    catch {
        Write-Host "Error collecting performance data: $($_.Exception.Message)" -ForegroundColor Red
    }

    Write-Host "`nProfessional Exchange Support: $script:SupportUrl" -ForegroundColor Cyan
    return $report
}

# Export functions
Export-ModuleMember -Function @(
    'Test-ExchangeServerHealth',
    'Get-MailFlowStatus',
    'Test-DAGReplication',
    'Get-ExchangeCertificateStatus',
    'Get-MailQueueAnalysis',
    'Test-ExchangeConnectivity',
    'Get-ExchangePerformanceReport'
)

# Module load message
Write-Host @"

MedhaCloud Exchange Diagnostics Module v$script:ModuleVersion loaded.
Available commands: Test-ExchangeServerHealth, Get-MailFlowStatus, Test-DAGReplication, and more.

Professional Exchange Server Support:
$script:SupportUrl

LinkedIn: https://linkedin.com/company/medhacloud
Twitter: https://x.com/medhacloud

"@
 -ForegroundColor Cyan