简体   繁体   English

Powershell - 查找登录了特定用户名的计算机

[英]Powershell - Find computers with a specific username logged in

I've been trying to figure out a powershell script that doing a search of all computers with a specific username logged in.我一直在试图找出一个 powershell 脚本来搜索所有登录了特定用户名的计算机。

so far I've found a way looking via all computers the 'explorer.exe' process and owner到目前为止,我已经找到了一种通过所有计算机查看“explorer.exe”进程和所有者的方法

Also I found this script:我还发现了这个脚本:

Function Get-Username {
    Clear-Host
    $Global:Username = Read-Host "Enter Username"
    if ($Username -eq $null){
        Write-Host "Username cannot be blank, please enter a Username"
        Get-Username
    }
    $UserCheck = Get-ADUser $Username
    if ($UserCheck -eq $null){
        Write-Host "Invalid Username, please verify this is the logon id for the account"
        Get-Username
    }
}
Get-Username

Function Get-Computername {
    Clear-Host
    $Global:Prefix = Read-Host "Enter Computername"
    Clear-Host
}
Get-Computername

$computers = Get-ADComputer -Filter {Enabled -eq 'true' -and SamAccountName -like $Prefix}
$CompCount = $Computers.Count
Write-Host "Searching for $Username on $Prefix on $CompCount Computers`n"

foreach ($comp in $computers){
    $Computer = $comp.Name
    $Reply = $null
    $Reply = test-connection $Computer -count 1 -quiet
    if($Reply -eq 'True'){
        if($Computer -eq $env:COMPUTERNAME){
            $proc = gwmi win32_process -ErrorAction SilentlyContinue -computer $Computer -Filter "Name = 'explorer.exe'"
        }
        else{
            $proc = gwmi win32_process -ErrorAction SilentlyContinue -Credential $Credential -computer $Computer -Filter "Name = 'explorer.exe'"
        }           

            $progress++         
            ForEach ($p in $proc) {             
                $temp = ($p.GetOwner()).User
                Write-Progress -activity "Working..." -status "Status: $progress of $CompCount Computers checked" -PercentComplete (($progress/$Computers.Count)*100)
                if ($temp -eq $Username){
                write-host "$Username is logged on $Computer"
                }
            }
        }   
    }

This is not my script, only trying to use it.这不是我的脚本,只是尝试使用它。

this script can take a very long long time, trying to run on about 300 machines, it can take for 20 minutes run.这个脚本可能需要很长时间,尝试在大约 300 台机器上运行,可能需要运行 20 分钟。

now im not sure, which part is taking most of the time, im thinking either the gwmi win32 process, that checking on each machine if the process 'explorer.exe' is on or the part test-connection that trying to ping each machine first, and then check for the 'explorer.exe' process.现在我不确定,哪个部分占用了大部分时间,我想要么是 gwmi win32 进程,要么是在每台机器上检查进程“explorer.exe”是否打开,要么是首先尝试 ping 每台机器的部分测试连接,然后检查“explorer.exe”进程。

for your knowledge, is there any faster way with powershell script to do this kind of checkup?据您所知,使用 powershell 脚本进行此类检查是否有更快的方法? to search for a specific username on which machines logged on?搜索在哪些机器上登录的特定用户名?

Thanks in advance for any help!在此先感谢您的帮助!

Continuing from my comment.继续我的评论。 . . . .

Here's an quick and dirty approach you can take:这是您可以采用的一种快速而肮脏的方法:

Function Get-Username {
    Clear-Host
    $Global:Username = Read-Host "Enter Username"
    if ($Username -eq $null){
        Write-Host "Username cannot be blank, please enter a Username"
        Get-Username
    }
    $UserCheck = Get-ADUser $Username
    if ($UserCheck -eq $null){
        Write-Host "Invalid Username, please verify this is the logon id for the account"
        Get-Username
    }
}
Get-Username
Function Get-Computername {
    Clear-Host
    $Global:Prefix = Read-Host "Enter Computername"
    Clear-Host
}
Get-Computername

