首页 > 解决方案 > 从 Get-ADObject 服务器超时捕获错误

问题描述

当我从Tim Rhymer运行此脚本时,我从一些域控制器中收到错误,其中服务器由于某种原因超时。当我收到以下错误时,它会暂停脚本 15-30 秒,而不是查询域控制器需要 1-2 秒。我怎样才能捕捉到错误并且不让脚本停顿这么久?以下是该部分

错误信息:

Get-ADObject:无法联系服务器。这可能是因为这个服务器
不存在、当前已关闭或没有 Active Directory
Web 服务正在运行。
在 C:\Users\jimbob\AD_Lookup.ps1:58 char:54
+ ... countName | Get-ADObject -Server $hostname -Properties lastlogon <# - ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~
    + CategoryInfo : ResourceUnavailable: (CN=jimbob...DC=domaincontroller,DC=com:User) [Get-ADObject], ADServerDownException
    + FullyQualifiedErrorId : ActiveDirectoryServer:0,Microsoft.ActiveDirectory.Management.Commands.GetADObject

下面是查询域控制器以从所有域控制器获取上次登录日期的脚本。

Import-Module ActiveDirectory

function Get-ADUsersLastLogon() {
  $dcs = Get-ADDomainController -Filter {Name -like "*"}
  $users = Get-ADUser -Filter *
  $time = 0
  $exportFilePath = "c:lastLogon.csv"
  $columns = "name,username,datetime"

  Out-File -FilePath $exportFilePath -Force -InputObject $columns

  foreach ($user in $users) {
    foreach ($dc in $dcs) { 
      $hostname = $dc.HostName
      $currentUser = Get-ADUser $user.SamAccountName | Get-ADObject -Server $hostname -Properties lastLogon

      if ($currentUser.LastLogon -gt $time) {
        $time = $currentUser.LastLogon
      }
    }

    $dt = [DateTime]::FromFileTime($time)
    $row = $user.Name + "," + $user.SamAccountName + "," + $dt

    Out-File -FilePath $exportFilePath -Append -NoClobber -InputObject $row

    $time = 0
  }
}

Get-ADUsersLastLogon

我认为我需要更改某些内容以捕获错误或绕过导致脚本以下部分之一错误的问题:

$dcs = Get-ADDomainController -Filter {Name -like "*"}

或者

$currentUser = Get-ADUser $user.SamAccountName |
               Get-ADObject -Server $hostname -Properties lastLogon

标签: powershellerror-handlingactive-directory

解决方案


正如您从错误中看到的那样,您得到了一个ADServerDownException. 您可以像这样捕获和处理特定异常:

try {
    $currentUser = Get-ADUser $user.SamAccountName |
                   Get-ADObject -Server $hostname -Properties lastLogon
} catch [Microsoft.ActiveDirectory.Management.ADServerDownException] {
    # handle AD server unreachable
} catch {
    # handle all other exceptions
}

catch如果您希望所有其他异常只是冒泡给调用者而不是自己处理它们,则可以省略最后的毯子。

但是,这不会加快您的代码速度,因为只有在发生超时才会抛出异常。要减少代码的执行时间,您可以

  • 在尝试与之交互之前检查服务器是否可访问,例如

    if (Test-Connection -Computer $hostname -Count 2 -Quiet) {
        $currentUser = ...
        ...
    }
    
  • 使用后台作业并行运行操作

    $jobs = foreach ($dc in $dcs) {
        Start-Job -ScriptBlock {
            Param($username, $hostname)
            Get-ADUser $username |
                Get-ADObject -Server $hostname -Properties lastLogon
        } -ArgumentList $user.SamAccountName, $dc.HostName
    }
    $jobs | Wait-Job | Receive-Job
    $jobs | Remove-Job
    

但是,可以说您的代码中最大的瓶颈是它首先从 AD () 获取所有用户,然后针对每个 DC ( )再次Get-ADUser -Filter *单独查询每个用户,然后针对特定 DC () 运行实际查询。Get-ADUser $user.SamAccountNameGet-ADObject

您应该能够通过针对每个 DC 对所有用户运行单个查询来显着加快代码速度。将结果分组SamAccountName并从每个组中选择具有最新时间戳的记录,然后将结果导出到 CSV。

$fltr  = '(&(objectClass=user)(objectCategory=person))'
$props = 'SamAccountName', 'LastLogon'

Get-ADDomainController -Filter * |
    Select-Object -Expand HostName |
    Where-Object { Test-Connection -Computer $_ -Count 2 -Quiet } |
    ForEach-Object { Get-ADObject -LDAPFilter $fltr -Properties $props -Server $_ } |
    Select-Object Name, SamAccountName,
        @{n='LastLogon';e={[DateTime]::FromFileTime($_.LastLogon)}} |
    Group-Object SamAccountName |
    ForEach-Object { $_.Group | Sort-Object LastLogon | Select-Object -Last 1 } |
    Export-Csv $exportFilePath -NoType

推荐阅读