Como detectar o tempo ocioso do usuário usando o PowerShell

A detecção de tempo ocioso em computadores tem sido um aspecto crítico para os profissionais de TI, especialmente na era das crescentes ameaças à segurança cibernética e ao gerenciamento de recursos. À medida que as organizações se digitalizam rapidamente e os usuários permanecem conectados por longos períodos, a detecção do tempo ocioso do usuário torna-se fundamental por motivos operacionais e de segurança. Nesta postagem, vamos nos aprofundar em um script do PowerShell desenvolvido para detectar o maior tempo ocioso dos usuários, tornando a tarefa perfeita e eficiente.

Histórico

A capacidade de determinar há quanto tempo um usuário está inativo pode fornecer informações valiosas para profissionais de TI e provedores de serviços gerenciados (MSPs). Seja para liberar recursos, garantir que as sessões não permaneçam abertas a possíveis ameaças à segurança ou até mesmo considerações de faturamento para provedores de serviços em nuvem, é essencial ter um método eficaz e confiável para determinar a inatividade do usuário. Esse script específico aproveita a capacidade do PowerShell de interagir diretamente com o sistema operacional Windows e extrair dados relevantes para medir os tempos de inatividade.

O roteiro

#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 {}

 

Acesse mais de 300 scripts no NinjaOne Dojo

Obter acesso

Detalhamento

  • Cmdlet Binding & Parâmetros: O script começa com um CmdletBinding, permitindo que ele seja usado como um cmdlet. Ele contém um parâmetro opcional para um nome de usuário específico.
  • Funções internas:
  • O Test-IsElevated verifica se o script é executado com privilégios administrativos.
  • Get-QueryUser faz interface com query.exe para obter detalhes sobre os usuários atuais.
  • Integração de bibliotecas externas: Uma biblioteca externa(GetLastUserInput) é usada para obter a contagem exata de ticks ociosos.
  • Bloco de processo: A lógica central está aqui.
  • Em primeiro lugar, ele verifica se o script é executado com direitos de administrador.
  • Se o Remote Desktop Services (RDS) estiver instalado, ele obterá o tempo ocioso de cada usuário conectado.
  • Se o RDS não estiver presente, ele volta à abordagem de consulta ao usuário e calcula o tempo ocioso com base na última entrada do usuário.

Casos de uso em potencial

Imagine que você é um administrador de TI de uma organização de médio porte. Você notou que muitos usuários deixam suas estações de trabalho ligadas e conectadas, mesmo depois do horário de expediente. Além de ser um risco à segurança, isso também consome recursos de rede e energia. Ao implementar esse script em toda a empresa, é possível determinar rapidamente quais usuários estão trabalhando ativamente e quais estações de trabalho estão ociosas. Com essas informações, é possível aplicar políticas de logout automático ou enviar lembretes aos usuários para que desliguem suas máquinas.

Comparações

Embora existam ferramentas de terceiros disponíveis que detectam o tempo ocioso, elas geralmente vêm com inchaço adicional ou podem não ter os detalhes granulares necessários. Algumas pessoas podem optar por consultar o Log de Eventos do Windows em busca de eventos específicos, mas essa abordagem pode perder nuances como um vídeo em execução que mantém a sessão ativa. Esse script fornece um método direto e personalizável, aproveitando as ferramentas e os comandos nativos do Windows.

Implicações

A detecção de tempo ocioso não se refere apenas ao gerenciamento de recursos. Uma sessão aberta e inativa pode ser uma vulnerabilidade em potencial. Atores mal-intencionados, uma vez dentro da rede, podem sequestrar essas sessões. Portanto, a detecção oportuna e o gerenciamento de sessões são cruciais para a segurança de TI.

Recomendações

  • Sempre execute o script com privilégios administrativos para obter resultados precisos.
  • Atualize e mantenha o script regularmente para atender às mudanças no ambiente do Windows.
  • Integre-se às ferramentas de monitoramento para obter alertas em tempo real sobre sessões ociosas prolongadas.

Fique à frente dos problemas antes que eles se tornem problemas.
Com o monitoramento remoto da NinjaOne & Alerting, você pode detectar, diagnosticar e resolver incidentes de TI mais rapidamente – sem interromper os usuários finais.

Considerações finais

No contexto do gerenciamento de sessões de usuários ociosas, ferramentas como o NinjaOne podem ampliar ainda mais a eficácia, fornecendo uma plataforma unificada para monitorar, alertar e gerenciar. Seja para otimizar os recursos ou reforçar o perímetro de segurança, entender o comportamento dos usuários em tempos ociosos pode ser um divisor de águas. O aproveitamento de scripts como o discutido pode formar a espinha dorsal desse esforço, especialmente quando combinado com soluções abrangentes de gerenciamento de TI.

Add a Comment

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

FAQs

Não, ele foi projetado para detectar tempos ociosos com e sem RDS.

É preciso para o nível de ticks do sistema desde a última entrada do usuário.

Próximas etapas

Montar uma equipe de TI eficaz requer uma solução centralizada que seja a principal ferramenta de entrega de serviços. Com NinjaOne, a TI monitora, gerencia, protege e oferece suporte a todos os dispositivos, onde quer que estejam, dispensando infraestrutura complexa no local.

Saiba mais sobre a solução NinjaOne Remote Script Deployment, agende uma demonstração, ou inicie sua avaliação gratuita da plataforma NinjaOne.

Categories:

You might also like