受限语言环境下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
适配受限环境的原因
- 无跨作用域变量修改:所有状态通过参数传递和返回值更新,完全不依赖修改脚本作用域的全局变量,符合受限语言模式限制
- 仅使用允许的语法:仅用到标准PowerShell函数参数传递、自定义对象创建和返回,未使用
$script:、Get-Variable -Scope等受限操作 - 从根源避免无限循环:通过传递已扫描集合,每次递归都能检查当前实体是否已处理,彻底阻止无限循环触发
内容的提问来源于stack exchange,提问作者Kevin
相关产品推荐
相关产品推荐

