I'm trying to write a script (based on some internet examples) for some telnet/ssh command execution, using powershell and plink. But the main feature, which I'd like to implement - is to catch this command's output into the variable or a text file. How can I intercept the output of the specific command?
For example: when I send "get-status" command, it returns "Status is 01 02 03". Can I assign this string to the var or text file? Maybe, only "01 02 03" without "Status" text?
$ps = New-Object -TypeName System.Diagnostics.Process
$ps.StartInfo.UseShellExecute = $false
$ps.StartInfo.RedirectStandardInput = $true
$ps.StartInfo.FileName = "plink"
$ps.StartInfo.Arguments = "-telnet XXX.XXX.XXX.XXX"
[void]$ps.Start()
$PlinkStreamWriter = $ps.StandardInput
Start-Sleep -m 500
$PlinkStreamWriter.Write("login`r")
Start-Sleep -m 500
$PlinkStreamWriter.Write("password`r")
Start-Sleep -m 500
$PlinkStreamWriter.Write("get-status")
Start-Sleep -m 500
Write-Host "Status string: " .....
$PlinkStreamWriter.Write("exit`r")
$PlinkStreamWriter.Close();
if (!$ps.HasExited) { $ps.Kill() }
If I understand you correctly, when you send
$PlinkStreamWriter.Write("get-status")
You receive
Status is 01 02 03
If this is consistent and you want to assign this value to a variable, just define the variable at the beginning of your command:
$result = $PlinkStreamWriter.Write("get-status")
Now if you want to remove "Status is" from the value stored in $result:
$result = $result -replace "Status is ",""
Now $result contains only "01 02 03"
You can now update your write-host command:
Write-Host "Status string: $result"
A simple redirect (>) is all that is needed. I verified this works for me in PowerShell v5.1.22000.282. Here is my approach for anyone who needs it.
$filepath = "$PSScriptRoot"
$PUTTY="$Env:ProgramFiles\PuTTY\plink.exe"
$commands = #"
y
vncserver -geometry 1920x1080
exit"#
#echoes yes onto putty prompt if it exists and spits output to out.txt
echo $commands | & $PUTTY -ssh $IP -l $USER -pw $PW > "$filepath/out.txt"
This may be of use, you can redirect the outputs from error messages and warning messages into standard out. Example:
Invoke-Expression "cmd /c 'C:\Program Files (x86)\PuTTY\plink.exe'$plinkcmd 4>&1 3>&1 2>&1"
Related
I have a PowerShell script that calls plink.exe regularly. Normally, the two output lines about keyboard-interactive prompts are simply annoying.
However, when run using Start-Job, they get output as error text as soon as I call Receive-Job.
Is there any way to suppress these? I'd rather not suppress all errors.
My test code:
$test_scriptblock = [scriptblock]::Create(#"
param(
`$argumentlist
)
`$pw = `$argumentlist.pw
& 'C:\Program Files\Putty\Plink.exe' -ssh `"admin#*.*.*.*" -pw `$pw -batch whoami
"#)
$testParm = #{
pw = Read-Host "password"
}
$testjob = Start-Job -scriptblock $test_scriptblock -Argumentlist $testParm
$i = 0
do {
$i++
sleep 2
$results = Receive-Job $testjob
ForEach ($result in $results) {
Write-Host $result
}
if ($testjob.State -eq "Completed") {
$jobcompleted = $true
}
If ($i -gt 10) {
Stop-job $testjob
$jobcompleted = $true
}
} until ($jobcompleted)
Just add the stderr redirect to your plink or pscp commandline, to an extra dummy file, like
pscp ... 2> stderr.txt
With a caveat that it may swallow other valid error msgs, at your own risk :)
There's no way to suppress keyboard-interactive prompts in Plink.
I suggest you use a native .NET SSH implementation, like SSH.NET instead of driving an external console application.
Ir was a bit cumbersome, but finally I managed to suppress the "keyboard-interactive" messages this way:
[String] $Plink = 'C:\Program Files\PuTTY\plink.exe'
[Array] $PlinkPar = #("-ssh", "-l", $usr, "-pw", $pwd, $hst) # Set plink parameters
[Boolean] $PlinkOK = $True
Write-Host "Accept possibly unknown host key"
"y" | & $Plink $PlinkPar "exit" 2>&1 | Tee-Object -Variable PlinkOut | Out-Null
$PlinkOut | Foreach-Object {
$PlinkStr = $_.ToString()
If ($_ -is [System.Management.Automation.ErrorRecord]) {
If (! $PlinkStr.Contains("eyboard-interactive")) {
Write-Host "Error: $PlinkStr"
$PlinkOK = $False
}
} else {
Write-Host "$PlinkStr"
}
}
If (! $PlinkOK) { exit }
$PlinkPar += "-batch
And the output is like this:
>powershell .\InstCR.ps1 -usr myuser -pwd mypassword -hst doesnotexist
Accept possibly unknown host key
Error: Unable to open connection:
Error: Host does not exist
This plink call is just to accept a possibly unknown host key (without "-batch" and piping the "y" to answer the prompt). Then "-batch" is added to the Plink parameters to be used on all subsequent plink calls.
Basically the title. My friend provided me a script to batch change RHEL passwords via Powershell and PuTTY, but the new password I entered doesn't work when I try to log in. I think the issue is that it doesn't escape one of the special characters that's in the new password, but I can't figure out what the new password would have been.
The "new password" I used was similar to this: a1b2c3d"4e5f6g7
I attempted to replace the secure strings for regular strings, or use telnet instead of SSH with a packet capture to determine what exactly is being sent, but none of that has worked thus far.
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned
# Displays prompt
Write-Host "This will update the root password on the Linux Servers"
# Get the running directory
$rundirectory = Split-Path $MyInvocation.MyCommand.Path
#$rundirectory = Split-Path $rundirectory
# Get old root credential
$oldrootPassword = Read-Host "Enter old root password" -AsSecureString
$oldrootCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "root", $oldrootPassword
# Get new root credential
$newrootPassword = Read-Host "Enter new root password" -AsSecureString
$newrootCredential = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "root", $newrootPassword
$newrootPassword2 = Read-Host "Retype new root password" -AsSecureString
$newrootCredential2 = New-Object -TypeName System.Management.Automation.PSCredential -ArgumentList "root", $newrootPassword2
# $gc = get-content \linuxservers.txt
if ($newrootCredential.GetNetworkCredential().Password -ceq $newrootCredential2.GetNetworkCredential().Password) {
$templogfile = $rundirectory + "\Temp\log.txt"
$tempchfile = $rundirectory + "\Temp\pwd_changes.txt"
$log = $rundirectory + "\Logs\RHEL\Password_Changes_$(Get-Date -f MMddyyyy).log"
$newrootPassword = $newrootCredential.GetNetworkCredential().Password
$serverlist = $rundirectory + "\linuxservers.txt"
Get-Content $serverlist | %{
# Connects to host and stores SSH key in case it does not have one already
echo y | plink.exe -ssh -pw $oldrootCredential.GetNetworkCredential().Password root#$_ exit
# Opens a session to the server to use for disaster recovery
putty.exe -ssh -pw $oldrootCredential.GetNetworkCredential().Password root#$_
# Adds delay to complete login before password is changed
Start-Sleep -Milliseconds 900
# Command sent to host to change password that is then logged
echo y | plink.exe -ssh -v -pw $oldrootCredential.GetNetworkCredential().Password root#$_ "echo root:'$newrootPassword' | chpasswd" 2>&1 >> $templogfile
# Parses file and stores output in variable
$outpt = cat $templogfile | Select-String "Session sent command exit status"
# Adds server name and variable to changes file
echo `n $_.ToUpper() `n$outpt `n "------------------------------------" >> $tempchfile
# Removes the log file to be used again in loop
Remove-Item $templogfile
# Opens second PuTTY session to make sure password works
putty.exe -ssh -pw $newrootCredential.GetNetworkCredential().Password root#$_
}
} else {
$writehost = "ERROR: New root passwords do not match. Exiting..."
}
if ($writehost -ceq "ERROR: New root passwords do not match. Exiting...") {
Write-Host "ERROR: New root passwords do not match. Exiting..."
} else {
# Places contents of results file in variable
$pwresults = cat $tempchfile
# Adds comment at top of file and creates new results file
echo "Investigate all servers that do not have a command exit status of 0" $pwresults >> $log
# Removes the changes file
Remove-Item $tempchfile
# Opens results file for administrator to investigate
Invoke-Item $log
}
I expected the new password to be a1b2c3d"4e5f6g7; however, this does not work upon login.
Try this. Backslash the doublequote. You might have changed it to the password without the doublequote. You need some way to undo these things if they don't work.
$newrootPassword = $newrootpassword -replace '"','\"'
I am trying to use the Write-Output command in Powershell to write a line to Netcat, but I cannot seem to do this without a new line being sent. So far I have tried...
Write-Output "command" | nc -w1 aa.bb.cc.dd xxxx
and
"command" | nc -w1 aa.bb.cc.dd xxxx
... however both cause new lines to be sent along with "command". Can someone help me find a solution that would be similar to Write-Host -NoNewLine or Echo -n in Linux?
Taking from Can I send some text to the STDIN of an active process under Windows?
$psi = New-Object System.Diagnostics.ProcessStartInfo;
$psi.FileName = "ncat.exe"
$psi.UseShellExecute = $false
$psi.RedirectStandardInput = $true
$psi.Arguments = 'localhost','9990' #ncat connection args
$psi.CreateNoWindow = $true
$p = [System.Diagnostics.Process]::Start($psi)
Start-Sleep -s 2
$p.StandardInput.Write("command") #StandardInput is a StreamWriter
$p.StandardInput.Write("command")
$p.Close()
I'm using a simple net use command to map a network drive
net use \\$HOSTIP $PASSWD /user:$UNAME
i must use net use instead of of New-PSDrive because the scripts runs for more then 400 machines in multiple instances and just wouldn't be doable.
I want to filter the error message then net use return like
System error 64 has occurred.
or
System error 67 has occurred.
How can I do this?
You can do the following:
#set process startup info (redirect stderr)
$pinfo = new-object System.Diagnostics.ProcessStartInfo
$pinfo.Filename = "net.exe"
$pinfo.UseShellExecute = $false
$pinfo.Arguments = #("use","\\$($HOSTIP)","$($PASSWD)","/user:$($UNAME)")
$pinfo.redirectstandardError = $true
#start process and wait for it to exit
$p = New-Object System.Diagnostics.Process
$p.StartInfo = $pinfo
$p.start() | out-null
$p.waitforexit()
#check the returncode
if($p.exitcode -ne 0){
#rc != 0 so we grab the stderr output
$err = $p.standardError.ReadToEnd()
#first line of the output contains the string from your question, matching it against regex
if($err[0] -match "System error ([0-9]*) has occurred"){
#switching the error code
switch($Matches[1]){
64 {do-something64;break;}
67 {do-something67;break;}
}
}
}
This should do the trick, although i cant make a statement about how performant it is, you will have to try. If the output can differ from the string you posted in your question you will have to write your own regexes to handle them.
Keep in mind that the output from net is localized so the regex in my example will not work on systems where the system language is not english.
Hope that helps
Use cmd to redirect stderr to stdout:
$log = cmd /c "2>&1" net use \\$HOSTIP $PASSWD /user:$UNAME
Now the variable contains an array of strings (2 with text and 2 empty):
System error 53 has occurred.
The network path was not found.
You can parse it:
$log = cmd /c "2>&1" net use \\$HOSTIP $PASSWD /user:$UNAME
if ($LASTEXITCODE -and $log -join "`n" -match '^.+?(\d+).+?\n+(.+)') {
$errCode = [int]$matches[1]
$errMessage = $matches[2]
}
I have been trying both standard PS1 using what I read about using:
ECHO y | plink .....
But the issue I have is that plink is being saved on a network drive. I can call it using:
$plink = "P:\path\plink.exe"
echo y | &$plink -ssh -l user-pw password $addrss exit
&$plink -ssh -l user -pw password -m "P:\path\SCOTDiagScript.txt" $addrss
The issue I have is that if I run plink manually via cmd, I get output, that I need to read and possibly display to the user. Piping the last line to Out-File gives me nothing and if I try to call cmd to then redirect to a txt file, I get an error because the path for plink has a space in it; and I am using quotes.
I have also given using .NET a try, but doing a .StandardOutput.ReadToEnd() causes it to hang; deadlock maybe? Even though I am placing it well before the exit and I have it sleep a bit before any type of exit takes place:
$ps = New-Object -TypeName System.Diagnostics.Process
$ps.StartInfo.UseShellExecute = $false
$ps.StartInfo.RedirectStandardInput = $true
$ps.StartInfo.RedirectStandardOutput = $true;
$ps.StartInfo.FileName = "P:\path\plink.exe"
$ps.StartInfo.Arguments = "-ssh -l user -pw password $addrss"
[void]$ps.Start()
Start-Sleep -m 500
$ps.StandardInput.Write("cd c:/scot/bin`r")
Start-Sleep -m 500
$ps.StandardInput.Write("GetDiagFiles.exe`r")
Start-Sleep -m 500
$ps.StandardInput.Write("cd c:/temp`r")
Start-Sleep -m 500
$ps.StandardInput.Write("ls *.zip`r")
Start-Sleep -m 500
$ps.StandardInput.Write("cd c:/scot/monitor`r")
$Out = $ps.StandardOutput.ReadToEnd();
Start-Sleep -s 10
$ps.StandardInput.Write("exit`r")
$PlinkStreamWriter.Close();
if (!$ps.HasExited) { $ps.Kill() }
$Out
I'm sure I'm doing something wrong, but I have scoured over everything at MSDN and nothing.
Doing what you ask should be as easy as:
plink -ssh -l user -pw password -m script.txt > output.log
Where the script.txt contains your commands:
cd c:/scot/bin
GetDiagFiles.exe
cd c:/temp
ls *.zip
cd c:/scot/monitor
exit
If this does not work (as you claim you have tried something similar), give us more details about the problem.
You have not created a System.Diagnostics.ProcessStartInfo object to feed into the System.Diagnostics.Process object.
You needed to put `n at the end of the commands not `r, or you can remove them altogether when using the .WriteLine method
I tested the code below using some cmd.exe and made the alterations you need, it should work with plink.exe
$ProcessStartInfo = New-Object System.Diagnostics.ProcessStartInfo
$ProcessStartInfo.UseShellExecute = $false
$ProcessStartInfo.RedirectStandardError = $true
$ProcessStartInfo.RedirectStandardInput = $true
$ProcessStartInfo.RedirectStandardOutput = $true
$ProcessStartInfo.FileName = "P:\path\plink.exe"
$ProcessStartInfo.Arguments = "-ssh -l user -pw password $addrss"
$Process = New-Object System.Diagnostics.Process
$Process.StartInfo = $ProcessStartInfo
$Process.Start() | Out-Null
$Process.StandardInput.WriteLine("cd c:/scot/bin")
$Process.StandardInput.WriteLine("GetDiagFiles.exe")
$Process.StandardInput.WriteLine("cd c:/temp")
$Process.StandardInput.WriteLine("ls *.zip")
$Process.StandardInput.WriteLine("cd c:/scot/monitor")
$Process.StandardInput.WriteLine("exit")
$Process.WaitForExit()
$stdoutput = $Process.StandardOutput.ReadToEnd()
$erroutput = $Process.StandardError.ReadToEnd()
Write-Host "Standard Output: $stdoutput"
Write-Host "Error Output : $erroutput"
Write-Host "exit code: " + $Process.ExitCode