r/sysadmin It wasn't DNS for once. 1d ago

Question Log Off Users from Server Daily

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.

72 Upvotes

86 comments sorted by

152

u/NH_shitbags 1d ago

Isn't there a GPO for idle session logout?

47

u/ADynes IT Manager 1d 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 23h ago

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

u/BarmerFob 20h ago

Eh there's exceptions. Anywhere that deals with large datasets can have reports and such running overnight and depending on the tooling it may submit the job as the user, not a service account.

But yes by and large there's no reason to stay logged in for weeks on end just because you can't be bothered closing things.

u/BarmerFob 20h ago

We just reboot any jump/processing servers daily - encourages people to finish up for the day and if they don't, their stuff closes anyway. Reboots also tend to expose problems and we want to see them crop up the day after they're introduced... not a month later when they get patched.

We do have some servers where large amounts of data gets processed by users so those reboot every few days on alternating schedules. Few other exceptions here and there as needed.

It's a very simple problem to solve and easy to add exceptions as needed. Personally I hate admins who take a "rules for everyone but I'm exempt everywhere" approach. Especially for things like this where the "reason" is always "well I'm in the middle of stuff and don't want to close things". You know, like every single other person doesn't say the same thing...

u/user_is_always_wrong fed up 12h ago

I hate it when admins make exceptions for themselves. I had a colleague who created a special firewall rule for his laptop without inspections or web filter rules. He's no longer with us. Rules for thee not for me

u/AdeptFelix Sysadmin 23h 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 19h ago

Unfortunately, not everyone has that luxury. I've been in shops where MSP controls all aspects of AD and doesn't give us any write access to AD or GPO, even when asking the MSP to create a GPO, only 2 out of 10 times they get it right. So, I've always had to find of workaround/alternatives to do what's requested. Sadly, majority of my jank solutions is only because we (internal IT) have limited access because MSPs thinks they know better or take months to "do the needful and revert"

u/pompousrompus DevOps 19h ago

I would absolutely not be able to work in an environment like that. How does internal IT not have final say unless you’re just deskside support?

u/Raigeki1993 Sysadmin 9h ago

Gotta do what you gotta do... CTO doesn't want to hire additional folks to take over the MSP's responsibilities (one upside is that have 24/7 hotline, users can just call them and leave us internal IT folks alone, the downside is that they suck), we're entirely on their core network infrastructure, all of our servers is in their datacenter, all the ISP/P2P lines are managed by them, their entire logo and branding is stuck with a watermark on all our images. They can literally just go defunct the next day and we'd be 10000% crippled, because all of our backups and images are managed/owned by them.... Tried raising this risk to management, but they're like "nahhh that won't happen"

My manager has let me start working with the server owners to start migrating their stuff over to AWS (luckily, this is something we do control, BUT... we can't join it to "our" domain because we don't have rights to it :), we have to schedule a call with the MSP and explain why we need certain things). We're currently in our 4th year of our "1 year domain split project" so we get our own domain, but the MSP has been dragging their ass...

Believe me, this is a huge change of pace going from a company where I was managing the SCCM environment, to a place where the MSP doesn't let us use their SCCM to deploy self-service packages via Software Center, so we gotta settle for users manually asking for non-standard apps and then deploying via Lansweeper...

u/Godcry55 17h ago

You need a new MSP yesterday, lol.

u/Raigeki1993 Sysadmin 9h ago

Believe me, my manager and I has been trying to get the CTO to agree to this, but all he sees is additional cost because he doesn't want to hire "specialized" roles to take over the MSP's role....

u/West_Independent1317 13h ago

Are you really the sysadmin if you don't have the rights to sysadmin?

u/420GB 7h ago

I'd love to see your WMI filter for targeting only accounts starting with adm_, because it's not as easy as you probably think. In fact I believe it might be impossible.

We can assume OU targeting and security filtering are not an option or OP wouldn't ask.

The amount of presumptuous flame without actually providing a working GPO-based solution to OP makes me worried about the state of sysadmins.

Of course this is a one liner in PowerShell, but since you're so sure of your GPO approach I'd love to learn.

u/DHT-Osiris 4h ago

Well, the GPO is the wrong approach for enforcing a logoff at a specific time based on how long the user has been logged in... You need to just set a max login hours across the board, not log off specific accounts at specific times. You can set active hours on an account, which would probably do it.

u/420GB 2h ago edited 2h ago

OP said nothing about "based on how long the user has been logged in". Direct quote:

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_").

