Lösenordsprinciper är det bästa 😀 Ibland leder de dock till att konton loggas ut när någon glömmer att logga ut från en session någonstans i nätverket. Det kan vara TS-sessionen som de använder en gång i kvartalet för rapportering eller kanske känner du till känslan när du RDP till en server bara för att upptäcka att den är låst av två andra administratörer som glömde att logga ut när de gick. (Av för att detta aldrig händer… vi använder alla PowerShell…) Hur som helst fick detta mig att leta efter en användarsession någonstans i nätverket. Det värsta är när mitt eget lösenord löper ut. Jag hatar när mitt konto slutar med att bli låst. Därför har jag gjort det till en regel att bara kontrollera alla servrar innan jag byter lösenord. Det finns flera sätt att göra detta, men jag tenderar naturligtvis att gå PowerShell-vägen.
Research
Den ursprungliga metoden jag använde är från TechNet gallery
I korthet: Get-WmiObject -Class Win32_process
Detta hittar i princip alla unika användare som kör processer på maskinen. Detta är coolt eftersom det hittar allt, även saker som körs som en tjänst, men jag är inte övertygad om att det är det mest effektiva sättet.
Kontrollera upp med Google hittar jag en hel del kreativa sätt att kontrollera vem som är inloggad på din box.
peetersonline.nl/2008/11/oneliner-get-logged-on-users-with-powershell/ gav mig idén att kontrollera Win32_LoggedOnUser vilket verkar uppenbart.
Detta ser bra ut och verkar fungera med Get-CimInstance också även om resultatet är lite annorlunda.
learn-powershell.net/…/Quick-hit-find-currently-logged-on-users/ tog ett lite mer gammaldags tillvägagångssätt som jag gillar eftersom det är lite grovt och tvingar mig att leka med min mallbaserade analysering.
Jag är inte riktigt säker på vilken metod som är snabbare så varför inte prova att implementera alla tre i en modul och testa det.
Skiss
Det är alltid en bra idé att börja med att göra en skiss av vad du försöker åstadkomma.
Pseudo code:Get-ActiveUser -ComputerName -Method Wanted output:Username ComputerName-------- ------------TestUser1 Svr3TestUser3 Svr3DonaldDuck Client2
Nu har jag all information jag behöver för att konfigurera GitHub-repositoriet.
github.com/mrhvid/Get-ActiveUser
Code
Först och främst är parametrarna som jag är intresserad av ComputerName och Method.
Param ( # Computer name, IP, Hostname ] $ComputerName, # Choose method, WMI, CIM or Query $Method )
Jag har redan 3 möjliga Methods i åtanke så jag ställer in ValidateSet med de 3 möjligheterna. Då behöver jag inte oroa mig för den inmatningen senare.
Process { switch ($Method) { 'WMI' { } 'CIM' { } 'Query' { } } }
I Process-delen av min funktion använder jag helt enkelt en switch för de 3 olika metoderna som jag tillåtit i Parameter.
Nu är det grundläggande att fylla i blanketterna.
WMI
Min gamla lösning är enkel och fungerar bra.
$WMI = Get-WmiObject -Class Win32_Process -ComputerName $ComputerName -ErrorAction Stop$ProcessUsers = $WMI.getowner().user | Select-Object -Unique
Men nu när jag hittade Win32_LoggedOnUser känns det fel att göra det på detta sätt. Låt oss titta på den nya idén istället.
Det här är all rätt data, men det verkar vara i ett strängformat så jag måste manipulera lite. Detta kan göras på en miljon olika sätt.
function Get-MyLoggedOnUsers { param($Computer) Get-WmiObject Win32_LoggedOnUser -ComputerName $Computer | Select Antecedent -Unique | %{"{0}{1}" -f $_.Antecedent.ToString().Split('"'), $_.Antecedent.ToString().Split('"')} }
Peters tidigare nämnda one-liner verkade inte särskilt läsarvänlig för mig, vilket är okej för en one-liner, men jag skulle vilja att den var lite mer läsbar om möjligt.
$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 här verkar rätt 🙂 Jag ska spara resultatet i variabeln $ActiveUsers och göra samma sak för CIM och Query.
CIM
Låtsas försöka med CIM.
Det här ser mycket mer strukturerat ut.
CIM slutar med att bli en lättbegriplig one-liner 😀
$ActiveUsers = (Get-CimInstance Win32_LoggedOnUser -ComputerName $ComputerName).antecedent.name | Select-Object -Unique
Query
Användning av den gamla goda Query.exe fann jag den mallbaserade parsing som diskuterades tidigare mycket användbar.
$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 behöver jag bara formatera och ge ut användarna på ett trevligt sätt. Jag vill ha rena objekt med ComputerName och 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 jag ett problem. Jag kan inte testa detta eftersom jag inte har ett gäng testservrar till mitt förfogande. Alla mina tester har gjorts mot min egen Windows 10-box. Det är verkar som om query är mycket snabbare att köra lokalt men WMI/CIM kan ge en mer komplett bild av vilka tjänster som körs.
Jag har ett gäng standardtjänstkonton som körs och som kan vara bra att ta bort från utdata. För att detta ska vara användbart vill vi också köra det mot många maskiner.
Kombinera Get-ActiveUser med Start-Multithread från förra veckans inlägg verkar fungera som det var tänkt.
Start-Multithread -Script { param($C) Get-ActiveUser -ComputerName $C -Method Query } -ComputerName ::1,Localhost | Out-GridView
Att pipa ovanstående till Out-GridView är förmodligen mitt personliga favoritsätt att åstadkomma något riktigt användbart.
Nu har vi all data på ett trevligt sökbart sätt och det är riktigt enkelt att kontrollera om användaren är inloggad på någon slumpmässig maskin. Det är också ett enkelt sätt att kontrollera om det finns röda användare i ditt nätverk.
Publicering och feedback
Koden är publicerad på PowerShellGallery.
Hjälp mig gärna genom att testa den åt mig. Jag skulle gärna vilja veta om detta fungerar i verkligheten 🙂
# To install Get-ActiveUserInstall-Module Get-ActiveUser#To install Start-MultithreadInstall-Module Start-Multithread
Detta bör fungera när du har WMF 5 + installerat och på Windows 10 out of the box.