PowerShell Test-Port: Ein Leitfaden für ein benutzerdefiniertes Tool

Lassen Sie uns eintauchen, wie man ein PowerShell-Testport-Tool erstellt, das Ihnen ermöglicht, offene Ports nach Portnummer und Bezeichnung zu testen.

Um ein robustes Skript zu erstellen, das nicht bei der Hälfte Ihrer Server abstürzt, ist es wichtig, zunächst sicherzustellen, dass die Voraussetzungen erfüllt sind, damit Sie Ihr gewünschtes Ergebnis erzielen können.

Was sind diese Voraussetzungen? Voraussetzungen sind Dienste wie FTP, HTTP, DCOM, WMI, WSMAN usw. Die Verbindung, die Sie zu einem Server herstellen möchten, hängt in der Regel von Diensten wie diesen ab.

Es gibt eine Hierarchie von mehreren Ebenen der Überprüfungen, die Sie an Ihren Servern durchführen können, bevor Sie versuchen, eine Verbindung herzustellen, abhängig davon, wie genau Sie vorgehen möchten.

Zum Anfang haben Sie den gesamten OSI-Stack auf der Netzwerkseite zu durchqueren. Das schließt nicht die Dienste auf dem Hostsystem ein, von dem aus Sie das Skript ausführen, und alles andere auf dem entfernten Hostsystem.

Einer der ersten Tests, die Sie durchführen müssen, wenn Sie einen entfernten Server abfragen, ist die Überprüfung, ob die entsprechenden Netzwerkports offen und zugänglich sind. Abhängig von den Diensten, die auf dem entfernten Server laufen, hängt es von den Ports ab, die Sie abfragen müssen.

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.

Mit Hilfe dieses Technet-Skripts ist es mir gelungen, ein ziemlich gutes Paar von PowerShell-Funktionen zu erstellen, die es Ihnen nicht nur ermöglichen, offene TCP- und UDP-Ports zu testen, sondern auch Portgruppen nach Serverrolle zu testen. Kein ständiges Googeln mehr, jedes Mal wenn man wissen will, welche Ports welcher Dienst verwendet!

Gewährt, insbesondere für Active Directory können die Ports je nach Serverbetriebssystem, verschiedenen Diensten auf einem Domänencontroller usw. variieren. Passen Sie sie bei Bedarf für Ihre Umgebung an.

Hier ist ein Bildschirmfoto eines Anwendungsbeispiels:

Testing ports with PowerShell

Wie Sie sehen können, können Sie so viele Server wie gewünscht angeben und es wird eine schöne Liste von Objekten erstellt, die nach der Dienstportgruppe und dem Port für jeden Computer aufgeschlüsselt sind. Bisher hat es sich als sehr nützlich erwiesen! Ich hoffe, Sie können ebenso davon profitieren wie ich!

function Test-ServerRolePortGroup {
	<#
	.SYNOPSIS
		Diese Funktion testet offene TCP/UDP-Ports nach Serverrolle.
	.DESCRIPTION
		Diese Funktion testet alle relevanten TCP/UDP-Ports nach Serverrolle, sodass Sie nicht alle Ports auswendig lernen oder jedes Mal nachschlagen müssen, wenn Sie die Remote-Konnektivität für eine bestimmte Serverrolle überprüfen möchten.
	.NOTES
		Verweis auf Portnummern:
		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
		Ein oder mehrere entfernte, durch Komma getrennte Computernamen.
	.PARAMETER ServerRole
		Die Dienste auf dem Computer, für die Sie offene Ports finden möchten. Dies können gängige Dienste wie WinRm, Smb, Dns, Active Directory und NetBIOS sein.
	.EXAMPLE
		PS> Test-ServerRolePortGroup -Computername 'LABDC','LABDC2' -ServerRole NetBIOS,WinRm,Dns
		
		Dieses Beispiel testet die Netzwerkports, die für NetBIOS, WinRm und Dns auf den Servern LABDC und LABDC2 benötigt werden.
	#>
	
	[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
		Diese Funktion testet offene TCP/UDP-Ports.
	.DESCRIPTION
		Diese Funktion testet einen beliebigen TCP/UDP-Port, um zu sehen, ob er geöffnet oder geschlossen ist.
	.NOTES
		Bekanntes Problem: Wenn diese Funktion innerhalb von 10-20 aufeinanderfolgenden Aufrufen auf denselben Port und Computer ausgeführt wird, gibt der UDP-Port-Check $false aus, wenn es eigentlich $true sein sollte. Ich habe noch nicht herausgefunden, warum dies passiert.
	.PARAMETER Computername
		Ein oder mehrere entfernte, durch Komma getrennte Computernamen.
	.PARAMETER Port
		Eine oder mehrere durch Komma getrennte Portnummern, die Sie testen möchten.
	.PARAMETER Protocol
		Das Protokoll (UDP oder TCP), das Sie testen werden.
	.PARAMETER TcpTimeout
		Die Anzahl der Millisekunden, die die Funktion wartet, bis sie den TCP-Port als geschlossen erklärt.
	.PARAMETER UdpTimeout
		Die Anzahl der Millisekunden, die die Funktion wartet, bis sie den UDP-Port als geschlossen erklärt.
	.EXAMPLE
		PS> Test-Port -Computername 'LABDC','LABDC2' -Protocol TCP 80,443
		
		Dieses Beispiel testet die TCP-Netzwerkports 80 und 443 auf den Servern LABDC und 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)
					#Das IPEndPoint-Objekt ermöglicht es uns, Datagramme von jeder Quelle zu lesen.
					Write-Verbose "$($MyInvocation.MyCommand.Name) - Creating remote endpoint"
					$remoteendpoint = New-Object system.net.ipendpoint([system.net.ipaddress]::Any, 0)
					try {
						#Blockiert, bis eine Nachricht von einem entfernten Host auf diesem Socket zurückkehrt.
						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/