How to catch plink failure when host not known - powershell

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

Related

Why is Export-PnPFlows Skipping Flows?

This is my first post, so please pardon formatting errors!
I've been trying to export my tenant's Power Automate flows via Export-PnPFlow. I have a few hundred flows, so doing it by hand isn't really feasible.
The script works well enough for some flows, but is throwing an error for others, but I can't see why.
It does not seem to be caused by if it's enabled/disabled, owned by a certain user, in a certain environment, or in/out of a solution.
The ones that work, work perfectly; the others give the following error:
Export-PnPFlow : {"error":{"code":"ConnectionAuthorizationFailed","message":"The caller object id is '08#####-#####-####-###'. Connection '2#####-#####-####-####' to 'shared_logicflows' cannot be used to activate this flow, either because
this is not a valid connection or because it is not a connection you have access permission for. Either replace the connection with a valid connection you can access or have the connection owner activate the flow, so the connection is shared with you in the context of
this flow."}}
At C:\Users\jutrust\script.ps1:13 char:21
+ ... Export-PnPFlow -Environment $environment -Identity $flow. ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Export-PnPFlow], HttpRequestException
+ FullyQualifiedErrorId : System.Net.Http.HttpRequestException,PnP.PowerShell.Commands.PowerPlatform.PowerAutomate.ExportFlow
My question is, is it possible that these flows are deleted and that's why I get this error? If so, how can I check?
Code below.
Connect-PnPOnline -url https://########.sharepoint.com
$environments = get-pnppowerplatformenvironment
foreach($environment in $environments)
{
$flows = Get-PnPFlow -Environment $environment -AsAdmin
foreach ($flow in $flows)
{
$filename = $flow.Properties.DisplayName.Replace(" ", "")
$timestamp = Get-Date -Format "yyyymmddhhmmss"
$exportPath = "$($filename)_$($timestamp)"
$exportPath = $exportPath.Split([IO.Path]::GetInvalidFileNameChars()) -join '_'
Export-PnPFlow -Environment $environment -Identity $flow.Name | Out-File "C:\Users\jutrust\documents\$exportPath.json"
}
}
Help!
Edit: Updated error code

RTC SCM Query workspace exit code

I am trying to write a query to see if a workspace with specific name already exists in repository or not. Following is the query I am using after logging in a previous command:
$wsQueryResult = &scm query -r https://myrationaluri -w "name='myworkspace"
I get output similar to following:
At C:\scripts\vso-rtc\get-code-fromRTC.ps1:27 char:19
+ $wsQueryResult = &scm query -r myurl -w "name='my ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (Problem running 'query'::String) [], RemoteException
+ FullyQualifiedErrorId : NativeCommandError
No repository workspace matches the query "name='myworkspace'".
The query is inside a powershell script. I understand the last part of the response "No repository workspace matches the query..." However, why do I get Problem running query::String. The value of $wsQueryResult is null after this execution. I was hoping that there will be an exit code or something.
Is the problem in my query syntax or is this normal. If it is normal, do I need to examine the output for line "no repository workspace"
Thank you for your help.
I ended up examining the output from query and determining if the workspace was found or not. I used following code:
$workspaceName = $env:USERNAME + "-" + $patch
$output=scm query -r $rtcURI -w "name='$workspaceName'" *>&1
$output = $output | Select-String "No repository workspace matches"
if ($output -like "No repo*") {
write-host "Repository doesn't exist, will create new one"
scm create workspace -r $rtcURI -s $streamName $workspaceName
} else {
write-host "Workspace $workspaceName Already exists... will refresh"
}

Powershell does not capture stderr to variable in script

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!

Powershell error - GetHostByAddress

Stumped on a problem here - running Powershell 1.0
Code (Assume an ip address which is valid is being passed in):
$ips = #(Import-CSV $attachmentLocation)
foreach ($ip in $ips){
$ipAddress = $ip.IPAddress
$length = $ipaddress.length
write-host "Length is: ($length)"
if(Test-Connection -ComputerName $ipAddress -Count 1 -ea silentlycontinue) {
write-host $ipAddress
$hostData = ([Net.Dns]::GetHostByAddress($ipAddress)).HostName
}
}
Output:
Length is: (11)
10.xx.xx.xx
Exception calling "GetHostByAddress" with "1" argument(s): "The requested name is valid, but no data of the requested type was found"
At FileName:13 char:43
+ $hostData = ([Net.Dns]::GetHostByAddress <<<< ($ipAddress)).HostName
+ CategoryInfo : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : DotNetMethodException
If I run the following it works fine - seems to be data type issue (im passing in a string value):
$hostData = ([Net.Dns]::GetHostByAddress("10.xx.xx.xx")).HostName
Working Code:
$ipAddress = "10.xx.xx.xx"
$hostData = ([Net.Dns]::GetHostByAddress($ipAddress)).HostName
Answer:
Issue was with the ActiveDirectory Domain DNS resolution not the command, while some IP addresses were pingable they were not resolving properly on machine where the script was run. This was causing the error 'no data of the requested type was found' refers to the fact it couldn't resolve the IP to a DNS name.
I've got two ideas you could try:
GetHostByAddress() supports string and ipaddress. So try casting to ipaddress-type before running the function.
if(Test-Connection -ComputerName $ipAddress -Count 1 -ea silentlycontinue) {
write-host $ipAddress
$hostData = ([Net.Dns]::GetHostByAddress(([ipaddress]$ipAddress))).HostName
}
If you're on PS 1.0, then your first priority should be to upgrade the machines to at least PowerShell 2.0. Nothing works well in PS 1.0.
Running PS3, I get the error shown when traversing subnets on our domain. Especially remote locations.
I'm checking 5 different C-class subnets on our domain to make sure there aren't devices we don't have in AD.
It's also possible some devices aren't PCs with a hostname: routers, switches, firewalls, scanners, and the like.
When my code gets to my local office, no error.
I'm not using a file, instead I generate the subnets via code in the script.

How to validate Windows account and password with Perl?

I have solved this in Powershell 2.0, but need to port to Perl 5.10 for a legacy app.
I have Windows service account credentials which cannot INTERACTIVELY LOGON passed to a perl script as $Account and $Password. The $Account variable includes the domain name and AD user account name. My Powershell solution (one-liner) is:
PS C:\> New-PsSession -Credential (new-object -typename System.Management.Automation.PSCredential -argumentlist '$Account', (ConvertTo-SecureString '$Password' -AsPlainText -Force)); exit-PsSession;
If the new PsSession gets created then the account validation passed and I get output to STDOUT like:
Id Name ComputerName State ConfigurationName Availability
-- ---- ------------ ----- ----------------- ------------
1 Session1 localhost Opened Microsoft.PowerShell Available
If the validation failed I get output like this to STDERR:
[localhost] Connecting to remote server failed with the following error message
: Access is denied. For more information, see the about_Remote_Troubleshooting
Help topic.
+ CategoryInfo : OpenError: (System.Manageme....RemoteRunspace:Re
moteRunspace) [], PSRemotingTransportException
+ FullyQualifiedErrorId : PSSessionOpenFailed
I can parse these results in my program. I want to do a similar thing in Perl 5.10 on Windows. All I want to do is test that a password works for an account bearing in mind that interactive logon is denied. Any ideas?
A completely different approach is to contact AD directly using Net::LDAP, but then you have a lot of new challenges.
I also used something like the following (calling win ole) long time ago. But had to abandon on win200X-server i think. If it may be of any help: (Please excuse poor coding)
But this will actually check that the current user is a member of a group.
I presume it may be used to verify username pwd somehow as well.
require Win32::OLE;
sub GetObject {
Win32::OLE->GetObject(#_);
}
my ( $module, $database, $mode ) = #_;
my $env = {%ENV}; #makes a copy, makes it possible to override $ENV for test etc
my $oRoot = GetObject("LDAP://rootDSE");
my $domain_name = $oRoot && $oRoot->Get("defaultNamingContext");
my $domain_host = $oRoot && $oRoot->Get("dnsHostName");
$domain_host .= "/" unless $domain_host =~ /\/$/;
my $strAttributeName ="sAMAccountname"; #could be userPrincipalName, cn etc
my #user_parts = ($user_name); # the last one (i.e. -1) will be the user name
my $alias = $user_name;
my $group_prefix = $system_info_defs->{adAuthGroupPrefix};
my $strADsPath = "LDAP://$domain_host$domain_name";
my $objConnection = Win32::OLE->new("ADODB.Connection")
or do{warn "Cannot create ADODB Connection!";return undef};#die ("Cannot create ADODB object!");
my $objCommand = Win32::OLE->new("ADODB.Command")
or do{warn "Cannot create ADODB command!";return undef};#die ("Cannot create ADODB object!");
$objConnection->{Provider} = ("ADsDSOObject");
$objConnection->Open();
$objCommand->{ActiveConnection} = ($objConnection);
etc. etc.
use a system call from perl i.e. backticks
my $a = `powershell -command "ls;exit;"`;
if ($a =~ /pl$/) {
print "folder contains a perl file";
} else {
print "folder contains no perl file (strange)";
}
if this works then replace the simple ls-command with your more complex text.
Probalby you may need to elaborate on \" " ' \' "" and '' to get it right.