Powershell Log Off Remote Session
Asked Answered
I

12

39

I am trying to formulate a Powershell command to remotely log off a user. We have a terminal server with a very unstable program that sometimes locks sessions. We have to remotely log off a user but I'm trying to write a Powershell statement that will log off the person who ran the script. I Googled around, and found this command:

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

However, the command returns "incorrect function." I can run other commands successfully in the brackets, such as Get-Process.

The idea is for me to put that into a script that users can run to log themselves off of the server (since when it locks, they cannot access the start menu or ALT+CTRL+END to do it through the GUI).

The flow would be this: Bob logs into MyServer via RDP but his session freezes. On his local desktop, he can run MyScript (containing a command similar to above) which will log off his session on MyServer.

Idomeneus answered 12/8, 2013 at 17:9 Comment(1)
Have look here #43353405Vogt
V
57

Perhaps surprisingly you can logoff users with the logoff command.

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.

The session ID can be determined with the qwinsta (query session) or quser (query user) commands (see here):

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

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

logoff $session /server:$server
Vasquez answered 12/8, 2013 at 17:52 Comment(5)
Actually, it works against a non-terminal server but not a terminal server connection. Is there anything I need to adjust?Idomeneus
You may need admin privileges to log off users on a terminal server. I'll check that tomorrow.Vasquez
@Idomeneus You may be able to work around this by mapping a local drive in the RDP session and run quser | find "%USERNAME%" >D:\path\to\session.txt in a logon script on the terminal server (replace D:\path\to\session.txt with the path to the output file on the mapped drive). That way a script on the client can read the session ID from a local file.Vasquez
Love the reqex split on the output of quser. How would I be able to get multiple session names [2] for logging of multiple or even all sessions on a remote server?Desultory
@Desultory Replace ? { $_ -match $username } with select -Skip 1.Vasquez
Q
18

Here's a great scripted solution for logging people out remotely or locally. I'm using qwinsta to get session information and building an array out of the given output. This makes it really easy to iterate through each entry and log out only the actual users, and not the system or RDP listener itself which usually just throws an access denied error anyway.

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

In the first line of this script give $serverName the appropriate value or localhost if running locally. I use this script to kick users before an automated process attempts to move some folders around. Prevents "file in use" errors for me. Another note, this script will have to be ran as an administrator user otherwise you can get accessed denied trying to log someone out. Hope this helps!

Quintie answered 7/3, 2016 at 16:9 Comment(5)
This solution works great and allows for filtering on pretty much any criteria, such as partial username matches, or disconnected sessions only. You can wrap the whole thing in another foreach loop to run against multiple servers, too.Wrench
What does the '^' in the '^ SESSIONNAME' section on the second line do here? Curious as it seems to work with our without.Chapel
It is a regular expression character which means beginning of line or beginning of string. Fairly universal across most regex implementations out there. Here's a topic about this very thing. It may not be necessary, but can help to make matches more specific in some cases. #5516619Quintie
Worked great, but that 'if statement' is redundent, no point checking empty and short length. If it's meant so you put the current user in the -ne filter to not log yourself out, the -or needs to be -and instead for the length filter to apply.Leatherwood
I honestly don't remember as I wrote this long ago. Looking at this now, you're probably right. Could have been one of those things I added while trying to get things working correctly and then didn't change it after it did what I wanted.Quintie
L
15

Adding plain DOS commands, if someone is so inclined. Yes, this still works for Win 8 and Server 2008 + Server 2012.

Query session /server:Server100

Will return:

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

And to log off a session, use:

Reset session 3 /server:Server100
Lawman answered 4/10, 2015 at 16:26 Comment(0)
D
7

This is oldschool and predates PowerShell, but I have used the qwinsta / rwinsta combo for YEARS to remotely log off stale RDP sessions. It's built in on at least Windows XP and forward (possibly earlier)

Determine the session ID:

qwinsta /SERVER:<NAME>

Remove the session in question:

