Acabei de reescrever o script para usar o VIC. O WMI neste ambiente era instável demais para ser usado.
Estou trabalhando em um projeto para reinicializar uma grande quantidade de computadores. Um dos requisitos importantes é preparar as reinicializações para que todas as máquinas não sejam reinicializadas de uma vez (muito rápido e causará problemas na SAN).
Eu tentei fazer isso em um fluxo de trabalho acelerando para 50 ações paralelas e adicionando um atraso de 15 segundos (200 reinicializações por minuto).
workflow Bounce-Computer {
param(
[string[]]$Computers
)
foreach -parallel -throttlelimit 50 ($computer in $Computers) {
Restart-Computer -PSComputerName $computer -Force
Start-Sleep -Seconds 15
}
}
Mas me deparei com um problema em que o fluxo de trabalho seria interrompido se o WMI fosse quebrado em um computador de destino.
Fora de consertar o WMI em todas as máquinas de destino (existem vários milhares), como eu faria algo assim de maneira controlada? Empregos?
Acho que há mais de uma maneira de resolver seu problema, pelo menos pelo que vejo como a causa. Aqui está como eu faria:
Workflow Invoke-MassRestart{
Param
(
[parameter(mandatory=$true,
ValueFromPipelineByPropertyName=$true)]
[string[]]
$ComputerName,
[int]
$Throttle = 5,
[int]
$Delay = 5
)
Foreach -parallel -ThrottleLimit $Throttle ($Computer in $ComputerName){
Sequence {
InlineScript{
[void](Restart-Computer -PSComputerName $using:Computer)
}
Start-Sleep -Seconds $Delay
}
}
}
Deixe-me saber se há alguma parte disso que você quer que eu explique. Gostaria de chamar minha transmissão de Restart-Computer
para o [void]
. Isso basicamente diz ao sistema para emitir o comando e seguir em frente. Eu acho que você estava sendo lavado quando estava esperando por 50 operações para relatar algum tipo de status. Observe também o escopo exclusivo necessário dentro do bloco InlineScript{}
. Eu também decidi fazer isso para que ele pudesse aceitar entrada de pipeline de Get-ADComputer
.
Sem retrabalhar seu script em demasia, você pode deixar o script detectar o erro de tempo limite do WMI (se ele gerar um erro durante o travamento) e reinicializar o próximo computador. A instrução catch pode gerar o problema e o erro do computador para você revisar mais tarde.
Workflow Bounce-Computer {
param([string[]]$Computers)
foreach -parallel -throttlelimit 50 ($computer in $Computers) {
try{
Restart-Computer -PSComputerName $computer -Force -ErrorAction stop
Start-Sleep -Seconds 15
}
catch [AppropriateWMIExceptionError]
{
echo 'WMI timeout error' #Or another action to note the problem computer
}
}
Eu usaria o mecanismo de trabalho e faria algo como abaixo. Isso não é totalmente testado, mas deve funcionar pode ter perdido algo em algum lugar.
Dessa forma, você também faz com que eles sejam executados em paralelo e, se um reinício travar, é apenas esse trabalho que leva tempo e, se um trabalho levar muito tempo, removemos esse trabalho.
Código abaixo:
function restart-myComputer (
[Parameter(ParameterSetName="restart", Mandatory=".")]
[Parameter(ParameterSetName="bg", Mandatory=".")]
[string]$computer,
[Parameter(ParameterSetName="restart")]
[int]$wait,
[Parameter(ParameterSetName="bg")]
[switch]$bg)
{
if ($bg) {
Start-Job -name $computer -ScriptBlock ([scriptblock]::create("Restart-Computer -PSComputerName $computer -Force -ErrorAction stop")) > $null
} else {
Restart-Computer -PSComputerName $computer -Force -ErrorAction stop
Start-Sleep -Seconds $wait
}
}
function remove-myJobs (
[Parameter(ParameterSetName="remove")]
[string[]]$names,
[Parameter(ParameterSetName="remove")]
[int]$maxRunTime = 30
{
$d = get-date
[string[]]$stillRunningJobArr = @()
foreach ($name in $names) {
$job = get-job -name $name
$diffTime = $d - $job.PSBeginTime
if($diffTime.TotalSeconds -gt $maxRunTime) {
remove-job -name $name -Force
write-host "Removed job $name that ran longer then $maxRunTime seconds"
} else {
write-host "Job $name has still not been running for more then $maxRunTime"
$stillRunningJobArr += $name
}
}
return $stillRunningJobArr
}
function remove-allMyJobs (
[Parameter(ParameterSetName="remove")]
[int]$maxRunTime = 30)
{
$d = get-date
foreach ($job in get-job) {
$diffTime = $d - $job.PSBeginTime
$name = $job.name
if($diffTime.TotalSeconds -gt $maxRunTime) {
remove-job -name $name -Force
write-host "Removed job $name that ran longer then $maxRunTime seconds"
} else {
write-host "Job $name has still not been running for more then $maxRunTime"
}
}
}
function restart-myComputers (
[Parameter(ParameterSetName="restart", Mandatory=".")]
[Parameter(ParameterSetName="bg", Mandatory=".")]
[string[]]$computers,
[Parameter(ParameterSetName="bg", Mandatory=".")]
[int]$maxConcurrentJobs,
[Parameter(ParameterSetName="bg", Mandatory=".")]
[Parameter(ParameterSetName="restart")]
[int]$wait,
[Parameter(ParameterSetName="bg")]
[switch]$bg)
{
[string[]]$restartedComputerArr = @()
if ($bg) {
foreach ($computer in $computers) {
if((get-job -state 'Running').Count -gt $maxConcurrentJobs) {
$restartedComputerArr = stop-myJobs -$restartedComputerArr -maxRunTime $maxRunTime
sleep $wait # Wait to get as many jobs to complete as possible.
}
Restart-myComputer -computer $computer -bg
$restartedComputerArr += $computer
}
} else {
Restart-myComputer -computer $computer -wait $wait
}
remove-allMyJobs
}
Tags powershell