Hello Guys,
Happy New Year to you all. This is my first post in 2016 and I would love to share something useful for those who play around with AWS EC2 instances. Let’s get started with the prerequisites.
Prerequisites
Before you start, I would love to warn you about python and its discipline around indentation. That’s no joke, unless you are already familiar to python or are a veteran python guru. The second thing to know is to be familiar with how Amazon Web Services(AWS) works or have a system admin who can create an AWS user and its privileges and give you an AWS API access key and API secret key. This post assumes a working knowledge of AWS . So to run this script you need:
- Have AWS IAM user with adequate privileges
- Have python installed
- Have Awscli library
In order to securely use AWS API it’s best to create users with specific privileges. So this two posts http://docs.aws.amazon.com/IAM/latest/UserGuide/id_users_create.html and http://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_managed-using.html#attach-managed-policy-console will help you achieve it. For now let’s say an AmazonEC2FullAccess is required. But this should be restricted further to specifics.
We are going to use python for this and if you are using linux python 2 should come by default with it. For windows folks there are posts out there which can get you there.
The awscli is a python library which is build upon the boto python library which is the python “implementation” of the aws API. There are many other language specific ones, java, php, .Net etc. You can install both boto and awscli through pip
1 2 |
pip install boto pip install awscli |
The Script
Below is the script created at /srv/script/ec2_start_stop.py :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 |
#/usr/bin/python import boto.ec2 import sys, json, urllib2 def main_worker(): ## List of currently available amazon region to check argumeent against. ## Should be updated as soon as amazon opens up a new region aws_regions = ['us-east-1','us-west-2','us-west-1','eu-west-1','eu-central-1','ap-southeast-1','ap-northeast-1','ap-southeast-2','ap-northeast-2','sa-east-1'] ## Checking if we less number of arguments we are expecting. if len(sys.argv) < 4: print_usage() sys.exit(0) else: action = sys.argv[1] if not sys.argv[2] or sys.argv[2].isspace(): print_usage_region() elif not sys.argv[2] in aws_regions: print_usage_wrongregion() if not sys.argv[3] or sys.argv[3].isspace(): print_usage_instance() if action == "start": start_instance(sys.argv[3]) elif action == "stop": stop_instance(sys.argv[3]) else: print_wrongaction() def print_usage(): print "Usage: python ec2_start_stop.py [start|stop] [us-east-1|us-west-2|us-west-1|eu-west-1|eu-central-1|ap-southeast-1|ap-northeast-1|ap-southeast-2|ap-northeast-2|sa-east-1] [instanceid] \n" sys.exit(0) def print_wrongaction(): print "Wrong Action: '{0}' : Action, the first argument should be any of [start|stop]".format(sys.argv[1]) def print_usage_region(): print "Oops!! Looks like region argument is missing: region should be any of [us-east-1|us-west-2|us-west-1|eu-west-1|eu-central-1|ap-southeast-1|ap-northeast-1|ap-southeast-2|ap-northeast-2|sa-east-1]" sys.exit(0) def print_usage_wrongregion(): print "Region should be any of [us-east-1|us-west-2|us-west-1|eu-west-1|eu-central-1|ap-southeast-1|ap-northeast-1|ap-southeast-2|ap-northeast-2|sa-east-1]" sys.exit(0) def print_usage_instance(): print "Oops!! Looks like instance id is missing" sys.exit(0) def start_instance(instance_id): print "Attempt on starting the Instance Id : {0} ".format(instance_id) conn = None instance = None try: ## Get connection to aws EC2. conn = boto.ec2.connect_to_region(sys.argv[2]) except Exception, ec: error = "Oops!! %s " % str(ec) print error sys.exit(0) try: ## Query for account instances passing to the method a list of ## instance ids. In this case only one id is passed instance = conn.get_only_instances(instance_ids=[instance_id])[0] if not instance.state == "running": print "Starting instance %s" % str(instance_id) instance.start() print "Instance %s Started" % str(instance_id) ## Call method to send slack notification to a particular channel slack_notifier("running", instance_id, "None") else: ## This is not an error but can be important for someone to know ## instance was already running so slack is sent a notification print "Oops!! it looks like instance %s is already running" % str(instance_id) slack_notifier("running", instance_id, "was already running") except Exception, ei: ## This block is for when there is a real error while calling the aws api ## Call slack accordingly error = "Oops!! %s" % str(ei) print error slack_notifier("Not running", instance_id, error) sys.exit(0) def stop_instance(instance_id): print "Attempt on stopping the Instance Id : {0} ".format(instance_id) conn = None instance = None try: ## Get connection to aws EC2. conn = boto.ec2.connect_to_region(sys.argv[2]) except Exception, ec: error = "Oops!! %s " % str(ec) print error sys.exit(0) try: ## Query for account instances passing to the method a list of ## instance ids. In this case only one id is passed instance = conn.get_only_instances(instance_ids=[instance_id])[0] if instance.state == "running": print "Stopping instance %s" % str(instance_id) instance.stop() print "Instance %s Stopped" % str(instance_id) slack_notifier("stopped", instance_id, "None") else: ## This is not an error but can be important for someone to know ## instance was already not running so slack is sent a notification print "Oops!! it looks like instance %s is already off" % str(instance_id) slack_notifier("stopped", instance_id, "was already off") except Exception, ei: ## This block is for when there is a real error while calling the aws api ## Call slack accordingly error = "Oops!! %s" % str(ei) print error slack_notifier("Not Stopped", instance_id, error) sys.exit(0) def slack_notifier(status, instance, err): text = "Name: *Luminous Server*\nInstance: *{0}* \nStatus: *{1}*\nError: *{2}*".format(instance,status, err) post = { "text":"{0}".format(text) } try: req = urllib2.Request("https://hooks.slack.com/services/xxxxxxx/xxxxxxx/xxxxxxxxx") req.add_header("Content-Type", "application/json") response = urllib2.urlopen(req, json.dumps(post)) except Exception, em: error = "Oops!! %s" % str(em) print error main_worker() |
Further tweaks and explanations
About the arguments
1 2 3 4 5 6 |
## Checking if we less number of arguments we are expecting. if len(sys.argv) < 4: print_usage() sys.exit(0) else: action = sys.argv[1] |
There is a little explanation I would like to provide about this sys.argv and the fact that we have not used argv[0] anywhere. Argv is the array of argument passed to python and not the script. So to run our script we should type the following command:
1 |
python /srv/script/ec2_start_stop.py start us-east-1 i-xxxxxxx |
Because python is the one passed the command
sys.argv[0] = /srv/script/ec2_start_stop.py (This is the script itself and first argument to python)
sys.argv[1] = start (this is actually the first argument of the script but second argument to python)
sys.argv[2] = us-east-1 (this is the aws region to which we making connection to)
sys.argv[3] = i-xxxxxxx (this is the instance we are either starting or stopping)
About IAM credentials
In this script I have not used the IAM access and secret key directly but rather , in my os installation. After installing awscli , I had configured it using the command aws configure this command will ask for the access key and the secret key and the default aws region. If that was not done, then I should have included the credentials in the script like shown below at the connection part:
1 |
conn = boto.ec2.connect_to_region(sys.argv[2],aws_access_key_id="theaccesskey", aws_secret_access_key="thesecretkey") ## It's advisable to put the access and secret key as global variable after the import section at the top of the script. |
If the server on which the script is run is itself an aws instance, with amazon role assigned to the this instance, there is no need to do aws configure or put the credentials in script
About Slack
In case you might not know what slack is, I can briefly say it’s a team communication plaform a more enterprise whastapp …. kind off .Building a full slack integration for a script is just an overkill. Instead we used slack webhooks to push notifictions to the channel the hook is for. Basically , it takes to post some json object to a url generated in/by slack to have this done nicely.
I hope this will be useful.
I see you don’t monetize your website, don’t waste your traffic, you
can earn extra bucks every month because you’ve got hi quality content.
If you want to know how to make extra bucks, search for:
Mertiso’s tips best adsense alternative