Timus Connect Troubleshooter (Windows) — Data Collection Guide

Overview

The Timus Connect Troubleshooter collects a comprehensive endpoint and network diagnostic snapshot required by Timus Support and Engineering teams to perform accurate and efficient root cause analysis.

The data gathered through this process enables investigation of:

  • Authentication failures

  • Tunnel instability

  • DNS resolution issues

  • Degraded performance

  • Local agent errors

By collecting a standardized dataset, we eliminate guesswork and significantly reduce time to resolution.


Collection Options

You may collect diagnostics in one of the following ways:


Option 1 – Automated Script (Recommended)

Run the provided PowerShell script as administrator to automatically collect all required diagnostics.

The script:

  • Collects all system and network diagnostics

  • Runs MTU and connectivity tests

  • Performs DNS and HTTP validation

  • Executes iPerf3 performance testing

  • Gathers firewall, disk, process, and event data

  • Organizes outputs into a structured folder automatically

Timus Connect Troubleshooter – How to Run (Windows)

1️⃣ Create the Script File

  1. Open Notepad.

  2. Copy the full script.

  3. Paste it into Notepad.

  4. Click File → Save As.

  5. Set:

  • File name:

    timus.ps1
  • Save as type: All Files

  • Encoding: UTF-8

  1. Save it (Desktop is recommended).


2️⃣ Open PowerShell as Administrator

  1. Right-click Start

  2. Select PowerShell (Run as Administrator)

Administrator rights are required for network and system diagnostics.


3️⃣ Set Temporary Execution Policy

Run:

Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass

This change applies only to the current PowerShell session.


4️⃣ Navigate to the Script Location

If saved on Desktop:

cd $env:USERPROFILE\Desktop

Or use the full path:

cd C:\Users\YourUsername\Desktop

5️⃣ Run the Script

.\timus.ps1

Here is the script:

# Timus Connect Troubleshooter - Windows
# Created: 1/7/2026
# Last Modified: 1/19/2026
# PowerShell Run as Administrator

Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
Add-Type -AssemblyName System.Windows.Forms

# ==========================================================
# CONFIG / CONSTANTS 
# ==========================================================
$ScriptName = "Timus Connect Troubleshooter"
$CreatedDate = "1/7/2026"

# iPerf3
$iperfUrl    = "https://files.budman.pw/?file=iperf3.20_64.zip"
$tmpDir      = "C:\tmp"
$zipFileName = "iperf3.zip"

# Ping packet sizes
$packetSizes = 1420,1410,1400,1390,1380,1370,1360,1350,1340,1330,1320,1310,1300,1290,1280

# DNS/CURL targets
$domains = @(
    "auth.timuscloud.com",
    "user.timuscloud.com",
    "device.timuscloud.com",
    "config.timuscloud.com",
    "my.timusnetworks.com",
    "localhost"
)

$urls = @(
    "https://auth.timuscloud.com",
    "https://user.timuscloud.com",
    "https://device.timuscloud.com",
    "https://config.timuscloud.com",
    "https://my.timusnetworks.com",
    "http://localhost:49202",
    "http://localhost:49204",
    "http://localhost:49202/device/info",
    "http://localhost:49202/vpn/state"
)

# iPerf3 regions (edit freely)
$regions = @(
    @{ Key="1"; Name="Ashburn (US)";    Host="ash.speedtest.clouvider.net"; TcpPort="5200"; UdpPort="5201" },
    @{ Key="2"; Name="Frankfurt (DE)";  Host="fra.speedtest.clouvider.net"; TcpPort="5200"; UdpPort="5201" },
    @{ Key="3"; Name="Amsterdam (NL)";  Host="ams.speedtest.clouvider.net"; TcpPort="5200"; UdpPort="5201" },
    @{ Key="4"; Name="London (UK)";     Host="lon.speedtest.clouvider.net"; TcpPort="5200"; UdpPort="5201" }
)

# iPerf3 retry behavior
$iperfMaxAttempts     = 5
$iperfBusyMaxRetries  = 5
$iperfBusySleepSec    = 5
$iperfAttemptSleepSec = 5

# Download retry
$downloadMaxRetries   = 3
$downloadTimeoutSec   = 60
$downloadRetrySleep   = 5

# Event log filter
$eventLogMaxEvents = 50
$eventLogPattern   = "VPN|Network"

# ==========================================================
# HELPERS (functions)
# ==========================================================
function Write-StepHeader {
    param(
        [Parameter(Mandatory=$true)][string]$StepId,
        [Parameter(Mandatory=$true)][string]$Title
    )
    Write-Host ""
    Write-Host ("[{0}] START: {1}" -f $StepId, $Title) -ForegroundColor Cyan
}

function Write-StepOk {
    param(
        [Parameter(Mandatory=$true)][string]$StepId,
        [Parameter(Mandatory=$true)][string]$Title
    )
    Write-Host ("[{0}] COMPLETED: {1}" -f $StepId, $Title) -ForegroundColor Green
}

function Write-StepFail {
    param(
        [Parameter(Mandatory=$true)][string]$StepId,
        [Parameter(Mandatory=$true)][string]$Title,
        [Parameter(Mandatory=$true)][string]$Message
    )
    Write-Host ("[{0}] FAILED: {1}  -> {2}" -f $StepId, $Title, $Message) -ForegroundColor Red
}

