Create an AMI from your own VM image
Creating your own AMI from scratch
Continuing the trend of AWS-related articles, this time I am looking at how to generate an Amazon Machine Image (AMI) from an ISO source.
Why?
There are a large number of publicly available AMIs. For example the ones owned by Amazon can be searched per region using
aws --region eu-central-1 ec2 describe-images --owner amazon
Those AMIs can be used as a basis for customized AMIs, generated for example using the excellent packer amazon-ebs builder.
Some times though there is a need to create an AMI from an existing virtualization source e.g. a .vmdk
file from VirtualBox.
It would be great if we could create our customized OS image either through an interactive installation or in an automated way via Packer and import it as an AMI in EC2.
It turns out that this is possible using AWS ImportImage.
Prerequisites and limitations
- Virtualization engine that can produce .ova or .vmdk artifacts. In this article I am focusing on virtualbox .vmdk artifacts and vagrant boxes (.box format)
- The produced AMIs are suitable for HVM virtualization. pv requires more steps such as installing a pv enabled kernel.
- Define an s3 bucket (in a region close to you, to speed up uploads.) This will be used to upload the images for conversion to AMI.
- Define roles and policies in AWS. In particular:
- A
vmimport
service role and a policy attached to it, precisely as explained in this AWS doc.. - If you are an IAM AWS user (as opposed to root user) you also need to attach the following inline policy. Replace
<youraccountid>
with your own.{ "Version": "2012-10-17", "Statement": [ { "Sid": "380", "Effect": "Allow", "Action": [ "iam:CreateRole", "iam:PutRolePolicy" ], "Resource": [ "arn:aws:iam::<youraccountid>:role/vmimport" ] } ] }
- Fast upstream bandwidth as you will be uploading the image to s3!
Manual steps
With the prerequisites satisfied the process is:
- Create your VM. Your vbox needs to have cloud-init installed and configured. RedHat based distributions include
cloud-utils
in the EPEL repo; the following script can be used to configure the expected login user asec2-user
:#!/bin/bash -eux yum install -y cloud-init cloud-utils-growpart dracut-modules-growroot cat >/etc/cloud/cloud.cfg <<-'EOF' users: - default disable_root: 1 ssh_pwauth: 0 locale_configfile: /etc/sysconfig/i18n mount_default_fields: [~, ~, 'auto', 'defaults,nofail', '0', '2'] resize_rootfs_tmp: /dev ssh_deletekeys: 0 ssh_genkeytypes: ~ syslog_fix_perms: ~ cloud_init_modules: - bootcmd - write-files - resizefs - set_hostname - update_hostname - update_etc_hosts - rsyslog - users-groups - ssh cloud_config_modules: - mounts - locale - set-passwords - timezone - runcmd cloud_final_modules: - scripts-per-once - scripts-per-boot - scripts-per-instance - scripts-user - ssh-authkey-fingerprints - keys-to-console - final-message system_info: distro: rhel default_user: name: ec2-user paths: cloud_dir: /var/lib/cloud templates_dir: /etc/cloud/templates ssh_svcname: sshd EOF
- When the job is complete describe-import-image-tasks will report
Status: completed
and theImageId
:{ "Status": "completed", "LicenseType": "BYOL", "Description": "AMI FROM VMDK", "ImageId": "ami-XXXXXX", "Platform": "Linux", "Architecture": "x86_64", "SnapshotDetails": [ { "DeviceName": "/dev/sda1", "Description": "AMIFROMVMDK", "Format": "VMDK", "DiskImageSize": 902047744.0, "SnapshotId": "snap-c768a943", "UserBucket": { "S3Bucket": "mybucket", "S3Key": "myvm.vmdk" } } ], "ImportTaskId": "import-ami-fh9g0yjn" }
- At this point we can use the new AMI. Note that the produced AMI will have some auto generated name and description; the ones we supplied in the aws cli command are used only by the import-image task. I could not find a way to modify the name/description, so we can just copy the image using:
aws --region <destionation_region> --source-region <the_region_you_used_before> --source-image-id <produced_image_id> --name "My new OS" --description "Details of the AMI"
Remember to deregister and clean up the associated snapshot id for the temporary AMI created by import-image.
Automating the process
The above steps can be tedious and since I needed to import vagrant boxes, I created a tool to automate this:
The required parameters are:
--vboxfile
Path to the vagrant .box file you wish to convert to AMI. Currently only virtualbox providers are supported.--s3bucket
[--s3key]
The s3bucket and the (temporary) key used for uploading the VM. s3key
is optional but if you omit it, vboxfile
expect a certain naming convention like osdistroVER-othermetadata.box
For example ./oel7.1-x86_64-virtualbox.box is a valid name.
--verbose
Displays progress statistics. Very useful if the script is not run from another program.
By default it will create copies of the temporary AMI that AWS import-image creates in three regions -- us-east-1, us-west-2, eu-central-1. It's easy to add or remove destination regions in this list
Example
For an existing oracle linux vagrant box:
$ ./amiimporter.py --s3bucket mybucket --vboxfile ./oel7.1-x86_64-virtualbox.box --verbose INFO:root:Uploading ./tmpdir/packer-virtualbox-iso-1453910880-disk1.vmdk to s3 99% INFO:root:Running: aws --region eu-west-1 ec2 import-image --cli-input-json {"Description": "temp-hvm-oel-7-20160129134521", "DiskContainers": [{"UserBucket": {"S3Bucket": "mybucket", "S3Key": "temp-hvm-oel-7-20160129134521"}, "Description": "temp-hvm-oel-7-20160129134521"}]} INFO:root:AWS is now importing vdmk to AMI. 98% INFO:root:Done, amiid is ami-7c7bcc0f INFO:root:Successfully created temporary AMI ami-7c7bcc0f Created ami-8d322ae1 in region eu-central-1 Created ami-8d16f1ed in region us-west-2 Created ami-89e4c9e3 in region us-east-1 INFO:root:Deregistering temporary AMI ami-7c7bcc0f INFO:root:Deleting updateded s3 file s3://mybucket/temp-hvm-oel-7-20160129134521
Warnings
If you use a Vagrant box as a source for your AMIs make sure that the vagrant user does not have the default password and/or insecure key as otherwise your deployed instances will be easily hacked.
Also, depending on the distribution, cloud-utils
may be unable to resize your root partition automatically. This article/script may come handy.
Conclusion
Despite the fact that AWS public AMI store is very rich in images, there are always corner cases where you need to create something from scratch. I hope the process illustrated above is not too daunting and perhaps the included script will make it even easier!