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

受限语言环境下PowerShell递归函数填充脚本作用域数组的问题

受限语言环境下PowerShell递归遍历AD并检测循环组的解决方案

核心思路

受限语言模式下无法直接修改脚本作用域变量,因此放弃全局数组修改方案,改用递归函数返回结果集+传递已扫描状态参数的方式:

  • 用自定义对象封装四类结果(已扫描用户、循环用户、已扫描组、循环组)
  • 递归调用时传入当前已扫描的用户/组集合,避免重复扫描触发无限循环
  • 函数处理完成后返回更新后的结果集,由顶层脚本汇总所有递归结果

重构后的代码

function Get-ADInfos {
    param(
        [Parameter(Mandatory)]
        $Entry,
        # 传递已扫描状态,避免依赖全局变量
        [Parameter()]
        [PSObject[]] $ScannedUsers = @(),
        [Parameter()]
        [PSObject[]] $ScannedGroups = @(),
        [Parameter()]
        [PSObject[]] $LoopUsers = @(),
        [Parameter()]
        [PSObject[]] $LoopGroups = @()
    )

    # 获取当前实体的成员/所属组
    $entityList = if ($Entry.objectClass -eq 'user') {
        Get-ADPrincipalGroupMembership -Identity $Entry
    } elseif ($Entry.objectClass -eq 'group') {
        Get-ADGroupMember -Identity $Entry
    } else {
        @()
    }

    foreach ($entity in $entityList) {
        if ($entity.objectClass -eq 'user') {
            # 检查用户是否已扫描(用distinguishedName做唯一标识)
            $isScanned = $ScannedUsers.User.distinguishedName -contains $entity.distinguishedName
            if ($isScanned) {
                # 记录循环用户归属
                $loopUserRow = [PSCustomObject]@{
                    User  = $entity
                    Group = $Entry
                }
                $LoopUsers += $loopUserRow
                Write-Host "$($entity.Name) 已扫描过,检测到循环归属"
            } else {
                # 记录已扫描用户
                $scannedUserRow = [PSCustomObject]@{
                    User  = $entity
                    Group = $Entry
                }
                $ScannedUsers += $scannedUserRow
                Write-Host "$($entity.Name) 已加入扫描列表"
                
                # 递归调用并更新状态
                $result = Get-ADInfos -Entry $entity -ScannedUsers $ScannedUsers -ScannedGroups $ScannedGroups -LoopUsers $LoopUsers -LoopGroups $LoopGroups
                $ScannedUsers = $result.ScannedUsers
                $ScannedGroups = $result.ScannedGroups
                $LoopUsers = $result.LoopUsers
                $LoopGroups = $result.LoopGroups
            }
        } elseif ($entity.objectClass -eq 'group') {
            # 检查组是否已扫描(用distinguishedName做唯一标识)
            $isScanned = $ScannedGroups.Group.distinguishedName -contains $entity.distinguishedName
            if ($isScanned) {
                # 记录循环组关系
                $loopGroupRow = [PSCustomObject]@{
                    ScannedGroup = $entity
                    ParentGroup  = $Entry
                }
                $LoopGroups += $loopGroupRow
                Write-Host "$($entity.Name) 已扫描过,检测到循环组"
            } else {
                # 记录已扫描组
                $scannedGroupRow = [PSCustomObject]@{
                    Group = $entity
                }
                $ScannedGroups += $scannedGroupRow
                Write-Host "$($entity.Name) 已加入扫描列表"
                
                # 递归调用并更新状态
                $result = Get-ADInfos -Entry $entity -ScannedUsers $ScannedUsers -ScannedGroups $ScannedGroups -LoopUsers $LoopUsers -LoopGroups $LoopGroups
                $ScannedUsers = $result.ScannedUsers
                $ScannedGroups = $result.ScannedGroups
                $LoopUsers = $result.LoopUsers
                $LoopGroups = $result.LoopGroups
            }
        }
    }

    # 返回当前所有状态结果
    return [PSCustomObject]@{
        ScannedUsers = $ScannedUsers
        ScannedGroups = $ScannedGroups
        LoopUsers = $LoopUsers
        LoopGroups = $LoopGroups
    }
}

# 顶层调用示例
$startGroup = Get-ADGroup -Identity "Domain Users"
$finalResult = Get-ADInfos -Entry $startGroup

# 输出结果
Write-Host "`n已扫描用户列表:"
$finalResult.ScannedUsers | Select-Object @{N='UserName';E={$_.User.Name}}, @{N='GroupName';E={$_.Group.Name}} | Format-Table -AutoSize

Write-Host "`n循环用户归属:"
$finalResult.LoopUsers | Select-Object @{N='UserName';E={$_.User.Name}}, @{N='GroupName';E={$_.Group.Name}} | Format-Table -AutoSize

Write-Host "`n已扫描组列表:"
$finalResult.ScannedGroups | Select-Object @{N='GroupName';E={$_.Group.Name}} | Format-Table -AutoSize

Write-Host "`n循环组关系:"
$finalResult.LoopGroups | Select-Object @{N='ScannedGroupName';E={$_.ScannedGroup.Name}}, @{N='ParentGroupName';E={$_.ParentGroup.Name}} | Format-Table -AutoSize

适配受限环境的原因

  1. 无跨作用域变量修改:所有状态通过参数传递和返回值更新,完全不依赖修改脚本作用域的全局变量,符合受限语言模式限制
  2. 仅使用允许的语法:仅用到标准PowerShell函数参数传递、自定义对象创建和返回,未使用$script:、Get-Variable -Scope等受限操作
  3. 从根源避免无限循环:通过传递已扫描集合,每次递归都能检查当前实体是否已处理,彻底阻止无限循环触发

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.16 10:40:35