An example of using Paramiko to issue commands to an EC2 instance
An example of using the Python library Paramiko to ‘remote control’ an EC2 instance .
“Do that, Do this”
Spinning off the side of that I’ve taken a look at the Paramiko module which “implements the SSH2 protocol for secure (encrypted and authenticated) connections to remote machines”.
There’s a good article on beginning to use Paramiko “SSH Programming with Paramiko” by Jesse Noller which I found very helpful but there’s enough stuff I had to change to deal with using EC2 and the controlling Python script running on Windows that I thought it would be worth recording my sample script.
So first off I’d seen the comments about Paramiko maybe needing a special compilation step for installation to Windows but I’m pleased to say that’s not true, I downloaded 184.108.40.206 to my Windows Vista machine, did a quick…
python setup.py install
… and it all went very smoothly, just to be sure I tried out an import …
>>> import paramiko
… no problem.
Starting the EC2 instance
I now needed a server to talk to so I used Bellatrix to spin up a micro instance of Ubuntu like this :
python "C:\bin\installed\Python2.6\Scripts\bellatrix" start --security_groups mySecGrp ami-3e9b4957 mykeypair
The arguments you can see here are :
- “mySecGrp” is a Security Group I’ve previously setup via the AWS Management Console;
- ‘ami-3e9b4957’ is the AMI of Ubuntu 10.04 (Lucid Lynx); and
- ‘mykeypair’ is the name of a Key Pair that, again, I’ve previously setup via the AWS Management Console.
When you run that command you get an output that looks like this :
C:\Users\Richard Shea>python "C:\bin\installed\Python2.6\Scripts\bellatrix" start --security_groups mySecGrp ami-3e9b4957 mykeypair 2012-04-19 21:27:03,135 INFO starting EC2 instance... 2012-04-19 21:27:03,180 INFO ami:ami-3e9b4957 type:t1.micro key_name:mykeypair security_groups:mySecGrp new size:None 2012-04-19 21:27:07,657 INFO starting image: ami-3e9b4957 key mykeypair type t1.micro shutdown_behavior terminate new size None 2012-04-19 21:27:08,555 INFO we got 1 instance (should be only one). 2012-04-19 21:27:08,556 INFO tagging instance:i-f1234567 key:Name value:Bellatrix started me 2012-04-19 21:27:12,361 INFO instance:i-f1234567 was successfully tagged with: key:Name value:Bellatrix started me 2012-04-19 21:27:12,361 INFO getting the dns name for instance: i-f1234567 time out is: 300 seconds... 2012-04-19 21:27:34,173 INFO DNS name for i-f1234567 is ec2-10-20-30-40.compute-1.amazonaws.com 2012-04-19 21:27:34,173 INFO waiting until instance: i-f1234567 is ready. Time out is: 300 seconds... 2012-04-19 21:27:34,174 INFO Instance i-f1234567 is running
And the key thing here is that we now now have access to the host name of the EC2 instance we’ve just spun up:
Talking to the EC2 instance
We’re now ready to send commands to our new instance. Making use of some of Jesses code I was able to write :
import paramiko ssh = paramiko.SSHClient() ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) ssh.connect('ec2-107-22-80-32.compute-1.amazonaws.com', username='ubuntu', key_filename='''mykeypair-ssh2-rsa.openssh''') stdin, stdout, stderr = ssh.exec_command("uptime;ls -l;touch mickymouse;ls -l;uptime") stdin.flush() data = stdout.read().splitlines() for line in data: print line ssh.close()
Anyone who’s got this far can probably see what’s happening here, but just to be sure :
- having instantiated an instance of paramiko.SSHClient we’re able to use our private key file and the address of our EC2 server to start an SSH session.
- We then use the exec_command method to submit a string of commands and get back three references to files corresponding to : standard input, standard output and standard error.
- By reading through the standard output file we can print locally the output from the commands executed on the EC2 instance.
The Key Thing
As you can see to identify ourselves to the remote server we’re doing a key exchange. Our private key is ‘mykeypair-ssh2-rsa.openssh’. A point worth mentioning here is that generally I logon to EC2 instances using the excellent PuTTY . The private key files used by default by PuTTY are not in the same format as the ones required by Paramiko so as a result when I first tried this I found Paramiko fell over complaining my ‘key_filename’ argument was ‘not a valid dsa private key file’.
PuttyGen to the rescue
Well the great thing is that PuTTY actually comes with a tool PuttyGen which will import a standard PuTTY key file (foo.ppk) – you need to do ‘Conversions’ | ‘Import Key’ and then ‘Conversions’ | ‘Export OpenSSH Key’
Bear in mind that the way the SSHClient connect method is used above is suitable for an Ubuntu instance as it is by default however you can’t rely on all *nix instance working just that way.
Seeing the Output
Just to close out I’ll show you the output
12:39:45 up 3:12, 0 users, load average: 0.08, 0.02, 0.01 total 0 total 0 -rw-r--r-- 1 ubuntu ubuntu 0 2012-04-19 12:39 mickymouse 12:39:45 up 3:12, 0 users, load average: 0.08, 0.02, 0.01
The combination of Bellatrix allowing you to spin up EC2 instances with a single command and Paramiko allowing you send arbitary commands to those servers is powerful stuff and I’m impressed at the work done by their respective developers. Of course Bellatrix can do ‘for free’ what I’ve used Paramiko to do here are part of it’s Provisioning commands but it was an interesting exercise for me to do my own version of that.