Une meilleure façon de trouver un utilisateur connecté à distance en utilisant PowerShell

Il existe une montagne de différentes façons d’obtenir des utilisateurs connectés, mais j’ai un favori! En utilisant qwinsta, le seul problème est qu’il renvoie une chaîne et que PowerShell aime les objets: (Mais n’ayez jamais peur! On peut régler ça.

qwinsta

Pré-requis

J’ai rencontré un problème d’accès refusé d’erreur 5 lorsque j’ai commencé à utiliser qwinsta, il y a un correctif de registre pour cela (merci StackOverflow:

"AllowRemoteRPC"=dword:00000001

Comme il s’agit d’un blog PowerShell, voici la syntaxe PowerShell pour l’ajouter à un ordinateur distant, car nous travaillerons probablement sur des ordinateurs distants.

$ComputerName = 'Computer' #replace with the computer name$LMtype = ::LocalMachine$LMkey = "SYSTEM\CurrentControlSet\Control\Terminal Server"$LMRegKey = ::OpenRemoteBaseKey($LMtype,$ComputerName)$regKey = $LMRegKey.OpenSubKey($LMkey,$true)If($regKey.GetValue("AllowRemoteRPC") -ne 1){ $regKey.SetValue("AllowRemoteRPC",1) Start-Sleep -Seconds 1}$regKey.Dispose()$LMRegKey.Dispose()

Syntaxe

La syntaxe de qwinsta est assez simple:

qwinsta /server:ServerName

Et la sortie semble plutôt peu visible:

 SESSIONNAME USERNAME ID STATE TYPE DEVICE services 0 Disc console Anthony 1 Active rdp-tcp 65536 Listen

Le seul problème est que si vous regardiez un serveur RDS, il n’y a pas de moyen facile de filtrer les chaînes dans PowerShell. Donc tu ne pouvais pas faire quelque chose comme:

qwinsta /server:RDSServer | Where-Object USERNAME -like "Anth*"

Mais on peut régler ça ! Permet d’envelopper qwinsta afin que nous puissions objectiver cette sortie!

Wrapping qwinsta

Nous allons créer une fonction PowerShell ici, attention au lecteur.

Essorer la chaîne pour ses valeurs

Outre tous les éléments de paramètres, auxquels nous allons accéder, la première chose que nous allons faire est de diriger la sortie vers une variable:

$result = qwinsta /server:$ComputerName

Maintenant, $result ressemblera à la sortie d’avant, mais il sera formaté comme un tableau de chaînes (string). Nous pouvons, bien sûr, le vérifier avec Get-Member:

($result | Get-Member).TypeName

La prochaine chose est donc de récupérer uniquement les informations que nous voulons. J’ai décidé d’une boucle for, en sautant la première ligne car nous n’avons pas besoin des en-têtes:

ForEach($line in $result)

Pour chaque ligne, avec quoi chaque valeur est-elle délimitée ? Espaces! Nous pouvons donc utiliser le.Méthode Split() sur les chaînes et split avec chaque espace. La seule mise en garde est que nous devrons également tester la valeur sur chaque division, car vous pouvez diviser plusieurs espaces et obtenir des valeurs nulles.

$tmp = $line.split(" ") | ?{$_.length -gt 0}

Une chose à noter est que la sortie de qwinsta est à largeur fixe, ce qui signifie que la largeur ne change pas quelle que soit la durée de l’une des valeurs. Nous pouvons en profiter et utiliser certains indices à notre avantage.

La première chose à faire est de déterminer quelles lignes contiennent réellement un utilisateur. Nous pouvons compter les caractères et constater que la valeur du NOM D’UTILISATEUR commence au 19ème caractère (le premier caractère est en fait l’espace, regardez attentivement). Donc, si le 19ème caractère n’est pas vide, nous avons un utilisateur.

If(($line -ne " "))

La prochaine chose que j’ai remarquée en testant avec qwinsta est que la propriété SessionName n’a de valeur que si la session est à l’état “Actif”. Nous devons donc également vérifier si l’État est ‘Actif’, ce que nous pouvons faire en vérifiant l’index de cet état pour un ‘A’

If($line -eq "A")

Si elle est ‘Active’, la variable $tmp aura des quantités de valeurs différentes.

Actif:

#raw rdp-tcp#0 anthony 2 Active #$tmp variable (split)rdp-tcp#0anthony2Active

Non Actif:

#raw anthony 2 Disc #$tmp variable (split)anthony2Disc

Convertir les valeurs en objets utilisables

Une fois que nous avons toutes les valeurs comprises, nous pouvons créer un nouveau PSObject, mon préféré! Si vous avez PowerShellv5, vous pouvez utiliser l’accélérateur de type {} (x), mais j’utiliserai l’applet de commande New-Object ici pour une compatibilité ultime.

New-Object PSObject -Property @{ "ComputerName" = $ComputerName "SessionName" = $tmp "UserName" = $tmp "ID" = $tmp "State" = $tmp "Type" = $tmp}

Dans ce cas, puisque nous savons que la session est active, la première chaîne de $tmp est le nom de la session, puis, dans l’ordre, vous avez le reste des propriétés. Il est possible que ‘Type’ ou ‘Nom de session’ reviennent vides.

Si l’état n’est pas “Actif”, nous le changerions en:

New-Object PSObject -Property @{ "ComputerName" = $ComputerName "SessionName" = $null "UserName" = $tmp "ID" = $tmp "State" = $tmp "Type" = $null}

Le produit final

Comme il ne s’agit pas d’un article sur la création de fonctions, je vais passer en revue l’autre syntaxe et vous montrer la fonction finale que j’ai écrite. Cela inclut certaines bonnes pratiques comme la vérification de la connectivité et des droits d’administrateur avant d’entrer dans les choses passionnantes.

Function Get-ActiveSessions{ Param( $Name , $Quiet ) Begin{ $return = @() } Process{ If(!(Test-Connection $Name -Quiet -Count 1)){ Write-Error -Message "Unable to contact $Name. Please verify its network connectivity and try again." -Category ObjectNotFound -TargetObject $Name Return } If(((::GetCurrent()).groups -match "S-1-5-32-544")){ #check if user is admin, otherwise no registry work can be done #the following registry key is necessary to avoid the error 5 access is denied error $LMtype = ::LocalMachine $LMkey = "SYSTEM\CurrentControlSet\Control\Terminal Server" $LMRegKey = ::OpenRemoteBaseKey($LMtype,$Name) $regKey = $LMRegKey.OpenSubKey($LMkey,$true) If($regKey.GetValue("AllowRemoteRPC") -ne 1){ $regKey.SetValue("AllowRemoteRPC",1) Start-Sleep -Seconds 1 } $regKey.Dispose() $LMRegKey.Dispose() } $result = qwinsta /server:$Name If($result){ ForEach($line in $result){ #avoiding the line 0, don't want the headers $tmp = $line.split(" ") | ?{$_.length -gt 0} If(($line -ne " ")){ #username starts at char 19 If($line -eq "A"){ #means the session is active ("A" for active) $return += New-Object PSObject -Property @{ "ComputerName" = $Name "SessionName" = $tmp "UserName" = $tmp "ID" = $tmp "State" = $tmp "Type" = $tmp } }Else{ $return += New-Object PSObject -Property @{ "ComputerName" = $Name "SessionName" = $null "UserName" = $tmp "ID" = $tmp "State" = $tmp "Type" = $null } } } } }Else{ Write-Error "Unknown error, cannot retrieve logged on users" } } End{ If($return){ If($Quiet){ Return $true } Else{ Return $return } }Else{ If(!($Quiet)){ Write-Host "No active sessions." } Return $false } }}

Get-ActiveSessions

Vous pouvez le trouver dans le dépôt GitHub de mes utilitaires. C’est mon premier script publié! Hourra! Voyons donc comment l’utiliser:

Utilisation simple

Pour récupérer les utilisateurs connectés à un ordinateur distant ou local, nous utiliserions simplement:

Get-ActiveSessions ComputerName

Et cela devrait renvoyer quelque chose de similaire à:

ID : 2SessionName :Type :UserName : anthonyComputerName : dc01State : Disc

Et si vous le passez à une variable et regardez le nom du type, nous devrions avoir un PSCustomObject, ce qui signifie que nous pouvons utiliser toutes les autres applets de commande utiles pour le filtrage et autres !

$sessions = Get-ActiveSessions ComputerName($sessions | Get-Member).TypeNameSystem.Management.Automation.PSCustomObject

Utilisation avancée

Nous avons deux choses à faire ici, nous pouvons accepter l’entrée du pipeline ET il produit un objet. Nous pouvons donc faire des choses avant et après Get-ActiveSessions.

Que se passe-t-il si vous souhaitez trouver tous les serveurs de votre environnement sur lesquels un utilisateur particulier s’est connecté ?

Get-ADComputer -Filter {OperatingSystem -like '*Server*'} | Get-ActiveSessions | Where-Object UserName -eq 'Anthony'

Ou que diriez-vous de tirer une liste d’ordinateurs à partir d’un fichier texte et de sortir la liste des utilisateurs uniques connectés à tous?

get-content C:\temp\computers.txt | Get-ActiveSessions | Select-Object -Unique UserName -ExpandProperty UserName | Out-File C:\temp\users.txt

Si vous trouvez des utilisations utiles de Get-ActiveSessions, commentez!

Conclusion

J’espère que vous comprenez pourquoi j’écrirais une fonction pour faire ce genre de choses pour moi. Honnêtement, je ne pouvais pas compter le nombre de fois où j’ai utilisé cela pour faciliter mon travail.

De toute façon, j’ai quelques messages notables à venir, en particulier le partenaire de Get-ActiveSessions: Close-ActiveSessions.

Spoiler:

Get-ActiveSessions Computer | Close-ActiveSessions

Et aussi: ‘Créer des tâches planifiées pour les scripts PowerShell’, ce qui sera un voyage puisque je n’ai pas encore fait d’images!

Leave a Reply