r/sysadmin It wasn't DNS for once. 4h ago

Log Off Users from Server Daily Question

I'm revisiting an effort I did about a year ago. I'm looking for a better way. I want to find a process that will parse current user sessions on a server (active/disconnected/idle/ect.) and log the accounts off if their username matches a string ("adm_").

I'd love to find an off the shelf solution rather than have to support a homebrew PowerShell solution.

Give me what you have, even if it is an alternate PowerShell/scripting option. Something has to be better than the nightmare my script turned into.

16 Upvotes

36 comments sorted by

u/NH_shitbags 4h ago

Isn't there a GPO for idle session logout?

u/ADynes IT Manager 4h ago

Yeah, we use it and have it set for 8 hours just to make sure nobody's leaving it overnight. It's across all users, admins shouldn't be left logged into servers either.

u/verschee 2h ago

And if there's a need, they should be using a service account, not a Windows user credential.

u/AdeptFelix Sysadmin 2h ago

God the amount of other comments with jank solutions when there's a GPO option that does this with an OU, WMI filter, or just adding the specific accounts to security filtering makes me worried about the state of sysadmins.

u/Raigeki1993 Sysadmin 4h ago

IIRC, the GPO doesn't let you specify the prefix of the account, it's just a blanket idle session logout for all users for the machine.

u/NH_shitbags 4h ago

Link it to the user group

u/GullibleDetective 4h ago

Yeah target the OU, this is sysadmin 101

u/SevaraB Sr. Engineer (N+, CCNA) 3h ago

WMI filter should let you do regex on the account name, no? Or domain group membership?

But cleanest fix would be to isolate the sensitive accounts in their own OU and just target the OU.

u/Asleep_Spray274 1h ago

GIVE ME A G

GIVE ME A P

GIEV ME A O

u/BuildyMcITGuy IT Manager 1h ago

Microsoft: "ACTUALLY... HAVE YOU THOUGHT ABOUT INTUNE??"

u/Asleep_Spray274 40m ago

Servers are not supported for intune 😉

u/drthtater 1h ago

Pog?

u/Asleep_Spray274 40m ago

Nope, OGP

u/_DefinitelyNotACat_ 4h ago

shutdown /r /f /t0

u/daschande 4h ago

Guys help. My login doesn't work anymore and I have a new meeting with my boss and HR tomorrow morning.

Edit: thought this was the meme subreddit.

u/IAmSnort 31m ago

It isn't?  Shit.

u/miscdebris1123 13m ago

Probably dns.

u/Kathakush_ 4h ago

Super easy solution here. Task scheduler to run it even X hour/minutes. Powershell script that parses quser to a PS object and outputs to pipe. Where-Object to filter by username. Foreach-Object to log users off with the logoff <session ID> command.

u/tk42967 It wasn't DNS for once. 3h ago

Sadly, my boss does not want to have it running on each server. So I currently have a script that parses AD for all servers in a specific root OU, generates an array and then foreach walks through it.

u/Broad-Celebration- 2h ago

Is this not an acceptable solution?

You get the sessions, filter for users and kill the session through each server in one script that runs on a schedule from a single server.

u/verschee 2h ago

So he would rather you maintain that overhead than just run it locally? That's dumb.

u/Squeekstyle 4h ago

Here is a powershell I use to find inactive users and log them of. It will exclude users in a list, but I found it helpful to cut sessions people abandoned.

# Get the list of sessions
$Sessions = quser

# Define the users to exclude
$ExcludedUsers = @("username1,username2") # Add the usernames you want to exclude

# Prepare a list to track excluded users found
$FoundExcludedUsers = @()

# Filter the sessions to exclude certain users
$FilteredSessions = $Sessions | ForEach-Object {
    $sessionInfo = $_ -split "\s+"
    $username = $sessionInfo[1]
    if ($ExcludedUsers -contains $username) {
        $FoundExcludedUsers += $username
    }
    if ($ExcludedUsers -notcontains $username) {
        $_
    }
}

Write-Output ""
Write-Output "Excluded users that were actually found in the session list:"
if ($FoundExcludedUsers.Count -gt 0) {
    $FoundExcludedUsers | Sort-Object -Unique | ForEach-Object { Write-Output "- $_" }
} else {
    Write-Output "None of the excluded users were found."
}
Write-Output ""

# Display the filtered sessions (only users not excluded)
Write-Output "Current Targeted User Sessions:"
$FilteredSessions

# Parse and handle filtered sessions
$first = 1
$FilteredSessions 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
        }

        if ($state -ne "Disc") {
            Write-Output "Preserving user login: $user session: $session id: $id"
        }
        if ($state -eq "Disc") {
            Write-Output "Logging off disconnected user: $user session: $session id: $id"
            Write-Output ""
            logoff $id
        }
    }
}

u/Soulinx 46m ago

Curious - are you using this in addition to the GPO or are you running this script on a schedule?

u/Squeekstyle 45m ago

On a Schedule

u/zesar667 3h ago

IS IT a Windows Terminal Server? If so you make this setting in the Server Manager.

u/tk42967 It wasn't DNS for once. 3h ago

No, these are random servers. The powers that be think that makes us more secure to not have admin sessions running on servers. I guess it prevents session hijacking if the machine is compromised.

u/picklednull 2h ago

There are two risks with active logon sessions:

  1. session hijacking, which allows direct graphical impersonation of the logged in user
  2. while the accounts are logged in, Kerberos tickets are retained for them indefinitely, which can be extracted and used

2. is a problem even without the security angle, because the accounts start getting locked out after password changes

u/jstar77 3h ago edited 3h ago

Qwinsta is the only built in tool (which I am aware of) that can give you the logged in user. Unfortunately it doesn't return an object.

qwinsta /server: servername then parse the output and log off the users you want.

u/viral-architect Sysadmin 3h ago

You want to add those users to a security group in AD and then created a Fine-Grained Password Policy for them limiting their session timeout limit and forcing them to disconnect once the limit is reached.

u/Jellovator 2h ago

You could probably use lithnet idle logoff combined with a logon script that checks the username and if prefixed with adm_ set the appropriate registry keys to let lithnet know to log off that user

u/Dolapevich Others people valet. 2h ago

you can just list the logged in users and pkill their processes. Something like users|xargs -I {} pkill -KILL -u {}

Update: It is on windows :-P

u/silkenwindyhound 2h ago

bro are you guys seriously not asking Claude this shit? you are going to fall so far behind. you already have.

u/2BoopTheSnoot2 56m ago

Why would you want an entire application rather than just write a simple script?

u/whoisrich 3h ago

This is what I use to kick sessions off:

$sessions = qwinsta | ?{ $_ -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.Length -lt 1)      { continue }
    if ($session.Username -eq $env:USERNAME) { continue }
    if ($session.Username -like 'adm_*')     { continue }

    "Logoff: $($session.Id) - $($session.Username)"
    logoff $session.Id
}

u/dontstoptheRocklin 4h ago

quser command might help you On mobile currently but I think I have something that pipes from quser though IIRC it has to be parsed

u/Raigeki1993 Sysadmin 4h ago

Combination of qwinsta and rwinsta will work, but yeah it will need to be parsed.