To force a logoff on a remote computer, admins have a few options at their disposal. In this article, we'll go over three ways to make this happen with PowerShell.

Adam Bertram

Adam Bertram is a 20-year IT veteran, Microsoft MVP, blogger, and trainer. Adam is the founder of the e-learning tech screencast platform TechSnips. Catch up on Adam’s articles at adamtheautomator.com, or follow TechSnips on Twitter at @techsnips_io.

There are a number of reasons to do this, including employee termination, as a troubleshooting measure or simply to free up some sessions on a remote desktop server. Knowing how to do this from the command line will prevent either having to yell at users over the phone to log off, or having to access their remote consoles via VNC. Using the command line also gives an admin the opportunity to perform this task across many machines or users at once, if necessary.

The first way is by using the logoff command. This command has been around a long time. Many admins are not aware of this command, let alone that it has the ability to perform remote logoffs!

The logoff utility can log off users remotely but requires an extra step of finding a session ID. So first, we must find this ID. We can do this by using the quser utility and the server argument as you can see below.

Finding the session ID

Finding the session ID

Notice the value of 2 under the ID field. That is the session ID we need. However, unfortunately, since quser is not a PowerShell command that would return a structured object, we'll have to parse this string to pull out that value. To pull out this value by itself, we can use the Where-Object command and do a little regular expression matching to make it happen.

Above, I am searching for any user with the name of administrator and only returning the session ID. Once I have the session ID, I can now use this ID with the logoff command to log off the administrator account remotely on the DC computer.

Another method is to use WMI/CIM and the Win32Shutdown() method. This method doesn't force you to find a user session first but also doesn't give you the ability to pick a user either. This method will just log off all users interactively logged on to the remote computer. You can do this on a single line in PowerShell.

Below, you can see that I'm invoking a CIM method on the DC computer again and specifying 4 as a flag. This forcefully logs off the user. You can use 0 here too to perform a "graceful" logoff.

There's one last method if the client system from which you're invoking the command is Windows 10. In this case, you should have the Invoke-RDUserLogoff command available to you. Using this method is similar to using the logoff utility. You must first obtain the session ID using quser or the qwinsta utility if the remote server is not running Remote Desktop Services.

After acquiring the session ID, you can pass it to Invoke-RDUserLogoff with the UnifiedSessionId parameter and use the HostServer parameter to pass along the remote computer name.

By now, I hope you've seen enough ways to log off a user from a remote computer. Each method performs the task in a different way but in the end gets the same job done.

Join the 4sysops PowerShell group!

12+

Users who have LIKED this post:

  • avatar
  • avatar
  • avatar
