AWS CLI出力結果をJMESPathでできるだけ見やすくする

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

西澤です。みんな大好きAWS CLIでは、--queryオプション(JMESPath)を利用して、様々な出力制御を行うことができます。どこまでAWS CLIでやるべきか、という議論は一旦置いておいて、今回試してみたことの一部を簡単にご紹介したいと思います。

JMESPath関連の記事も充実して来ているので、ぜひ他の記事も合わせてご覧ください。

AWS CLIの出力結果を見やすくする(基本編)

そのままjsonで出力

まずはそのままjsonで出力します。これを順番に整形していきます。

$ aws ec2 describe-availability-zones \
  --region us-west-2
{
    "AvailabilityZones": [
        {
            "State": "available",
            "RegionName": "us-west-2",
            "Messages": [],
            "ZoneName": "us-west-2a"
        },
        {
            "State": "available",
            "RegionName": "us-west-2",
            "Messages": [],
            "ZoneName": "us-west-2b"
        },
        {
            "State": "available",
            "RegionName": "us-west-2",
            "Messages": [],
            "ZoneName": "us-west-2c"
        }
    ]
}

任意のKeyのみを取り出して表示

この辺りは既にご存知の方が多いと思いますが、一部のKeyのValueのみを取り出して表示することができます。

$ aws ec2 describe-availability-zones \
  --region us-west-2 \
  --query "AvailabilityZones[].[State,ZoneName]"
[
    [
        "available",
        "us-west-2a"
    ],
    [
        "available",
        "us-west-2b"
    ],
    [
        "available",
        "us-west-2c"
    ]
]

Valueのみの配列では都合が悪い場合は、MultiSelect Hashで連想配列にすることができます。

$ aws ec2 describe-availability-zones \
  --region us-west-2 \
  --query "AvailabilityZones[].{state:State,name:ZoneName}"
[
    {
        "state": "available",
        "name": "us-west-2a"
    },
    {
        "state": "available",
        "name": "us-west-2b"
    },
    {
        "state": "available",
        "name": "us-west-2c"
    }
]

AWS CLIの出力結果を見やすくする(中級編)

そのままjsonで出力

まずはそのままjsonで出力します。これを順番に整形していきます。

$ aws ec2 describe-regions
{
    "Regions": [
        {
            "Endpoint": "ec2.ap-south-1.amazonaws.com",
            "RegionName": "ap-south-1"
        },
        {
            "Endpoint": "ec2.eu-west-1.amazonaws.com",
            "RegionName": "eu-west-1"
        },
        {
            "Endpoint": "ec2.ap-southeast-1.amazonaws.com",
            "RegionName": "ap-southeast-1"
        },
        {
            "Endpoint": "ec2.ap-southeast-2.amazonaws.com",
            "RegionName": "ap-southeast-2"
        },
        {
            "Endpoint": "ec2.eu-central-1.amazonaws.com",
            "RegionName": "eu-central-1"
        },
        {
            "Endpoint": "ec2.ap-northeast-2.amazonaws.com",
            "RegionName": "ap-northeast-2"
        },
        {
            "Endpoint": "ec2.ap-northeast-1.amazonaws.com",
            "RegionName": "ap-northeast-1"
        },
        {
            "Endpoint": "ec2.us-east-1.amazonaws.com",
            "RegionName": "us-east-1"
        },
        {
            "Endpoint": "ec2.sa-east-1.amazonaws.com",
            "RegionName": "sa-east-1"
        },
        {
            "Endpoint": "ec2.us-west-1.amazonaws.com",
            "RegionName": "us-west-1"
        },
        {
            "Endpoint": "ec2.us-west-2.amazonaws.com",
            "RegionName": "us-west-2"
        }
    ]
}

出力結果をソートする

sort_by関数を使って結果をソートしたい場合があります。

$ aws ec2 describe-regions \
  --query "sort_by(Regions,&RegionName)[].RegionName"
[
    "ap-northeast-1",
    "ap-northeast-2",
    "ap-south-1",
    "ap-southeast-1",
    "ap-southeast-2",
    "eu-central-1",
    "eu-west-1",
    "sa-east-1",
    "us-east-1",
    "us-west-1",
    "us-west-2"
]

単一のValueのみなら、sort関数も使えます。

$ aws ec2 describe-regions --query "sort(Regions[].RegionName)"
[
    "ap-northeast-1",
    "ap-northeast-2",
    "ap-south-1",
    "ap-southeast-1",
    "ap-southeast-2",
    "eu-central-1",
    "eu-west-1",
    "sa-east-1",
    "us-east-1",
    "us-west-1",
    "us-west-2"
]

MultiSelect Hashで出力した結果をソートすることも可能です。特に出力したいValueの階層が深い場合等ではこの方法が有効です。

$ aws ec2 describe-regions \
  --query "sort_by(Regions[].{A:RegionName,B:Endpoint},&A)[].A"
[
    "ap-northeast-1",
    "ap-northeast-2",
    "ap-south-1",
    "ap-southeast-1",
    "ap-southeast-2",
    "eu-central-1",
    "eu-west-1",
    "sa-east-1",
    "us-east-1",
    "us-west-1",
    "us-west-2"
]

パイプで渡した連想配列をソートすることも可能です。前から順番にJMESPathを作って行く場合が多いので、この方が読みやすく、書きやすい場合が多いかもしれません。

