
{"id":7188,"date":"2021-01-29T18:47:31","date_gmt":"2021-01-29T18:47:31","guid":{"rendered":"https:\/\/rax-stg-wp-aws.com\/blogs\/how-to-use-the-aws-cli-to-find-untagged-instances\/"},"modified":"2026-11-03T19:28:11","modified_gmt":"2026-11-03T19:28:11","slug":"how-to-use-the-aws-cli-to-find-untagged-instances","status":"publish","type":"blogs","link":"https:\/\/www.rackspace.com\/de\/blogs\/how-to-use-the-aws-cli-to-find-untagged-instances\/","title":{"rendered":"How to use the AWS CLI to find untagged instances"},"content":{"rendered":"<p><em><strong>Originally published in Feb 2017, at the Onica blog <a href=\"https:\/\/onica.com\/blog\/\">here<\/a>.<\/strong><\/em><\/p>\n<p>I often need to find AWS EC2 instances without a particular tag. Usually, it&#8217;s the <strong>Name<\/strong> tag, but other tags come up from time to time (we use a tag of <em>Owner<\/em>&nbsp;quite<br \/>\na bit).&nbsp;<\/p>\n<p>Use the AWS CLI to find instances without a <em>Name<\/em> tag<\/p>\n<p>After coming across <a href=\"https:\/\/www.reddit.com\/r\/aws\/comments\/5unh0g\/finding_ec2_instances_which_lack_a_certain_tag\/\">this Reddit post<\/a>&nbsp;and <a href=\"https:\/\/stackoverflow.com\/questions\/18858120\/finding-all-amazon-aws-instances-that-do-not-have-a-certain-tag\">this Stack Overflow question<\/a>,&nbsp;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.<\/p>\n<p><strong>The &#8211;Query Parameter, JMESPath, and filtering<\/strong><\/p>\n<p><a href=\"https:\/\/jmespath.org\/\">JMESPath&nbsp;<\/a>is the engine behind the <strong>&#8211;query<\/strong>&nbsp;parameter in AWS CLI. In its most basic form, it helps you filter out or traverse JSON. AWS has <a href=\"https:\/\/docs.aws.amazon.com\/cli\/latest\/userguide\/cli-usage-output.html\">some nice documentation<\/a><br \/>\ndescribing how to use it at a basic level. In this post, we focus on the filtering ability of JMESPath.<\/p>\n<p>Using the bracket and a question mark triggers the JMESPath filter: <em>[? &#8230; ]<\/em><\/p>\n<p><strong>Find an instance with a specific tag<\/strong><br \/>\nFor example, many people want to get the instance <strong>Name<\/strong>&nbsp;tag and the <strong>InstanceId<\/strong>. You can do this by using the filter for the <em>Tags -&gt; Key==&#8217;Name<\/em>`, as shown in the following example:<\/p>\n<p>&nbsp; &nbsp; aws ec2 describe-instances \\<br \/>\n&nbsp; &nbsp; &nbsp;&#8211;output text \\<br \/>\n&nbsp; &nbsp; &nbsp;&#8211;query <em>Reservations[].Instances[].[Tags[?Key==`Name`].Value|[0],InstanceId]<\/em><\/p>\n<p><strong>Find an instance without a tag<\/strong><\/p>\n<p>While this is useful, we want to do the opposite. So, we start by diving down into each instance:<\/p>\n<p>aws ec2 describe-instances \\<br \/>\n&nbsp; &nbsp; &nbsp;&#8211;query Reservations[].Instances[]<\/p>\n<p>This operation spits out each of the instances. Then, we create a filter on each of these where the <strong>Name<\/strong>&nbsp;tag doesn&#8217;t exist. A tag that exists would look similar to the following example:<\/p>\n<p><em>&nbsp; &nbsp; aws ec2 describe-instances \\<br \/>\n&nbsp; &nbsp; &nbsp;&#8211;query &#8216;Reservations[].Instances[?Tags[?Key != `Name`]]&#8217;<\/em><\/p>\n<p>The problem with this filter is that as we loop through the <strong>Tags<\/strong>&nbsp;array, we&#8217;ll most likely hit an instance with more than one tag. That instance registers <em>true<\/em>&nbsp;because the<br \/>\nfirst tag may have `&#8221;Key:&#8221; &#8220;SomeTag&#8221;` (but the second could be `&#8221;Key:&#8221; &#8220;Name&#8221;`). So we want to look for the object with the <strong>Name<\/strong>&nbsp;key but with no value (aka <em>null<\/em>).<\/p>\n<p><strong>Solution<\/strong><\/p>\n<p>First, let&#8217;s get the value from the <strong>Name<\/strong>&nbsp;tag:<\/p>\n<p><em>&nbsp; &nbsp; aws ec2 describe-instances \\<br \/>\n&nbsp; &nbsp; &nbsp;&#8211;query &#8216;Reservations[].Instances[].Tags[?Key == `Name`].Value&#8217;<\/em><\/p>\n<p>Now we can use the JMESPath <strong>not_null<\/strong>()function to filter for ones that exist:<\/p>\n<p><em>&nbsp; &nbsp; aws ec2 describe-instances \\<br \/>\n&nbsp; &nbsp; &nbsp;&#8211;query &#8216;Reservations[].Instances[?not_null(Tags[?Key == `Name`].Value)]&#8217;<\/em><\/p>\n<p>The problem is that this still captures anything <em>with<\/em>&nbsp;a <strong>Name&nbsp;<\/strong>tag. So we negate it. That is, we want anything that <em>isn&#8217;t<\/em>&nbsp;<em>not_null<\/em> [aka not not null]!<\/p>\n<p><em>&nbsp; &nbsp; aws ec2 describe-instances \\<br \/>\n&nbsp; &nbsp; &nbsp;&#8211;query &#8216;Reservations[].Instances[?!not_null(Tags[?Key == `Name`].Value)]&#8217;<\/em><br \/>\n&nbsp;<\/p>\n<p>This operation returns something similar to the following:<\/p>\n<p>&nbsp; &nbsp; [<br \/>\n&nbsp; &nbsp; &nbsp; [<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &#8220;Monitoring&#8221;: {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &#8220;State&#8221;: &#8220;disabled&#8221;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; },&nbsp;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &#8220;PublicDnsName&#8221;: &#8220;&#8221;,&nbsp;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &#8220;RootDeviceType&#8221;: &#8220;ebs&#8221;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &#8230;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; }<br \/>\n&nbsp; &nbsp; &nbsp; ],&nbsp;<br \/>\n&nbsp; &nbsp; &nbsp; [],&nbsp;<br \/>\n&nbsp; &nbsp; &nbsp; [],&nbsp;<br \/>\n&nbsp; &nbsp; &nbsp; [],&nbsp;<br \/>\n&nbsp; &nbsp; &nbsp; [],&nbsp;<br \/>\n&nbsp; &nbsp; &nbsp; [],&nbsp;<br \/>\n&nbsp; &nbsp; &nbsp; [],&nbsp;<br \/>\n&nbsp; &nbsp; &nbsp; [<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &#8220;Monitoring&#8221;: {<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &#8220;State&#8221;: &#8220;disabled&#8221;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; },&nbsp;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &#8220;PublicDnsName&#8221;: &#8220;&#8221;,&nbsp;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &#8220;RootDeviceType&#8221;: &#8220;ebs&#8221;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &#8230;<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; }<br \/>\n&nbsp; &nbsp; &nbsp; ],&nbsp;<br \/>\n&nbsp; &nbsp; &nbsp; []<br \/>\n&nbsp; &nbsp; ]<\/p>\n<p>\nWe don&#8217;t want to have these empty arrays, so we flatten the instance object with a pipe:<\/p>\n<p><em>aws ec2 describe-instances \\<br \/>\n&nbsp; &nbsp; &nbsp; &#8211;query &#8216;Reservations[].Instances[?!not_null(Tags[?Key == `Name`].Value)] | []&#8217;<\/em><\/p>\n<p><em>And that&#8217;s it!&nbsp;<\/em><\/p>\n<p><strong>Other Examples<\/strong><\/p>\n<p>If you want a few more examples of some common uses, here you go:<\/p>\n<p>&#8211; Display InstanceId of instances that have no Name tag:<\/p>\n<p>&nbsp; &nbsp; &nbsp; &nbsp; aws ec2 describe-instances \\<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &#8211;output text \\<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &#8211;query &#8216;Reservations[].Instances[?!not_null(Tags[?Key == `Name`].Value)] | [].[InstanceId]&#8217;<br \/>\n&nbsp;<br \/>\n&#8211; Display InstanceId of running instances that have no Owner tag:<\/p>\n<p>&nbsp; &nbsp; &nbsp; &nbsp; aws ec2 describe-instances \\<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &#8211;output text \\<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &#8211;filters Name=instance-state-name,Values=running \\<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &#8211;query &#8216;Reservations[].Instances[?!not_null(Tags[?Key == `Owner`].Value)] | [].[InstanceId]&#8217;<br \/>\n&nbsp;<br \/>\n&#8211; Display VolumeId and Size of volumes that have no Name tag:<\/p>\n<p>&nbsp; &nbsp; &nbsp; &nbsp; aws ec2 describe-volumes \\<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &#8211;output text \\<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &#8211;query &#8216;Volumes[?!not_null(Tags[?Key == `Name`].Value)] | [].[VolumeId,Size]&#8217;<br \/>\n&nbsp;<br \/>\n&#8211; Display SnapshotId and StartTime of my snapshots that have no CreatedBy tag:<\/p>\n<p>&nbsp; &nbsp; &nbsp; &nbsp; aws ec2 describe-snapshots \\<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &#8211;output text \\<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &#8211;owner-ids self \\<br \/>\n&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &#8211;query &#8216;Snapshots[?!not_null(Tags[?Key == `CreatedBy`].Value)] | [].[SnapshotId,StartTime]&#8217;<br \/>\n&nbsp;<\/p>\n<p><strong>Why search for untagged instances with AWS CLI?<\/strong><\/p>\n<p>There are many reasons to tag instances, including <a href=\"https:\/\/d1.awsstatic.com\/whitepapers\/aws-tagging-best-practices.pdf\">automation and console organization<\/a>, but at Rackspace Technology, our biggest driver is c<em>ost<\/em>. Employing a tagging policy to help track your instances&#8217; cost allocation is an important step in optimizing your AWS cost!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>How to use the AWS CLI to find untagged instances<\/p>\n","protected":false},"author":2,"featured_media":7190,"menu_order":0,"comment_status":"open","ping_status":"open","template":"","format":"standard","meta":{"content-type":"","nid":"26297","content_type":"blog","created_by":"10130","authored_on":"2023-11-27T18:47:31+00:00","field_article_industries":"","tag_category":"Technical","field_blog_author_text":"By Rackspace Technical Staff ","seo_description":"Discover effective strategies to find AWS EC2 instances without specific tags. Maintain a well-structured cloud environment.\r\n","field_featured_image":"","insights_image":"\/wp-content\/uploads\/2026\/blog_technical_images\/26297-insights_image-aws-technical-blog-1-insights.png","field_partners":"","rxt_platforms":"Amazon Web Services (AWS)","product":"","publisheddate":"2021-01-29T18:47:31","rxt_solutions":"Onica by Rackspace Technology, Data","opengraph_tag":"","x_tag":"","teaser_flag":"Technical Blog","seo_titile":"Effective AWS Management Untagged EC2 | Rackspace","field_topics":"","field_slug":"how-to-use-the-aws-cli-to-find-untagged-instances","footnotes":""},"categories":[178],"tags":[204],"class_list":["post-7188","blogs","type-blogs","status-publish","format-standard","has-post-thumbnail","hentry","category-blog","tag-technical"],"_links":{"self":[{"href":"https:\/\/www.rackspace.com\/de\/wp-json\/wp\/v2\/blogs\/7188","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.rackspace.com\/de\/wp-json\/wp\/v2\/blogs"}],"about":[{"href":"https:\/\/www.rackspace.com\/de\/wp-json\/wp\/v2\/types\/blogs"}],"author":[{"embeddable":true,"href":"https:\/\/www.rackspace.com\/de\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.rackspace.com\/de\/wp-json\/wp\/v2\/comments?post=7188"}],"version-history":[{"count":1,"href":"https:\/\/www.rackspace.com\/de\/wp-json\/wp\/v2\/blogs\/7188\/revisions"}],"predecessor-version":[{"id":7189,"href":"https:\/\/www.rackspace.com\/de\/wp-json\/wp\/v2\/blogs\/7188\/revisions\/7189"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.rackspace.com\/de\/wp-json\/wp\/v2\/media\/7190"}],"wp:attachment":[{"href":"https:\/\/www.rackspace.com\/de\/wp-json\/wp\/v2\/media?parent=7188"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.rackspace.com\/de\/wp-json\/wp\/v2\/categories?post=7188"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.rackspace.com\/de\/wp-json\/wp\/v2\/tags?post=7188"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}