Python版AWS CLI 1.3の強化されたqueryオプションを使いこなすスニペット4つ

AWS

ども、大瀧です。
先日、AWSのコマンドラインツールであるAWS CLIバージョン1.3がリリースされましたAWS Data Pipeline対応が目立ちますが、--queryオプションの強化が個人的にはインパクトが大きかったので、なにができるようになったのかと具体的な用例をご紹介します。

バージョン1.3で追加された--queryオプションの機能

では、まずは追加された機能を確認しておきましょう。以下の2点です。

  • フィルタ
  • 組み込み関数

フィルタは従来AWS CLIの--filterオプションやjqと組み合わせて行っていた処理に代わるもの、組み込み関数はjqの関数(Builtin functions)に追随するものです。つまるところ、AWS CLIの出力結果の抽出・加工を--queryオプションに一本化することができるようになりました。

...えーと、地味とか言わないでくださいw
従来、--queryオプションはコマンドの出力項目(テーブル構造で言う「列」)を抽出するのみで、出力結果(テーブル構造で言う「行」)を絞りこむためには--filterオプションを別途指定するか、パイプラインで繋いでjqで処理していました。(参考エントリー)
これを、--queryオプションのみで抽出を完結することができます。手で打つには難解な構文だった--filterオプションよりも直感的に書けますし、--queryオプションのバックエンドであるJMESPathとjqとの微妙な構文の違いに混乱しきりだったので、--queryオプションのみで完結することは非常に大きな意義があると思います!
少なくとも、私はjqコマンドからしばらく手を引こうと心に誓いました!! *1

1. フィルタ(基本 : 単一条件)

では、フィルタから使い方と使用例を見ていきましょう。まずは構文を。

ParentNode[?<値1><演算子><値2>]

