michimani.net

AWS CLI の --query オプションと jq コマンド

2020-07-20

AWS CLI には、出力結果を制御する --query というオプションがあります。これまで AWS CLI の出力結果を加工する際には jq コマンドを使用していたのですが、今回はそれらを --query オプションで置き換えるべく、色々触ってみようと思います。

目次

概要

今回は AWS CLI の --query について、公式ドキュメントを元にその動作・使い方を確認してみます。また、これまで使っていた jq コマンドで同様の出力結果を得るためにはどうしていたかも合わせて確認してみます。

--query オプション

公式ドキュメントには次のように説明されています。

AWS CLI は、–query オプションによって、組み込みの JSON ベースの出力フィルタリング機能を提供します。–query パラメータは、JMESPath の仕様に準拠している文字列を受け入れます。

JMESPath については次のページを参照してください。

公式ドキュメントでは EBS を取得する ec2 describe-volumes コマンドに対する出力結果について書かれていますが、今回は Lambda に対する lambda list-functions コマンドで色々試してみます。

ひとまず、オプション無しで実行した場合の結果を見てみます。

$ aws lambda list-functions
{
    "Functions": [
        {
            "FunctionName": "SEStoSlack",
            "FunctionArn": "arn:aws:lambda:us-east-1:123456789012:function:SEStoSlack",
            "Runtime": "python3.7",
            "Role": "arn:aws:iam::123456789012:role/read-write-s3-objects",
            "Handler": "lambda_function.lambda_handler",
            "CodeSize": 4042,
            "Description": "",
            "Timeout": 180,
            "MemorySize": 128,
            "LastModified": "2019-11-21T02:34:36.825+0000",
            "CodeSha256": "123456789012FBC+dJ1XxbQZwJgeExxykdDJ0UZ4WxI=",
            "Version": "$LATEST",
            "VpcConfig": {
                "SubnetIds": [],
                "SecurityGroupIds": [],
                "VpcId": ""
            },
            "TracingConfig": {
                "Mode": "PassThrough"
            },
            "RevisionId": "425ad537-1734-4509-88c7-9a7e96d62d5d"
        },
        {
            "FunctionName": "CFBasicAuthenticattion",
            "FunctionArn": "arn:aws:lambda:us-east-1:123456789012:function:CFBasicAuthenticattion",
            "Runtime": "nodejs10.x",
            "Role": "arn:aws:iam::123456789012:role/service-role/lambda_edge_exection",
            "Handler": "index.handler",
            "CodeSize": 561,
            "Description": "",
            "Timeout": 3,
            "MemorySize": 128,
            "LastModified": "2019-10-21T01:03:51.582+0000",
            "CodeSha256": "123456789012JHZPsxKj0uAtf5hKJJeDmuchKo9EPpQ=",
            "Version": "$LATEST",
            "VpcConfig": {
                "SubnetIds": [],
                "SecurityGroupIds": [],
                "VpcId": ""
            },
            "TracingConfig": {
                "Mode": "PassThrough"
            },
            "RevisionId": "61627b30-9c51-4fb4-8637-2eff1e126ab8"
        },
        {
            "FunctionName": "amplify-boyaki-production-UpdateRolesWithIDPFuncti-WDZSC7TXY5G8",
            "FunctionArn": "arn:aws:lambda:us-east-1:123456789012:function:amplify-boyaki-production-UpdateRolesWithIDPFuncti-WDZSC7TXY5G8",
            "Runtime": "nodejs10.x",
            "Role": "arn:aws:iam::123456789012:role/amplify-boyaki-production-185237-authRole-idp",
            "Handler": "index.handler",
            "CodeSize": 2850,
            "Description": "",
            "Timeout": 300,
            "MemorySize": 128,
            "LastModified": "2020-05-29T09:59:03.177+0000",
            "CodeSha256": "123456789012x9TDi44KnZHySIe5hjE8uVRgyJJoPss=",
            "Version": "$LATEST",
            "TracingConfig": {
                "Mode": "PassThrough"
            },
            "RevisionId": "7434a66b-daf0-416a-bef5-e0e8385ba6d4"
        },
        {
            "FunctionName": "CFRedirectIndexDocument",
            "FunctionArn": "arn:aws:lambda:us-east-1:123456789012:function:CFRedirectIndexDocument",
            "Runtime": "nodejs12.x",
            "Role": "arn:aws:iam::123456789012:role/Lambda@Edge_service_role",
            "Handler": "index.handler",
            "CodeSize": 986,
            "Description": "",
            "Timeout": 5,
            "MemorySize": 128,
            "LastModified": "2020-07-14T06:04:22.489+0000",
            "CodeSha256": "123456789012nqnsmKKR0TMbr1IvscKqk7Za7xRXzxo=",
            "Version": "$LATEST",
            "VpcConfig": {
                "SubnetIds": [],
                "SecurityGroupIds": [],
                "VpcId": ""
            },
            "TracingConfig": {
                "Mode": "PassThrough"
            },
            "RevisionId": "f013317f-9a05-4d69-83da-26aea503770b"
        },
        {
            "FunctionName": "amplify-boyaki-production-185-UserPoolClientLambda-4AHDAEXH1A3K",
            "FunctionArn": "arn:aws:lambda:us-east-1:123456789012:function:amplify-boyaki-production-185-UserPoolClientLambda-4AHDAEXH1A3K",
            "Runtime": "nodejs10.x",
            "Role": "arn:aws:iam::123456789012:role/upClientLambdaRole185237-production",
            "Handler": "index.handler",
            "CodeSize": 2451,
            "Description": "",
            "Timeout": 300,
            "MemorySize": 128,
            "LastModified": "2020-05-29T09:58:01.018+0000",
            "CodeSha256": "123456789012cywCXQVnJQOSVla0JU3I/FIpcZnDVLQ=",
            "Version": "$LATEST",
            "TracingConfig": {
                "Mode": "PassThrough"
            },
            "RevisionId": "e28bf64c-c58c-483a-b600-8b6424357df0"
        },
        {
            "FunctionName": "SNStoSlack",
            "FunctionArn": "arn:aws:lambda:us-east-1:123456789012:function:SNStoSlack",
            "Runtime": "python3.7",
            "Role": "arn:aws:iam::123456789012:role/service-role/toSlack",
            "Handler": "lambda_function.lambda_handler",
            "CodeSize": 889,
            "Description": "",
            "Timeout": 63,
            "MemorySize": 128,
            "LastModified": "2019-07-25T00:44:09.944+0000",
            "CodeSha256": "123456789012g0hoF+8LqRIimBXOmzkWVZk/VHLvI78=",
            "Version": "$LATEST",
            "VpcConfig": {
                "SubnetIds": [],
                "SecurityGroupIds": [],
                "VpcId": ""
            },
            "Environment": {
                "Variables": {
                    "HookUrl": "https://hooks.slack.com/services/XXXXXXXXX/XXXXXXXXX/abAcaaBAcacbbBBabCCCCAcC",
                    "slackChannel": "dev"
                }
            },
            "TracingConfig": {
                "Mode": "PassThrough"
            },
            "RevisionId": "3b562f87-05cc-48ef-8879-ff9e4cf67b9b"
        }
    ]
}

