Password-politikker er de bedste 😀 Nogle gange fører de dog til logout af konti, når nogen glemmer at logge ud af en session et sted på netværket. Det kan være TS-sessionen, som de bruger en gang i kvartalet til rapportering, eller måske kender du følelsen, når du RDP til en server kun for at finde ud af, at den er låst af 2 andre admins, der har glemt at logge af, da de gik. (Off cause this never happens… we all use PowerShell…) Anyway, this had me searching for a user session somewhere on the network. Det værste er, når min egen adgangskode udløber. Jeg hader, når min konto ender med at blive låst. Derfor har jeg gjort det til en regel at bare tjekke alle servere, før jeg ændrer password. Der er flere måder at gøre dette på, men jeg har selvfølgelig en tendens til at gå PowerShell-ruten.
Research
Den oprindeligt metode jeg brugte er fra TechNet gallery
Kort sagt: Get-WmiObject -Class Win32_process
Dette finder dybest set alle unikke brugere, der kører processer på maskinen. Dette er cool, fordi det finder alt, selv ting, der kører som en tjeneste, men jeg er ikke overbevist om, at det er den mest effektive måde.
Tjekke op med google finder jeg en masse kreative måder at kontrollere, hvem der er logget på din boks.
peetersonline.nl/2008/11/oneliner-get-logged-on-users-with-powershell/ gav mig ideen til at kontrollere Win32_LoggedOnUser, som synes indlysende.
Det ser godt ud og synes også at virke med Get-CimInstance, selv om output er lidt anderledes.
learn-powershell.net/…/Quick-hit-find-currently-logged-on-users/ tog en lidt mere gammeldags tilgang, som jeg ligesom kan lide, fordi det er lidt groft og tvinger mig til at lege med min skabelonbaserede parsing.
Jeg er ikke helt sikker på hvilken metode der er hurtigere, så hvorfor ikke prøve at implementere alle 3 i et modul og teste det af.
Skitsering
Det er altid en god idé at starte med at lave en skitse af hvad du prøver at opnå.
Pseudo code:Get-ActiveUser -ComputerName -Method Wanted output:Username ComputerName-------- ------------TestUser1 Svr3TestUser3 Svr3DonaldDuck Client2
Nu har jeg alle de oplysninger, jeg skal bruge til at oprette GitHub-repositoriet.
github.com/mrhvid/Get-ActiveUser
Kode
Først af alt er de parametre, jeg er interesseret i, ComputerName og Method.
Param ( # Computer name, IP, Hostname ] $ComputerName, # Choose method, WMI, CIM or Query $Method )
Jeg har allerede 3 mulige Methods i tankerne, så jeg indstiller ValidateSet med de 3 muligheder. Så behøver jeg ikke at bekymre mig om det input senere.
Process { switch ($Method) { 'WMI' { } 'CIM' { } 'Query' { } } }
I Process-delen af min funktion bruger jeg simpelthen en switch for de 3 forskellige metoder, som jeg tillod i Parameter.
Nu er det grundlæggende udfyldning af felter.
WMI
Min gamle løsning er simpel og fungerer fint.
$WMI = Get-WmiObject -Class Win32_Process -ComputerName $ComputerName -ErrorAction Stop$ProcessUsers = $WMI.getowner().user | Select-Object -Unique
Men nu da jeg fandt Win32_LoggedOnUser virker det forkert at gøre det på denne måde. Lad os se på den nye idé i stedet.
Dette er alle de rigtige data, men det ser ud til at være i et string-format, så jeg bliver nødt til at lave lidt manipulation. Det kan gøres på en million måder.
function Get-MyLoggedOnUsers { param($Computer) Get-WmiObject Win32_LoggedOnUser -ComputerName $Computer | Select Antecedent -Unique | %{"{0}{1}" -f $_.Antecedent.ToString().Split('"'), $_.Antecedent.ToString().Split('"')} }
Peters førnævnte one-liner virkede ikke særlig læsevenlig på mig, hvilket er ok for en one-liner, men jeg vil gerne have den lidt mere læsevenlig, hvis det er muligt.
$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)}
Det ser rigtigt ud 🙂 Jeg gemmer outputtet i variablen $ActiveUsers og gør det samme for CIM og Query.
CIM
Lader os prøve med CIM.
Det ser langt mere struktureret ud.
CIM ender med at blive en letforståelig one-liner 😀
$ActiveUsers = (Get-CimInstance Win32_LoggedOnUser -ComputerName $ComputerName).antecedent.name | Select-Object -Unique
Query
Ved hjælp af den gode gamle Query.exe fandt jeg den tidligere omtalte skabelonbaserede parsing meget nyttig.
$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
Nu skal jeg bare formatere og outputte brugerne på en pæn måde. Jeg vil have rene objekter med ComputerName og UserName.
# Create nice output format$UsersComputersToOutput = @()foreach($User in $ActiveUsers) { $UsersComputersToOutput += New-Object psobject -Property @{ ComputerName=$ComputerName; UserName=$User } }}# output data$UsersComputersToOutput
Testing
Nu har jeg et problem. Jeg kan ikke teste dette, da jeg ikke har en masse testserveres til min rådighed. Alle mine tests er blevet udført mod min egen Windows 10-boks. Det er ser ud til at forespørgsel er meget hurtigere at køre lokalt, men WMI/CIM kan give et mere komplet overblik over hvilke tjenester der kører.
Jeg har en masse standard tjenestekonti kørende, som det kunne være rart at fjerne fra output. Også for at dette skal være nyttigt vil vi ønske at køre det mod en masse maskiner.
Kombination af Get-ActiveUser med Start-Multithread fra sidste uges indlæg ser ud til at virke efter hensigten.
Start-Multithread -Script { param($C) Get-ActiveUser -ComputerName $C -Method Query } -ComputerName ::1,Localhost | Out-GridView
Piping ovenstående til Out-GridView er proberbly min personlige favorit måde at opnå noget virkelig nyttigt.
Nu har vi alle data i en dejlig søgbar måde, og det er virkelig nemt at kontrollere, om din bruger er logget ind på en tilfældig maskine. Det også en nem måde at tjekke for rouge brugere på dit netværk.
Publicering og feedback
Koden er publiceret på PowerShellGallery.
Hjælp mig gerne ved at teste den for mig. Jeg vil meget gerne vide, om det virker i den virkelige verden 🙂
# To install Get-ActiveUserInstall-Module Get-ActiveUser#To install Start-MultithreadInstall-Module Start-Multithread
Dette skulle virke, når du har WMF 5 + installeret og på Windows 10 out of the box.