Password policies are the best 😀 Sometimes they lead to account logouts when someone forgets to logout of a session somewhere on the network though. Pode ser a sessão TS que eles usam uma vez por trimestre para reportar ou talvez você saiba a sensação quando você RDP para um servidor apenas para descobrir que ele está bloqueado por 2 outros administradores que se esqueceram de logoff quando eles saíram. (Desligado porque isto nunca acontece… todos nós usamos o PowerShell…) De qualquer forma, isto fez-me procurar por uma sessão de utilizador algures na rede. A pior coisa é quando a minha própria senha expira. Detesto quando a minha conta acaba por ser bloqueada. Por isso, eu fiz uma regra para verificar todos os servidores antes de mudar a senha. Há várias maneiras de fazer isso, mas é claro que eu tendo a ir pelo caminho do PowerShell.
Pesquisa
O método original que usei é da galeria TechNet
Em resumo: Get-WmiObject -Class Win32_process
Esta basicamente encontra todos os usuários únicos executando processos na máquina. Isso é legal porque ele encontra tudo, mesmo coisas rodando como um serviço, mas eu não estou convencido de que seja a maneira mais eficiente.
Check up with google I find a lot of creative ways to check who is logged on to your box.
peetersonline.nl/2008/11/oneliner-get-logged-on-users-with-powershell/ deu-me a idéia de verificar Win32_LoggedOnUser o que parece óbvio.
Isto parece ótimo e parece funcionar com o Get-CimInstance também, embora a saída seja um pouco diferente.
learn-powershell.net/…/Quick-hit-hit-find-currently-logged-on-users/ tomou uma abordagem um pouco mais old-school que eu meio que gosto porque é um pouco áspero e me força a brincar com o meu parsing baseado em templates.
Não tenho a certeza de qual é o método mais rápido, então porque não tentar implementar os 3 num módulo e testá-lo.
Sketching
É sempre uma boa ideia começar por fazer um esboço do que se está a tentar realizar.
Pseudo code:Get-ActiveUser -ComputerName -Method Wanted output:Username ComputerName-------- ------------TestUser1 Svr3TestUser3 Svr3DonaldDuck Client2
Agora eu tenho todas as informações necessárias para configurar o repositório GitHub.
github.com/mrhvid/Get-ActiveUser
Código
Primeiro de todos os parâmetros que me interessam são ComputerName e Método.
Param ( # Computer name, IP, Hostname ] $ComputerName, # Choose method, WMI, CIM or Query $Method )
Eu já tenho 3 Métodos possíveis em mente então eu configurei ValidateSet com as 3 possibilidades. Então eu não tenho que me preocupar com essa entrada mais tarde.
Process { switch ($Method) { 'WMI' { } 'CIM' { } 'Query' { } } }
Na parte Process da minha função eu simplesmente uso um switch para os 3 métodos diferentes que eu permiti no Parâmetro.
Agora é o preenchimento básico em branco.
WMI
A minha antiga solução é simpel e funciona bem.
$WMI = Get-WmiObject -Class Win32_Process -ComputerName $ComputerName -ErrorAction Stop$ProcessUsers = $WMI.getowner().user | Select-Object -Unique
Mas agora que encontrei o Win32_LoggedOnUser parece errado fazer desta forma. Vamos antes olhar para a nova ideia.
Estes são todos os dados correctos mas parece estar num formato de string por isso vou ter que fazer uma pequena manipulação. Isto pode ser feito de milhões de maneiras.
function Get-MyLoggedOnUsers { param($Computer) Get-WmiObject Win32_LoggedOnUser -ComputerName $Computer | Select Antecedent -Unique | %{"{0}{1}" -f $_.Antecedent.ToString().Split('"'), $_.Antecedent.ToString().Split('"')} }
Peter’s aforementioned one-liner não me pareceu muito fácil de ler, o que é ok para um one-liner, mas eu gostaria que fosse um pouco mais legível se possível.
$WMI = (Get-WmiObject Win32_LoggedOnUser).Antecedent$ActiveUsers = @()foreach($User in $WMI) { $StartOfUsername = $User.LastIndexOf('=') + 2 $EndOfUsername = $User.Length - $User.LastIndexOf('=') -3 $ActiveUsers += $User.Substring($StartOfUsername,$EndOfUsername)}
Isto parece certo 🙂 Vou salvar a saída em $ActiveUsers variável e fazer o mesmo para CIM e Query.
CIM
Vamos tentar com CIM.
Esta parece muito mais estruturada.
CIM acaba sendo um one-liner fácil de entender 😀
$ActiveUsers = (Get-CimInstance Win32_LoggedOnUser -ComputerName $ComputerName).antecedent.name | Select-Object -Unique
Query
Usando o bom ol’ Query.exe Achei muito útil a análise baseada no template discutido anteriormente.
$Template = @' USERNAME SESSIONNAME ID STATE IDLE TIME LOGON TIME>{USER*:jonas} console 1 Active 1+00:27 24-08-2015 22:22 {USER*:test} 2 Disc 1+00:27 25-08-2015 08:26'@$Query = query.exe user$ActiveUsers = $Query | ConvertFrom-String -TemplateContent $Template | Select-Object -ExpandProperty User
Saída
Agora eu só preciso formatar e dar saída aos usuários de uma maneira legal. Eu quero objetos limpos com ComputerName e UserName.
# Create nice output format$UsersComputersToOutput = @()foreach($User in $ActiveUsers) { $UsersComputersToOutput += New-Object psobject -Property @{ ComputerName=$ComputerName; UserName=$User } }}# output data$UsersComputersToOutput
Testing
Agora eu tenho um problema. Não posso testar isto porque não tenho um monte de servidores de teste à minha disposição. Todos os meus testes foram feitos contra a minha própria caixa do Windows 10. Parece que a consulta é muito mais rápida rodando localmente, mas o WMI/CIM pode dar uma visão mais completa de quais serviços estão rodando.
Eu tenho um monte de contas de serviços padrão em execução que pode ser bom para remover da saída. Também para que isso seja útil vamos querer executá-lo contra muitas máquinas.
Combinando Get-ActiveUser com Start-Multithread do post das últimas semanas parece estar funcionando como pretendido.
Start-Multithread -Script { param($C) Get-ActiveUser -ComputerName $C -Method Query } -ComputerName ::1,Localhost | Out-GridView
Piping the above to Out-GridView é provavelmente a minha forma preferida de realizar algo verdadeiramente útil.
Agora temos todos os dados de uma forma pesquisável e é muito fácil verificar se o seu usuário está logado em alguma máquina aleatória. É também uma maneira fácil de verificar se há usuários rouge na sua rede.
Publicação e feedback
O código é publicado no PowerShellGallery.
Por favor me ajude testando-o para mim. Eu adoraria saber se isto funciona no mundo real 🙂
# To install Get-ActiveUserInstall-Module Get-ActiveUser#To install Start-MultithreadInstall-Module Start-Multithread
Isto deve funcionar quando se tem o WMF 5 + instalado e no Windows 10 fora da caixa.