My Journey to the cloud…

In pursuit of excellence….


Boto3 Script to create and attach an EBS Volume to an EC2


import boto3
import logging
import datetime
import argparse
import time
from datetime import datetime
from botocore.exceptions import ClientError

logger = logging.getLogger()
logger.setLevel(logging.INFO)

VolumeList=[]

expirationDate = expiration_Date = ""

DEFAULT_AWS_Account_ID = "1111222222"
DEFAULT_REGION = "us-east-1"

def parse_commandline_arguments():

    global REGION
    global AWS_Account_ID
    global instance_id
    global server_name
    global size_of_volume
    global kms_key

    parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter,
                                     description='Boto 2 Scritp to create and attach volume to a given Ec2 Instance.')
    parser.add_argument("-accountID", "--ownerID", dest="aws_ID", type=str, default=DEFAULT_AWS_Account_ID,
                        help="The AWS Account ID where volume tagging is  to be done")
    parser.add_argument("-r", "--region", dest="region", type=str,
                        default=DEFAULT_REGION, help="Specify the region of the AWS Account")
    parser.add_argument("-server_name", "--ServerName", dest="servername", type=str,
                        help="Specify the Instance Name to be terminated")
    parser.add_argument("-volume_size","--Volume_Size",dest="volumesize", type=int,
                        help="Specify the size of new volume to be created and attached")
    parser.add_argument("-kmsId","--KMS_ID",dest="kms_key_id",type=str,
                        help="Specify the KMS Key ID  to encrypt the volume")

    args = parser.parse_args()
    REGION = args.region
    AWS_Account_ID = args.aws_ID
    server_name = args.servername
    size_of_volume = args.volumesize
    kms_key = args.kms_key_id


def ec2_client(region):
    """
    Connects to EC2, returns a connection object
    """
    try:
        conn = boto3.client('ec2', region_name=region)

    except Exception as e:
        sys.stderr.write(
            'Could not connect to region: %s. Exception: %s\n' % (region, e))
        conn = None

    return conn



def wait_for_state (instance, target_state):
    # Waits for instance to move to desired state
    # Vol Creation State: 'creating'|'available'|'in-use'|'deleting'|'deleted'|'error'
    # Vol Attachment State: 'attaching'|'attached'|'detaching'|'detached'
    status = ec2.Instance(instance).state['Name']
    while status != target_state:
        print("Waiting for Instance - {} to come in {} state" .format(instance,target_state))
        time.sleep (5)
        status = ec2.Instance(instance).state['Name']



def create_and_attach_volume(client,serverName,volSize,kmsId):
    global VolumeList
    device = "/dev/sdh"
    print(serverName)
    # Get Instance ID from the given Instance Name
    filters = [ {'Name': 'tag:Name',
                'Values': [serverName]}
                ]
    for attempt in range(5):
        try:
            response = client.describe_instances(Filters=filters)["Reservations"]
            #response = client.describe_instances()
            instanceid = response[0]['Instances'][0]['InstanceId']
            avaialbilityZone = response[0]['Instances'][0]['Placement']['AvailabilityZone']
            print(instanceid + ":" + avaialbilityZone)
        except BaseException as err:
            logger.error(err)
            logger.info("*** ERROR *** during EC2 Describe proceess - retry...")
            time.sleep(0.5)
        else:
            logger.info("--> Done")
            break
    else:
        logger.error("*** ERROR *** - All attempt to describe instance failed - exit with error")
        raise Exception("*** ERROR *** - Can't describe instance")

    # Create volume
    for attempt in range(5):
        try:
            response = client.create_volume(
                            AvailabilityZone=avaialbilityZone,
                            Encrypted=True,
                            KmsKeyId=kmsId,
                            Size=volSize,
                            VolumeType='gp3' ## Default Volume Type
                        )
            #print(response)
        except BaseException as err:
            logger.error(err)
            logger.info("*** ERROR *** during EC2 Volume creation proceess - retry...")
            time.sleep(0.5)
        else:
            logger.info("--> Done")
            break
    else:
        logger.error("*** ERROR *** - All attempt to create EC2 Volume failed - exit with error")
        raise Exception("*** ERROR *** - Can't create EBS Volume")


    if response['ResponseMetadata']['HTTPStatusCode']== 200:
        volume_id= response['VolumeId']
        print('***volume:', volume_id)
        client.get_waiter('volume_available').wait(
                VolumeIds=[volume_id]
                )
        print('***Success!! volume:', volume_id, 'created...')

    VolumeList.append(volume_id)
    print(VolumeList)

    # Add tag on newly created Volumes
    logger.info("Tagging for deletion following Volumes:")
    for volume in VolumeToDelList:
        logger.info("- " + volume)
    for attempt in range(5):
        try:
            print("creating Tag for Volume ID {}" .format(VolumeToDelList))
            client.create_tags(
                    Resources=VolumeToDelList,
                    Tags=[
                        {
                            'Key': 'InsntanceId',
                            'Value': instanceid
                        }
                    ]
            )
        except BaseException as err:
            logger.error(err)
            logger.error("*** ERROR *** during tagging Volumes - retry...")
            time.sleep(0.6)
        else:
            logger.info("--> Done")
            break
    else:
        logger.error("*** ERROR *** - All attempt to tagging volumes - exit with error")
        raise Exception("*** ERROR *** - Can't tagging Volumes")

    # Attach Volume to EC2 Instance
    logger.info("--> Attaching volume to EC2")
    for attempt in range(5):
        try:
            if volume_id:
                print('***attaching volume:', volume_id, 'to:', instanceid)
                response = client.attach_volume(
                            Device=device,
                            InstanceId=instanceid,
                            VolumeId=volume_id,
                            DryRun=False
                            )
                if response['ResponseMetadata']['HTTPStatusCode']== 200:
                    client.get_waiter('volume_in_use').wait(
                            VolumeIds=[volume_id],
                            DryRun=False
                            )
                    print('***Success!! volume:', volume_id, 'is attached to instance:', instanceid)

        except BaseException as err:
            logger.error(err)
            logger.error("*** ERROR *** during EC2 Volume attachment process - retry...")
            time.sleep(0.6) # second
        else:
            logger.info("--> Done")
            break
    else:
        logger.error("*** ERROR *** - All attempt to attach volume to instance failed - exit with error")
        raise Exception("*** ERROR *** - Can't attach volume to EC2")





if __name__ == '__main__':
    try:
        parse_commandline_arguments()
        client=ec2_client(REGION)
        create_and_attach_volume(client,server_name,size_of_volume,kms_key)
    except Exception as error:
        logging.error(error)
        print(str(error))

Happy Reading !!!!

-Anand M



Leave a comment

About Me

I’m a Hands-On Technical & Entrprise Solutions Architect based out of Houston, TX. I have been working on Oracle ERP, Oracle Database and Cloud technologies for over 20 years and still going strong for learning new things.

You can connect me on Linkedin and also reach out to me

I am certified for 8x AWS, OCP (Oracle Certified Professionals), PMP, ITTL and 6 Sigma.

Disclaimer

This is a personal blog. Any views or opinions represented in this blog are personal and belong solely to the blog owner and do not represent those of people, institutions or organizations that the owner may or may not be associated with in professional or personal capacity, unless explicitly stated.
All content provided on this blog is for informational purposes only. The owner of this blog makes no representations as to the accuracy or completeness of any information on this site or found by following any link on this site.

The owner will not be liable for any errors or omissions in this information nor for the availability of this information. The owner will not be liable for any losses, injuries, or damages from the display or use of this information. Any script available on the blog post MUST be tested before they are run against Production environment.

Newsletter