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:
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:
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.
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': ''
},
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,
)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With