PowerShell script to SSH to Multiple Linux devices and restart/reboot them - linux

I am comfortable with writing single queries. I am new to writing bash scripts trying to automate the daily stuff. I need help on creating a PowerShell or bash script where I can SSH to multiple Linux devices with same SSH key and then reboot the devices.
I access linux devices manually with the following command in PowerShell
ssh -i C:\<path-to-the-private-key\test.ppk test#XX.X.X.XXX (X - IP Address)
Then I enter the following command
sudo reboot
It asks me to type the password and then restarts the device.
I have 100+ devices that I need to restart.
I can get a list of all IP address in a text file. How can we search for all the IP address in a text file, authenticate with the SSH private key and run the sudo command to restart the device?
Would it also be possible to throw a message if it was not able to restart a device?
Any help would be appreciated.
This is the script that I have.
testreboot.sh
#!/bin/bash
pw="test123"
hosts='IP.txt'
while read -r line; do {
/usr/bin/expect << EOF do
ssh test#"$hosts" 'sudo reboot'
expect "*?assword*"
send "%pw\r"
EOF
}
done < $hosts
IP.txt
XXX.XX.XX.XX
XXX.XX.XX.XX
XXX.XX.XX.XX
XXX.XX.XX.XX
I have Ubuntu 20.04 installed from Windows App Store. I am trying to run the testreboot.sh from PowerShell using the following command and get the following error message.
bash testreboot.sh
testreboot.sh: line 2: $'\r': command not found
testreboot.sh: line 3: $'\r': command not found
testreboot.sh: line 5: $'\r': command not found
testreboot.sh: line 16: syntax error near unexpected token `done'
testreboot.sh: line 16: `done < $hosts'

A better solution to this problem is to use something like Ansible, Chef, or Puppet to solve these multi-server coordination needs.

Here is an example in a shell script using expect to ssh to another server and login:
NOTE: When you use expect in this manner, you need to escape " and $ and
other items. If password has $, it must be escaped.
This is where after logging in and expect see a command prompt
For example:
44 [localhost.localdomain]/home/map%
This is where you would need to add sudo reboot command
-re \"$reg_ex_prompt\" {
}
Test Script:
#!/bin/sh
#
debug=0
exit_val=0
spawn_item="ssh"
destination="user_name#<IP of server to shh>"
reg_ex_prompt='\[%|>|\$|#\] $'
#
# Change -D 0 to -D 1 to debug interactivly
#
expect -D $debug -c "
spawn $spawn_item $destination
set ret 1
set timeout 20
expect {
timeout {
send_user \"Timeout reached!\"
exit \$ret
}
eof {
puts \"End of test connection reached!\"
if { \$ret == 0 } {
puts \"Connection test Successful!\"
puts \"Exiting $destination ...\"
} else {
puts \"Connection Failure!\"
}
exit \$ret
}
\"Connection refused\" {
puts \"ERROR: Trouble connecting to $device_type_parm destination $destination\"
puts \"Aborting...\"
exit \$ret
}
\"Permission denied\" {
puts \"ERROR: User name or password is incorrect for $destination\"
puts \"Aborting...\"
exit \$ret
}
\"Connection reset by peer\" {
puts \"ERROR: Trouble connecting to $destination\"
puts \"Aborting...\"
exit \$ret
}
\"you sure you want to continue connecting\" {
send \"yes\r\"
exp_continue
}
\"assword:\" {
puts \"\r\nSending password\"
send \"password\\\$\r\"
exp_continue
}
\"Choice? \" {
send \"1\r\"
exp_continue
}
\"Q. Quit\" {
send \"q\r\"
exp_continue
}
-re \"$reg_ex_prompt\" {
send \"sudo reboot\r\"
sleep 2
set ret 0
exit \$ret
}
interact
} "
# get the exit value from expect statment above
exit_val=$?

Related

How to access value of a jenkins groovy variable in shell script for loop

