如何优化跨所有域控制器的查询以查找最新登录日期?

问题描述

我目前正在尝试查询域控制器以查找用户的最新登录日期。我知道我可以使用 lastlogondate 但这不会像我想要的那样经常复制,而且我无法控制它。我尝试制作一个脚本来查询每个域控制器并根据用户哈希表检查它,但事实证明,即使查询一个 DC 也非常慢。我知道我可以为每个 DC 运行后台作业,但仍然只是访问一个域控制器的所有最新登录时间需要很长时间。有什么建议吗?

$dcs = Get-ADDomainController -Filter {Name -like "*"}
$users = Get-ADUser -filter * -SearchBase "OU=users,DC=com"
$outfile = "c:users.csv"
$usertable = @{}

# creating table for all users
foreach($user in $users) {
    $userobject = New-Object PSObject
    $userobject | Add-Member -MemberType NoteProperty -Name DomainController -Value "0"
    $userobject | Add-Member -MemberType NoteProperty -Name Lastlogon -Value "0"

    $usertable.Add($user.SamAccountName,$userObject)
}    # end foreach

# Method for looping through domain controllers to find last logon

foreach($dc in $dcs) {
    $hostname = $dc.HostName

    $logons = Get-ADUser -Filter * -SearchBase "OU=users,DC=com" -Property Lastlogon -Server $hostname

    foreach($u in $logons) {
        $sam = $u.SamAccountName

        if($u.Lastlogon -gt $usertable.$sam.Lastlogon) {
            $usertable.$sam.Lastlogon = $u.Lastlogon
            $usertable.$sam.DomainController = $dc.HostName
            Write-Host "$sam has the latest date"
            $usertable.$sam.DomainController
        }
    }    #end inner foreach
}        #end outer domain foreach

foreach($h in $usertable.GetEnumerator()) {
    $sam = $($h.Name)
    $newdate = $($h.Value).Lastlogon

    $append = "$sam,$newdate"
    $append | out-file $outfile -Encoding ascii -Force -Append
}

解决方法

我想我已经弄清楚了。通过测试和我自己的类似程序对比很明显,延迟在以下循环中:

foreach($u in $logons) {
    $sam = $u.SamAccountName

    if($u.LastLogon -gt $usertable.$sam.LastLogon) {
        $usertable.$sam.LastLogon = $u.LastLogon
        $usertable.$sam.DomainController = $dc.HostName
        Write-Host "$sam has the latest date"
        $usertable.$sam.DomainController
    }
} # end inner foreach

现在我的程序和你的不完全一样。可以对此进行消毒,但出于所有意图和目的,以下部分与上述摘录具有相同的功能:

# Note: the re-use of $DC...
ForEach($DC in $RDCs )
{   # Should you add any measurements here??
    Write-Host "Working on $DC : $(Get-Date -Format G) ..."
    Get-ADUser -Filter $Filter -SearchBase $SearchBase -Server $DC -Properties $AD_Props |
    ForEach-Object{
        If ( $Users.Contains( $_.samAccountName ) )
        {   # So you don't attemp when it wasn't in the root set.
            If( $_.lastlogon -gt $Users[$_.samAccountName].LastLogon )
            {
                $Users[$_.samAccountName].LastLogon = $_.lastlogon
                $Users[$_.samAccountName].DC = $DC
            }
        }
    }
}

您可以看到我使用的是 ForEach-Object 循环而不是 ForEach 构造。然而,这并不是一个充分的解释,尤其是后者通常更快。

注意:我以几种不同的方式为您的循环计时,并在脚本的前面说明了代码。例如,我没有以同样的方式创建对象,但我排除了这一因素。

无法弄清楚为什么您的循环会“显着”变慢,我不情愿地将其重写为:

$logons |
ForEach-Object{
    If( $_.LastLogon -gt $usertable[$_.samAccountName].LastLogon )
    {
        $usertable[$_.samAccountName].LastLogon = $_.LastLogon
        $usertable[$_.samAccountName].DomainController = $hostname

然而,这似乎并没有快多少。所以,我更不情愿地尝试了:

Get-ADUser -Filter * -SearchBase $UsersOU -Property LastLogon -Server $hostname |    
ForEach-Object{
    If( $_.LastLogon -gt $usertable[$_.samAccountName].LastLogon )
    {
        $usertable[$_.samAccountName].LastLogon = $_.LastLogon
        $usertable[$_.samAccountName].DomainController = $hostname

    }
}

现在我开始看到与我自己的程序类似的性能。事实上,它看起来有点快,可能是因为没有检查哈希。

根据经验,我只能假设这些对象是活动的并且保持某种与源 DC 的连接。我之前在 PowerShell 社区扩展 (PSCX) 中见过这种类型的东西,从 Get-TerminalSession 返回的对象表现出类似的行为。我可能是疯了,我很高兴得到纠正,坦率地说,我希望有人有更好的解释。

我还可以在这里提出一些其他建议,但没有像上述那样有影响力。另请注意,我没有测试到底,所以不能保证没有其他问题。