$ aws ec2 describe-regions \
  --query "Regions[].{A:RegionName,B:Endpoint}|sort_by(@,&A)[].A"
[
    "ap-northeast-1",
    "ap-northeast-2",
    "ap-south-1",
    "ap-southeast-1",
    "ap-southeast-2",
    "eu-central-1",
    "eu-west-1",
    "sa-east-1",
    "us-east-1",
    "us-west-1",
    "us-west-2"
]

出力結果を逆順ソートする

reverseを使えば、出力結果をそっくり逆順に並べ替えることもできます。

$ aws ec2 describe-regions \
  --query "reverse(sort_by(Regions,&RegionName)[].RegionName)"
[
    "us-west-2",
    "us-west-1",
    "us-east-1",
    "sa-east-1",
    "eu-west-1",
    "eu-central-1",
    "ap-southeast-2",
    "ap-southeast-1",
    "ap-south-1",
    "ap-northeast-2",
    "ap-northeast-1"
]

AWS CLIの出力結果を見やすくする(上級編)

任意の文字列を出力結果に含める

エスケープ処理が面倒なのですが、バックスラッシュ("\")を使えば任意の文字列を出力結果に含めることができます。

$  aws ec2 describe-availability-zones \
  --region us-west-2 \
  --query "AvailabilityZones[].[RegionName,\`で利用できるAZは\`,ZoneName,\`です\`]" \
  --output text
us-west-2   で利用できるAZは   us-west-2a  です
us-west-2   で利用できるAZは   us-west-2b  です
us-west-2   で利用できるAZは   us-west-2c  です

これをjoin関数を利用することで、こんな形にしてみるのはどうでしょうか?区切り文字を空文字にして、joinしたい文字列を配列で並べればOKです。

$  aws ec2 describe-availability-zones \
  --region us-west-2 \
  --query "AvailabilityZones[].[join(\`\`,[RegionName,\`で利用できるAZは\`,ZoneName,\`です\`])]" \
  --output text
us-west-2で利用できるAZはus-west-2aです
us-west-2で利用できるAZはus-west-2bです
us-west-2で利用できるAZはus-west-2cです

AWSアカウントIDをこんな形でそのまま環境変数に入れられるようにしてみるのも良いかもしれません。応用例は色々と考えられそうです。

$ aws sts get-caller-identity \
  --query "join(\`\`,[\`export AWSID=\`,Account])" \
  --output text
export AWSID=123456789012

CSV出力する

このjoin関数では区切り文字と配列を指定できますので、CSV出力することもできます。下記例ではRegioNameでソートした結果を、CSVで出力しています。

$ aws ec2 describe-regions \
  --query "Regions[].{A:RegionName,B:Endpoint}|sort_by(@,&A)[].[join(\`,\`,[A,B])]" \
  --output text
ap-northeast-1,ec2.ap-northeast-1.amazonaws.com
ap-northeast-2,ec2.ap-northeast-2.amazonaws.com
ap-south-1,ec2.ap-south-1.amazonaws.com
ap-southeast-1,ec2.ap-southeast-1.amazonaws.com
ap-southeast-2,ec2.ap-southeast-2.amazonaws.com
eu-central-1,ec2.eu-central-1.amazonaws.com
eu-west-1,ec2.eu-west-1.amazonaws.com
sa-east-1,ec2.sa-east-1.amazonaws.com
us-east-1,ec2.us-east-1.amazonaws.com
us-west-1,ec2.us-west-1.amazonaws.com
us-west-2,ec2.us-west-2.amazonaws.com

jqの方が簡単というご指摘はもっともです。ちょっと無理が出てきている気もします。

表形式で出力する

joinする配列の先頭と最後に空文字をつけてパイプを区切り文字にすれば、こんな出力もできました。

$ aws ec2 describe-regions \
  --query "Regions[].{A:RegionName,B:Endpoint}|sort_by(@,&A)[].[join(\`|\`,[\`\`,A,B,\`\`])]" \
  --output text
|ap-northeast-1|ec2.ap-northeast-1.amazonaws.com|
|ap-northeast-2|ec2.ap-northeast-2.amazonaws.com|
|ap-south-1|ec2.ap-south-1.amazonaws.com|
|ap-southeast-1|ec2.ap-southeast-1.amazonaws.com|
|ap-southeast-2|ec2.ap-southeast-2.amazonaws.com|
|eu-central-1|ec2.eu-central-1.amazonaws.com|
|eu-west-1|ec2.eu-west-1.amazonaws.com|
|sa-east-1|ec2.sa-east-1.amazonaws.com|
|us-east-1|ec2.us-east-1.amazonaws.com|
|us-west-1|ec2.us-west-1.amazonaws.com|
|us-west-2|ec2.us-west-2.amazonaws.com|

まとめ

AWS CLIだけでどこまで出力結果を整形できるか、色々と試してみました。正直ちょっと無理している感も否めませんが、何度も実行して結果を残したいような情報収集の為のスクリプトでは活用できる場面もあるのではないでしょうか?今回は説明をわかりやすくする為に、シンプルなjsonの結果を対象にしていますが、jsonが複雑で階層が深くなっていくと、苦戦が強いられます。その辺りのTipsも時間があるときに改めてまとめたいと思っています。

どこかの誰かのお役に立てば嬉しいです。