Start-AdConDeltaSync.psm1

<#
.SYNOPSIS
    Initiates a delta synchronization cycle on the active Azure AD Connect server.
 
.DESCRIPTION
    This function identifies the active Azure AD Connect server from a provided list of servers,
    waits for any ongoing synchronization to complete, and then starts a delta sync cycle.
 
    It checks each server for staging mode and selects the one that is actively syncing.
    If a sync is already in progress, it waits and retries until the sync is complete.
 
.PARAMETER Servers
    An array of server names to check for Azure AD Connect activity.
    The function will select the first server that is not in staging mode.
 
.PARAMETER SleepTimeSeconds
    The number of seconds to wait between checks if a sync cycle is already in progress.
    Default is 8 seconds.
 
.EXAMPLE
    Start-AdConDeltaSync
 
    Runs the function with the default server list and sleep time of 8 seconds.
 
.EXAMPLE
    Start-AdConDeltaSync -Servers @('adconnect01.domain.com', 'adconnect02.domain.com') -SleepTimeSeconds 10
 
    Runs the function with a custom list of servers and a sleep interval of 10 seconds.
 
.NOTES
    Author: Anthony J. Celaya
    Created: August 2025
    Purpose: Automate delta sync initiation for Azure AD Connect in a multi-server setup.
 
    This function assumes that:
    - PowerShell remoting is enabled and accessible on the target servers.
    - The `Get-ADSyncScheduler` and `Start-ADSyncSyncCycle` cmdlets are available on those servers.
 
.LINK
    https://learn.microsoft.com/en-us/azure/active-directory/hybrid/how-to-connect-sync-feature-scheduler
#>

function Start-AdConDeltaSync {
    [CmdletBinding(SupportsShouldProcess)]
    param (
        [Parameter()]
        [String[]]$Servers = @(),

        [Parameter()]
        [int]$SleepTimeSeconds = 8
    )

    begin {
        $activeServer = $null

        if (-not $servers -or $servers.Count -eq 0) {
            $configPath = [System.IO.FileInfo](Join-Path $PSScriptRoot "config.ini")
            $config = @{}
            if ( $configPath.Exists ){
                foreach ( $line in $(get-Content $configPath)) {
                    if ($line -match '^(\w+)=(.+)$') {
                        $config[$matches[1]] = $matches[2].Trim()
                    }
                }
            }
            $servers = @($config["Primary"], $config["Secondary"])
        }
    }

    process {
        # Find the active server
        foreach ($s in $servers) {
            try {
                $sync = Invoke-Command -ComputerName $s -ScriptBlock { Get-ADSyncScheduler } -ErrorAction Stop
                if (-not $sync.StagingModeEnabled) {
                    $activeServer = $sync.PSComputerName
                    Write-Verbose ("{0:s}: Found active server '{1}'." -f (Get-Date), $activeServer)
                    break
                }
            } catch {
                Write-Warning ("{0:s}: Failed to query server '{1}': {2}" -f (Get-Date), $s, $_.Exception.Message)
            }
        }

        if (-not $activeServer) {
            throw ("{0:s}: No active server found!" -f (Get-Date))
        }

        # Wait for any ongoing sync to finish
        $counter = 0
        do {
            $counter++
            $sync = Invoke-Command -ComputerName $activeServer -ScriptBlock { Get-ADSyncScheduler }
            if ($sync.SyncCycleInProgress) {
                Write-Verbose ("{0:s}: Sync in progress. Retrying in {1} seconds..." -f (Get-Date), $SleepTimeSeconds)
                Start-Sleep -Seconds $SleepTimeSeconds
            }
        } while ($sync.SyncCycleInProgress)

        # Double-check staging mode before starting sync
        if ($sync.StagingModeEnabled) {
            Write-Warning ("{0:s}: '{1}' is in staging mode. Aborting sync." -f (Get-Date), $activeServer)
        } else {
            Write-Verbose ("{0:s}: Starting delta sync on '{1}'." -f (Get-Date), $activeServer)
            if ( $PsCmdlet.ShouldProcess("Server: $activeServer", "Start delta sync") ){
                Invoke-Command -ComputerName $activeServer -ScriptBlock { Start-ADSyncSyncCycle -PolicyType 'Delta' }
            }
        }
    }
}