PowerShell:ForEach-Object -Parallel与Credential联用为何失效?
问题重现
你编写的脚本通过ForEach-Object -Parallel批量查询计算机本地管理员组,提前用Get-Credential获取了凭据,但同时使用-parallel和-Credential参数时触发报错:
Invoke-Command: Cannot process argument transformation on parameter 'Credential'. A command that prompts the user failed because the host program or the command type does not support user interaction. The host was attempting to request confirmation with the following message: Enter your credentials.
原代码示例:
#Get a list of all systems in an OU. $SearchBase = Get-ADOrganizationalUnit -Filter 'Name -like "*"' | Where-Object -Property DistinguishedName -Like "OU=$OU*" | Select-Object -First 1 $AllObjectsInOU = Get-ADComputer -Filter * -SearchBase $SearchBase | Select-object -Property * $AllObjectsInOU.count #For testing limit the search to the first 5 systems. $allthings = $AllObjectsinOU[0..4] #Get credentials with administrative access to the systems. $credential=Get-Credential $allthings | foreach-object -parallel{ Invoke-Command -ComputerName $_.name -Credential $credential -ScriptBlock {Get-LocalGroupMember -Group 'Administrators'} }
报错原因
ForEach-Object -Parallel会为每个循环项启动独立的PowerShell运行空间,这些运行空间默认和父脚本的作用域隔离,无法直接读取父作用域中的$credential变量。
当并行线程里的Invoke-Command尝试读取$credential时,发现变量在当前运行空间不存在,PowerShell会自动尝试将空值转换为PSCredential类型——这一过程会触发交互式的Get-Credential提示。但并行运行空间不支持用户交互,因此抛出参数转换失败的错误。
而移除-parallel时,循环串行执行,共享同一个运行空间,$credential可以直接访问;移除-Credential时,Invoke-Command使用当前账户凭据,不需要读取$credential,所以两种情况都能正常运行。
解决办法
核心是将父作用域的$credential变量传递到并行运行空间中,推荐以下两种方式:
方法1:使用Using作用域修饰符(首选)
在并行循环内引用$credential时添加Using:前缀,告诉PowerShell从父作用域读取该变量并传递到当前并行运行空间:
$allthings | foreach-object -parallel{ Invoke-Command -ComputerName $_.name -Credential $using:credential -ScriptBlock {Get-LocalGroupMember -Group 'Administrators'} }
方法2:通过-ArgumentList传递变量
如果Using修饰符在特殊场景下无法使用,可以通过ForEach-Object的-ArgumentList参数传递变量,并行循环内通过$_数组的索引获取:
$allthings | foreach-object -parallel{ $targetCred = $_[1] Invoke-Command -ComputerName $_.name -Credential $targetCred -ScriptBlock {Get-LocalGroupMember -Group 'Administrators'} } -ArgumentList $credential
注:此时$_是一个数组,第一个元素是当前的计算机对象,第二个元素是传递的凭据变量。
内容的提问来源于stack exchange,提问作者Bagheera

