GreenGarden
GreenGarden

Reputation: 33

How do I log off all currently logged on users?

I would like to create a GPO that includes a scheduled task, that should be run once every day. The scheduled task should get a list of all users currently logged on a specific workstation and log them off (just lock Windows would be prefered, but a regular log off seems easier to make)

First I tried to execute the following with the scheduled task: "shutdown.exe /f /l" to log off users. But the command was being executed by the SYSTEM user, so all regular users was not being logged off.

2nd idea was then to create a Powershell Script and place on each workstation. The script should be able to extract all currently logged on users on the workstation and log off / lock screen for all users.

$Sessions = quser

# Parse the session IDs from the output
$SessionIds = ($Sessions -split ' +')[3]
$SessionIds

# Loop through each session ID and pass each to the logoff command
ForEach-Object ($SessionId in $SessionIds){
    logoff $SessionId
    } 

Upvotes: 1

Views: 29327

Answers (4)

itocdw
itocdw

Reputation: 1

I had to change the -5 to -6 based on @AnsgarWiecher's answer for Windows 10.

quser | Select-Object -Skip 1 | ForEach-Object {
    $id = ($_ -split ' +')[-6]
    logoff $id
}

Thank you Ansgar.

Upvotes: 0

js2010
js2010

Reputation: 27423

Might as well turn this into a parser.

# q.ps1

$first = 1
quser 2>$null | ForEach-Object {
    if ($first -eq 1) {
        $userPos = $_.IndexOf("USERNAME")
        $sessionPos = $_.IndexOf("SESSIONNAME")
        $idPos = $_.IndexOf("ID") - 2   # ID is right justified
        $statePos = $_.IndexOf("STATE")
        $idlePos = $_.IndexOf("IDLE TIME")
        $logonPos = $_.IndexOf("LOGON TIME")
        $first = 0
    }
    else {
        $user = $_.substring($userPos,$sessionPos-$userPos).Trim()
        $session = $_.substring($sessionPos,$idPos-$sessionPos).Trim()
        $id = [int]$_.substring($idPos,$statePos-$idPos).Trim()
        $state = $_.substring($statePos,$idlePos-$statePos).Trim()
        $idle = $_.substring($idlePos,$logonPos-$idlePos).Trim()
        $logon = [datetime]$_.substring($logonPos,$_.length-$logonPos).Trim()
        [pscustomobject]@{User = $user; Session = $session; ID = $id; 
            State = $state; Idle = $idle; Logon = $logon}
    }
}
.\q | ft

User  Session  ID State  Idle Logon
----  -------  -- -----  ---- -----
admin console 111 Active none 2/6/2022 11:21:00 PM
.\q | % { logoff $_.id }

Upvotes: 1

Randy Groom
Randy Groom

Reputation: 76

Neither of the above solutions worked for me for all cases because of the variation in blank fields and time formatting. The following worked better.

$first = 1
quser | ForEach-Object {
    if ($first -eq 1) {
        $userPos = $_.IndexOf("USERNAME")
        $sessionPos = $_.IndexOf("SESSIONNAME")
        $idPos = $_.IndexOf("ID")
        $statePos = $_.IndexOf("STATE")
        $first = 0
    }
    else {
        $user = $_.substring($userPos,$sessionPos-$userPos).Trim()
        $session = $_.substring($sessionPos,$idPos-$sessionPos).Trim()
        $id = $_.substring($idPos,$statePos-$idPos).Trim()
        Write-Output "Logging off user:$user session:$session id:$id"
        logoff $id
    }
}

Upvotes: 3

Ansgar Wiechers
Ansgar Wiechers

Reputation: 200273

The expression ($Sessions -split ' +')[3] gives you the 4th element out of all elements produced by the split. What you actually want is the 4th element from each split line. Also, you should index from the end of the line, because otherwise you'd miss disconnected sessions, as those don't have a session name:

PS C:\> quser
 USERNAME              SESSIONNAME        ID  STATE   IDLE TIME  LOGON TIME
 foo                                       7  Disc         1:23  23.10.2019 09:12
>bar                   rdp-tcp#114        12  Active          .  23.10.2019 10:12
 baz                   rdp-tcp#112        13  Active          .  23.10.2019 10:13

Something like this should work:

quser | Select-Object -Skip 1 | ForEach-Object {
    $id = ($_ -split ' +')[-5]
    logoff $id
}

You could also use qwinsta instead of quser, but then you should filter out the listener as well as the "services" (and maybe the "console") session before terminating sessions.

Upvotes: 1

Related Questions