function Ensure-Directory {
    param([Parameter(Mandatory=$true)][string]$Path)
    if (-not (Test-Path $Path)) { New-Item -ItemType Directory -Path $Path | Out-Null }
}

function Test-IPv4 {
    param([string]$ip)
    return ($ip -match '^(\d{1,3}\.){3}\d{1,3}$')
}

# ==========================================================
# Region menu: only 1/2/3/4; retry on invalid; hard-fail after iperf3MaxInvalid
# ==========================================================
function Show-IperfRegionMenu {
    param([array]$RegionList)

    $iperf3MaxInvalid = 5
    $invalidCount     = 0

    while ($true) {

        Write-Host ""
        Write-Host "Select iPerf3 region:" -ForegroundColor Cyan

        foreach ($r in $RegionList) {
            if ($r.Key -match '^[1-4]$') {
                Write-Host ("  [{0}] {1}  ({2})" -f $r.Key, $r.Name, $r.Host)
            }
        }

        Write-Host ""
        Write-Host "Press 1/2/3/4..." -ForegroundColor Yellow

        $k = [System.Console]::ReadKey($true).KeyChar.ToString().ToUpperInvariant()

        # Invalid selection -> retry
        if ($k -notmatch '^[1-4]$') {
            $invalidCount++

            Write-Host ""
            Write-Host "Invalid region selection." -ForegroundColor Red
            Write-Host "Please select a valid region (1-4). Choose the region closest to you for best results." -ForegroundColor Yellow

            $remaining = $iperf3MaxInvalid - $invalidCount
            if ($remaining -gt 0) {
                Write-Host ("Remaining attempts: {0}" -f $remaining) -ForegroundColor Yellow
                Start-Sleep -Seconds 2
                continue
            }

            throw "Region selection failed after $iperf3MaxInvalid invalid attempts. The script could not reliably collect test data."
        }

        $match = $RegionList | Where-Object { $_.Key -eq $k } | Select-Object -First 1
        if ($null -ne $match) {
            return @($match)
        }

        # Safety net (should not happen)
        $invalidCount++
        Write-Host ""
        Write-Host "Selected region is not available. Please try again." -ForegroundColor Red

        $remaining2 = $iperf3MaxInvalid - $invalidCount
        if ($remaining2 -gt 0) {
            Write-Host ("Remaining attempts: {0}" -f $remaining2) -ForegroundColor Yellow
            Start-Sleep -Seconds 2
            continue
        }

        throw "Region selection failed after $iperf3MaxInvalid invalid attempts. The script could not reliably collect test data."
    }
}

function Invoke-IperfCommandWithBusyRetry {
    param(
        [Parameter(Mandatory=$true)][string]$IperfDir,
        [Parameter(Mandatory=$true)][string[]]$Cmd,
        [Parameter(Mandatory=$true)][string]$LogFile,
        [Parameter(Mandatory=$false)][int]$BusyMaxRetries = 5,
        [Parameter(Mandatory=$false)][int]$BusySleepSeconds = 5
    )

    if (-not $Cmd -or $Cmd.Count -lt 1 -or [string]::IsNullOrWhiteSpace($Cmd[0])) {
        "ERROR: Cmd is empty/invalid. Cmd = '$($Cmd -join ' ')'" | Out-File $LogFile -Append -Encoding UTF8
        return @{ ExitCode = 9999; BusyExhausted = $false }
    }

    $busyTry = 0
    while ($true) {
        Push-Location $IperfDir
        try {
            $exe  = $Cmd[0]
            $args = @()
            if ($Cmd.Count -gt 1) { $args = $Cmd[1..($Cmd.Count-1)] }

            $output = & $exe @args 2>&1
            $exit = $LASTEXITCODE
        } finally {
            Pop-Location
        }

        $output | Tee-Object -FilePath $LogFile -Append
        $text = ($output | Out-String)

        if ($text -match "server is busy running a test") {
            $busyTry++
            ("iPerf3 server busy. Waiting {0} seconds (busy retry {1}/{2})..." -f $BusySleepSeconds, $busyTry, $BusyMaxRetries) |
                Out-File $LogFile -Append -Encoding UTF8
            if ($busyTry -ge $BusyMaxRetries) { return @{ ExitCode = $exit; BusyExhausted = $true } }
            Start-Sleep -Seconds $BusySleepSeconds
            continue
        }

        return @{ ExitCode = $exit; BusyExhausted = $false }
    }
}

function Get-IperfTestsForRegion {
    param($region, [string]$IperfExe)

    return @(
        @{Name="TCP Download"; Cmd=@($IperfExe,"-c",$region.Host,"-p",$region.TcpPort,"-t","10","-P","8","-R")},
        @{Name="TCP Upload";   Cmd=@($IperfExe,"-c",$region.Host,"-p",$region.TcpPort,"-t","10","-P","8")},
        @{Name="UDP Download"; Cmd=@($IperfExe,"-c",$region.Host,"-p",$region.UdpPort,"-t","10","-u","-b","1000M","-R")},
        @{Name="UDP Upload";   Cmd=@($IperfExe,"-c",$region.Host,"-p",$region.UdpPort,"-t","10","-u","-b","1000M")}
    )
}

