PowerShellを使用してリモートでログオンしたユーザーを見つけるためのより良い方法

ログオンしたユーザーを取得するにはさまざまな方法がありますが、私はお気に入りを持っています! 使用してqwinsta、唯一の問題は、文字列を返し、PowerShellがオブジェクトを好むことです:(しかし、決して恐れることはありません! それを直せるQwinsta

前提条件

私は最初にqwinstaを使い始めたときにエラー5アクセス拒否の問題に遭遇しました、そのためのレジストリ修正があります(StackOverflowに感謝:

"AllowRemoteRPC"=dword:00000001

これはPowerShellのブログなので、ここでは、リモートコンピュータ上で作業する可能性が高いので、リモートコンピュータに追加するためのPowerShell構文です。

$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()

構文

qwinstaの構文はかなり簡単です:

qwinsta /server:ServerName

そして、出力はむしろunmenacingに見えます:

 SESSIONNAME USERNAME ID STATE TYPE DEVICE services 0 Disc console Anthony 1 Active rdp-tcp 65536 Listen

唯一の問題は、Rdsサーバーを見ていた場合、PowerShellで文字列をフィルタリングする簡単な方法がないことです。 だからあなたは次のようなことをすることができませんでした:

qwinsta /server:RDSServer | Where-Object USERNAME -like "Anth*"

しかし、我々はそれを修正することができます! Qwinstaをラップして、その出力をオブジェクト化できるようにしましょう!

ラッピングqwinsta

ここでPowerShell関数を作成します。

その値の文字列を絞り出す

私たちが得るすべてのパラメータのもののほかに、私たちがやる最初のコアのことは、出力を変数に向けることです:

$result = qwinsta /server:$ComputerName

$resultちょうど前からの出力のようになりますが、それは文字列配列(string)としてフォーマットされます。 私たちは、もちろん、それを検証することができますGet-Member:

($result | Get-Member).TypeName

だから、次のことは、我々が望むだけの情報を取得することです。 私はヘッダーを必要としないので、最初の行をスキップしてforループを決めました:

ForEach($line in $result)

各行について、各値は何で区切られていますか? スペース! だから我々は使用することができます。文字列のSplit()メソッドと各スペースで分割します。 唯一の注意点は、複数のスペースを分割してnull値を取得できるため、各分割で値をテストする必要があることです。

$tmp = $line.split(" ") | ?{$_.length -gt 0}

注意すべきことの1つは、qwinstaからの出力はfixed-widthであり、値の長さに関係なく幅が変更されないことです。 私達はそれを利用し、私達の利点にある索引を使用してもいい。

最初に行うことは、実際にどの行にユーザーが含まれているかを判断することです。 文字を数えて、ユーザー名の値が19番目の文字で始まることがわかります(最初の文字は実際にはスペースですが、慎重に見てください)。 したがって、19番目の文字が空でない場合、ユーザーがいます。

If(($line -ne " "))

私がqwinstaでテストして気づいた次のことは、セッションが’Active’状態にある場合にのみSessionNameプロパティが値を持つことです。 そのため、状態が「アクティブ」であるかどうかを確認する必要もあります。’

If($line -eq "A")

それが’Active’の場合、$tmp変数は異なる量の値を持ちます。

アクティブ:

#raw rdp-tcp#0 anthony 2 Active #$tmp variable (split)rdp-tcp#0anthony2Active

アクティブではない:

#raw anthony 2 Disc #$tmp variable (split)anthony2Disc

値を使用可能なオブジェクトに変換する

すべての値を理解したら、私のお気に入りの新しいPSObjectを作成できます! Powershellv5を使用している場合は、{}型アクセラレータ(x)を使用できますが、最終的な互換性のためにここでNew-Objectコマンドレットを使用します。

New-Object PSObject -Property @{ "ComputerName" = $ComputerName "SessionName" = $tmp "UserName" = $tmp "ID" = $tmp "State" = $tmp "Type" = $tmp}

この場合、セッションがアクティブであることがわかっているため、$tmpの最初の文字列はセッション名であり、残りのプロパティが順番にあります。 ‘Type’または’SessionName’が空に戻る可能性があります。

状態が’アクティブ’でない場合は、それを次のように切り替えます:

New-Object PSObject -Property @{ "ComputerName" = $ComputerName "SessionName" = $null "UserName" = $tmp "ID" = $tmp "State" = $tmp "Type" = $null}

最終製品

これは関数の作成に関する投稿ではないので、他の構文をスキップして、私が書いた最終的な関数を紹介します。 これには、エキサイティングなものに入る前に、接続性や管理者権限をチェックするなどのベストプラクティスが含まれています。

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

これは私のUtilities GitHub repoで見つけることができます。 これは私の最初の公開されたスクリプトです! 万歳! だから、それを使用する方法を上に行くことができます:

簡単な使用法

リモートまたはローカルコンピュータにログインしているユーザーを取得するには、次のように単純に使用します:

Get-ActiveSessions ComputerName

そして、これは次のようなものを返すはずです:

ID : 2SessionName :Type :UserName : anthonyComputerName : dc01State : Disc

そして、それを変数に渡して型名を見ると、PSCustomObjectが必要です。

$sessions = Get-ActiveSessions ComputerName($sessions | Get-Member).TypeNameSystem.Management.Automation.PSCustomObject

高度な使用法

ここでは、パイプライン入力を受け入れることができ、オブジェクトを出力することができます。 だから我々はGet-ActiveSessionsの前後に何かをすることができます。

特定のユーザーがログインしている環境内のすべてのサーバーを検索する場合はどうすればよいですか?

Get-ADComputer -Filter {OperatingSystem -like '*Server*'} | Get-ActiveSessions | Where-Object UserName -eq 'Anthony'

または、テキストファイルからコンピュータのリストを取得し、それらすべてにログインした一意のユーザーのリストを出力するのはどうですか?

get-content C:\temp\computers.txt | Get-ActiveSessions | Select-Object -Unique UserName -ExpandProperty UserName | Out-File C:\temp\users.txt

あなたはGet-ActiveSessionsの任意の有用な用途を思い付く場合は、コメント!

結論

うまくいけば、私は私のためにこのようなものを行うための関数を書くだろう理由のための感謝を持っています。 私は正直なところ、私は私の仕事を容易にするためにこれを使用した回数を数えることができませんでした。

とにかく、私はいくつかの注目すべき投稿、特にGet-ActiveSessionsのパートナー:Close-ActiveSessionsが登場しています。

ネタバレ:

Get-ActiveSessions Computer | Close-ActiveSessions

また、”PowerShellスクリプトのスケジュールされたタスクの作成”は、私はまだ画像を行っていないので、旅行になります!

Leave a Reply