Powershell - 查找登录了特定用户名的计算机
Powershell - Find computers with a specific username logged in
我一直在尝试找出一个可以搜索所有计算机的 powershell 脚本
使用特定用户名登录。
到目前为止,我已经找到了一种通过所有计算机查看 'explorer.exe' 进程和所有者的方法
我还找到了这个脚本:
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"
}
}
}
}
这不是我的脚本,只是尝试使用它。
此脚本可能需要很长时间,尝试 运行 在大约 300 台机器上,可能需要 20 分钟 运行。
现在我不确定,哪个部分占用了大部分时间,我想要么是 gwmi win32 进程,要么是在每台机器上检查进程 'explorer.exe' 是否开启
或首先尝试 ping 每台机器的部分测试连接,然后检查 'explorer.exe' 进程。
据您所知,是否有使用 powershell 脚本进行此类检查的更快方法?
搜索登录计算机的特定用户名?
在此先感谢您的帮助!
继续我的评论。 . .
这是您可以采用的一种快速而肮脏的方法:
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."
}
鉴于您没有对无法 ping 的系统执行任何操作,您可以使输出静音,只返回在线系统的结果。针对您的场景和问题的最佳解决方案是利用 并行化 ,Get-CIMInstance
允许您在将一组计算机传递给 -ComputerName
时执行此操作。
Write-Progress
往往非常慢,如果您正在寻找一个快速的解决方案,这肯定会让您慢下来。
Get-WMIObject
是一个已弃用的 cmdlet,已被 Get-CIMInstance
取代,它提供相同的功能,但远程处理过程更安全。
- 使用
-Query
,我们可以在查询时使用WQL进行搜索,进一步加快处理速度。对一些管理员来说唯一的缺点是它遵循自己的语法。
有一点要注意,如果你能ping通一台机器,并不代表你能连接上它。
这是一种略有不同的方法。 [grin] 它假定您可能想要检查多个用户名,因此它会抓取所有系统上的所有用户。一旦你有了它,你就可以查询你想要的那个......然后查询下一个,而不必等待系统列表被再次扫描。
代码 ...
$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})
今天我的系统输出...
UserName ComputerName QueryComputerName
-------- ------------ -----------------
MyUserName MySysName MySysName
MyUserName MySysName LocalHost
MyUserName MySysName 127.0.0.1
代码的作用...
- 构建要检查的系统列表
当您准备好使用真实数据执行此操作时,只需删除整个 #region/#endregion
块并将其替换为您的 Get-ADComputer
调用。
- 为
Get-CimInstance
调用构建 splat
参数
- 将
G-CimI
调用发送到目标系统
- 生成包含所需属性的
[PSCustomObject]
- 将该 PSCO 发送到结果集合
- 获取用户名
- 过滤结果集合
- 显示用户名登录的系统[=] - 如果有的话
请注意,我只有一个系统,所以 3 个响应都来自同一个盒子。另请注意,可以通过 DNS 名称、IP 地址或 LocalHost
.
引用计算机
你有能力读取这些计算机上的远程注册表吗?
如果是:
- 从\\{Computername}\HKU
获取注册表项列表
- 过滤掉所有不以“_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
变成
HKEY_USERS\S-1-5-21-4155237567-380904213-1424831347-1005_Classes
HKEY_USERS\S-1-5-21-515967899-1938372697-839532595-3976_Classes
- 从现在过滤的列表中删除键末尾的“_Classes”。
HKEY_USERS\S-1-5-21-4155237567-380904213-1424831347-1005_Classes
HKEY_USERS\S-1-5-21-515967899-1938372697-839532595-3976_Classes
变成
HKEY_USERS\S-1-5-21-4155237567-380904213-1424831347-1005
HKEY_USERS\S-1-5-21-515967899-1938372697-839532595-3976
- 如果还有剩余,则尝试从每个“易变环境”键中读取“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}
- 使用 REG.EXE 从 BATCH 语言执行此操作并且不相信我见过 2 个名称从上面的第 4 步返回。如果在第 3 步之后没有密钥,或者在第 4 步中所有读取 USERNAME 的尝试都失败,则没有人登录。如果一个名字回来了,我猜可能是系统崩溃遗留下来的鬼,但理论上应该是登录的用户。
我看到 PSLoggedOn \{ComputerName} 无法提供正确的信息,所以我认为这是一件非常困难的事情,因为 Windows 会崩溃,或者有其他问题,并留下幽灵碎片来自过去的用户。
抱歉,我不会编写 PowerShell 代码,对 PowerShell 还是很陌生,还没有深入研究注册表。
如果我真的需要知道登录或未登录的事实,我会调查是否有办法在用户登录时每 2 分钟将脚本发送到 运行,并让脚本在每次 运行 时在 \Users\Public 中的日志文件末尾保存一个 UserName/DateTime 标记。但我不得不希望它做那么多工作。
这是一个替代方法,它不会迭代域中的所有计算机,但是它依赖于所有用户将他们的主目录重定向到网络共享。
如果您的域中出现这种情况,请尝试:
# 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
老实说,我认为您永远无法超快地访问谁已登录。唯一真正的方法是用 Split-Pipeline 之类的东西 multi-thread 它。
https://github.com/nightroman/SplitPipeline
安装 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"
}
}
我一直在尝试找出一个可以搜索所有计算机的 powershell 脚本 使用特定用户名登录。
到目前为止,我已经找到了一种通过所有计算机查看 'explorer.exe' 进程和所有者的方法
我还找到了这个脚本:
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"
}
}
}
}
这不是我的脚本,只是尝试使用它。
此脚本可能需要很长时间,尝试 运行 在大约 300 台机器上,可能需要 20 分钟 运行。
现在我不确定,哪个部分占用了大部分时间,我想要么是 gwmi win32 进程,要么是在每台机器上检查进程 'explorer.exe' 是否开启 或首先尝试 ping 每台机器的部分测试连接,然后检查 'explorer.exe' 进程。
据您所知,是否有使用 powershell 脚本进行此类检查的更快方法? 搜索登录计算机的特定用户名?
在此先感谢您的帮助!
继续我的评论。 . .
这是您可以采用的一种快速而肮脏的方法:
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."
}
鉴于您没有对无法 ping 的系统执行任何操作,您可以使输出静音,只返回在线系统的结果。针对您的场景和问题的最佳解决方案是利用 并行化 ,Get-CIMInstance
允许您在将一组计算机传递给 -ComputerName
时执行此操作。
Write-Progress
往往非常慢,如果您正在寻找一个快速的解决方案,这肯定会让您慢下来。Get-WMIObject
是一个已弃用的 cmdlet,已被Get-CIMInstance
取代,它提供相同的功能,但远程处理过程更安全。- 使用
-Query
,我们可以在查询时使用WQL进行搜索,进一步加快处理速度。对一些管理员来说唯一的缺点是它遵循自己的语法。
有一点要注意,如果你能ping通一台机器,并不代表你能连接上它。
这是一种略有不同的方法。 [grin] 它假定您可能想要检查多个用户名,因此它会抓取所有系统上的所有用户。一旦你有了它,你就可以查询你想要的那个......然后查询下一个,而不必等待系统列表被再次扫描。
代码 ...
$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})
今天我的系统输出...
UserName ComputerName QueryComputerName
-------- ------------ -----------------
MyUserName MySysName MySysName
MyUserName MySysName LocalHost
MyUserName MySysName 127.0.0.1
代码的作用...
- 构建要检查的系统列表
当您准备好使用真实数据执行此操作时,只需删除整个#region/#endregion
块并将其替换为您的Get-ADComputer
调用。 - 为
Get-CimInstance
调用构建splat
参数 - 将
G-CimI
调用发送到目标系统 - 生成包含所需属性的
[PSCustomObject]
- 将该 PSCO 发送到结果集合
- 获取用户名
- 过滤结果集合
- 显示用户名登录的系统[=] - 如果有的话
请注意,我只有一个系统,所以 3 个响应都来自同一个盒子。另请注意,可以通过 DNS 名称、IP 地址或 LocalHost
.
你有能力读取这些计算机上的远程注册表吗?
如果是:
- 从\\{Computername}\HKU 获取注册表项列表
- 过滤掉所有不以“_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
变成
HKEY_USERS\S-1-5-21-4155237567-380904213-1424831347-1005_Classes
HKEY_USERS\S-1-5-21-515967899-1938372697-839532595-3976_Classes
- 从现在过滤的列表中删除键末尾的“_Classes”。
HKEY_USERS\S-1-5-21-4155237567-380904213-1424831347-1005_Classes
HKEY_USERS\S-1-5-21-515967899-1938372697-839532595-3976_Classes
变成
HKEY_USERS\S-1-5-21-4155237567-380904213-1424831347-1005
HKEY_USERS\S-1-5-21-515967899-1938372697-839532595-3976
- 如果还有剩余,则尝试从每个“易变环境”键中读取“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}
- 使用 REG.EXE 从 BATCH 语言执行此操作并且不相信我见过 2 个名称从上面的第 4 步返回。如果在第 3 步之后没有密钥,或者在第 4 步中所有读取 USERNAME 的尝试都失败,则没有人登录。如果一个名字回来了,我猜可能是系统崩溃遗留下来的鬼,但理论上应该是登录的用户。
我看到 PSLoggedOn \{ComputerName} 无法提供正确的信息,所以我认为这是一件非常困难的事情,因为 Windows 会崩溃,或者有其他问题,并留下幽灵碎片来自过去的用户。
抱歉,我不会编写 PowerShell 代码,对 PowerShell 还是很陌生,还没有深入研究注册表。
如果我真的需要知道登录或未登录的事实,我会调查是否有办法在用户登录时每 2 分钟将脚本发送到 运行,并让脚本在每次 运行 时在 \Users\Public 中的日志文件末尾保存一个 UserName/DateTime 标记。但我不得不希望它做那么多工作。
这是一个替代方法,它不会迭代域中的所有计算机,但是它依赖于所有用户将他们的主目录重定向到网络共享。
如果您的域中出现这种情况,请尝试:
# 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
老实说,我认为您永远无法超快地访问谁已登录。唯一真正的方法是用 Split-Pipeline 之类的东西 multi-thread 它。 https://github.com/nightroman/SplitPipeline
安装 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"
}
}