読者です 読者をやめる 読者になる 読者になる

双六工場日誌

平凡な日常を淡々と綴ります。

AWS CLIとjqを使って、AWSのELBボリュームがアタッチされているEC2インスタンス名を出力するワンライナーを書いた

件名の通りのAWSのELBボリュームがアタッチされているEC2インスタンス名を、AWS CLIで取得しようと思ったら、ちょっと手間取ったので、その結果をワンライナーにしたメモ。

結果はこちらです。こういうワンライナーになったのは、"join"コマンドを使ってみたかったという雑念も半分ぐらいありますね。

join <(aws ec2 describe-volumes | jq -r '.Volumes[].Attachments[] | .InstanceId + " " + .VolumeId' | sort) <(aws ec2 describe-instances | jq -r '.Reservations[].Instances[] | .InstanceId + " " + ( .Tags[] | select(.Key == "Name")|.Value )' | sort) 

"join"コマンドを使わない別解はこちら。上は、ボリュームの情報から撮り始めてしまっていましたが、EC2の構成情報を見たら、そちらに必要な情報がすべて入っていたので、上のようにトリッキーなことをしなくても大丈夫でした。

aws ec2 describe-instances | jq -r '.Reservations[].Instances[] | .InstanceId + " " + .BlockDeviceMappings[].Ebs.VolumeId + " " + ( .Tags[] | select(.Key == "Name")|.Value )'

実行の際には、AWS CLIとjqが必要なので、事前にインストールして、AWS CLIからAWSの適切なリージョンにアクセスできるようにセットアップまで済ませておいてください。AWS CLI自体のセットアップ方法は、いろいろなブログに書いてあると思うので、ここでは割愛します。

結論はここまで。以下は、詳細説明です。

1つ目のワンライナーの内容

1つ目のワンライナーでは、情報取得には以下の2つのコマンドを使っています。

  • aws ec2 describe-volumes
  • aws ec2 describe-instances

"aws ec2 describe-volumes"では、EBSのボリューム一覧を取得することができます。

取得結果は以下のようなJSON

{
    "Volumes": [
        {
            "AvailabilityZone": "ap-northeast-1c",
            "Attachments": [
                {
                    "AttachTime": "2014-05-31T10:57:57.000Z",
                    "InstanceId": "i-ec56a9f5",
                    "VolumeId": "vol-07d00601",
                    "State": "attached",
                    "DeleteOnTermination": true,
                    "Device": "/dev/sda1"
                }
            ],
            "Encrypted": false,
            "VolumeType": "standard",
            "VolumeId": "vol-07d00601",
            "State": "in-use",
            "SnapshotId": "snap-7351cb92",
            "CreateTime": "2014-05-31T10:57:57.834Z",
            "Size": 30
       }
    ]
}

インスタンスにAttachされている時は、そのインスタンスの情報が出てきますが、取れるインスタンス情報はインスタンスIDだけなので続いて、"aws ec2 describe-instances"でインスタンスの情報を取得します。

こちらの取得結果は以下のようなもの。 長いので、ここでのポイント以外は省略します。

{
    "Reservations": [
        {
            "OwnerId": "021275017910",
            "ReservationId": "r-ac0116aa",
            "Groups": [],
            "Instances": [
                {
                   (中略)
                    "VpcId": "vpc-00aca562",
                    "StateTransitionReason": "User initiated (2014-06-30 16:59:55 GMT)",
                    "InstanceId": "i-ec56a9f5",
                    "ImageId": "ami-13e29a12",
                   (中略)
                    "BlockDeviceMappings": [
                        {
                            "DeviceName": "/dev/sda1",
                            "Ebs": {
                                "Status": "attached",
                                "DeleteOnTermination": true,
                                "VolumeId": "vol-07d00601",
                                "AttachTime": "2014-05-31T10:57:57.000Z"
                            }
                        },
                        {
                            "DeviceName": "/dev/sdf",
                            "Ebs": {
                                "Status": "attached",
                                "DeleteOnTermination": false,
                                "VolumeId": "vol-8f550991",
                                "AttachTime": "2015-01-21T15:23:46.000Z"
                            }
                        }
                    ],
                    (中略)
                    "RootDeviceName": "/dev/sda1",
                    "VirtualizationType": "paravirtual",
                    "RootDeviceType": "ebs",
                    "Tags": [
                        {
                            "Value": "servername",
                            "Key": "Name"
                        }
                    ],
                    "AmiLaunchIndex": 0
                }
            ]
        }
    ]
}

