28

Using aws-cli 1.3.6 I am trying to get a simple table of my ec2 instances with the Name and state. I have been looking at the --query and JMESpath documentation and I have been able to select the "Value" item of a Map which "Key" item is equal to Name. This is useful to get the instance-name. Therefore, the code below seems to work

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

And delivers this:

-------------------
|DescribeInstances|
+-----------------+
|  Name1          |
|  Name2          |
+-----------------+

However, if I want to add the state, things get not as I would have expected. Using

aws ec2 describe-instances --output table --query 'Reservations[].Instances[].[Tags[?Key==`Name`].Value,State.Name]'

Delivers

-------------------
|DescribeInstances|
+-----------------+
|  Name1          |
|  stopped        |
|  Name2          |
|  stopped        |
+-----------------+

instead of a two column table with name and state.

If we turn the output to JSON, we can see that the Tags selection returns a list (one-element list) and that's probably the issue:

[
    [
        [
            "Name1"
        ],
        "stopped"
    ],
    [
        [
            "Name2"
        ],
        "stopped"
    ]
]

I have not been able to turn this list into an scalar by selecting the first element. This, does not work. Returns an empty list as the Name.

aws ec2 describe-instances --output json --query 'Reservations[].Instances[].[Tags[?Key==`Name`].Value[0],State.Name]'

The same as this

aws ec2 describe-instances --output json --query 'Reservations[].Instances[].[Tags[?Key==`Name`].Value[][0],State.Name]'

The only way I have figured out of addressing this is by means of the join function. Since I only expect one element, it is ok but I seems to be a little bit hacky.

aws ec2 describe-instances --output table --query 'Reservations[].Instances[].[join(`,`,Tags[?Key==`Name`].Value),State.Name]'

---------------------------
|    DescribeInstances    |
+-------------+-----------+
|  Name1      |  stopped  |
|  Name2      |  stopped  |
+-------------+-----------+

The question, therefore, is: is there any way of picking the first element of the result of the filter (?Key==XXXX) bearing in mind that suffixing it with [0] seems not to work?

Thanks in advance!

dnlbrky
  • 9,396
  • 2
  • 51
  • 64
c-garcia
  • 598
  • 1
  • 9
  • 17
  • 1
    I'm trying to do something similar, except I want to list out the associated security groups. However, anytime I try to use join(), I get an "Illegal token value", including when I paste in your example above. Frustrating. – MikeThomas Apr 18 '14 at 15:30
  • 1
    @MikeThomas - while this doesn't explain the issue you are facing, you might want to look into the new [Pipe Expressions](https://jmespath.readthedocs.org/en/latest/specification.html#pipe-expressions) available as of aws-cli 1.3.7, which might ease achieving your goal by a different approach, see [my answer](https://stackoverflow.com/a/23445073/45773) for details. – Steffen Opel May 03 '14 at 14:08

3 Answers3

31

The question, therefore, is: is there any way of picking the first element of the result of the filter (?Key==XXXX) bearing in mind that suffixing it with [0] seems not to work?

The way you phrased this question hints towards the solution in fact, namely Pipe Expressions (only available as of version 1.3.7 of the aws-cli though, hence impossible to figure out at question time):

pipe-expression  = expression "|" expression

A pipe expression combines two expressions, separated by the | character. It is similar to a sub-expression with two important distinctions:

  1. Any expression can be used on the right hand side. A sub-expression restricts the type of expression that can be used on the right hand side.
  2. A pipe-expression stops projections on the left hand side for propagating to the right hand side. If the left expression creates a projection, it does not apply to the right hand side.

The emphasized part is key, as shown in the subsequent examples, notably:

If you instead wanted only the first sub list, ["first1", "second1"], you can use a pipe-expression:

foo[*].bar[0] -> ["first1", "first2"]
foo[*].bar | [0] -> ["first1", "second1"]

Solution

Thus, applying a pipe expression yields the desired result:

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

----------------------------------
|        DescribeInstances       |
+--------------------+-----------+
|  Name1             |  stopped  |
|  Name2             |  stopped  |
+--------------------+-----------+
Steffen Opel
  • 63,899
  • 11
  • 192
  • 211
  • @c-garcia - Just realized that the underlying [JEP-4: Pipe Expressions](https://jmespath.readthedocs.org/en/latest/proposals/pipes.html) has been [merged as of April 15](https://github.com/boto/jmespath/commit/ff12d0a9842dca97de77e28322b21217ebd98359) only and consequently requires aws-cli 1.3.7, so you would have a very hard time to figure this one out when asking your question ;) - I've updated the answer accordingly. – Steffen Opel May 03 '14 at 14:04
  • thanks for your kind and precise answer. Tested with 1.3.8 (just updated the package) and works as expected! – c-garcia May 05 '14 at 04:42
  • @Steffen, great example .. helped me get atop a query I was struggling with. Thanks. Now, what would even be better is giving an example that queries more than one tag. ie, just as above, but include another column that will display another tag, say Tags[?Key==\`Project\']. – Chux Uzoeto Jul 16 '14 at 01:00
  • 4
    The TLDR is: `Tags[?Key==\`Name\`]|[0].Value]` – wisbucky Sep 25 '14 at 20:15
  • Splendid! Just what I was looking for. Thanks @SteffenOpel – Sankalp Jun 02 '15 at 05:19
  • I'm trying to do something similar but I'm trying to get only the `InstanceId`. I get a `null` when I select only the `InstanceId`. But if I add `[0].Value`, it works but also displays that value. How do I get it to display only the InstanceId and why does/doesn't that work? – chizou Mar 28 '18 at 04:13
3
#!/bin/bash
for r in `aws ec2 describe-regions --query Regions[*].RegionName --output text`
do

    #echo $r
    aws ec2 describe-instances --region $r --query 'Reservations[*].Instances[*].{ID:InstanceId, type:InstanceType, launched:LaunchTime, name:Tags[?Key==`Name`].Value[]}' --output json | jq --arg R $r -r '.[] | .[] | [$R, .ID, .type, .launched, .name[0]] | @csv'

done

Output:

"us-east-1","i-054f8253b9ed0746d","t2.micro","2018-10-31T01:57:52.000Z","xxx"
"us-east-1","i-0638792b8b3057ce2","t2.nano","2018-10-23T03:49:24.000Z","yyy"
Junhui Liu
  • 29
  • 3
1

It tells the Instance ID along with the server state

Command:
aws ec2 describe-instances --filter Name=tag:Name,Values=eep --query 'Reservations[*].Instances[*].{id:State,ID:InstanceId}' --output table

Query part in the above command
It changes as per the requirement
--query 'Reservations[*].Instances[*].{id:State,ID:InstanceId}' --output table