How to force a user logoff remotely with PowerShell

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.

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!

Your question was not answered? Ask in the forum!

  1. commotion 4 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



    • German Pulido 2 years 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").


  2. Alfred Kuhnert 3 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:


  3. Dan 2 years ago

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


  4. Greg Shelton 2 years 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..


  5. 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 (, it has multiple options such as tslogoff * /server:<servername> to logoff everyone, or tslogoff /disc /server:<servername> to logoff disconnected sessions, etc.

    David F.


  6. Koen 2 years 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
    $a = new-object -comobject
    $popup = "Do you want to Select the servers to scan ?"
    $intAnswer = $a.popup($popup,
    If ($intAnswer -eq 6) {
    $answer = "yes"
    } else {
    $answer = $null
    return $Answer
    Function DisplayInfo
    $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
    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
    $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
    #$ErrorActionPreference = "SilentlyContinue"
    $SessionIds = quser /server:$server 2>&1
    #$ErrorActionPreference = "Continue"
    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
    $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
    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
    $List = $completelist |% {New-Object psobject -Property $_}
    $Users = ($completelist |% {New-Object psobject -Property $_}).user | sort -Unique
    $a = new-object -comobject
    $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
    $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"}
    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}

    while ($step -lt $answer2.count)

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


  7. Koen 2 years ago


  8. Peter Velinov 2 years ago

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


  9. Danny 2 years ago


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

    Many Thanks


  10. Koen 2 years ago


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

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



  11. Eric O 2 years ago

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


  12. Derli Dias Campos Junior 2 years 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.


    • @Derli Dias Campos Junior

      Try this in the catch section:


  13. Bob 2 years 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


    • @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:



  14. Mohamed 11 months ago

    @ Koen  It really very useful script,Thank you very much for it

    I tried to use it for all AD computers but it shows "[5]:Access - is - denied." when Checking server,However it is working fine with servers 

    my user is domain admins,it there any thing to do from my side to fix this issue.Last thank you again for this fantastic script


  15. Jeremy 7 months ago

    I commonly need to remove all other folks on a machine, so this is what I created for it. I'm posting it here since I haven't seen something that deals with quser's nullable session name. I've found that only that column is nullable, so you can count the number of populated fields to determine where the session ID is:


  16. Logan 6 months ago

    The post below seems to be a replication of this work, Adam. It doesn't appear to give you credit for it. I am uncertain if this is authorized or not, but thought I would mention it.


  17. Jasper 2 months ago

    If you are running an RDS farm environment, the simple thing to do is combine the Get-RDUserSession cmdlet (which returns the UnifiedSessionId) with Invoke-RDUserLogoff.


    If there is no farm, you can also take the quser output and pipe it to ConvertFrom-String to have a simple object to work with. You can avoid regex this way.


Leave a reply

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


© 4sysops 2006 - 2020


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


Log in with your credentials


Forgot your details?

Create Account