I would like to call openssl via a Powershell script and in case of an error capture stderr to a variable to log the error. Actually, the openssl error is what I want to see here - this is not the problem. My problem is how to deal with the situation when such a problem with openssl occurs.
It works like a charm when I do this directly in the Powershell itself:
PS > $bin = 'C:\OpenSSL-Win32\bin\openssl.exe'
PS > $parm = 'smime', '-encrypt', '-aes-256-gcm', '-outform', 'PEM', '-out', '<SomePathHere>\testd.xml.pem', '-in', '<SomePathHere>\testd.xml', '<SomePathHere>\Zert\part1.pem'
PS > & $bin $parm
Loading 'screen' into random state - done
Error opening recipient certificate file <SomePathHere>\part1.pem
2032:error:02001002:system library:fopen:No such file or directory:.\crypto\bio\bss_file.c:391:fopen('<SomePathHere>\part1.pem','rb')
2032:error:20074002:BIO routines:FILE_CTRL:system lib:.\crypto\bio\bss_file.c:393:
unable to load certificate
unable to write 'random state'
PS > $out = & $bin $parm 2>&1
PS > $out
openssl.exe : Loading 'screen' into random state - done
In Zeile:1 Zeichen:8
+ $out = & $bin $parm 2>&1
+ ~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (Loading 'screen...om state - done:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
Error opening recipient certificate file <SomePathHere>\part1.pem
7228:error:02001002:system library:fopen:No such file or directory:.\crypto\bio\bss_file.c:391:fopen('<SomePathHere>\part1.pem','rb')
7228:error:20074002:BIO routines:FILE_CTRL:system lib:.\crypto\bio\bss_file.c:393:
unable to load certificate
unable to write 'random state'
When I put the same code into a script and run it as a script, stderr does not make it into the variable. Anybody having an idea about what I'm doing wrong?
Here is the relevant code of the script:
$OpenSSLParam = 'smime', '-encrypt', '-aes-256-gcm', '-outform', 'PEM', '-out', $OpenSSLAusgabeDatei, '-in', $OpenSSLEingabeDatei, $OpenSSLSchluessel
$Old_ErrorActionPreference = $ErrorActionPreference
$ErrorActionPreference = 'SilentlyContinue'
$outssl = & $OpenSSLBinary $OpenSSLParam 2>&1
if($LASTEXITCODE -ne 0)
{
$LogMsg = #"
OpenSSL-Fehler!
Exit Code: $LASTEXITCODE
Aufruf: $osslcmd
Error: $outssl
"#
Write-EventLog -LogName Application -Source $EventlogSource -EventId 1002 -EntryType Error -Message $LogMsg
exit $LASTEXITCODE
}
If I run this through the debugger, I get:
PS > <SomePathHere>\encrypt_xml.ps1
Treffer Zeilenhaltepunkt bei "<SomePathHere>\encrypt_xml.ps1:71"
[DBG]: PS >> $LASTEXITCODE
2
[DBG]: PS >> $outssl
[DBG]: PS >>
I'd love to understand why Powershell does not populate the $outssl Variable. Any help is very much appreciated. As a goodie, I'd love to know if there might be a way to only capture the openssl stderr without the stderr of Powershell.
Okay. I have found the problem. Powershell behaves as told!
If I replace
$ErrorActionPreference = 'SilentlyContinue'
with
$ErrorActionPreference = 'Continue'
I get the desired result!
Related
I have this little script, which works fine in PowerShell (outside of VS Code):
$a = #{Portfolio = "CALoan"; Folder = "S:\Data\{yymmdd}"; Filename = "LN{yymmdd}.txt"}
$l = #($a)
$l | ForEach-Object -Process {
$p = ($_.Folder + '\' + $_.Filename).Replace("{yymmdd}", "190911")
if (Test-Path $p) {
[pscustomobject] #{Portfolio = $_.Portfolio; Path = $p; CreateTime = (Get-ChildItem $p).CreationTime}
} else {
[pscustomobject] #{Portfolio = $_.Portfolio; Path = $p; CreateTime = "not found"}
}
} | Out-GridView
However, when viewing the script in the VSCode editor, if I just right-click and choose Run Code (using the Code Runner extension), I get tons of errors like this:
PS C:\Users\me> $l | ForEach-Object -Process {
Missing closing '}' in statement block or type definition.
At line:0 char:0
PS C:\Users\me> $p = ($_.Folder + '\' + $_.Filename).Replace("{yymmdd}", "190911")
PS C:\Users\me> if (Test-Path $p) {
Missing closing '}' in statement block or type definition.
At line:0 char:0
PS C:\Users\me> [pscustomobject] #{Portfolio = $_.Portfolio; Path = $p; CreateTime = (Get-ChildItem $p).CreationTime}
Portfolio Path CreateTime
--------- ---- ----------
\ {2/28/2019 12:41:46 PM, 2/20/2019 12:32:15 PM, 1/24/2019 3:54:50 PM, 3/15/2019 1:46:40 PM...}
PS C:\Users\me> } else {
At line:1 char:1
+ } else {
+ ~
Unexpected token '}' in expression or statement.
At line:1 char:8
+ } else {
+ ~
Missing closing '}' in statement block or type definition.
PS C:\Users\me> [pscustomobject] #{Portfolio = $_.Portfolio; Path = $p; CreateTime = "not found"}
Portfolio Path CreateTime
--------- ---- ----------
\ not found
PS C:\Users\me> }
At line:1 char:1
+ }
+ ~
Unexpected token '}' in expression or statement.
PS C:\Users\me> } | Out-GridView
At line:1 char:1
+ } | Out-GridView
+ ~
Unexpected token '}' in expression or statement.
At line:1 char:3
+ } | Out-GridView
+ ~
An empty pipe element is not allowed.
PS C:\Users\me>
It's as if the integrated terminal is executing one line at a time rather than sending the whole script to PowerShell. What is the right way to do this?
BTW If I start a new PowerShell terminal within VS Code, it works as expected (also using Code Runner). So, what's up with the PowerShell Integrated Console and Code Runner?
I can't speak to the Code Runner extension, but you can bypass the problem by installing the PowerShell extension, which is invaluable for both editing and running PowerShell code in Visual Studio Code.
It allows you to run selected code reliably and faster (because no intermediate script file and external PowerShell process is involved - see below) by:
either pressing F8
or right-clicking the selected text and clicking Run Selection.
Caveat:
By default, code you run via the PowerShell extension executes in the same PowerShell session, so that subsequent invocations can be affected by previous ones; e.g., if you highlight a line containing (++$i) and run it repeatedly, the value of $i keeps incrementing.
Turning on setting Create Temporary Integrated Console (via File > Preferences > Settings or Ctrl+,) can be used to change that, so that every invocation creates a new, temporary session to run in.
Please, observe:
The method
PS C:\> (Get-Command Invoke-SilentlyAndReturnExitCode).ScriptBlock
param([scriptblock]$Command, $Folder)
$ErrorActionPreference = 'Continue'
Push-Location $Folder
try
{
& $Command > $null 2>&1
$LASTEXITCODE
}
catch
{
-1
}
finally
{
Pop-Location
}
PS C:\>
The command to silence
PS C:\> $ErrorActionPreference = "Stop"
PS C:\> $Command = { cmd /c dir xo-xo-xo }
PS C:\> & $Command > $null 2>&1
cmd : File Not Found
At line:1 char:14
+ $Command = { cmd /c dir xo-xo-xo }
+ ~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (File Not Found:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
PS C:\>
As you can see, it fails with an exception. But we can silence it easily, right?
PS C:\> $ErrorActionPreference = 'SilentlyContinue'
PS C:\> & $Command > $null 2>&1
PS C:\> $LASTEXITCODE
1
PS C:\>
All is good. Now my function does the same, so let us try it:
PS C:\> $ErrorActionPreference = "Stop"
PS C:\> Invoke-SilentlyAndReturnExitCode $Command
-1
PS C:\>
Yikes! It returns -1, not 1.
The problem appears to be that setting $ErrorActionPreference inside the function does not actually propagate to the command scope. Indeed, let me add some output:
PS C:\> (Get-Command Invoke-SilentlyAndReturnExitCode).ScriptBlock
param([scriptblock]$Command, $Folder)
$ErrorActionPreference = 'Continue'
Push-Location $Folder
try
{
Write-Host $ErrorActionPreference
& $Command > $null 2>&1
$LASTEXITCODE
}
catch
{
-1
}
finally
{
Pop-Location
}
PS C:\> $Command = { Write-Host $ErrorActionPreference ; cmd /c dir xo-xo-xo }
PS C:\> Invoke-SilentlyAndReturnExitCode $Command
Continue
Stop
-1
PS C:\>
So, the problem is really around $ErrorActionPreference - why does it not propagate? Powershell uses dynamic scoping, so the command definition should not capture its value, but use the one from the function. So, what is going on? How to fix it?
tl;dr
Because your Invoke-SilentlyAndReturnExitCode function is defined in a module, you must recreate your script block in the scope of that module for it to see the module-local $ErrorActionPreference value of Continue:
# Use an in-memory module to demonstrate the behavior.
$null = New-Module {
Function Invoke-SilentlyAndReturnExitCode {
param([scriptblock] $Command, $Folder)
$ErrorActionPreference = 'Continue'
Push-Location $Folder
try
{
Write-Host $ErrorActionPreference # local value
# *Recreate the script block in the scope of this module*,
# which makes it see the module's variables.
$Command = [scriptblock]::Create($Command.ToString())
# Invoke the recreated script block, suppressing all output.
& $Command *>$null
# Output the exit code.
$LASTEXITCODE
}
catch
{
-1
}
finally
{
Pop-Location
}
}
}
$ErrorActionPreference = 'Stop'
$Command = { Out-Host -InputObject $ErrorActionPreference; cmd /c dir xo-xo-xo }
Invoke-SilentlyAndReturnExitCode $Command
On Windows, the above now prints the following, as expected:
Continue
Continue
1
That is, the recreated $Command script block saw the function-local $ErrorActionPreference value, and the catch block was not triggered.
Caveat:
This will only work if the $Command script block contains no references to variables in the originating scope other than variables in the global scope.
The alternative to avoid this limitation is to define the function outside of a module (assuming you're also calling it from code that lives outside modules).
Background Information
The behavior implies that your Invoke-SilentlyAndReturnExitCode function is defined in a module, and each module has its own domain of scopes (hierarchy of scopes).
Your $Command script block, because it was defined outside that module, is bound to the default scope domain, and even when executed from inside a module, it continues see the variables from the scope domain in which it was defined.
Therefore, $Command still sees the Stop $ErrorActionPreference value, even though for module-originated code inside the function it would be Continue, due to setting a local copy of $ErrorActionPreference inside the module function.
Perhaps surprisingly, it is still the $ErrorActionPreference in effect inside $Command that controls the behavior, not the function-local value.
With a redirection such as 2>$null for *>$null in effect while Stop is the effective $ErrorActionPreference value, the mere presence of stderr output from an external program - whether it indicates a true error of not - triggers a terminating error and therefore the catch branch.
This particular behavior - where the explicit intent to suppress stderr output triggers an error - should be considered a bug, and has been reported in this GitHub issue.
The general behavior, however - a script block executing in the scope in which it was defined - while non-obvious, is by design.
Note: The remainder of this answer is its original form, which contains general background information that, however, does not cover the module aspect discussed above.
*> $null can be used to silence all output from a command - no need for suppressing the success output stream (>, implied 1>) and the error output stream (2>) separately.
Generally, $ErrorActionPreference has no effect on error output from external programs (such as git), because stderr output from external programs bypasses PowerShell's error stream by default.
There is on exception, however: setting $ErrorActionPreference to 'Stop' actually makes redirections such as 2>&1 and *>$null throw a terminating error if an external program such as git produces any stderr output.
This unexpected behavior is discussed in this GitHub issue.
Otherwise, a call to an external program never triggers a terminating error that a try / catch statement would handle. Success or failure can only be inferred from the automatic $LASTEXITCODE variable.
Therefore, write your function as follows if you define (and call) it outside a module:
function Invoke-SilentlyAndReturnExitCode {
param([scriptblock]$Command, $Folder)
# Set a local copy of $ErrorActionPreference,
# which will go out of scope on exiting this function.
# For *> $null to effectively suppress stderr output from
# external programs *without triggering a terminating error*
# any value other than 'Stop' will do.
$ErrorActionPreference = 'Continue'
Push-Location $Folder
try {
# Invoke the script block and suppress all of its output.
# Note that if the script block calls an *external program*, the
# catch handler will never get triggered - unless the external program
# cannot be found.
& $Command *> $null
$LASTEXITCODE
}
catch {
# Output the exit code used by POSIX-like shells such
# as Bash to signal that an executable could not be found.
127
} finally {
Pop-Location
}
}
Why does a PowerShell script not end when there is a non-zero exit code when using the call operator and $ErrorActionPerference = "Stop"?
Using the following example, I get the result managed to get here with exit code 1:
$ErrorActionPreference = "Stop"
& cmd.exe /c "exit 1"
Write-Host "managed to get here with exit code $LASTEXITCODE"
The Microsoft documentation for the call operator does not discuss what should happen when using call operator, it only states the following:
Runs a command, script, or script block. The call operator, also known as the "invocation operator," lets you run commands that are stored in variables and represented by strings. Because the call operator does not parse the command, it cannot interpret command parameters.
Additionally, if this is expected behaviour, is there any other way to have the call operator cause an error rather than let it continue?
The return code is not a PowerShell error - it's seen the same way as any other variable.
You need to then act on the variable and throw an error using PowerShell for you script to see it as a terminating error:
$ErrorActionPreference = "Stop"
& cmd.exe /c "exit 1"
if ($LASTEXITCODE -ne 0) { throw "Exit code is $LASTEXITCODE" }
In almost all my PowerShell scripts, I prefer to "fail fast," so I almost always have a small function that looks something like this:
function Invoke-NativeCommand() {
# A handy way to run a command, and automatically throw an error if the
# exit code is non-zero.
if ($args.Count -eq 0) {
throw "Must supply some arguments."
}
$command = $args[0]
$commandArgs = #()
if ($args.Count -gt 1) {
$commandArgs = $args[1..($args.Count - 1)]
}
& $command $commandArgs
$result = $LASTEXITCODE
if ($result -ne 0) {
throw "$command $commandArgs exited with code $result."
}
}
So for your example I'd do this:
Invoke-NativeCommand cmd.exe /c "exit 1"
... and this would give me a nice PowerShell error that looks like:
cmd /c exit 1 exited with code 1.
At line:16 char:9
+ throw "$command $commandArgs exited with code $result."
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : OperationStopped: (cmd /c exit 1 exited with code 1.:String) [], RuntimeException
+ FullyQualifiedErrorId : cmd /c exit 1 exited with code 1.
I try to figure how to determine if a command throw with Invoke-Expression fail.
Even the variable $?, $LASTEXITCODE or the -ErrorVariable don't help me.
For example :
PS C:\> $cmd="cat c:\xxx.txt"
Call $cmd with Invoke-Expression
PS C:\> Invoke-Expression $cmd -ErrorVariable err
Get-Content : Cannot find path 'C:\xxx.txt' because it does not exist.
At line:1 char:4
+ cat <<<< c:\xxx.txt
+ CategoryInfo : ObjectNotFound: (C:\xxx.txt:String) [Get-Content], ItemNotFoundExcep
tion
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetContentCommand
The $? is True
PS C:\> $?
True
The $LASTEXITCODE is 0
PS C:\> $LASTEXITCODE
0
And the $err is empty
PS C:\> $err
PS C:\>
The only way I found is to redirect STD_ERR in a file and test if this file is empty
PS C:\> Invoke-Expression $cmd 2>err.txt
PS C:\> cat err.txt
Get-Content : Cannot find path 'C:\xxx.txt' because it does not exist.
At line:1 char:4
+ cat <<<< c:\xxx.txt
+ CategoryInfo : ObjectNotFound: (C:\xxx.txt:String) [Get-Content], ItemNotFoundExcep
tion
+ FullyQualifiedErrorId : PathNotFound,Microsoft.PowerShell.Commands.GetContentCommand
Is it the only and best way to do this ?
I was going crazy trying to make capturing the STDERR stream to a variable work. I finally solved it. There is a quirk in the invoke-expression command that makes the whole 2&>1 redirect fail, but if you omit the 1 it does the right thing.
function runDOScmd($cmd, $cmdargs)
{
# record the current ErrorActionPreference
$ep_restore = $ErrorActionPreference
# set the ErrorActionPreference
$ErrorActionPreference="SilentlyContinue"
# initialize the output vars
$errout = $stdout = ""
# After hours of tweak and run I stumbled on this solution
$null = iex "& $cmd $cmdargs 2>''" -ErrorVariable errout -OutVariable stdout
<# these are two apostrophes after the >
From what I can tell, in order to catch the stderr stream you need to try to redirect it,
the -ErrorVariable param won't get anything unless you do. It seems that powershell
intercepts the redirected stream, but it must be redirected first.
#>
# restore the ErrorActionPreference
$ErrorActionPreference=$ep_restore
# I do this because I am only interested in the message portion
# $errout is actually a full ErrorRecord object
$errrpt = ""
if($errout)
{
$errrpt = $errout[0].Exception
}
# return a 3 member arraylist with the results.
$LASTEXITCODE, $stdout, $errrpt
}
It sounds like you're trying to capture the error output of a native in a variable without also capturing stdout. If capturing stdout was acceptable, you'd use 2>&1.
Redirecting to a file might be the simplest. Using Invoke-Expression for it's -ErrorVariable parameter almost seems like a good idea, but Invoke-Expression has many problems and I usually discourage it.
Another option will look a little cumbersome, but it can be factored into a function. The idea is to merge output streams using 2>&1, but then split them again based on the type of the object. It might look like this:
function Split-Streams
{
param([Parameter(ValueFromPipeline=$true)]$InputObject)
begin
{
$stdOut = #()
$stdErr = #()
}
process
{
if ($InputObject -is [System.Management.Automation.ErrorRecord])
{
# This works well with native commands but maybe not as well
# for other commands that might write non-strings
$stdErr += $InputObject.TargetObject
}
else
{
$stdOut += $InputObject
}
}
end
{
,$stdOut
,$stdErr
}
}
$o, $e = cat.exe c:\xxx.txt 2>&1 | Split-Streams
I have a problem with one of my scripts. Basically it runs very smoothly but it needs some user input from an excel file. Users can cause Errors and thats my problem.
My Script connects via ssh with plink (lil PuTTY) to certain devices and sends them commands.
Now to the real problem: if someone just misspells the FQDN, the script will go on but I need to be reported via Email that one process has failed. If someone entirely screws up the Excel-Sheet the whole Script will fail - and I need to know again. I have already designed some parametres to send me emails. The only problem is errorhandling.
Code:
try {
$FQDN | foreach {
$directory = $dir[$counter]
$errorzahl = $error.count + [int]1
&$process -ssh -l $loginname[$counter] -pw $pw[$counter] $FQDN[$counter] "config global execute $cmd config ftp $filename $FTPADRESS $FTPLOGIN $FTPPW"
$names = $KN[$counter]
if ($error.Count -gt $errorzahl) {
$names = $KN[$counter]
$smtp.Send("$ErrorSender", "$ErrorRecipient", "Powershell Script Error",
"$Emailinhaltlow, Problems: $names")
}
$counter = $counter + [int]1
}
} catch {
$errornachricht = $_.Exception.Message
if ($_.Exception.Message) {
$smtp.Send("$ErrorSender", "$ErrorRecipient", "Powershell Error",
"$Emailinhalt, Probleme mit: $names")
unregister-scheduledjob -Name $jobname -Force
}
}
This doesn't work as it should. I get an email for every single String in the array FQDN, if there is just a single error in the excel sheet and the "try and catch" also sends me the errormail, what shouldn't happen if the scripts finishes its job.
If I just remove this conditional:
if ($error.Count -gt $errorzahl) {
$names = $KN[$counter]
$smtp.Send("$ErrorSender", "$ErrorRecipient", "Powershell Error",
"$Emailinhaltlow, Problems: $names")
}
I don't get any Emails. Even if there are some small errors in the foreach loop.
EDIT ERRORCODES (Powershelloutput in red)
Error - if someone forgets to connect once with ssh and accept the certificate once - !Stops the whole Script!:
plink.exe : The server's host key is not cached in the registry. You
In C:\Users\USER\Desktop\VPNscript.ps1:10 Zeichen:1
+ &$process -ssh -l $loginname -pw $pw $FQDN "y
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (The server's ho...e registry. You:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
have no guarantee that the server is the computer you
think it is.
The server's rsa2 key fingerprint is:
ssh-rsa 2048 c2:ea:62:af:70:14:e4:f0:a0:79:88:45:85:fc:cd:cc
If you trust this host, enter "y" to add the key to
PuTTY's cache and carry on connecting.
If you want to carry on connecting just once, without
adding the key to the cache, enter "n".
If you do not trust this host, press Return to abandon the
connection.
Store key in cache? (y/n)
Not mainly the topic, but if I am already uploading the errors, someone might fix one, without oppening a new question. This is one of them. I can't send the "y" to plink.
Another Error - if the FQDN is Wrong (does not exist - spelling Error in the Excelsheet) - Script will continue, but fails with one line in the excelsheet:
plink.exe : Unable to open connection:
In Zeile:73 Zeichen:1
+ &$process -ssh -l $loginname[$counter] -pw $pw[$counter] $FQDN[$counter] "config ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (Unable to open connection::String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
Host does not exist
EDIT 2:
"y" | &$process -ssh -l $Loginname -pw $pw $FQDN
plink.exe : The server's host key is not cached in the registry. You
In Zeile:6 Zeichen:7
+ "y" | &$process -ssh -l $Loginname -pw $pw $FQDN
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (The server's ho...e registry. You:String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
have no guarantee that the server is the computer you
think it is.
The server's rsa2 key fingerprint is:
ssh-rsa 2048 c2:ea:62:af:70:14:e4:f0:a0:79:88:45:85:fc:cd:cc
If you trust this host, enter "y" to add the key to
PuTTY's cache and carry on connecting.
If you want to carry on connecting just once, without
adding the key to the cache, enter "n".
If you do not trust this host, press Return to abandon the
connection.
Store key in cache? (y/n)
plink failing doesn't throw a (PowerShell) error and thus doesn't increment the $error count. Check $LastExitCode if you want to detect whether the command succeeded or failed.
As for someone "messing up the Excel sheet": how exactly does the script fail in that case? Handling this error will depend greatly on the actual error.
The host key issue can be handled by echoing "y" into the command:
"y" | &$process -ssh -l $loginname[$counter] ...
or by writing the key to the registry before running the command:
$key = "HKCU:\Software\SimonTatham\PuTTY\SshHostKeys"
$val = "0x23,0x3857aee9..."
Set-ItemProperty $key "rsa2#22:$($FQDN[$counter])" $val
&$process -ssh -l $loginname[$counter] ...
Running plink against a non-existing FQDN did not raise a terminating error in my tests:
PS C:\> $process = "plink.exe"
PS C:\> $fqdn = "correct.intern.example.org", "fail.intern.example.org"
PS C:\> nslookup $fqdn[1]
Server: ns.intern.example.org
Address: 10.42.23.1
DNS request timed out.
timeout was 2 seconds.
DNS request timed out.
timeout was 2 seconds.
*** ns.intern.example.org can't find fail.intern.example.org: Non-existent domain
PS C:\> &$process -ssh -l username $fqdn[1] "echo foo"
Unable to open connection:
Host does not exist
not even when I set $ErrorActionPreference = "stop".
However, if you do get a terminating error, it should increment $error.Count as well. Your check won't work, though, because you remember the previous error count plus one:
$errorzahl = $error.Count + [int]1
and then check if the new error count is greater than that:
if ($error.Count -gt $errorzahl) {
...
Try this instead:
$errorzahl = $error.Count
...
if ($error.Count -gt $errorzahl) {
...
You might want to use a function like this ( from psake):
function Exec
{
[CmdletBinding()]
param(
[Parameter(Position=0,Mandatory=1)][scriptblock]$cmd,
[Parameter(Position=1,Mandatory=0)][string]$errorMessage = ($msgs.error_bad_command -f $cmd)
)
& $cmd
if ($lastexitcode -ne 0) {
throw ("Exec: " + $errorMessage)
}
}
Now you can call your external programs like:
Exec { external.exe } "Failed!"
I think is an issue with Powershell 2.0 in 4.0 the error is returned.
To force the error to be thrown append 2>&1 to the external command.
& $process -ssh -l username $fqdn "echo foo" 2>&1