Question Get-NetBiosInfos

Plus d'informations
il y a 13 ans 4 mois #9652 par Matthew BETTON
Get-NetBiosInfos a été créé par Matthew BETTON
Bonjour,

Ci-après un script qui permet de récupérer, depuis une adresse IP, le nom NetBios et le domain NetBios d'une machine (ou de plusieurs).

Évidemment, s'il s'agit d'une machine VMS, Linux sans Samba, etc... Pas d'information NetBios !

L'équivalent de la commande DOS \"nbtstat -a @IP\", sauf qu'ici, le script renvoie des objets...

La fonction accepte les entrées depuis le pipeline.

Ce script peut par exemple servir à découvrir des machines sur le réseau.

Un intérêt particulier pour le simple fait de pouvoir récupérer le nom NetBios du domaine de la machine (Domaine ou WorkGroup), sans avoir à s'authentifier (scan de niveau 1).

Un dernier intérêt pour découvrir les fonctions avancées, l'utilisation de \"Command based Help\", la création et l'utilisation d'objet PSObject, le traitement de valeur(s) récupérée(s) depuis le pipeline (begin / process /end).

Enjoy B)


[code:1]# ===============================================
# Script Get-NetBiosInfos
# Date : Le 13 mai 2011
# Auteur : Matthew BETTON (www.via-powershell.fr)
# Lieu : FRANCE / Basse Normandie / Manche
# ===============================================

Function Get-NetBiosInfos{
<#
.SYNOPSIS
Récupère des informations NetBios à partir d'une adresse IP.
.DESCRIPTION
Cette fonction permet de récupérer des informations NetBios pour l'adresse IP
d'une machine donnée en paramètre.
Ces informations NetBios sont le nom NetBios de la machine et le nom NetBios du domaine d'appartenance de la machine.
L'adresse IP doit correspondre à une adresse IP valide.
Si certains octets de l'adresse ne sont pas renseignés, ils sont implicitement remplacés par la valeur 0 (ex. : 10.69 va correspondre à l'adresse IP 10.0.0.69)
.PARAMETER IPAddress
Ce paramètre est obligatoire.
Spécifie l'adresse IP de la machine pour laquelle on souhaite récupérer les informations NetBios.
.EXAMPLE
Get-NetBiosInfos 192.168.1.2

Description
Renvoie les informations NetBios (Nom et Domaine) de la machine correspondant à l'adresse IP 192.168.1.2 :
- Si celle-ci est joignable sur le réseau ;
- Si la machine dispose de ces informations.
.EXAMPLE
Get-NetBiosInfos -IP 192.168.1.2, 192.168.1.3

Description
Renvoie les informations NetBios (Nom et Domaine) de la machine correspondant aux adresses mentionnées.
.EXAMPLE
\"192.168.1.2\", \"192.168.1.3\" | Get-NetBiosInfos

