Creating Public AMIs Securely for EC2

Amazon published a tutorial about best practices in creating public AMIs for use on EC2 last week:

How To Share and Use Public AMIs in A Secure Manner

Though the general principles put forth in the tutorial are good, some of the specifics are flawed in how to accomplish those principles. (Comments here relate to the article update from June 7, 2011 3:45 AM GMT.)

The primary message of the article is that you should not publish private information on a public AMI. Excellent advice!

Unfortunately, the article seems to recommend or at least to assume that you are building the public AMI by taking a snapshot of a running instance. Though this method seems an easy way to build an AMI and is fine for private AMIs, it is is a dangerous approach for public AMIs because of how difficult it is to identify private information and to clear that private information from a running system in such a way that it does not leak into the public AMI.

Building EBS Boot AMIs Using Canonical's Downloadable EC2 Images

In the last article, I described how to use the vmbuilder software to build an EBS boot AMI from scratch for running Ubuntu on EC2 with a persistent root disk.

In the ec2ubuntu Google group, Scott Moser pointed out that users can take advantage of the Ubuntu images for EC2 that Canonical has already built with vmbuilder. This can simplify and speed up the process of building EBS boot AMIs for the rest of us.

Let’s walk through the steps, creating an EBS boot AMI for Ubuntu 9.10 Karmic.

  1. Run an instance of the Ubuntu 9.10 Karmic AMI, either 32-bit or 64-bit depending on which architecture AMI you wish to build. Make a note of the resulting instance id:

     # 32-bit
     instanceid=$(ec2-run-instances   \
       --key YOURKEYPAIR              \
       --availability-zone us-east-1a \
       ami-1515f67c |
       egrep ^INSTANCE | cut -f2)
     echo "instanceid=$instanceid"
    
     # 64-bit
     instanceid=$(ec2-run-instances   \
       --key YOURKEYPAIR              \
       --availability-zone us-east-1a \
       --instance-type m1.large       \
       ami-ab15f6c2 |
       egrep ^INSTANCE | cut -f2)
     echo "instanceid=$instanceid"
    

    Wait for the instance to move to the “running” state, then note the public hostname:

     while host=$(ec2-describe-instances "$instanceid" | 
       egrep ^INSTANCE | cut -f4) && test -z $host; do echo -n .; sleep 1; done
     echo host=$host
    

    Copy your X.509 certificate and private key to the instance. Use the correct locations for your credential files:

     rsync                            \
       --rsh="ssh -i YOURKEYPAIR.pem" \
       --rsync-path="sudo rsync"      \
       ~/.ec2/{cert,pk}-*.pem         \
       ubuntu@$host:/mnt/
    

    Connect to the instance:

     ssh -i YOURKEYPAIR.pem ubuntu@$host
    
  2. Install EC2 API tools from the Ubuntu on EC2 ec2-tools PPA because they are more up to date than the ones in Karmic, letting us register EBS boot AMIs:

     export DEBIAN_FRONTEND=noninteractive
     echo "deb http://ppa.launchpad.net/ubuntu-on-ec2/ec2-tools/ubuntu karmic main" |
       sudo tee /etc/apt/sources.list.d/ubuntu-on-ec2-ec2-tools.list &&
     sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 9EE6D873 &&
     sudo apt-get update &&
     sudo -E apt-get dist-upgrade -y &&
     sudo -E apt-get install -y ec2-api-tools
    
  3. Set up some parameters:

     codename=karmic
     release=9.10
     tag=server
     if [ $(uname -m) = 'x86_64' ]; then
       arch=x86_64
       arch2=amd64
       ebsopts="--kernel=aki-fd15f694 --ramdisk=ari-c515f6ac"
       ebsopts="$ebsopts --block-device-mapping /dev/sdb=ephemeral0"
     else
       arch=i386
       arch2=i386
       ebsopts="--kernel=aki-5f15f636 --ramdisk=ari-0915f660"
       ebsopts="$ebsopts --block-device-mapping /dev/sda2=ephemeral0"
     fi
    
  4. Download and unpack the latest released Ubuntu server image file. This contains the output of vmbuilder as run by Canonical.

     imagesource=http://uec-images.ubuntu.com/releases/$codename/release/unpacked/ubuntu-$release-$tag-uec-$arch2.img.tar.gz
     image=/mnt/$codename-$tag-uec-$arch2.img
     imagedir=/mnt/$codename-uec-$arch2
     wget -O- $imagesource |
       sudo tar xzf - -C /mnt
     sudo mkdir -p $imagedir
     sudo mount -o loop $image $imagedir
    
  5. [OPTIONAL] At this point /mnt/$image contains a mounted filesystem with the complete Ubuntu image as released by Canonical. You can skip this step if you just want an EBS boot AMI which is an exact copy of the released S3 based Ubuntu AMI from Canonical, or you can make any updates, installations, and customizations you’d like to have in your resulting AMI.

    In this example, we’ll perform similar steps as the previous tutorial and update the software packages to the latest releases from Ubuntu. Remember that the released EC2 image could be months old.

     # Allow network access from chroot environment
     sudo cp /etc/resolv.conf $imagedir/etc/
     # Fix what I consider to be a bug in vmbuilder
     sudo rm -f $imagedir/etc/hostname
     # Add multiverse
     sudo perl -pi -e 's%(universe)$%$1 multiverse%' \
       $imagedir/etc/ec2-init/templates/sources.list.tmpl
     # Add Alestic PPA for runurl package (handy in user-data scripts)
     echo "deb http://ppa.launchpad.net/alestic/ppa/ubuntu karmic main" |
       sudo tee $imagedir/etc/apt/sources.list.d/alestic-ppa.list
     sudo chroot $imagedir \
       apt-key adv --keyserver keyserver.ubuntu.com --recv-keys BE09C571
     # Add ubuntu-on-ec2/ec2-tools PPA for updated ec2-ami-tools
     echo "deb http://ppa.launchpad.net/ubuntu-on-ec2/ec2-tools/ubuntu karmic main" |
       sudo tee $imagedir/etc/apt/sources.list.d/ubuntu-on-ec2-ec2-tools.list
     sudo chroot $imagedir \
       apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 9EE6D873
     # Upgrade the system and install packages
     sudo chroot $imagedir mount -t proc none /proc
     sudo chroot $imagedir mount -t devpts none /dev/pts
     cat <<EOF > $imagedir/usr/sbin/policy-rc.d
     #!/bin/sh
     exit 101
     EOF
     chmod 755 $imagedir/usr/sbin/policy-rc.d
     DEBIAN_FRONTEND=noninteractive
     sudo chroot $imagedir apt-get update &&
     sudo -E chroot $imagedir apt-get dist-upgrade -y &&
     sudo -E chroot $imagedir apt-get install -y runurl ec2-ami-tools
     sudo chroot $imagedir umount /proc
     sudo chroot $imagedir umount /dev/pts
     rm -f $imagedir/usr/sbin/policy-rc.d
    

    Again, the above step is completely optional and can be skipped to create the EBS boot AMI that Canonical would have published.

  6. Copy the image files to a new EBS volume, snapshot it, and register the snapshot as an EBS boot AMI. Make a note of the resulting AMI id:

     size=15 # root disk in GB
     now=$(date +%Y%m%d-%H%M)
     prefix=ubuntu-$release-$codename-$tag-$arch-$now
     description="Ubuntu $release $codename $tag $arch $now"
     export EC2_CERT=$(echo /mnt/cert-*.pem)
     export EC2_PRIVATE_KEY=$(echo /mnt/pk-*.pem)
     volumeid=$(ec2-create-volume --size $size --availability-zone us-east-1a |
       cut -f2)
     instanceid=$(wget -qO- http://instance-data/latest/meta-data/instance-id)
     ec2-attach-volume --device /dev/sdi --instance "$instanceid" "$volumeid"
     while [ ! -e /dev/sdi ]; do echo -n .; sleep 1; done
     sudo mkfs.ext3 -F /dev/sdi
     ebsimage=$imagedir-ebs
     sudo mkdir $ebsimage
     sudo mount /dev/sdi $ebsimage
     sudo tar -cSf - -C $imagedir . | sudo tar xvf - -C $ebsimage
     sudo umount $ebsimage
     ec2-detach-volume "$volumeid"
     snapshotid=$(ec2-create-snapshot "$volumeid" | cut -f2)
     ec2-delete-volume "$volumeid"
     while ec2-describe-snapshots "$snapshotid" | grep -q pending
       do echo -n .; sleep 1; done
     ec2-register                   \
       --architecture $arch         \
       --name "$prefix"             \
       --description "$description" \
       $ebsopts                     \
       --snapshot "$snapshotid"
    
  7. Depending on what you want to keep from the above process, there are various things that you might want to clean up.

    If you no longer want to use an EBS boot AMI:

     ec2-deregister $amiid
     ec2-delete-snapshot $snapshotid
    

    When you’re done with the original instance:

     ec2-terminate-instance $instanceid
    

In this example, I set /mnt to the first ephemeral store on the instance even on EBS boot AMIs. This more closely matches the default on the S3 based AMIs, but means that /mnt will not be persistent across a stop/start of an EBS boot instance. If Canonical starts publishing EBS boot AMIs, they may or may not choose to make the same choice.

Community feedback, bug reports, and enhancements for these instructions are welcomed.

[Update 2009-01-14: Wrapped upgrade/installs inside of /usr/sbin/policy-rc.d setting to avoid starting daemons in chroot environment.]

[Update 2010-01-22: New location for downloadable Ubuntu images.]

[Update 2010-03-26: Path tweak, thanks to paul.]

Building EBS Boot and S3 Based AMIs for EC2 with Ubuntu vmbuilder

Here’s my current recipe for how to build an Ubuntu 9.10 Karmic AMI, either the new EBS boot or the standard S3 based, using the Ubuntu vmbuilder software. The Ubuntu vmbuilder utility replaces ec2ubuntu-build-ami for building EC2 images and it can build images for a number of other virtual machine formats as well.

There is a lot of room for simplification and scripting in the following instructions, but I figured I’d publish what is working now so others can take advantage of the research to date. Happy New Year!

Some sections are marked [For EBS boot AMI] or [For S3 based AMI] and should only be followed when you are building that type of AMI. The rest of the sections apply to either type. It is possible to follow all instructions to build both types of AMIs at the same time.

  1. Run an instance of Ubuntu 9.10 Karmic AMI, either 32-bit or 64-bit depending on which architecture AMI you wish to build. I prefer the c1.* instance types to speed up the builds, but you can get by cheaper with the m1.* instance types. Make a note of the resulting instance id:

     # 32-bit
     instanceid=$(ec2-run-instances   \
       --key YOURKEYPAIR              \
       --availability-zone us-east-1a \
       --instance-type c1.medium      \
       ami-1515f67c |
       egrep ^INSTANCE | cut -f2)
     echo "instanceid=$instanceid"
    
     # 64-bit
     instanceid=$(ec2-run-instances   \
       --key YOURKEYPAIR              \
       --availability-zone us-east-1a \
       --instance-type c1.xlarge      \
       ami-ab15f6c2 |
       egrep ^INSTANCE | cut -f2)
     echo "instanceid=$instanceid"
    

    Wait for the instance to move to the “running” state, then note the public hostname:

     while host=$(ec2-describe-instances "$instanceid" | 
       egrep ^INSTANCE | cut -f4) && test -z $host; do echo -n .; sleep 1; done
     echo host=$host
    
  2. Copy your X.509 certificate and private key to the instance. Use the correct locations for your credential files:

     rsync                            \
       --rsh="ssh -i YOURKEYPAIR.pem" \
       --rsync-path="sudo rsync"      \
       ~/.ec2/{cert,pk}-*.pem         \
       ubuntu@$host:/mnt/
    
  3. Connect to the instance:

     ssh -i YOURKEYPAIR.pem ubuntu@$host
    
  4. Install the image building software. We install the python-vm-builder package from Karmic, but we’re going to be using the latest vmbuilder from the development branch in Launchpad because it has good bug fixes. We also use the EC2 API tools from the Ubuntu on EC2 ec2-tools PPA because they are more up to date than the ones in Karmic, letting us register EBS boot AMIs:

     export DEBIAN_FRONTEND=noninteractive
     echo "deb http://ppa.launchpad.net/ubuntu-on-ec2/ec2-tools/ubuntu karmic main" |
       sudo tee /etc/apt/sources.list.d/ubuntu-on-ec2-ec2-tools.list &&
     sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 9EE6D873 &&
     sudo apt-get update &&
     sudo -E apt-get upgrade -y &&
     sudo -E apt-get install -y \
       python-vm-builder ec2-ami-tools ec2-api-tools bzr &&
     bzr branch lp:vmbuilder
    

    You can ignore the “Launchpad ID” warning from bzr.

  5. Fill in your AWS credentials:

     export AWS_USER_ID=...
     export AWS_ACCESS_KEY_ID=...
     export AWS_SECRET_ACCESS_KEY=...
     export EC2_CERT=$(echo /mnt/cert-*.pem)
     export EC2_PRIVATE_KEY=$(echo /mnt/pk-*.pem)
    

    Set up parameters and create files to be used by the build process. The bucket value is only required for S3 based AMIs:

     bucket=...
     codename=karmic
     release=9.10
     tag=server
     if [ $(uname -m) = 'x86_64' ]; then
       arch=x86_64
       arch2=amd64
       pkgopts="--addpkg=libc6-i386"
       kernelopts="--ec2-kernel=aki-fd15f694 --ec2-ramdisk=ari-c515f6ac"
       ebsopts="--kernel=aki-fd15f694 --ramdisk=ari-c515f6ac"
       ebsopts="$ebsopts --block-device-mapping /dev/sdb=ephemeral0"
     else
       arch=i386
       arch2=i386
       pkgopts=
       kernelopts="--ec2-kernel=aki-5f15f636 --ec2-ramdisk=ari-0915f660"
       ebsopts="--kernel=aki-5f15f636 --ramdisk=ari-0915f660"
       ebsopts="$ebsopts --block-device-mapping /dev/sda2=ephemeral0"
     fi
     cat > part-i386.txt <<EOM
     root 10240 a1
     /mnt 1 a2
     swap 1024 a3
     EOM
     cat > part-x86_64.txt <<EOM
     root 10240 a1
     /mnt 1 b
     EOM
    
  6. Create a script to perform local customizations to the image before it is bundled. This is passed to vmbuilder below using the --execscript option:

     cat > setup-server <<'EOM'
     #!/bin/bash -ex
     imagedir=$1
     # fix what I consider to be bugs in vmbuilder
     perl -pi -e "s%^127.0.1.1.*\n%%" $imagedir/etc/hosts
     rm -f $imagedir/etc/hostname
     # Use multiverse
     perl -pi -e 's%(universe)$%$1 multiverse%' \
       $imagedir/etc/ec2-init/templates/sources.list.tmpl
     # Add Alestic PPA for runurl package (handy in user-data scripts)
     echo "deb http://ppa.launchpad.net/alestic/ppa/ubuntu karmic main" |
       tee $imagedir/etc/apt/sources.list.d/alestic-ppa.list
     chroot $imagedir \
       apt-key adv --keyserver keyserver.ubuntu.com --recv-keys BE09C571
     # Add ubuntu-on-ec2/ec2-tools PPA for updated ec2-ami-tools
     echo "deb http://ppa.launchpad.net/ubuntu-on-ec2/ec2-tools/ubuntu karmic main" |
       sudo tee $imagedir/etc/apt/sources.list.d/ubuntu-on-ec2-ec2-tools.list
     chroot $imagedir \
       sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 9EE6D873
     # Install packages
     chroot $imagedir apt-get update
     chroot $imagedir apt-get install -y runurl
     chroot $imagedir apt-get install -y ec2-ami-tools
     EOM
     chmod 755 setup-server
    
  7. Build the image:

     now=$(date +%Y%m%d-%H%M)
     dest=/mnt/dest-$codename-$now
     prefix=ubuntu-$release-$codename-$arch-$tag-$now
     description="Ubuntu $release $codename $arch $tag $now"
     sudo vmbuilder/vmbuilder xen ubuntu       \
       --suite=$codename                       \
       --arch=$arch2                           \
       --dest=$dest                            \
       --tmp=/mnt                              \
       --ec2                                   \
       --ec2-version="$description"            \
       --manifest=$prefix.manifest             \
       --lock-user                             \
       --part=part-$arch.txt                   \
       $kernelopts                             \
       $pkgopts                                \
       --execscript ./setup-server             \
       --debug
    

    [For S3 based AMI] include the following options in the vmbuilder command above. This does not preclude you from also building an EBS boot AMI with the same image. Make a note of the resulting AMI id output by vmbuilder:

       --ec2-bundle                            \
       --ec2-upload                            \
       --ec2-register                          \
       --ec2-bucket=$bucket                    \
       --ec2-prefix=$prefix                    \
       --ec2-user=$AWS_USER_ID                 \
       --ec2-cert=$EC2_CERT                    \
       --ec2-key=$EC2_PRIVATE_KEY              \
       --ec2-access-key=$AWS_ACCESS_KEY_ID     \
       --ec2-secret-key=$AWS_SECRET_ACCESS_KEY \
    
  8. [For EBS boot AMI] Copy the image files to a new EBS volume, snapshot it, and register the snapshot as an EBS boot AMI. Make a note of the resulting AMI id:

     size=15 # root disk in GB
     volumeid=$(ec2-create-volume --size $size --availability-zone us-east-1a |
       cut -f2)
     instanceid=$(wget -qO- http://instance-data/latest/meta-data/instance-id)
     ec2-attach-volume --device /dev/sdi --instance "$instanceid" "$volumeid"
     while [ ! -e /dev/sdi ]; do echo -n .; sleep 1; done
     sudo mkfs.ext3 -F /dev/sdi
     ebsimage=$dest/ebs
     sudo mkdir $ebsimage
     sudo mount /dev/sdi $ebsimage
     imageroot=$dest/root
     sudo mkdir $imageroot
     sudo mount -oloop $dest/root.img $imageroot
     sudo tar -cSf - -C $imageroot . | sudo tar xvf - -C $ebsimage
     sudo umount $imageroot $ebsimage
     ec2-detach-volume "$volumeid"
     snapshotid=$(ec2-create-snapshot "$volumeid" | cut -f2)
     ec2-delete-volume "$volumeid"
     while ec2-describe-snapshots "$snapshotid" | grep -q pending
       do echo -n .; sleep 1; done
     ec2-register                   \
       --architecture $arch         \
       --name "$prefix"             \
       --description "$description" \
       $ebsopts                     \
       --snapshot "$snapshotid"
    
  9. Depending on what you want to keep from the above process, there are various things that you might want to clean up.

    If you no longer want to use an S3 based AMI:

     ec2-deregister $amiid
     ec2-delete-bundle                     \
       --access-key $AWS_ACCESS_KEY_ID     \
       --secret-key $AWS_SECRET_ACCESS_KEY \
       --bucket $bucket                    \
       --prefix $prefix
    

    If you no longer want to use an EBS boot AMI:

     ec2-deregister $amiid
     ec2-delete-snapshot $snapshotid
    

    When you’re done with the original instance:

     ec2-terminate-instance $instanceid
    

In the above instructions I stray a bit from the defaults. For example, I add the runurl package from the Alestic PPA so that it is available for use in user-data scripts on first boot. I enable multiverse for easy access to more software, and I install ec2-ami-tools which works better for me than the current euca2ools.

I also set /mnt to the first ephemeral store on the instance even on EBS boot AMIs. This more closely matches the default on the S3 based AMIs, but means that /mnt will not be persistent across a stop/start of an EBS boot instance.

Explore and set options as you see fit for your applications. Go wild with the --execscript feature (similar to the ec2ubuntu-build-ami --script option) to customize your image.

The following vmbuilder options do not currently work with creating EC2 images: --mirror, --components, --ppa. I have submitted bug 502490 to track this.

As with much of my work here, I’m simply explaining how to use software that others have spent a lot of energy building. In this case a lot of thanks go to the Ubuntu server team for developing vmbuilder, the EC2 plugin, the ec2-init startup software, and the code which builds the official Ubuntu AMIs; especially Søren Hansen, Scott Moser, and Chuck Short. I also appreciate the folks who reviewed early copies of these instructions and provided feedback including Scott Moser, Art Zemon, Trifon Trifonov, Vaibhav Puranik, and Chris.

Community feedback, bug reports, and enhancements for these instructions are welcomed.

Creating a New Image for EC2 by Rebundling a Running Instance

NOTE: This is an article from 2009, back before EBS boot instances were available on Amazon EC2. I recommend you use EBS boot instances which make it trivial to create new AMIs (single command/API call). Please stop reading this article now and convert to EBS boot AMIs!

When you start up an instance (server) on Amazon EC2, you need to pick the image or AMI (Amazon Machine Image) to run. This determines the Linux distribution and version as well as the initial software installed and how it is configured.

There are a number of public images to choose from with EC2 including the Ubuntu and Debian image published on https://alestic.com but sometimes it is appropriate to create your own private or public images. There are two primary ways to create an image for EC2:

  1. Create an EC2 image from scratch. This process lets you control every detail of what goes into the image and is the easiest way to automate image creation.

  2. Rebundle a running EC2 instance into a new image. This approach is the topic of the rest of this article.

After you rebundle a running instance to create a new image, you can then run new EC2 instances of that image. Each instance starts off looking exactly like the original instance as far as the files on the disk go (with a few exceptions).

This guide is primarily written in the context of running Ubuntu on EC2, but the concepts should apply without too much changing on Debian and other Linux distributions.

To use this rebundling approach, you start by running an instance of an image that (1) is as close as possible to the image you want to create, and (2) is published by a source you trust. You then proceed to install software and configure that instance so that it contains exactly what you want to be available on new instances right down to the startup scripts.

The next step is to bundle the instance’s disk image into a new AMI, but before we get to that, it is important to understand a few things about security.

Security

If you are creating a new EC2 image, you need to be very careful what pieces of information you inadvertently leave on the image, especially if you have the goal of publishing it as a public AMI. Anybody who runs an instance of that AMI will have access to the files you included in the bundle, and there is no way to modify an AMI after it has been created (though you can delete it).

For example, you don’t want to leave your AWS certificate or private key on the disk. You’ll even want to clear out the shell history file in case you had typed secret information in commands or in setting environment variables.

You also want to consider the security concerns from the perspective of the people who run the new image. For example, you don’t want to leave any passwords active on accounts. You should also make sure you don’t include your public ssh key in authorized_keys files. Leaving a back door into other people’s servers is in poor taste even if you have no intention of ever using it.

Here are some sample commands, but only you can decide if this wipes out too much or what other files you need to exclude depending on how you set up and used the instance you are bundling:

sudo rm -f /root/.*hist* $HOME/.*hist*
sudo rm -f /var/log/*.gz
sudo find /var/log -name mysql -prune -o -type f -print | 
  while read i; do sudo cp /dev/null $i; done

Whole directories can be excluded from the image using the --exclude option of the ec2-bundle-vol command (see below).

Rebundling

Now we’re ready to bundle the actual EC2 image (AMI). To start, you need to copy your certificate and key to the instance ephemeral storage. Adjust the sample command to use the appropriate keypair file for authentication and the appropriate location of your certification and private key files. If you are not running a modern Ubuntu image, then change remoteuser to “root”.

remotehost=<ec2-instance-hostname>
remoteuser=ubuntu

rsync \
  --rsh="ssh -i KEYPAIR.pem" \
  --rsync-path="sudo rsync" \
  PATHTOKEYS/{cert,pk}-*.pem \
  $remoteuser@$remotehost:/mnt/

Set up some environment variables for convenience in the following commands. A single S3 bucket can be used for multiple AMIs. The manifest prefix should be descriptive, especially if you plan to publish the AMI publicly, as it is the only piece of documentation many users will see when they look through AMI lists. At a minimum, I recommend including the Linux distribution (e.g, “ubuntu”), the architecture (e.g., “i386” or “32”), and the date (e.g., “20090621”), as well as some tag that indicates the special nature of the image (e.g., “desktop” or “lamp”).

bucket=<your-bucket-name>
prefix=<descriptive-image-title>

On the EC2 instance itself, you also set up some environment variables to help the bundle and upload commands. You can find these values in your EC2 account.

export AWS_USER_ID=<your-value>
export AWS_ACCESS_KEY_ID=<your-value>
export AWS_SECRET_ACCESS_KEY=<your-value>

if [ $(uname -m) = 'x86_64' ]; then
  arch=x86_64
else
  arch=i386
fi

Bundle the files on the current instance into a copy of the image under /mnt:

sudo -E ec2-bundle-vol \
  -r $arch \
  -d /mnt \
  -p $prefix \
  -u $AWS_USER_ID \
  -k /mnt/pk-*.pem \
  -c /mnt/cert-*.pem \
  -s 10240 \
  -e /mnt,/root/.ssh,/home/ubuntu/.ssh

Upload the bundle to a bucket on S3:

ec2-upload-bundle \
   -b $bucket \
   -m /mnt/$prefix.manifest.xml \
   -a $AWS_ACCESS_KEY_ID \
   -s $AWS_SECRET_ACCESS_KEY

Now that the AMI files have been uploaded to S3, you register the image as a new AMI. This is done back on your local system (with the API tools installed):

ec2-register \
  --name "$bucket/$prefix" \
  $bucket/$prefix.manifest.xml

The output of this command is the new AMI id which is used to run new instances of that image.

It is important to use the same account access information for the ec2-bundle-vol and ec2-register commands even though they are run on different systems. If you don’t you’ll get an error indicating you don’t have the rights to register the image.

Public Images

By default, the new EC2 image is private, which means it can only be seen and run by the user who created it. You can share access with another individual account or with the public.

To let another EC2 user run the image without giving access to the world:

ec2-modify-image-attribute -l -a <other-user-id> <ami-id>

To let all other EC2 users run instances of your image:

ec2-modify-image-attribute -l -a all <ami-id>

Cost

AWS will charge you standard S3 charges for the stored AMI files which comes out to $0.15 per GB per month. Note, however, that the bundling process uses sparse files and compression, so the final storage size is generally very small and your resulting cost may only be pennies per month.

The AMI owner incurs no charge when users run the image in new instances. The users who run the AMI are responsible for the standard hourly instance charges.

Cleanup

Before removing any public image, please consider the impact this might have on people who depend on that image to run their business. Once you publish an AMI, there is no way to tell how many users are regularly creating instances of that AMI and expecting it to stay available. There is also no way to communicate with these users to let them know that the image is going away.

If you decide you want to remove an image anyway, here are the steps to take.

Deregister the AMI

ec2-deregister ami-XXX

Delete the AMI bundle in S3:

ec2-delete-bundle \
  --access-key $AWS_ACCESS_KEY_ID \
  --secret-key $AWS_SECRET_ACCESS_KEY \
  --bucket $bucket \
  --prefix $prefix

[Update 2009-09-12: Security tweak for running under non-root.] [Update 2010-02-01: Update to use latest API/AMI tools and work for Ubuntu 9.10 Karmic.]

Building EC2 Images from Scratch with ec2ubuntu-build-ami

Update: The process described here is deprecated for building Ubuntu AMIs. The Ubuntu vmbuilder software has replaced ec2ubuntu-build-ami. In addition I recommend starting with the Ubuntu images published by Canonical as described in this article:

Building EBS Boot AMIs Using Canonical’s Downloadable EC2 Images

The below information may be useful for building Debian AMIs, but EC2 kernels are slowly getting out of date for running modern Debian releases.



The Ubuntu and Debian images for EC2 which are published on https://alestic.com were built with the ec2ubuntu-build-ami software. This program can also be used by advanced users who wish to build from scratch their own Ubuntu or Debian images for EC2.

http://ec2ubuntu-build-ami.notlong.com

The basic instructions for using this script are as follows: