Using contains will not really lead you anywhere, because, the mere fact that the Tags array contains "Key": "eks", won't tell you much without the associated value.
And if you go on dissociating the Key and Value, it will be a nightmare to find back which is what.
Here is an oversimplified example to actually do the tag filtering — in an inclusive way:
Reservations[]
.Instances[?
Tags[?Key == `env` && Value == `dev`]
].[InstanceId]
With this, we are stating that we want all instances having a tag object that would have both its Key equal to env and its Value equal to dev.
Now, we can add an and expression and further filter it
Reservations[]
.Instances[?
Tags[?Key == `env` && Value == `dev`]
&& Tags[?Key == `eks` && Value == `false`]
].[InstanceId]
Now, we also account for the object having the tag eks with a value of false.
You will still miss some case with this, though, because you also have to account for instances that are totally missing the eks tag. And this one is a bit trickier:
Reservations[]
.Instances[?
Tags[?Key == `env` && Value == `dev`]
&& (
!not_null(Tags[?Key == `eks`])
|| Tags[?Key == `eks` && Value == `false`]
)
].[InstanceId]
Ends up being the final expression you need. We added here an or expression that allows us our proviso query to also that do not have an eks tag, with a double negation (we do not want any object that have a non-empty array of eks tags, so we want all the one that do have an empty eks array).
So, your command line ends up being:
aws ec2 describe-instances \
--query 'Reservations[]
.Instances[?
Tags[?Key == `env` && Value == `dev`]
&& (
!not_null(Tags[?Key == `eks`])
|| Tags[?Key == `eks` && Value == `false`]
)
].[InstanceId]' \
--output text
Side note: A further simplification of the above query could be
Reservations[]
.Instances[?
Tags[?Key == `env` && Value == `dev`]
&& (
!not_null(Tags[?Key == `eks`])
|| Tags[?Key == `eks` && !Value]
)
].[InstanceId]
As the Value field should be a boolean already, we can just negate it with a not expression.