无法使用 C# 枚举 AD 组的成员

Failure to enumerate AD Group's members using C#

你能帮帮我吗?我正在尝试使用以下代码从远程计算机枚举 AD 组的成员:

using (var entry = new DirectoryEntry(..
{
    foreach (object member in (IEnumerable)entry.Invoke("Members", null))
    {

除一种环境外,此代码运行良好。

在这个特定的环境中,当我尝试枚举 AD 组的成员时,它抛出以下异常:

System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.

System.ArgumentException: Value does not fall within the expected range. --- End of inner exception stack trace ---

at System.DirectoryServices.DirectoryEntry.Invoke(String methodName, Object[] args)

我 运行 通过 power shell 执行相同的命令(下面复制的脚本)并得到相同的错误:

Exception calling "Invoke" with "2" argument(s): "Value does not fall within the expected range.

At C:\Temp\PSTest_AD_Group_Members2.ps1:23 char:5

  • $members = $DirectoryEntry.Invoke("Members", $null)
  • 
    
  • CategoryInfo : NotSpecified: (:) [], MethodInvocationException
  • FullyQualifiedErrorId : DotNetMethodTargetInvocation

请注意

  1. 我尝试在不同域的多个 DC 上重现它,但没有成功。它只发生在一个特定的环境中
  2. 我尝试在线搜索此异常,但没有找到任何相关内容
  3. 我正在使用域管理员来运行此代码
  4. 代码是 运行ning on Windows Server 2016 with latest updates
  5. 我有一个产生相同行为的 PowerShell 脚本(在下面复制)
  6. 我在事件查看器中没有看到相关条目,表明源计算机和目标计算机都出现了问题

谁能帮我理解为什么这段代码无法仅在那个特定环境中获取 AD 组的成员?

DC 端有没有办法了解哪里出了问题?也许是 incoming/attempted 命令的 DC 日志?

感谢您的帮助

=========================== powershell script ==============================

Add-Type -AssemblyName System.DirectoryServices.AccountManagement
$cred = Get-Credential
$domain = "<domain name>"
$groupname = "<group name>"
$results = "<result csv file path>"

cls

$ctx = New-Object 'DirectoryServices.AccountManagement.PrincipalContext' ([DirectoryServices.AccountManagement.ContextType]::Domain, $domain, $cred.UserName, $cred.GetNetworkCredential().Password)
$timing = Measure-Command {$group = [System.DirectoryServices.AccountManagement.GroupPrincipal]::FindByIdentity($ctx, $groupname)}
$props = [ordered]@{
    'Type' = $group.StructuralObjectClass
    'Name' = $group.SamAccountName
    'SID' = $group.sid
    'RetrivedIn(s)' = $timing.TotalSeconds
    'Retrievedcount' = $group.count
    'UserPrincipalName' = $group.UserPrincipalName
}
New-Object -TypeName PSObject -Property $props | Export-Csv -Path $results -NoTypeInformation

$timing = Measure-Command {
    $DirectoryEntry = New-Object System.DirectoryServices.DirectoryEntry ("LDAPS://$($group.DistinguishedName)", $cred.UserName, $cred.GetNetworkCredential().Password)
    
$members = $DirectoryEntry.Invoke("Members")
}

$props = [ordered]@{
    'Type' = $members.gettype().name
    'Name' = "GroupDirectoryEntry"
    'SID' = "n/a"
    'RetrivedIn(s)' = $timing.TotalSeconds
    'Retrievedcount' = ($members | Measure-Object).count
    'UserPrincipalName' = "n/a"
}
New-Object -TypeName PSObject -Property $props | Export-Csv -Path $results -Append -NoTypeInformation

$members |
ForEach-Object {
    $bytesSid = $_.gettype().InvokeMember("objectSid","GetProperty",$null,$_,$null)
    $sid = New-Object System.Security.Principal.SecurityIdentifier ($bytesSid, 0)
    $timing = Measure-Command {$acct = [DirectoryServices.AccountManagement.Principal]::FindByIdentity($ctx, 4, $sid.value)}
    $props = [ordered]@{
        'Type' = $acct.StructuralObjectClass
        'Name' = $acct.SamAccountName
        'SID' = $acct.Sid
        'RetrivedIn(s)' = $timing.TotalSeconds
        'Retrievedcount' = $acct.count
        'UserPrincipalName' = $acct.UserPrincipalName
    }
    New-Object -TypeName PSObject -Property $props | Export-Csv -Path $results -Append -NoTypeInformation
}
============================= end of powershell script ================================

为什么不使用 GroupPrincipal 而不是反射?

就这么简单:

PrincipalContext ctx = new PrincipalContext(ContextType.Domain,                                                                      
                                            "fabrikam.com",   
                                            "DC=fabrikam,DC=com",   
                                            "administrator",   
                                            "SecretPwd123");  

GroupPrincipal grp = GroupPrincipal.FindByIdentity(ctx,   
                                                   IdentityType.Name,   
                                                   "Domain Admins");  

if (grp != null)  
{  
    foreach (Principal p in grp.GetMembers(true))  
    {  
         Console.WriteLine(p.Name);  
    }  
    grp.Dispose();  
}  

ctx.Dispose();

错误解释得很清楚:

target of an invocation. That is the Members function

System.ArgumentException: Value does not fall within the expected range This most probably refers to the null value.

我找出问题所在。 由于使用 third-party 产品准备其活动目录以获得支持,问题环境在其 AD 环境中进行了自定义。作为集成的一部分,它 运行 架构更新,将“成员”属性添加到他们的 AD 架构中。 当我们在目录条目上调用成员时,这会导致冲突。