Powershell注销远程会话
我正在尝试制定Powershell命令以远程注销用户。我们有一个终端服务器,其程序非常不稳定,有时会锁定会话。我们必须远程注销用户,但我正在尝试编写一个Powershell语句来注销运行脚本的人。我搜索了一下,发现了这个命令:Powershell注销远程会话,powershell,rdp,Powershell,Rdp,我正在尝试制定Powershell命令以远程注销用户。我们有一个终端服务器,其程序非常不稳定,有时会锁定会话。我们必须远程注销用户,但我正在尝试编写一个Powershell语句来注销运行脚本的人。我搜索了一下,发现了这个命令: Invoke-Command -ComputerName MyServer -Command {shutdown -l} 但是,该命令返回“不正确的函数”。我可以在括号中成功运行其他命令,例如Get Process 我的想法是将其放入一个脚本中,用户可以运行该脚本注销服
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+”)
索引获取会话ID,是从数组背面开始计数的第6个字符串,您需要这样做,因为[-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
}
}
}