AWS CLIのqueryオプション(JMESPath)で雑にgrepする方法

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

西澤です。ブログがずいぶんご無沙汰になってしまったので、リハビリを兼ねてちょっとした小ネタを書きます。みんな大好きAWS CLIで、雑に出力する対象をフィルタリングしたい時に、少し便利な方法を見つけたのでご紹介してみます。

JMESPathとは?

AWS CLIのqueryオプションで利用することができるjsonをクエリする言語のことです。詳しくは、下記リンクをご覧ください。

JMESPathで普通にフィルタする方法

AWS CLIで出力する対象を絞って表示したい場合、よく使われる方法として以下のようなものがあります。

完全一致でフィルタする

この記事を読もうというモチベーションがある方は、これはご存知なのではないかと思います。

  • [?【キー名】==\`【検索文字】\`]
$ aws ec2 describe-regions \
    --query "Regions[?RegionName==\`ap-northeast-1\`]"
[
    {
        "Endpoint": "ec2.ap-northeast-1.amazonaws.com",
        "RegionName": "ap-northeast-1"
    }
]

部分一致でフィルタする

JMESPathのcontains関数を利用することで、部分一致でフィルタすることもできます。

  • [?contains(【キー名】,\`【検索文字】\`)]
$ aws ec2 describe-regions \
    --query "Regions[?contains(RegionName,\`ap-northeast\`)]"
[
    {
        "Endpoint": "ec2.ap-northeast-2.amazonaws.com",
        "RegionName": "ap-northeast-2"
    },
    {
        "Endpoint": "ec2.ap-northeast-1.amazonaws.com",
        "RegionName": "ap-northeast-1"
    }
]

JMESPathで雑にフィルタする

今回の本題はこちら。完全一致はまあ良いとして、部分一致でフィルタをかけたいときに、contains関数だと苦戦してしまうことがあります。contains関数の検索対象は文字列である必要がある為、対象に文字列ではないものが混在していたり、そもそもそのキーが無かったり、リストだったり、といったものが混在するパターンではcontains関数が上手く使えないのです。

もう既に答えを書いたも同然なので結論からお伝えすると、文字列であればcontains関数でフィルタすることが可能なので、to_string関数を用いることで、検索対象を無理矢理文字列に変えてgrepしてしまおうというのが、今回ご紹介したい方法です。

  • [?contains(to_string(【キー名】),\`【検索文字】\`))]

ご理解いただくには、具体例を見ていただくのが良さそうなので、今回の方法を見つける経緯となった例をいくつかそのままご紹介します。

特定のIPアドレスからのアクセス許可のあるSecurityGroup

SecurityGroupのIpPermissions(Inbound許可)には色々な記述が入る為、雑にgrepした方が探したいものを見つけたい場合が多いです。IpPermissionsEgress(Outbound許可)も同じように利用可能です。

$ aws ec2 describe-security-groups \
    --query "SecurityGroups[?contains(to_string(IpPermissions),\`192.0.2.0/24\`)].[VpcId,IpPermissions]"

Principalの中に特定のAWSアカウントIDを含むIAMロール

IAMロールのPrincipalには、Service:Federated:AWS:など様々な表現がある上に、さらにリストが含まれるケースもある為、今回のような方法が有効になると思います。

$ aws iam list-roles \
    --query "Roles[?contains(AssumeRolePolicyDocument.Statement[0].to_string(Principal),\`arn:aws:iam::12345678012\`)].[RoleName,AssumeRolePolicyDocument]"

ここでちょっと注意ですが、contains関数では検索文字も文字列であることが必要となる為、数字のみを検索文字として指定するとエラーとなります。

$ aws iam list-roles \
    --query "Roles[?contains(AssumeRolePolicyDocument.Statement[0].to_string(Principal),\`12345678012\`)].[RoleName,AssumeRolePolicyDocument]"

'in <string>' requires string as left operand, not int

なので、検索文字もto_stringしてあげる必要があることに注意しましょう。

$ aws iam list-roles \
    --query "Roles[?contains(AssumeRolePolicyDocument.Statement[0].to_string(Principal),to_string(\`12345678012\`))].[RoleName,AssumeRolePolicyDocument]"

ぜひ下記記事も合わせてご覧ください。

【小ネタ】AWS CLIのqueryオプション(JMESPath)でstringな数値をfilterしたい

Tagsの中にaws:cloudformation:stack-idを含むEC2インスタンス

to_stringしてしまうので、実は検索対象はValueでなくても大丈夫です。CloudFormationで作成したことを示すタグKeyが存在するかどうかの検索にも使えます(※厳密にはKeyもValueも全部ひっくるめて、その文字列が含まれることを検索しているだけですが)。

$ aws ec2 describe-instances \
    --query "Reservations[].Instances[?contains(to_string(Tags),\`aws:cloudformation:stack-id\`)].[InstanceId,Tags]"

まとめ

今回ご紹介した方法は、jsonを細かく解析せずに緩やかに検索したいときにすぐに使えるので、フィットするケースもあるのではないかと思いましたがいかがでしょうか?試してみなければピンと来ないところもあるかと思いますので、まずは、検索したいキーをto_stringで出力してイメージを掴んでもらえればと思います。

JMESPath好きとしては、城岸さんboto3でqueryオプションっぽいことを実行するも合わせてぜひ読みいただきたいです。

小ネタの割に時間がかかってしまいましたが、サボらずアウトプットしていきたいと思います。どこかの誰かのお役に立てば嬉しいです。