Вопрос или проблема
Как добавить компьютер в массив в пределах Invoke-Command с использованием оператора try/catch?
У меня есть следующий код, который не ведет себя так, как я ожидаю:
$IisServers = "Server1","Server2","Server3"
$Action = "remove"
$FileName = "iisstart.htm"
If ($Action -eq "remove") {
"`n`r*** НАСТРОЙКА IIS НА УДАЛЕННЫХ КОМПЬЮТЕРАХ ***" #| Tee-Object -Append $LogFile
# Запуск команды настройки на массиве - Результаты будут приходить несортированными из-за параллельной обработки invoke-command
Invoke-Command -ComputerName $IisServers -ErrorAction stop {
Try {
# Импорт модуля PowerShell (необходимо только для серверов Win 2k8r2)
Import-Module WebAdministration
# Удаление имени файла из списка, если оно существует
If (Get-WebConfiguration -PSPath 'MACHINE/WEBROOT/APPHOST' -Filter 'system.webServer/defaultDocument/files/add' | Where-Object {$_.value -eq $Using:FileName}) {
Remove-webconfigurationproperty /system.webServer/defaultDocument -name files -atElement @{value=$Using:Filename}
Write-Output "$Env:ComputerName`: '$Using:FileName' удалено из списка документов по умолчанию"
}
Else {
Write-Output "$Env:ComputerName`: '$Using:FileName' не существует в списке документов по умолчанию"
}
}
Catch {
$ErrorMessage = $_.Exception.Message
Write-Output "$Env:ComputerName`: $ErrorMessage"
$ExecutionIssues += $Env:ComputerName
}
$props = @{ComputerName=$ExecutionIssues}
New-Object -Type PSObject -Prop $Props
} #| Tee-Object -Append $LogFile
}
- Цикл If/Else игнорируется, и каждый раз при запуске просто выполняется секция If независимо.
- Try/Catch, похоже, работает для выполнения команд ошибок If, но аргумент “ErrorAction Stop” останавливает сценарий на первой ошибке подключения WinRM, с которой он сталкивается, вместо логирования и продолжения с остальными.
- Ничего не добавляется в переменную $ExecutionIssues в случае ошибки.
Я что-то очевидное упустил?
Ответ Кита в следующем посте дал мне подходящее решение:
https://stackoverflow.com/questions/12600921/handle-errors-in-scriptblock-in-invoke-command-cmdlet
Использование этого без оператора try/catch решило все вышеуказанные проблемы.
Ответ или решение
Теория
Когда вы работаете с PowerShell и используете Invoke-Command
, важно правильно организовать обработку ошибок и управление данными. Особенно если вы параллельно отправляете команды на несколько удалённых серверов, как в вашем случае с $IisServers
. Для управления ошибками обычно используют конструкцию Try/Catch
, чтобы перехватывать и обрабатывать исключения. Однако в вашем случае ErrorAction Stop
останавливает выполнение скрипта при первой же ошибке, что мешает обработке других серверов. Также важно правильно инициализировать и использовать переменные, такие как $ExecutionIssues
, чтобы накапливать данные об ошибках.
Пример
Рассмотрим ваш код:
Invoke-Command -ComputerName $IisServers -ErrorAction stop {
Try {
Import-Module WebAdministration
If (Get-WebConfiguration -PSPath 'MACHINE/WEBROOT/APPHOST' -Filter 'system.webServer/defaultDocument/files/add' | Where-Object {$_.value -eq $Using:FileName}) {
Remove-webconfigurationproperty /system.webServer/defaultDocument -name files -atElement @{value=$Using:Filename}
Write-Output "$Env:ComputerName`: '$Using:FileName' removed from Default Document list"
} Else {
Write-Output "$Env:ComputerName`: '$Using:FileName' doesn't exist in Default Document list"
}
} Catch {
$ErrorMessage = $_.Exception.Message
Write-Output "$Env:ComputerName`: $ErrorMessage"
$ExecutionIssues += $Env:ComputerName
}
$props = @{ComputerName=$ExecutionIssues}
New-Object -Type PSObject -Prop $Props
}
Применение
- Обработка ошибок: Вместо того чтобы останавливать скрипт при ошибке с помощью
ErrorAction Stop
, лучше использоватьContinue
и регистрировать ошибки в переменной. ИзменитеErrorAction
или удалите его. - Инициализация переменных: Убедитесь, что переменная
$ExecutionIssues
инициализирована как массив внеInvoke-Command
, чтобы к ней можно было добавлять данные:$ExecutionIssues = @()
- Использование массива: Добавление данных в массив лучше производить с помощью подходящих методов. Внутри
Invoke-Command
правильно накопите имена компьютеров с проблемами:Catch { $ErrorMessage = $_.Exception.Message Write-Output "$Env:ComputerName`: $ErrorMessage" $ExecutionIssues = $ExecutionIssues + $Using:Env:ComputerName }
- Корректная работа с выводом: Если необходимо возвращать объекты с информацией, убедитесь, что они создаются и возвращаются правильно.
Таким образом, вы сможете корректно обрабатывать ошибки и накапливать нужные данные в массиве, обеспечив параллельное выполнение на всех серверах из $IisServers
. Если у вас остались вопросы или требуются дополнительные пояснения, рассмотрите использование -ErrorVariable
для накопления ошибок на уровне cmdlet, чтобы глубже разобраться в механике работы ошибок в PowerShell.