Search code examples
amazon-web-servicesaws-clijmespath

AWS cli JMESpath query tag values


As a follow-up to my previous question, I am having trouble getting tags to work right in this type of output. I want to print a table with each property as a row. This is how I expect it to look:

% aws --output table ec2 describe-instances --instance-id $id --query "Reservations[].Instances[0].[{ Property: 'Type', Value: InstanceType }]"         
-------------------------------
|      DescribeInstances      |
+-----------+-----------------+
| Property  |      Value      |
+-----------+-----------------+
|  Type     |  g4dn.12xlarge  |
+-----------+-----------------+

But with tag names it looks like this:

% aws --output table ec2 describe-instances --instance-id $id --query "Reservations[].Instances[0].[{ Property: 'Name', Value: Tags[?Key =='Name'].Value }]"   
-------------------
|DescribeInstances|
+-----------------+
|    Property     |
+-----------------+
|  Name           |
+-----------------+
||     Value     ||
|+---------------+|
||  Elliott-TKD  ||
|+---------------+|

The tag value is correct, but the formatting is weird, and when combined with more other rows the table gets really ugly.


Solution

  • The filter part of your query ([?Key == 'Name']) is creating what JMESPath is describing as a projection.
    You will have to reset this projection in order to extract a single string out of it.
    Resetting a projection can be achieved using pipes.

    Projections are an important concept in JMESPath. However, there are times when projection semantics are not what you want. A common scenario is when you want to operate of the result of a projection rather than projecting an expression onto each element in the array. For example, the expression people[*].first will give you an array containing the first names of everyone in the people array. What if you wanted the first element in that list? If you tried people[*].first[0] that you just evaluate first[0] for each element in the people array, and because indexing is not defined for strings, the final result would be an empty array, []. To accomplish the desired result, you can use a pipe expression, <expression> | <expression>, to indicate that a projection must stop.

    Source: https://jmespath.org/tutorial.html#pipe-expressions

    So your issue is very close to what they are describing here in the documentation and the reset of that projection can be achieved using:

    Tags[?Key =='Name']|[0].Value 
    

    or, with:

    Tags[?Key =='Name'].Value | [0]
    

    which are two strictly identical queries.

    Given the JSON:

    {
      "Reservations": [
        {
          "Instances": [
            {
    
              "Tags": [
                {
                  "Key": "Name",
                  "Value": "Elliott-TKD"
                },
                {
                  "Key": "Foo",
                  "Value": "Bar"
                }
              ]
            }
          ]
        }
      ]
    }
    

    The query

    Reservations[].Instances[0].[{ Property: `Name`, Value: Tags[?Key == `Name`]|[0].Value }]
    

    Will give the expected

    [
      [
        {
          "Property": "Name",
          "Value": "Elliott-TKD"
        }
      ]
    ]
    

    So it will render properly in your table

    ------------------------------
    |      DescribeInstance      |
    +------------+---------------+
    |  Property  |     Value     |
    +------------+---------------+
    |    Name    |  Elliott-TKD  |
    +------------+---------------+