AWS CLI で describe した JSON の diff でイライラしないために! dictknife normalize を使おう

TL;DR

  • dictknife diff --normalize で しあわせになりましょう!
  • pip でインストールできます!

背景

みなさん、AWS CLI 使ってますか!(挨拶

例えば、既存のある EC2 インスタンスから AMI を取得して別の EC2 インスタンスを作成したりする際など、ふたつの EC2 インスタンス(あるいは他の AWS リソース同士)の差を比較したいという場面は時々あります。

そういうときには AWS CLI で状態を取得、たとえば EC2 の場合であれば describe-instances して、得られた JSON を比較すればいいのですが、、、実際にやってみると、たま(?)に困ったことが起きます。

例えばこんな感じです:

diff -ub i-0000aaaaaaaaaa000.json i-1111bbbbbbbbbb111.json
--- i-0000aaaaaaaaaa000.json 2019-03-xx 14:51:38.000000000 +0900
+++ i-1111bbbbbbbbbb111.json 2019-03-xx 15:03:45.000000000 +0900
(略)
     "Tags": [
         {
-            "Key": "Name",
-            "Value": "server-1"
+            "Key": "Role",
+            "Value": "webapp"
         },
         {
             "Key": "Env",
             "Value": "Prod"
         },
         {
-            "Key": "Role",
-            "Value": "webapp"
+            "Key": "Name",
+            "Value": "server-2"
         },
         {
             "Key": "BillingGroup",
(略)

この!

差分が 1 箇所なのか 2 箇所なのかびみょうに分かりづらい感じ、伝わりますでしょうか!!

こちらとしては InstanceId とか PrivateIpAddress とか、あるいは LaunchTime などの出て当然の差分、さらにはパラメータ指定ミスなどの 出て欲しくないのに出てしまった差分が無いか が見たい(むしろ本命はこちら)のであって、こういった「実際は差が無いのに差分として表示されるもの」はノイズでしかない、出て欲しくないわけです。

ところが AWS CLI にて describe した結果の JSON では、このような差分が往々にして出力されてしまいます。 Tags の他にも SecurityGroups などもそうですね。。 jq--sort-keys (-S) オプションはキーに対してしか効果がなく、配列まではソートしてくれないためここでは使えません。

では他に何かないか。。。と探して、 id:podhmo 氏が公開されている dictknife にたどり着いたためご紹介します。

dictknife のマニュアルは下記で公開されていますが、すこし前のバージョンしかないようです。

インストール後は --help でオプションを確認できます。

$ dictknife diff --help
usage: dictknife diff [-h] [--normalize] [--verbose] [--n N] [--skip-empty]
                      [-i {yaml,json,toml,csv,tsv,raw,env,md,markdown,spreadsheet}]
                      [-o {diff,dict,md,tsv,jsonpatch}] [-S]
                      left right

diff dict

positional arguments:
  left
  right

optional arguments:
  -h, --help            show this help message and exit
  --normalize
  --verbose
  --n N
  --skip-empty
  -i {yaml,json,toml,csv,tsv,raw,env,md,markdown,spreadsheet}, --input-format {yaml,json,toml,csv,tsv,raw,env,md,markdown,spreadsheet}
  -o {diff,dict,md,tsv,jsonpatch}, --output-format {diff,dict,md,tsv,jsonpatch}
  -S, --sort-keys

使い方

インストール

pip で導入できます。

pip install dictknife

差分比較

diff と使い方はあまりかわりません。 --normalize オプションを付けて実行するだけです。出力は diff (unified) 形式( diff -u )固定になります。

dictknife diff --normalize i-0000aaaaaaaaaa000.json i-1111bbbbbbbbbb111.json

前後の行数を広げたい・狭めたいときには --n NN オプションをつけます。 -n ではなく -ふたつですのでお気をつけください。

dictknife diff --normalize --n 10 i-0000aaaaaaaaaa000.json i-1111bbbbbbbbbb111.json

例えば先ほどのサンプル部分は、下記のような感じになります:

(略)
         {
           "Key": "BillingGroup",
           "Value": "Team1"
         },
         {
           "Key": "Env",
           "Value": "Prod"
         },
         {
           "Key": "Name",
-          "Value": "server-1"
+          "Value": "server-2"
         },
         {
           "Key": "Role",
           "Value": "webapp"
         }
       ],
(略)

Key 部分がいい感じにソートされてますね!

なお、dictknife も jq と同じく --sort-keys (-S) オプションが使えます。 AWS CLI の出力はキー順が概ね一定でそんなに困ったことはないですが、もし必要なら指定しましょう。

YAML入力

dictknife は YAML 形式もそのまま入力できるため、YAMLの比較も可能です。

dictknife diff --normalize i-0000aaaaaaaaaa000.yml i-1111bbbbbbbbbb111.yml

ただしその場合でも、内部では JSON に変換して比較が行われるのか、JSON での差分出力になります。

ちなみに

dictknife の出力はシンプルなので、colordiffhighlight でいい感じに色づけできます。個人的には colordiff が単機能で好みですが、highlight は高機能で HTML 出力まで出来てしまうので、必要に応じて使い分けるとよいかと思います。

brew install colordiff
brew install highlight
dictknife diff --normalize a.json b.json | colordiff
dictknife diff --normalize a.json b.json | highlight -S diff -O ansi

colordiff の出力としてはこんな感じです:

なお「 armyknife of handling dict object 」と謳われているとおり、dictknife には他にも便利そうな機能が備わっています。正直なところ自分は使い込むところまでいっていないため、まだその便利さに気付けていませんが、もし JSON を扱う上で困ったことがおありなら、ドキュメントやヘルプを参照してみてください。

まとめ

dictknife をご紹介しました。AWS CLI を使う上で長年面倒に思っていた仕様なのですが、解決策は探せばあるものなのですね。。。

便利なツールを公開してくださっている podhmo 氏に感謝しつつ、みなさんも是非使ってみて下さい。