How to catch plink failure when host not known

2019-09-10 18:25发布

问题:

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) 

回答1:

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) {
  ...


回答2:

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!"


回答3:

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