Description
Renvoie les informations NetBios (Nom et Domaine) de la machine correspondant aux adresses renvoyées par le pipeline.
.INPUTS
System.String
il est possible de transmettre une adresse ou plusieurs adresses à cette fonction via le pipeline.
.OUTPUTS
System.Management.Automation.PSCustomObject
Peut être une valeur $null : Cas de machine(s) qui ne répond(ent) pas ou qui ne dispos(ent) pas d'informations NetBios.
#>
[CmdletBinding()]
param(
[Parameter(Position=0,Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[Alias(\"IP\",\"Address\"«»)]
[ValidateScript({[BOOL][system.net.IPAddress]::«»Parse($_)})]
[string[]]$IpAddress
)

begin{
Function GetNBTInfos([String]$myIP){
$myAddress = [system.net.IPAddress]::«»Parse($myIP)

Write-Debug \"Contrôle que la machine cible ayant l'adresse $($myAddress.IPAddressToString) est bien joignable\"
$pingsender = new-object system.net.networkinformation.ping
$reply = $pingsender.send($myAddress.IPAddressToString)
if($reply.status -ne \"Success\"«»){
Write-Debug \"L'adresse $($myAddress.IPAddressToString) n'est pas joignable, on quitte la fonction\"
Write-Warning \"L'adresse IP '$($myAddress.IPAddressToString)' ne répond pas !\"
return $null
}

Write-Debug \"Définition du port UDP 137, port de destination pour la requêtre NetBios\"
[int]$Port = 137

Write-Debug \"Création de l'objet qui servira à stocker les informations NetBios\"
$TargetInfos = New-Object PSObject -Property @{IPAddress = $myAddress.IPAddressToString;NetBiosName = \"\";NetBiosDomain = \"\"}
$Target = New-Object System.Net.IPEndPoint $myAddress, $port
$SenderIPEndPoint = New-Object Net.IPEndPoint([Net.IPAddress]::Any, 0)
$SenderEndPoint = [Net.EndPoint]$SenderIPEndPoint

Write-Debug \"Préparation à l'instance de la classe Socket ...\"
$Saddrf= [System.Net.Sockets.AddressFamily]::InterNetwork
$Stype = [System.Net.Sockets.SocketType]::«»Dgram
$Ptype = [System.Net.Sockets.ProtocolType]::UDP
$Sock= New-Object System.Net.Sockets.Socket $saddrf, $stype, $ptype
Write-Debug \"Définition des paramètres SendTimeOut et ReceiveTimeOut ...\"
$Sock.SendTimeOut = 1000
$Sock.ReceiveTimeout = 1000

Write-Debug \"Création du buffer qui servira à 'lire' la réponse\"
$buffer = new-object byte[] 1KB
Write-Debug \"Définition du datagram pour une requête NetBios\"
[Byte[]] $namerequest = 0x80, 0x94, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x20, 0x43, 0x4b, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x00, 0x00, 0x21,
0x00, 0x01

Write-Debug \"Transmission du datagram vers '$($myAddress.IPAddressToString)'\"
$a = $Sock.bind($SenderIPEndPoint)
$a = $sock.sendto($namerequest, $Target)

try{
$receiveByteCount = $Sock.ReceiveFrom($buffer, [Ref]$SenderEndPoint)
if($receiveByteCount -ge 90){
$Encoding = New-Object System.Text.ASCIIEncoding
Write-Debug \"Lecture du nom NetBios de la machine, dans le Buffer ...\"
$TargetInfos.NetBiosName = ($Encoding.GetString($buffer, 57, 15)).Trim()
Write-Debug \"Lecture du nom NetBios du domaine de la machine, dans le Buffer ...\"
$TargetInfos.NetBiosDomain = ($Encoding.GetString($buffer, 75, 15)).Trim()
}
}
catch{
Write-Debug \"La lecture du Buffer n'a pas fonctionné (cas probable d'une machine Unix, Linux, VMS, etc...)\"
Write-Warning \"Le périphérique cible n'a pu être identifié par un nom NetBios\"
return $null
}

Write-Debug \"Retourne l'objet comprenant les propriétés IPAddress, NetBiosName et NetBiosDomain ...\"
return $TargetInfos
}

$TabInfos = @()

}

process{
if ($PSBoundParameters) {
Write-Verbose \"Le paramétre '-IpAddress' a été utilisé\"
foreach($ipa in $IpAddress){
$TabInfos += GetNBTInfos $ipa
}
}
else{
Write-Verbose \"Le paramétre '-IpAddress' n'a pas été utilisé\"
Write-Verbose \"Récupération de l'adresse ou des adresses depuis le pipeline\"
$TabInfos += GetNBTInfos $IpAddress
}
}

end{
Write-Debug \"Retourne le tableau de résultat(s)\"
$TabInfos
}

}
[/code:1]<br><br>Message édité par: Matthew BETTON, à: 14/05/11 08:40

Connexion ou Créer un compte pour participer à la conversation.

Plus d'informations
il y a 13 ans 4 mois #9654 par Laurent Dardenne
Réponse de Laurent Dardenne sur le sujet Re:Get-NetBiosInfos
Salut,
qq remarques.

Une coquille :
Write-Debug \&quot;Définition du port UDP 137, port de destination pour la requête NetBios\&quot;

Je pense que le bloc process peut être réduit à ceci :
[code:1]
foreach($ipa in $IpAddress){
Write-Debug \&quot;Traitement de l'adresse $ipa\&quot;
$TabInfos += GetNBTInfos $ipa
}
[/code:1]
Si tu précises un seul paramètre obligatoire on est assuré qu'il soit lié.
De plus son type étant [string[], ps le transformera tjr ton paramètre un tableau :
[code:1]
trace-command parameterbinding {Get-NetBiosInfos 1} -FilePath c:\temp\log1.txt
trace-command parameterbinding {Get-NetBiosInfos @(1)} -FilePath c:\temp\log2.txt
#comparer avec winMerge, par exemple
[/code:1]
Sans compter la covariance .net sur les tableaux ( string -&gt; Ipaddress)

l'appel à Parse dans GetNBTInfos me semble redondant:
[code:1]
$myAddress = [system.net.IPAddress]::«»Parse($myIP)
[/code:1]
si le premier appel sur l'attribut validatescript réussi, le second réussira .
Si le premier échoue, on ne passera pas par le second...

L'usage d'un bloc end fait que l'on attend la fin du traitement de toute les adresses IP avant de les réémettre dans le pipeline.

Le bloc try/catch sur l'appel $Sock.ReceiveFrom peut être plus précis sur le rapport d'erreur, voir la note sur msdn .
Et je me demande si un Write-Error en lieu et place d'un Write-Warning ne serait pas plus pertinent.


Ensuite qq adresses pose pb :
[code:1]
Get-NetBiosInfos 0
Get-NetBiosInfos 127.0.0.1
[/code:1]
Enfin ce point de la doc :

Si certains octets de l'adresse ne sont pas renseignés, ils sont implicitement remplacés par la valeur 0 (ex. : 10.69 va correspondre à l'adresse IP 10.0.0.69)

pourrait être précisé par exemple avec un renvoi sur msdn, car on ne connait pas la régle appliquée. On peut aussi utiliser ceci :
[code:1]
Get-NetBiosInfos 527
#AVERTISSEMENT : L'adresse IP '0.0.2.15' ne répond pas !
[/code:1]

Une possible amélioration, implémenter en natif, si possible, les paramètres -AsJob et -ThrottleLimit :-)

[code:1]
Function Foreach-Parallel
{
#Auteur Nicolas Vallée
Param (
[Parameter(Mandatory=$true,Position=0)][scriptblock] $Task,
[Parameter(Mandatory=$true,ValueFromPipeline=$true,Position=1)][object[]] $Data,
[Parameter(Mandatory=$false)][ValidateScript({$_ -gt 0})][int] $Max = 4,
[Parameter(Mandatory=$false)][string[]] $Dependancies
)
Begin
{
$jobs = New-Object System.Collections.ArrayList
#Set-Variable pool (New-Object System.Threading.Semaphore $Max,$Max) -scope global
}
Process
{
$init = {
Set-Variable -Scope global -Name FinalExtension -Value $global:FinalExtension
Set-Variable -Scope global -Name Converter -Value $global:Converter
}.ToString()
$init = New-ScriptBlock ($executioncontext.InvokeCommand.ExpandString($init))
#$job = { $global:«»pool.WaitOne() ; . $Task $Data ; $global:«»pool.Release() }
$job = {
Param($dep,$t,$d)
$dep | Foreach-Object { Invoke-Expression \&quot;. '$_'\&quot; }
#Write-Warning \&quot;$t`n'$d'\&quot;
$sb = New-ScriptBlock ($t)
Invoke-Command $sb -Argument @($d)
} #; $global:«»pool.Release() }
#[void] $jobs.Add((Start-Thread -ScriptBlock $job ))
#[void] $global:«»pool.WaitOne()
[void] $jobs.Add((Start-Job $job -Argument @(@($dependancies),$Task,@($Data)) -InitializationScript $init ))
}
End
{
Wait-Job -Job $jobs | Receive-Job
#$jobs | Foreach-Object { Join-Thread -thread $_ }
}
}
[/code:1]
source .
Maintenant faut que je trouve du temps pour proposer une implémentation :(

Tutoriels PowerShell

Connexion ou Créer un compte pour participer à la conversation.

Plus d'informations
il y a 13 ans 4 mois #9662 par Matthew BETTON
Réponse de Matthew BETTON sur le sujet Re:Get-NetBiosInfos
Salut,

Un GRAND merci pour tes remarques. ;)

Ci-après le script modifié :

[code:1]

# ===============================================
# Script Get-NetBiosInfos
# Date : Le 16 mai 2011
# Auteur : Matthew BETTON (www.via-powershell.fr)
# Lieu : FRANCE / Basse Normandie / Manche
# ===============================================

Function Get-NetBiosInfos{
&lt;#
.SYNOPSIS
Récupère des informations NetBios à partir d'une adresse IP.
.DESCRIPTION
Cette fonction permet de récupérer des informations NetBios pour l'adresse IP
d'une machine donnée en paramètre.
Ces informations NetBios sont le nom NetBios de la machine et le nom NetBios du domaine
d'appartenance de la machine.
L'adresse IP doit correspondre à une adresse IP valide.
Voir à l'adresse msdn.microsoft.com/en-us/library/system....ipaddress.parse.aspx (section 'Remarks') pour plus de détails sur le format attendu.
.PARAMETER IPAddress
Ce paramètre est obligatoire.
Spécifie l'adresse IP de la machine pour laquelle on souhaite récupérer les informations NetBios.
.EXAMPLE
Get-NetBiosInfos 192.168.1.2

Description
Renvoie les informations NetBios (Nom et Domaine) de la machine correspondant à l'adresse IP 192.168.1.2 :
- Si celle-ci est joignable sur le réseau ;
- Si la machine dispose de ces informations.
.EXAMPLE
Get-NetBiosInfos -IP 192.168.1.2, 192.168.1.3

Description
Renvoie les informations NetBios (Nom et Domaine) de la machine correspondant aux adresses mentionnées.
.EXAMPLE
\&quot;192.168.1.2\&quot;, \&quot;192.168.1.3\&quot; | Get-NetBiosInfos

Description
Renvoie les informations NetBios (Nom et Domaine) de la machine correspondant aux adresses renvoyées par le pipeline.
.EXAMPLE
'toto', '127.0.0.1', '192.168.0.1', '192.168.1.2', '192.168.1.1', '192.168.1.31' | Get-NetBiosInfos -ErrorAction SilentlyContinue
Description
Renvoie les informations NetBios (Nom et Domaine) de la machine correspondant aux adresses renvoyées par le pipeline.
Les erreurs ne sont pas tracées.
.INPUTS
System.String
Il est possible de transmettre une adresse ou plusieurs adresses à cette fonction via
le pipeline.
.OUTPUTS
System.Management.Automation.PSCustomObject
Peut être une valeur $null : Cas de machine(s) qui ne répond(ent) pas ou qui ne
dispos(ent) pas d'informations NetBios.
#&gt;
[CmdletBinding()]
param(
[Parameter(Position=0,Mandatory=$True,ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[Alias(\&quot;IP\&quot;,\&quot;Address\&quot;)]
[ValidateScript({[BOOL][system.net.IPAddress]::Parse($_)})]
[string[]]$IpAddress
)

begin{
Function GetNBTInfos([String]$myIP){

Write-Debug \&quot;Contrôle que la machine cible ayant l'adresse $($myAddress.IPAddressToString) est bien joignable\&quot;
$pingsender = new-object system.net.networkinformation.ping
$reply = $pingsender.send($myIP)
if($reply.status -ne \&quot;Success\&quot;){
Write-Debug \&quot;L'adresse $myIP n'est pas joignable, on quitte la fonction\&quot;
Write-Warning \&quot;L'adresse IP '$myIP' ne répond pas !\&quot;
return $null
}

Write-Debug \&quot;Définition du port UDP 137, port de destination pour la requête NetBios\&quot;
[int]$Port = 137

Write-Debug \&quot;Création de l'objet qui servira à stocker les informations NetBios\&quot;
$TargetInfos = New-Object PSObject -Property @{IPAddress = $myIP;NetBiosName = \&quot;\&quot;;NetBiosDomain = \&quot;\&quot;}
$Target = New-Object System.Net.IPEndPoint (([Net.IPAddress]$myIP).Address), $port
$SenderIPEndPoint = New-Object Net.IPEndPoint([Net.IPAddress]::Any, 0)
$SenderEndPoint = [Net.EndPoint]$SenderIPEndPoint

Write-Debug \&quot;Préparation à l'instance de la classe Socket ...\&quot;
$Saddrf= [System.Net.Sockets.AddressFamily]::InterNetwork
$Stype = [System.Net.Sockets.SocketType]::Dgram
$Ptype = [System.Net.Sockets.ProtocolType]::UDP
$Sock= New-Object System.Net.Sockets.Socket $saddrf, $stype, $ptype
Write-Debug \&quot;Définition des paramètres SendTimeOut et ReceiveTimeOut ...\&quot;
$Sock.SendTimeOut = 1000
$Sock.ReceiveTimeout = 1000

Write-Debug \&quot;Création du buffer qui servira à 'lire' la réponse\&quot;
$buffer = new-object byte[] 1KB
Write-Debug \&quot;Définition du datagram pour une requête NetBios\&quot;
[Byte[]] $namerequest = 0x80, 0x94, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x20, 0x43, 0x4b, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0x41,
0x41, 0x41, 0x41, 0x41, 0x41, 0x00, 0x00, 0x21,
0x00, 0x01

Write-Debug \&quot;Transmission du datagram vers '$myIP'\&quot;
$a = $Sock.bind($SenderIPEndPoint)
$a = $sock.sendto($namerequest, $Target)

Try{
$receiveByteCount = $Sock.ReceiveFrom($buffer, [Ref]$SenderEndPoint)
}
Catch [System.ArgumentNullException]{
Write-Debug \&quot;La lecture du Buffer n'a pas fonctionné : $($_.Exception.Message)\&quot;
Write-Error \&quot;'$myIP' : Au moins un des arguments ('buffer' ou 'remoteEP') vaut '`$null'.\&quot;
return $null
}
Catch [System.Net.Sockets.SocketException]{
Write-Debug \&quot;La lecture du Buffer n'a pas fonctionné : $($_.Exception.Message)\&quot;
Write-Error \&quot;'$myIP' : Une erreur s'est produite lors de la tentative d'accès au socket.\&quot;
return $null
}
Catch [System.ObjectDisposedException]{
Write-Debug \&quot;La lecture du Buffer n'a pas fonctionné : $($_.Exception.Message)\&quot;
Write-Error \&quot;'$myIP' : Le socket a été fermé.\&quot;
return $null
}
Catch [System.Security.SecurityException]{
Write-Debug \&quot;La lecture du Buffer n'a pas fonctionné : $_.Exception.Message\&quot;
Write-Error \&quot;'$myIP' : Un appelant de la pile des appels ne dispose pas des autorisations requises.\&quot;
return $null
}
Catch {
Write-Debug \&quot;La lecture du Buffer n'a pas fonctionné : $_.Exception.Message\&quot;
Write-Error \&quot;'$myIP' : Une erreur s'est produite ($_.Exception.Message)\&quot;
return $null
}

if($receiveByteCount -ge 90){
$Encoding = New-Object System.Text.ASCIIEncoding
Write-Debug \&quot;Lecture du nom NetBios de la machine, dans le Buffer ...\&quot;
$TargetInfos.NetBiosName = ($Encoding.GetString($buffer, 57, 15)).Trim()
Write-Debug \&quot;Lecture du nom NetBios du domaine de la machine, dans le Buffer ...\&quot;
$TargetInfos.NetBiosDomain = ($Encoding.GetString($buffer, 75, 15)).Trim()
}

Write-Debug \&quot;Retourne l'objet comprenant les propriétés IPAddress, NetBiosName et NetBiosDomain ...\&quot;
return $TargetInfos
}

}

process{
foreach($ipa in $IpAddress){
GetNBTInfos $ipa
}
}

}

[/code:1]<br><br>Message édité par: Matthew BETTON, à: 16/05/11 23:26

Connexion ou Créer un compte pour participer à la conversation.

Plus d'informations
il y a 13 ans 4 mois #9664 par Matthew BETTON
Réponse de Matthew BETTON sur le sujet Re:Get-NetBiosInfos
Laurent Dardenne écrit:

Je pense que le bloc process peut être réduit à ceci :
[code:1]
foreach($ipa in $IpAddress){
Write-Debug \&quot;Traitement de l'adresse $ipa\&quot;
$TabInfos += GetNBTInfos $ipa
}
[/code:1]
Si tu précises un seul paramètre obligatoire on est assuré qu'il soit lié.
De plus son type étant [string[], ps le transformera tjr ton paramètre un tableau :
[code:1]
trace-command parameterbinding {Get-NetBiosInfos 1} -FilePath c:\temp\log1.txt
trace-command parameterbinding {Get-NetBiosInfos @(1)} -FilePath c:\temp\log2.txt
#comparer avec winMerge, par exemple
[/code:1]
Sans compter la covariance .net sur les tableaux ( string -&gt; Ipaddress)


Salut,

Comme tu pourras le voir dans le dernier post, j'ai tenu compte de ta judicieuse remarque...
Cependant, juste pour information, je viens de retrouver l'article du Technet qui m'avait inspiré il y a quelques temps : technet.microsoft.com/en-us/magazine/gg537351.aspx

Le bloque \&quot;Process\&quot; proposé par Don Jones est le suivant

[code:1]PROCESS {
if ($PSBoundParameters.ContainsKey('computername')) {
foreach($computer in $computername) {
OSInfoWorker –computername $computer –logfile $logfile
}
} else {
OSInfoWorker –computername $computername –logfile $logfile
}
}[/code:1]

Alors que la variable $Computername est bien déclarée en tant que [string[]].

Comme quoi, il faut de temps à autre se méfier de ce que l'on peut lire sur le Net, ici et là :P

:laugh:

@+

Matthew<br><br>Message édité par: Matthew BETTON, à: 17/05/11 10:47

Connexion ou Créer un compte pour participer à la conversation.

Plus d'informations
il y a 13 ans 4 mois #9665 par Laurent Dardenne
Réponse de Laurent Dardenne sur le sujet Re:Get-NetBiosInfos
Matthew BETTON écrit:

Alors que la variable $Computername est bien déclarée en tant que [string[]].

En y regardant bien, si ici on utilise $computername il ne peut être que lié.
Quant à l'usage d'un tableau de string, il est nécessaire pour ce cas :
[code:1]
Get-OSInfo –computername Server-R2,ServerDC4
[/code:1]
Là la fonction reçoit bien un tableau de string

Matthew BETTON écrit:

Comme quoi, il faut de temps à autre se méfier de ce que l'on peut lire sur le Net, ici et là

Disons qu'il faut garder l'esprit critique, personne n'est à l'abri d'une erreur.

Tutoriels PowerShell

Connexion ou Créer un compte pour participer à la conversation.

Temps de génération de la page : 0.121 secondes
Propulsé par Kunena