PeoplePickerPortProber.psm1

<#
    .SYNOPSIS
    Start-PeoplePickerPortProber
    .DESCRIPTION
    Start-PeoplePickerPortProber resolves domain names, checks for open ports, and queries user objects to determine if SharePoint can contact the Domain Controller(s).
    .PARAMETER LdapPath
        Provide the LDAP path to connect to the domain.
    .PARAMETER LdapPort
        The LDAP port for the Domain Controller(s).
    .PARAMETER LdapsPort
        The LDAPS port for the Domain Controller(s).
    .PARAMETER LdapFilter
        The LDAP filter to use.
    .PARAMETER FindOne
        Restrict the LDAP search to a single object. Default true.
    .PARAMETER Credential
        Credentials used to connect to the Domain Controllers.
    .PARAMETER Timeout
        The time to wait to open connections to ports.
    .EXAMPLE
        Start-PeoplePickerPortProber -LdapPath 'LDAP://DC=example,DC=com' -Credential (Get-Credential)
    .EXAMPLE
        Start-PeoplePickerPortProber -LdapPath 'LDAP://DC=subdomain,DC=example,DC=com' -LdapFilter '(&(objectClass=User))'
    .NOTES
        Author: Trevor Seward
        Date: 09/15/2018
        https://thesharepointfarm.com
        https://github.com/Nauplius
#>


Function Start-PeoplePickerPortProber {
    param (
        [string]
        [ValidatePattern('(?i)^ldap(?-i)://.*(?i)DC(?-i)=.*')]
        [Parameter(Mandatory=$true)]
        $LdapPath,
        [int32]
        [Parameter(Mandatory=$false)]
        $LdapPort = 389,
        [int32]
        [Parameter(Mandatory=$false)]
        $LdapsPort = 636,
        [string]
        [Parameter(Mandatory=$false)]
        $LdapFilter = "(&(objectClass=user)(objectCategory=user))",
        [switch]
        [Parameter(Mandatory=$false)]
        $FindOne,
        [pscredential]
        $Credential,
        [int32]
        [Parameter(Mandatory=$false)]
        $Timeout = 200
    )

        $dnsName = Get-DnsName $LdapPath

        if($dnsName -eq '')
        {
            break
        }

        Write-Host -ForegroundColor Cyan "Resolved domain: $($dnsName)"

        $IPs = Test-Dns $dnsName

        foreach($IP in $IPs)
        {
            Test-Ports $IP $LdapPort $LdapsPort
        }

        Invoke-LdapSearch $LdapPath $LdapFilter $Credential $FindOne
        
}

Function Get-DnsName {
    param (
        [string]
        [Parameter(Mandatory=$true)]
        $LdapPath
    )
    $LdapPath = $LdapPath.ToLowerInvariant()
    $str = $LdapPath.Remove(0,$LdapPath.IndexOf('dc='))
    $dnsName = $str.Replace('dc=','').Replace(',','.')
    return $dnsName
}
Function Test-Dns {
    param (
        [string]
        [Parameter(Mandatory=$true)]
        $dnsName
    )
    $IPs = @([System.Net.Dns]::GetHostAddresses($dnsName))

    foreach($IP in $IPs)
    {
        try {
            $hostName = ([System.Net.Dns]::GetHostEntry($IP)).HostName
            Write-Host("$($IP) [$($hostName)]")          
        }
        catch {
            Write-Host -ForegroundColor Red "Unable to resolve hostname for $($IP)"
        }
    }

    return $IPs
}
Function Test-Ports {
    param (
        [string]
        [Parameter(Mandatory=$true)]
        $IPAddr,
        [int32]
        [Parameter(Mandatory=$true)]
        $LdapPort,
        [int32]
        [Parameter(Mandatory=$true)]
        $LdapsPort
    )

    $tcpPorts = @($LdapPort, $LdapsPort, 135, 137, 138, 139, 3268, 3269, 53, 88, 445, 749, 750)

    foreach($tcpPort in $tcpPorts)
    {
        [string]$optionalPort

        if ($tcpPort -eq 749 -or $tcpPort -eq 750)
        {
            $optionalPort = "[Opt]"
        }

        [System.Net.Sockets.Socket]$socket = $null
        $socket = New-Object System.Net.Sockets.Socket([System.Net.Sockets.AddressFamily]::InternetWork, [System.Net.Sockets.SocketType]::Stream, [System.Net.Sockets.ProtocolType]::Tcp)
        $socket.SetSocketOption([System.Net.Sockets.SocketOptionLevel]::Socket, [System.Net.Sockets.SocketOptionName]::DontLinger, $false)
        [System.IAsyncResult]$result = $socket.BeginConnect($IPAddr, $tcpPort, $null, $null)
        [bool]$connected = $result.AsyncWaitHandle.WaitOne($timeout, $true)

        if($connected -eq $true)
        {
            Write-Host -ForegroundColor Green "$($IPAddr)`t:$($optionalPort)TCP/$($tcpPort) connected." -NoNewline
        }
        else {
            Write-Host -ForegroundColor Red "$($IPAddr)`t:$($optionalPort)TCP/$($tcpPort) failed." -NoNewline
        }
        $optionalPort = ''
    }
    Write-Host ''
}
Function Invoke-LdapSearch {
    param (
        [string]
        [Parameter(Mandatory=$true)]
        $LdapPath,
        [string]
        [Parameter(Mandatory=$false)]
        $LdapFilter,
        [pscredential]
        $Credential,
        [bool]
        [Parameter(Mandatory=$false)]
        $FindOne = $true
    )

    $de = New-Object System.DirectoryServices.DirectoryEntry($LdapPath.ToUpper())

    if($Credential) {
        [System.IntPtr] $bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($Credential.Password)
        $de.Username = $Credential.UserName
        $de.Password = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr)
        [System.Runtime.InteropServices.Marshal]::FreeBSTR($bstr)
        $de.AuthenticationType = [System.DirectoryServices.AuthenticationTypes]::Secure
    }

    $ds = New-Object System.DirectoryServices.DirectorySearcher($de)
    $ds.Filter = $LdapFilter
    $ds.SearchScope = [System.DirectoryServices.SearchScope]::Subtree
    $ds.PageSize = 100
    $ds.ReferralChasing = [System.DirectoryServices.ReferralChasingOption]::All

    if($FindOne) {
        [System.DirectoryServices.SearchResult]$sr = $ds.FindOne()
        $dEntry = $sr.GetDirectoryEntry()
        Write-Host $dEntry.Path
    }
    else {
        [System.DirectoryServices.SearchResultCollection]$sc = $ds.FindAll()
        foreach($sr in $sc) {
            Write-Host $sr.Path
        }
    }
}

Export-ModuleMember -Function *