Public/Get-EntraConnectSyncStatus.ps1

function Get-EntraConnectSyncStatus {
<#
.SYNOPSIS
    Reports the health and sync status of an Entra Connect (Azure AD Connect) server.

.DESCRIPTION
    Connects to the local or a remote Entra Connect server and collects:

    - Last sync cycle time and result (delta / initial / export)
    - Connector export/import error counts per connector
    - Password hash sync status
    - Staging mode state
    - Auto-upgrade setting
    - Pending export objects (adds, updates, deletes) per connector

    Outputs a summary to the console and optionally exports a CSV.
    Requires the ADSync module installed on the Entra Connect server.

.PARAMETER ComputerName
    Name of the Entra Connect server. Defaults to the local machine.

.PARAMETER ExportPath
    Optional path to export connector results as CSV.
    Example: "C:\temp\EntraConnectStatus.csv"

.EXAMPLE
    Get-EntraConnectSyncStatus
    Runs against the local machine (must be executed on the Entra Connect server).

.EXAMPLE
    Get-EntraConnectSyncStatus -ComputerName "AADCONN01" -ExportPath "C:\temp\sync.csv"
    Runs against a remote Entra Connect server and exports connector stats.

.NOTES
    Author: K Shankar R Karanth
    Website: https://karanth.ovh
    Version: 1.0
    Requires: ADSync PowerShell module (installed with Entra Connect),
              run as local Administrator or ADSyncAdmins group member
#>


    [CmdletBinding()]
    param (
        [string]$ComputerName = $env:COMPUTERNAME,
        [string]$ExportPath   = "C:\ADOpsKit\Reports\Get-EntraConnectSyncStatus\$(Get-Date -Format 'yyyy-MM-dd')_EntraConnectStatus.csv"
    )

    # ============ HELPERS ============

    function Write-Header {
        param([string]$Title)
        Write-Host "`n===== $Title =====" -ForegroundColor Cyan
    }

    function Write-StatusLine {
        param([string]$Label, $Value, [string]$OkValue = "")
        $color = if ($OkValue -and $Value -ne $OkValue) { 'Yellow' } else { 'White' }
        Write-Host (" {0,-40} {1}" -f $Label, $Value) -ForegroundColor $color
    }

    # ============ REMOTE OR LOCAL EXECUTION ============

    $isRemote = $ComputerName -ne $env:COMPUTERNAME

    $syncScript = {
        $ErrorActionPreference = 'Stop'

        if (-not (Get-Module -ListAvailable -Name ADSync)) {
            throw "ADSync module not found. Run this script on the Entra Connect server."
        }
        Import-Module ADSync

        # -- Global sync state --
        $scheduler   = Get-ADSyncScheduler
        $version     = (Get-ADSyncGlobalSettings).Parameters |
                       Where-Object Name -eq 'Microsoft.Synchronize.SynchronizationVersion' |
                       Select-Object -ExpandProperty Value

        # -- Connector summary --
        $connectors = Get-ADSyncConnector | ForEach-Object {
            $stats = Get-ADSyncConnectorStatistics -ConnectorName $_.Name

            [PSCustomObject]@{
                ConnectorName    = $_.Name
                Type             = $_.Type
                ExportErrors     = $stats.ExportErrors
                ImportErrors     = $stats.ImportErrors
                PendingExportAdd = $stats.PendingExportAdd
                PendingExportUpdate = $stats.PendingExportUpdate
                PendingExportDelete = $stats.PendingExportDelete
            }
        }

        # -- Password sync --
        $pwdSync = $null
        try {
            $pwdSync = Get-ADSyncAADPasswordSyncConfiguration -SourceConnector (
                $connectors | Where-Object Type -eq 'AD' | Select-Object -First 1 -ExpandProperty ConnectorName
            )
        } catch { <# Password sync not configured — skip #> }

        [PSCustomObject]@{
            Version              = $version
            StagingModeEnabled   = $scheduler.StagingModeEnabled
            AutoUpgradeState     = (Get-ADSyncAutoUpgrade).State
            SyncEnabled          = $scheduler.SyncEnabled
            NextSyncCycle        = $scheduler.NextSyncCyclePolicyType
            SchedulerSuspended   = $scheduler.SchedulerSuspended
            LastSuccessfulSync   = $scheduler.LastSyncRunStartTime
            PasswordSyncEnabled  = if ($pwdSync) { $pwdSync.Enabled } else { 'N/A' }
            Connectors           = $connectors
        }
    }

    # ============ EXECUTE ============

    try {
        if ($isRemote) {
            Write-Host "Connecting to $ComputerName..." -ForegroundColor Cyan
            $result = Invoke-Command -ComputerName $ComputerName -ScriptBlock $syncScript
        } else {
            $result = & $syncScript
        }
    } catch {
        Write-Error "Failed to retrieve Entra Connect status: $_"
        return
    }

    # ============ OUTPUT ============

    Write-Header "Entra Connect Global Status"
    Write-StatusLine "Server"                 $ComputerName
    Write-StatusLine "Entra Connect Version"  $result.Version
    Write-StatusLine "Staging Mode"           $result.StagingModeEnabled   "False"
    Write-StatusLine "Sync Enabled"           $result.SyncEnabled          "True"
    Write-StatusLine "Scheduler Suspended"    $result.SchedulerSuspended   "False"
    Write-StatusLine "Auto-Upgrade"           $result.AutoUpgradeState     "Enabled"
    Write-StatusLine "Next Sync Policy"       $result.NextSyncCycle
    Write-StatusLine "Last Successful Sync"   $result.LastSuccessfulSync
    Write-StatusLine "Password Sync Enabled"  $result.PasswordSyncEnabled  "True"

    Write-Header "Connector Summary"

    $result.Connectors | Format-Table -AutoSize `
        ConnectorName, Type, ExportErrors, ImportErrors,
        PendingExportAdd, PendingExportUpdate, PendingExportDelete

    $hasErrors = $result.Connectors | Where-Object { $_.ExportErrors -gt 0 -or $_.ImportErrors -gt 0 }
    if ($hasErrors) {
        Write-Host " ATTENTION: One or more connectors have sync errors." -ForegroundColor Red
        $hasErrors | Format-Table -AutoSize ConnectorName, ExportErrors, ImportErrors
    } else {
        Write-Host " All connectors report zero sync errors." -ForegroundColor Green
    }

    if ($result.StagingModeEnabled -eq $true) {
        Write-Host "`n WARNING: Entra Connect is in STAGING MODE - no changes are being written to Azure AD." -ForegroundColor Yellow
    }

    # ============ EXPORT ============

    if ($ExportPath -ne "") {
        $exportDir = Split-Path $ExportPath
        if ($exportDir -and -not (Test-Path $exportDir)) { New-Item -ItemType Directory -Path $exportDir -Force | Out-Null }
        $result.Connectors | Export-Csv -NoTypeInformation -Path $ExportPath
        Write-Host "`nConnector stats exported to $ExportPath" -ForegroundColor Green
    }
}