PowerShell Test-Port:カスタムツールガイド

PowerShellテストポートツールを構築する方法について詳しく見ていきましょう。このツールを使用すると、ポート番号とラベルによるオープンポートのテストができます。

サーバーの半分でスクリプトが失敗しない堅牢なスクリプトを構築するためには、最初に望む結果を得るために必要な前提条件が満たされていることを確認することが重要です。

これらの前提条件はFTP、HTTP、DCOM、WMI、WSMANなどのサービスです。サーバーに接続しようとしている接続は、通常、これらのサービスに依存します。

接続を試みる前に、サーバー上で様々なレイヤーのチェックを実行する階層があります。どれだけ厳密にチェックするかによって、実行するチェックの数が異なります。

まず、ネットワーク側でOSIスタック全体をトラバースする必要があります。これには、スクリプトを実行しているホストシステム上のサービスやリモートホストシステム上の他のすべてのものは含まれません。

リモートサーバーにクエリを実行する際に最初に行うテストの1つは、適切なネットワークポートが開かれていてアクセス可能であることを確認することです。リモートサーバーで実行されているサービスによって、クエリする必要のあるポートが異なります。

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のスクリプトの助けを借りて、TCPおよびUDPポートが開いているかどうかをテストするだけでなく、サーバーロールによるポートグループのテストを行うことができる便利なPowerShell関数を作成しました。サービスごとにどのポートを使用するかを毎回Googleで検索する必要はありません!

特に、Active Directoryについては、サーバーのOSによってポートが異なる場合や、ドメインコントローラー上のさまざまなサービスによっても異なる場合があります。環境に合わせて必要に応じて調整してください。

以下に、使用例のスクリーンショットを示します。

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
		1つ以上のリモートコンピュータ名をコンマで区切って指定します。
	.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ポートチェックは$trueであるべきところを$falseで出力します。これについてはまだ理由がわかりません。
	.PARAMETER Computername
		1つ以上のリモートコンピュータ名をコンマで区切って指定します。
	.PARAMETER Port
		テストしたい1つ以上のコンマで区切られたポート番号です。
	.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/