# ==========================================================
# STEP 0: Folder selection
# ==========================================================
Write-StepHeader -StepId "0" -Title "Select output folder"
try {
    $folderBrowser = New-Object System.Windows.Forms.FolderBrowserDialog
    $folderBrowser.Description = "Select folder to save output files"
    $folderBrowser.ShowNewFolderButton = $true

    $dialogResult = $folderBrowser.ShowDialog()
    if ($dialogResult -ne [System.Windows.Forms.DialogResult]::OK) { throw "User cancelled folder selection." }

    $opath = $folderBrowser.SelectedPath
    $savePath = Join-Path $opath $ScriptName

    if (Test-Path $savePath) {
        Write-Host "Existing folder detected. Clearing contents..."
        Remove-Item "$savePath\*" -Recurse -Force -ErrorAction Stop
    } else {
        New-Item -ItemType Directory -Path $savePath -ErrorAction Stop | Out-Null
    }

    Write-StepOk -StepId "0" -Title "Select output folder"
} catch {
    Write-StepFail -StepId "0" -Title "Select output folder" -Message $_.Exception.Message
    exit
}

# ==========================================================
# STEP 0.5: iPerf3 download + extract
# ==========================================================
Write-StepHeader -StepId "0.5" -Title "Download and extract iPerf3"
try {
    Ensure-Directory -Path $tmpDir
    $zipFile = Join-Path $tmpDir $zipFileName

    $success = $false
    for ($i = 1; $i -le $downloadMaxRetries; $i++) {
        try {
            Write-Host ("Downloading iPerf3 (attempt {0} of {1})..." -f $i, $downloadMaxRetries)
            Invoke-WebRequest -Uri $iperfUrl -OutFile $zipFile -TimeoutSec $downloadTimeoutSec -UseBasicParsing -ErrorAction Stop
            if (Test-Path $zipFile) { $success = $true; break }
        } catch {
            Write-Host ("Download failed: {0}. Retrying in {1} seconds..." -f $_.Exception.Message, $downloadRetrySleep)
            Start-Sleep -Seconds $downloadRetrySleep
        }
    }
    if (-not $success) { throw "Failed to download iPerf3 after $downloadMaxRetries attempts." }

    Write-Host "Extracting iPerf3..."
    Expand-Archive -Path $zipFile -DestinationPath $tmpDir -Force -ErrorAction Stop

    $iperfPath = Get-ChildItem -Path $tmpDir -Filter "iperf3.exe" -Recurse | Select-Object -First 1 | Select-Object -ExpandProperty FullName
    if (-not $iperfPath) { throw "iPerf3 executable not found after extraction." }

    $iperfDir = Split-Path -Parent $iperfPath
    $iperfExe = Join-Path $iperfDir "iperf3.exe"
    if (-not (Test-Path $iperfExe)) { throw "iPerf3 executable not found at expected path: $iperfExe" }

    Write-StepOk -StepId "0.5" -Title "Download and extract iPerf3"
} catch {
    Write-StepFail -StepId "0.5" -Title "Download and extract iPerf3" -Message $_.Exception.Message
    exit
}

# ==========================================================
# STEP 1: Interfaces/MTU + IP config
# ==========================================================
Write-StepHeader -StepId "1" -Title "Collect network interfaces + MTU + IP configuration"
try {
    $mtuFile = Join-Path $savePath "NetworkInterfaces_MTU.txt"
    "netsh interface ipv4 show subinterfaces`n" | Out-File $mtuFile -Encoding UTF8
    netsh interface ipv4 show subinterfaces | Out-File $mtuFile -Append -Encoding UTF8
    "`n=== IP Configuration ===`n" | Out-File $mtuFile -Append -Encoding UTF8
    ipconfig /all | Out-File $mtuFile -Append -Encoding UTF8

    Write-StepOk -StepId "1" -Title "Collect network interfaces + MTU + IP configuration"
} catch {
    Write-StepFail -StepId "1" -Title "Collect network interfaces + MTU + IP configuration" -Message $_.Exception.Message
}

# ==========================================================
# STEP 1a: Wi-Fi details
# ==========================================================
Write-StepHeader -StepId "1a" -Title "Collect Wi-Fi interface details"
try {
    $wifiFile = Join-Path $savePath "WiFi_Details.txt"
    "=== Wi-Fi Interface Details ===`n" | Out-File $wifiFile -Encoding UTF8
    netsh wlan show interfaces | Out-File $wifiFile -Append -Encoding UTF8
    Write-StepOk -StepId "1a" -Title "Collect Wi-Fi interface details"
} catch {
    Write-StepFail -StepId "1a" -Title "Collect Wi-Fi interface details" -Message $_.Exception.Message
}

# ==========================================================
# STEP 1b: NIC brand/model
# ==========================================================
Write-StepHeader -StepId "1b" -Title "Collect NIC brand/model"
try {
    $nicFile = Join-Path $savePath "NIC_Brand_Model.txt"
    Get-CimInstance Win32_NetworkAdapter | Where-Object { $_.NetEnabled -eq $true } |
        Select-Object Name, Manufacturer, InterfaceDescription |
        Format-Table -AutoSize | Out-String | Out-File $nicFile -Encoding UTF8
    Write-StepOk -StepId "1b" -Title "Collect NIC brand/model"
} catch {
    Write-StepFail -StepId "1b" -Title "Collect NIC brand/model" -Message $_.Exception.Message
}