When i am passing value of a variable declared in jenkins Groovy script its value is not retained in for loop which is running on a remote server. Strange thing is i am able to access the same value outside the for loop.
Here is the sample code i am trying to use
#!/usr/bin/env groovy
def config
def COMMANDS_TO_CHECK='curl grep hello awk tr mkdir bc'
pipeline {
agent {
label "master"
}
stages {
stage ('Validation of commands') {
steps {
script {
sh """
#!/bin/bash
/usr/bin/sshpass -p passwrd ssh user#host << EOF
hostname
echo $COMMANDS_TO_CHECK ---> This is printed
for CURRENT_COMMAND in \$COMMANDS_TO_CHECK
do
echo ${CURRENT_COMMAND} ---> Why This is not printed?
echo \${CURRENT_COMMAND} ----> Why This is not printed?
done
hostname
EOF
exit
"""
}
}
}
}
}
Output
[workspace#3] Running shell script
+ /usr/bin/sshpass -p passwrd ssh user#host
Pseudo-terminal will not be allocated because stdin is not a terminal.
illinsduck01
curl grep hello awk tr mkdir bc
illinsduck01
+ exit
You can wrap sh in """ ... """ as below
#!/usr/bin/env groovy
def config
pipeline {
agent {
label "master"
}
stages {
stage ('Validation of commands') {
steps {
script {
sh """#!/bin/sh
/usr/bin/sshpass -p password ssh username#hostname << EOF
COMMANDS_TO_CHECK="curl grep hello awk tr mkdir bc"
hostname
echo \$COMMANDS_TO_CHECK
for CURRENT_COMMAND in \$COMMANDS_TO_CHECK
do
echo \$CURRENT_COMMAND
which \$CURRENT_COMMAND
status=\$?
if [ \${status} -eq 0 ]
then
echo \${CURRENT_COMMAND} command is OK
else
echo "Failed to find the \${CURRENT_COMMAND} command"
fi
done
hostname
EOF
exit
"""
}
}
}
}
}

How to automate ctrl+d action in the ssh expect script in bash?

I have a usecase where I need to execute a command after connecting to the host through ssh. After the command execution, I need to perform Ctrl-D and Ctrl-M so that I can issue other commands.
I tried with using EOF but It is completely closing the session.
expect << EOF
spawn ssh -o StrictHostKeyChecking=no LocalCOMUser#$nodeIp -p $ssh_port
expect {
"password:" {}
timeout { send_user "Timed out in ssh connection" ;exit 1}
}
send "p#ssword\r"
expect {
">" {}
timeout { send_user "Timed out in ssh connection" ;exit 1}
}
set timeout 120
send "mml\r"
expect {
"<" {}
timeout { send_user "Timed out in ssh connection" ;exit 1}
}
send "$command1\r"
expect {
"<" {}
}
send "exit;\r"
expect {
">" {}
timeout { send_user "Timed out in ssh connection" ;exit 1}
}
send "exit\r"
EOF
Need a command that does action as Ctrl-d.
You can simulate pressing Ctrl-D by sending the ^D/␄ character:
send "\x04"

Unable to access local variable via a function call through ssh

This the code I,m trying to execute:
#!/bin/bash
set -e -a
err_report() {
echo "Error on line $1"
}
trap 'err_report $LINENO' ERR
IFS=$'\n'
value=($(cat builds.txt))
prevBuild=${value[0]}
prevBuildDir=$prevBuild
currentBuild=${value[1]}
currentBuildDir=$currentBuild
splitPrevBuild=(${prevBuild//./$'\n'})
splitcurrentBuild=(${currentBuild//./$'\n'})
prevBuildSR=${splitPrevBuild[2]}
prevBuildHF=${splitPrevBuild[3]}
prevBuildNum=${splitPrevBuild[4]}
currentBuildSR=${splitcurrentBuild[2]}
currentBuildHF=${splitcurrentBuild[3]}
currentBuildNum=${splitcurrentBuild[4]}
function change {
cd ~/InstallationFiles/${currentBuildSR}/${currentBuildDir}/${currentBuild}extracted
}
change
pwd
ssh -T atao52#kvs-in-hsglap17.in.kronos.com <<-EOF
$(typeset -f)
change
pwd
echo hi
exit
EOF
How to access the variables as currently, it says
cd: /home/atao52/InstallationFiles///extracted: No such file or directory

Execute sudo using expect inside ssh from bash

I want to create a script that automates a installation on multiple linux hosts.
I login to the hosts using ssh keys and inside the login I want to do a sudo, I am trying to use expect, which I have on the stations but I don't have on the server which runs the script.
How do I do this, this is my try, but no luck with it:
#!/bin/bash
ssh user#station04 <<EOF
expect -d -c "
send \"sudo ls\"
expect {
\"password:\" { send '1234'; exp_continue }
\"$user#\"{
send "exit\r"
}
default {exit 1}
}"
EOF
exit
The result:
send: sending "sudo ls" to { exp0 }
expect: does "" (spawn_id exp0) match glob pattern "password:"? no
expect: read eof
expect: set expect_out(spawn_id) "exp0"
expect: set expect_out(buffer) ""
argv[0] = expect argv[1] = -d argv[2] = -c argv[3] =
send "sudo ls\r"
expect {
"password:" { send '1234'; exp_continue }
"#"{
send exitr
}
default {exit 1}
}
set argc 0
set argv0 "expect"
set argv ""
A.K
What about this? <- just make sure of the expected prompts.
#!/bin/bash
expect <<'END'
spawn ssh user#station04
expect "password:"
send "$pw\r"
expect "#"
send "sudo ls\r"
END
I suggest you would use public key authentication for the ssh part, then just use something like:
ssh -t username#server-ip -C "echo sudo-password | /usr/bin/sudo -S ls"
You got the usage of expect not quite right - don't send a command; rather spawn the command and send just its input. So, your script becomes:
ssh … <<EOF
expect -d -c "
spawn sudo ls
expect -nocase password: { send 1234\r }
expect eof
"
exit
EOF

Transfer environment variables with expect

I am writing an expect script and need to transfer environment variables over a telnet session (which the man page proudly touts as a feature but provides no other mention).
So something like this:
#!/usr/bin/expect -c
spawn telnet 1.2.3.4
set rpath ""
expect "#" { set rpath $PATH }
where $PATH is in the environment of the remote system..any ideas?
You could easily do this by spawning bash and then issue telnet to the remote system.
I presume you want to set the path variable from the local machine to the remote machine.
#!/bin/sh
# the next line restarts using tclsh \
exec expect "$0" "$#"
set prompt "~$"
set hostname "anyhost"
spawn bash
send "echo $PATH\r"
expect {
$prompt {
set pathVariable $expect_out(buffer)
}
timeout {
send_user "path hasn't been set - exiting\n"
close
exit 1
}
}
send "telnet $hostname\r"
expect {
"Login:" {}
"telnet: " {
send_error "$argv0 couldn't login to $hostname\n"
exit 1;
}
timeout {
send_error "$argv0 couldn't login to $hostname, timeout of $timeout passed\n"
exit 1;
}
}
send "$username\r"
expect "Password:"
send "$password\r"
expect $remotePrompt
send "bash\r"
send "export PATH=$pathVariable\r"
# continue with whatever you want.

Resources