PowerShell Test-Port: 一个自定义工具指南

让我们深入了解如何构建一个 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 端口,还可以按服务器角色测试端口组。不再需要每次都搜索哪个服务使用了哪些端口了!

已授予,特别是对于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
		
		此示例测试LABDC和LABDC2服务器上操作NetBIOS、WinRm和Dns所需的网络端口。
	#>
	
	[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端口检查将在可以为$true时输出$false。我还没有弄清楚为什么会这样。
	.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/