functions/Sync-AdcObject.ps1

function Sync-AdcObject
{
<#
    .SYNOPSIS
        Replicate a single item between DCs.
     
    .DESCRIPTION
        Replicate a single item between DCs.
        Can use either WinRM or LDAP to trigger a bulk sync of a single object in parallel.
         
        Use this command to ensure a specific object in AD has been replicated across all targeted DCs.
     
    .PARAMETER Object
        The object to replicate.
        Must be a distinguishedName.
     
    .PARAMETER Source
        The server that contains the current state of the targeted object.
     
    .PARAMETER Target
        The server(s) that should receive the latest updates for the targeted object.
     
    .PARAMETER Credential
        Credentials tro use for authenticating the operation.
     
    .PARAMETER Type
        Which protocl should be used to trigger the replication.
        Can use either LDAP or WinRM, defaults to LDAP.
     
    .PARAMETER Reverse
        Reverses the replication order in an LDAP-based replication task.
        In this case, rather than fanning out network connections, the source server is ordered to replicate with each specified target server.
     
    .EXAMPLE
        PS C:\> Sync-AdcObject -Object $userAccount -Source $pdc -Target (Get-ADComputer -LdapFilter "(&(primaryGroupID=516)(!(name=$pdc)))").DNSHostName
         
        Replicates the AD object stored in $userAccount across all domain controllers in the current domain.
#>

    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]
        $Object,
        
        [Parameter(Mandatory = $true)]
        [string]
        $Source,
        
        [Parameter(Mandatory = $true)]
        [string[]]
        $Target,
        
        [PSCredential]
        $Credential,
        
        [ValidateSet('LDAP','WinRM')]
        [string]
        $Type = 'LDAP',
        
        [switch]
        $Reverse
    )
    
    begin
    {
        $credParam = $PSBoundParameters | ConvertTo-PSFHashtable -Include Credential
    }
    process
    {
        $results = switch ($Type) {
            #region LDAP-triggered replication
            'LDAP'
            {
                $adObject = Get-ADObject @credParam -Identity $Object -Server $Source -Properties ObjectGUID
                Sync-LdapObjectParallel @credParam -Object $adObject.ObjectGUID -Server $Target -Target $Source -Reverse:$Reverse
            }
            #endregion LDAP-triggered replication
            #region WinRM-triggered replication
            'WinRM'
            {
                Invoke-PSFCommand @credParam -ComputerName $Target -ScriptBlock {
                    param (
                        $TargetDC,
                        
                        $ADObject
                    )
                    
                    $message = repadmin.exe /replsingleobj $env:COMPUTERNAME $TargetDC $ADObject *>&1
                    $result = 0 -eq $LASTEXITCODE
                    
                    [PSCustomObject]@{
                        ComputerName = $env:COMPUTERNAME
                        Success         = $result
                        Message         = ($message | Where-Object { $_ })
                        ExitCode     = $LASTEXITCODE
                        Error         = $null
                    }
                } -ArgumentList $Source, $Object -ErrorVariable errorVar -ErrorAction SilentlyContinue | Select-PSFObject -KeepInputObject -TypeName 'ADMF.Core.SyncResult'
                
                foreach ($errorObject in $errorVar) {
                    if ($errorObject.InvocationInfo.InvocationName -ne 'Invoke-PSFCommand') { continue }
                    Write-PSFMessage -Level Warning -String 'Sync-AdcObject.ConnectError' -StringValues $errorObject.TargetObject -ErrorRecord $errorObject
                    [PSCustomObject]@{
                        PSTypeName   = 'ADMF.Core.SyncResult'
                        ComputerName = $errorObject.TargetObject.ConnectionInfo.ComputerName
                        Success         = $false
                        Message         = $errorObject.Exception.Message
                        ExitCode     = 1
                        Error         = $errorObject
                    }
                }
            }
            #endregion WinRM-triggered replication
        }
        
        [PSCustomObject]@{
            Success = $results.Success -notcontains $false
            SuccessPercent = @($results).Where{ $_.Success }.Count / $Target.Count * 100
            Results = $results
            ServerSuccess = @($results).Where{ $_.Success }.ComputerName
            ServerFailed = @($results).Where{ -not $_.Success }.ComputerName
            SuccessCount = ($results | Where-Object Success | Measure-Object).Count
        }
    }
}