Rilevare il tempo di inattività di un utente o di un computer è un aspetto critico per i professionisti IT, soprattutto in un momento storico in cui sono aumentate le minacce alla sicurezza informatica e si è intensificata la necessità di una corretta gestione delle risorse. Con la rapida digitalizzazione delle organizzazioni e con utenti spesso connessi per periodi prolungati, rilevare il tempo di inattività di un utente diventa fondamentale sia per motivi di sicurezza che operativi. In questo post analizzeremo uno script PowerShell progettato per rilevare il tempo di inattività più lungo degli utenti, e che permette di rendere questo compito fluido ed efficiente.
Background
Rilevare il tempo di inattività di un utente in modo preciso può fornire informazioni preziose ai professionisti IT e ai fornitori di servizi gestiti (MSP). Che si tratti di liberare risorse, di garantire che le sessioni non rimangano aperte a potenziali minacce alla sicurezza o anche di fare considerazioni sulla fatturazione per i fornitori di servizi cloud, disporre di un metodo efficace e affidabile per rilevare il tempo di inattività di un utente diventa essenziale. Questo particolare script sfrutta l’abilità di PowerShell di interagire direttamente con il sistema operativo Windows per estrarre i dati utili a rilevare il tempo di inattività di un utente.
Lo script per rilevare il tempo di inattività di un utente
#Requires -Version 5.1 <# .SYNOPSIS Returns the longest idle time of any user logged in or for a specific user. .DESCRIPTION Returns the longest idle time of any user logged in or for a specific user. If RDS(Remote Desktop Services) is installed and the RSAT tools for it as well, then this will get the idle time of each logged in user. For workstations and servers(with out RDS installed), this will get the current idle of the currently logged in user. If a user is logged in via the console and another is via the admin RDP session, then both will be considered as one user for calculating idle time. .EXAMPLE No parameters needed. Returns the longest idle time of all users logged in. .EXAMPLE -UserName "Fred" Returns the longest idle time of the user Fred. .EXAMPLE PS C:> Get-User-Idle-Time.ps1 -UserName "Fred" Returns the longest idle time of the user Fred. .OUTPUTS PSCustomObject[] .NOTES Minimum OS Architecture Supported: Windows 10, Windows Server 2016 Release Notes: Adds functions to get idle time from RDS and non-RDS computers. By using this script, you indicate your acceptance of the following legal terms as well as our Terms of Use at https://www.ninjaone.com/terms-of-use. Ownership Rights: NinjaOne owns and will continue to own all right, title, and interest in and to the script (including the copyright). NinjaOne is giving you a limited license to use the script in accordance with these legal terms. Use Limitation: You may only use the script for your legitimate personal or internal business purposes, and you may not share the script with another party. Republication Prohibition: Under no circumstances are you permitted to re-publish the script in any script library or website belonging to or under the control of any other software provider. Warranty Disclaimer: The script is provided “as is” and “as available”, without warranty of any kind. NinjaOne makes no promise or guarantee that the script will be free from defects or that it will meet your specific needs or expectations. Assumption of Risk: Your use of the script is at your own risk. You acknowledge that there are certain inherent risks in using the script, and you understand and assume each of those risks. Waiver and Release: You will not hold NinjaOne responsible for any adverse or unintended consequences resulting from your use of the script, and you waive any legal or equitable rights or remedies you may have against NinjaOne relating to your use of the script. EULA: If you are a NinjaOne customer, your use of the script is subject to the End User License Agreement applicable to you (EULA). .COMPONENT ManageUsers #> [CmdletBinding()] param ( # Specify one user on a Terminal Services Server, else leave blank for normal servers and workstations [Parameter(Mandatory = $false)] $UserName ) begin { function Test-IsElevated { $id = [System.Security.Principal.WindowsIdentity]::GetCurrent() $p = New-Object System.Security.Principal.WindowsPrincipal($id) if ($p.IsInRole([System.Security.Principal.WindowsBuiltInRole]::Administrator)) { Write-Output $true } else { Write-Output $false } } Function Get-QueryUser() { Param() $Result = @() # Replaces all occurrences of 2 or more spaces in a row with a single comma $Lines = @(query.exe user).foreach({ $(($_) -replace ('s{2,}', ',')) }) if ($Lines.Count -gt 1) { $Header = $($Lines[0].split(',').trim()) for ($i = 1; $i -lt $($Lines.Count); $i++) { $Res = "" | Select-Object $Header $Line = $($Lines[$i].split(',')).foreach({ $_.trim().trim('>') }) # Accounts for disconnected users if ($Line.count -eq 5) { $Line = @($Line[0], "$($null)", $Line[1], $Line[2], $Line[3], $Line[4] ) } for ($j = 0; $j -lt $($Line.count); $j++) { $Res.$($Header[$j]) = $Line[$j] } $Result += $Res Remove-Variable Res } return $Result } else { return $null } } Add-Type @" using System; using System.Runtime.InteropServices; using System.ComponentModel; namespace GetLastUserInput { public class GetLastUserInput { private struct LASTINPUTINFO { public uint cbSize; public uint dwTime; } private static LASTINPUTINFO lastInPutNfo; static GetLastUserInput() { lastInPutNfo = new LASTINPUTINFO(); lastInPutNfo.cbSize = (uint)Marshal.SizeOf(lastInPutNfo); } [DllImport("User32.dll")] private static extern bool GetLastInputInfo(ref LASTINPUTINFO plii); /// <summary> /// Idle time in ticks /// </summary> /// <returns></returns> public static uint GetIdleTickCount() { return ((uint)Environment.TickCount - GetLastInputTime()); } /// <summary> /// Last input time in ticks /// </summary> /// <returns></returns> public static uint GetLastInputTime() { if (!GetLastInputInfo(ref lastInPutNfo)) { throw new Win32Exception(Marshal.GetLastWin32Error()); } return lastInPutNfo.dwTime; } } } "@ } process { if (-not (Test-IsElevated)) { Write-Error -Message "Access Denied. Please run with Administrator privileges." exit 1 } if ($(Get-Module -Name "RemoteDesktop") -and $(Get-RDServer -ErrorAction SilentlyContinue)) { try { $Sessions = Get-RDUserSession $Sessions | Select-Object UserName, IdleTime } catch { Write-Warning -Message "A Remote Desktop Services deployment does not exist on $env:COMPUTERNAME." } } else { Write-Warning -Message "Remote Desktop Services is not installed on this computer, Falling back to query user." $Results = Get-QueryUser if ($null -eq $Results) { Write-Host "No user(s) logged in." exit 0 } # Parse query results and loop through each user $Results | ForEach-Object { $CurrentUser = $_.USERNAME # If UserName param is used, only filter that user; If UserName param isn't used, return all users if ($CurrentUser -like $UserName -or ([string]::IsNullOrEmpty($UserName) -or [string]::IsNullOrWhiteSpace($UserName))) { # Output a PowerShell Custom Object array [PSCustomObject]@{ UserName = $CurrentUser SessionName = $_.SESSIONNAME Id = $_.ID State = $_.STATE LogonTime = $_.'LOGON TIME' IdleTime = if ($_.'IDLE TIME' -like 'none') { 0 }else { $_.'IDLE TIME' } } } } | Sort-Object -Property IdleTime | Select-Object -Property UserName, @{ # Modify IdleTime when it shows none Label = "IdleTime" Expression = { New-TimeSpan -Start $(Get-Date) -End $(Get-Date).AddMilliseconds([GetLastUserInput.GetLastUserInput]::GetIdleTickCount()) } } } } end {}
Accedi a oltre 700 script nel Dojo di NinjaOne
Analisi dettagliata
- Cmdlet Binding e parametri: Lo script per rilevare il tempo di inattività di un utente inizia con un CmdletBinding, che ne consente l’utilizzo come cmdlet. Contiene un parametro opzionale per un nome utente specifico.
- Funzioni interne:
- Test-IsElevated controlla se lo script per rilevare il tempo di inattività di un utente viene eseguito con privilegi amministrativi.
- Get-QueryUser si interfaccia con query.exe per ottenere dettagli sugli utenti attuali.
- Integrazione della libreria esterna: Per ottenere il conteggio esatto dei tempi di inattività si utilizza una libreria esterna (GetLastUserInput).
- Blocco del processo: L’operazione principale viene svolta qui.
- In primo luogo, c’è un controllo per capire se lo script per rilevare il tempo di inattività di un utente viene eseguito con diritti di amministratore.
- Se Remote Desktop Services (RDS) è installato, lo script per rilevare il tempo di inattività di un utente recupera il tempo di inattività per ogni utente collegato.
- Se RDS non è presente, si ricorre all’approccio ”query user” e si calcola il tempo di inattività in base all’ultimo input dell’utente.
Casi d’uso potenziali
Immagina di essere un amministratore IT di un’organizzazione di medie dimensioni. Hai notato che molti utenti lasciano le loro postazioni di lavoro accese e collegate, anche dopo l’orario di lavoro. Questo non solo rappresenta un rischio per la sicurezza, ma anche un consumo di risorse di rete e di energia. Distribuendo questo script per rilevare il tempo di inattività di un utente a livello aziendale, puoi determinare rapidamente quali utenti stanno lavorando attivamente e quali postazioni di lavoro sono rimaste inattive. Grazie a queste informazioni, puoi applicare i criteri di disattivazione automatica o inviare promemoria agli utenti per lo spegnimento dei loro computer.
Confronti
Sebbene esistano strumenti di terze parti in grado di rilevare il tempo di inattività di un utente, spesso sono caratterizzati da un’aggiunta di elementi di disturbo o non dispongono dei dettagli granulari richiesti. Alcuni potrebbero optare per una query del registro eventi di Windows alla ricerca di eventi specifici, ma questo approccio potrebbe perdere sfumature come un video in esecuzione che mantiene attiva la sessione. Questo script per rilevare il tempo di inattività di un utente fornisce un metodo diretto e personalizzabile, sfruttando gli strumenti e i comandi nativi di Windows.
Domande frequenti
- RDS è necessario per questo script per rilevare il tempo di inattività di un utente?
No, lo script è stato progettato per rilevare i tempi di inattività di un utente sia con che senza RDS. - Quanto è preciso il calcolo del tempo di inattività?
È preciso a livello di “tick del sistema” dall’ultimo input dell’utente.
Implicazioni
Il rilevamento dei tempi morti non riguarda solo la gestione delle risorse. Una sessione aperta e inattiva può rappresentare una potenziale vulnerabilità. I malintenzionati, una volta entrati nella rete, possono usare a loro vantaggio queste sessioni. Pertanto, il rilevamento tempestivo e la gestione delle sessioni sono fondamentali per la sicurezza informatica.
Raccomandazioni
- Per ottenere risultati precisi, esegui sempre lo script per rilevare il tempo di inattività di un utente con privilegi amministrativi.
- Aggiorna e controlla regolarmente lo script per rilevare il tempo di inattività di un utente in modo da allinearlo alle modifiche dell’ambiente Windows.
- Integra lo script per rilevare il tempo di inattività di un utente con gli strumenti di monitoraggio per ottenere avvisi in tempo reale sulle sessioni inattive prolungate.
Considerazioni finali
Nel contesto della gestione delle sessioni utente inattive, strumenti come NinjaOne possono amplificare ulteriormente l’efficacia fornendo una piattaforma unificata per monitorare, avvisare e gestire. Sia che si tratti di ottimizzare le risorse che di rafforzare il perimetro di sicurezza, la comprensione dei comportamenti degli utenti nei tempi di inattività può essere una svolta. L’utilizzo di script come quello discusso per rilevare il tempo di inattività di un utente può costituire la spina dorsale di questo approccio, soprattutto se abbinato a soluzioni di gestione IT complete.