これらにそれぞれjqでフィルタを掛けて取り出します。

jq -r '.Volumes[].Attachments[] | .InstanceId + " " + .VolumeId'
jq -r '.Reservations[].Instances[] | .InstanceId + " " + (.Tags[] | select( .Key == "Name")|.Value )'

それぞれの結果は以下のようなテキストです。

i-ec56a9f5 vol-07d00601
i-ec56a9f5 vol-8f550991
i-ec56a9f5 servername

それぞれ結果が得られたところで、これをマージします。マージには"join"コマンドを使います。このコマンドは、SQLのJOINのように同じキーがあるレコードをマージして一つのレコードにしてくれるコマンドです。

前提として、それぞれのファイルの結合キーがソートされている必要があるので、"sort"コマンドでソートします。結合キーはオプションで指定できますが、デフォルトでは1列目になります。

また、各コマンドの出力結果を書き出さずに、直接joinコマンドに食わせるために、bashのプロセス置換を使っています。プロセス置換については、以前に記事にしているので、そちらを見てみてください。

これをまとめたのが1つ目のワンライナーです。出力結果は以下の通り。

i-ec56a9f5 vol-07d00601 servername
i-ec56a9f5 vol-8f550991 servername

2つ目のワンライナーの内容

2つ目のワンライナーは、改めて他に方法がないか考えた際、インスタンス構成情報の中にボリュームIDが入っていたので、そっちだけで出力したものです。

インスタンス構成情報は、1つ目と同じなので割愛するとして、こちらのポイントはjqのフィルタですね。

jq -r '.Reservations[].Instances[] | .InstanceId + " " + .BlockDeviceMappings[].Ebs.VolumeId + " " + (.Tags[] | select( .Key == "Name")|.Value )'

出力の際のフィルタに".BlockDeviceMappings[].Ebs.VolumeId"を入れていることで、複数のボリュームがある場合は複数行で出るようになっています。リストの扱い、selectでのフィルタ、出力項目の絞込など、jq独特のクエリなので、毎回トライアンドエラーになるんですよね。。。

なので、1つ目の方がjqのフィルタがまだ素直な分、結果にたどり着くのは早いような気がします。jqのフィルタに慣れている人ならそうでもないかもしれませんが。

かくいう僕も、jqを使うたびに自分のブログをググって、書き方を確認しています。このブログも半分以上はその目的w

俺得記事ですが、ググってもなかなか見つけにくい情報だと思うので、誰かのお役に立てば。

今日はこの辺で。

追記

"--query"についてコメントいただいたので、そちらでトライした結果も貼っておきます。この指定だと各項目ごとに改行が入ってしまうので、目的は達成できず。"--output table"と指定しても同様で、こちらの場合は複数のボリュームがある場合は出力できず。

aws ec2 describe-instances --query 'Reservations[].Instances[].[InstanceId, BlockDeviceMappings[].Ebs.VolumeId, Tags[?Key==`Name`].Value[] ]' --output text

queryはあくまで出力する情報の絞り込みなので、出したい項目を絞り込むときは有効かなと思います。一方、jqはテキスト整形コマンドなので、テキスト整形はより柔軟なような気がします。

こちらは自分でもよくわかっていないので、queryでうまく整形する方法があれば教えてもらいたいです。

このあたりも時間があるときに記事にしたいですねー