Powershell注销远程会话

时间:2013-08-12 17:09:02

标签: powershell rdp

我正在尝试制定一个Powershell命令来远程注销用户。我们有一个终端服务器,程序非常不稳定,有时会锁定会话。我们必须远程注销用户,但我正在尝试编写一个Powershell语句,该语句将注销运行该脚本的人员。我用Google搜索,发现了这个命令:

Invoke-Command -ComputerName MyServer -Command {shutdown -l}

但是,该命令返回“错误的功能”。我可以在括号中成功运行其他命令,例如Get-Process。

我的想法是让我把它放到一个脚本中,用户可以运行该脚本将自己从服务器上注销(因为当它锁定时,他们无法访问开始菜单或ALT + CTRL + END通过GUI执行此操作)

流程将是这样的: Bob通过RDP登录MyServer,但他的会话冻结了。在他的本地桌面上,他可以运行MyScript(包含类似于上面的命令),这将在MyServer上注销他的会话。

12 个答案:

答案 0 :(得分:45)

也许令人惊讶的是,您可以使用logoff命令注销用户。

C:\> logoff /?
Terminates a session.

LOGOFF [sessionname | sessionid] [/SERVER:servername] [/V] [/VM]

  sessionname         The name of the session.
  sessionid           The ID of the session.
  /SERVER:servername  Specifies the Remote Desktop server containing the user
                      session to log off (default is current).
  /V                  Displays information about the actions performed.
  /VM                 Logs off a session on server or within virtual machine.
                      The unique ID of the session needs to be specified.

可以使用qwinstaquery session)或quserquery user)命令确定会话ID(请参阅here):

$server   = 'MyServer'
$username = $env:USERNAME

$session = ((quser /server:$server | ? { $_ -match $username }) -split ' +')[2]

logoff $session /server:$server

答案 1 :(得分:12)

这是一个很棒的脚本解决方案,可以远程或本地记录人员。我正在使用qwinsta获取会话信息并从给定输出中构建数组。这样就可以很容易地遍历每个条目并仅注销实际用户,而不是系统或RDP监听器本身,这通常只会抛出拒绝访问错误。

$serverName = "Name of server here OR localhost"
$sessions = qwinsta /server $serverName| ?{ $_ -notmatch '^ SESSIONNAME' } | %{
$item = "" | Select "Active", "SessionName", "Username", "Id", "State", "Type", "Device"
$item.Active = $_.Substring(0,1) -match '>'
$item.SessionName = $_.Substring(1,18).Trim()
$item.Username = $_.Substring(19,20).Trim()
$item.Id = $_.Substring(39,9).Trim()
$item.State = $_.Substring(48,8).Trim()
$item.Type = $_.Substring(56,12).Trim()
$item.Device = $_.Substring(68).Trim()
$item
} 

foreach ($session in $sessions){
    if ($session.Username -ne "" -or $session.Username.Length -gt 1){
        logoff /server $serverName $session.Id
    }
}

在此脚本的第一行中,如果在本地运行,则为$ serverName提供适当的值或localhost。在自动进程尝试移动某些文件夹之前,我使用此脚本来踢用户。为我防止“文件使用中”错误。另请注意,此脚本必须以管理员用户身份运行,否则您可能会被拒绝尝试将某人记录下来。希望这有帮助!

答案 2 :(得分:11)

添加纯DOS命令,如果有人如此倾向。是的,这仍适用于Win 8和Server 2008 + Server 2012。

Query session /server:Server100

将返回:

SESSIONNAME       USERNAME                 ID  STATE   TYPE        DEVICE
rdp-tcp#0         Bob                       3  Active  rdpwd
rdp-tcp#5         Jim                       9  Active  rdpwd
rdp-tcp                                 65536  Listen

要注销会话,请使用:

Reset session 3 /server:Server100

答案 3 :(得分:7)

这是oldschool并且早于PowerShell,但是我使用qwinsta / rwinsta组合为YEARS远程注销陈旧的RDP会话。它内置于至少Windows XP和前进(可能更早)

确定会话ID:

qwinsta /SERVER:<NAME>

删除有问题的会话:

rwinsta <SESSION_ID> /SERVER:<NAME>

答案 4 :(得分:4)

试试Terminal Services PowerShell Module

Get-TSSession -ComputerName comp1 -UserName user1 | Stop-TSSession -Force

答案 5 :(得分:4)

您可以使用 Invoke-RDUserLogoff

注销特定组织单位的Active Directory用户的示例:

$users = Get-ADUser -filter * -SearchBase "ou=YOUR_OU_NAME,dc=contoso,dc=com"

Get-RDUserSession | where { $users.sAMAccountName -contains $_.UserName } | % { $_ | Invoke-RDUserLogoff -Force }

在管道的末尾,如果您尝试仅使用foreach(%),它将仅注销一个用户。但是使用foreach和pipe的这种组合:

  

| %{$ _ |命令}

将按预期工作。

聚苯乙烯。作为Adm运行。

答案 6 :(得分:3)

我已修改Casey's answer以仅通过执行以下操作来注销已断开连接的会话:

   foreach($Server in $Servers) {
    try {
        query user /server:$Server 2>&1 | select -skip 1 | ? {($_ -split "\s+")[-5] -eq 'Disc'} | % {logoff ($_ -split "\s+")[-6] /server:$Server /V}
    }
    catch {}
    }

答案 7 :(得分:2)

从计算机注销所有用户:

try {
   query user /server:$SERVER 2>&1 | select -skip 1 | foreach {
     logoff ($_ -split "\s+")[-6] /server:$SERVER
   }
}
catch {}