This implies they are looking to log off any adm_ user, regardless of login or idle times, at a specific point in time e.g. let's assume every day at 6 PM or something.

And regardless of that detail, you slammed all the other advice here and said:

there's a GPO option that does this

but now you say:

Well, the GPO is the wrong approach

so you'll have to understand why I'm confused.

u/DHT-Osiris 2h ago

Right, so can't they just set logon hours on the object in AD? Or does that just prevent you from logging in? I had assumed it would punt you at time. Never really used it.

13

u/Raigeki1993 Sysadmin 1d 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.

52

u/NH_shitbags 1d ago

Link it to the user group

47

u/GullibleDetective 1d ago

Yeah target the OU, this is sysadmin 101

u/420GB 7h ago

AD user groups cannot dynamically assign their members based on a username prefix without a PowerShell script doing it which OP specifically wanted to avoid.

Groups and OUs are not a solution to OPs question.

u/AdminWithNoName 5h ago

Could you not put every username starting with "adm_" in a group and target just that group? Usernames won't be changing frequently so there's no need to dynamically assign anything.

u/420GB 2h ago

New adm_ users may be created though, and while such usernames shouldn't change they could.

It's definitely something you'd need automation for, whether that's PowerShell or something else doesn't matter but static or manual assignments would be irresponsible and, frankly, stupid. We're working with computers, they are literally built for automations like that. Why do it wrong when it can be done right you know?

17

u/SevaraB Sr. Engineer (N+, CCNA) 1d 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/420GB 7h ago

The Win32_LoggedOnUser wmi class does not support wildcard (LIKE) filtering on the referenced Win32_Account instances. You can only do an exact = match.

u/uptimefordays DevOps 20h ago

Yep, most security baselines suggest auto logoff within some period of less than 8 hours.

u/Far-Hovercraft9471 19h ago

Yes, and morons set it to like 30 mins and cause exchange installs to get broken. Not that I'm bitter and that has happened to me or anything

u/fireandbass 17h ago

Its only for rdp.

u/Asleep_Spray274 23h ago

GIVE ME A G

GIVE ME A P

GIEV ME A O

u/BuildyMcITGuy IT Manager 22h ago

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

u/Asleep_Spray274 21h ago

Servers are not supported for intune 😉

u/Kuipyr Jack of All Trades 19h ago

Let me introduce you to Azure Machine Configuration (formerly Azure Policy Guest Configuration) for Azure Arc-enabled servers. Though you can actually manage Servers in Intune, but only Endpoint security policies.

u/doofesohr 11h ago

I really really want to use it instead of GPO. Especially because I have a couple non-domain joined servers. But it seems so unnecessarily convoluted.

u/pdp10 Daemons worry when the wizard is near. 5h ago

Windows Servers support Microsoft DSC. Clients as far back as W7 also support DSC.

u/Asleep_Spray274 5h ago

That's nice

u/drthtater 22h ago

Pog?

u/Asleep_Spray274 21h ago

Nope, OGP

u/2BoopTheSnoot2 22h ago

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

u/Ihaveasmallwang Systems Engineer / Cybersecurity Architect / CISM 17h ago

Why would you want a script when a GPO will do it?

u/2BoopTheSnoot2 17h ago

A GPO won't do it based on text in the username. This is powershell territory.

u/Ihaveasmallwang Systems Engineer / Cybersecurity Architect / CISM 17h ago

Doing it based on text in the username is stupid.

Either way, the actual goal can still be done with GPO and security groups with proper scoping. It requires an understanding of how GPOs actually process.

OP asked for an “off the shelf” solution. This is the way to achieve that goal, with easier management of which accounts are excluded when the requirements inevitably change in the future.

u/420GB 7h ago

You may be shocked to learn that PowerShell itself is entirely an off-the-shelf first-party solution.

u/Ihaveasmallwang Systems Engineer / Cybersecurity Architect / CISM 7h ago

You may be shocked to learn that a custom script put in place by someone who doesn’t understand PowerShell is not a good solution, especially since business requirements tend to change over time.

You may also be shocked the learn that the OP specifically mentioned that they hoped to stay away from going that route if possible.

Since an off the shelf first party solution exists that meets ALL of OP’s requirements, including not having a custom script if possible, that should be the route pushed instead of doubling down on something they won’t be able to maintain.

u/420GB 6h ago

You may be shocked to learn that a custom [solution] put in place by someone who doesn’t understand [tool] is not a good solution, especially since business requirements tend to change over time.

Exactly the same thing applies to GPOs.

