エンジニア

2016.03.07

jqを使ってAWSの情報を取得しよう①

誘惑に負け、AmazonPrimeに再登録しました。
吉田です。

プライムビデオ、ミュージック
非常に良いです。しかもお急ぎ便無料・・・
ああ、素晴らしい。家から出たくない・・・

どこだろうと書きます(笑)

AWSを使っていて避けられない問題があります。
それはJSONです。

人間には優しくないJSONですが、AWSを使っているとデータは基本的にJSONで返ってきます。
こいつをどうにかゴリゴリしないといけません。

今回は数回に分けてAWSでjsonをゴリゴリする方法について書いていきます。

自分はjqを使っています。
このjq便利なので様々なブログで使い方が紹介されていますが、今回はAWSで情報をゴリゴリすることだけに絞って使い方を見ていこうと思います。

aws-cliコマンドの返り値を例に説明をしていきます。
awscilコマンドが使え、jqコマンドを使用できる事を前提とします。

aws ec2 describe-instances

さあ、情報を取りたい!と考えたとき、一番最初に使うのがこのコマンドだと思います。
クラウドネイティブ時代にインスタンスなんてなんてことを!?という話は置いておいて。

このコマンドから欲しい情報取れるようになれば必要なことは大抵できると思います。

まず出力されるJSONの例を見てください

aws ec2 describe-instances

{
    "Reservations": [
        {
            "ReservationId": "r-********",
            "Groups": [],
            "OwnerId": "************",
            "Instances": [
                {
                    "PrivateDnsName": "ip-****.ap-northeast-1.compute.internal",
                    "EbsOptimized": false,
                    "Monitoring": {
                        "State": "disabled"
                    },
                    "SecurityGroups": [
                        {
                            "GroupName": "RDP",
                            "GroupId": "sg-********"
                        }
                    ],
                    "State": {
                        "Name": "stopped",
                        "Code": 80
                    },
                    "NetworkInterfaces": [
                        {
                            "PrivateDnsName": "ip-********.ap-northeast-1.compute.internal",
                            "PrivateIpAddresses": [
                                {
                                    "PrivateDnsName": "ip-****.ap-northeast-1.compute.internal",
                                    "PrivateIpAddress": "172.**.**.***",
                                    "Primary": true
                                }
                            ],
                            "OwnerId": "************",
                            "Groups": [
                                {
                                    "GroupName": "RDP",
                                    "GroupId": "sg-********"
                                }
                            ],
                            "PrivateIpAddress": "172.**.**.***",
                            "Attachment": {
                                "DeleteOnTermination": true,
                                "AttachTime": "2016-02-24T05:53:59.000Z",
                                "AttachmentId": "eni-attach-*******",
                                "Status": "attached",
                                "DeviceIndex": 0
                            },
                            "Description": "",
                            "SourceDestCheck": true,
                            "MacAddress": "************",
                            "Status": "in-use",
                            "NetworkInterfaceId": "eni-************",
                            "VpcId": "vpc-************",
                            "SubnetId": "subnet-************"
                        }
                    ],
                    "PrivateIpAddress": "172.**.**.***",
                    "RootDeviceType": "ebs",
                    "StateTransitionReason": "User initiated (2016-02-24 10:01:43 GMT)",
                    "Placement": {
                        "GroupName": "",
                        "Tenancy": "default",
                        "AvailabilityZone": "ap-northeast-1a"
                    },
                    "Hypervisor": "xen",
                    "KeyName": "macpro",
                    "RootDeviceName": "/dev/sda1",
                    "ImageId": "ami-************",
                    "LaunchTime": "2016-02-24T05:53:59.000Z",
                    "VpcId": "vpc-************",
                    "BlockDeviceMappings": [
                        {
                            "Ebs": {
                                "DeleteOnTermination": true,
                                "AttachTime": "2016-02-24T05:54:04.000Z",
                                "VolumeId": "vol-************",
                                "Status": "attached"
                            },
                            "DeviceName": "/dev/sda1"
                        }
                    ],
                    "Tags": [
                        {
                            "Key": "Name",
                            "Value": "test"
                        }
                    ],
                    "SubnetId": "subnet-************",
                    "ProductCodes": [],
                    "Architecture": "x86_64",
                    "InstanceType": "t2.micro",
                    "Platform": "windows",
                    "StateReason": {
                        "Message": "Client.UserInitiatedShutdown: User initiated shutdown",
                        "Code": "Client.UserInitiatedShutdown"
                    },
                    "InstanceId": "i-************",
                    "SourceDestCheck": true,
                    "ClientToken": "rduVq************",
                    "AmiLaunchIndex": 0,
                    "PublicDnsName": "",
                    "VirtualizationType": "hvm"
                }
            ]
        }
    ]
}