Share
16 Comments
  1. commotion 2 years ago

    You actually need to have

    $sessionId = ((quser /server:DC | Where-Object { $_ -match $userName }) -split ' +')[3]

    instead of the 2 on the end otherwise your output will be consolename not id

     

    7+

    • German Pulido 9 months ago

      Actually, sometimes you need the second column, sometimes you need the third column. Using "quser" in scripts is simply not doable, since sometimes (as in the screenshot shown in this blog) SESSIONNAME is blank, but other times it has a value (either the number of the RDP session, or "console").

      0

  2. Alfred Kuhnert 2 years ago

    Here is the final revised functional script that is being used at my company. I found a PC today with 16 users currently logged on!!! The script contains a loop so you can logoff more than one person on the targeted PC:

    16+

  3. Dan 1 year ago

    The script Alfred posted is my new favorite thing.  Thank you!

    1+

  4. Greg Shelton 1 year ago

    I love this script.. but what could I add as an option to say log off "all users"?

    we have a process that allows 50 people to connect, and I don't want to list each session ID one at a time..

    An option to remove all users by typing "All" instead of a number would make this flawless!

    thanks so much..

    3+

  5. David Figueroa 1 year ago

    A few notes for everyone:

    1. QUser and QWinsta do *not* require admin rights to get session information from *any* Windows system with Windows 2003/XP or newer.
    2. Logoff, Reset, and QProcess *do* require admin rights on the machine you will be talking to (unless you are logging off or querying processes from your own session).
    3. All of these commands support a /v:<servername> option to interact with a remote machine

    It's easy to process the output with powershell since the output is just strings.

    One other utility that can be incredibly helpful is the tslogoff.exe command from C-A-D consulting (http://www.ctrl-alt-del.com.au/CAD_TSUtils.htm), it has multiple options such as tslogoff * /server:<servername> to logoff everyone, or tslogoff /disc /server:<servername> to logoff disconnected sessions, etc.

    David F.

    1+

  6. Koen 1 year ago

    I wrote this:  It will scan all servers you select for users that are logged on and lets you select users to log off.

    Function YesNo
    {
    Param($provide)
    $a = new-object -comobject wscript.shell
    $popup = "Do you want to Select the servers to scan ?"
    $intAnswer = $a.popup($popup, `
    0,$provide,4)
    If ($intAnswer -eq 6) {
    $answer = "yes"
    } else {
    $answer = $null
    }
    return $Answer
    }
    Function DisplayInfo
    {
    Param($Servers)
    cls
    write-host
    $count = $servers.count
    write-host $count -ForegroundColor Yellow -NoNewline
    write-host " servers selected to investigate." -ForegroundColor Green
    write-host "The total of servers in the AD is :" -ForegroundColor Green -NoNewline
    Write-Host (get-adcomputer -filter {Operatingsystem -like "Windows Server*"} | select name).count -ForegroundColor Red
    Write-Host
    }
    Function Selection
    {
    #$servers = (get-adcomputer -filter {Operatingsystem -like "Windows Server*"} | sort name | select name).name
    $servers = (get-adcomputer -LDAPFilter "(&(objectCategory=computer)(operatingSystem=Windows Server*)(!serviceprincipalname=*MSClusterVirtualServer*)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))" -Property name | sort-object Name).name
    if (yesNo -eq "Yes")
    {
    $servers = $servers | out-gridview -passthru
    }
    return $servers
    }

    Function DoIt
    {
    Param($Servers)
    $completelist = @()
    foreach ($server in $servers)
    {
    $serverdisp = $server.PadRight(($servers | Measure-Object -Maximum -Property Length).Maximum)
    $Skip = 0
    Write-host "Checking server " -ForegroundColor Green -NoNewline; write-host $Server -ForegroundColor Yellow -NoNewline
    if (Test-Connection $server -count 1 -Quiet)
    {
    Write-Host " ------ (online)" -ForegroundColor Green
    $sessionId = $null
    try
    {
    #$ErrorActionPreference = "SilentlyContinue"
    $SessionIds = quser /server:$server 2>&1
    #$ErrorActionPreference = "Continue"
    }
    catch
    {
    Write-Host " - " -ForegroundColor Green -NoNewline
    write-host "$serverdisp seems inaccessible." -ForegroundColor Red -NoNewline
    Write-Host " No data available." -ForegroundColor Yellow
    write-host " ---------------------- " -ForegroundColor green
    $skip = 1
    }
    if ($Skip -eq 0)
    {
    $nr = $sessionIds.count-2
    $c=0
    do
    {
    $c++
    $user = (($sessionIds[$c]) -split ' +')[1]
    $session = (($sessionIds[$c]) -split ' +')[2]
    $State = (($sessionIds[$c]) -split ' +')[3]
    $IdleT = (($sessionIds[$c]) -split ' +')[4]
    $LogonT = (($sessionIds[$c]) -split ' +')[5] + " " + (($sessionIds[$c]) -split ' +')[6]
    Write-Host " - " -ForegroundColor Green -NoNewline
    write-host $serverdisp -ForegroundColor Yellow -NoNewline
    Write-Host " - " -ForegroundColor Green -NoNewline
    write-host $user -ForegroundColor Magenta -NoNewline
    Write-Host " - " -ForegroundColor Green -NoNewline
    write-host $session -ForegroundColor Cyan -NoNewline
    Write-Host " - " -ForegroundColor Green -NoNewline
    write-host $State -ForegroundColor Cyan -NoNewline
    Write-Host " - " -ForegroundColor Green -NoNewline
    write-host $LogonT -ForegroundColor Cyan -NoNewline
    Write-Host " => " -ForegroundColor Green -NoNewline
    write-host $IdleT -ForegroundColor Cyan
    # $Ser+=@($server); $use+=@($user);$Ses+=@($session);$sta+=@($state);$Idl+=@($IdleT);$Log+=@($LogonT)
    $completelist += @{server=$server;user=$user;session=$session;State=$state;IdleTime=$idleT;LogonTime=$logonT}
    }
    while ($c -le $nr)
    write-host " ---------------------- " -ForegroundColor green
    }
    }
    else
    {
    Write-Host " ------ (offline)" -ForegroundColor Red
    Write-Host " - No data available." -ForegroundColor Yellow
    write-host " ---------------------- " -ForegroundColor green
    }
    }
    <# $completeList = New-Object PSObject
    $completeList | Add-Member NoteProperty Server $Ser
    $completeList | Add-Member NoteProperty User $use
    $completeList | Add-Member NoteProperty Session $ses
    $completeList | Add-Member NoteProperty State $sta
    $completeList | Add-Member NoteProperty IdleTime $Idl
    $completeList | Add-Member NoteProperty Logon $Log
    $completelist = @(server=$ser;user=$use;session=$ses;State=$sta;IdleTime=$idl;LogonTime=$log) #>
    return $completeList
    }
    Function YesNo2
    {
    Param($Completelist)
    $List = $completelist |% {New-Object psobject -Property $_}
    $Users = ($completelist |% {New-Object psobject -Property $_}).user | sort -Unique
    $a = new-object -comobject wscript.shell
    $intAnswer = $a.popup("Do you want to Log Off (A) certain User(s)?", `
    0,"Logoff Users",4)
    If ($intAnswer -eq 6)
    {
    $answer2 = $Users | Out-GridView -PassThru
    $step = 0
    do
    {
    $logs = $list | where {$_.user -eq $answer2}
    foreach ($log in $logs)
    {
    write-host $log.user -ForegroundColor green -NoNewline
    write-host " logged on to device " -ForegroundColor Yellow -NoNewline
    write-host $log.server -ForegroundColor Green -NoNewline
    Write-Host " since " -ForegroundColor Yellow -NoNewline
    write-host $log.Logontime -ForegroundColor Cyan
    write-host "Attempt log off:" -ForegroundColor Yellow -NoNewline
    $connect = $log.server
    try{Invoke-RDUserLogoff -HostServer $connect -UnifiedSessionId $Log.session -Force ;$logof="OK"}
    catch{$logof="NOK"}
    if($Logof -eq "OK"){ write-host " was logged off!" -ForegroundColor Green}
    else { write-host " wasn't logged off!" -ForegroundColor red; write-host "Please check manually." -ForegroundColor red}

    }
    $step++
    }
    while ($step -lt $answer2.count)
    }
    }

    # SO now let's play!
    $Servers = Selection
    Displayinfo $servers
    $completelist = DoIt $servers
    yesno2 $completelist

    1+

  7. Koen 1 year ago

    6+

  8. Peter Velinov 1 year ago

    Koen, thank you! This script is really ... really very useful!

    1+

  9. Danny 8 months ago

    Koen,

    How would I edit this to search through all computers on an AD Domain?

    Many Thanks

    0

  10. Koen 8 months ago

    Danny,

    if you really want all AD-devices, then alter line 30 to:

    $servers = (get-adcomputer -Filter * -Property name | sort-object Name).name

    grts

    Koen

    1+

    Users who have LIKED this comment:

    • avatar
  11. Eric O 7 months ago

    Thank you @Adam B for this post! Very Helpful!

    0

  12. Derli Dias Campos Junior 5 months ago

    This piece of code is exactly what I need, it just won't work if the user is not connected, the catch part isn't working.

    0

    • Luc Fullenwarth 5 months ago

      @Derli Dias Campos Junior

      Try this in the catch section:

      0

  13. Bob 4 months ago

    For Alfred and Keon's scripts I get the below error:

    [%servername%] Connecting to remote server l%servername% failed with the following error
    message : The client cannot connect to the destination specified in the request. Verify that the service on the
    destination is running and is accepting requests. Consult the logs and documentation for the WS-Management service
    running on the destination, most commonly IIS or WinRM. If the destination is the WinRM service, run the following
    command on the destination to analyze and configure the WinRM service: "winrm quickconfig". For more information, see
    the about_Remote_Troubleshooting Help topic.
        + CategoryInfo          : OpenError: (%servername%:String) [], PSRemotingTransportException
        + FullyQualifiedErrorId : CannotConnect,PSSessionStateBroken

    0

    • Luc Fullenwarth 4 months ago

      @Bob

      Please try to see if port 5985 is reachable on the remote computer.

      If your local workstation runs on Windows 8 or above:

      If your local workstation runs Windows 7

      Install the Telnet Client feature and run this:

       

      0

Leave a reply

Your email address will not be published. Required fields are marked *

*

© 4sysops 2006 - 2019

CONTACT US

Please ask IT administration questions in the forums. Any other messages are welcome.

Sending

Log in with your credentials

or    

Forgot your details?

Create Account