フィルタは、ParentNode配下から演算条件を満たすノードのみを抽出します。従来は[]や、[*]というように、全ての子ノードを指定していたところに?から条件を指定するスタイルです。<演算子>には==, !=, <, <=, >, >=が使用できます。<値>はノードもしくはリテラル(`で囲む)が指定でき、文字列、数値、Bool値、Nullで比較できます。詳細は、JMESPathのドキュメントを参照ください。

では、利用例行ってみましょう。お題のコマンドはElastic IP一覧の取得で、最初はフィルタなしです。

ikkomon:~ ryuta$ aws ec2 describe-addresses --output table \
--query 'Addresses[].[PublicIp,InstanceId,PrivateIpAddress]'
--------------------------------------------------
|                DescribeAddresses               |
+----------------+--------------+----------------+
|  54.199.200.X  |  i-XXXXXXXX  |  10.0.72.87    |
|  54.199.233.X  |  None        |  None          |
|  54.199.200.X  |  i-XXXXXXXX  |  10.0.192.100  |
|  54.199.200.X  |  i-XXXXXXXX  |  10.0.2.168    |
|  54.199.152.X  |  i-XXXXXXXX  |  10.0.12.157   |
|  54.199.200.X  |  i-XXXXXXXX  |  10.0.128.100  |
+----------------+--------------+----------------+
ikkomon:~ ryuta$

では、このうちEC2インスタンスを割り当てていない(InstanceIdが`null`である)、未使用のElastic IPのみをフィルタしてみます。

ikkomon:~ ryuta$ aws ec2 describe-addresses --output table \
--query 'Addresses[?InstanceId==`null`].[PublicIp,InstanceId,PrivateIpAddress]'
------------------------------------
|         DescribeAddresses        |
+-----------------+-------+--------+
|  54.199.233.XXX |  None |  None  |
+-----------------+-------+--------+
ikkomon:~ ryuta$

いい感じですね!

2. フィルタ(応用 : 複数条件の絞り込み)

お次は応用編です。複数条件で絞り込みたいときには、[?〜]を続けて記述します。

実はこの指定方法、どこかのドキュメントに載っているわけではなくいろいろ試行錯誤するうちに見つけた記法なので、もしかしたら使えなくなるかもしれません。どこかにオフィシャルの記述を見つけた方がいたら教えてください!

ParentNode[?<値1><演算子><値2>][?<値1><演算子><値2>]

では、例を。今度はRDSの一覧から。

ikkomon:~ ryuta$ aws rds describe-db-instances --output table \
--query 'DBInstances[].[Endpoint.Address,MultiAZ,BackupRetentionPeriod]'
------------------------------------------------------------------------
|                          DescribeDBInstances                         |
+---------------------------------------------------------+-------+----+
|  mysqldb1.XXXXXXXXXXXX.ap-northeast-1.rds.amazonaws.com | False |  0 |
|  mysqldb2.XXXXXXXXXXXX.ap-northeast-1.rds.amazonaws.com | True  | 35 |
|  mysqldb3.XXXXXXXXXXXX.ap-northeast-1.rds.amazonaws.com | True  | 35 |
|  mysqldb4.XXXXXXXXXXXX.ap-northeast-1.rds.amazonaws.com | True  |  0 |
+---------------------------------------------------------+-------+----+
ikkomon:~ ryuta$

ここから、Multi-AZは有効(true)なのに自動バックアップが無効(バックアップ保存件数が1よりも小さい)というDBインスタンスを抽出してみます。

ikkomon:~ ryuta$ aws rds describe-db-instances --output table \
--query 'DBInstances[?MultiAZ==`true`][?BackupRetentionPeriod<`1`].[Endpoint.Address,MultiAZ,BackupRetentionPeriod]'
------------------------------------------------------------------------
|                          DescribeDBInstances                         |
+---------------------------------------------------------+-------+----+
|  mysqldb4.XXXXXXXXXXXX.ap-northeast-1.rds.amazonaws.com |  True |  0 |
+---------------------------------------------------------+-------+----+
ikkomon:~ ryuta$

ちゃんと取れてますね!

3. 組み込み関数 contains関数による文字列一致

いろいろな関数が用意されて目移りしてしまいます。詳細はJMESPathのドキュメントを参照ください。

  • abs
  • avg
  • contains
  • ceil
  • floor
  • join
  • keys
  • length
  • max
  • max_by
  • min
  • min_by
  • not_null
  • sort
  • sort_by
  • to_string
  • to_number
  • type
  • values

ここでは、contains関数と集計関数(length/max)の用例をご紹介します。

contains関数は、文字列一致をtrue/falseで返す関数です。

contains(<文字列>|<配列>, <比較文字列>)

--queryでは、AWS CLIの悲願だったNameタグの検索に活用してみます。まずは、インスタンス一覧を示します。

ikkomon:~ ryuta$ aws ec2 describe-instances --output table \
--query 'Reservations[].Instances[].[InstanceId,Tags[?Key==`Name`].Value]'
---------------------------------
|       DescribeInstances       |
+-------------------------------+
|  i-XXXXXXXX                   |
|  Web1                         |
|  i-YYYYYYYY                   |
|  Web2                         |
|  i-ZZZZZZZZ                   |
|  AP1                          |
+-------------------------------+
ikkomon:~ ryuta$

では、contains関数でNameタグの一致を判定し、それをフィルタの条件に指定します。

ikkomon:~ ryuta$ aws ec2 describe-instances --output table \
--query 'Reservations[].Instances[][?contains(Tags[?Key==`Name`].Value, `AP1`)==`true`].[InstanceId,Tags[?Key==`Name`].Value]'
--------------------
| DescribeInstances|
+------------------+
|  i-ZZZZZZZZ      |
|  AP1             |
+------------------+
ikkomon:~ ryuta$

え、難しすぎ?確かに、試行錯誤の跡が見えるクエリですよねwもっとスマートに書ける方法があれば、ぜひ教えてください!また、JMESPathのマニュアルには部分一致と書かれているのですが、試した限りでは部分一致ではひっかかってくれず完全一致のようです。

ikkomon:~ ryuta$ aws ec2 describe-instances --output table \
--query 'Reservations[].Instances[][?contains(Tags[?Key==`Name`].Value, `AP`)==`true`].[InstanceId,Tags[?Key==`Name`].Value]'
ikkomon:~ ryuta$

4. 組み込み関数 集計関数length/max/avg

集計に使える関数は、SQLの関数と同様の感覚で使えます。--queryオプションの中では、個別のノードに対しての実行と複数行での実行をうまく使い分けましょう。

1つ目の例は、EC2インスタンスのPrivate IPの数をlength関数で数えています。

ikkomon:~ ryuta$ aws ec2 describe-instances --output table \
--query 'Reservations[].Instances.[InstanceId,NetworkInterfaces[].length(PrivateIpAddresses)]'
-------------------
|DescribeInstances|
+-----------------+
|  i-XXXXXXXX     |
|  1              |
|  i-YYYYYYYY     |
|  1              |
|  i-ZZZZZZZZ     |
|  1              |
+-----------------+

続いて、全インスタンスのうち一番Private IPが多い場合の数と平均をmax関数、avg関数で求めてみます。

ikkomon:~ ryuta$ aws ec2 describe-instances --output json \
--query 'max(Reservations[].Instances[].NetworkInterfaces[].length(PrivateIpAddresses))'
1
ikkomon:~ ryuta$ aws ec2 describe-instances --output json \
--query 'avg(Reservations[].Instances[].NetworkInterfaces[].length(PrivateIpAddresses))'
1.0
ikkomon:~ ryuta$

できてますね!

まとめ

というわけで、AWS CLIの--queryオプションの利用法を紹介しました。上手く利用して、AWSのシステム管理にお役立てください!

脚注

  1. こうなると、--filterオプションに果たして未来はあるのかちょっと不安にはなりますが(苦笑)