Test-NetConnection is the built-in PowerShell cmdlet that answers the three questions a sysadmin asks when something on the network stops working: can I reach the host at all, is the TCP port actually open, and which path does the traffic take. It ships with Windows in the NetTCPIP module, so there is nothing to install and nothing to enable.
That matters because the old toolset is either missing or misleading. The Telnet Client is an optional feature that is switched off on every modern Windows install, so the classic telnet host 443 port check fails before it starts. ping reports failure on hosts where ICMP is simply blocked by policy, and tracert gives you a hop list with no view of which local interface or source address Windows actually picked.
The official reference lists the parameters but not the decisions behind them. This article covers what each parameter set is genuinely useful for, how to read the source address and route fields that make multi-homed and VPN hosts diagnosable, and how to turn the cmdlet into scripted output instead of a wall of text. For the narrower question of which ports are listening on a machine, see how to check open ports on Windows.
Applies to: Windows 10 / 11 and Windows Server 2016 / 2019 / 2022 / 2025. The cmdlet first shipped with Windows 8.1 and Windows Server 2012 R2.
Quick answer
Two commands cover most of the daily work. The first replaces telnet and returns a full result object; the second returns a plain boolean, which is what you want inside a script or an if statement.
# Replaces "telnet sql01.corp.local 1433" - checks whether the TCP handshake completes
Test-NetConnection -ComputerName sql01.corp.local -Port 1433
# Same test, boolean only - Quiet suppresses the object and returns True or False
Test-NetConnection -ComputerName sql01.corp.local -Port 1433 -InformationLevel Quiet
The first command prints a six-field summary. TcpTestSucceeded is the line that answers the question.
ComputerName : sql01.corp.local
RemoteAddress : 10.20.0.31
RemotePort : 1433
InterfaceAlias : Ethernet0
SourceAddress : 10.20.0.55
TcpTestSucceeded : True
What it does
The cmdlet has four parameter sets, and which one you land in depends entirely on the parameters you pass. This is the single most useful thing to internalise: with no port parameter it sends ICMP, with a port parameter it opens a TCP socket instead, and the two tests are never run together.
| Parameter | Parameter set | What it does |
|---|---|---|
-ComputerName | all | DNS name or IP address of the target. Positional, so it can be passed without the parameter name. |
-Port | RemotePort | Any TCP port number. Switches the test from ICMP to a TCP connect. |
-CommonTCPPort | CommonTCPPort | Named shortcut. Accepted values: HTTP, RDP, SMB, WINRM. |
-TraceRoute | ICMP | Runs a trace to the target and fills the TraceRoute property. |
-Hops | ICMP | Caps the number of hops the trace will traverse. |
-DiagnoseRouting | NetRouteDiagnostics | Reports which route and source address the stack would select for this destination. |
-ConstrainSourceAddress | NetRouteDiagnostics | Forces route selection to be evaluated from a given local IP address. |
-ConstrainInterface | NetRouteDiagnostics | Same, but constrained to an interface index. |
-InformationLevel | all | Accepted values: Quiet and Detailed. Omit it for the standard summary. |
-Port, -CommonTCPPort, -TraceRoute and -DiagnoseRouting belong to different parameter sets and cannot be combined. Asking for a port test and a trace route in one command is a parameter binding error, not a slow command.
Practical examples
1. Prove a firewall rule without installing telnet
The problem: An application on SRV-APP-02 cannot reach the SQL instance on sql01.corp.local and the network team says the rule is in place. You need evidence from the application server itself.
The solution: A TCP connect test from the exact host that is failing, which is what telnet used to do before it was removed from the default install.
Run this on SRV-APP-02. A completed handshake means the port is open end to end, including every firewall in the path. A failure prints a warning line before the object.
# tnc is the built-in short alias, useful when typing this into a live incident call
tnc sql01.corp.local -Port 1433
WARNING: TCP connect to (10.20.0.31 : 1433) failed
ComputerName : sql01.corp.local
RemoteAddress : 10.20.0.31
RemotePort : 1433
InterfaceAlias : Ethernet0
SourceAddress : 10.20.0.55
TcpTestSucceeded : False
RemoteAddress resolved, so DNS is fine. TcpTestSucceeded : False narrows the fault to the path or the listener, not to name resolution.
2. See which interface and source address Windows picked
The problem: A server with a management NIC and a production NIC reaches the domain controller intermittently, and you suspect traffic is leaving through the wrong adapter.
The solution: -InformationLevel Detailed adds the name resolution results and the selected next hop to the standard output, so you can see the routing decision instead of guessing at it.
# Detailed adds NameResolutionResults and NetRoute (NextHop) to the summary,
# which is what tells you the adapter choice rather than just the ping result
Test-NetConnection -ComputerName dc01.corp.local -InformationLevel Detailed
ComputerName : dc01.corp.local
RemoteAddress : 10.20.0.11
NameResolutionResults : 10.20.0.11
InterfaceAlias : Ethernet0
SourceAddress : 10.20.0.55
NetRoute (NextHop) : 10.20.0.1
PingSucceeded : True
PingReplyDetails (RTT) : 1 ms
If InterfaceAlias names the management NIC when it should name the production NIC, the routing table is the problem, not the firewall.
3. Replace tracert and keep the hop list as data
The problem: Traffic to a remote site is slow and you want the hop list, but a broken path makes tracert sit through thirty hops of timeouts.
The solution: -TraceRoute with -Hops caps the trace, and the result is a string array you can index instead of screen text you have to read.
# -Hops 12 stops the trace early - a broken path otherwise costs 30 timeouts
$trace = Test-NetConnection -ComputerName 8.8.8.8 -TraceRoute -Hops 12
# TraceRoute is a plain array, so the first element is always the local gateway
$trace.TraceRoute[0]
$trace.TraceRoute.Count
10.20.0.1
9
* in the array, exactly as they do in tracert output, and that is normal rather than a fault.
4. Check a list of ports in one pass
The problem: A new file and application server has to be validated against a firewall change request that lists five ports, and running five separate commands produces five screens of output nobody wants to compare.
The solution: Pipe the port list through ForEach-Object with -InformationLevel Quiet, and format one line per port.
$target = 'sql01.corp.local'
443,445,1433,3389,5985 | ForEach-Object {
# Quiet returns a bare boolean; SilentlyContinue hides the red WARNING per failed port
$ok = Test-NetConnection -ComputerName $target -Port $_ `
-InformationLevel Quiet -WarningAction SilentlyContinue
# {0,-6} left-aligns the port in a 6 character column so the states line up
'{0,-6}{1}' -f $_, $(if ($ok) { 'open' } else { 'blocked' })
}
443 open
445 open
1433 blocked
3389 open
5985 open
The backtick at the end of the third line is the PowerShell line continuation character. It lets one command span two lines and must be the last character on the line, with no trailing space after it.
5. Report only the hosts that fail
The problem: After a firewall change you need to know which of twenty servers lost SMB, and reading twenty True values to find the one False is how mistakes happen.
The solution: Because Quiet returns a boolean, the whole test fits inside Where-Object and only the failures come back.
# -not inverts the boolean, so only hosts that failed the SMB test are emitted
'dc01','sql01','fs01' | Where-Object {
-not (Test-NetConnection $_ -CommonTCPPort SMB `
-InformationLevel Quiet -WarningAction SilentlyContinue)
}
fs01
6. Diagnose route selection on a VPN or multi-homed host
The problem: A laptop on a split tunnel VPN can reach some internal subnets and not others, and the routing table alone does not make the winner obvious.
The solution: -DiagnoseRouting replaces the connectivity fields with route selection fields, so you see the decision rather than the outcome.
# Detailed is what exposes RouteSelectionEvents - without it the reasoning is hidden
Test-NetConnection -ComputerName 10.20.0.11 -DiagnoseRouting -InformationLevel Detailed
The fields worth reading are SelectedSourceAddress, OutgoingInterfaceIndex, SelectedNetRoute and RouteDiagnosticsSucceeded. If SelectedSourceAddress holds the physical adapter address while you expect the VPN address, the tunnel route is losing to a more specific local route.
-ConstrainSourceAddress then answers the follow-up question, which is whether the destination would be reachable if the stack had chosen the other address.
# Forces route selection to be evaluated as if traffic left the VPN address
Test-NetConnection -ComputerName 10.20.0.11 -DiagnoseRouting `
-ConstrainSourceAddress 10.99.4.7 -InformationLevel Detailed
Hidden gems
PingSucceeded False does not mean the host is down. This is the single most misread result. A bare Test-NetConnection dc01.corp.local sends ICMP only, and most hardened Windows builds and nearly every cloud firewall drop ICMP by default. The host can be serving traffic perfectly while this field reads False. Always confirm with a port test before declaring a host unreachable.
# These two lines can legitimately disagree - the first is ICMP, the second is TCP
Test-NetConnection dc01.corp.local -InformationLevel Quiet
Test-NetConnection dc01.corp.local -Port 445 -InformationLevel Quiet
False
True
Quiet is the fast form, Detailed is the slow one. Detailed performs full name resolution and a route lookup on top of the connectivity test, which is noticeable when you loop over a host list. Use Quiet for anything that runs more than a handful of times and keep Detailed for the single host you are actually investigating.
The warning stream is separate from the result. A failed test writes a yellow WARNING line that is not part of the returned object, so it survives redirection and clutters logs. -WarningAction SilentlyContinue removes it without changing the result, which is why every scripted example above carries it.
There is no timeout parameter. A TCP test against a silently dropped port waits for the operating system connect timeout, which is roughly twenty seconds and cannot be shortened from the cmdlet. On PowerShell 7 the alternative is Test-Connection -TargetName host -TcpPort 1433 -TimeoutSeconds 2, which does accept one.
Cross-shell equivalent
The mapping below is the one to keep in mind when you inherit old scripts or move a check between platforms.
| Goal | Legacy CMD | Windows PowerShell 5.1 | Linux shell |
|---|---|---|---|
| Reachability | ping host | Test-NetConnection host | ping -c 4 host |
| TCP port open | telnet host 443 | Test-NetConnection host -Port 443 | nc -zv host 443 |
| Path to target | tracert host | Test-NetConnection host -TraceRoute | traceroute host |
| Boolean for a script | parse %ERRORLEVEL% | -InformationLevel Quiet | nc -z host 443 && echo up |
On PowerShell 7 there is a second option that also runs on Linux and macOS. Test-Connection gained a TCP port mode and a real timeout, so cross-platform scripts should prefer it.
# PowerShell 7 only - returns True or False and gives up after 2 seconds
Test-Connection -TargetName sql01.corp.local -TcpPort 1433 -TimeoutSeconds 2
# PowerShell 7 traceroute equivalent, also cross-platform
Test-Connection -TargetName 8.8.8.8 -Traceroute
Test-NetConnection lives in the Windows NetTCPIP module, so it exists on Windows only. Scripts that have to run on Linux or macOS need the Test-Connection form above.
Where this matters
- Firewall change validation: run the port list before and after the change window and attach both outputs to the ticket.
- Domain join and replication faults: confirm 445, 389 and 3268 to the domain controller before opening an Active Directory investigation.
- WinRM and remoting setup:
-CommonTCPPort WINRMseparates a blocked port 5985 from an authentication failure in seconds. - Multi-homed servers:
-DiagnoseRoutingshows which adapter wins for a given destination, which the routing table alone rarely makes obvious. - Split tunnel VPN complaints: compare the selected source address against the VPN address to prove whether the tunnel route is being used.
- Migration cutovers: a scheduled Where-Object check that emits nothing while everything is healthy makes a quiet mailbox the success signal.
Tips and limitations
- No elevation is required for a normal test. The detailed TCP output reports
IsAdminandMatchingIPsecRules, and the IPsec rule list is only populated when the session is elevated. - TCP only. UDP services such as DNS on port 53 or syslog on 514 cannot be tested with this cmdlet.
-CommonTCPPortaccepts exactly four values: HTTP, RDP, SMB and WINRM. Anything else needs-Port.- A successful TCP test proves the port is reachable and something is listening. It does not prove the service behind it is healthy or that the certificate is valid.
- The cmdlet is Windows only. It is present in PowerShell 7 on Windows but has no Linux or macOS equivalent in the same module.
- Parameter sets do not mix. One test per command, so a port check and a trace route are two separate invocations.
Official documentation
- Test-NetConnection (NetTCPIP) | Microsoft Learn
- Test-Connection | Microsoft Learn
- tracert | Microsoft Learn
Related tools
- Port Checker: tests a port from outside your network, which is the other half of the answer when the local test succeeds.
- Network Diagnostics Tool: browser side checks to run alongside the local cmdlet during an incident.
- DNS Lookup Tool: confirm the record independently when RemoteAddress resolves to something unexpected.
Related guides
- How to check open ports on Windows: the listener side of the same problem, from the server rather than the client.
- NETSTAT command: once a port test fails, netstat confirms whether anything is listening on the target at all.
- NSLOOKUP command: use it when RemoteAddress resolves to the wrong host and the fault is DNS rather than the network.
- IPCONFIG command: the local adapter and gateway view behind the InterfaceAlias and SourceAddress fields.