Skip to content

Instantly share code, notes, and snippets.

@aspring
Created October 21, 2016 02:03
Show Gist options
  • Save aspring/720a0df5680a7fb8afc599e739f822c1 to your computer and use it in GitHub Desktop.
Save aspring/720a0df5680a7fb8afc599e739f822c1 to your computer and use it in GitHub Desktop.
Modified: https://github.com/blalor/ami-creator/blob/master/utils/create-ami.sh for managing multiple externally built disk images
#!/usr/bin/env bash
# This script is loosely based off of: https://github.com/blalor/ami-creator/blob/master/utils/create-ami.sh
set -e -u
die() {
echo "$@"
exit 1
}
# This function displays the usage options for this script
usage() {
cat << EOF
usage: `basename $0` options
This script assists in the creation of an Amazon Machine Image from a Disk Image.
OPTIONS:
-d <image,device,size> The Disk Image, block device, and size comma separated.
-h Show this message.
-n <name> (Required) The name for the Amazon Machine Image.
-s <name> (Required) The S3 bucket for HVM imported volume.
-z <name> (Required) The availability zone for the HVM volume.
EOF
}
# Thie function validates the user provided arguments for correctness.
validate_arguments() {
# Make sure we have at least one disk
if [ ${#ARG_DISKS[@]} -eq 0 ]; then
die "ERROR: must specify at least one disk."
fi
# Split the disk strings into pieces
for (( i=0; i<${#ARG_DISKS[@]}; i++ ));
do
ARG_IMAGES+=($(echo ${ARG_DISKS[$i]} | cut -f1 -d,))
ARG_DEVICES+=($(echo ${ARG_DISKS[$i]} | cut -f2 -d,))
ARG_SIZES+=($(echo ${ARG_DISKS[$i]} | cut -f3 -d,))
done
# Verify an AMI name was provided
[[ ! -z $ARG_NAME ]] || die "ERROR: An AMI name must be provided"
if [ ${#ARG_NAME} -lt 3 ] || [ ${#ARG_NAME} -gt 128 ]; then
die "ERROR: illegal length for AMI name; must be >= 3, <= 128"
fi
if echo $ARG_NAME | egrep -q '[^A-Za-z0-9 ()./_-]' ; then
die "ERROR: illegal characters in AMI name; must be [A-Za-z0-9 ()./_-]"
fi
# Verify that the AMI Name is not already in use
if [ "$( aws ec2 describe-images --filters "Name=name,Values=${ARG_NAME}" | /usr/local/bin/jq -r '.Images | length' )" -ne 0 ]; then
die "ERROR: An AMI with that name already exists!"
fi
return 0
}
# This function validates the environment provided applications and configration for sanity
validate_environment() {
# Verify this is run as the root user
[ $EUID -eq 0 ] || die "ERROR: This script must be run as the 'root' user."
# Verify we have the necessary third party tools
which aws >/dev/null 2>&1 || die "ERROR: 'aws' not found"
which curl >/dev/null 2>&1 || die "ERROR: 'curl' not found"
which /usr/local/bin/jq >/dev/null 2>&1 || die "ERROR: 'jq' not found"
# Verify AWS environment variables
[[ ! -z $AWS_ACCESS_KEY_ID ]] || die "ERROR: AWS_ACCESS_KEY_ID not set"
[[ ! -z $AWS_SECRET_ACCESS_KEY ]] || die "ERROR: AWS_SECRET_ACCESS_KEY not set"
[ -n $AWS_ACCESS_KEY_ID ] || die "ERROR: AWS_ACCESS_KEY_ID not set"
[ -n $AWS_SECRET_ACCESS_KEY ] || die "ERROR: AWS_SECRET_ACCESS_KEY not set"
return 0
}
#
# Script Logic
#
# Disk related arrays
ARG_DISKS=()
ARG_DEVICES=()
ARG_IMAGES=()
ARG_SIZES=()
ARG_BUCKET=
ARG_KERNEL=
ARG_NAME=
ARG_REGION=
ARG_TYPE=
EXTRA_OPTIONS=""
# Validate our environment
validate_environment
while getopts "d:h:n:s:z:" optname
do
case "$optname" in
d)
echo "Adding Disk Configuration: $OPTARG"
ARG_DISKS+=($OPTARG)
;;
h)
usage
exit 1
;;
n)
echo "Using AMI Name: $OPTARG"
ARG_NAME=$OPTARG
;;
s)
echo "Using S3 Bucket: $OPTARG"
ARG_BUCKET=$OPTARG
;;
z)
echo "Using AWS Region: $OPTARG"
ARG_REGION=$OPTARG
;;
*)
echo "Unknown option -$optname"
usage
exit 1
;;
esac
done
# Validate our arguments (after we know our region and AZ)
validate_arguments
VOLUME_ID=()
SNAPSHOT_ID=()
# Import each of the disk images into EC2 -- storing the volume name
for (( i=0; i<${#ARG_IMAGES[@]}; i++ ));
do
image=${ARG_IMAGES[$i]}
echo "Importing the $image into EC2..."
conv_id=$(ec2-import-volume -f raw -o $AWS_ACCESS_KEY_ID -w $AWS_SECRET_ACCESS_KEY -b ${ARG_BUCKET} -z ${ARG_REGION} ${image} | grep -o 'import\-vol\-[a-zA-Z0-9]*' -m 1)
echo "Waiting for conversion task ${conv_id} to complete (this can take a LONG time)..."
while [ $( aws ec2 describe-conversion-tasks --conversion-task-ids ${conv_id} | /usr/local/bin/jq -r '.ConversionTasks[].State' ) != "completed" ]; do
sleep 15
done
volume_id=$(aws ec2 describe-conversion-tasks --conversion-task-ids ${conv_id} | /usr/local/bin/jq -r '.ConversionTasks[0].ImportVolume.Volume.Id')
echo "Tagging ${volume_id}..."
aws ec2 create-tags --resources ${volume_id} --tags \
Key=Name,Value="${ARG_NAME}-vol-${i}" \
Key=fedup,Value=1
VOLUME_ID+=("${volume_id}")
echo "Conversion task (${conv_id}) completed generating volume ${volume_id}!"
done
# Create a snapshot of the volume
for (( i=0; i<${#VOLUME_ID[@]}; i++ ));
do
volume_id=${VOLUME_ID[$i]}
echo "Creating snapshot of the volume..."
snap_id=$( aws ec2 create-snapshot --volume-id ${VOLUME_ID[$i]} --description "Root Image for ${ARG_NAME}" | /usr/local/bin/jq -r .SnapshotId )
echo "Waiting for snapshot ${snap_id} to complete"
while [ $( aws ec2 describe-snapshots --snapshot-ids ${snap_id} | /usr/local/bin/jq -r .Snapshots[].State ) != "completed" ]; do
sleep 15
done
echo "Tagging ${snap_id}..."
aws ec2 create-tags --resources ${snap_id} --tags \
Key=Name,Value="${ARG_NAME}-snapshot-${i}" \
Key=fedup,Value=1
SNAPSHOT_ID+=("${snap_id}")
echo "Snapshot (${snap_id}) completed!"
done
# Delete the volumes
for (( i=0; i<${#VOLUME_ID[@]}; i++ ));
do
volume_id=${VOLUME_ID[$i]}
echo "Deleting volume ${volume_id}..."
$( aws ec2 delete-volume --volume-id ${VOLUME_ID[$i]} )
echo "Volume deletion (${volume_id}) completed!"
done
# Build the block devive mappings JSON
BLOCK_DEVICE_MAPPINGS=()
for (( i=0; i<${#SNAPSHOT_ID[@]}; i++ ));
do
BLOCK_DEVICE_MAPPINGS+=("{\"DeviceName\":\"${ARG_DEVICES[$i]}\",\"Ebs\":{\"SnapshotId\":\"${SNAPSHOT_ID[$i]}\",\"VolumeSize\":${ARG_SIZES[$i]}}}")
done
IFS=$',';
BLOCK_DEVICE_MAPPINGS_STRING="${BLOCK_DEVICE_MAPPINGS[*]}"
#
# Create an AMI from the snapshot
#
echo
echo "Generating AMI from snapshot..."
image_id=$( \
aws ec2 register-image \
--architecture x86_64 \
--name "${ARG_NAME}" \
--root-device-name "${ARG_DEVICES[0]}" \
--block-device-mappings "[${BLOCK_DEVICE_MAPPINGS_STRING}]" \
--virtualization-type hvm \
| /usr/local/bin/jq -r .ImageId
)
echo "AMI '${ARG_NAME}' created with id ${image_id}"
#
# Generate a JSON file with all of the relevant information
#
{
echo "{"
echo " \"ami_id\": \"${image_id}\", "
echo " \"ami_name\": \"${ARG_NAME}\", "
echo " \"virt_type\": \"${ARG_TYPE}\""
echo "}"
} > "ami.json"
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment