You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何让基于ADSI的PowerShell AD组成员获取函数支持递归?

实现AD组成员递归查询的PowerShell函数修改方案

现有一段PowerShell代码用于获取AD组成员并填充CSV,但不支持递归,无法获取嵌套安全组内的成员,请问如何修改使其支持递归?

原代码如下:

function getGroupMembers($groupName){
    #get the group
    $group = [adsi]$("LDAP://$($groupName)") 

    #set the initial from value
    $from = 0 

    #escape trigger when the $ds.findall() errors
    $all = $false 

    #array for the members of the group
    $members = @() 


    while (! $all) { 
       #catch an error and set all to $true to escape
       trap{$script:all = $True;continue} 

       #top end of the range so initally 0-999. a Range of 1000 is used to make sure it works on all versions of AD
       $to = $from + 999 

       #Query the group object for members using "member;range=$from-$to" to just return the range of objects for this pass.
       #This will generate an error with an invalid range
       $DS = New-Object DirectoryServices.DirectorySearcher($Group,"(objectClass=*)","member;range=$from-$to",'Base') 

       $allResult = $ds.findOne()
       #as the variable name for the group name is not member, but member;range=0-999 etc, the $_.PropertyNames -like 'member;*' catches all instances
       $members += $allResult.properties | foreach {$_.item($_.PropertyNames -like 'member;*')}
       $all = $allResult.properties["member;range=$from-*"].Count -gt 0

       #set up the next search range
       $from += 1000        
    }
    $members
}

修改后的递归版本代码

function getGroupMembers($groupName, [ref]$processedGroups) {
    # 初始化已处理组集合,避免循环引用
    if (-not $processedGroups.Value) {
        $processedGroups.Value = [System.Collections.Generic.HashSet[string]]::new()
    }

    # 若当前组已处理过,直接返回空,防止无限递归
    if (-not $processedGroups.Value.Add($groupName)) {
        return @()
    }

    # 获取目标组对象
    $group = [adsi]"LDAP://$groupName" 

    $from = 0 
    $all = $false 
    $members = @() 

    while (-not $all) { 
        trap{
            $script:all = $true
            continue
        } 

        $to = $from + 999 

        $DS = New-Object DirectoryServices.DirectorySearcher($Group,"(objectClass=*)","member;range=$from-$to",'Base') 

        $allResult = $ds.findOne()
        $currentMembers = $allResult.properties | foreach {$_.item($_.PropertyNames -like 'member;*')}
        
        foreach ($member in $currentMembers) {
            # 获取成员的AD对象
            $memberObj = [adsi]"LDAP://$member"
            # 判断成员是否为组,是则递归查询其成员
            if ($memberObj.objectClass -contains "group") {
                $members += getGroupMembers -groupName $member -processedGroups $processedGroups
            } else {
                # 非组成员直接加入结果集
                $members += $member
            }
        }

        $all = $allResult.properties["member;range=$from-*"].Count -gt 0
        $from += 1000        
    }
    $members
}

# 调用示例(需传入组的完整LDAP路径)
# $allMembers = getGroupMembers -groupName "CN=TestGroup,OU=Groups,DC=domain,DC=com" -processedGroups ([ref]$null)

修改说明

  • 新增递归逻辑:遍历每个成员时,通过objectClass属性判断是否为AD组,若是则递归调用函数获取嵌套成员。
  • 防止循环引用:引入[ref]$processedGroups参数,用哈希集合记录已处理的组,避免组互相嵌套导致的无限递归。
  • 成员分类处理:区分普通成员(用户、计算机等)和组成员,仅对组执行递归操作,普通成员直接加入结果。

内容的提问来源于stack exchange,提问作者Tom

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 10:15:59