How to use the AWS CLI to find untagged instances


Originally published in Feb 2017, at Onica.com/blog

I often need to find AWS® EC2® instances without a particular tag. Usually, it’s the Name tag, but other tags come up from time to time (we use a tag of Owner quite a bit).

Use the AWS CLI to find instances without a Name tag

After coming across this Reddit post and this Stack Overflow question, we made it our mission to figure out how to get instances that were missing a tag through the AWS command line interface (CLI) instead of using external tools like jq or python.

The –Query Parameter, JMESPath, and filtering

JMESPath is the engine behind the –query parameter in AWS CLI. In its most basic form, it helps you filter out or traverse JSON. AWS has some nice documentation describing how to use it at a basic level. In this post, we focus on the filtering ability of JMESPath.

Using the bracket and a question mark triggers the JMESPath filter: [? ... ]

Find an instance with a specific tag

For example, many people want to get the instance Name tag and the InstanceId. You can do this by using the filter for the Tags -> Key=='Name', as shown in the following example:

aws ec2 describe-instances \
 --output text \
 --query 'Reservations[].Instances[].[Tags[?Key==`Name`].Value|[0],InstanceId]'

Find an instance without a tag

While this is useful, we actually want to do the opposite. So, we start by diving down into each instance:

aws ec2 describe-instances \
 --query 'Reservations[].Instances[]'

This operation spits out each of the instances. Then, we create a filter on each of these where the Name tag doesn’t exist. A tag that exists would look similar to the following example:

aws ec2 describe-instances \
 --query 'Reservations[].Instances[?Tags[?Key != `Name`]]'

The problem with this filter is that as we loop through the Tags array, we’ll most likely hit an instance with more than one tag. That instance registers true because the first tag may have "Key:" "SomeTag" (but the second could be "Key:" "Name"). So we want to look for the object with the Name key but with no value (aka null).

Solution

First, lets get the value from the Name tag:

aws ec2 describe-instances \
 --query 'Reservations[].Instances[].Tags[?Key == `Name`].Value'

Now we can use the JMESPath not_null() function to filter for ones that exist:

aws ec2 describe-instances \
 --query 'Reservations[].Instances[?not_null(Tags[?Key == `Name`].Value)]'

The problem is that this still captures anything with a Name tag. So we negate it. That is, we want anything that isn’t not_null [aka not not null]!

aws ec2 describe-instances \
 --query 'Reservations[].Instances[?!not_null(Tags[?Key == `Name`].Value)]'

This operation returns something similar to the following:

[
  [
    {
        "Monitoring": {
            "State": "disabled"
        }, 
        "PublicDnsName": "", 
        "RootDeviceType": "ebs"
        ...
    }
  ], 
  [], 
  [], 
  [], 
  [], 
  [], 
  [], 
  [
    {
        "Monitoring": {
            "State": "disabled"
        }, 
        "PublicDnsName": "", 
        "RootDeviceType": "ebs"
        ...
    }
  ], 
  []
]

We don’t want to have these empty arrays, so we flatten the instance object with a pipe:

aws ec2 describe-instances \
  --query 'Reservations[].Instances[?!not_null(Tags[?Key == `Name`].Value)] | []'

And that’s it!

Other examples

If you want a few more examples of some common uses, here you go:

  • Display InstanceId of instances that have no Name tag:

      aws ec2 describe-instances \
        --output text \
        --query 'Reservations[].Instances[?!not_null(Tags[?Key == `Name`].Value)] | [].[InstanceId]'
    
  • Display InstanceId of running instances that have no Owner tag:

      aws ec2 describe-instances \
        --output text \
        --filters Name=instance-state-name,Values=running \
        --query 'Reservations[].Instances[?!not_null(Tags[?Key == `Owner`].Value)] | [].[InstanceId]'
    
  • Display VolumeId and Size of volumes that have no Name tag:

      aws ec2 describe-volumes \
        --output text \
        --query 'Volumes[?!not_null(Tags[?Key == `Name`].Value)] | [].[VolumeId,Size]'
    
  • Display SnapshotId and StartTime of my snapshots that have no CreatedBy tag:

      aws ec2 describe-snapshots \
        --output text \
        --owner-ids self \
        --query 'Snapshots[?!not_null(Tags[?Key == `CreatedBy`].Value)] | [].[SnapshotId,StartTime]'
    

Why search for untagged instances with AWS CLI?

There are many reasons to tag instances, including automation and console organization, but at Rackspace Technology Onica, our biggest driver is cost. Employing a tagging policy to help track your instances' cost allocation is an important step in optimizing your AWS cost!

For more tips and tricks to lower your monthly AWS cost by up to 50%, download our eBook!

(Learn more about Rackspace AWS services.) for assistance.

Use the Feedback tab to make any comments or ask questions. You can also start a conversation with us.

post avatar
Rackspace Onica team

Share this information: