jqやpecoと組み合わせてaws-cliを便利に使う

## EC2 InstanceのPrivateIpAddressと名前の一覧

/etc/hosts風に出力:

```bash
$ aws ec2 describe-instances | jq . > instances.json
$ cat instances.json | jq -r '.Reservations[] | .Instances[] | [.PrivateIpAddress,.InstanceId,"# "+(.Tags[] | select(.Key == "Name") | .Value // "")] | join("\t")'
172.31.16.30	i-0610880b	# test
172.31.48.10	i-94c9199b	# nat
```

runningだけをpecoで選んでsshする:

```bash
$ eval `cat instances.json | jq -r '.Reservations[] | .Instances[] | select(.State.Name == "running") | ["ssh",.PrivateIpAddress,"# "+(.Tags[] | select(.Key == "Name") | .Value // "")] | join("\t")' | peco`
```

## CloudFormationでローカルのテンプレートファイルでスタック作成・アップデート

テンプレートに空白とかが入ってても""で括るだけで渡せるのか〜

```bash
$ aws cloudformation update-stack --stack-name iam-role-test --template-body "`cat iam-role.json`" --parameters ParameterKey=AllowAction,ParameterValue=s3:List* --capabilities CAPABILITY_IAM
```

## SQSのメッセージのBodyをdecodeし、さらにその中のSNSのMessageをdecodeする

例えばAutoScalingのNotificationをSNS経由でSQSに飛ばした場合、AutoScalingからの通知のJSONが、文字列にエンコードされてSNSのMessageに入り、そのSNSからの通知のJSONが、SQSのBodyに文字列にエンコードされて入っています。

fromjsonという関数で、JSON文字列をJSONに変換できるんですね！

```bash
aws sqs receive-message --queue-url https://sqs.us-west-2.amazonaws.com/123456789012/sys-status > message.json
cat message.json | jq '.Messages[0].Body | fromjson | .Message | fromjson'
```

## 複数のjsonファイルをディープマージしたい

```bash
cat *.json | jq -s 'reduce .[] as $obj ({}; . * $obj)'
```

## jqの出力結果をRubyで使いたい

jqで[]とかを使って結果が複数になる場合、Rubyの標準のJSONモジュールでは読み込めません。(バラさずに配列のまま、mapを使って処理を連結することもできますが、面倒です)

yajl-rubyというgemで、複数のJSONが連なったストリームを読み込んで処理することができます。(Yet Another JSON LibraryというC実装のRubyバインディングのようです)

```ruby:Gemfile
gem 'yajl-ruby', require: 'yajl'
```

例えば、aws ec2 describe-instancesを、jqでinstance単位にバラした結果を、YAMLで見たい場合:

```bash
aws ec2 describe-instances | jq -S '.Reservations[].Instances[]' | bundle exec ruby -ryajl -ryaml -e 'Yajl::Parser.parse(STDIN){|h| puts YAML.dump h}'
```

## 古いAMIのうちAutoScalingで使っていないものだけ削除

Packerとかで毎週AMIをビルドしてると、古いAMIをまとめて消したくなりますが、
うっかりAutoScalingGroupで使ってるAMIを消してしまうと、
次にインスタンスが起動する際、起動に失敗し続けて、大惨事になります。

そこで、2週間より古いAMIから、現在AutoScalingGroupで参照されているものを除いた
ImageIdの一覧を表示します。

(ビルドするAMIのTagに、"timestamp"というキーで、タイムスタンプ(Epoch秒)
が入ってるものとします。)

```bash:gc_image
#!/bin/bash

aws autoscaling describe-launch-configurations | jq . > tmp/launch-configurations.json

cat tmp/launch-configurations.json | jq -r '.LaunchConfigurations[] | .ImageId' | sort | uniq > tmp/used.txt

aws ec2 describe-images --owners self | jq . > tmp/images.json

# 2 weeks ago
time=`date -v-14d +%s`

echo "Old AMIs:"
cat tmp/images.json | jq -r --arg time $time '.Images[] | [.ImageId, ((.Tags // empty) | map(select(.Key=="timestamp"))[0] | .Value), .Name] | select(. | length > 2) | select((.[1] | tonumber) < ($time | tonumber)) | join(" ")' | sort | tee old.txt

echo "Used AMIs:"
cat tmp/images.json | jq -r --arg ids "`cat tmp/used.txt`" '.Images[] | [.ImageId, ((.Tags // empty) | map(select(.Key=="timestamp"))[0] | .Value), .Name] | select(.[0] as $id | ($ids | split("\n")) | contains([$id])) | join(" ")' | sort | tee used.txt

echo "Old unused AMIs:"
diff old.txt used.txt | awk '/^</{print $2, $3, $4}'
```

## 参考

- http://dev.classmethod.jp/tool/jq-manual-japanese-translation-roughly/
- `man jq`
- `aws help`