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-порты, но и тестировать группы портов по роли сервера. Больше не нужно искать в Google каждый раз, чтобы узнать, какие порты использует та или иная служба!

Предоставляется, особенно для Active Directory, порты могут различаться в зависимости от операционной системы сервера, различных служб на контроллере домена и т. д. Не стесняйтесь настраивать их по необходимости для вашей среды.

Вот скриншот примера использования:

Testing ports with PowerShell

Как видите, вы можете указать любое количество серверов, и он выведет красивый список объектов, разбитых по группе портов служб и порту для каждого компьютера. Пока что это очень пригодилось! Надеюсь, вы также найдете в нем пользу, как и я!

function Test-ServerRolePortGroup {
	<#
	.СИНОПСИС
		Эта функция проверяет открытые порты TCP/UDP по роли сервера.
	.ОПИСАНИЕ
		Эта функция проверяет все соответствующие порты TCP/UDP по роли сервера, чтобы вам не приходилось
		запоминать или искать все порты, которые необходимо проверить каждый раз,
		когда вы хотите проверить удаленное подключение к конкретной роли сервера.
	.ЗАМЕТКИ
		Ссылки на порты:
		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
	.ПАРАМЕТР Компьютерное_имя
		Одно или несколько удаленных, через запятую, имен компьютеров
	.ПАРАМЕТР Роль_сервера
		Службы на компьютере, для которых вы хотели бы найти открытые порты. Это может быть
		общие службы, такие как WinRm, Smb, Dns, Active Directory и NetBIOS
	.ПРИМЕР
		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 {
	<#
	.СИНОПСИС
		Эта функция проверяет открытые порты TCP/UDP.
	.ОПИСАНИЕ
		Эта функция проверяет любой порт TCP/UDP, чтобы увидеть, открыт он или закрыт.
	.ЗАМЕТКИ
		Известная проблема: Если эта функция вызывается в пределах 10-20 последовательно на одном порту
			и компьютере, проверка UDP-порта выдаст $false, когда она может быть
			$true. Я не понял, почему это происходит.
	.ПАРАМЕТР Компьютерное_имя
		Одно или несколько удаленных, через запятую, имен компьютеров
	.ПАРАМЕТР Порт
		Один или несколько через запятую номеров портов, которые вы хотели бы протестировать.
	.ПАРАМЕТР Протокол
		Протокол (UDP или TCP), который вы будете тестировать
	.ПАРАМЕТР TcpTimeout
		Количество миллисекунд, которое функция будет ждать, пока
		TCP-порт будет объявлен закрытым.
	.ПАРАМЕТР UdpTimeout
		Количество миллисекунд, которое функция будет ждать, пока
		UDP-порт будет объявлен закрытым.
	.ПРИМЕР
		PS> Test-Port -Computername 'LABDC','LABDC2' -Protocol TCP 80,443
		
		Этот пример тестирует TCP-сетевые порты 80 и 443 на серверах LABDC
		и 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)
					#Объект 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/