6 個の関数が出力されました。

では、ここからは --query オプションで出力結果を制御してみます。

要素の件数を出力する

まずは、出力結果の要素の件数を出力してみます。今回であれば Lambda 関数の件数を出力します。

$ aws lambda list-functions \
--query "length(Functions)"
6

この出力を jq コマンドで得るためには次のようにします。

$ aws lambda list-functions \
| jq ".Functions | length"
6

jq コマンドではパイプ | を使用して加工していくため、 --query オプションとは書き方が異なってきます。

特定の値だけを出力する

続いて、各 Lambda 関数の情報のうち、 関数名ラインタイム だけを出力してみます。

$ aws lambda list-functions \
--query "Functions[].{FnName:FunctionName,FnRuntime:Runtime}"
[
  {
    "FnName": "SEStoSlack",
    "FnRuntime": "python3.7"
  },
  {
    "FnName": "CFBasicAuthenticattion",
    "FnRuntime": "nodejs10.x"
  },
  {
    "FnName": "amplify-boyaki-production-UpdateRolesWithIDPFuncti-WDZSC7TXY5G8",
    "FnRuntime": "nodejs10.x"
  },
  {
    "FnName": "CFRedirectIndexDocument",
    "FnRuntime": "nodejs12.x"
  },
  {
    "FnName": "amplify-boyaki-production-185-UserPoolClientLambda-4AHDAEXH1A3K",
    "FnRuntime": "nodejs10.x"
  },
  {
    "FnName": "SNStoSlack",
    "FnRuntime": "python3.7"
  }
]

