Now that we have a host in mind, we can learn more about it with this one liner designed to attempt TCP connections to all specified ports:
> 1..1024 | % {echo ((New-Object Net.Sockets.TcpClient).Connect("192.168.63.147", $_)) "Open port - $_"} 2>$null
As you can see, this is just taking the basics we've learned to the next level. 1..1024 defines our port range and pipes the array into %; with each iteration, a TCP client module is brought up to attempt a connection on the port. 2>$null blackholes STDERR; in other words, a returned error means the port isn't open and the response is thrown in the trash.
We know from TCP and working with tools like Nmap that there is a variety of port scanning strategies; for example, half-open scanning, where SYNs are sent to elicit the SYN-ACK of an open port, but without completing the handshake with an ACK. So, what is happening behind the scenes with our quick and dirty port scanner script? It's a Connect module for TcpClient—it's designed to actually create TCP connections. It doesn't know that it's being used for port scanning. It's attempting to create full three-way handshakes and it will return successfully if the handshake is completed. It's important that we understand what's happening on the network.