Automating EC2 With Python

$ python -m venv venv
$ source venv/bin/activate
(venv)$ pip install boto3 pprint awscli
$ aws configure
AWS Access Key ID [****************3XRQ]: **************
AWS Secret Access Key [****************UKjF]: ****************
Default region name [None]:
Default output format [None]:

Creating an EC2 Instance to Work On

Boto3 Session and Client

# awsutilsimport boto3def get_session(region):
return boto3.session.Session(region_name=region)
>>> import awsutils
>>> session = awsutils.get_session('us-east-1')
>>> client = session.client('ec2')
>>> import pprint
>>> pprint.pprint(client.describe_instances())
...

Retrieving EC2 Instance Details

>>> demo = client.describe_instances(Filters=[{'Name': 'tag:Name', 'Values': ['demo-instance']}])
>>> pprint.pprint(demo)
...

Starting and Stopping an EC2 Instance

>>> demo = client.describe_instances(Filters=[{'Name': 'tag:Name', 'Values': ['demo-instance']}])
>>> pprint.pprint(client.terminate_instances(InstanceIds=[instance_id]))
{'ResponseMetadata': {'HTTPHeaders': {'content-type': 'text/xml;charset=UTF-8',
'date': 'Fri, 09 Aug 2019 13:59:20 GMT',
'server': 'AmazonEC2',
'transfer-encoding': 'chunked',
'vary': 'Accept-Encoding'},
'HTTPStatusCode': 200,
'RequestId': '78881a08-0240-47df-b502-61a706bfb3ab',
'RetryAttempts': 0},
'TerminatingInstances': [{'CurrentState': {'Code': 32,
'Name': 'shutting-down'},
'InstanceId': 'i-0c462c48bc396bdbb',
'PreviousState': {'Code': 16, 'Name': 'running'}}]}
>>> demo = client.describe_instances(Filters=[{'Name': 'tag:Name', 'Values': ['demo-instance']}])
>>> demo['Reservations'][0]['Instances'][0]['State']
{'Code': 80, 'Name': 'stopped'}
>>> pprint.pprint(client.start_instances(InstanceIds=[instance_id]))
{'ResponseMetadata': {'HTTPHeaders': {'content-length': '579',
'content-type': 'text/xml;charset=UTF-8',
'date': 'Fri, 09 Aug 2019 14:10:20 GMT',
'server': 'AmazonEC2'},
'HTTPStatusCode': 200,
'RequestId': '21c65902-6665-4137-9023-43ac89f731d9',
'RetryAttempts': 0},
'StartingInstances': [{'CurrentState': {'Code': 0, 'Name': 'pending'},
'InstanceId': 'i-0c462c48bc396bdbb',
'PreviousState': {'Code': 80, 'Name': 'stopped'}}]}
>>> demo = client.describe_instances(Filters=[{'Name': 'tag:Name', 'Values': ['demo-instance']}])
>>> demo['Reservations'][0]['Instances'][0]['State']
{'Code': 16, 'Name': 'running'}

Alternative Approach to Fetching, Starting, and Stopping

>>> ec2 = session.resource('ec2')
>>> instance = ec2.Instance(instance_id)
>>> instance.state
{'Code': 16, 'Name': 'running'}
>>> pprint.pprint(instance.stop())
{'ResponseMetadata': {'HTTPHeaders': {'content-length': '579',
'content-type': 'text/xml;charset=UTF-8',
'date': 'Fri, 09 Aug 2019 14:40:20 GMT',
'server': 'AmazonEC2'},
'HTTPStatusCode': 200,
'RequestId': 'a2f76028-cbd2-4727-be3e-ae832b12e1ff',
'RetryAttempts': 0},
'StoppingInstances': [{'CurrentState': {'Code': 64, 'Name': 'stopping'},
'InstanceId': 'i-0c462c48bc396bdbb',
'PreviousState': {'Code': 16, 'Name': 'running'}}]}
>>> instance.state
{'Code': 80, 'Name': 'stopped'}
>>> pprint.pprint(instance.start())
{'ResponseMetadata': {'HTTPHeaders': {'content-length': '579',
'content-type': 'text/xml;charset=UTF-8',
'date': 'Fri, 09 Aug 2019 14:50:20 GMT',
'server': 'AmazonEC2'},
'HTTPStatusCode': 200,
'RequestId': '3cfc6061-5d64-4e52-9961-5eb2fefab2d8',
'RetryAttempts': 0},
'StartingInstances': [{'CurrentState': {'Code': 0, 'Name': 'pending'},
'InstanceId': 'i-0c462c48bc396bdbb',
'PreviousState': {'Code': 80, 'Name': 'stopped'}}]}
>>> instance.state
{'Code': 16, 'Name': 'running'}

