functions/Sync-KrbAccount.ps1
function Sync-KrbAccount { <# .SYNOPSIS Forces a single item AD Replication. .DESCRIPTION Will command the replication of an AD User object between two DCs. Uses PowerShell remoting against the source DC(s). .PARAMETER SourceDC The DC to start the synchronization command from. .PARAMETER TargetDC The DC to replicate with. .PARAMETER Credential Credentials to use for performing AD actions .PARAMETER Identity The user identity to replicate. Defaults to krbtgt. .PARAMETER ReplicationMode Whether to trigger replication through WinRM or LDAP. Defaults to LDAP .PARAMETER EnableException This parameters disables user-friendly warnings and enables the throwing of exceptions. This is less user friendly, but allows catching exceptions in calling scripts. .EXAMPLE PS C:\> Sync-KrbAccount -SourceDC 'dc1' -TargetDC 'dc2' Replicates the krbtgt account between dc1 and dc2. #> [CmdletBinding()] param ( [Parameter(Mandatory = $true, ValueFromPipeline = $true)] [PSFComputer[]] $SourceDC, [Parameter(Mandatory = $true)] [string] $TargetDC, [System.Management.Automation.PSCredential] $Credential, [string] $Identity = 'krbtgt', [ValidateSet('LDAP', 'WinRM')] [string] $ReplicationMode = (Get-PSFConfigValue -FullName 'Krbtgt.Sync.Protocol' -Fallback 'LDAP'), [switch] $EnableException ) begin { $credParam = $PSBoundParameters | ConvertTo-PSFHashtable -Include Credential try { $krbtgtDN = (Get-ADUser @credParam -Identity $Identity -Server $TargetDC -ErrorAction Stop).DistinguishedName } catch { Stop-PSFFunction -String 'Sync-KrbAccount.UserNotFound' -StringValues $Identity, $TargetDC -ErrorRecord $_ return } } process { if (Test-PSFFunctionInterrupt) { return } $errorVar = @() $pwdLastSet = [System.DateTime]::FromFileTimeUtc((Get-ADObject @credParam -Identity $krbtgtDN -Server $TargetDC -Properties PwdLastSet).PwdLastSet) switch ($ReplicationMode) { #region LDAP Based 'LDAP' { Sync-LdapObjectParallel @credParam -Object $krbtgtDN -Server $SourceDC -Target $TargetDC -Reverse } #endregion LDAP Based #region WinRM Based 'WinRM' { Write-PSFMessage -String 'Sync-KrbAccount.Connecting' -StringValues ($SourceDC -join ', '), $krbtgtDN -Target $SourceDC Invoke-PSFCommand @credParam -ComputerName $SourceDC -ScriptBlock { param ( $TargetDC, $KrbtgtDN, $PwdLastSet ) $message = repadmin.exe /replsingleobj $env:COMPUTERNAME $TargetDC $KrbtgtDN *>&1 $result = 0 -eq $LASTEXITCODE # Verify the password change was properly synced $pwdLastSetLocal = [System.DateTime]::FromFileTimeUtc((Get-ADObject -Identity $KrbtgtDN -Server $env:COMPUTERNAME -Properties PwdLastSet).PwdLastSet) if ($pwdLastSetLocal -ne $PwdLastSet) { $result = $false } [PSCustomObject]@{ ComputerName = $env:COMPUTERNAME Success = $result Message = ($message | Where-Object { $_ }) ExitCode = $LASTEXITCODE Error = $null } } -ArgumentList $TargetDC, $krbtgtDN, $pwdLastSet -ErrorVariable errorVar -ErrorAction SilentlyContinue | Select-PSFObject -KeepInputObject -TypeName 'Krbtgt.SyncResult' foreach ($errorObject in $errorVar) { Write-PSFMessage -Level Warning -String 'Sync-KrbAccount.ConnectError' -StringValues $errorObject.TargetObject -ErrorRecord $errorObject [PSCustomObject]@{ PSTypeName = 'Krbtgt.SyncResult' ComputerName = $errorObject.TargetObject Success = $false Message = $errorObject.Exception.Message ExitCode = 1 Error = $errorObject } } } #endregion WinRM Based } } } |