こんなJSONが返ってきます。
このJSONをゴリゴリしながらjqの使い方を見ていきましょう。

JSONの基本

JSONはキーと値のセットで表現されています。
キーと値のセットは以下の様に表されます。
また”,”で区切って複数のセットを列挙することができます。

{"キー":"値"}
{"キー":"値","キー":"値"}

JSONは配列を持ち、配列は[]で表されます。
要素は”,”で区切って表記されます。

この記法をサンプルで確認してみましょう。

"Tags": [
    {
        "Key": "Name",
        "Value": "test"
    }
]

この例はキー:Tagsに対する値を配列形式で持っている事を示しています。ただし、この例では要素数1の配列です。

一列で書いた方がわかりやすいかもしれないのでこっちも載せておきます。

"Tags": [ { "Key": "Name" , "Value": "test" } ]

配列が複数の要素を持つ場合こんな形になります。

"Tags": [
    {
        "Key": "hoge",
        "Value": "***********"
    },
        "Key": "fuga",
        "Value": "***********"
    }
]
"Tags": [ { "Key": "hoge" , "Value": "*****" } , { "Key": "fuga" , "Value": "****" } ]

ここまでわかればJSONの記法は理解できました!
では、jqの値の取り方を見ていきます。

jqの使い方

jaのコマンドを使うときは以下の様に使います。

aws ec2 describe-instances | jq -オプション 'ゴリゴリするために書く'

awsコマンドの返り値を | -パイプ- を使ってjqコマンドにつなぎます。
jqコマンドには以下のような表記のお約束が存在します。

jaのお約束

取得する値のキーを .キー の形で表記する
キーに対する値を配列で持つ場合 .キー[] と表記する

では、実際にインスタンスidを取得しながら確認してみましょう。
インスタンスidを取るためJSONの構造を確認します。

①キー:Reservations に対して配列で値を持っています。
②配列の要素、キー:Instancesは配列形式で値を持っています。また、配列は各要素にインスタンスの情報を持っています。
③インスタンスの情報の中にキー:InstanceId があります。
という形になっています。では、実際に取得してみましょう。

つまり、Reservationsの配列の中のInstances配列の中にInstanceIdが入ってますってことです。

aws ec2 describe-instances | jq '.Reservations[].Instances[].InstanceId'

結果
“i-************”

見事にインスタンスidを取得することが出来ました!
コマンドの読み方をもう一度確認します。

jq ‘.Reservations[].Instances[].InstanceId’

① .Reservations[]:値を配列で保持する、キー:Reservationsの値配列を取得する
② .Instances[]:取得した値配列の中で、値を配列で保持する、キー:Instancesの値配列を取得する
③ .InstanceId:取得した値配列の中でキー:InstanceIdの値を取得する

という構成になっています。
これは途中で配列の構造が複雑になっても変わりません。
インスタンスに当てられたセキュリティグループの一覧情報を取得したい場合下のようになります。

jq '.Reservations[].Instances[].NetworkInterfaces[].Groups[]'

結果

{
  "GroupName": "RDP",
  "GroupId": "sg-984bb5fc"
}

もし、このGroupIdが欲しい場合は

jq '.Reservations[].Instances[].NetworkInterfaces[].Groups[].GroupId'

とすれば良いというわけです。

返された値を変数にセットすることでシェルスクリプトを用いていろいろな事を自動化することができます。
その際、返された値が “i-************” といったように””でくくられていると何かと不便です。
こういった時はjqのオプション -r を使用することで””を剥くことが出来ます。

aws ec2 describe-instances | jq -r '.Reservations[].Instances[].InstanceId'

結果
i-************

さて、jqの使い方について簡単にみていきましたがどうでしょうか。
返されたJSONをゴリゴリ削っていくことで欲しい値が手に入ることがわかったと思います。

いろいろ試してみてください。

つづき jqを使ってAWSの情報を取得しよう②

一覧に戻る