Creating a Backup Image of an EC2.Instance

>>> import datetime
>>> date = datetime.datetime.utcnow().strftime('%Y%m%d')
>>> date
'20190809'
>>> name = f"InstanceID_{instance_id}_Image_Backup_{date}"
>>> name
'InstanceID_i-0c462c48bc396bdbb_Image_Backup_20181221'
>>> name = f"InstanceID_{instance_id}_Backup_Image_{date}"
>>> name
'InstanceID_i-0c462c48bc396bdbb_Backup_Image_20181221'
>>> pprint.pprint(client.create_image(InstanceId=instance_id, Name=name))
{'ImageId': 'ami-00d7c04e2b3b28e2d',
'ResponseMetadata': {'HTTPHeaders': {'content-length': '242',
'content-type': 'text/xml;charset=UTF-8',
'date': 'Fri, 09 Aug 2019 15:00:20 GMT',
'server': 'AmazonEC2'},
'HTTPStatusCode': 200,
'RequestId': '7ccccb1e-91ff-4753-8fc4-b27cf43bb8cf',
'RetryAttempts': 0}}
>>> image = instance.create_image(Name=name + '_2')

Tagging Images and EC2 Instances

>>> instance.tags
[{'Key': 'BackUp', 'Value': ''}, {'Key': 'Name', 'Value': 'demo-instance'}]
>>> image.create_tags(Tags=[{'Key': 'RemoveOn', 'Value': remove_on}])
[ec2.Tag(resource_id='ami-081c72fa60c8e2d58', key='RemoveOn', value='20190809')]
>>> pprint.pprint(client.create_tags(Resources=['ami-00d7c04e2b3b28e2d'], Tags=[{'Key': 'RemoveOn', 'Value': remove_on}]))
{'ResponseMetadata': {'HTTPHeaders': {'content-length': '221',
'content-type': 'text/xml;charset=UTF-8',
'date': 'Fri, 09 Aug 2019 15:10:20 GMT',
'server': 'AmazonEC2'},
'HTTPStatusCode': 200,
'RequestId': '645b733a-138c-42a1-9966-5c2eb0ca3ba3',
'RetryAttempts': 0}}

Creating an EC2 Instance from a Backup Image

>>> pprint.pprint(client.run_instances(ImageId='ami-081c72fa60c8e2d58', MinCount=1, MaxCount=1, InstanceType='t2.micro'))
...

Removing Backup Images

>>> remove_on = '20190809'
>>> images = client.describe_images(Filters=[{'Name': 'tag:RemoveOn', 'Values': [remove_on]}])
>>> remove_on = '201812022'
>>> for img in images['Images']:
... client.deregister_image(ImageId=img['ImageId'])

Terminating an EC2 Instance

>>> demo = client.describe_instances(Filters=[{'Name': 'tag:Name', 'Values': ['demo-instance']}])
>>> pprint.pprint(client.terminate_instances(InstanceIds=[instance_id]))
{'ResponseMetadata': {'HTTPHeaders': {'content-type': 'text/xml;charset=UTF-8',
'date': 'Fri, 09 Aug 2019 15:55:20 GMT',
'server': 'AmazonEC2',
'transfer-encoding': 'chunked',
'vary': 'Accept-Encoding'},
'HTTPStatusCode': 200,
'RequestId': '78881a08-0240-47df-b502-61a706bfb3ab',
'RetryAttempts': 0},
'TerminatingInstances': [{'CurrentState': {'Code': 32,
'Name': 'shutting-down'},
'InstanceId': 'i-0c462c48bc396bdbb',
'PreviousState': {'Code': 16, 'Name': 'running'}}]}

Pulling Things Together for an Automation Script

# ec2backup.pyfrom datetime import datetime, timedelta
import awsutils
def backup(region_id='us-east-1'):
'''This method searches for all EC2 instances with a tag of BackUp
and creates a backup images of them then tags the images with a
RemoveOn tag of a YYYYMMDD value of three UTC days from now
'''
created_on = datetime.utcnow().strftime('%Y%m%d')
remove_on = (datetime.utcnow() + timedelta(days=3)).strftime('%Y%m%d')
session = awsutils.get_session(region_id)
client = session.client('ec2')
resource = session.resource('ec2')
reservations = client.describe_instances(Filters=[{'Name': 'tag-key', 'Values': ['BackUp']}])
for reservation in reservations['Reservations']:
for instance_description in reservation['Instances']:
instance_id = instance_description['InstanceId']
name = f"InstanceId({instance_id})_CreatedOn({created_on})_RemoveOn({remove_on})"
print(f"Creating Backup: {name}")
image_description = client.create_image(InstanceId=instance_id, Name=name)
images.append(image_description['ImageId'])
image = resource.Image(image_description['ImageId'])
image.create_tags(Tags=[{'Key': 'RemoveOn', 'Value': remove_on}, {'Key': 'Name', 'Value': name}])
if __name__ == '__main__':
backup()
# amicleanup.pyfrom datetime import datetime
import awsutils
def cleanup(region_id='us-east-1'):
'''This method searches for all AMI images with a tag of RemoveOn
and a value of YYYYMMDD of the day its ran on then removes it
'''
today = datetime.utcnow().strftime('%Y%m%d')
session = awsutils.get_session(region_id)
client = session.client('ec2')
resource = session.resource('ec2')
images = client.describe_images(Filters=[{'Name': 'tag:RemoveOn', 'Values': [today]}])
for image_data in images['Images']:
image = resource.Image(image_data['ImageId'])
name_tag = [tag['Value'] for tag in image.tags if tag['Key'] == 'Name']
if name_tag:
print(f"Deregistering {name_tag[0]}")
image.deregister()
if __name__ == '__main__':
cleanup()

