Skip to content

Instantly share code, notes, and snippets.

@plockaby
Created July 28, 2019 02:34
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save plockaby/9d838905dd3a6beaca5557202e1b3611 to your computer and use it in GitHub Desktop.
Save plockaby/9d838905dd3a6beaca5557202e1b3611 to your computer and use it in GitHub Desktop.
Converts an AWS Volume to AWS AMI
#!/bin/bash
# This script require the following EC2 APIs:
# DescribeSnapshots
# CreateSnapshot
# RegisterImage
# runtime overridable defaults:
AMI_VIRT_TYPE=hvm
AMI_ARCH=x86_64
AMI_RELEASE=buster
AMI_VOLTYPE=gp2
AMI_VOLSIZE=8
AMI_ON_TERM=true
AMI_ROOTDEV=xvda
PROFILE=default
region_flag=""
DRY_RUN=
command -v 'jq' > /dev/null || {
echo 'jq is not available. exiting' >&2
exit 1
}
usage() {
cat <<EOF
$0 [OPTIONS] VOLUME_ID
OPTIONS:
-v, --virt-type EC2 virtualization type (default=hvm)
-a, --arch EC2 architecture (default=x86_64)
-r, --release Debian release (default=buster)
-t, --volume-type Root volume type (default=gp2)
-z, --volume-size Root volume size in GB (default=8)
-o, --on-terminate Instance termination behavior (default=delete)
-d, --root-device Root device name (default=xvda)
-p, --profile Use the given profile for AWS API commands
-D, --dry-run Dry run mode, no snapshots or AMIs created
-F, --final This AMI build is intended to be public (affects AMI name)
--region Override the EC2 region
-h, --help Print usage
EOF
}
TEMP=$(getopt -o v:a:r:t:z:o:r:d:p:FDh \
--long virt-type:,arch:,release:,volume-type:,volume-size:,on-terminate:,root-device:,profile:,final,dry-run,region:,help \
-n "$0" -- "$@")
if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi
eval set -- "$TEMP"
while true ; do
case "$1" in
-v|--virt-type)
AMI_VIRT_TYPE="$2" ; shift 2
;;
-a|--arch)
AMI_ARCH="$2" ; shift 2
;;
-r|--release)
AMI_RELEASE="$2" ; shift 2
;;
-t|--volume-type)
AMI_VOLTYPE="$2" ; shift 2
;;
-z|--volume-size)
AMI_VOLSIZE="$2" ; shift 2
;;
-o|--on-terminate)
AMI_ON_TERM="$2" ; shift 2
;;
-d|--root-device)
AMI_ROOTDEV="$2" ; shift 2
;;
-p|--profile)
PROFILE="$2" ; shift 2
;;
-D|--dry-run)
DRY_RUN=true ; shift
;;
-F|--final)
FINAL=true ; shift
;;
--region)
region_flag="--region $2" ; shift 2
;;
-h|--help)
usage; shift
exit 0
;;
--)
shift ; break
;;
esac
done
vol_id=$1; shift
if [ "${vol_id%%-*}" != "vol" ]; then
echo "First argument must be an EBS volume ID"
exit 1
fi
if [ "${VIRT_TYPE}" == "paravirtual" ] ; then
sriov=""
ena_support="false"
else
sriov="simple"
ena_support="true"
fi
awscmd="aws $region_flag --profile $PROFILE"
snapshot_state() {
local snapid="$1"
$awscmd ec2 describe-snapshots \
--output json \
--snapshot-id "$snapid" \
| jq -r '.Snapshots[].State'
}
# Encode the date in the AMI in a form like 2016-11-05-78872. The
# intent is to generate a human-friendly sortable image name while
# also taking reasonable steps to avoid name collisions.
img_stamp=$(date -u +%Y-%m-%d-)$(($(date +%s)%86400))
if [ "${FINAL}" == "true" ]; then
image_name="debian-${AMI_RELEASE}-${AMI_VIRT_TYPE}-${AMI_ARCH}-${AMI_VOLTYPE}-$img_stamp"
else
image_name="scratch-${USER}-${AMI_RELEASE}-${AMI_VIRT_TYPE}-${AMI_ARCH}-${AMI_VOLTYPE}-$img_stamp"
fi
cmd="$awscmd --output json ec2 create-snapshot --description \"$image_name\" --volume-id $vol_id"
if [ -n "$DRY_RUN" ]; then
echo "Dry run: $cmd"
snap_state="completed"
else
snap_id=$($cmd | jq -r .SnapshotId)
echo "Snapshot $snap_id creating. Waiting for it to become available"
echo EXEC $awscmd ec2 wait snapshot-completed --snapshot-ids "$snap_id"
$awscmd ec2 wait snapshot-completed --snapshot-ids "$snap_id" || exit 1
snap_state=$(snapshot_state $snap_id)
fi
if [ "$snap_state" != "completed" ]; then
echo -n "ERROR: $snap_id is not ready. State is $snap_state"
exit 1
fi
json_body=$(mktemp) || exit 1
cat > "$json_body" <<EOF
{
"DryRun": false,
"Name": "$image_name",
"Description": "Debian 10 'buster'",
"Architecture": "$AMI_ARCH",
"RootDeviceName": "$AMI_ROOTDEV",
"BlockDeviceMappings": [
{
"DeviceName": "$AMI_ROOTDEV",
"Ebs": {
"SnapshotId": "$snap_id",
"VolumeSize": 8,
"DeleteOnTermination": true,
"VolumeType": "$AMI_VOLTYPE"
}
}
],
"VirtualizationType": "$AMI_VIRT_TYPE",
"SriovNetSupport": "$sriov",
"EnaSupport": $ena_support
}
EOF
echo "Wrote API request body to $json_body"
cmd="$awscmd ec2 register-image --cli-input-json file://$json_body"
if [ -n "$DRY_RUN" ]; then
echo "Dry run: $cmd"
echo "Input data:"
cat "$json_body"
else
$cmd
fi
# Local variables:
# mode: shell-script
# tab-width: 4
# indent-tabs-mode: nil
# end:
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment