internal/functions/Sync-LdapObjectParallel.ps1

function Sync-LdapObjectParallel
{
<#
    .SYNOPSIS
        Start LDAP-based single-object replication in parallel.
     
    .DESCRIPTION
        Start LDAP-based single-object replication.
        All servers will start sync with the target DC in parallel.
     
        Use this to minimize replication latency for critical changes on a single object, such as the krbtgt account.
     
    .PARAMETER Object
        The distinguished name of the object to replicate.
     
    .PARAMETER Server
        The server(s) from which to trigger the replication.
     
    .PARAMETER Target
        The target DC to replicate with.
     
    .PARAMETER Throttle
        Up to how many replications should be triggered in parallel.
        Defaults to 8 times the CPU count (this action mostly consists on waiting for the network response).
     
    .PARAMETER Credential
        Credentials to use when connecting to the Server(s).
        No connecting credentials for the target server are necessary, as this is handled by the servers operated against.
 
    .PARAMETER Reverse
        Reverse the sync order.
        By default, each server is instructed to replicate with the target.
        By reversing this, the target is instructed to replicate with each server instead.
     
    .PARAMETER Configuration
        Whether the object being replicated is in the configuration partition.
     
    .EXAMPLE
        PS C:\> Sync-LdapObjectParallel -Object 'CN=krbtgt,CN=Users,DC=contoso,DC=com' -Server 'dc2.contoso.com','dc3.contoso.com' -Target 'dc1.contoso.com'
     
        Replicates the krbtgt account from dc1 to dc2 & dc3 in parallel.
#>

    [Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSReviewUnusedParameter", "")]
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true, Position = 0)]
        [string]
        $Object,
        
        [Parameter(Mandatory = $true, Position = 1)]
        [string[]]
        $Server,
        
        [Parameter(Mandatory = $true, Position = 2)]
        [string]
        $Target,
        
        [int]
        $Throttle = ($env:NUMBER_OF_PROCESSORS * 8),
        
        [PSCredential]
        $Credential,

        [switch]
        $Reverse,
        
        [switch]
        $Configuration
    )
    
    begin
    {
        #region Scriptblock
        $scriptblock = {
            param (
                $Settings
            )
            & (Get-Module krbtgt) {
                try
                {
                    $null = Sync-LdapObject @Settings
                    [PSCustomObject]@{
                        ComputerName = $Settings.Server
                        Success         = $true
                        Object         = $Settings.Object
                        Message         = ""
                        ExitCode     = 0
                        Error         = $null
                    }
                }
                catch
                {
                    [PSCustomObject]@{
                        ComputerName = $Settings.Server
                        Success         = $false
                        Object         = $Settings.Object
                        Message         = "$_"
                        ExitCode     = 1
                        Error         = $_
                    }
                }
            }
        }
        #endregion Scriptblock
        
        $parameters = $PSBoundParameters | ConvertTo-PSFHashtable -Include Object, Target, Configuration, Credential
        if ($Reverse) { $parameters.Server = $Target }

        $initialSessionState = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault()
        $initialSessionState.ImportPSModule(("{0}\PSFramework.psd1" -f (Get-Module PSFramework).ModuleBase))
        $initialSessionState.ImportPSModule(("{0}\krbtgt.psd1" -f (Get-Module krbtgt).ModuleBase))
        $pool = [RunspaceFactory]::CreateRunspacePool($initialSessionState)
        $null = $pool.SetMinRunspaces(1)
        $null = $pool.SetMaxRunspaces($Throttle)
        $pool.ApartmentState = "MTA"
        $pool.Open()
        $runspaces = @()
    }
    process
    {
        foreach ($serverName in $Server)
        {
            $tempParameters = $parameters.Clone()
            if ($Reverse) { $tempParameters.Target = $serverName }
            else { $tempParameters.Server = $serverName }
            $runspace = [PowerShell]::Create()
            $null = $runspace.AddScript($scriptBlock)
            $null = $runspace.AddArgument($tempParameters)
            $runspace.RunspacePool = $pool
            $runspaces += [PSCustomObject]@{ Pipe = $runspace; Status = $runspace.BeginInvoke() }
        }
        foreach ($runspace in $runspaces)
        {
            $runspace.Pipe.EndInvoke($runspace.Status)
            $runspace.Pipe.Dispose()
        }
    }
    end
    {
        $pool.Close()
        $pool.Dispose()
    }
}