rwinsta <SESSION_ID> /SERVER:<NAME>
Dialogue answered 12/8, 2013 at 18:32 Comment(2)
Any way to do this in bulk? I.e. how to remove all the sessions?Desultory
@joshua_mckinnon Worked perfectly when the command is issued from the local machine, but the server name wasn't enough. Is there a different syntax if the remote machine is on a domain? Or is it never going to work because it's assuming a Remote Desktop Host and not just someone signed on to a machine on the network?Hairbrush
O
6

You can use Invoke-RDUserLogoff

An example logging off Active Directory users of a specific Organizational Unit:

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

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

At the end of the pipe, if you try to use only foreach (%), it will log off only one user. But using this combination of foreach and pipe:

| % { $_ | command }

will work as expected.

Ps. Run as Adm.

Occlusive answered 14/5, 2014 at 21:48 Comment(2)
This is great, but it appears to be limited to Windows Server 2012 R2 / Windows 8.1?Desultory
@Desultory Indeed, "Applies To: Windows 8.1, Windows PowerShell 4.0, Windows Server 2012 R2", from technet.microsoft.com/en-us/library/jj215468.aspxOcclusive
M
4

Try the Terminal Services PowerShell Module:

Get-TSSession -ComputerName comp1 -UserName user1 | Stop-TSSession -Force
Marder answered 12/8, 2013 at 18:18 Comment(0)
P
3

Log off all users from a machine:

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

Details:

  • the try/catch is used when there are no users are on the server, and the query returns an error. however, you could drop the 2>&1 part, and remove the try/catch if you don't mind seeing the error string
  • select -skip 1 removes the header line
  • the inner foreach logs off each user
  • ($_ -split "\s+") splits the string to an array with just text items
  • [-6] index gets session ID and is the 6th string counting from the reverse of the array, you need to do this because the query output will have either 8 or 9 elements depending if the users connected or disconnected from the terminal session
Passionless answered 6/6, 2014 at 2:32 Comment(2)
What is this "query" thing?Cirillo
Mine worked with index [-4] instead of [-6], and "\s\s+" instead of "\s+"Occlusive
H
3

I've modified Casey's answer to only logoff disconnected sessions by doing the following:

   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 {}
    }
Hulking answered 25/6, 2015 at 13:49 Comment(1)
Not sure exactly what your last line is for. It doesn't entirely look like valid PS, and it doesn't look like much of an explanation either.Janitor
V
2

Below script will work well for both active and disconnected sessions as long as user has access to run logoff command remotely. All you have to do is change the servername from "YourServerName" on 4th line.

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!"

Some sample outputs. For active session: enter image description here

For disconnected sessions: enter image description here

if no sessions found: enter image description here Check out this solution as well to query all AD servers for your username and logoff only disconnected sessions. The script will also tell you if there were error connecting or querying the server.

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

Vogt answered 18/8, 2018 at 2:56 Comment(0)
G
1

I am sure my code will be easier and works faster.

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

    foreach ($id in $logon_sessions.si) {
        logoff $id
    }
Gussy answered 22/6, 2019 at 7:30 Comment(0)
T
1

Below simple script will logoff all the disconnected user in the same computer

$hostname = hostname
if (Test-Connection -ComputerName $hostname -Quiet -Count 1){
    $result = query session /server:$hostname
    $rows = $result -split "`n" 
    foreach ($row in $rows) {   
        if ($row -NotMatch "services|console" -and $row -match "Disc") {
            $sessionusername = $row.Substring(19,20).Trim()
            $sessionid = $row.Substring(39,9).Trim()
            Write-Output "Logging Off RDP Disconnected Sessions User $sessionusername"#, $session[2], $session[3]"
            logoff $sessionid /server:$hostname
        }
    }
}

Output will be

Logging Off RDP Disconnected Sessions User xyz
Tracee answered 2/5, 2022 at 17:43 Comment(0)
B
0

here is what i came up with. combining multiple answers

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

 }
 }
Blissful answered 23/7, 2020 at 3:31 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.