详细说明:

  • 当服务器上没有用户时使用try / catch,并且query返回错误。但是,您可以删除2>&1部分,如果您不介意查看错误字符串,则删除try / catch
  • select -skip 1删除标题行
  • 内部foreach注销每个用户
  • ($_ -split "\s+")将字符串拆分为仅包含文本项的数组
  • [-6]索引获取会话ID,并且是从数组的反向计数的第6个字符串,您需要这样做,因为query输出将具有8或9个元素,具体取决于用户是否连接或与终端会话断开连接

答案 8 :(得分:0)

由于我们位于PowerShell区域,因此如果我们可以返回正确的PowerShell对象,则特别有用...

出于个人的简洁性,我个人喜欢这种解析方法:

((quser) -replace '^>', '') -replace '\s{2,}', ',' | ConvertFrom-Csv

注意: 这不能解决断开连接的用户,但是如果您只想快速获取用户列表,效果很好并且不在乎其余信息。我只想要一个列表,不在乎它们当前是否断开连接。

如果您确实关心其余数据,则稍微复杂一点:

(((quser) -replace '^>', '') -replace '\s{2,}', ',').Trim() | ForEach-Object {
    if ($_.Split(',').Count -eq 5) {
        Write-Output ($_ -replace '(^[^,]+)', '$1,')
    } else {
        Write-Output $_
    }
} | ConvertFrom-Csv

I take it a step farther and give you a very clean object on my blog.

答案 9 :(得分:0)

下面的脚本对于活动会话和断开连接的会话都适用,只要用户可以远程运行注销命令即可。您所要做的就是从第四行的“ YourServerName”更改服务器名称。

param (
    $queryResults = $null,
    [string]$UserName = $env:USERNAME,
    [string]$ServerName = "YourServerName"
)


if (Test-Connection $ServerName -Count 1 -Quiet) {  
    Write-Host "`n`n`n$ServerName is online!" -BackgroundColor Green -ForegroundColor Black


    Write-Host ("`nQuerying Server: `"$ServerName`" for disconnected sessions under UserName: `"" + $UserName.ToUpper() + "`"...") -BackgroundColor Gray -ForegroundColor Black

        query user $UserName /server:$ServerName 2>&1 | foreach {  

            if ($_ -match "Active") {
                Write-Host "Active Sessions"
                $queryResults = ("`n$ServerName," + (($_.trim() -replace ' {2,}', ','))) | ConvertFrom-Csv -Delimiter "," -Header "ServerName","UserName","SessionName","SessionID","CurrentState","IdealTime","LogonTime"


                $queryResults | ft
                Write-Host "Starting logoff procedure..." -BackgroundColor Gray -ForegroundColor Black

                $queryResults | foreach {
                    $Sessionl = $_.SessionID
                    $Serverl = $_.ServerName
                    Write-Host "Logging off"$_.username"from $serverl..." -ForegroundColor black -BackgroundColor Gray
                    sleep 2
                    logoff $Sessionl /server:$Serverl /v

                }


            }                
            elseif ($_ -match "Disc") {
                Write-Host "Disconnected Sessions"
                $queryResults = ("`n$ServerName," + (($_.trim() -replace ' {2,}', ','))) |  ConvertFrom-Csv -Delimiter "," -Header "ServerName","UserName","SessionID","CurrentState","IdealTime","LogonTime"

                $queryResults | ft
                Write-Host "Starting logoff procedure..." -BackgroundColor Gray -ForegroundColor Black

                $queryResults | foreach {
                    $Sessionl = $_.SessionID
                    $Serverl = $_.ServerName
                    Write-Host "Logging off"$_.username"from $serverl..."
                    sleep 2
                    logoff $Sessionl /server:$Serverl /v

                }
            }
            elseif ($_ -match "The RPC server is unavailable") {

                Write-Host "Unable to query the $ServerName, check for firewall settings on $ServerName!" -ForegroundColor White -BackgroundColor Red
            }
            elseif ($_ -match "No User exists for") {Write-Host "No user session exists"}

    }
}
else {

    Write-Host "`n`n`n$ServerName is Offline!" -BackgroundColor red -ForegroundColor white
    Write-Host "Error: Unable to connect to $ServerName!" -BackgroundColor red -ForegroundColor white
    Write-Host "Either the $ServerName is down or check for firewall settings on server $ServerName!" -BackgroundColor Yellow -ForegroundColor black
}





Read-Host "`n`nScript execution finished, press enter to exit!"

一些示例输出。对于活动会话: enter image description here

对于断开的会话: enter image description here

如果未找到会话: enter image description here 也请查看此解决方案,以查询所有AD服务器的用户名,并仅注销断开连接的会话。该脚本还将告诉您连接或查询服务器时是否出错。

Powershell to find out disconnected RDP session and log off at the same time

答案 10 :(得分:0)

我确信我的代码会更容易并且运行更快。

    $logon_sessions = get-process -includeusername | Select-Object -Unique -Property UserName, si | ? { $_ -match "server" -and $_ -notmatch "admin" }

    foreach ($id in $logon_sessions.si) {
        logoff $id
    }

答案 11 :(得分:0)

这是我想出的。结合多个答案

#computer list below
$computers = (
'computer1.domain.local',
'computer2.domain.local'

)
 
foreach ($Computer in $computers) {

 Invoke-Command -ComputerName $Computer -ScriptBlock { 
  Write-Host '______  '$Env:Computername
  $usertocheck = 'SomeUserName'
$sessionID = ((quser | Where-Object { $_ -match $usertocheck }) -split ' +')[2]
 

If([string]::IsNullOrEmpty($sessionID)){            
    Write-Host -ForegroundColor Yellow  "User Not Found."           
} else {            
    write-host -ForegroundColor Green  'Logging off ' $usertocheck 'Session ID' $sessionID
    logoff $sessionID    
}

 }
 }