Cron Implementation

0 23 * * * /path/to/venv/bin/python /path/to/ec2backup.py
30 23 * * * /path/to/venv/bin/python /path/to/amicleanup.py

AWS Lambda Implementation

import boto3
import os
from datetime import datetime, timedelta
def get_session(region, access_id, secret_key):
return boto3.session.Session(region_name=region,
aws_access_key_id=access_id,
aws_secret_access_key=secret_key)
def lambda_handler(event, context):
'''This method searches for all EC2 instances with a tag of BackUp
and creates a backup images of them then tags the images with a
RemoveOn tag of a YYYYMMDD value of three UTC days from now
'''
created_on = datetime.utcnow().strftime('%Y%m%d')
remove_on = (datetime.utcnow() + timedelta(days=3)).strftime('%Y%m%d')
session = get_session(os.getenv('REGION'),
os.getenv('ACCESS_KEY_ID'),
os.getenv('SECRET_KEY'))
client = session.client('ec2')
resource = session.resource('ec2')
reservations = client.describe_instances(Filters=[{'Name': 'tag-key', 'Values': ['BackUp']}])
for reservation in reservations['Reservations']:
for instance_description in reservation['Instances']:
instance_id = instance_description['InstanceId']
name = f"InstanceId({instance_id})_CreatedOn({created_on})_RemoveOn({remove_on})"
print(f"Creating Backup: {name}")
image_description = client.create_image(InstanceId=instance_id, Name=name)
image = resource.Image(image_description['ImageId'])
image.create_tags(Tags=[{'Key': 'RemoveOn', 'Value': remove_on}, {'Key': 'Name', 'Value': name}])
  • REGION with a value of the region of the EC2 instances to backup which is us-east-1 in this example
  • ACCESS_KEY_ID with the value of the access key from the section where the boto3-user was setup
  • SECRET_KEY with the value of the secret key from the section where the boto3-user was setup
import boto3
from datetime import datetime
import os
def get_session(region, access_id, secret_key):
return boto3.session.Session(region_name=region,
aws_access_key_id=access_id,
aws_secret_access_key=secret_key)
def lambda_handler(event, context):
'''This method searches for all AMI images with a tag of RemoveOn
and a value of YYYYMMDD of the day its ran on then removes it
'''
today = datetime.utcnow().strftime('%Y%m%d')
session = get_session(os.getenv('REGION'),
os.getenv('ACCESS_KEY_ID'),
os.getenv('SECRET_KEY'))
client = session.client('ec2')
resource = session.resource('ec2')
images = client.describe_images(Filters=[{'Name': 'tag:RemoveOn', 'Values': [today]}])
for image_data in images['Images']:
image = resource.Image(image_data['ImageId'])
name_tag = [tag['Value'] for tag in image.tags if tag['Key'] == 'Name']
if name_tag:
print(f"Deregistering {name_tag[0]}")
image.deregister()

Conclusion

--

--

--

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

Alpha Centauri 3.0 — Mainnet- Release notes

5 Mistakes to Avoid When First Learning Jobs to be Done

Bash Bunny — Wallpaper Changer

Implementing Simon’s Algorithm in Qiskit

Memory Champion: My 3 Week Journey to Attempt Memorizing The First 100 Digits of Pi

Interesting Story about myself and introduction to navigation in the UNIX SHELL.

Adding a README to the GitHub profile |2021| 💪Step by Step Tutorial

Traditional to headless CMS — why and how

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
ABHISHEK KUMAR

ABHISHEK KUMAR

More from Medium

Perimeter security with Fastly edge and AWS — Part I

Deploying web application on AWS using fargate and cloudformation.

Performance Analysis for Arm vs x86 CPUs in AWS EC 2

Docker Commands for Beginners