PowerShell 測試端口:自定義工具指南

讓我們深入了解如何建立一個 PowerShell 測試端口工具,這個工具可以讓您根據端口號和標籤來測試開放的端口。

要建立一個堅固的腳本,確保它不會在您的一半伺服器上出現故障,首先確保您的終極目標需要的前提條件已經滿足是很重要的。

這些前提條件是什麼?前提條件包括像 FTP、HTTP、DCOM、WMI、WSMAN 等服務。您嘗試連接到伺服器的連接通常依賴於這些服務。

在嘗試建立連接之前,您可以對伺服器執行幾個層次的檢查,具體取決於您希望進行多嚴格的檢查。

首先,您需要在網路端遍歷整個 OSI 協定棧。這還不包括您從中執行腳本的主機系統上的服務以及遠端主機系統上的其他所有東西。

在查詢遠端伺服器時,您需要執行的第一個測試之一是檢查適當的網路端口是否已打開並可訪問。取決於遠端伺服器上運行的服務,您需要查詢的端口也會不同。

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.

有了這個 Technet 腳本的幫助,我成功地創建了一對非常好用的 PowerShell 函數,它不僅可以讓您測試開放的 TCP 和 UDP 端口,還可以根據伺服器角色測試端口組。不再需要每次都 Google 服務使用的端口了!

當然,尤其是對於Active Directory,端口可能因服務器操作系統、域控制器上的各種服務等而有所不同。根據您的環境需要進行調整。

這裡是一個使用示例的屏幕截圖:

Testing ports with PowerShell

您可以看到,您可以指定任意多的服務器,它將輸出一個漂亮的對象列表,按服務端口組和每台計算機的端口進行分類。到目前為止,它非常有用!希望您也像我一樣能從中受益!

function Test-ServerRolePortGroup {
	<#
	.SYNOPSIS
		此功能測試伺服器角色的開放 TCP/UDP 埠。
	.DESCRIPTION
		此功能按伺服器角色測試所有適當的 TCP/UDP 埠,因此您不需要記住或查找每次想要驗證特定伺服器角色上的遠程連接性時需要測試的所有埠。
	.NOTES
		連接埠參考連結:
		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
		一個或多個遠程、以逗號分隔的計算機名稱
	.PARAMETER ServerRole
		您想要為其找到開放埠的計算機上的服務。這可以是常見的服務,如 WinRm、Smb、Dns、Active Directory 和 NetBIOS
	.EXAMPLE
		PS> Test-ServerRolePortGroup -Computername 'LABDC','LABDC2' -ServerRole NetBIOS,WinRm,Dns
		
		此示例測試運行 NetBIOS、WinRm 和 Dns 所需的網絡埠
		在 LABDC 和 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
		此功能測試開放的 TCP/UDP 埠。
	.DESCRIPTION
		此功能測試任何 TCP/UDP 埠以查看它是開放還是關閉。
	.NOTES
		已知問題:如果此功能在同一埠和計算機上連續 10-20 次調用,UDP 埠檢查將輸出 $false,當它可以是
		$true。我還沒有弄清楚為什麼會這樣。
	.PARAMETER Computername
		一個或多個遠程、以逗號分隔的計算機名稱
	.PARAMETER Port
		您想要測試的一個或多個以逗號分隔的埠號。
	.PARAMETER Protocol
		您將測試的協議(UDP 或 TCP)
	.PARAMETER TcpTimeout
		函數將等待的毫秒數,直到宣布
		TCP 埠關閉。
	.PARAMETER
		函數將等待的毫秒數,直到宣布
		UDP 埠關閉。
	.EXAMPLE
		PS> Test-Port -Computername 'LABDC','LABDC2' -Protocol TCP 80,443
		
		此示例測試 LABDC
		和 LABDC2 伺服器上的 TCP 網絡埠 80 和 443。
	#>
	[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)
					#IPEndPoint 對象將允許我們讀取從任何源發送的數據報。
					Write-Verbose "$($MyInvocation.MyCommand.Name) - Creating remote endpoint"
					$remoteendpoint = New-Object system.net.ipendpoint([system.net.ipaddress]::Any, 0)
					try {
						#阻塞直到此套接字從遠程主機返回一條消息。
						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/