Test de port PowerShell : Guide d’utilisation d’un outil personnalisé

Plongeons dans la façon de construire un outil de test de port PowerShell qui vous permet de tester les ports ouverts par numéro de port et par libellé.

Pour construire un script robuste qui ne va pas s’effondrer et échouer sur la moitié de vos serveurs, il est important de s’assurer d’abord que les prérequis nécessaires pour obtenir votre résultat final sont satisfaits.

Quels sont ces prérequis ? Les prérequis sont des services tels que FTP, HTTP, DCOM, WMI, WSMAN, etc. La connexion que vous essayez d’établir avec un serveur dépend généralement de services comme ceux-ci.

Il existe une hiérarchie de plusieurs niveaux de vérifications que vous pouvez effectuer sur vos serveurs avant de tenter une connexion, selon le degré de précision souhaité.

Pour commencer, vous avez toute la pile OSI à traverser du côté réseau. Cela n’inclut pas les services sur le système hôte à partir duquel vous exécutez le script et tout le reste sur le système hôte distant.

Un des premiers tests que vous devez effectuer lors de l’interrogation d’un serveur distant consiste à vérifier que les ports réseau appropriés sont ouverts et accessibles. Selon les services exécutés sur le serveur distant, les ports à interroger peuvent varier.

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.

Grâce à l’aide de ce script Technet, j’ai réussi à créer une paire de fonctions PowerShell assez bonnes qui vous permettront non seulement de tester les ports TCP et UDP ouverts, mais aussi de tester des groupes de ports par rôle de serveur. Plus besoin de chercher sur Google à chaque fois pour savoir quels ports sont utilisés par quel service !

Accordé, surtout pour Active Directory, les ports peuvent varier en fonction du système d’exploitation du serveur, des différents services sur un contrôleur de domaine, etc. N’hésitez pas à les ajuster selon les besoins de votre environnement.

Voici une capture d’écran d’un exemple d’utilisation:

Testing ports with PowerShell

Comme vous pouvez le voir, vous pouvez spécifier autant de serveurs que vous le souhaitez, et il générera une belle liste d’objets répartis par groupe de ports de service et le port pour chaque ordinateur. Jusqu’à présent, cela m’a été très utile ! J’espère que vous en tirerez autant de profit que moi !

function Test-ServerRolePortGroup {
	<#
    .SYNOPSIS
        Cette fonction teste les ports TCP/UDP ouverts par rôle de serveur.
    .DESCRIPTION
        Cette fonction teste tous les ports TCP/UDP appropriés par rôle de serveur afin que vous n'ayez pas à mémoriser ou rechercher tous les ports à tester chaque fois que vous souhaitez vérifier la connectivité à distance sur un rôle de serveur spécifique.
    .NOTES
        Liens de référence des ports :
        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
    .PARAMÈTRE NomOrdinateur
        Un ou plusieurs noms d'ordinateurs distants, séparés par des virgules.
    .PARAMÈTRE RôleServeur
        Les services sur l'ordinateur pour lesquels vous souhaitez trouver les ports ouverts. Cela peut être des services courants comme WinRm, Smb, Dns, Active Directory et NetBIOS.
    .EXEMPLE
        PS> Test-ServerRolePortGroup -NomOrdinateur 'LABDC','LABDC2' -RôleServeur NetBIOS,WinRm,Dns
        
        Cet exemple teste les ports réseau nécessaires pour NetBIOS, WinRm et Dns
        pour fonctionner sur les serveurs LABDC et 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
        Cette fonction teste les ports TCP/UDP ouverts.
    .DESCRIPTION
        Cette fonction teste n'importe quel port TCP/UDP pour voir s'il est ouvert ou fermé.
    .NOTES
        Problème connu : Si cette fonction est appelée 10 à 20 fois consécutivement sur le même port
            et ordinateur, la vérification du port UDP renverra $false alors qu'elle pourrait être
            $true. Je n'ai pas compris pourquoi cela se produit.
    .PARAMÈTRE NomOrdinateur
        Un ou plusieurs noms d'ordinateurs distants, séparés par des virgules.
    .PARAMÈTRE Port
        Un ou plusieurs numéros de port, séparés par des virgules, que vous souhaitez tester.
    .PARAMÈTRE Protocole
        Le protocole (UDP ou TCP) que vous testerez.
    .PARAMÈTRE DélaiAttenteTcp
        Le nombre de millisecondes pendant lesquelles la fonction attendra avant de déclarer
        le port TCP fermé.
    .PARAMÈTRE DélaiAttenteUdp
        Le nombre de millisecondes pendant lesquelles la fonction attendra avant de déclarer
        le port UDP fermé.
    .EXEMPLE
        PS> Test-Port -NomOrdinateur 'LABDC','LABDC2' -Protocole TCP 80,443
        
        Cet exemple teste les ports réseau TCP 80 et 443 sur les serveurs LABDC
        et 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)
					#L'objet IPEndPoint nous permettra de lire les datagrammes envoyés depuis n'importe quelle source.
					Write-Verbose "$($MyInvocation.MyCommand.Name) - Creating remote endpoint"
					$remoteendpoint = New-Object system.net.ipendpoint([system.net.ipaddress]::Any, 0)
					try {
						#Bloque jusqu'à ce qu'un message revienne sur ce socket en provenance d'un hôte distant.
						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/