lepszy sposób na zdalne znalezienie zalogowanego użytkownika za pomocą PowerShell
istnieje wiele różnych sposobów na zalogowanie użytkowników,ale mam ulubionego! Używając qwinsta
, jedynym problemem jest to, że zwraca ciąg znaków, a PowerShell lubi obiekty: (ale nigdy się nie bój! Możemy to naprawić.
qwinsta
wymagania wstępne
napotkałem błąd 5 problem z odmową dostępu, gdy po raz pierwszy zacząłem używać qwinsta
, istnieje poprawka rejestru (dziękuję StackOverflow:
"AllowRemoteRPC"=dword:00000001
ponieważ jest to blog PowerShell, oto składnia PowerShell, aby dodać go do zdalnego komputera, ponieważ prawdopodobnie będziemy pracować na komputerach zdalnych.
$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()
składnia
składnia qwinsta jest dość prosta:
qwinsta /server:ServerName
A wyjście wygląda raczej niesmacznie:
SESSIONNAME USERNAME ID STATE TYPE DEVICE services 0 Disc console Anthony 1 Active rdp-tcp 65536 Listen
jedynym problemem jest to, że jeśli patrzysz na serwer RDS, nie ma łatwego sposobu filtrowania ciągów w PowerShell. Więc nie mogłeś zrobić czegoś takiego jak:
qwinsta /server:RDSServer | Where-Object USERNAME -like "Anth*"
ale możemy to naprawić! Zawińmy qwinsta, abyśmy mogli zobiektywizować ten wynik!
Zawijanie qwinsta
stworzymy tutaj funkcję PowerShell, uważaj czytelniku.
wykręcenie łańcucha dla jego wartości
oprócz wszystkich parametrów, do których dojdziemy, pierwszą podstawową rzeczą, którą zrobimy, jest skierowanie wyjścia do zmiennej:
$result = qwinsta /server:$ComputerName
teraz $result
będzie wyglądać tak samo jak wyjście z poprzedniego, ale będzie sformatowane jako tablica łańcuchowa (string
). Możemy to oczywiście zweryfikować za pomocą Get-Member
:
($result | Get-Member).TypeName
następną rzeczą jest pobranie tylko tych informacji, których chcemy. Zdecydowałem się na pętlę for, pomijając pierwszą linię, ponieważ nie potrzebujemy nagłówków:
ForEach($line in $result)
czym dla każdej linii jest rozdzielana każda wartość? Miejsca! Więc możemy użyć .Metoda Split () na ciągach i split z każdą spacją. Jedynym zastrzeżeniem jest to, że będziemy musieli również sprawdzić wartość dla każdego podziału, ponieważ możesz podzielić wiele spacji i uzyskać wartości null.
$tmp = $line.split(" ") | ?{$_.length -gt 0}
należy zauważyć, że wyjście z qwinsta
ma stałą szerokość, co oznacza, że szerokość nie zmienia się niezależnie od długości którejkolwiek z wartości. Możemy to wykorzystać i wykorzystać pewne indeksy na naszą korzyść.
pierwszą rzeczą do zrobienia jest określenie, które linie faktycznie zawierają użytkownika. Możemy policzyć znaki i stwierdzić, że wartość USERNAME zaczyna się od 19. znaku (pierwszy znak jest w rzeczywistości spacją, przyjrzyj się uważnie). Więc jeśli 19. znak nie jest pusty, mamy użytkownika.
If(($line -ne " "))
następną rzeczą, którą zauważyłem podczas testowania z qwinsta
, jest to, że właściwość SessionName ma wartość tylko wtedy, gdy sesja jest w stanie ‘Active’. Tak więc musimy również sprawdzić, czy stan jest’ aktywny’, co możemy zrobić, sprawdzając indeks tego stanu dla ‘a’
If($line -eq "A")
jeśli jest’ aktywny’, zmienna $tmp
będzie miała różne wartości.
Aktywne:
#raw rdp-tcp#0 anthony 2 Active #$tmp variable (split)rdp-tcp#0anthony2Active
Nie Aktywny:
#raw anthony 2 Disc #$tmp variable (split)anthony2Disc
konwertowanie wartości do obiektów użytkowych
gdy już wszystkie wartości zostaną ustalone, możemy utworzyć nowy PSObject, mój ulubiony! Jeśli masz PowerShellv5, możesz użyć akceleratora typu {}
(x), ale użyję tutaj polecenia New-Object
, aby uzyskać ostateczną kompatybilność.
New-Object PSObject -Property @{ "ComputerName" = $ComputerName "SessionName" = $tmp "UserName" = $tmp "ID" = $tmp "State" = $tmp "Type" = $tmp}
w tym przypadku, ponieważ wiemy, że sesja jest aktywna, pierwszy łańcuch w $tmp
jest nazwą sesji, a następnie, w kolejności, masz resztę właściwości. Możliwe, że’ Type ‘lub’ SessionName ‘ mogą wrócić puste.
jesli stan nie jest ‘aktywny’ przestawimy na:
New-Object PSObject -Property @{ "ComputerName" = $ComputerName "SessionName" = $null "UserName" = $tmp "ID" = $tmp "State" = $tmp "Type" = $null}
produkt końcowy
ponieważ nie jest to post na temat tworzenia funkcji, zamierzam pominąć przechodzenie przez inną składnię i pokazać ostatnią funkcję, którą napisałem. Obejmuje to kilka najlepszych praktyk, takich jak sprawdzanie łączności i praw administratora przed przejściem do ekscytujących rzeczy.
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
możesz to znaleźć w moim repo GitHub Utilities. To mój pierwszy opublikowany scenariusz! Hura! Więc przejdźmy nad tym, jak go używać:
proste użycie
aby pobrać użytkowników zalogowanych na zdalnym lub lokalnym komputerze, po prostu użyjemy:
Get-ActiveSessions ComputerName
i to powinno zwrócić coś podobnego do:
ID : 2SessionName :Type :UserName : anthonyComputerName : dc01State : Disc
a jeśli przekażesz go do zmiennej i spojrzysz na nazwę typu, powinniśmy mieć PSCustomObject
, co oznacza, że możemy użyć wszystkich innych użytecznych cmdletów do filtrowania i tak dalej!
$sessions = Get-ActiveSessions ComputerName($sessions | Get-Member).TypeNameSystem.Management.Automation.PSCustomObject
zaawansowane użycie
mamy tutaj dwie rzeczy do zrobienia, możemy zaakceptować wejście potoku i wyjmuje obiekt. Więc możemy robić rzeczy przed i po Get-ActiveSessions
.
co zrobić, jeśli chcesz znaleźć wszystkie serwery w Twoim środowisku, które mają zalogowanego konkretnego użytkownika?
Get-ADComputer -Filter {OperatingSystem -like '*Server*'} | Get-ActiveSessions | Where-Object UserName -eq 'Anthony'
albo co powiesz na wyciągnięcie listy komputerów z pliku tekstowego i wypisanie listy unikalnych użytkowników zalogowanych do nich wszystkich?
get-content C:\temp\computers.txt | Get-ActiveSessions | Select-Object -Unique UserName -ExpandProperty UserName | Out-File C:\temp\users.txt
jeśli wymyślisz jakieś użyteczne zastosowania Get-ActiveSessions
, skomentuj!
podsumowanie
mam nadzieję, że masz docenienie, dlaczego miałbym napisać funkcję, która zrobi to za mnie. Szczerze mówiąc, nie mogłem policzyć, ile razy używałem tego, aby ułatwić sobie pracę.
w każdym razie mam kilka godnych uwagi postów, szczególnie partnera do Get-ActiveSessions
: Close-ActiveSessions
.
Spojler:
Get-ActiveSessions Computer | Close-ActiveSessions
a także: “tworzenie zaplanowanych zadań dla skryptów PowerShell”, co będzie podróżą, ponieważ jeszcze nie zrobiłem żadnych zdjęć!
Leave a Reply