GoldenWest
GoldenWest

Reputation: 281

Log Off multiple Idle users

$idle_timout = New-TimeSpan -minutes 1
Add-Type @'
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace PInvoke.Win32 {

    public static class UserInput {

        [DllImport("user32.dll", SetLastError=false)]
        private static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);

        [StructLayout(LayoutKind.Sequential)]
        private struct LASTINPUTINFO {
            public uint cbSize;
            public int dwTime;
        }

        public static DateTime LastInput {
            get {
                DateTime bootTime = DateTime.UtcNow.AddMilliseconds(-Environment.TickCount);
                DateTime lastInput = bootTime.AddMilliseconds(LastInputTicks);
                return lastInput;
            }
        }

        public static TimeSpan IdleTime {
            get {
                return DateTime.UtcNow.Subtract(LastInput);
            }
        }

        public static int LastInputTicks {
            get {
                LASTINPUTINFO lii = new LASTINPUTINFO();
                lii.cbSize = (uint)Marshal.SizeOf(typeof(LASTINPUTINFO));
                GetLastInputInfo(ref lii);
                return lii.dwTime;
            }
        }
    }
}
'@

$userId = (Get-Process -PID $pid).SessionID
echo $userID
$loggedOff = 0

foreach ($user in $userid){
   do
{
    $idle_time = [PInvoke.Win32.UserInput]::IdleTime

    if (($loggedOff -eq 0) -And ($idle_time -gt $idle_timout))
    {
        logoff $user

        $loggedOff = 1
    }

    if ($idle_time -lt $idle_timout)
    {
        $loggedOff = 0
    }
}
while (1 -eq 1)
 
}

What I am trying to do its log off all users in our conference rooms after a given idle time. What I am trying to do is find all session id's and log off all active sessions. I am not worried about losing work as these are conference room computers. The problem I have is that I can get the session id for the current logged in user but not for all the users logged in. If anyone has any insight it would be greatly appreciated.

Upvotes: 3

Views: 5994

Answers (5)

Matti
Matti

Reputation: 53

I realize that I'm several years late on this. However, I recently worked on a similar script.

That being said, your original PowerShell Script would have worked just fine, if you simply got rid of the Foreach Loop, thereby formatting it as follows, and ran it as a User Logon/Startup Script (so that it runs under the User Context).

$idle_timout = New-TimeSpan -minutes 1
Add-Type @'
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

namespace PInvoke.Win32 {

    public static class UserInput {

        [DllImport("user32.dll", SetLastError=false)]
        private static extern bool GetLastInputInfo(ref LASTINPUTINFO plii);

    [StructLayout(LayoutKind.Sequential)]
    private struct LASTINPUTINFO {
        public uint cbSize;
        public int dwTime;
    }
    
    public static DateTime LastInput {
        get {
                DateTime bootTime = DateTime.UtcNow.AddMilliseconds(-Environment.TickCount);
                DateTime lastInput = bootTime.AddMilliseconds(LastInputTicks);
                return lastInput;
            }
        }

        public static TimeSpan IdleTime {
            get {
            return DateTime.UtcNow.Subtract(LastInput);
        }
    }

    public static int LastInputTicks {
        get {
            LASTINPUTINFO lii = new LASTINPUTINFO();
            lii.cbSize = (uint)Marshal.SizeOf(typeof(LASTINPUTINFO));
            GetLastInputInfo(ref lii);
            return lii.dwTime;
            }
        }
    }
}
'@

$loggedOff = 0

do
{
    $idle_time = [PInvoke.Win32.UserInput]::IdleTime

    if (($loggedOff -eq 0) -And ($idle_time -gt $idle_timout))
    {
        logoff

        $loggedOff = 1
    }

    if ($idle_time -lt $idle_timout)
    {
        $loggedOff = 0
    }
}
while ($LoggedOff -eq 0)

EXIT 0

Upvotes: 0

GoldenWest
GoldenWest

Reputation: 281

The script I used to achieve this can be found here https://github.com/NebulaCyberSolutions/PowershellScripts/blob/master/logoffdual.ps1. I paired this with a scheduled task that ran at any logon event that would auto log off the oldest user logged in. Effectively only allowing one login at a time to conference room pc's

Upvotes: 0

mklement0
mklement0

Reputation: 437688

Building on Anthony Stringer's helpful answer:

To get an array of all session names associated with users:

$userSessionNames = query user | Select-Object -Skip 1 |  % { (-split $_)[2] }

((Get-Process).SessionId | Sort-Object -Unique would give you all distinct session IDs, but that would include sessions not associated with users. That said, if non-user sessions - such as 0 for services and 65537 for rdp-tcp - are all well-known, they could be filtered out.)

Note, however, that your foreach ($user in $userid) loop (where $user really refers to a user session):

  • Runs in a tight loop, as it runs continuously without yielding CPU time.
  • Only ever checks the calling session's idle time, because "GetLastInputInfo does not provide system-wide user input information across all running sessions. Rather, GetLastInputInfo provides session-specific user input information for only the session that invoked the function." - see https://msdn.microsoft.com/en-us/library/windows/desktop/ms646302(v=vs.85).aspx

Upvotes: 1

DisplayName
DisplayName

Reputation: 1016

you can better do it with remote desktop session host configuration idle time limit to your desired time. enter image description here

Upvotes: 2

Anthony Stringer
Anthony Stringer

Reputation: 2001

here are some options that may be of use

powershell way

$computer = 'localhost'
$owners = @{}
Get-WmiObject win32_process -ComputerName $computer -Filter 'name = "explorer.exe"' | % {$owners[$_.handle] = $_.getowner().user}
Get-Process -ComputerName $computer explorer | % {$owners[$_.id.tostring()]}

or these

$server = 'localhost'

(quser /server:$server) -replace '\s{2,}', ',' | ConvertFrom-Csv

# IDENTICAL
query session /server:$server
qwinsta /server:$server

# IDENTICAL
query user /server:$server
quser /server:$server

qprocess explorer.exe /server:$server

Upvotes: 3

Related Questions