$computers = Get-ADComputer -Filter {Enabled -eq $true -and SamAccountName -like $Prefix}

$check = Get-CimInstance -Query "SELECT Name,UserName from Win32_ComputerSystem WHERE UserName LIKE '%$userName%'" `
                -ComputerName $Computers.Name`
                -ErrorAction SilentlyContinue
if ($check) { 
    # $check.UserName/Name may return an array same name users are found. 
    Write-Output -InputObject ($check.UserName + " is logged into: " + $check.Name)
}
else {
    Write-Output -InputObject "Unable to find $userName."
}

Seeing as you're not doing nothing with the systems that you're unable to ping, you can silence the output and only returning the results for the ones that are online.鉴于您没有对无法 ping 的系统执行任何操作,您可以使 output 静音,只返回在线系统的结果。 Best solution for you scenario and question, is to take advantage of the parallelization that Get-CIMInstance allows you to do when passing an array of computers to -ComputerName .针对您的场景和问题的最佳解决方案是利用Get-CIMInstance允许您在将计算机数组传递给-ComputerName时执行的并行化

  • Write-Progress tends to be terribly slow and if you're looking for a fast solution, this will definitely slow you down. Write-Progress往往非常慢,如果您正在寻找一个快速的解决方案,这肯定会让您慢下来。
  • Get-WMIObject is a deprecated cmdlet that has been superseded by Get-CIMInstance which offers the same functionality, with a safer remoting procedure. Get-WMIObject是一个已弃用的 cmdlet,已被Get-CIMInstance取代,后者提供相同的功能,但远程处理过程更安全。
  • Using -Query , we can use WQL to search at the time of query speeding up the process some more.使用-Query ,我们可以在查询时使用WQL进行搜索,进一步加快处理速度。 The only downside to some admins is that it follows its own syntax.对一些管理员来说唯一的缺点是它遵循自己的语法。

One thing to note, if you can ping a machine, it doesn't mean you can connect to it.需要注意的一点是,如果您可以 ping 通一台机器,并不意味着您可以连接到它。

this is a slightly different approach.这是一种略有不同的方法。 [ grin ] it presumes that you may want to check on more than one user name, so it grabs ALL the users on ALL the systems. [咧嘴笑] 它假定您可能想要检查多个用户名,因此它会抓住所有系统上的所有用户。 once you have that, you can query for the one you want... and then query for the next without having to wait for the system list to be scanned again.一旦你有了它,你就可以查询你想要的那个……然后查询下一个,而不必等待系统列表被再次扫描。

the code...代码...

$ComputerList = @"
LocalHost
10.0.0.1
127.0.0.1
BetterNotBeThere
$env:COMPUTERNAME
"@ -split [System.Environment]::NewLine
#endregion >>> fake reading in a list of computer names


$GCIMI_Params = @{
    ClassName = 'CIM_ComputerSystem'
    ComputerName = $ComputerList
    ErrorAction = 'SilentlyContinue'
    # this will hold the errors from the non-repsonding systems - if you want that info
    ErrorVariable =  'NonResponderList'
    }
