Password policies are the best 😀 A volte portano a logout di account quando qualcuno dimentica di uscire da una sessione da qualche parte nella rete. Potrebbe essere la sessione TS che usano una volta al trimestre per i rapporti o forse conoscete la sensazione quando fate una RDP su un server solo per scoprire che è bloccato da altri 2 amministratori che hanno dimenticato di fare il logout quando sono usciti. (Spento perché questo non succede mai… usiamo tutti PowerShell…) Comunque, questo mi ha portato a cercare una sessione utente da qualche parte nella rete. La cosa peggiore è quando la mia password scade. Odio quando il mio account finisce per essere bloccato. Perciò mi sono imposta la regola di controllare tutti i server prima di cambiare la password. Ci sono più modi per farlo, ma naturalmente tendo a seguire la strada di PowerShell.
Ricerca
Il metodo originale che ho usato è dalla galleria TechNet
In breve: Get-WmiObject -Class Win32_process
Questo sostanzialmente trova tutti gli utenti unici che eseguono processi sulla macchina. Questo è bello perché trova tutto, anche le cose in esecuzione come servizio, ma non sono convinto che sia il modo più efficiente.
Controllando con google trovo un sacco di modi creativi per controllare chi è connesso alla vostra macchina.
peetersonline.nl/2008/11/oneliner-get-logged-on-users-with-powershell/ mi ha dato l’idea di controllare Win32_LoggedOnUser che sembra ovvio.
Questo sembra ottimo e sembra funzionare anche con Get-CimInstance sebbene l’output sia un po’ diverso.
learn-powershell.net/…/Quick-hit-find-currently-logged-on-users/ ha adottato un approccio un po’ più vecchia scuola che mi piace perché è un po’ grezzo e mi costringe a giocare con il mio parsing basato su template.
Non sono davvero sicuro di quale metodo sia più veloce, quindi perché non provare ad implementare tutti e 3 in un modulo e testarlo.
Sketching
È sempre una buona idea iniziare facendo uno schizzo di ciò che si sta cercando di realizzare.
Pseudo code:Get-ActiveUser -ComputerName -Method Wanted output:Username ComputerName-------- ------------TestUser1 Svr3TestUser3 Svr3DonaldDuck Client2
Ora ho tutte le informazioni di cui ho bisogno per impostare il repository GitHub.
github.com/mrhvid/Get-ActiveUser
Code
Prima di tutto i parametri che mi interessano sono ComputerName e Method.
Param ( # Computer name, IP, Hostname ] $ComputerName, # Choose method, WMI, CIM or Query $Method )
Ho già in mente 3 possibili Methods quindi imposto ValidateSet con le 3 possibilità. Quindi non devo preoccuparmi di quell’input in seguito.
Process { switch ($Method) { 'WMI' { } 'CIM' { } 'Query' { } } }
Nella parte Process della mia funzione uso semplicemente un interruttore per i 3 metodi diversi che ho permesso nel Parametro.
Ora è un semplice riempimento degli spazi vuoti.
WMI
La mia vecchia soluzione è semplice e funziona bene.
$WMI = Get-WmiObject -Class Win32_Process -ComputerName $ComputerName -ErrorAction Stop$ProcessUsers = $WMI.getowner().user | Select-Object -Unique
Ma ora che ho trovato Win32_LoggedOnUser sembra sbagliato farlo così. Guardiamo invece la nuova idea.
Questi sono tutti i dati giusti ma sembrano essere in un formato stringa quindi dovrò fare una piccola manipolazione. Questo può essere fatto in un milione di modi.
function Get-MyLoggedOnUsers { param($Computer) Get-WmiObject Win32_LoggedOnUser -ComputerName $Computer | Select Antecedent -Unique | %{"{0}{1}" -f $_.Antecedent.ToString().Split('"'), $_.Antecedent.ToString().Split('"')} }
Il suddetto one-liner di Peter non mi sembrava molto leggibile, il che va bene per un one-liner, ma vorrei che fosse un po’ più leggibile se possibile.
$WMI = (Get-WmiObject Win32_LoggedOnUser).Antecedent$ActiveUsers = @()foreach($User in $WMI) { $StartOfUsername = $User.LastIndexOf('=') + 2 $EndOfUsername = $User.Length - $User.LastIndexOf('=') -3 $ActiveUsers += $User.Substring($StartOfUsername,$EndOfUsername)}
Questo sembra giusto 🙂 Salverò l’output nella variabile $ActiveUsers e farò lo stesso per CIM e Query.
CIM
Prova con CIM.
Questo sembra molto più strutturato.
CIM finisce per essere una semplice riga 😀
$ActiveUsers = (Get-CimInstance Win32_LoggedOnUser -ComputerName $ComputerName).antecedent.name | Select-Object -Unique
Query
Usando il buon vecchio Query.exe ho trovato molto utile il parsing basato su template discusso in precedenza.
$Template = @' USERNAME SESSIONNAME ID STATE IDLE TIME LOGON TIME>{USER*:jonas} console 1 Active 1+00:27 24-08-2015 22:22 {USER*:test} 2 Disc 1+00:27 25-08-2015 08:26'@$Query = query.exe user$ActiveUsers = $Query | ConvertFrom-String -TemplateContent $Template | Select-Object -ExpandProperty User
Output
Ora ho solo bisogno di formattare ed emettere gli utenti in un modo carino. Voglio oggetti puliti con ComputerName e UserName.
# Create nice output format$UsersComputersToOutput = @()foreach($User in $ActiveUsers) { $UsersComputersToOutput += New-Object psobject -Property @{ ComputerName=$ComputerName; UserName=$User } }}# output data$UsersComputersToOutput
Testing
Ora ho un problema. Non posso testare questo perché non ho un mucchio di server di prova a mia disposizione. Tutti i miei test sono stati fatti contro il mio box Windows 10. Sembra che la query sia molto più veloce in esecuzione locale, ma WMI/CIM potrebbe dare una visione più completa di quali servizi sono in esecuzione.
Ho un mucchio di account di servizi standard in esecuzione che potrebbe essere bello rimuovere dall’output. Inoltre, affinché questo sia utile, dovremo eseguirlo su molte macchine.
Combinando Get-ActiveUser con Start-Multithread del post della scorsa settimana sembra funzionare come previsto.
Start-Multithread -Script { param($C) Get-ActiveUser -ComputerName $C -Method Query } -ComputerName ::1,Localhost | Out-GridView
Il piping di quanto sopra in Out-GridView è probabilmente il mio modo preferito di realizzare qualcosa di veramente utile.
Ora abbiamo tutti i dati in un bel modo ricercabile ed è veramente facile controllare se il vostro utente è loggato su qualche macchina a caso. È anche un modo semplice per controllare se ci sono utenti in fuga sulla tua rete.
Pubblicazione e feedback
Il codice è pubblicato su PowerShellGallery.
Per favore aiutatemi testandolo per me. Mi piacerebbe sapere se questo funziona nel mondo reale 🙂
# To install Get-ActiveUserInstall-Module Get-ActiveUser#To install Start-MultithreadInstall-Module Start-Multithread
Questo dovrebbe funzionare quando hai WMF 5 + installato e su Windows 10 fuori dalla scatola.