Teste do PowerShell-Porta: Um Guia de Ferramentas Personalizadas

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 estejam atendidos.

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

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

Para começar, você tem toda a pilha OSI para percorrer no lado da rede. Isso não inclui os serviços no sistema host de onde 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, depende das portas que você precisa consultar.

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 Technet, consegui criar um bom par de funções PowerShell 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 procurar no Google toda vez para saber quais portas um 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á-las 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, é possível especificar quantos servidores desejar, e ele produzirá uma lista organizada 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ê tire proveito dele assim como eu!

function Test-ServerRolePortGroup {
	<#
	.SINOPSE
		Esta função testa 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
		memorizar ou procurar todas as portas que precisam ser testadas toda vez
		que desejar verificar a conectividade remota em uma função específica do servidor.
	.NOTAS
		Links de referência de porta:
		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
	.PARÂMETRO NomeDoComputador
		Um ou mais nomes de computadores remotos, separados por vírgula
	.PARÂMETRO FunçãoDoServidor
		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 -NomeDoComputador 'LABDC','LABDC2' -FunçãoDoServidor 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
		Esta função testa portas TCP/UDP abertas.
	.DESCRIÇÃO
		Esta função testa qualquer porta TCP/UDP para ver se está aberta ou fechada.
	.NOTAS
		Problema conhecido: Se esta função for chamada dentro de 10-20 consecutivamente na mesma porta
			e computador, a verificação de porta UDP irá retornar $false quando deveria
			retornar $true. Ainda não descobri por que isso acontece.
	.PARÂMETRO NomeDoComputador
		Um ou mais nomes de computadores remotos, separados por vírgula
	.PARÂMETRO Porta
		Um ou mais números de porta separados por vírgula que você gostaria de testar.
	.PARÂMETRO Protocolo
		O protocolo (UDP ou TCP) que você estará testando
	.PARÂMETRO TcpTimeout
		O número de milissegundos que a função aguardará até declarar
		a porta TCP fechada.
	.PARÂMETRO UdpTimeout
		O número de milissegundos que a função aguardará até declarar
		a porta UDP fechada.
	.EXEMPLO
		PS> Test-Port -NomeDoComputador '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 origem.
					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 soquete 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/