Un modo migliore per trovare un utente connesso in remoto utilizzando PowerShell
Ci sono una montagna di modi diversi per ottenere gli utenti connessi, ma ho un preferito! Usando qwinsta
, l’unico problema è che restituisce una stringa e a PowerShell piacciono gli oggetti: (Ma non temere mai! Possiamo rimediare.
qwinsta
Prereqs
Mi sono imbattuto in un errore 5 accesso negato problema quando ho iniziato a utilizzare qwinsta
, c’è una correzione del registro di sistema per questo (grazie StackOverflow:
"AllowRemoteRPC"=dword:00000001
Poiché questo è un blog PowerShell, ecco la sintassi PowerShell per aggiungerlo a un computer remoto, dal momento che probabilmente lavoreremo su computer remoti.
$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()
Sintassi
La sintassi di qwinsta è abbastanza semplice:
qwinsta /server:ServerName
E l’uscita sembra piuttosto unmenacing:
SESSIONNAME USERNAME ID STATE TYPE DEVICE services 0 Disc console Anthony 1 Active rdp-tcp 65536 Listen
L’unico problema è che se si stesse guardando un server RDS, non vi è alcun modo semplice per filtrare le stringhe in PowerShell. Quindi non potevi fare qualcosa come:
qwinsta /server:RDSServer | Where-Object USERNAME -like "Anth*"
Ma possiamo rimediare! Consente di avvolgere qwinsta in modo da poter oggettivare quell’output!
Wrapping qwinsta
Stiamo andando a creare una funzione PowerShell qui, reader attenzione.
Strizzare la stringa per i suoi valori
Oltre a tutte le cose dei parametri, a cui arriveremo, la prima cosa fondamentale che faremo è dirigere l’output su una variabile:
$result = qwinsta /server:$ComputerName
Ora $result
sarà simile all’output di prima, ma sarà formattato come un array di stringhe (string
). Possiamo, naturalmente, verificare che con Get-Member
:
($result | Get-Member).TypeName
Quindi la prossima cosa è recuperare solo le informazioni che vogliamo. Ho deciso un ciclo for, saltando la prima riga poiché non abbiamo bisogno delle intestazioni:
ForEach($line in $result)
Per ogni riga, con che cosa è delimitato ogni valore? Spazi! Così possiamo usare il .Split () metodo su stringhe e dividere con ogni spazio. L’unica avvertenza è che dovremo anche testare il valore su ogni divisione poiché è possibile dividere più spazi e ottenere valori nulli.
$tmp = $line.split(" ") | ?{$_.length -gt 0}
Una cosa da notare è che l’output da qwinsta
è a larghezza fissa, il che significa che la larghezza non cambia indipendentemente dalla durata di uno qualsiasi dei valori. Possiamo approfittarne e utilizzare determinati indici a nostro vantaggio.
La prima cosa da fare è determinare quali righe contengono effettivamente un utente. Possiamo contare i caratteri e scoprire che il valore del nome utente inizia dal 19 ° carattere (il primo carattere è in realtà lo spazio, guarda attentamente). Quindi se il 19 ° carattere non è vuoto, abbiamo un utente.
If(($line -ne " "))
La prossima cosa che ho notato nel test con qwinsta
è che la proprietà SessionName ha valore solo se la sessione è nello stato “Attivo”. Quindi dobbiamo anche controllare se lo stato è “Attivo”, cosa che possiamo fare controllando l’indice di quello stato per un “A’
If($line -eq "A")
Se è ‘Attivo’, la variabile $tmp
avrà diverse quantità di valori.
Attivo:
#raw rdp-tcp#0 anthony 2 Active #$tmp variable (split)rdp-tcp#0anthony2Active
Non attivo:
#raw anthony 2 Disc #$tmp variable (split)anthony2Disc
Conversione dei valori in oggetti utilizzabili
Una volta che abbiamo capito tutti i valori, possiamo creare un nuovo PSObject, il mio preferito! Se hai PowerShellv5 puoi usare {}
type accelerator (x), ma userò il cmdlet New-Object
qui per la massima compatibilità.
New-Object PSObject -Property @{ "ComputerName" = $ComputerName "SessionName" = $tmp "UserName" = $tmp "ID" = $tmp "State" = $tmp "Type" = $tmp}
In questo caso, poiché sappiamo che la sessione è attiva, la prima stringa in $tmp
è il nome della sessione e quindi, in ordine, hai il resto delle proprietà. È possibile che ‘Type’ o ‘SessionName’ possano tornare vuoti.
Se lo stato non è “Attivo”, lo passeremo a:
New-Object PSObject -Property @{ "ComputerName" = $ComputerName "SessionName" = $null "UserName" = $tmp "ID" = $tmp "State" = $tmp "Type" = $null}
Il prodotto finale
Poiché questo non è un post sulla creazione di funzioni, salterò l’altra sintassi e mostrerò la funzione finale che ho scritto. Ciò include alcune best practice come il controllo della connettività e dei diritti di amministratore prima di entrare nelle cose interessanti.
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
Puoi trovare questo nel mio repo di utilità GitHub. Questo è il mio primo script pubblicato! Urrà! Così lascia andare oltre come usarlo:
Semplice utilizzo
Per recuperare gli utenti registrati in un computer locale o remoto, abbiamo semplicemente utilizzare:
Get-ActiveSessions ComputerName
E questo dovrebbe restituire qualcosa di simile a:
ID : 2SessionName :Type :UserName : anthonyComputerName : dc01State : Disc
E se si passa a una variabile e guardare il nome del tipo, dovremmo avere un PSCustomObject
, significato siamo in grado di utilizzare tutte le altre utili cmdlet per il filtro e quant’altro!
$sessions = Get-ActiveSessions ComputerName($sessions | Get-Member).TypeNameSystem.Management.Automation.PSCustomObject
Utilizzo avanzato
Abbiamo due cose in corso per noi qui, possiamo accettare l’input della pipeline E ouputs un oggetto. Quindi possiamo fare cose prima e dopo Get-ActiveSessions
.
Cosa succede se si desidera trovare tutti i server nel proprio ambiente che hanno un particolare utente connesso a loro?
Get-ADComputer -Filter {OperatingSystem -like '*Server*'} | Get-ActiveSessions | Where-Object UserName -eq 'Anthony'
O che ne dici di estrarre un elenco di computer da un file di testo e inviare l’elenco di utenti unici connessi a tutti loro?
get-content C:\temp\computers.txt | Get-ActiveSessions | Select-Object -Unique UserName -ExpandProperty UserName | Out-File C:\temp\users.txt
Se ti vengono in mente degli usi utili di Get-ActiveSessions
, commenta!
Conclusione
Speriamo che tu abbia un apprezzamento per il motivo per cui vorrei scrivere una funzione per fare questa roba per me. Onestamente non riuscivo a contare il numero di volte che ho usato questo per rendere il mio lavoro più facile.
Ad ogni modo, ho un paio di post degni di nota in arrivo, in particolare il partner di Get-ActiveSessions
: Close-ActiveSessions
.
Spoiler:
Get-ActiveSessions Computer | Close-ActiveSessions
E anche: ‘Creazione di attività pianificate per gli script PowerShell’, che sarà un viaggio dal momento che non ho ancora fatto nessuna immagine!
Leave a Reply