Teste de Porta do PowerShell: Um Guia de Ferramentas Personalizado

Vamos mergulhar em como construir uma ferramenta de teste de porta PowerShell que permite testar portas abertas por número de porta e rótulo.

Para construir um script robusto que não vai falhar e morrer em metade dos seus servidores, é importante primeiro garantir que os pré-requisitos necessários para obter o resultado final sejam atendidos.

Quais são esses pré-requisitos? Os pré-requisitos são serviços como FTP, HTTP, DCOM, WMI, WSMAN, etc. A conexão que você está tentando fazer com um servidor normalmente depende de serviços como esses.

Existem várias camadas de verificações que você pode realizar em seus servidores antes de tentar fazer uma conexão, dependendo do quanto você quer ser detalhista.

Para começar, você tem toda a pilha OSI para percorrer do lado da rede. Isso não inclui os serviços no sistema host em que você está executando o script e tudo mais no sistema host remoto.

Um dos primeiros testes que você precisa realizar ao consultar um servidor remoto é verificar se as portas de rede apropriadas estão abertas e acessíveis. Dependendo dos serviços em execução no servidor remoto, as portas que você precisa consultar podem variar.

I always used to either not even attempting to test port connections or fumbling around with finding that latest Test-Port script I had scavenged somewhere. Once I found it to figure out what ports I actually needed to a test ahead of time. Today was the final straw.

I needed to query a set of domain controllers before running some CIM queries against them. I went about my normal fumbling around and decided enough was enough and sat down and built my own, fully featured port testing script.

Com a ajuda deste script do Technet, consegui criar um par de funções PowerShell muito boas que não só permitem testar portas TCP e UDP abertas, mas também testar grupos de portas por função do servidor. Não é mais necessário pesquisar no Google toda vez para descobrir quais portas cada serviço usa!

Concedido, especialmente para o Active Directory, as portas podem variar de acordo com o sistema operacional do servidor, vários serviços em um controlador de domínio, etc. Sinta-se à vontade para ajustá-los conforme necessário para o seu ambiente.

Aqui está uma captura de tela de um exemplo de uso:

Testing ports with PowerShell

Como você pode ver, você pode especificar quantos servidores quiser e ele irá gerar uma lista agradável de objetos divididos pelo grupo de porta de serviço e a porta para cada computador. Até agora, tem sido muito útil! Espero que você consiga tirar proveito dele como eu tenho!

function Test-ServerRolePortGroup {
	<#
	.SINOPSE
		Essa função testa as portas TCP/UDP abertas por função do servidor.
	.DESCRIÇÃO
		Essa função testa todas as portas TCP/UDP apropriadas por função do servidor para que você não precise memorizá-las ou procurar por todas as portas que precisam ser testadas toda vez que desejar verificar a conectividade remota em uma função de servidor específica.
	.NOTAS
		Referências de porta de link:
		http://technet.microsoft.com/en-us/library/dd772723(v=ws.10).aspx
		http://en.wikipedia.org/wiki/Server_Message_Block
		http://technet.microsoft.com/en-us/library/cc940063.aspx
	.PARAMETER NomeComputador
		Um ou mais nomes de computadores remotos, separados por vírgula
	.PARAMETER FuncaoServidor
		Os serviços no computador para os quais você gostaria de encontrar portas abertas. Isso pode ser
		serviços comuns como WinRm, Smb, Dns, Active Directory e NetBIOS
	.EXEMPLO
		PS> Test-ServerRolePortGroup -NomeComputador 'LABDC','LABDC2' -FuncaoServidor NetBIOS,WinRm,Dns
		
		Este exemplo testa as portas de rede necessárias para NetBIOS, WinRm e Dns
		funcionarem nos servidores LABDC e LABDC2.
	#>
	
	[CmdletBinding()]
	[OutputType([System.Management.Automation.PSCustomObject])]
	param (
		[Parameter(Mandatory)]
		[ValidateScript({ Test-Connection -ComputerName $_ -Count 1 -Quiet})]
		[string[]]$Computername,
		[Parameter(Mandatory)]
		[ValidateSet('WinRm','Smb','Dns','ActiveDirectoryGeneral','ActiveDirectoryGlobalCatalog','NetBios')]
		[string[]]$ServerRole
	)
	begin {
		$PortGroups = @{
			'WinRm' = @{ 'TCP' = 5985}
			'Smb' = @{ 'TCP' = 445; 'UDP' = 445 }
			'Dns' = @{ 'TCP' = 53; 'UDP' = 53 }
			'ActiveDirectoryGeneral' = @{ 'TCP' = 25, 88, 389, 464, 636, 5722, 9389; 'UDP' = 88,123,389,464 }
			'ActiveDirectoryGlobalCatalog' = @{ 'TCP' = 3268, 3269 }
			'NetBios' = @{ 'TCP' = 135, 137, 138, 139; 'UDP' = 137,138,139 }
		}
	}
	process {
		foreach ($Computer in $Computername) {
			Write-Verbose "Beginning port tests on computer '$Computer'"
			try {
				$TestPortGroups = $PortGroups.GetEnumerator() | where { $ServerRole -contains $_.Key }
				Write-Verbose "Found '$($TestPortGroups.Count)' port group(s) to test"
				foreach ($PortGroup in $TestPortGroups) {
					$PortGroupName = $PortGroup.Key
					$PortGroupValues = $PortGroup.Value
					foreach ($Value in $PortGroupValues.GetEnumerator()) {
						$Protocol = $Value.Key
						$Ports = $Value.Value
						$TestResult = Test-Port -ComputerName $Computer -Protocol $Protocol -Port $Ports
						$TestResult | Add-Member -MemberType 'NoteProperty' -Name 'PortSet' -Value $PortGroupName
						$TestResult
					}
				}
			} catch {
				Write-Verbose "$($MyInvocation.MyCommand.Name) - Computer: $Computer - Error: $($_.Exception.Message) - Line Number: $($_.InvocationInfo.ScriptLineNumber)"
				$false
			}
		}
	}
}

function Test-Port {
	<#
	.SINOPSE
		Essa função testa portas TCP/UDP abertas.
	.DESCRIÇÃO
		Essa função testa qualquer porta TCP/UDP para verificar se está aberta ou fechada.
	.NOTAS
		Problema Conhecido: Se essa função for chamada de forma consecutiva dentro de 10-20 vezes na mesma porta
			e computador, a verificação da porta UDP irá retornar $false quando poderia ser
			$true. Eu não descobri por que isso acontece.
	.PARAMETER NomeComputador
		Um ou mais nomes de computadores remotos, separados por vírgula
	.PARAMETER Porta
		Um ou mais números de porta separados por vírgula que você gostaria de testar.
	.PARAMETER Protocolo
		O protocolo (UDP ou TCP) que você estará testando
	.PARAMETER TempoEsperaTcp
		O número de milissegundos que a função esperará até declarar
		a porta TCP fechada.
	.PARAMETER TempoEsperaUdp
		O número de milissegundos que a função esperará até declarar
		a porta UDP fechada.
	.EXEMPLO
		PS> Test-Port -NomeComputador 'LABDC','LABDC2' -Protocolo TCP 80,443
		
		Este exemplo testa as portas de rede TCP 80 e 443 em ambos os servidores LABDC
		e LABDC2.
	#>
	[CmdletBinding(DefaultParameterSetName='TCP')]
	[OutputType([System.Management.Automation.PSCustomObject])]
	param (
		[Parameter(Mandatory)]
		[string[]]$ComputerName,
		[Parameter(Mandatory)]
		[int[]]$Port,
		[Parameter(Mandatory)]
		[ValidateSet('TCP', 'UDP')]
		[string]$Protocol,
		[Parameter(ParameterSetName='TCP')]
		[int]$TcpTimeout = 1000,
		[Parameter(ParameterSetName = 'UDP')]
		[int]$UdpTimeout = 1000
	)
	process {
		foreach ($Computer in $ComputerName) {
			foreach ($Portx in $Port) {
				$Output = @{ 'Computername' = $Computer; 'Port' = $Portx; 'Protocol' = $Protocol; 'Result' = '' }
				Write-Verbose "$($MyInvocation.MyCommand.Name) - Beginning port test on '$Computer' on port '$Protocol<code>:$Portx'"
				if ($Protocol -eq 'TCP') {
					$TcpClient = New-Object System.Net.Sockets.TcpClient
					$Connect = $TcpClient.BeginConnect($Computer, $Portx, $null, $null)
					$Wait = $Connect.AsyncWaitHandle.WaitOne($TcpTimeout, $false)
					if (!$Wait) {
						$TcpClient.Close()
						Write-Verbose "$($MyInvocation.MyCommand.Name) - '$Computer' failed port test on port '$Protocol</code>:$Portx'"
						$Output.Result = $false
					} else {
						$TcpClient.EndConnect($Connect)
						$TcpClient.Close()
						Write-Verbose "$($MyInvocation.MyCommand.Name) - '$Computer' passed port test on port '$Protocol<code>:$Portx'"
						$Output.Result = $true
					}
					$TcpClient.Close()
					$TcpClient.Dispose()
				} elseif ($Protocol -eq 'UDP') {
					$UdpClient = New-Object System.Net.Sockets.UdpClient
					$UdpClient.Client.ReceiveTimeout = $UdpTimeout
					$UdpClient.Connect($Computer, $Portx)
					Write-Verbose "$($MyInvocation.MyCommand.Name) - Sending UDP message to computer '$Computer' on port '$Portx'"
					$a = new-object system.text.asciiencoding
					$byte = $a.GetBytes("$(Get-Date)")
					[void]$UdpClient.Send($byte, $byte.length)
					#O objeto IPEndPoint nos permitirá ler datagramas enviados de qualquer fonte.
					Write-Verbose "$($MyInvocation.MyCommand.Name) - Creating remote endpoint"
					$remoteendpoint = New-Object system.net.ipendpoint([system.net.ipaddress]::Any, 0)
					try {
						#Bloqueia até que uma mensagem retorne neste socket de um host remoto.
						Write-Verbose "$($MyInvocation.MyCommand.Name) - Waiting for message return"
						$receivebytes = $UdpClient.Receive([ref]$remoteendpoint)
						[string]$returndata = $a.GetString($receivebytes)
						If ($returndata) {
							Write-Verbose "$($MyInvocation.MyCommand.Name) - '$Computer' passed port test on port '$Protocol</code>:$Portx'"
							$Output.Result = $true
						}
					} catch {
						Write-Verbose "$($MyInvocation.MyCommand.Name) - '$Computer' failed port test on port '$Protocol`:$Portx' with error '$($_.Exception.Message)'"
						$Output.Result = $false
					}
					$UdpClient.Close()
					$UdpClient.Dispose()
				}
				[pscustomobject]$Output
			}
		}
	}
}

Source:
https://adamtheautomator.com/powershell-test-port/