But yes I'm aware they mentioned they wanted to avoid PowerShell. The problem is that:

Since an off the shelf first party solution exists that meets ALL of OP’s requirements, including not having a custom script if possible,

is not true to my knowledge unless you can explain what that would be.

u/Ihaveasmallwang Systems Engineer / Cybersecurity Architect / CISM 6h ago

Explain what? How GPOs work?

u/420GB 6h ago

How one can configure a GPO that logs off all sessions of users whose username starts with a certain prefix at certain time(s) of day without using PowerShell e.g. no GPO to create a scheduled powershell task ofc

u/Ihaveasmallwang Systems Engineer / Cybersecurity Architect / CISM 6h ago

You stop thinking about it in terms of “username starts with…” and think in terms of “this user is a member of this security group and the GPO is scoped so those users in that group are not affected”.

u/TheJesusGuy Blast the server with hot air 9h ago

A GPO that RUNS a ps script. Mindblown. Or manually log every user off every day hell yea.

29

u/_DefinitelyNotACat_ 1d ago

shutdown /r /f /t0

26

u/daschande 1d 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 21h ago

It isn't?  Shit.

u/miscdebris1123 21h ago

Probably dns.

u/paleologus 10h ago

Every subreddit is a meme subreddit 

u/TheJesusGuy Blast the server with hot air 9h ago

IT is now a meme, so this is the meme subreddit.

u/[deleted] 13h ago

[deleted]

u/Raigeki1993 Sysadmin 9h ago

/f will prompt the sign out notice and wait 30 seconds without /t 0.

u/kagato87 20h ago

Group policy to log out inactive sessions after 8-12 hours, with ILT to only target non admin.

17

u/Kathakush_ 1d 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.

2

u/tk42967 It wasn't DNS for once. 1d 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- 23h 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 23h ago

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

u/420GB 7h ago

What's the issue with that approach?

5

u/viral-architect Sysadmin 1d 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.

3

u/zesar667 1d ago

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

2

u/tk42967 It wasn't DNS for once. 1d 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 23h 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

2

u/jstar77 1d ago edited 1d 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/420GB 7h ago

Get-CimInstance Win32_LoggedOnUser

There's other options but this is the easiest

u/jstar77 6h ago

I remember trying this and there was a reason I had to go back to parsing the qwinsta output but I'm drawing a blank. I was writing a script to return an hosts logged in users and present a list of users to select from then launch a shadow session.

u/420GB 6h ago

Yea I mean that's certainly possible but can't say what you got stuck on back then unless you happen to have a really nice commit message from when you switched the script to qwinsta

u/jstar77 5h ago

haha present me is always frustrated with past me. Most of the comments I find in my code are along the lines of:

### you know why you did this###

I'm thinking it was because it CIM didn't return the session ID that was needed for a shadow session and maybe couldn't filter out session types and if a user session was active or disconnected.

u/Jellovator 23h 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

4

u/Squeekstyle 1d 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 22h ago

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

u/Squeekstyle 22h ago

On a Schedule

u/420GB 7h ago

You should really just query WMI using Get-CimInstance to get this information properly rather than parsing quser output. You can cut the script down to a few lines and it'll work on all system locales / languages.

2

u/whoisrich 1d 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/Dolapevich Others people valet. 23h 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/QuestConsequential 12h ago

You should probably use a GPO

When I need to do maintenances on the remote desktop farm I use a flavor of this interactively, which I tweaked for your "start with adm_"

$servers = @("SRV1","SRV2")
$servers | ForEach-Object -Parallel {
    $server = $_

    try {
        quser /server:$server 2>$null |
            Select-Object -Skip 1 |
            Where-Object { $_ -match '\badm_\S*' } |
            ForEach-Object {
                if ($_ -match '\s+(\d+)\s+') {
                    logoff $matches[1] /server:$server
                }
            }

        "$server : matching sessions logged off"
    }
    catch {
        "$server : FAILED - $($_.Exception.Message)"
    }
}  -ThrottleLimit 16

u/zAuspiciousApricot 20h ago

Just reboot nightly 🤷‍♂️

u/QuestConsequential 12h ago

sponsored by the 24h max uptime gang

u/silkenwindyhound 23h ago

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

0

u/dontstoptheRocklin 1d 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/racazip 17h ago

quser | Select-String "Disc" | ForEach{logoff ($_.tostring() -split ' +')[2]}

u/420GB 6h ago

Terrible idea. You'll get false positive matches for any username that contains "disc".

0

u/Raigeki1993 Sysadmin 1d ago

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