Have you ever spent a good portion of your day repeatedly logging into all of the network devices in your data center to add the IP address of a new syslog server? Or perhaps removing the local user account of a former peer who jumped ship to greener pastures?
As a Network Engineer there have undoubtedly been times when you have had to make the same configuration change to a large group of devices. Unless you spent a few years honing your scripting skills as a Systems Administrator in the past, chances are that your idea of “automating” the process involved manually logging into devices and pasting the same configuration from a text file over and over.
You know that scripting could help, but who has time to learn to write code when you’re busy researching connectivity issues? After all you’re a Network Engineer and not a Developer, right?
Why Use Expect?
1. It’s Easy!
Expect is a scripting program written in Tcl that was built to automate tasks for other interactive programs, such as Cisco’s IOS and NX-OS command line interfaces. It’s rather intuitive to understand at the basic level.
Once you have a working baseline script, such as the one I am providing below, it’s fast and easy to modify it for use in any situation where you need to make the same configuration change on multiple devices.
2. It’s Already Installed
If you’re primary workstation is a Mac then there is no installation necessary! Expect is already installed in /usr/bin/expect.
3. Excellent Online Resources Available
The more you use Expect, the more you’re going to want to do with it. Luckily you don’t need to take an online course or buy the O’Reilly Media book like you would with Python and Perl. You can Google search your way to just about anything.
Simple Expect Script For Cisco Network Devices
I’m sharing a basic Expect script which you can use to make configuration changes to Cisco IOS and NX-OS devices from a Mac. If you want to run it from a Linux server, just update the path from /usr/bin/expect to wherever the program is located locally.
The script simply establishes an SSH session, enters enable mode (if you’re not already dropped in it), enters configure mode, sends whatever commands you specify, then does a write to memory before exiting and moving onto the next device. You need three files to accomplish this:
device-list.txt
This is simply a text file with IP addresses and/or hostnames of the devices with which you want the script to connect to:
core1.mydomain.com nexus1a nexus1b 10.0.0.1
configure-cisco.sh
This is a bash shell script which you will run to start the entire process. It will prompt for your ssh password and an enable password, and then feed it to the Expect script along with the contents of device_list.txt. I found this more secure then reading the passwords from a file and easier to do in bash than in expect.
#!/bin/bash # Collect the current user's ssh and enable passwords echo -n "Enter the SSH password for $(whoami) " read -s -e password echo -ne '\n' echo -n "Enter the Enable password for $(whoami) " read -s -e enable echo -ne '\n' # Feed the expect script a device list & the collected passwords for device in `cat device-list.txt`; do ./configure-cisco.exp $device $password $enable ; done
configure-cisco.exp
This is the expect script that I explained earlier. It has some intelligence built in to gracefully handle ssh connectivity issues and will send results both to the screen and to a file called results.log in your home directory:
#!/usr/bin/expect -f # Set variables set hostname [lindex $argv 0] set username $env(USER) set password [lindex $argv 1] set enablepassword [lindex $argv 2] # Log results log_file -a ~/results.log # Announce which device we are working on and at what time send_user "\n" send_user ">>>>> Working on $hostname @ [exec date] <<<<<\n" send_user "\n" # Don't check keys spawn ssh -o StrictHostKeyChecking=no $username\@$hostname # Allow this script to handle ssh connection issues expect { timeout { send_user "\nTimeout Exceeded - Check Host\n"; exit 1 } eof { send_user "\nSSH Connection To $hostname Failed\n"; exit 1 } "*#" {} "*assword:" { send "$password\n" } } # If we're not already in enable mode, get us there expect { default { send_user "\nEnable Mode Failed - Check Password\n"; exit 1 } "*#" {} "*>" { send "enable\n" expect "*assword" send "$enablepassword\n" expect "*#" } } # Let's go to configure mode send "conf t\n" expect "(config)#" # Enter your commands here. Examples listed below #send "tacacs-server host 10.0.0.5\n" #expect "(config)#" #send "tacacs-server directed-request\n" #expect "(config)#" #send "tacacs-server key 7 0000000000000\n" #expect "(config)#" #send "ntp server 10.0.0.9\n" #expect "(config)#" #send "ip domain-name yourdomain.com\n" #expect "(config)#" send "end\n" expect "#" send "write mem\n" expect "#" send "exit\n" expect ":~\$" exit
To run the script, create all three files in your Mac home directory, chmod+x the scripts, then run configure-cisco.sh.
[12/07/2012 1:45pm @paul-mac-air:~]% ./configure-cisco.sh Enter the SSH password for paul Enter the Enable password for paul > Working on core1.mydomain.com @ Fri Dec 7 13:45:22 PST 2012 < spawn ssh -o StrictHostKeyChecking=no paul@core1.mydomain.com ...
Examples of Simple Enhancements
The scripts I included above have been stripped down to the bare essentials so that there are fewer lines of code to get started with. It gets the job done, but the more you use the scripts the more you may want to enhance it.
I am by no means an expert in Expect or Bash, but I’ve spent a little time playing with both to help tailor my scripts to do the things I want. The more you use Expect, the more you may want to tailor it as well. Below are a few very basic examples.
1. Add terminal vt100 to for connecting to Nexus devices
I simply got tired of seeing this line every time I connected to a Nexus device:
Nexus 5000 Switch
Password:
Bad terminal type: “xterm-256color”. Will assume vt100.
You can specify a terminal type of vt100 by adding this line to the configure-cisco.sh script (assuming you don’t want to do it in your terminal settings):
# Set terminal to vt100 so Nexus devices don't complain export TERM=vt100
2. Save and rotate logs
I like to log all of the input from my script to a single file called “results.log” which I can then parse later and/or save for reference. To make sure I don’t overwrite a previous log file, I added a few lines of code to configure-cisco.sh to copy and rename “results.log” to “results.log.timestamp”
timestamp=`date +%m%d%H%M` logdir=~/logs/expect # This will rotate and append date stamp... logfile=$logdir/results.log newlogfile=$logfile.$timestamp cp $logfile $newlogfile
Summary
Expect is the perfect tool for easily automating redundant tasks and configuration changes on network equipment. It’s preloaded on Mac OS X and simple to install on server running the Linux flavor of your choice. You don’t need to fully dive in to the language in order to take advantage of it’s time saving capabilities.
//
// ]]>
Hi there,
At first I want to thank you for sharing this wonderful script. It saved me a lot of research time, since it perfectly fits my needs till the login-part was done.
Currently I am working on a troubleshooting-system which monitors interfaces’ health and displays the ports on a website depending on their status (good, alert, critical). Later on, I want the system to repair itself on logical base. If you have to check for physical issues you (the network specialist) get contacted.
Although your post is several years old: If you are interested in working together, please feel free to contact me via e-mail. I also do have some more ideas about Cisco automatization.
Thankful regards
Fred
I know this is an older post, but I’m trying to get this setup and going.
When I run configure-cisco.sh it prompts for passwords then quits with:
“: no such file or directory
I’m not super versed on scripting, so I might be missing something stupid simple.
Things I’ve checked:
All three files are in the same directory. CHMOD applied appropriately.
Things I’ve modified:
I’ve changed the configure-cisco.sh script to allow for using one of our Tacacs usernames.
I’ve changed the configure-cisco.exp script to reflect the username rather than whoami response.
I did some tweaking to the log file for the expect script, not sure if this works, haven’t gotten this far.
set Directory ~/logs
# Log results
log_file -a $Directory/session_$hostname.log
send_log “### /START-SSH-SESSION/ IP: @hostname @ [exec date] ###\r”
Hi Paul, Just wanted to say a quick thanks for this post, I appreciate it was posted best part of 5 years ago but it is still being useful!
Awesome, thank you for taking the time to send this message Curtis!
Thanks for this post. is still working. you are the best
Dude, you are amazing! I really appreciate your work!
It works like a charm. I would like to thank you for sharing such knowledge.
Best regards,
Luiz Oliveira
Great to hear, thanks Luiz!
Thanks for the Great work , I can use it with static passwords but not with RSA secureid (PIN+token) please share any example thanks
Many Thanks Paul for your fantastic work.
Paul hi i Need your help now for meine expect script ,i run this script in my bash for creat vlan Switch but alles Dialoge in Switch Show in my bash .thas means i dont like return script Switch in my bash .please help me
Hi all
am new in expect script ,i need your help for my script.in my script i ask for configure switch (for creat automaticaly vlan and tunking vlan ) its good works. in secondly i said in output show running,its works too,but in finally i want ask user “Are u sure configuration is finished ? ” its for checking configure switch ,if yes exit and if not can return ,but i cant write end of part script
please help me for finally step,can you say where is my problem ?
here is my script :
#!/usr/bin/expect -f
#set variables
set hostname …
set username..
set ip …
foreach hostname [array names interface] {
set timeout 10
match_max 500000
# Log results
log_file -a ~/results.log
send_user “\n”
send_user “>>>> Working on $hostname @ [exec date]<<<< \n"
send_user "\n"
#ssh
spawn ssh -2 -o strictHostKeyChecking=no $username\@$ipaddress
expect "username:"
send "$username\r"
expect "#"
expect "password:"
send "$password\r"
expect -re $prompt
#enable configure mode
send "conf t\n"
expect "(config#)"
#vlan trunking mode
send "$interface($hostname)\n"
expect "(config-if-range)#"
send "switchport access vlan 9\n"
expect "(config-if-range)#"
send "switchport trunk encapsulation dot1q\n"
expect "(config-if-range)#"
send "switchport mode trunk\n"
expect "(config-if-range)#"
send "switchport trunk allowed vlan 7\n"
expect "(config-if-range)#"
send "end\n"
expect "#"
send "write mem\n"
expect "#"
send "terminal length 0\r"
expect "#"
send "show running-config\r"
expect "#"
}
set output $expect_out(buffer)
#Here dont working
exp_sleep 1
stty echo
send_user — "Are you sure configuration is finished?(Y/n):\n"
expect_before "(yes/no)?"
send_user — "\n
if [ "$(#)" != "yes" ];then
[ send_user "exit\n" ]
expect "#"
else
return
exp_send — "\n"
puts "$output"
expect eof
exit
Hallo, Paul,
Your Expect script is very popular and gets cited a lot, quite often.
Because it has staying power it would help greatly to update it to patch the fairly large security hole in the SSH connection steps. Specifically the StrictHostKeysChecking must be set to either “accept-new” or “yes”. Otherwise with it set to “no” it leaves the connection vulnerable to a man-in-the-middle attack such that if an attack occurs, it will have 100% success. Having it set to “no” does not in itself increase the risk of MitM, unless someone finds out about the setting, but it ensures that any MitM attack will always succeed.
A second matter would be to encourage the use of keys for authentication instead of passwords, but that is another matter. However, the important part would be to fix the StrictHostKeysChecking setting to reduce the risks for the copy-pasta crowd.
I agree with your suggestion and will make the change. Thanks for taking the time to leave this comment! – Paul
Thanks for posting the script Paul, can you let us know when you update the StrictHostKeysChecking? Also if I want to remove a username account using the script and it asks me to hit confirm, what would be the command for that in the cisco.exp file? I have the no username portion working, but the script fails when the switch waits for the confirmation or the pressing of the return button on the keyboard.
Hi Paul,
Thanks for your sharing knowledge. That is very wonderfull. I have an question, do we can runninh expect with variable to change current vlan to new vlan. For eq : I want to automatiy change every vlan 10 in every interface will be change to vlan 20.
Thanks.
Hi Paul,
Thanks for sharing the scripts.
I’m trying to use it, but I get the following error:
line 17: ./script.exp: Permission denied
line 17 is:
./script.exp $device $username $password ;
The permissions of the expect file are:
-rwxrwxrwx. 1 xxx xxx 933 Aug 6 17:05 script.exp
I’m not sure what I’m missing here, probably something trivial.
I’d appreciate any guidance.