uma maneira melhor de encontrar um usuário conectado remotamente usando PowerShell
há uma montanha de maneiras diferentes de fazer login nos usuários, mas eu tenho um favorito! Usando qwinsta
, o único problema é que ele retorna uma string e PowerShell gosta de objetos: ( mas nunca tenha medo! Podemos resolver isso.
qwinsta
Prereqs
encontrei um erro 5 problema de acesso negado quando comecei a usar qwinsta
, há uma correção de registro para isso (obrigado StackOverflow:
"AllowRemoteRPC"=dword:00000001
como este é um blog do PowerShell, aqui está a sintaxe do PowerShell para adicioná-lo a um computador remoto, pois provavelmente estaremos trabalhando em computadores remotos.
$ComputerName = 'Computer' #replace with the computer name$LMtype = ::LocalMachine$LMkey = "SYSTEM\CurrentControlSet\Control\Terminal Server"$LMRegKey = ::OpenRemoteBaseKey($LMtype,$ComputerName)$regKey = $LMRegKey.OpenSubKey($LMkey,$true)If($regKey.GetValue("AllowRemoteRPC") -ne 1){ $regKey.SetValue("AllowRemoteRPC",1) Start-Sleep -Seconds 1}$regKey.Dispose()$LMRegKey.Dispose()
Sintaxe
A sintaxe do qwinsta é bastante simples:
qwinsta /server:ServerName
E a saída parece bastante unmenacing:
SESSIONNAME USERNAME ID STATE TYPE DEVICE services 0 Disc console Anthony 1 Active rdp-tcp 65536 Listen
O único problema é que se você estivesse olhando para um servidor RDS, não há nenhuma maneira fácil para seqüências de filtro no PowerShell. Então você não poderia fazer algo como:
qwinsta /server:RDSServer | Where-Object USERNAME -like "Anth*"
mas podemos consertar isso! Vamos embrulhar qwinsta para que possamos objetivar essa saída!
Wrapping qwinsta
vamos criar uma função PowerShell aqui, cuidado com o leitor.
Espremer a seqüência de caracteres para seus valores
Além de tudo o parâmetro coisas, que nós vamos chegar, o primeiro núcleo coisa que vamos fazer é direcionar a saída para uma variável:
$result = qwinsta /server:$ComputerName
Agora $result
irá olhar apenas como a saída de antes, mas ele será formatado como uma matriz de seqüência de caracteres (string
). Podemos, é claro, verificar que com Get-Member
:
($result | Get-Member).TypeName
então, a próxima coisa é recuperar apenas as informações que queremos. Decidi fazer um loop for, pulando a primeira linha, pois não precisamos dos cabeçalhos:
ForEach($line in $result)
para cada linha, com o que cada valor é delimitado? Espaços! Para que possamos usar o.Método Split() em strings e dividir com cada espaço. A única ressalva é que também precisaremos testar o valor em cada divisão, pois você pode dividir vários espaços e obter valores nulos.
$tmp = $line.split(" ") | ?{$_.length -gt 0}
Uma coisa a notar é que a saída do qwinsta
largura fixa, o que significa que a largura não muda, independentemente do tempo de qualquer dos valores. Podemos aproveitar isso e usar certos índices a nosso favor.
a primeira coisa a fazer é determinar quais linhas realmente contêm um usuário. Podemos contar caracteres e descobrir que o valor do nome de usuário começa no 19º caractere (o primeiro caractere é na verdade o espaço, olhe com cuidado). Portanto, se o 19º caractere não estiver vazio, temos um usuário.
If(($line -ne " "))
a próxima coisa que notei ao testar com qwinsta
é que a propriedade SessionName só tem valor se a sessão estiver no estado ‘ativo’. Então, nós precisamos verificar para ver se o Estado é “Ativo”, o que podemos fazer consultando o índice do estado para um ‘Uma’
If($line -eq "A")
Se ele é “Ativo”, o $tmp
variável terá diferentes quantidades de valores.
Activo:
#raw rdp-tcp#0 anthony 2 Active #$tmp variable (split)rdp-tcp#0anthony2Active
Não Ativo:
#raw anthony 2 Disc #$tmp variable (split)anthony2Disc
convertendo os valores em objetos utilizáveis
depois de descobrir todos os valores, podemos criar um novo PSObject, meu favorito! Se você tiver PowerShellv5, poderá usar o Acelerador de tipo {}
(x), mas usarei o cmdlet New-Object
aqui para compatibilidade final.
New-Object PSObject -Property @{ "ComputerName" = $ComputerName "SessionName" = $tmp "UserName" = $tmp "ID" = $tmp "State" = $tmp "Type" = $tmp}
nesse caso, como sabemos que a sessão está ativa, a primeira string em $tmp
é o nome da sessão e, em seguida, em ordem, você tem o restante das propriedades. É possível que ‘Type ‘ ou’ SessionName ‘ possa voltar vazio.
Se o estado não é “Ativo” nós tínhamos que mudar para:
New-Object PSObject -Property @{ "ComputerName" = $ComputerName "SessionName" = $null "UserName" = $tmp "ID" = $tmp "State" = $tmp "Type" = $null}
O produto final
uma vez que este não é um post sobre a criação de funções, eu vou pular de passar sobre a sintaxe e mostrar-lhe a função final eu escrevi. Isso inclui algumas coisas de melhores práticas, como verificar a conectividade e os direitos de administrador antes de entrar nas coisas interessantes.
Function Get-ActiveSessions{ Param( $Name , $Quiet ) Begin{ $return = @() } Process{ If(!(Test-Connection $Name -Quiet -Count 1)){ Write-Error -Message "Unable to contact $Name. Please verify its network connectivity and try again." -Category ObjectNotFound -TargetObject $Name Return } If(((::GetCurrent()).groups -match "S-1-5-32-544")){ #check if user is admin, otherwise no registry work can be done #the following registry key is necessary to avoid the error 5 access is denied error $LMtype = ::LocalMachine $LMkey = "SYSTEM\CurrentControlSet\Control\Terminal Server" $LMRegKey = ::OpenRemoteBaseKey($LMtype,$Name) $regKey = $LMRegKey.OpenSubKey($LMkey,$true) If($regKey.GetValue("AllowRemoteRPC") -ne 1){ $regKey.SetValue("AllowRemoteRPC",1) Start-Sleep -Seconds 1 } $regKey.Dispose() $LMRegKey.Dispose() } $result = qwinsta /server:$Name If($result){ ForEach($line in $result){ #avoiding the line 0, don't want the headers $tmp = $line.split(" ") | ?{$_.length -gt 0} If(($line -ne " ")){ #username starts at char 19 If($line -eq "A"){ #means the session is active ("A" for active) $return += New-Object PSObject -Property @{ "ComputerName" = $Name "SessionName" = $tmp "UserName" = $tmp "ID" = $tmp "State" = $tmp "Type" = $tmp } }Else{ $return += New-Object PSObject -Property @{ "ComputerName" = $Name "SessionName" = $null "UserName" = $tmp "ID" = $tmp "State" = $tmp "Type" = $null } } } } }Else{ Write-Error "Unknown error, cannot retrieve logged on users" } } End{ If($return){ If($Quiet){ Return $true } Else{ Return $return } }Else{ If(!($Quiet)){ Write-Host "No active sessions." } Return $false } }}
Get-ActiveSessions
você pode encontrar isso em Meus utilitários GitHub repo. Este é o meu primeiro roteiro publicado! Hooray! Então vamos ver como usá-lo:
Simples de uso de
Para recuperar os usuários registrados em um computador remoto ou local, nós simplesmente usar:
Get-ActiveSessions ComputerName
E isso deve retornar algo semelhante a:
ID : 2SessionName :Type :UserName : anthonyComputerName : dc01State : Disc
E se você passá-lo para uma variável e olhar para o nome do tipo, devemos ter uma PSCustomObject
, o que significa que pode usar todos os outros útil cmdlets para a filtragem e outras coisas mais!
$sessions = Get-ActiveSessions ComputerName($sessions | Get-Member).TypeNameSystem.Management.Automation.PSCustomObject
uso avançado
temos duas coisas para nós aqui, podemos aceitar a entrada do pipeline e ele elimina um objeto. Então podemos fazer coisas antes e depois Get-ActiveSessions
.
e se você quiser encontrar todos os servidores em seu ambiente que têm um determinado usuário conectado a eles?
Get-ADComputer -Filter {OperatingSystem -like '*Server*'} | Get-ActiveSessions | Where-Object UserName -eq 'Anthony'
ou que tal puxar uma lista de computadores de um arquivo de texto e produzir a lista de usuários únicos logados em todos eles?
get-content C:\temp\computers.txt | Get-ActiveSessions | Select-Object -Unique UserName -ExpandProperty UserName | Out-File C:\temp\users.txt
se você tiver algum uso útil de Get-ActiveSessions
, comente!
conclusão
espero que você tenha uma apreciação por que eu escreveria uma função para fazer essas coisas para mim. Sinceramente, não consegui contar o número de vezes que usei isso para facilitar meu trabalho.
de qualquer forma, tenho algumas postagens notáveis chegando, particularmente o parceiro para Get-ActiveSessions
: Close-ActiveSessions
.
Spoiler:
Get-ActiveSessions Computer | Close-ActiveSessions
e também: ‘criando tarefas agendadas para Scripts do PowerShell’, o que será uma viagem, pois ainda não fiz nenhuma imagem!
Leave a Reply