jqを使ってAWSの情報を取得しよう②
リテールテック、JAWS-HUB、JAWSDAYS、出張手羽の会、JAWS-UG CLI専門支部と最近は怒涛のイベントラッシュでした。
吉田です。
週末はイノベーションエッグに参加するため大阪に行ってきました。
大阪のたこ焼きは本当に美味しかった!
大阪旅行記は次回の冒頭にでも仕込もうかと思います。
書き出しの数行は個人ブログだと思っているのでいつも好きに書いていますが、そろそろ本題に移りましょう。
前回jqを使ってAWSの情報を取得しよう①というタイトルでjqについて書きました。
基本的にはあんなイメージでやっていけば情報を取得することが出来ます。
今回はもう少し便利に使うための手法について見ていこうと思います。
前回はインスタンス1台分の情報で書きましたが、今回は停止中と起動中の2台分のJSONなので長くなっています。
まあ、前回1台分ちゃんと取れたのでこれが2台だろうが100台だろうが大丈夫です。
台数が増えた時に取りやすくするための方法を見ていくのでぜひ活用してみてください。
サンプルのJSONは長すぎるので最下部に置きます。
select()
selectの条件式()内を満たすものだけを出力します。
例えば、起動しているインスタンスのみを出力したい場合
select(.Reservations[].Instances[].State.Name == “running”)という形になり、条件式が真の要素のみを返します。
サンプルで確認しましょう。
aws ec2 describe-instances | jq '.Reservations[] | select(.Instances[].State.Name == "running")'
下のサンプルJSONの前半、下記のように返却されます。
Reservations配列下を表示しろ。ただし、Instances配列内Stateが持つNameの値が“running”だけね!
という意味です。
{
"OwnerId": "************",
"ReservationId": "r-********",
"Groups": [],
"Instances": [
{
"Monitoring": {
"State": "disabled"
},
"PublicDnsName": "ec2-*************.ap-northeast-1.compute.amazonaws.com",
"State": {
"Code": 16,
"Name": "running"
},
"EbsOptimized": false,
"LaunchTime": "2016-03-08T08:25:51.000Z",
"PublicIpAddress": "************",
"PrivateIpAddress": "172.31.23.149",
"ProductCodes": [],
"VpcId": "vpc-********",
"StateTransitionReason": "",
"InstanceId": "i-********",
"ImageId": "ami-59bdb937",
"PrivateDnsName": "ip-172-31-23-149.ap-northeast-1.compute.internal",
"KeyName": "ec2kye",
"SecurityGroups": [
{
"GroupName": "launch-wizard-4",
"GroupId": "sg-********"
}
],
"ClientToken": "AKWfs123456789",
"SubnetId": "subnet-********",
"InstanceType": "t2.micro",
"NetworkInterfaces": [
{
"Status": "in-use",
"MacAddress": "06:e4:22:c1:df:c5",
"SourceDestCheck": true,
"VpcId": "vpc-********",
"Description": "",
"Association": {
"PublicIp": "*************",
"PublicDnsName": "ec2-*********.ap-northeast-1.compute.amazonaws.com",
"IpOwnerId": "amazon"
},
"NetworkInterfaceId": "eni-********",
"PrivateIpAddresses": [
{
"PrivateDnsName": "ip-172-31-23-149.ap-northeast-1.compute.internal",
"Association": {
"PublicIp": "************",
"PublicDnsName": "ec2-***********.ap-northeast-1.compute.amazonaws.com",
"IpOwnerId": "amazon"
},
"Primary": true,
"PrivateIpAddress": "172.31.23.149"
}
],
"PrivateDnsName": "ip-172-31-23-149.ap-northeast-1.compute.internal",
"Attachment": {
"Status": "attached",
"DeviceIndex": 0,
"DeleteOnTermination": true,
"AttachmentId": "eni-attach-*********",
"AttachTime": "2016-03-08T08:25:51.000Z"
},
"Groups": [
{
"GroupName": "launch-wizard-1",
"GroupId": "sg-*********"
}
],
"SubnetId": "subnet-*************",
"OwnerId": "************",
"PrivateIpAddress": "172.31.23.149"
}
],
"SourceDestCheck": true,
"Placement": {
"Tenancy": "default",
"GroupName": "",
"AvailabilityZone": "ap-northeast-1a"
},
"Hypervisor": "xen",
"BlockDeviceMappings": [
{
"DeviceName": "/dev/xvda",
"Ebs": {
"Status": "attached",
"DeleteOnTermination": true,
"VolumeId": "vol-**********",
"AttachTime": "2016-03-08T08:25:52.000Z"
}
}
],
"Architecture": "x86_64",
"RootDeviceType": "ebs",
"RootDeviceName": "/dev/xvda",
"VirtualizationType": "hvm",
"AmiLaunchIndex": 0
}
]
},
さて、停止しているインスタンスと起動しているインスタンスがある場合に、起動しているインスタンスの情報だけを取得することができました。
でも実際情報取る時って、ここからさらに削っていかなきゃいけないですよね?
| ーパイプーでどんどん繋いでいく
返ってきたJSONをパイプでまたjqに喰わせればいいんです。
個人的には好きでないですけど別にgrepやawkで引っ張ったって良いんです
だって、ただのシェルだもん!
恐れることはありません。
aws ec2 describe-instances | jq '.Reservations[] | select(.Instances[].State.Name == "stopped")' | jq '.Instances[].NetworkInterfaces[].VpcId'
さっきのJSON出力をもう1回jqに喰わせてVPCのIDを取得しています。
コマンドでさっきと変わったところは後半にもう1文jqが入っている点です。
上記実行すると
"vpc-********"
このように値が取得できます。
スクリプトで利用するなら
VPC_ID=`aws ec2 describe-instances | jq '.Reservations[] | select(.Instances[].State.Name == "stopped")' | jq -r '.Instances[].NetworkInterfaces[].VpcId'`
というように実行自体を``(バックスラッシュ)で括って変数に突っ込めば大丈夫です。
“”が邪魔だったのでjqを使ってAWSの情報を取得しよう①でも書いた「-r』 オプションを使って値だけ取得しています
echo ${VPC_ID}
と変数の中身を確認すると
vpc-********
というように結果だけを取得することができます。
このように必要な情報を変数化して保持することで、プログラム中で利用することが出来ます。
スクリプト中にインスタンスidやパブリックIPなどをハードコーティングしていないのでAWSの仕様変更などがあっても悲しいことになりません。
AWSCLIの返却値はとにかくJSONをゴリゴリ削っていけば欲しい値が手に入ります。
変数化してしまえば後はシェルスクリプト芸でいくらでもどうとでもなります。
AWSだから!とビビることはありません。ここまでくればシェル芸の世界です。
JSONの再整形
JSONを使っているとその処理を次に繋げるためにほしい情報だけでもう一回JSONを組み直して表示したい!
なんてこともあるかと思います。
jqを使ったJSONの再整形の仕方についても基礎的な部分を確認したいと思います。
といってもやり方は前回と今回の説明でもう説明しきっているんですけどね
ここまでで使った知識をフルに使って整形します。
aws ec2 describe-instances | jq '.Reservations[] | select(.Instances[].State.Name == "stopped") | {"instance id":.Instances[].InstanceId,"instance parm":[{"VPC ID":.Instances[].NetworkInterfaces[].VpcId}],"instance type":.Instances[].InstanceType}'
結果
{
"instance id": "i-********",
"instance parm": [
{
"VPC ID": "vpc-c06de9a5"
}
],
"instance type": "vpc-********"
}
あえて、配列の中に辞書型を仕込んだりとめんどくさくしています。
再整形は前回説明した辞書と配列の仕組みをしっかり再確認すればわかると思います。
みなさんもJSONと仲良くなってAWSCLIで遊びましょう!!
次回は・・・次回のおたのしみに!
おまけTIPS
length
入力されたJSONの要素の長さを出力してくれます。
地味に便利なこともあるので(僕はあまり使わないんだけど)
配列[]:要素数を返します
[0,1,2]という要素数3の配列の場合3が返ってきます。
辞書{“Key”:”Value”}キーと値のセットの個数を返します。
{“Key1″:”Value1″,”Key2″:”Value2”}だったら2が返ってきます。
・nullは0を返し、文字列に当てると文字列の字数を返します。
Sample-JSON
{
"Reservations": [
{
"OwnerId": "************",
"ReservationId": "r-********",
"Groups": [],
"Instances": [
{
"Monitoring": {
"State": "disabled"
},
"PublicDnsName": "ec2-*************.ap-northeast-1.compute.amazonaws.com",
"State": {
"Code": 16,
"Name": "running"
},
"EbsOptimized": false,
"LaunchTime": "2016-03-08T08:25:51.000Z",
"PublicIpAddress": "************",
"PrivateIpAddress": "172.31.23.149",
"ProductCodes": [],
"VpcId": "vpc-********",
"StateTransitionReason": "",
"InstanceId": "i-********",
"ImageId": "ami-59bdb937",
"PrivateDnsName": "ip-172-31-23-149.ap-northeast-1.compute.internal",
"KeyName": "ec2kye",
"SecurityGroups": [
{
"GroupName": "launch-wizard-4",
"GroupId": "sg-********"
}
],
"ClientToken": "AKWfs123456789",
"SubnetId": "subnet-********",
"InstanceType": "t2.micro",
"NetworkInterfaces": [
{
"Status": "in-use",
"MacAddress": "06:e4:22:c1:df:c5",
"SourceDestCheck": true,
"VpcId": "vpc-********",
"Description": "",
"Association": {
"PublicIp": "*************",
"PublicDnsName": "ec2-*********.ap-northeast-1.compute.amazonaws.com",
"IpOwnerId": "amazon"
},
"NetworkInterfaceId": "eni-********",
"PrivateIpAddresses": [
{
"PrivateDnsName": "ip-172-31-23-149.ap-northeast-1.compute.internal",
"Association": {
"PublicIp": "************",
"PublicDnsName": "ec2-***********.ap-northeast-1.compute.amazonaws.com",
"IpOwnerId": "amazon"
},
"Primary": true,
"PrivateIpAddress": "172.31.23.149"
}
],
"PrivateDnsName": "ip-172-31-23-149.ap-northeast-1.compute.internal",
"Attachment": {
"Status": "attached",
"DeviceIndex": 0,
"DeleteOnTermination": true,
"AttachmentId": "eni-attach-*********",
"AttachTime": "2016-03-08T08:25:51.000Z"
},
"Groups": [
{
"GroupName": "launch-wizard-1",
"GroupId": "sg-*********"
}
],
"SubnetId": "subnet-*************",
"OwnerId": "************",
"PrivateIpAddress": "172.31.23.149"
}
],
"SourceDestCheck": true,
"Placement": {
"Tenancy": "default",
"GroupName": "",
"AvailabilityZone": "ap-northeast-1a"
},
"Hypervisor": "xen",
"BlockDeviceMappings": [
{
"DeviceName": "/dev/xvda",
"Ebs": {
"Status": "attached",
"DeleteOnTermination": true,
"VolumeId": "vol-**********",
"AttachTime": "2016-03-08T08:25:52.000Z"
}
}
],
"Architecture": "x86_64",
"RootDeviceType": "ebs",
"RootDeviceName": "/dev/xvda",
"VirtualizationType": "hvm",
"AmiLaunchIndex": 0
}
]
},
{
"OwnerId": "**************",
"ReservationId": "r-**********",
"Groups": [],
"Instances": [
{
"Monitoring": {
"State": "disabled"
},
"PublicDnsName": "",
"Platform": "windows",
"State": {
"Code": 80,
"Name": "stopped"
},
"EbsOptimized": false,
"LaunchTime": "2016-02-24T05:53:59.000Z",
"PrivateIpAddress": "172.31.31.197",
"ProductCodes": [],
"VpcId": "vpc-c**********",
"StateTransitionReason": "User initiated (2016-02-24 10:01:43 GMT)",
"InstanceId": "i-**********",
"ImageId": "ami-2044434e",
"PrivateDnsName": "ip-172-31-31-197.ap-northeast-1.compute.internal",
"KeyName": "macpro",
"SecurityGroups": [
{
"GroupName": "RDP",
"GroupId": "sg-**********"
}
],
"ClientToken": "rduVq123456789",
"SubnetId": "subnet-*********",
"InstanceType": "t2.micro",
"NetworkInterfaces": [
{
"Status": "in-use",
"MacAddress": "06:34:e3:b1:a2:0d",
"SourceDestCheck": true,
"VpcId": "vpc-********",
"Description": "",
"NetworkInterfaceId": "eni-**********",
"PrivateIpAddresses": [
{
"PrivateDnsName": "ip-172-31-31-197.ap-northeast-1.compute.internal",
"Primary": true,
"PrivateIpAddress": "172.31.31.197"
}
],
"PrivateDnsName": "ip-172-31-31-197.ap-northeast-1.compute.internal",
"Attachment": {
"Status": "attached",
"DeviceIndex": 0,
"DeleteOnTermination": true,
"AttachmentId": "eni-attach-********",
"AttachTime": "2016-02-24T05:53:59.000Z"
},
"Groups": [
{
"GroupName": "RDP",
"GroupId": "sg-**********"
}
],
"SubnetId": "subnet-*********",
"OwnerId": "************",
"PrivateIpAddress": "172.31.31.197"
}
],
"SourceDestCheck": true,
"Placement": {
"Tenancy": "default",
"GroupName": "",
"AvailabilityZone": "ap-northeast-1a"
},
"Hypervisor": "xen",
"BlockDeviceMappings": [
{
"DeviceName": "/dev/sda1",
"Ebs": {
"Status": "attached",
"DeleteOnTermination": true,
"VolumeId": "vol-**********",
"AttachTime": "2016-02-24T05:54:04.000Z"
}
}
],
"Architecture": "x86_64",
"StateReason": {
"Message": "Client.UserInitiatedShutdown: User initiated shutdown",
"Code": "Client.UserInitiatedShutdown"
},
"RootDeviceName": "/dev/sda1",
"VirtualizationType": "hvm",
"RootDeviceType": "ebs",
"Tags": [
{
"Value": "test",
"Key": "Name"
}
],
"AmiLaunchIndex": 0
}
]
}
]
}