I have been spending a lot of my time lately looking at ways to optimize our spend in AWS and Google Cloud.  Part of that is submitting reservations for our EC2 instances.  Since I am not a fan of clicking around in the GUI, I decided to figure out what CLI commands I would need be to lookup and purchase EC2 reserved instances.  For a single account, you can easily turn this into a script that will do all the work for you.  In an organization that a large number of accounts, the logic is a little more complex, and I have not had the time to put together a proper program, so in the short term I use the CLI to gather the data to put into a spreadsheet and then use the CLI commands to order the numbers I need.

To be able to use the CLI to lookup and purchase reserved instances, you need to gather three pieces of information: the number of each type of servers you are running, the number of reservations that you currently have, and the offering id of the reserved instance type that you want to purchase.

Getting the number of instances you have have is relatively strait forward.  You can just query the instance type and then run them through uniq and get the counts.

aws ec2 describe-instances --filters Name=instance-state-name,Values=running \
	--query 'Reservations[*].Instances[*].[InstanceType]' \
	--output text|sort |uniq -c

If you are like me and have instances running in multiple regions, you can use a loop to go through each region.

for region in `aws ec2 describe-regions --output text | cut -f 2|cut -d . -f 2`; 
do 
echo "-- $region"; 
aws ec2 describe-instances --region $region \
	--filters Name=instance-state-name,Values=running \
	--query 'Reservations[*].Instances[*].[InstanceType]' \
	--output text|sort |uniq -c; 
echo; 
done

Once you have the number of instances, you can compare them against the current number of reservations you have.  Since I make multiple purchases throughout the year, I use the awk command to combine reservations of the same type to see all the total instances of each we have reserved in a single line.

aws ec2 describe-reserved-instances --filters Name=state,Values=active \
	--query "ReservedInstances[*].[InstanceType, InstanceCount]" \
	--output text|awk '{i[$1]+=$2} END{for(x in i){print x" "i[x]}}'

And again, if you are multi region, you will need to run a loop.

for region in `aws ec2 describe-regions --output text | cut -f 2|cut -d . -f 2`; 
  do 
  echo "-- $region"; 
  aws ec2 describe-reserved-instances --region $region \
	--filters Name=state,Values=active \
	--query "ReservedInstances[*].[InstanceType, InstanceCount]" \
	--output text|awk '{i[$1]+=$2} END{for(x in i){print x" "i[x]}}';         
  echo;
done

You can also add a date search to your query to get the number of RIs you will have on a specific date.  This is really helpful when you are trying to plan a future purchase.

aws ec2 describe-reserved-instances --filters Name=state,Values=active \
	--query 'ReservedInstances[?End>=`2019-08-11`].[InstanceType, InstanceCount]' \
	--output text|awk '{i[$1]+=$2} END{for(x in i){print x" "i[x]}}'

Once you compare the numbers and know how many instances you need to purchase, you will need to lookup the offering ID.  For that, you will need to set a couple of variables, including region, instance type, offering class, and offering type.

REGION="us-east-1" &&
INSTANCE_TYPE="t2.micro" &&
# Can be "standard", "convertable"
OFFERING_CLASS="standard" &&
# Can Be "No Upfront", "Partial Upfront", "No Upfront"
OFFERING_TYPE="All Upfront" &&
aws ec2 describe-reserved-instances-offerings \
    --region $REGION \
    --instance-type $INSTANCE_TYPE \
    --no-include-marketplace \
    --offering-class $OFFERING_CLASS \
    --instance-tenancy default \
    --offering-type "$OFFERING_TYPE" \
    --product-description "Linux/UNIX" \
    --min-duration  31536000 \
    --max-duration 31536000 \
    --filters "Name=scope, Values=Region" \
    --query "ReservedInstancesOfferings[*].ReservedInstancesOfferingId" \
    --output text

This will output the offering ID that you can use to reserve the instances.

purchase-reserved-instances-offering --instance-count X \
	--reserved-instances-offering-id $OUTPUT

With these commands, I can get an overview of the number of instances that I have, what reservations I currently own, and can quickly run through an purchase the instances that I need without clicking through a bunch of screens to order my reserved instances.