# ==========================================================
# STEP 1c: System version + gateways
# ==========================================================
Write-StepHeader -StepId "1c" -Title "Collect system version + default gateway info"
try {
    $sysFile = Join-Path $savePath "System_Modems.txt"
    "=== Computer Version ===`n" | Out-File $sysFile -Encoding UTF8
    Get-CimInstance Win32_OperatingSystem | Select-Object Caption, Version, BuildNumber |
        Format-Table -AutoSize | Out-String | Out-File $sysFile -Append -Encoding UTF8

    "`n=== Default Gateway(s) / Modem Info ===`n" | Out-File $sysFile -Append -Encoding UTF8
    Get-CimInstance Win32_NetworkAdapterConfiguration | Where-Object { $_.IPEnabled -eq $true } |
        Select-Object Description, @{Name="DefaultGateway";Expression={$_.DefaultIPGateway -join ", "}} |
        Format-Table -AutoSize | Out-String | Out-File $sysFile -Append -Encoding UTF8

    Write-StepOk -StepId "1c" -Title "Collect system version + default gateway info"
} catch {
    Write-StepFail -StepId "1c" -Title "Collect system version + default gateway info" -Message $_.Exception.Message
}

# ==========================================================
# STEP 1d: Installed Windows updates
# ==========================================================
Write-StepHeader -StepId "1d" -Title "Collect installed Windows updates"
try {
    $updateFile = Join-Path $savePath "Installed_Updates.txt"
    "=== Installed Windows Updates ===`n" | Out-File $updateFile -Encoding UTF8
    Get-HotFix | Select-Object HotFixID, Description, InstalledOn |
        Format-Table -AutoSize | Out-File $updateFile -Append -Encoding UTF8
    Write-StepOk -StepId "1d" -Title "Collect installed Windows updates"
} catch {
    Write-StepFail -StepId "1d" -Title "Collect installed Windows updates" -Message $_.Exception.Message
}

# ==========================================================
# STEP 2: Ping tests
# ==========================================================
Write-StepHeader -StepId "2" -Title "Gateway ping tests (MTU-ish sweep)"
try {
    $pingFile = Join-Path $savePath "PingTest.txt"

    do {
        $gateway = Read-Host "Enter gateway IP (IPv4 only)"
        if (-not (Test-IPv4 $gateway)) {
            Write-Host "Invalid IP format. Please enter a valid IPv4 address." -ForegroundColor Red
            $gateway = $null
        }
    } until ($gateway)

    $pingResults=@()
    foreach ($size in $packetSizes) {
        $pingResults += "Pinging $gateway with packet size $size..."
        $results = Test-Connection -ComputerName $gateway -Count 4 -BufferSize $size -ErrorAction SilentlyContinue
        foreach ($r in $results) { $pingResults += "Reply from $($r.Address): Time=$($r.ResponseTime)ms" }
        $pingResults += ""
    }
    $pingResults | Out-File $pingFile -Encoding UTF8

    Write-StepOk -StepId "2" -Title "Gateway ping tests (MTU-ish sweep)"
} catch {
    Write-StepFail -StepId "2" -Title "Gateway ping tests (MTU-ish sweep)" -Message $_.Exception.Message
}

# ==========================================================
# STEP 3: Traceroute
# ==========================================================
Write-StepHeader -StepId "3" -Title "Traceroute"
try {
    $tracerFile = Join-Path $savePath "Traceroute.txt"
    tracert $gateway | Out-File $tracerFile -Encoding UTF8
    Write-StepOk -StepId "3" -Title "Traceroute"
} catch {
    Write-StepFail -StepId "3" -Title "Traceroute" -Message $_.Exception.Message
}

# ==========================================================
# STEP 4: CPU & RAM
# ==========================================================
Write-StepHeader -StepId "4" -Title "Collect CPU and RAM usage"
try {
    $cpuFile = Join-Path $savePath "CPU_Memory.txt"

    "`n=== Top 20 CPU Usage ===`n" | Out-File $cpuFile -Encoding UTF8
    (Get-Counter '\Process(*)\% Processor Time' -SampleInterval 1 -MaxSamples 1).CounterSamples |
        Where-Object { $_.InstanceName -notmatch "_Total" } |
        Sort-Object CookedValue -Descending |
        Select-Object -First 20 @{Name="Name";Expression={$_.InstanceName}},@{Name="CPU(%)";Expression={[math]::Round($_.CookedValue/($env:NUMBER_OF_PROCESSORS),0)}} |
        Format-Table -AutoSize | Out-String | Out-File $cpuFile -Append -Encoding UTF8

    "`n=== Top 20 RAM Usage ===`n" | Out-File $cpuFile -Append -Encoding UTF8
    Get-Process | Sort-Object WorkingSet -Descending |
        Select-Object -First 20 Name,@{Name="CPU(%)";Expression={"N/A"}},@{Name="Memory(MB)";Expression={[math]::Floor($_.WorkingSet/1MB)}} |
        Format-Table -AutoSize | Out-String | Out-File $cpuFile -Append -Encoding UTF8

    Write-StepOk -StepId "4" -Title "Collect CPU and RAM usage"
} catch {
    Write-StepFail -StepId "4" -Title "Collect CPU and RAM usage" -Message $_.Exception.Message
}

# ==========================================================
# STEP 5: Network/system diagnostics + DNS/CURL
# ==========================================================
Write-StepHeader -StepId "5" -Title "Collect network/system diagnostics + DNS/CURL"
try {
    $ipFile       = Join-Path $savePath "IP_Configuration.txt";           ipconfig /all | Out-File $ipFile -Encoding UTF8
    $routeFile    = Join-Path $savePath "Routing_Table.txt";              route print | Out-File $routeFile -Encoding UTF8
    $arpFile      = Join-Path $savePath "ARP_Table.txt";                  arp -a | Out-File $arpFile -Encoding UTF8
    $tcpFile      = Join-Path $savePath "Active_TCP_Connections.txt";      Get-NetTCPConnection | Out-File $tcpFile -Encoding UTF8
    $udpFile      = Join-Path $savePath "Active_UDP_Connections.txt";      Get-NetUDPEndpoint | Out-File $udpFile -Encoding UTF8
    $nicStatsFile = Join-Path $savePath "NIC_Stats.txt";                   Get-NetAdapterStatistics | Out-File $nicStatsFile -Encoding UTF8
    $eventFile    = Join-Path $savePath "EventLog.txt"

    Get-WinEvent -LogName "System" -MaxEvents $eventLogMaxEvents |
        Where-Object { $_.Message -match $eventLogPattern } |
        Out-File $eventFile -Encoding UTF8

    $dnsFile  = Join-Path $savePath "DNS_Test.txt"
    $curlfile = Join-Path $savePath "Curl_Test.txt"

    if (Test-Path $dnsFile)  { Remove-Item $dnsFile -Force }
    if (Test-Path $curlfile) { Remove-Item $curlfile -Force }

    foreach ($domain in $domains) {
        "`nResolving $domain..." | Out-File $dnsFile -Append -Encoding UTF8
        try {
            Resolve-DnsName $domain | Select-Object Name, IPAddress, QueryType |
                Format-Table -AutoSize | Out-String | Out-File $dnsFile -Append -Encoding UTF8
        } catch {
            ("Failed to resolve {0}: {1}" -f $domain, $_.Exception.Message) | Out-File $dnsFile -Append -Encoding UTF8
        }
    }

    foreach ($url in $urls) {
        "`nCURL $url..." | Out-File $curlfile -Append -Encoding UTF8
        try {
            Invoke-WebRequest -Uri $url -UseBasicParsing |
                Select-Object -ExpandProperty Content |
                Out-File $curlfile -Append -Encoding UTF8
        } catch {
            ("Failed to fetch {0}: {1}" -f $url, $_.Exception.Message) | Out-File $curlfile -Append -Encoding UTF8
        }
    }

    Write-Host "DNS and CURL diagnostics collected. Outputs saved in $savePath"
    Write-StepOk -StepId "5" -Title "Collect network/system diagnostics + DNS/CURL"
} catch {
    Write-StepFail -StepId "5" -Title "Collect network/system diagnostics + DNS/CURL" -Message $_.Exception.Message
}

# ==========================================================
# STEP 6: iPerf3 tests
# ==========================================================
Write-StepHeader -StepId "6" -Title "Run iPerf3 tests (region selection + retries)"
try {
    $iperfFile = Join-Path $savePath "iPerf3_Test.txt"
    $repeatSelection = $true

    while ($repeatSelection) {

        $selectedRegions = Show-IperfRegionMenu -RegionList $regions

        if ($null -eq $selectedRegions -or $selectedRegions.Count -eq 0) {
            Write-Host "`nNo region selected. Returning to the region selection menu...`n" -ForegroundColor Yellow
            continue
        }

        $regionsToTest = $selectedRegions

        "" | Out-File $iperfFile -Encoding UTF8

        $hadAnyFailure = $false
        $abortToMenu   = $false

        :RegionLoop foreach ($region in $regionsToTest) {

            "`n===============================" | Out-File $iperfFile -Append -Encoding UTF8
            "Region: $($region.Name)  Host: $($region.Host)" | Out-File $iperfFile -Append -Encoding UTF8
            "===============================`n" | Out-File $iperfFile -Append -Encoding UTF8

            $tests = Get-IperfTestsForRegion -region $region -IperfExe $iperfExe

            foreach ($test in $tests) {

                "`n-- $($test.Name) Test ($($region.Name)) --`n" | Out-File $iperfFile -Append -Encoding UTF8

                $attempt = 1
                $success = $false

                while (-not $success -and $attempt -le $iperfMaxAttempts) {

                    Write-Host ("[{0} | {1}] Attempt {2} / {3}, please wait..." -f $region.Name, $test.Name, $attempt, $iperfMaxAttempts) -ForegroundColor Yellow

                    $result = Invoke-IperfCommandWithBusyRetry `
                        -IperfDir $iperfDir `
                        -Cmd $test.Cmd `
                        -LogFile $iperfFile `
                        -BusyMaxRetries $iperfBusyMaxRetries `
                        -BusySleepSeconds $iperfBusySleepSec

                    if ($result.ExitCode -eq 0) {
                        $success = $true
                        break
                    }

                    if ($result.BusyExhausted) {
                        ("[{0} | {1}] Server busy persisted after {2} retries. Marking as failed attempt.`n" -f $region.Name, $test.Name, $iperfBusyMaxRetries) |
                            Out-File $iperfFile -Append -Encoding UTF8
                    } else {
                        ("[{0} | {1}] FAIL on attempt {2} (exit {3})`n" -f $region.Name, $test.Name, $attempt, $result.ExitCode) |
                            Out-File $iperfFile -Append -Encoding UTF8
                    }

                    if ($attempt -lt $iperfMaxAttempts) {
                        Start-Sleep -Seconds $iperfAttemptSleepSec
                    }

                    $attempt++
                }

                if (-not $success) {
                    $hadAnyFailure = $true
                    $abortToMenu   = $true
                    break
                }
            }

            if ($abortToMenu) { break RegionLoop }
        }

        if ($hadAnyFailure) {
            Write-Host "`nAt least one test failed in the selected region(s). Please try again (the test server may be busy)." -ForegroundColor Red
            Write-Host "Returning to the region selection menu...`n" -ForegroundColor Yellow
            $repeatSelection = $true
        } else {
            $repeatSelection = $false
        }
    }

    Write-StepOk -StepId "6" -Title "Run iPerf3 tests (region selection + retries)"
}
catch {
    Write-StepFail -StepId "6" -Title "Run iPerf3 tests (region selection + retries)" -Message $_.Exception.Message

    Write-Host ""
    Write-Host "ERROR: The script could not reliably collect test data and will exit now." -ForegroundColor Red
    Write-Host "Please review the error above, fix the issue, and re-run the script." -ForegroundColor Yellow
    Write-Host ""
    Write-Host "Press any key to exit..." -ForegroundColor Cyan
    [System.Console]::ReadKey($true) | Out-Null

    exit 1
}

# ==========================================================
# STEP 7: Firewall
# ==========================================================
Write-StepHeader -StepId "7" -Title "Collect firewall status"
try {
    $firewallFile = Join-Path $savePath "Firewall_Status.txt"
    Get-NetFirewallProfile | Select-Object Name, Enabled, DefaultInboundAction, DefaultOutboundAction |
        Out-File $firewallFile -Encoding UTF8
    Write-StepOk -StepId "7" -Title "Collect firewall status"
} catch {
    Write-StepFail -StepId "7" -Title "Collect firewall status" -Message $_.Exception.Message
}

# ==========================================================
# STEP 8: NIC brand/model re-check
# ==========================================================
Write-StepHeader -StepId "8" -Title "Re-check NIC brand/model"
try {
    $nicFile = Join-Path $savePath "NIC_Brand_Model.txt"
    Get-CimInstance Win32_NetworkAdapter | Where-Object { $_.NetEnabled -eq $true } |
        Select-Object Name, Manufacturer, InterfaceDescription |
        Format-Table -AutoSize | Out-String | Out-File $nicFile -Encoding UTF8
    Write-StepOk -StepId "8" -Title "Re-check NIC brand/model"
} catch {
    Write-StepFail -StepId "8" -Title "Re-check NIC brand/model" -Message $_.Exception.Message
}

# ==========================================================
# STEP 9: System version & gateways re-check
# ==========================================================
Write-StepHeader -StepId "9" -Title "Re-check system version + gateway info"
try {
    $sysFile = Join-Path $savePath "System_Modems.txt"
    "=== Computer Version ===`n" | Out-File $sysFile -Encoding UTF8
    Get-CimInstance Win32_OperatingSystem | Select-Object Caption, Version, BuildNumber |
        Format-Table -AutoSize | Out-String | Out-File $sysFile -Append -Encoding UTF8

    "`n=== Default Gateway(s) / Modem Info ===`n" | Out-File $sysFile -Append -Encoding UTF8
    Get-CimInstance Win32_NetworkAdapterConfiguration | Where-Object { $_.IPEnabled -eq $true } |
        Select-Object Description, @{Name="DefaultGateway";Expression={$_.DefaultIPGateway -join ", "}} |
        Format-Table -AutoSize | Out-String | Out-File $sysFile -Append -Encoding UTF8

    Write-StepOk -StepId "9" -Title "Re-check system version + gateway info"
} catch {
    Write-StepFail -StepId "9" -Title "Re-check system version + gateway info" -Message $_.Exception.Message
}

# ==========================================================
# STEP 10: Disk usage
# ==========================================================
Write-StepHeader -StepId "10" -Title "Check disk usage"
try {
    $diskFile = Join-Path $savePath "Disk_Usage.txt"

    $disks = Get-CimInstance Win32_LogicalDisk -Filter "DriveType=3" | Select-Object DeviceID, `
        @{Name="Total(GB)";Expression={[math]::Round($_.Size/1GB,2)}}, `
        @{Name="Free(GB)";Expression={[math]::Round($_.FreeSpace/1GB,2)}}, `
        @{Name="Used(GB)";Expression={[math]::Round(($_.Size - $_.FreeSpace)/1GB,2)}}, `
        @{Name="Usage(%)";Expression={[math]::Round((($_.Size - $_.FreeSpace)/$_.Size)*100,2)}}

    "=== Disk Usage ===`n" | Out-File $diskFile -Encoding UTF8
    $disks | Format-Table -AutoSize | Out-String | Out-File $diskFile -Append -Encoding UTF8

    Write-StepOk -StepId "10" -Title "Check disk usage"
} catch {
    Write-StepFail -StepId "10" -Title "Check disk usage" -Message $_.Exception.Message
}