この出力結果を jq コマンドで得る場合は次のようにします。

$ aws lambda list-functions \
| jq "[.Functions[] | {FnName:.FunctionName,FnRuntime:.Runtime}]"

ここでも jq コマンド内でパイプ | を使う必要があり、やや複雑です。

出力結果をソートする

次に、出力結果を特定の属性の値でソートしてみます。今回はランタイムの値でソートします。

$ aws lambda list-functions \
--query "sort_by(Functions, &Runtime)[].{FnName:FunctionName,FnRuntime:Runtime}"
[
    {
        "FnName": "CFBasicAuthenticattion",
        "FnRuntime": "nodejs10.x"
    },
    {
        "FnName": "amplify-boyaki-production-UpdateRolesWithIDPFuncti-WDZSC7TXY5G8",
        "FnRuntime": "nodejs10.x"
    },
    {
        "FnName": "amplify-boyaki-production-185-UserPoolClientLambda-4AHDAEXH1A3K",
        "FnRuntime": "nodejs10.x"
    },
    {
        "FnName": "CFRedirectIndexDocument",
        "FnRuntime": "nodejs12.x"
    },
    {
        "FnName": "SEStoSlack",
        "FnRuntime": "python3.7"
    },
    {
        "FnName": "SNStoSlack",
        "FnRuntime": "python3.7"
    }
]

降順にする場合は次のようにします。

$ aws lambda list-functions \
--query "reverse(sort_by(Functions, &Runtime))[].{FnName:FunctionName,FnRuntime:Runtime}"
[
    {
        "FnName": "SNStoSlack",
        "FnRuntime": "python3.7"
    },
    {
        "FnName": "SEStoSlack",
        "FnRuntime": "python3.7"
    },
    {
        "FnName": "CFRedirectIndexDocument",
        "FnRuntime": "nodejs12.x"
    },
    {
        "FnName": "amplify-boyaki-production-185-UserPoolClientLambda-4AHDAEXH1A3K",
        "FnRuntime": "nodejs10.x"
    },
    {
        "FnName": "amplify-boyaki-production-UpdateRolesWithIDPFuncti-WDZSC7TXY5G8",
        "FnRuntime": "nodejs10.x"
    },
    {
        "FnName": "CFBasicAuthenticattion",
        "FnRuntime": "nodejs10.x"
    }
]

ソートを jq コマンドで実現する場合は、次のようにします。

$ aws lambda list-functions \
| jq "[(.Functions | sort_by(.Runtime))[] | {FnName:.FunctionName,FnRuntime:.Runtime}]"

降順にする場合は次のようにします。

$ aws lambda list-functions \
| jq "[(.Functions | sort_by(.Runtime) | reverse)[] | {FnName:.FunctionName,FnRuntime:.Runtime}]"

こちらも --query を使用したほうがシンプルで直感的に書ける気がします。

まとめ

AWS CLI には、出力結果を制御する --query というオプションについて、 jq コマンドで同じ出力を得る場合を比較してみました。

jq コマンドに慣れている場合はついついそのまま使いがちですが、環境によっては別途インストールする必要があります。その点、 --query オプションについては AWS CLI のオプションなので、 CLI を利用する環境では必ず利用できます。 CLI を用いた操作を手順書として残しておく場合、余計なツールのインストールをする必要がなく、 AWS から提供されているものという面からも、 --query オプションを使ったほうが良さそうです。……という話を JAWS-UG CLI 専門支部で話されており、それ以降は jq の代わりに積極的に --query を使うようになっています。

AWS を使うにあたって、マネジメントコンソールから操作をすることが多いと思いますが、 AWS を触り始めた初期の段階から AWS CLI での操作に慣れておくのは凄く良いことだなと思います。私自身も、もっと初期の頃から CLI での操作をしていればよかったと思います。これについてはクラスメソッドさんのテックイベント (DevelopersIO 2020 CONNECT) でもセッションが公開されていました。

動画内では jq コマンドを使用されていますが、 AWS CLI いいぞ! という思いは伝わってくると思います。

AWS では、マネジメントコンソールの UI は頻繁に変わってしまいますが、中で使われている API は変わりません。なので、 AWS CLI を使いこなすことは AWS を理解する近道にもなるのではないかと思っています。


comments powered by Disqus