Public/Test-CWAAPort.ps1

function Test-CWAAPort {
    <#
    .SYNOPSIS
        Tests connectivity to TCP ports required by the ConnectWise Automate agent.
    .DESCRIPTION
        Verifies that the local LTTray port is available and tests connectivity to
        the required TCP ports (70, 80, 443) on the Automate server, plus port 8002
        on the Automate mediator server.
        If no server is provided, the function attempts to detect it from the installed
        agent configuration or backup info.
    .PARAMETER Server
        The URL of the Automate server (e.g., https://automate.domain.com).
        If not provided, the function uses Get-CWAAInfo or Get-CWAAInfoBackup to discover it.
    .PARAMETER TrayPort
        The local port LTSvc.exe listens on for LTTray communication.
        Defaults to 42000 if not provided or not found in agent configuration.
    .PARAMETER Quiet
        Returns a boolean connectivity result instead of verbose output.
    .EXAMPLE
        Test-CWAAPort -Server 'https://automate.domain.com'
        Tests all required ports against the specified server.
    .EXAMPLE
        Test-CWAAPort -Quiet
        Returns $True if the TrayPort is available, $False otherwise.
    .NOTES
        Author: Chris Taylor
        Alias: Test-LTPorts
    .LINK
        https://github.com/christaylorcodes/ConnectWiseAutomateAgent
    #>

    [CmdletBinding()]
    [Alias('Test-LTPorts')]
    Param(
        [Parameter(ValueFromPipelineByPropertyName = $true, ValueFromPipeline = $True)]
        [string[]]$Server,
        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [int]$TrayPort,
        [Parameter(ValueFromPipelineByPropertyName = $true)]
        [switch]$Quiet
    )

    Begin {
        Write-Debug "Starting $($MyInvocation.InvocationName)"
        $MediatorServer = 'mediator.labtechsoftware.com'

        function Private:TestPort {
            Param(
                [parameter(Position = 0)]
                [string]$ComputerName,

                [parameter(Mandatory = $False)]
                [System.Net.IPAddress]$IPAddress,

                [parameter(Mandatory = $True, Position = 1)]
                [int]$Port
            )

            $RemoteServer = if ([string]::IsNullOrEmpty($ComputerName)) { $IPAddress } else { $ComputerName }
            if ([string]::IsNullOrEmpty($RemoteServer)) {
                Write-Error "No ComputerName or IPAddress was provided to test."
                return
            }

            $tcpClient = New-Object System.Net.Sockets.TcpClient
            Try {
                Write-Output "Connecting to $($RemoteServer):$Port (TCP).."
                $tcpClient.Connect($RemoteServer, $Port)
                Write-Output 'Connection successful'
            }
            Catch {
                Write-Output 'Connection failed'
            }
            Finally {
                $tcpClient.Close()
            }
        }
    }

    Process {
        if (-not ($Server) -and (-not ($TrayPort) -or -not ($Quiet))) {
            Write-Verbose 'No Server Input - Checking for names.'
            $Server = Get-CWAAInfo -EA 0 -Verbose:$False -WhatIf:$False -Confirm:$False -Debug:$False | Select-Object -Expand 'Server' -EA 0
            if (-not ($Server)) {
                Write-Verbose 'No Server found in installed Service Info. Checking for Service Backup.'
                $Server = Get-CWAAInfoBackup -EA 0 -Verbose:$False | Select-Object -Expand 'Server' -EA 0
            }
        }

        if (-not ($Quiet) -or (($TrayPort) -ge 1 -and ($TrayPort) -le 65530)) {
            if (-not ($TrayPort) -or -not (($TrayPort) -ge 1 -and ($TrayPort) -le 65530)) {
                # Discover TrayPort from agent configuration if not provided
                $TrayPort = (Get-CWAAInfo -EA 0 -Verbose:$False -WhatIf:$False -Confirm:$False -Debug:$False | Select-Object -Expand TrayPort -EA 0)
            }
            if (-not ($TrayPort) -or $TrayPort -notmatch '^\d+$') { $TrayPort = 42000 }

            [array]$processes = @()
            # Get all processes using the TrayPort (default 42000)
            Try {
                $netstatOutput = & "$env:windir\system32\netstat.exe" -a -o -n | Select-String -Pattern " .*[0-9\.]+:$($TrayPort).*[0-9\.]+:[0-9]+ .*?([0-9]+)" -EA 0
            }
            Catch {
                Write-Output 'Error calling netstat.exe.'
                $netstatOutput = $null
            }
            foreach ($netstatLine in $netstatOutput) {
                $processes += ($netstatLine -split ' {4,}')[-1]
            }
            $processes = $processes | Where-Object { $_ -gt 0 -and $_ -match '^\d+$' } | Sort-Object | Get-Unique

            if (($processes)) {
                if (-not ($Quiet)) {
                    foreach ($processId in $processes) {
                        if ((Get-Process -Id $processId -EA 0 | Select-Object -Expand ProcessName -EA 0) -eq 'LTSvc') {
                            Write-Output "TrayPort Port $TrayPort is being used by LTSvc."
                        }
                        else {
                            Write-Output "Error: TrayPort Port $TrayPort is being used by $(Get-Process -Id $processId | Select-Object -Expand ProcessName -EA 0)."
                        }
                    }
                }
                else { return $False }
            }
            elseif (($Quiet) -eq $True) {
                return $True
            }
            else {
                Write-Output "TrayPort Port $TrayPort is available."
            }
        }

        foreach ($serverEntry in $Server) {
            if ($Quiet) {
                $cleanServerAddress = ($serverEntry -replace 'https?://', '' | ForEach-Object { $_.Trim() })
                Test-Connection $cleanServerAddress -Quiet
                return
            }

            if ($serverEntry -match $Script:CWAAServerValidationRegex) {
                Try {
                    $cleanServerAddress = ($serverEntry -replace 'https?://', '' | ForEach-Object { $_.Trim() })
                    Write-Output 'Testing connectivity to required TCP ports:'
                    TestPort -ComputerName $cleanServerAddress -Port 70
                    TestPort -ComputerName $cleanServerAddress -Port 80
                    TestPort -ComputerName $cleanServerAddress -Port 443
                    TestPort -ComputerName $MediatorServer -Port 8002
                }
                Catch {
                    Write-Error "There was an error testing the ports for '$serverEntry'. $($_)" -ErrorAction Stop
                }
            }
            else {
                Write-Warning "Server address '$($serverEntry)' is not valid or not formatted correctly. Example: https://automate.domain.com"
            }
        }
    }

    End {
        if (-not ($Quiet)) {
            Write-Output 'Test-CWAAPort Finished'
        }
        Write-Debug "Exiting $($MyInvocation.InvocationName)"
    }
}