从计算机列表随机分配至4个AD组,无重复成员脚本问题
问题描述
需要将指定OU中的计算机随机分配至4个AD组,每台计算机仅能归属一个组。但当前编写的PowerShell脚本存在两个问题:
- 无法正确调用
$groupname执行Add-ADGroupMember操作 - 计算机重复出现在多个组中
现有脚本
$logfile = "results-$((Get-Date).ToString('MM-dd-yyyy_hhmmtt')).log" Get-Date | Out-File $logfile $groupnameprefix = "Automated" $organizationalunitpath="OU paths go here. There are multiple" $grouporganizationalunitpath = "OU paths for AD Security Groups go here" $NumberofComputersPerGroup = "300" # This is a list of computer names that will be used to generate group names $Patch_Groups = @( "_Group_1", "_Group_2", "_Group_3", "_Group_4" ) # Get all computers from the specified organizational unit [System.Collections.Generic.List[object]] $computers = $organizationalunitpath | ForEach-Object { Get-ADComputer -Filter * -SearchBase $_ } # Create the new group foreach ($Patch_Group in $Patch_Groups){ # Generate the group name by concatenating the prefix and the Patch Group name with spaces removed $groupname = $groupnameprefix + $Patch_Group.Replace(" ","") try { # Create the group using the generated name and the specified organizational unit path New-ADGroup -Name $groupName -path $grouporganizationalunitpath -GroupScope Global -verbose } catch { $message = "ADGroup $groupName already exists" $message | Out-File $logfile -Append Write-Warning $message Write-Warning $_.Exception.Message $_.Exception.Message | Out-File $logfile -Append } try { $AddComputerstoRandomADGroups = Get-Random -input $computers -count 300 $computers = $computers | where {$_ -notin $AddComputerstoRandomADGroups} Add-ADGroupMember -Identity $groupname -Members $AddComputerstoRandomADGroups -verbose } catch { $message = ' A problem occurred trying to add Members' $message | Out-File $logfile -Append Write-Warning $message Write-Warning $_.Exception.Message $_.Exception.Message | Out-File $logfile -Append } }
解决思路
一、修复$groupname调用失败问题
- 统一变量大小写:PowerShell变量区分大小写,脚本里生成的是
$groupname(小写n),但创建组时用的是$groupName(大写N),这会导致$groupname未正确赋值,统一改成$groupName即可。 - 改用组对象而非名称:创建组时加上
-PassThru参数直接获取组对象,后续添加成员时用该对象作为-Identity参数,避免因组名重复或识别问题导致失败:$group = New-ADGroup -Name $groupName -Path $grouporganizationalunitpath -GroupScope Global -PassThru -Verbose Add-ADGroupMember -Identity $group -Members $selectedComputers -Verbose - 处理组已存在的场景:如果组已存在,直接用
Get-ADGroup获取组对象,不要跳过这一步,否则后续添加成员会找不到组。
二、解决计算机重复分配问题
- 保持集合可修改性:原脚本中
$computers是List<object>类型,但用where {$_ -notin ...}过滤后返回的是普通数组,不再是List,导致后续Get-Random还是从原始集合中取值。改用List的RemoveAt方法按索引删除已分配的计算机(注意要倒序删除,避免索引偏移):# 随机生成要选中的计算机索引 $selectedIndices = Get-Random -InputObject (0..($computers.Count-1)) -Count $takeCount # 获取选中的计算机 $selectedComputers = $selectedIndices | ForEach-Object { $computers[$_] } # 倒序删除索引,防止前面的删除导致后面的索引错位 $selectedIndices | Sort-Object -Descending | ForEach-Object { $computers.RemoveAt($_) } - 替换硬编码数值:把硬编码的
300改成变量$NumberofComputersPerGroup,同时将该变量从字符串类型转为整数类型($NumberofComputersPerGroup = 300)。 - 处理剩余计算机不足的情况:每次分配前计算可分配的数量,取
每组应分配数和剩余计算机数的最小值,避免因剩余不足报错:$takeCount = [Math]::Min($NumberofComputersPerGroup, $computers.Count)
三、其他优化建议
- 日志输出改用
Add-Content更简洁,或者用Start-Transcript记录所有操作日志。 - 添加计算机总数检查,如果没有找到任何计算机直接退出脚本,避免无效操作。
- 捕获
Get-ADComputer的异常,处理OU路径错误或无权限的情况。
修改后的完整脚本
$logfile = "results-$((Get-Date).ToString('MM-dd-yyyy_hhmmtt')).log" Get-Date | Out-File $logfile $groupnameprefix = "Automated" # 建议改成数组格式,支持多个OU路径 $organizationalunitpath = @("OU=Computers,DC=domain,DC=com", "OU=Laptops,DC=domain,DC=com") $grouporganizationalunitpath = "OU=PatchGroups,DC=domain,DC=com" $NumberofComputersPerGroup = 300 # 转为整数类型 $Patch_Groups = @("_Group_1", "_Group_2", "_Group_3", "_Group_4") # 获取所有目标OU中的计算机,存入List以便修改 [System.Collections.Generic.List[object]] $computers = @() foreach ($ou in $organizationalunitpath) { try { $ouComputers = Get-ADComputer -Filter * -SearchBase $ou -ErrorAction Stop $computers.AddRange($ouComputers) } catch { $message = "获取OU [$ou] 的计算机失败: $($_.Exception.Message)" $message | Out-File $logfile -Append Write-Warning $message } } # 检查是否有计算机 if ($computers.Count -eq 0) { $message = "未找到任何计算机,脚本终止" $message | Out-File $logfile -Append Write-Warning $message exit } foreach ($Patch_Group in $Patch_Groups) { $groupName = $groupnameprefix + $Patch_Group.Replace(" ", "") $group = $null # 创建或获取组对象 try { $group = New-ADGroup -Name $groupName -Path $grouporganizationalunitpath -GroupScope Global -PassThru -Verbose -ErrorAction Stop $message = "成功创建组: $groupName" $message | Out-File $logfile -Append } catch { if ($_.Exception.Message -match "已存在|already exists") { $message = "组 $groupName 已存在,直接获取组对象" $message | Out-File $logfile -Append Write-Warning $message try { $group = Get-ADGroup -Filter { Name -eq $groupName } -SearchBase $grouporganizationalunitpath -ErrorAction Stop } catch { $message = "获取已存在组 $groupName 失败: $($_.Exception.Message)" $message | Out-File $logfile -Append Write-Warning $message continue # 跳过该组的成员添加 } } else { $message = "创建组 $groupName 失败: $($_.Exception.Message)" $message | Out-File $logfile -Append Write-Warning $message continue } } # 分配计算机到组 try { $takeCount = [Math]::Min($NumberofComputersPerGroup, $computers.Count) if ($takeCount -eq 0) { $message = "没有剩余计算机可分配给组 $groupName" $message | Out-File $logfile -Append Write-Warning $message continue } # 随机选择计算机索引 $selectedIndices = Get-Random -InputObject (0..($computers.Count-1)) -Count $takeCount $selectedComputers = $selectedIndices | ForEach-Object { $computers[$_] } # 删除已选中的计算机 $selectedIndices | Sort-Object -Descending | ForEach-Object { $computers.RemoveAt($_) } # 添加成员到组 Add-ADGroupMember -Identity $group -Members $selectedComputers -Verbose -ErrorAction Stop $message = "成功向组 $groupName 添加 $takeCount 台计算机" $message | Out-File $logfile -Append } catch { $message = "向组 $groupName 添加成员失败: $($_.Exception.Message)" $message | Out-File $logfile -Append Write-Warning $message } } $message = "脚本执行完成,剩余未分配计算机数量: $($computers.Count)" $message | Out-File $logfile -Append Write-Host $message
内容的提问来源于stack exchange,提问作者user3911968
相关产品推荐
相关产品推荐

