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

例えばふたつの EC 2インスタンスの describe-instances した結果を比較しようとして、SG や Tag の順番がそろってなくてイライラした経験はありませんか? 僕はあります。でも、そんなイライラから解放される方法があったのでご紹介します。
2019.03.25

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

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 氏に感謝しつつ、みなさんも是非使ってみて下さい。