Azure VM并行恢复PowerShell脚本故障求助
Azure VM并行恢复PowerShell脚本故障排查
针对你提供的并行恢复脚本,以下是核心问题点及修复方案:
Runspace池初始化参数错误
原脚本中创建RunspacePool的代码:$runspacePool = [RunspaceFactory]::CreateRunspacePool(1, [System.Management.Automation.Runspaces.Runspace]::DefaultRunspace)第二个参数应为最大并发数(整数),而非
DefaultRunspace对象。参数类型不匹配会直接导致Runspace池创建失败。修改为:# 可根据实际需求调整最大并发数,示例设为10 $runspacePool = [RunspaceFactory]::CreateRunspacePool(1, 10)Runspace未继承Azure上下文
每个Runspace是独立会话,默认不会继承当前PowerShell会话的Azure登录上下文。单VM恢复正常是因为在当前会话已登录,但并行Runspace中无登录信息,会导致Restore-VMAndSnapshots执行失败。
修复方案:将当前Azure上下文传递到每个Runspace,修改脚本块部分:# 在函数开头获取当前Azure上下文 $currentAzContext = Get-AzContext foreach ($vmInfo in $VMsInfo) { $job = [PowerShell]::Create().AddScript({ param ($info, $azContext) # 切换到目标上下文 Set-AzContext -Context $azContext | Out-Null Restore-VMAndSnapshots -SubscriptionId $info.SubscriptionId -ResourceGroupName $info.ResourceGroupName -VMName $info.VMName }).AddArgument($vmInfo).AddArgument($currentAzContext) $job.RunspacePool = $runspacePool $jobs += $job }自定义函数未在Runspace中加载
Restore-VMAndSnapshots是自定义函数,默认不会被加载到新的Runspace中,导致Runspace无法找到该函数。
修复方案:将函数定义注入到Runspace的初始化会话状态中,在创建RunspacePool前添加:$iss = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault() # 将自定义函数添加到会话状态 $functionDef = Get-Content Function:\Restore-VMAndSnapshots -Raw $iss.Commands.Add((New-Object System.Management.Automation.Runspaces.SessionStateFunctionEntry('Restore-VMAndSnapshots', $functionDef))) # 使用初始化会话状态创建RunspacePool $runspacePool = [RunspaceFactory]::CreateRunspacePool(1, 10, $iss, $host)缺失错误处理机制
原脚本未捕获BeginInvoke/EndInvoke过程中的错误,无法定位具体失败原因。添加错误处理:$results = $jobs | ForEach-Object { $_.BeginInvoke() } foreach ($job in $jobs) { try { $job.EndInvoke($results[$jobs.IndexOf($job)]) # 检查是否有错误 if ($job.HadErrors) { Write-Error "VM恢复失败: $($job.Streams.Error[0].Exception.Message)" } } catch { Write-Error "执行恢复时出错: $_" } finally { $job.Dispose() } }
修复后的完整示例脚本
# 假设Restore-VMAndSnapshots函数已在当前会话定义 function Restore-VMAndSnapshots { param ( [string]$SubscriptionId, [string]$ResourceGroupName, [string]$VMName ) # 原单VM恢复逻辑 # ... } function Restore-VMsInParallel { param ( [array]$VMsInfo ) # 获取当前Azure上下文 $currentAzContext = Get-AzContext # 创建初始化会话状态,注入自定义函数 $iss = [System.Management.Automation.Runspaces.InitialSessionState]::CreateDefault() $functionDef = Get-Content Function:\Restore-VMAndSnapshots -Raw $iss.Commands.Add((New-Object System.Management.Automation.Runspaces.SessionStateFunctionEntry('Restore-VMAndSnapshots', $functionDef))) # 创建正确的Runspace池,设置最小/最大并发数 $runspacePool = [RunspaceFactory]::CreateRunspacePool(1, 10, $iss, $host) $runspacePool.Open() $jobs = @() foreach ($vmInfo in $VMsInfo) { $job = [PowerShell]::Create().AddScript({ param ($info, $azContext) Set-AzContext -Context $azContext | Out-Null Restore-VMAndSnapshots -SubscriptionId $info.SubscriptionId -ResourceGroupName $info.ResourceGroupName -VMName $info.VMName }).AddArgument($vmInfo).AddArgument($currentAzContext) $job.RunspacePool = $runspacePool $jobs += $job } $results = $jobs | ForEach-Object { $_.BeginInvoke() } # 处理结果并捕获错误 foreach ($job in $jobs) { try { $output = $job.EndInvoke($results[$jobs.IndexOf($job)]) if ($job.HadErrors) { Write-Error "VM [$($job.Arguments[0].VMName)] 恢复失败: $($job.Streams.Error[0].Exception.Message)" } else { Write-Host "VM [$($job.Arguments[0].VMName)] 恢复成功" } } catch { Write-Error "处理VM [$($job.Arguments[0].VMName)] 时出错: $_" } finally { $job.Dispose() } } $runspacePool.Close() $runspacePool.Dispose() } # 读取CSV并转换为VM信息数组 $csvFilePath = "C:\CSV\file.csv" $vms = Import-Csv -Path $csvFilePath $vmInfoArray = $vms | ForEach-Object { [PSCustomObject]@{ ResourceGroupName = $_.ResourceGroupName VMName = $_.VMName SubscriptionId = $_.SubscriptionId } } # 执行并行恢复 Restore-VMsInParallel -VMsInfo $vmInfoArray
内容的提问来源于stack exchange,提问作者Kalai
相关产品推荐
相关产品推荐

