Powershell注销远程会话

Powershell注销远程会话,powershell,rdp,Powershell,Rdp,我正在尝试制定Powershell命令以远程注销用户。我们有一个终端服务器,其程序非常不稳定,有时会锁定会话。我们必须远程注销用户,但我正在尝试编写一个Powershell语句来注销运行脚本的人。我搜索了一下,发现了这个命令: Invoke-Command -ComputerName MyServer -Command {shutdown -l} 但是,该命令返回“不正确的函数”。我可以在括号中成功运行其他命令,例如Get Process 我的想法是将其放入一个脚本中,用户可以运行该脚本注销服

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

Invoke-Command -ComputerName MyServer -Command {shutdown -l}
但是,该命令返回“不正确的函数”。我可以在括号中成功运行其他命令,例如Get Process

我的想法是将其放入一个脚本中,用户可以运行该脚本注销服务器(因为当服务器锁定时,他们无法通过GUI访问开始菜单或ALT+CTRL+END)

流程如下所示:
Bob通过RDP登录MyServer,但他的会话被冻结。在他的本地桌面上,他可以运行MyScript(包含与上面类似的命令),该命令将在MyServer上注销他的会话。

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

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.
请尝试以下方法:


这是一个古老的版本,比PowerShell早,但我多年来一直使用qwinsta/rwinsta组合远程注销过时的RDP会话。它至少内置于Windows XP和forward(可能更早)上

确定会话ID:

qwinsta /SERVER:<NAME>
qwinsta/SERVER:
删除相关会话:

rwinsta <SESSION_ID> /SERVER:<NAME>
rwinta/SERVER:

您可以使用调用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的组合:

|%{$|命令}

将按预期工作


Ps.以管理员身份运行。

从计算机注销所有用户:

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

  • 当服务器上没有用户时,将使用
    try
    /
    catch
    ,并且
    查询
    返回错误。但是,如果您不介意看到错误字符串,可以删除
    2>&1
    部分,然后删除
    try
    /
    catch
  • 选择-跳过1
    删除标题行
  • 内部
    foreach
    注销每个用户
  • ($\u-split“\s+”)
    将字符串拆分为仅包含文本项的数组
  • [-6]
    索引获取会话ID,是从数组背面开始计数的第6个字符串,您需要这样做,因为
    查询
    输出将包含8或9个元素,具体取决于用户是否连接或断开了终端会话
通过执行以下操作,我已修改为仅注销断开连接的会话:

   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 {}
    }

如果有人愿意,可以添加普通的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

这是一个很棒的脚本解决方案,用于远程或本地注销用户。我使用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。我使用这个脚本在自动化进程尝试移动一些文件夹之前踢用户。防止我出现“文件正在使用”错误。另请注意,此脚本必须以管理员用户身份运行,否则您可能会被拒绝尝试注销某人。希望这有帮助

只要用户有权远程运行注销命令,下面的脚本就可以很好地用于活动会话和断开连接的会话。您所要做的就是将第四行的服务器名从“YourServerName”更改为“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!"
一些示例输出。对于活动会话:

对于断开连接的会话:

如果未找到会话: 检查此解决方案,并查询所有广告服务器的用户名和注销仅断开连接的会话。脚本还将告诉您连接或查询服务器时是否出错


我相信我的代码会更简单,运行速度更快

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

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

这是我想到的。综合多种答案

#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    
}

 }
 }

实际上,它针对的是非终端服务器,而不是终端服务器连接。有什么我需要调整的吗?您可能需要管理员权限才能注销终端服务器上的用户。我明天会检查。@jlacroix您可以通过在RDP会话中映射本地驱动器来解决这个问题,并在终端服务器上的登录脚本中运行
quser | find”%USERNAME%>D:\path\to\session.txt
(用映射驱动器上的输出文件路径替换
D:\path\to\session.txt
)。这样,客户端上的脚本就可以从本地文件读取会话ID。喜欢
quser
输出上的reqex拆分。如何才能获取多个会话名称[2],以便记录远程服务器上的多个甚至所有会话?@Jaans Replace
?{$\将$username}
选择-Skip 1
匹配。这很好,但似乎仅限于Windows Server 2012 R2/Windows 8.1?@Jaans确实,“适用于:Windows 8.1、Windows PowerShell 4.0、Windows Server 2012 R2”,是否可以批量执行此操作?例如,如何删除所有会话?@joshua_mckinnon在从本地计算机发出命令时工作得很好,但服务器名称不够。如果远程计算机位于域上,是否有不同的语法?或者它永远不会工作,因为它假设一个远程桌面主机,而不仅仅是有人登录到网络上的机器上?这是什么“查询”的东西?我的是索引[-4]而不是[-6],而“\s\s+”而不是“\s+”,不确定你的最后一行到底是干什么的。它看起来不完全像是有效的PS,也不像是一个很好的解释。这个解决方案工作得很好,可以在相当大的范围内进行过滤
    $logon_sessions = get-process -includeusername | Select-Object -Unique -Property UserName, si | ? { $_ -match "server" -and $_ -notmatch "admin" }

    foreach ($id in $logon_sessions.si) {
        logoff $id
    }
#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    
}

 }
 }