Zásady hesel jsou nejlepší 😀 Někdy však vedou k odhlášení účtu, když se někdo zapomene odhlásit z relace někde v síti. Může to být relace TS, kterou používají jednou za čtvrt roku pro hlášení, nebo možná znáte ten pocit, když se přes RDP dostanete na server, abyste zjistili, že je zamčený dvěma dalšími administrátory, kteří se při odchodu zapomněli odhlásit. (Mimo, protože tohle se nikdy nestává… všichni používáme PowerShell…) Každopádně mě to donutilo hledat uživatelskou relaci někde v síti. Nejhorší je, když mi vyprší vlastní heslo. Nesnáším, když můj účet skončí zablokovaný. Proto jsem si zavedl pravidlo, že před změnou hesla prostě zkontroluji všechny servery. Existuje více způsobů, jak to udělat, ale já se samozřejmě přikláním k cestě PowerShellu.
Vyhledávání
Původně jsem použil metodu z galerie TechNet
V krátkosti: Get-WmiObject -Class Win32_process
To v podstatě najde všechny unikátní uživatelské procesy běžící na počítači. Je to super, protože to najde všechno, i věci běžící jako služba, ale nejsem přesvědčen, že je to nejefektivnější způsob.
Podle googlu jsem našel spoustu kreativních způsobů, jak zkontrolovat, kdo je přihlášen na vašem boxu.
peetersonline.nl/2008/11/oneliner-get-logged-on-users-with-powershell/ mi vnukl nápad zkontrolovat Win32_LoggedOnUser, což se zdá být samozřejmé.
To vypadá skvěle a zdá se, že to funguje i s Get-CimInstance, i když výstup je trochu jiný.
learn-powershell.net/…/Quick-hit-find-currently-logged-on-users/ zvolil trochu staromódnější přístup, který se mi docela líbí, protože je trochu drsný a nutí mě hrát si s parsováním na základě šablon.
Nejsem si úplně jistý, která metoda je rychlejší, tak proč nezkusit implementovat všechny tři v modulu a vyzkoušet to.
Sketching
Vždy je dobré začít tím, že si uděláte náčrt toho, čeho se snažíte dosáhnout.
Pseudo code:Get-ActiveUser -ComputerName -Method Wanted output:Username ComputerName-------- ------------TestUser1 Svr3TestUser3 Svr3DonaldDuck Client2
Teď mám všechny informace, které potřebuji k nastavení úložiště GitHub.
github.com/mrhvid/Get-ActiveUser
Kód
Především mě zajímají parametry ComputerName a Method.
Param ( # Computer name, IP, Hostname ] $ComputerName, # Choose method, WMI, CIM or Query $Method )
Už mám v hlavě 3 možné Methods, takže nastavím ValidateSet s těmito 3 možnostmi. Pak se nemusím později starat o toto zadání.
Process { switch ($Method) { 'WMI' { } 'CIM' { } 'Query' { } } }
V části funkce Process jednoduše použiji přepínač pro 3 různé metody, které jsem povolil v Parametru.
Teď je to základní vyplňování prázdných políček.
WMI
Moje staré řešení je jednoduché a funguje dobře.
$WMI = Get-WmiObject -Class Win32_Process -ComputerName $ComputerName -ErrorAction Stop$ProcessUsers = $WMI.getowner().user | Select-Object -Unique
Ale teď, když jsem našel Win32_LoggedOnUser, se mi zdá, že je špatně to dělat tímto způsobem. Podívejme se místo toho na nový nápad.
To jsou všechno správné údaje, ale zdá se, že jsou ve formátu řetězce, takže s nimi budu muset trochu manipulovat. Dá se to udělat milionem způsobů.
function Get-MyLoggedOnUsers { param($Computer) Get-WmiObject Win32_LoggedOnUser -ComputerName $Computer | Select Antecedent -Unique | %{"{0}{1}" -f $_.Antecedent.ToString().Split('"'), $_.Antecedent.ToString().Split('"')} }
Petrův výše zmíněný jednořádkový text mi nepřišel příliš čtenářsky přívětivý, což u jednořádkového textu nevadí, ale rád bych, aby byl pokud možno trochu čitelnější.
$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)}
To vypadá správně 🙂 Výstup uložím do proměnné $ActiveUsers a totéž udělám pro CIM a Query.
CIM
Zkusíme to s CIM.
Tohle vypadá mnohem strukturovaněji.
CIM skončí jako snadno pochopitelná jednohubka 😀
$ActiveUsers = (Get-CimInstance Win32_LoggedOnUser -ComputerName $ComputerName).antecedent.name | Select-Object -Unique
Query
Použijeme starý dobrý Query.exe se mi velmi osvědčilo dříve probírané parsování na základě šablon.
$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
Teď už jen potřebuji pěkně naformátovat a vypsat uživatele. Chci čisté objekty s ComputerName a UserName.
# Create nice output format$UsersComputersToOutput = @()foreach($User in $ActiveUsers) { $UsersComputersToOutput += New-Object psobject -Property @{ ComputerName=$ComputerName; UserName=$User } }}# output data$UsersComputersToOutput
Testování
Teď mám problém. Nemohu to otestovat, protože nemám k dispozici hromadu testovacích serverů. Veškeré testování jsem prováděl proti vlastnímu boxu s Windows 10. Zdá se, že dotaz je mnohem rychlejší při lokálním spuštění, ale WMI/CIM by mohl poskytnout úplnější přehled o tom, jaké služby jsou spuštěny.
Mám spuštěno několik standardních účtů služeb, které by bylo dobré z výstupu odstranit. Také aby to bylo užitečné, budeme to chtít spustit proti mnoha počítačům.
Kombinace Get-ActiveUser se Start-Multithread z minulého týdne se zdá, že funguje, jak má.
Start-Multithread -Script { param($C) Get-ActiveUser -ComputerName $C -Method Query } -ComputerName ::1,Localhost | Out-GridView
Přepojení výše uvedeného do Out-GridView je pravděpodobně můj osobní oblíbený způsob, jak dosáhnout něčeho opravdu užitečného.
Teď máme všechna data pěkně pohromadě a je opravdu snadné zkontrolovat, zda je uživatel přihlášen na nějakém náhodném stroji. Je to také snadný způsob, jak zkontrolovat, zda se v síti nenachází nepovolaní uživatelé.
Publikování a zpětná vazba
Kód je zveřejněn na PowerShellGallery.
Prosím, pomozte mi tím, že mi ho otestujete. Rád bych věděl, jestli to funguje v reálném světě 🙂
# To install Get-ActiveUserInstall-Module Get-ActiveUser#To install Start-MultithreadInstall-Module Start-Multithread
Mělo by to fungovat, když máte nainstalovaný WMF 5+ a na Windows 10 out of the box.