# ==========================================================
# STEP 11: Tasklist
# ==========================================================
Write-StepHeader -StepId "11" -Title "Collect detailed task list"
try {
    $tasklistFile = Join-Path $savePath "tasklist.txt"
    tasklist /v | Out-File $tasklistFile -Encoding UTF8
    Write-StepOk -StepId "11" -Title "Collect detailed task list"
} catch {
    Write-StepFail -StepId "11" -Title "Collect detailed task list" -Message $_.Exception.Message
}

# ==========================================================
# STEP 12: Public IP (ipify)
# ==========================================================
Write-StepHeader -StepId "12" -Title "Get public IP (ipify)"
try {
    $publicIpFile = Join-Path $savePath "PublicIP.txt"

    # Simple HTTP GET, expects plain text IP
    $ip = (Invoke-WebRequest -Uri "https://api64.ipify.org/" -UseBasicParsing -TimeoutSec 20).Content
    $ip = $ip.Trim()

    if ([string]::IsNullOrWhiteSpace($ip)) { throw "Empty response from ipify." }

    ("Public IP: {0}" -f $ip) | Out-File $publicIpFile -Encoding UTF8
    Write-Host ("Public IP: {0}" -f $ip) -ForegroundColor Green

    Write-StepOk -StepId "12" -Title "Get public IP (ipify)"
} catch {
    Write-StepFail -StepId "12" -Title "Get public IP (ipify)" -Message $_.Exception.Message
}

# ==========================================================
# Cleanup
# ==========================================================
Write-StepHeader -StepId "CLEANUP" -Title "Remove temporary iPerf3 files"
try {
    if (Test-Path $tmpDir) {
        Remove-Item "$tmpDir\*" -Recurse -Force -ErrorAction Stop
    }
    Write-StepOk -StepId "CLEANUP" -Title "Remove temporary iPerf3 files"
} catch {
    Write-StepFail -StepId "CLEANUP" -Title "Remove temporary iPerf3 files" -Message $_.Exception.Message
}

Write-Host ""
Write-Host "All steps completed. All outputs saved in $savePath" -ForegroundColor Green

Option 2 – Manual Collection

Run the commands below step-by-step and save outputs into a folder named:

Timus Connect Troubleshooter

Zip the folder and send it to Timus Support.

Manual Data Collection (Script-Aligned)

Create a folder named:

Timus Connect Troubleshooter

Save each command output as a .txt file inside it.


Step 1 – Network Interfaces, MTU & IP Configuration

Purpose:
Collects active network adapters, MTU values, IP addressing, DNS servers, and gateway configuration.
This is foundational for detecting routing errors, DNS misconfiguration, or packet fragmentation issues affecting tunnel stability.

Command:

netsh interface ipv4 show subinterfaces & ipconfig /all

Step 2 – Wi-Fi Details

Purpose:
Captures SSID, signal strength, radio type, and connection state.
Required when connectivity issues occur only over Wi-Fi or appear intermittent.

Command:

netsh wlan show interfaces

Step 3 – NIC Brand & Model

Purpose:
Identifies network adapter manufacturer and interface description.
Used to detect driver conflicts or hardware-related issues.

Command:

Get-CimInstance Win32_NetworkAdapter | Where-Object {$_.NetEnabled -eq $true} | Select Name,Manufacturer,InterfaceDescription

Step 4 – System Version & Default Gateways

Purpose:
Captures Windows build information and configured default gateways.
Ensures OS compatibility and validates correct upstream routing.

Command:

Get-CimInstance Win32_OperatingSystem | Select Caption,Version,BuildNumber; Get-CimInstance Win32_NetworkAdapterConfiguration | Where-Object {$_.IPEnabled -eq $true} | Select Description,@{Name="DefaultGateway";Expression={$_.DefaultIPGateway -join ", "}}

Step 5 – Installed Windows Updates

Purpose:
Lists installed HotFix updates and installation dates.
Recent updates may impact VPN drivers or networking components.

Command:

Get-HotFix | Select HotFixID,Description,InstalledOn

Step 6 – Timus Gateway MTU Ping Sweep

Purpose:
Tests latency and packet behavior using multiple packet sizes.
Used to detect MTU fragmentation or packet loss toward the Timus gateway.

Use the Timus Gateway Public IP (not your local router).

Command:

ping <TIMUS_GATEWAY_PUBLIC_IP> -n 4 -l 1420

Repeat for packet sizes 1420 down to 1280 if necessary.


Step 7 – Traceroute to Timus Gateway

Purpose:
Maps the full network path from the endpoint to the Timus gateway.
Helps identify ISP routing issues or failing intermediate hops.

Command:

tracert <TIMUS_GATEWAY_PUBLIC_IP>

Step 8 – CPU & Memory Usage

Purpose:
Lists the top resource-intensive processes.
High system utilization may cause tunnel drops or degraded performance.

Command:

Get-Process | Sort-Object WorkingSet -Descending | Select -First 20 Name,@{Name="MemoryMB";Expression={[math]::Floor($_.WorkingSet/1MB)}}

Step 9 – Routing & ARP Tables

Purpose:
Captures local routing decisions and ARP neighbor mappings.
Provides a snapshot of the device’s network topology.

Command:

route print & arp -a

Step 10 – Active Connections & NIC Statistics

Purpose:
Lists live TCP/UDP sessions and interface-level packet statistics.
Helps detect blocked ports, abnormal traffic, or interface instability.

Command:

Get-NetTCPConnection; Get-NetUDPEndpoint; Get-NetAdapterStatistics

Step 11 – Windows Event Logs (VPN / Network)

Purpose:
Collects recent system-level networking and VPN-related events.
Used to identify driver errors, tunnel resets, or service failures.

Command:

Get-WinEvent -LogName System -MaxEvents 50 | Where-Object {$_.Message -match "VPN|Network"}

Step 12 – DNS Resolution Tests

Purpose:
Verifies that required Timus service domains resolve correctly.
Ensures DNS functionality and consistency.

Command:

Resolve-DnsName auth.timuscloud.com; Resolve-DnsName user.timuscloud.com; Resolve-DnsName device.timuscloud.com; Resolve-DnsName config.timuscloud.com; Resolve-DnsName my.timusnetworks.com; Resolve-DnsName localhost

Step 13 – HTTP Reachability Tests

Purpose:
Validates connectivity to Timus backend services and local agent endpoints.
Confirms endpoint communication and service availability.

Command:

Invoke-WebRequest https://auth.timuscloud.com -UseBasicParsing; Invoke-WebRequest https://user.timuscloud.com -UseBasicParsing; Invoke-WebRequest https://device.timuscloud.com -UseBasicParsing; Invoke-WebRequest https://config.timuscloud.com -UseBasicParsing; Invoke-WebRequest https://my.timusnetworks.com -UseBasicParsing

Local agent endpoints:

Invoke-WebRequest http://localhost:49202 -UseBasicParsing; Invoke-WebRequest http://localhost:49204 -UseBasicParsing; Invoke-WebRequest http://localhost:49202/device/info -UseBasicParsing; Invoke-WebRequest http://localhost:49202/vpn/state -UseBasicParsing

Step 14 – Firewall Status

Purpose:
Displays inbound and outbound firewall rule state.
Ensures required traffic is not being blocked locally.

Command:

Get-NetFirewallProfile | Select Name,Enabled,DefaultInboundAction,DefaultOutboundAction

Step 15 – Disk Usage

Purpose:
Checks available disk capacity.
Low storage may impact logging and service stability.

Command:

Get-CimInstance Win32_LogicalDisk -Filter "DriveType=3" | Select DeviceID,@{Name="TotalGB";Expression={[math]::Round($_.Size/1GB,2)}},@{Name="FreeGB";Expression={[math]::Round($_.FreeSpace/1GB,2)}},@{Name="UsagePercent";Expression={[math]::Round((($_.Size-$_.FreeSpace)/$_.Size)*100,2)}}

Step 16 – Running Processes (Verbose)

Purpose:
Provides a detailed snapshot of running processes and services.
Used to detect conflicts from VPN, proxy, or security software.

Command:

tasklist /v

Step 17 – Public IP Address

Purpose:
Retrieves the externally visible IP address.
Confirms correct outbound NAT and egress routing.

Command:

(Invoke-WebRequest https://api64.ipify.org -UseBasicParsing).Content

Step 18 – iPerf3 Performance Test (Manual)

Purpose:
Measures raw TCP and UDP throughput, packet loss, and jitter to identify bandwidth limitations, congestion, or ISP-level shaping impacting tunnel performance.


18.1 Download iPerf3

Download Windows 64-bit binary:

https://iperf.fr/iperf-download.php#windows

Extract the ZIP. Place iperf3.exe in a simple path (example: C:\tmp\iperf3\).


18.2 Select Test Region

Use the region closest to the user:

Region Host TCP Port UDP Port
Ashburn (US) ash.speedtest.clouvider.net 5200 5201
Frankfurt (DE) fra.speedtest.clouvider.net 5200 5201
Amsterdam (NL) ams.speedtest.clouvider.net 5200 5201
London (UK) lon.speedtest.clouvider.net 5200 5201

18.3 Run Tests (PowerShell or CMD)

Navigate to the iPerf3 directory:

cd C:\iperf3

TCP Download

iperf3.exe -c ash.speedtest.clouvider.net -p 5200 -t 10 -P 8 -R

TCP Upload

iperf3.exe -c ash.speedtest.clouvider.net -p 5200 -t 10 -P 8

UDP Download

iperf3.exe -c ash.speedtest.clouvider.net -p 5201 -t 10 -u -b 1000M -R

UDP Upload

iperf3.exe -c ash.speedtest.clouvider.net -p 5201 -t 10 -u -b 1000M

18.4 What To Collect

Save full console output of:

  • TCP Download

  • TCP Upload

  • UDP Download

  • UDP Upload

Into:

iPerf3_Test.txt

Place inside:

Timus Connect Troubleshooter

Once all steps are completed, please compress the “Timus Connect Troubleshooter” folder and send it to Timus Support along with a brief description of the issue and the exact time it occurred (including timezone).

Providing the complete diagnostic package ensures our Support and Engineering teams can perform an accurate root cause analysis and resolve the issue as quickly as possible.

Related to

Updated

Was this article helpful?

0 out of 0 found this helpful

Have more questions? Submit a request

Comments

0 comments

Please sign in to leave a comment.