ett bättre sätt att hitta en inloggad användare på distans med PowerShell
det finns ett berg av olika sätt att få inloggade användare, men jag har en favorit! Med qwinsta
är det enda problemet att det returnerar en sträng och PowerShell gillar objekt 🙁 men aldrig rädd! Vi kan fixa det.
qwinsta
Prereqs
jag stötte på ett fel 5 Åtkomst nekad fråga när jag först började använda qwinsta
, det finns en registerfix för det (tack StackOverflow:
"AllowRemoteRPC"=dword:00000001
eftersom det här är en PowerShell-blogg, här är PowerShell-syntaxen för att lägga till den på en fjärrdator, eftersom vi sannolikt kommer att arbeta på fjärrdatorer.
$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()
Syntax
syntaxen för qwinsta är ganska enkel:
qwinsta /server:ServerName
och utgången ser ganska obehaglig ut:
SESSIONNAME USERNAME ID STATE TYPE DEVICE services 0 Disc console Anthony 1 Active rdp-tcp 65536 Listen
det enda problemet är att om du tittade på en RDS-server finns det inget enkelt sätt att filtrera strängar i PowerShell. Så du kunde inte göra något som:
qwinsta /server:RDSServer | Where-Object USERNAME -like "Anth*"
men vi kan fixa det! Låt oss Linda qwinsta så att vi kan objektivera den utgången!
inslagning qwinsta
vi kommer att skapa en PowerShell-funktion här, läsare akta dig.
vrida ut strängen för dess värden
förutom alla parameterämnen, som vi kommer till, är den första kärnan vi ska göra att rikta utmatningen till en variabel:
$result = qwinsta /server:$ComputerName
nu kommer $result
att se ut som utmatningen från tidigare, men den kommer att formateras som en strängmatris (string
). Vi kan naturligtvis verifiera det med Get-Member
:
($result | Get-Member).TypeName
så nästa sak är att hämta bara den information vi vill ha. Jag har bestämt mig för en för loop, hoppar över första raden eftersom vi inte behöver rubrikerna:
ForEach($line in $result)
för varje rad, vad avgränsas varje värde med? Utrymmen! Så vi kan använda .Split () metod på strängar och dela med varje mellanslag. Den enda försiktigheten är att vi också måste testa för värde på varje delning eftersom du kan dela flera mellanslag och få null-värden.
$tmp = $line.split(" ") | ?{$_.length -gt 0}
en sak att notera är att utmatningen från qwinsta
är fast bredd, vilket innebär att bredden inte ändras oavsett hur länge någon av värdena är. Vi kan dra nytta av det och använda vissa index till vår fördel.
det första du behöver göra är att bestämma vilka rader som faktiskt innehåller en användare. Vi kan räkna tecken och upptäcka att ANVÄNDARNAMNVÄRDET börjar vid det 19: e tecknet (det första tecknet är faktiskt utrymmet, titta noga). Så om det 19: e tecknet inte är tomt har vi en användare.
If(($line -ne " "))
nästa sak jag märkte vid testning med qwinsta
är att SessionName-egenskapen bara har värde om sessionen är i ‘aktivt’ tillstånd. Så vi måste också kontrollera om staten är ‘aktiv’, vilket vi kan göra genom att kontrollera indexet för det tillståndet för en ‘A’
If($line -eq "A")
om den är’ aktiv ‘ kommer variabeln $tmp
att ha olika mängder värden.
Aktiv:
#raw rdp-tcp#0 anthony 2 Active #$tmp variable (split)rdp-tcp#0anthony2Active
Inte Aktiv:
#raw anthony 2 Disc #$tmp variable (split)anthony2Disc
konvertera värdena till användbara objekt
när vi har räknat ut alla värden kan vi skapa ett nytt PSObject, min favorit! Om du har PowerShellv5 kan du använda {}
typ accelerator (x), Men jag kommer att använda New-Object
cmdlet här för ultimat kompatibilitet.
New-Object PSObject -Property @{ "ComputerName" = $ComputerName "SessionName" = $tmp "UserName" = $tmp "ID" = $tmp "State" = $tmp "Type" = $tmp}
i det här fallet eftersom vi vet att sessionen är aktiv är den första strängen i $tmp
sessionsnamnet, och sedan har du resten av egenskaperna. Det är möjligt att ‘typ’ eller’ SessionName ‘ kan komma tillbaka tom.
om staten inte är ‘aktiv’ skulle vi byta till:
New-Object PSObject -Property @{ "ComputerName" = $ComputerName "SessionName" = $null "UserName" = $tmp "ID" = $tmp "State" = $tmp "Type" = $null}
slutprodukten
eftersom det här inte är ett inlägg om att skapa funktioner, kommer jag att hoppa över den andra syntaxen och visa dig den slutliga funktionen jag skrev. Detta inkluderar några bästa praxis saker som att kontrollera anslutning och administratörsrättigheter innan du går in i spännande saker.
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
du hittar detta i mina verktyg GitHub repo. Detta är mitt första publicerade manus! Hurra! Så låt oss gå igenom hur man använder det:
enkel användning
för att hämta användarna inloggade på en fjärr-eller lokal dator, skulle vi helt enkelt använda:
Get-ActiveSessions ComputerName
och detta borde återvända något som liknar:
ID : 2SessionName :Type :UserName : anthonyComputerName : dc01State : Disc
och om du skickar den till en variabel och tittar på typnamnet, borde vi ha en PSCustomObject
, vilket innebär att vi kan använda alla andra användbara cmdlets för filtrering och whatnot!
$sessions = Get-ActiveSessions ComputerName($sessions | Get-Member).TypeNameSystem.Management.Automation.PSCustomObject
avancerad användning
vi har två saker som går för oss här, vi kan acceptera pipeline input och det ouputs ett objekt. Så vi kan göra saker före och efter Get-ActiveSessions
.
vad händer om du vill hitta alla servrar i din miljö som har en viss användare inloggad på dem?
Get-ADComputer -Filter {OperatingSystem -like '*Server*'} | Get-ActiveSessions | Where-Object UserName -eq 'Anthony'
eller vad sägs om att dra en lista över datorer från en textfil och mata ut listan över unika användare som är inloggade på dem alla?
get-content C:\temp\computers.txt | Get-ActiveSessions | Select-Object -Unique UserName -ExpandProperty UserName | Out-File C:\temp\users.txt
om du kommer med några användbara användningar av Get-ActiveSessions
, kommentera!
slutsats
förhoppningsvis har du en uppskattning för varför jag skulle skriva en funktion för att göra det här för mig. Jag kunde ärligt talat inte räkna hur många gånger jag har använt detta för att göra mitt arbete enklare.
hur som helst, jag har ett par anmärkningsvärda inlägg som kommer upp, särskilt partnern till Get-ActiveSessions
: Close-ActiveSessions
.
Spoiler:
Get-ActiveSessions Computer | Close-ActiveSessions
och även: ‘skapa schemalagda uppgifter för PowerShell-skript’, vilket kommer att bli en resa eftersom jag inte har gjort några bilder ännu!
Leave a Reply