Обработка ошибок в ScriptBlock в командлете Invoke-Command

Я пытаюсь установить службу на удаленном компьютере с помощью powershell.

До сих пор у меня есть следующее:

Invoke-Command -ComputerName  $remoteComputerName -ScriptBlock {
         param($password=$password,$username=$username) 
         $secpasswd = ConvertTo-SecureString $password -AsPlainText -Force
         $credentials = New-Object System.Management.Automation.PSCredential ($username, $secpasswd)
         New-Service -Name "XXX" -BinaryPathName "c:\XXX.exe" -DisplayName "XXX XXX XXX" -Description "XXXXXX." -Credential $credentials -ErrorVariable errortext 
         Write-Host("Error in: " + $errortext)
        } -ArgumentList $password,$username -ErrorVariable errortext 


Write-Host("Error out: " + $errortext)

Когда при выполнении New-Service возникает ошибка, ошибка $errortext ErrorVariable устанавливается правильно в ScriptBlock, потому что текст: "Ошибка: показывает ошибку.

ErrorVariable из Invoke-Command не получает set (что я ожидал).

Мой вопрос:

Возможно ли установить ErrorVariable из Invoke-Command в ошибку, которую я получил внутри ScriptBlock?

Я знаю, что я мог бы также использовать InstalUtil, WMI и SC для установки службы, но в данный момент это не актуально.

Ответ 1

Нет, вы не можете установить Errorvariable из вызова Invoke-Command так же, как в скриптблоке.

Но если ваша цель - "обнаруживать и обрабатывать ошибки в скриптблоке, а также возвращать ошибки в контексте вызывающего Invoke-Command", просто выполните это вручную:

$results = Invoke-Command -ComputerName server.contoso.com -ScriptBlock {
   try
   {
       New-Service -ErrorAction 1
   }
   catch
   {
       <log to file, do cleanup, etc>
       return $_
   }
   <do stuff that should only execute when there are no failures>
}

$results теперь содержит информацию об ошибке.

Ответ 2

Список аргументов Invoke-Command - это односторонняя сделка. Вы можете либо вывести переменную ошибки в script, например. в последней строке скриптового блока:

$errortext

или еще лучше, просто не записывайте ошибку через -ErrorVariable вообще. Вывод сценария, включая ошибки, будет возвращаться к вызывающему абоненту даже по удаленному соединению.

C:\> Invoke-Command -cn localhost { Get-Process xyzzy } -ErrorVariable errmsg 2>$null
C:\> $errmsg
Cannot find a process with the name "xyzzy". Verify the process name and call the cmdlet again.
    + CategoryInfo          : ObjectNotFound: (xyzzy:String) [Get-Process], ProcessCommandException
    + FullyQualifiedErrorId : NoProcessFoundForGivenName,Microsoft.PowerShell.Commands.GetProcessCommand
    + PSComputerName        : localhost

В общем, я думаю, что гораздо лучше сохранить ошибки в потоке ошибок, отделенные от нормального вывода.

Ответ 3

В строгом смысле, я считаю, что ответ отрицательный, вы не можете установить Invoke-Command ErrorVariable в содержимое ErrorVariable внутри блока script. ErrorVariable предназначен только для команды, к которой она привязана.

Однако вы можете передать переменную в блоке script в область Invoke-Command. В вашем коде вы запускаете команду "Новый сервис" с помощью -ErrorVariable errortext. Вместо этого создайте свою переменную в области "script", предварительно присвоив имя переменной "script:", например: -ErrorVariable script:errortext. Это делает переменную доступной вне блока script, а также внутри.

Теперь ваша последняя строка Write-Host("Error out: " + $errortext) выведет ошибку, которая была сгенерирована внутри блока script.

Дополнительная информация здесь и здесь.

Ответ 4

Это почти наверняка не "правильный" ответ, но это то, что я использую, когда хочу, чтобы Invoke-Command выдавала ошибку в скрипте.

$error.Clear()
Invoke-Command -ComputerName localhost -ScriptBlock {Command-ThatFails}
$if ($error.Count -gt 0) { throw $error[0] }

Если вы хотите сохранить ошибку в переменной, вы можете сделать следующее:

$error.Clear()
Invoke-Command -ComputerName localhost -ScriptBlock {Command-ThatFails}
$if ($error.Count -gt 0) { $myErrorVariable = $error[0] }