Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Boto3 create_image for AMI creation - Save ONLY the root volume

I'm writing a script to create AMI images of instances.

My goal is to ONLY save the root volume.

I'm using boto3.

My test instance has 2x EBS volumes attached. I only want to save the root volume with the AMI.

Here is how my code works:

  1. Gather list of instances
  2. Analyze each instance to identify only the root device, gather its mapping data
  3. Execute boto3's instance.create_image to trigger, and passing ONLY the BlockDeviceMappings for the root device.

Expected result: AMI has only the Block Devices I had defined in the create_image call.

Actual result: AMI has all block devices that were attached to the source instance, regardless of the explicit BlockDeviceMappings defined during create_image

In my below code, I've written a function that takes already gathered source instance data elsewhere. I am ONLY defining the root volume info, the BlockDeviceMappings does not declare any of the other devices attached to the source volume.

def create_image(inst, instance, inst_dict):
    nowtime = datetime.datetime.now().strftime('%Y-%m-%d')
    try:
        image = instance.create_image(
            BlockDeviceMappings=[
                {
                    'DeviceName': inst_dict[inst]['root_dev_name'],
                    'Ebs': {
                        'Encrypted': inst_dict[inst]['vol_encr'],
                        'DeleteOnTermination': inst_dict[inst]['vol_del_rule'],
                        'VolumeSize': inst_dict[inst]['vol_size'],
                        'VolumeType': inst_dict[inst]['root_dev_type']
                    },
                },
            ],
            Description=inst_dict[inst]['inst_name'] + " " + str(nowtime),
            DryRun=False,
            Name=inst_dict[inst]['inst_name'] + " " + str(nowtime),
            NoReboot=True
        )
    except Exception, e:
        logging.error("Failed to create image! Instance: " + inst_dict[inst]['inst_name'])
        return 1

I do see that boto3 has a 'NoDevice': 'string' parameter that can be added to a BlockDeviceMapping object. But it's attached to the mapping itself - that confuses me, why would you declare a block device mapping, only to "exclude" it. I'm not sure how or why to use this NoDevice value.

In addition: A specific question:

  • If boto3's create_image will create all Block Devices regardless of what's put into the BlockDeviceMappings, then why is the BlockDeviceMappings even there at all if it just copies the source instance block devices?

Thanks for the help.

EDIT/Update:

I've attempted to declare the device mapping for the volume I do not want. Then use the NoDevice parameter:

    BlockDeviceMappings=[
        {
            'DeviceName': inst_dict[inst]['root_dev_name'],
            'Ebs': {
                'Encrypted': inst_dict[inst]['vol_encr'],
                'DeleteOnTermination': inst_dict[inst]['vol_del_rule'],
                'VolumeSize': inst_dict[inst]['vol_size'],
                'VolumeType': inst_dict[inst]['root_dev_type']
            },
        },
        {
            'DeviceName': '/dev/sdf',
            'Ebs': {
                'Encrypted': True,
                'DeleteOnTermination': False,
                'VolumeSize': 24,
                'VolumeType': 'gp2'
            },
            'NoDevice': '',
        },
    ],

The only thing I can see is setting an empty string for the NoDevice value. Setting it to a bool or anything else gives an error. I've tried 'NoDevice': 'true' and 'NoDevice': 'false' and 'NoDevice': True and 'NoDevice': False and 'NoDevice': '/dev/sdf' and they all give error. The only thing accepted is 'NoDevice': '' yet the outcome is the same, both devices are attached to the AMI.

like image 403
emmdee Avatar asked Oct 27 '17 17:10

emmdee


2 Answers

Ok, figured it out.

The trick is to not define anything other than the DeviceName in the mapping if you want to omit it using NoDevice

This is working now, adding ANY more information to the mapping will make the NoDevice void and ignored.

Hopefully helps someone else in the future:

{
    'DeviceName': '/dev/sdf',
    'NoDevice': ''
},
like image 160
emmdee Avatar answered Oct 03 '22 13:10

emmdee


I was able to make it work properly with this syntax:

createImage = client.create_image(
    BlockDeviceMappings=[
        {
            'DeviceName': '/dev/sda1',
            'Ebs': {
                'DeleteOnTermination': True,
                'VolumeSize': 20,
                'VolumeType': 'gp2',
                'Encrypted': False
            },
        'DeviceName': '/dev/xvdf',
            'Ebs':{},
        'NoDevice': '', 
        },
    ],
    Description='AMI created by me',
    InstanceId='i-xxxxxxxxxxxxxxxx',
    Name='Insert the AMI name here',
    NoReboot=False,
    DryRun=False,
)
like image 45
Matheus Santos Avatar answered Oct 03 '22 11:10

Matheus Santos