ADEssentials.psm1

function Get-WinADForestReplication {
    [CmdletBinding()]
    param([switch] $Extended,
        [Array] $DomainControllers)
    if (-not $DomainControllers) { $DomainControllers = Get-WinADForestControllers }
    $ProcessErrors = [System.Collections.Generic.List[PSCustomObject]]::new()
    $Replication = foreach ($DC in $DomainControllers) {
        try { Get-ADReplicationPartnerMetadata -Target $DC.HostName -Partition * -ErrorAction Stop } catch {
            Write-Warning -Message "Get-WinADForestReplication - Error on server $($_.Exception.ServerName): $($_.Exception.Message)"
            $ProcessErrors.Add([PSCustomObject] @{Server = $_.Exception.ServerName; StatusMessage = $_.Exception.Message })
        }
    }
    foreach ($_ in $Replication) {
        $ServerPartner = (Resolve-DnsName -Name $_.PartnerAddress -Verbose:$false -ErrorAction SilentlyContinue)
        $ServerInitiating = (Resolve-DnsName -Name $_.Server -Verbose:$false -ErrorAction SilentlyContinue)
        $ReplicationObject = [ordered] @{Server = $_.Server
            ServerIPV4 = $ServerInitiating.IP4Address
            ServerPartner = $ServerPartner.NameHost
            ServerPartnerIPV4 = $ServerPartner.IP4Address
            LastReplicationAttempt = $_.LastReplicationAttempt
            LastReplicationResult = $_.LastReplicationResult
            LastReplicationSuccess = $_.LastReplicationSuccess
            ConsecutiveReplicationFailures = $_.ConsecutiveReplicationFailures
            LastChangeUsn = $_.LastChangeUsn
            PartnerType = $_.PartnerType
            Partition = $_.Partition
            TwoWaySync = $_.TwoWaySync
            ScheduledSync = $_.ScheduledSync
            SyncOnStartup = $_.SyncOnStartup
            CompressChanges = $_.CompressChanges
            DisableScheduledSync = $_.DisableScheduledSync
            IgnoreChangeNotifications = $_.IgnoreChangeNotifications
            IntersiteTransport = $_.IntersiteTransport
            IntersiteTransportGuid = $_.IntersiteTransportGuid
            IntersiteTransportType = $_.IntersiteTransportType
            UsnFilter = $_.UsnFilter
            Writable = $_.Writable
            Status = if ($_.LastReplicationResult -ne 0) { $false } else { $true }
            StatusMessage = "Last successful replication time was $($_.LastReplicationSuccess), Consecutive Failures: $($_.ConsecutiveReplicationFailures)"
        }
        if ($Extended) {
            $ReplicationObject.Partner = $_.Partner
            $ReplicationObject.PartnerAddress = $_.PartnerAddress
            $ReplicationObject.PartnerGuid = $_.PartnerGuid
            $ReplicationObject.PartnerInvocationId = $_.PartnerInvocationId
            $ReplicationObject.PartitionGuid = $_.PartitionGuid
        }
        [PSCustomObject] $ReplicationObject
    }
    foreach ($_ in $ProcessErrors) { if ($null -ne $_.Server) { $ServerInitiating = (Resolve-DnsName -Name $_.Server -Verbose:$false -ErrorAction SilentlyContinue) } else { $ServerInitiating = [PSCustomObject] @{IP4Address = '127.0.0.1' } }
        $ReplicationObject = [ordered] @{Server = $_.Server
            ServerIPV4 = $ServerInitiating.IP4Address
            ServerPartner = 'Unknown'
            ServerPartnerIPV4 = '127.0.0.1'
            LastReplicationAttempt = $null
            LastReplicationResult = $null
            LastReplicationSuccess = $null
            ConsecutiveReplicationFailures = $null
            LastChangeUsn = $null
            PartnerType = $null
            Partition = $null
            TwoWaySync = $null
            ScheduledSync = $null
            SyncOnStartup = $null
            CompressChanges = $null
            DisableScheduledSync = $null
            IgnoreChangeNotifications = $null
            IntersiteTransport = $null
            IntersiteTransportGuid = $null
            IntersiteTransportType = $null
            UsnFilter = $null
            Writable = $null
            Status = $false
            StatusMessage = $_.StatusMessage
        }
        if ($Extended) {
            $ReplicationObject.Partner = $null
            $ReplicationObject.PartnerAddress = $null
            $ReplicationObject.PartnerGuid = $null
            $ReplicationObject.PartnerInvocationId = $null
            $ReplicationObject.PartitionGuid = $null
        }
        [PSCustomObject] $ReplicationObject
    }
}
function Get-WinADSiteConnections {
    [CmdletBinding()]
    param()
    [Flags()]
    enum ConnectionOption {
        None
        IsGenerated
        TwoWaySync
        OverrideNotifyDefault = 4
        UseNotify = 8
        DisableIntersiteCompression = 16
        UserOwnedSchedule = 32
        RodcTopology = 64
    }
    $NamingContext = (Get-ADRootDSE).configurationNamingContext
    $Connections = Get-ADObject –Searchbase $NamingContext -LDAPFilter "(objectCategory=ntDSConnection)" -Properties *
    $FormmatedConnections = foreach ($_ in $Connections) {
        $Dictionary = [PSCustomObject] @{CN = $_.CN
            Description = $_.Description
            DisplayName = $_.DisplayName
            EnabledConnection = $_.enabledConnection
            ServerFrom = if ($_.fromServer -match '(?<=CN=NTDS Settings,CN=)(.*)(?=,CN=Servers,)') { $Matches[0] } else { $_.fromServer }
            ServerTo = if ($_.DistinguishedName -match '(?<=CN=NTDS Settings,CN=)(.*)(?=,CN=Servers,)') { $Matches[0] } else { $_.fromServer }
            SiteFrom = if ($_.fromServer -match '(?<=,CN=Servers,CN=)(.*)(?=,CN=Sites,CN=Configuration)') { $Matches[0] } else { $_.fromServer }
            SiteTo = if ($_.DistinguishedName -match '(?<=,CN=Servers,CN=)(.*)(?=,CN=Sites,CN=Configuration)') { $Matches[0] } else { $_.fromServer }
            OptionsTranslated = [ConnectionOption] $_.Options
            Options = $_.Options
            WhenCreated = $_.WhenCreated
            WhenChanged = $_.WhenChanged
            IsDeleted = $_.IsDeleted
        }
        $Dictionary
    }
    $FormmatedConnections
}
function Get-WinADSiteLinks {
    [CmdletBinding()]
    param()
    $NamingContext = (Get-ADRootDSE).configurationNamingContext
    $SiteLinks = Get-ADObject -LDAPFilter "(objectCategory=sitelink)" –Searchbase $NamingContext -Properties *
    foreach ($_ in $SiteLinks) {
        [PSCustomObject] @{Name = $_.CN
            Cost = $_.Cost
            ReplicationFrequencyInMinutes = $_.ReplInterval
            Options = $_.Options
            Created = $_.WhenCreated
            Modified = $_.WhenChanged
            ProtectedFromAccidentalDeletion = $_.ProtectedFromAccidentalDeletion
        }
    }
}
function Set-WinADReplication {
    [CmdletBinding()]
    param([int] $ReplicationInterval = 15,
        [switch] $Instant)
    $NamingContext = (Get-ADRootDSE).configurationNamingContext
    Get-ADObject -LDAPFilter "(objectCategory=sitelink)" –Searchbase $NamingContext -Properties options | ForEach-Object { if ($Instant) { Set-ADObject $_ -replace @{replInterval = $ReplicationInterval }
            Set-ADObject $_ –replace @{options = $($_.options -bor 1) }
        } else { Set-ADObject $_ -replace @{replInterval = $ReplicationInterval }
        } }
}
function Set-WinADReplicationConnections {
    [CmdletBinding()]
    param([switch] $Force)
    [Flags()]
    enum ConnectionOption {
        None
        IsGenerated
        TwoWaySync
        OverrideNotifyDefault = 4
        UseNotify = 8
        DisableIntersiteCompression = 16
        UserOwnedSchedule = 32
        RodcTopology = 64
    }
    $NamingContext = (Get-ADRootDSE).configurationNamingContext
    $Connections = Get-ADObject –Searchbase $NamingContext -LDAPFilter "(objectCategory=ntDSConnection)" -Properties *
    foreach ($_ in $Connections) {
        $OptionsTranslated = [ConnectionOption] $_.Options
        if ($OptionsTranslated -like '*IsGenerated*' -and -not $Force) { Write-Verbose "Set-WinADReplicationConnections - Skipping $($_.CN) automatically generated link" } else {
            Write-Verbose "Set-WinADReplicationConnections - Changing $($_.CN)"
            Set-ADObject $_ –replace @{options = $($_.options -bor 8) }
        }
    }
}
function Sync-DomainController {
    [CmdletBinding()]
    param([string] $Domain = $Env:USERDNSDOMAIN)
    $DistinguishedName = (Get-ADDomain -Server $Domain).DistinguishedName
    (Get-ADDomainController -Filter * -Server $Domain).Name | ForEach-Object { Write-Verbose -Message "Sync-DomainController - Forcing synchronization $_"
        repadmin /syncall $_ $DistinguishedName /e /A | Out-Null }
}
Function Test-LDAP {
    [CmdletBinding()]
    param ([alias('Server', 'IpAddress')][Parameter(Mandatory = $True)][string[]]$ComputerName,
        [int] $GCPortLDAP = 3268,
        [int] $GCPortLDAPSSL = 3269,
        [int] $PortLDAP = 389,
        [int] $PortLDAPS = 636)
    foreach ($Computer in $ComputerName) {
        [Array] $ADServerFQDN = (Resolve-DnsName -Name $Computer -ErrorAction SilentlyContinue)
        if ($ADServerFQDN) {
            if ($ADServerFQDN.NameHost) { $ServerName = $ADServerFQDN[0].NameHost } else {
                [Array] $ADServerFQDN = (Resolve-DnsName -Name $Computer -ErrorAction SilentlyContinue)
                $FilterName = $ADServerFQDN | Where-Object { $_.QueryType -eq 'A' }
                $ServerName = $FilterName[0].Name
            }
        } else { $ServerName = '' }
        $GlobalCatalogSSL = Test-LDAPPorts -ServerName $ServerName -Port $GCPortLDAPSSL
        $GlobalCatalogNonSSL = Test-LDAPPorts -ServerName $ServerName -Port $GCPortLDAP
        $ConnectionLDAPS = Test-LDAPPorts -ServerName $ServerName -Port $PortLDAPS
        $ConnectionLDAP = Test-LDAPPorts -ServerName $ServerName -Port $PortLDAP
        $PortsThatWork = @(if ($GlobalCatalogNonSSL) { $GCPortLDAP }
            if ($GlobalCatalogSSL) { $GCPortLDAPSSL }
            if ($ConnectionLDAP) { $PortLDAP }
            if ($ConnectionLDAPS) { $PortLDAPS }) | Sort-Object
    [pscustomobject]@{Computer = $Computer
        ComputerFQDN = $ServerName
        GlobalCatalogLDAP = $GlobalCatalogNonSSL
        GlobalCatalogLDAPS = $GlobalCatalogSSL
        LDAP = $ConnectionLDAP
        LDAPS = $ConnectionLDAPS
        AvailablePorts = $PortsThatWork -join ','
    }
}
}
function Test-LDAPPorts {
    [CmdletBinding()]
    param([string] $ServerName,
        [int] $Port)
    if ($ServerName -and $Port -ne 0) {
        try {
            $LDAP = "LDAP://" + $ServerName + ':' + $Port
            $Connection = [ADSI]($LDAP)
            $Connection.Close()
            return $true
        } catch { if ($_.Exception.ToString() -match "The server is not operational") { Write-Warning "Can't open $ServerName`:$Port." } elseif ($_.Exception.ToString() -match "The user name or password is incorrect") { Write-Warning "Current user ($Env:USERNAME) doesn't seem to have access to to LDAP on port $Server`:$Port" } else { Write-Warning -Message $_ } }
        return $False
    }
}
Export-ModuleMember -Function @('Get-WinADForestReplication', 'Get-WinADSiteConnections', 'Get-WinADSiteLinks', 'Set-WinADReplication', 'Set-WinADReplicationConnections', 'Sync-DomainController', 'Test-LDAP') -Alias @()