$LoggedInUserList = Get-CimInstance @GCIMI_Params |
    ForEach-Object {
        [PSCustomObject]@{
            # the ".Split()" gets rid of the domain/workgroup/system part of the UserName
            UserName = $_.UserName.Split('\')[-1]
            ComputerName = $_.DNSHostName
            # this shows the name used by the G-CimI call - it can be different
            QueryComputerName = $_.PSComputerName
            }
        }

#get your target user name
$TargetUserName = $env:USERNAME

#get the system that name is logged into
$LoggedInUserList.Where({$_.UserName -eq $TargetUserName})
 

output on my system today... output 今天在我的系统上...

UserName    ComputerName QueryComputerName
--------    ------------ -----------------
MyUserName  MySysName    MySysName            
MyUserName  MySysName    LocalHost        
MyUserName  MySysName    127.0.0.1

what the code does...代码做什么...

  • builds a list of systems to check构建要检查的系统列表
    when you are ready to do this with real data, just remove the entire #region/#endregion block & replace it with your Get-ADComputer call.当您准备好使用真实数据执行此操作时,只需删除整个#region/#endregion块并将其替换为您的Get-ADComputer调用。
  • builds a splat of the parameters for the Get-CimInstance callGet-CimInstance splat构建参数图
  • sends the G-CimI call to the target systems向目标系统发送G-CimI调用
  • generates a [PSCustomObject] containing the desired properties生成包含所需属性的[PSCustomObject]
  • sends that PSCO out to a results collection将该 PSCO 发送到结果集合
  • grabs a user name获取用户名
  • filters the results collection过滤结果集合
  • shows the system[s] the user name is logged into - if there are any显示系统 [s] 用户名登录 - 如果有的话

note that i only have one system, so the 3 responses are all from that same box.请注意,我只有一个系统,所以 3 个响应都来自同一个盒子。 also note that the computer can be referred to by DNS name, IP address, or LocalHost .另请注意,计算机可以通过 DNS 名称、IP 地址或LocalHost来引用。

Do you have the ability to read the remote registry on these computers?你有能力读取这些计算机上的远程注册表吗?

If so:如果是这样的话:

  1. Get a list for registry keys from \\{Computername}\HKU从 \\{Computername}\HKU 获取注册表项列表
  2. Filter out all that DO NOT end with '_Classes'过滤掉所有不以“_Classes”结尾的
HKEY_USERS\.DEFAULT
HKEY_USERS\S-1-5-19
HKEY_USERS\S-1-5-20
HKEY_USERS\S-1-5-21-4155237567-380904213-1424831347-1001
HKEY_USERS\S-1-5-21-4155237567-380904213-1424831347-1005
HKEY_USERS\S-1-5-21-4155237567-380904213-1424831347-1005_Classes
HKEY_USERS\S-1-5-21-4155237567-380904213-1424831347-500
HKEY_USERS\S-1-5-21-515967899-1938372697-839532595-26824
HKEY_USERS\S-1-5-21-515967899-1938372697-839532595-3976
HKEY_USERS\S-1-5-21-515967899-1938372697-839532595-3976_Classes
HKEY_USERS\S-1-5-18

becomes成为

HKEY_USERS\S-1-5-21-4155237567-380904213-1424831347-1005_Classes
HKEY_USERS\S-1-5-21-515967899-1938372697-839532595-3976_Classes
  1. From the now filtered list remove '_Classes' from the end of the keys.从现在过滤的列表中删除键末尾的“_Classes”。
HKEY_USERS\S-1-5-21-4155237567-380904213-1424831347-1005_Classes
HKEY_USERS\S-1-5-21-515967899-1938372697-839532595-3976_Classes

becomes成为

HKEY_USERS\S-1-5-21-4155237567-380904213-1424831347-1005
HKEY_USERS\S-1-5-21-515967899-1938372697-839532595-3976
  1. If any remain, then try to read the "USERNAME" value from the "Volatile Environment" key from each.如果还有剩余,则尝试从每个“易失性环境”键中读取“USERNAME”值。
HKEY_USERS\S-1-5-21-4155237567-380904213-1424831347-1005\Volatile Environment
{Throws error, was just a ghost of past login - not real}
HKEY_USERS\S-1-5-21-515967899-1938372697-839532595-3976\Volatile Environment
    USERNAME    REG_SZ    {UserName}
  1. Been doing this from BATCH language with REG.EXE and don't believe I've ever seen 2 names come back from step 4 above.使用 REG.EXE 从 BATCH 语言执行此操作并且不相信我见过 2 个名称从上面的步骤 4 返回。 If no keys remains after step 3, or all attempts to read USERNAME fails in step 4, then no one is logged on.如果在第 3 步之后没有密钥,或者在第 4 步中所有读取 USERNAME 的尝试都失败,则没有人登录。 If one name comes back, I guess it could be a ghost left over from a system crash, but in theory, it should be the user logged on.如果一个名字回来了,我猜它可能是系统崩溃遗留下来的幽灵,但理论上应该是登录的用户。

I've seen PSLoggedOn \{ComputerName} fail to give correct info, so I think this is simply a very hard thing to do since Windows will crash, or have other problems, and leaves ghost fragments from past users.我已经看到 PSLoggedOn \{ComputerName} 无法提供正确的信息,所以我认为这是一件非常困难的事情,因为 Windows 会崩溃,或者有其他问题,并留下来自过去用户的幽灵碎片。

Sorry I don't PowerShell code, still fairly new to PowerShell and haven't nose dived into the registry that far yet.抱歉,我没有 PowerShell 代码,对于 PowerShell 来说还是相当新的,而且还没有深入了解注册表。

If I really needed to know for a fact who is logged on or not, I would investigate seeing if there is a way to get a script to run every 2 minutes while the user is logged in, and have the script save a UserName/DateTime stamp at the end of a log file in \Users\Public every time it ran.如果我真的需要知道登录或未登录的事实,我会调查是否有办法让脚本在用户登录时每 2 分钟运行一次,并让脚本保存一个用户名/日期时间每次运行时都在 \Users\Public 中的日志文件末尾标记。 But I would have to want it bad to do that much work.但我不得不希望它做那么多工作。

Here's is an alternative method that does not iterarte all computers in the domain, but it relies on all users have their Home directories redirected to a.network share.这是一种替代方法,它不会迭代域中的所有计算机,它依赖于所有用户将其主目录重定向到 .network 共享。

If that is the case in your domain, try:如果您的域中出现这种情况,请尝试:

# enter the SamAccountName of the user you are looking for
$user = 'SamAccountName'

# the UNC (\\Server\Share) name of the network share where all user homedirectories are
$usersHomePath = '\\HomesServer\HomesShare$' 

# split the UNC path to get the server name and share name in separate variables
$server, $share = $usersHomePath.TrimStart("\") -split '\\'



$result = Get-CimInstance -ClassName Win32_ServerConnection -ComputerName $server | 
            Where-Object { $_.ShareName -eq $share -and $user -eq $_.UserName } |
            Select-Object @{Name = "SamAccountName"; Expression = { $_.UserName }}, 
                          @{Name = "ComputerName"; Expression = {(([System.Net.Dns]::GetHostEntry($_.ComputerName).HostName) -split "\.")[0]}}

#output in console
$result

In all honesty, I don't think you will ever get super fast access of who is logged on.老实说,我认为您永远无法超快速地访问谁已登录。 The only real approach is to multi-thread it with something like Split-Pipeline.唯一真正的方法是使用诸如 Split-Pipeline 之类的东西对其进行多线程处理。 https://github.com/nightroman/SplitPipeline https://github.com/nightroman/SplitPipeline

Once you install SplitPipeline, your code will need a framework like this:安装 SplitPipeline 后,您的代码将需要这样的框架:

function MakePackage {
    param (
        [Parameter(Mandatory = $true, Position = 1)]
        [string]$UserName,
        [Parameter(ValueFromPipeline)]
        [string]$ComputerName
    )
    process {
        [PSCustomObject]@{
            ComputerName    = $ComputerName
            UserName        = $UserName
        }
    }
}

$UserName = Read-Host "Please enter the username to search for"
$ComputerNames = @( 'ComputerName1', 'ComputerName2', 'ComputerName3', 'ComputerName4')
$ComputerNames | MakePackage $UserName | Split-Pipeline -Count 10 {
    begin {
        # Your functions go here.
    }
    Process {
        [string]$ComputerName   = $_.ComputerName
        [string]$UserName       = $_.UserName
        # Your main code goes here.
        Write-Host "Looking for $UserName on computer $ComputerName"        
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM