PowerShell Test-Port: Guida Personalizzata per uno Strumento

Tuffiamoci in come creare uno strumento di test delle porte PowerShell che ti consente di testare le porte aperte per numero di porta e etichetta.

Per creare uno script robusto che non crollerà e morirà su metà dei tuoi server, è importante assicurarsi prima che siano soddisfatti i prerequisiti necessari per ottenere il risultato finale.

Quali sono questi prerequisiti? I prerequisiti sono servizi come FTP, HTTP, DCOM, WMI, WSMAN, ecc. La connessione che stai cercando di stabilire con un server dipende tipicamente da servizi come questi.

Esiste una gerarchia di diversi livelli di controlli che puoi eseguire sui tuoi server prima di tentare di stabilire una connessione, a seconda di quanto dettagliato desideri essere.

Per cominciare, hai l’intero stack OSI da attraversare sul lato di rete. Questo non include i servizi sul sistema host da cui stai eseguendo lo script e tutto il resto sul sistema host remoto.

Uno dei primi test che devi eseguire quando interroghi un server remoto è verificare che le porte di rete appropriate siano aperte e accessibili. A seconda dei servizi in esecuzione sul server remoto, dipende dalle porte che devi interrogare.

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.

Grazie a questo script Technet sono riuscito a creare un buon paio di funzioni PowerShell che non solo ti consentiranno di testare le porte TCP e UDP aperte, ma anche di testare gruppi di porte per ruolo del server. Non è più necessario cercare ogni volta su Google quali porte utilizza quale servizio!

Concesso, specialmente per Active Directory, le porte possono variare in base al sistema operativo del server, ai vari servizi su un controller di dominio, ecc. Sentiti libero di modificarli come necessario per il tuo ambiente.

Ecco uno screen host di un esempio di utilizzo:

Testing ports with PowerShell

Come puoi vedere, puoi specificare quanti server desideri e produrrà un bel elenco di oggetti suddivisi per gruppo di porte di servizio e la porta per ogni computer. Finora è stato molto utile! Spero che tu ne trai qualche vantaggio come ho fatto io!

function Test-ServerRolePortGroup {
	<#
	.SYNOPSIS
		Questa funzione testa le porte TCP/UDP aperte in base al ruolo del server.
	.DESCRIPTION
		Questa funzione testa tutte le porte TCP/UDP appropriate in base al ruolo del server, così da evitarti di memorizzare o cercare tutte le porte da testare ogni volta che desideri verificare la connettività remota su un ruolo specifico del server.
	.NOTES
		Link ai riferimenti delle porte:
		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 Computername
		Uno o più nomi di computer remoti separati da virgole
	.PARAMETER ServerRole
		I servizi sul computer per cui desideri trovare le porte aperte. Possono essere
		servizi comuni come WinRm, Smb, Dns, Active Directory e NetBIOS
	.EXAMPLE
		PS> Test-ServerRolePortGroup -Computername 'LABDC','LABDC2' -ServerRole NetBIOS,WinRm,Dns
		
		Questo esempio testa le porte di rete necessarie per NetBIOS, WinRm e Dns
		sui server 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 {
	<#
	.SYNOPSIS
		Questa funzione testa le porte TCP/UDP aperte.
	.DESCRIPTION
		Questa funzione testa qualsiasi porta TCP/UDP per verificare se è aperta o chiusa.
	.NOTES
		Problema noto: Se questa funzione viene chiamata consecutivamente 10-20 volte sulla stessa porta
			e sullo stesso computer, il controllo della porta UDP restituirà $false quando potrebbe essere
			$true. Non ho ancora capito il motivo di ciò.
	.PARAMETER Computername
		Uno o più nomi di computer remoti separati da virgole
	.PARAMETER Port
		Uno o più numeri di porta separati da virgole che desideri testare.
	.PARAMETER Protocol
		Il protocollo (UDP o TCP) che testerai
	.PARAMETER TcpTimeout
		Il numero di millisecondi che la funzione attende prima di dichiarare
		la porta TCP chiusa.
	.PARAMETER
		Il numero di millisecondi che la funzione attende prima di dichiarare
		la porta UDP chiusa.
	.EXAMPLE
		PS> Test-Port -Computername 'LABDC','LABDC2' -Protocol TCP 80,443
		
		Questo esempio testa le porte di rete TCP 80 e 443 su entrambi i server 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)
					#L'oggetto IPEndPoint ci consentirà di leggere i datagrammi inviati da qualsiasi origine.
					Write-Verbose "$($MyInvocation.MyCommand.Name) - Creating remote endpoint"
					$remoteendpoint = New-Object system.net.ipendpoint([system.net.ipaddress]::Any, 0)
					try {
						#Blocca l'esecuzione finché non viene restituito un messaggio su questo socket da un 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/