I’m trying to select the SerialNumber
of a specific AWS MFADevice
for different profiles.
This command returns the list of MFADevices for a certain profile:
aws iam list-mfa-devices --profile xxx
and this is a sample JSON output:
{
"MFADevices": [
{
"UserName": "[email protected]",
"SerialNumber": "arn:aws:iam::000000000000:mfa/foo",
"EnableDate": "2022-12-06T16:23:41+00:00"
},
{
"UserName": "[email protected]",
"SerialNumber": "arn:aws:iam::111111111111:mfa/bar_cli",
"EnableDate": "2022-12-12T09:13:10+00:00"
}
]
}
I would like to select the SerialNumber
of the device containing the string cli
. But in case there is only one device in the list (regardless of the presence or absence of the string cli
), I’d like to get its SerialNumber
.
I have this expression which already filters for the first condition, namely the desired string:
aws iam list-mfa-devices --profile xxx --query 'MFADevices[].SerialNumber | [?contains(@,`cli`)] | [0]'
However I still haven’t been able to figure out how to add the if number_of_devices == 1 then return the serial of that single device
.
I can get the number of MFADevices
with this command:
aws iam list-mfa-devices --profile yyy --query 'length(MFADevices)'
And as a first step towards my final solution I wanted to initially get the SerialNumber
only in the case the list has exactly one element, so, I thought of something like this:
aws iam list-mfa-devices --profile yyy --query 'MFADevices[].SerialNumber | [?length(MFADevices) ==`1`]'
but actually already at this stage I get the error below (left alone the fact that I still need to combine it with the cli
part):
In function length(), invalid type for value: None, expected one of: [‘string’, ‘array’, ‘object’], received: "null"
Does anybody know how to achieve what I want?
I know that I could just pipe the raw output to jq
and do the filtering there, but I was wondering if there is a way to do it directly in the command using some JMESPath expression.
2
Answers
With stedolan/jq you can filter for the substring and unconditonally add the first, then take the first of them:
Demo
or
Demo
Output:
In order to do those kind of condition in JMESPath you will have to rely on logical or (
||
) and logical and (&&
), because the language does not have a conditional keyword, per se.So, in pseudo-code, instead of doing:
You have to do, like in bash:
So, in JMESPath:
Note: this assumes that, if there are more than one element but none contains
cli
, we should getnull
.If you want the first element, even when there are multiple devices and the
SerialNumber
does not containscli
, then you can simplify it further and simply do a logicalor
, when the contains filter return nothing (as anull
result will evaluates tofalse
):