ぶろとよ

ネットワーク系クラウドエンジニア(AWS)の技術ブログ。自動化に興味があるためAWS CLIを勉強&アウトプット中。

AWS CLI:queryオプションについて学習してみた

この記事でやったこと

  • 今までなんとなく使っていたqueryオプションの再学習をした。
  • queryオプションの様々な例を書き出してみた。
  • queryは通常 JSON で考えるが、YAMLベースで考えてみた。

目次

検証環境

  • 検証日: 2021/10/30
  • PC環境:
    • Windows10 Home Ver21H1
      • PowerShell: Ver7.1.5
      • AWS CLI: aws-cli/2.1.34 Python/3.8.8 Windows/10 exe/AMD64 prompt/off
        • -- profile default : 読み取り専用
        • -- profile aws_RW : 書き込み権限あり
          • ※ 誤操作防止のため、変更操作時のみ-- profileオプション付与。
        • -- profile aws_PF : SystemsManagerポートフォワーディング用

注意事項

  • 本記事の内容はWindows PowerShellで検証しています。
  • AWS CLIはバージョン2を使用しています。
    • 本記事のコマンド出力でYAMLを使用してますが、これはバージョン2の機能です。
  • コマンド内の変数やパラメータやは検証で使用したものを記載しています。ご自身の環境に合わせ、書き換えて使用してください。
  • 個人で検証しているため実行結果に責任は持てません。必ずご自身でも検証してから使用してください。
  • queryは通常であれば JSON で考えたほうが良いのですが、YAMLを使いたいのでYAMLベースで記事を書いています。


1. 式の比較演算子

比較演算子 意味
A==B AとBは同じ
A!=B AとBは異なる
A<B AはB未満
A<=B AはB以下
A>B AはBより大きい
A>=B AはB以上


2. スライス式、ワイルドカード

スライス式を利用して、リスト内のインデックス指定で値を取得できます。
queryで使用してみた感じ、query文の最後の [ ] だけに指定可能?途中に式を書いても効果が無かった。

書式

[<start>:<stop>:<step>]
Start == リストの最初のインデックス、0。
Stop == リストの最後のインデックス。
Step == ステップスキップ。デフォルトは 1 です。
※ワイルドカード(全てに一致) [*] も利用可能

使用例

# スライスなし
aws ec2 describe-instances --query "Reservations[].Instances[].InstanceId"

<# 出力例
- i-1234567890AAAAAAA
- i-1234567890BBBBBBB
- i-1234567890CCCCCCC
#>


# リストの最初のものを表示([0])
aws ec2 describe-instances --query "Reservations[].Instances[].InstanceId | [0]"

<# 出力例
- i-1234567890AAAAAAA
#>


# リストの最初から2個目までを表示([:2])
aws ec2 describe-instances --query "Reservations[].Instances[].InstanceId | [:2]"

<# 出力例
- i-1234567890AAAAAAA
- i-1234567890BBBBBBB
#>


# リストの最初と3個目スキップで表示([::2])
aws ec2 describe-instances --query "Reservations[].Instances[].InstanceId | [::2]"

<# 出力例
- i-1234567890AAAAAAA
- i-1234567890CCCCCCC
#>


## ワイルドカードなし(Instances[])
aws ec2 describe-instances --query "Reservations[].Instances[]"

<# 出力例
- AmiLaunchIndex: 0
  Architecture: x86_64
#>


## ワイルドカードあり(Instances[*])
aws ec2 describe-instances --query "Reservations[].Instances[*]"

<# 出力例
- - AmiLaunchIndex: 0
    Architecture: x86_64
#>


3. 階層のフラット化

queryで階層の深いアイテム、階層が異なるアイテムを取った場合、出力に階層を示す「-」が行頭に出ます。
query文の末尾に [] を追加することで、階層をフラットにすることができます。

# --- コマンド 1 ------
# 階層が異なるアイテムを取得する
aws ec2 describe-instances `
    --query "Reservations[].Instances[].[InstanceId, BlockDeviceMappings[].DeviceName]"

<# 出力例: 階層に合わせて行頭に「-」が付く
- - i-1234567890AAAAAAA
  - - /dev/xvda
- - i-1234567890BBBBBBB
  - - /dev/xvda
- - i-1234567890CCCCCCC
  - - /dev/sda1
#>


# --- コマンド 2 ------
# 階層が異なるアイテムを取得する際、末尾に「[]」を付与する
aws ec2 describe-instances `
    --query "Reservations[].Instances[].[InstanceId, BlockDeviceMappings[].DeviceName][][]"

<# 出力例: 階層がフラット化され行頭の「-」が減っている
- i-1234567890AAAAAAA
- - /dev/xvda
- i-1234567890BBBBBBB
- - /dev/xvda
- i-1234567890CCCCCCC
- - /dev/sda1
#>


# --- コマンド 3 ------
# 更に末尾へ「[]」を付与する
aws ec2 describe-instances `
    --query "Reservations[].Instances[].[InstanceId, BlockDeviceMappings[].DeviceName][][]"

<# 出力例: 階層が更にフラット化され行頭の「-」が減っている
- i-1234567890AAAAAAA
- /dev/xvda
- i-1234567890BBBBBBB
- /dev/xvda
- i-1234567890CCCCCCC
- /dev/sda1
#>


4. 特定の項目だけを抽出する

# --- コマンド 1 ------
# EC2インスタンスID を取得する
aws ec2 describe-instances `
    --query "Reservations[].Instances[].InstanceId"

<# 出力例
- i-1234567890AAAAAAA
- i-1234567890BBBBBBB
- i-1234567890CCCCCCC
#>


# --- コマンド 2 ------
# EC2インスタンスNameタグ を取得する(Nested Lists)
aws ec2 describe-instances `
    --query "Reservations[].Instances[].Tags[?Key=='Name'].Value[]"

<# 出力例
- TEST-SERVER01
- TEST-SERVER02
- TEST-SERVER03
#>


# --- コマンド 3 ------
# EBSボリュームID を取得する(Nested Dictionary)
# ※「Ebs」は辞書型なので `.Ebs.` として `[]` は付けない
aws ec2 describe-instances `
    --query "Reservations[].Instances[].BlockDeviceMappings[].Ebs.VolumeId"

<# 出力例
- vol-1234567890AAAAA01
- vol-1234567890BBBBB01
- vol-1234567890BBBBB02
- vol-1234567890CCCCC01
#>


5. 複数の項目を抽出する

# --- コマンド 1 ------
# 階層が同じ、EC2インスタンスID と AMI-ID を取得する
# ※「[A, B, C, D]」とカンマ区切りで増やせる
aws ec2 describe-instances `
    --query "Reservations[].Instances[].[InstanceId, ImageId]"

<# 出力例
- - i-1234567890AAAAAAA
  - ami-1234567890aaaaaaa
- - i-1234567890BBBBBBB
  - ami-1234567890bbbbbbb
- - i-09c02fa7989d09393
  - ami-1234567890ccccccc
#>


# --- コマンド 2 ------
# 階層が異なる、EC2インスタンスID と EBSボリュームID(更に深い辞書) 取得する
aws ec2 describe-instances `
    --query "Reservations[].Instances[].[InstanceId, BlockDeviceMappings[].Ebs.VolumeId]"

<# 出力例
- - i-1234567890AAAAAAA
  - - vol-1234567890AAAAA01
- - i-1234567890BBBBBBB
  - - vol-1234567890BBBBB01
  - - vol-1234567890BBBBB02
- - i-1234567890CCCCCCC
  - - vol-1234567890CCCCC01
#>


# --- コマンド 3 ------
# 階層が異なる、EC2インスタンスID と Nameタグ(更に深いリスト) 取得する
aws ec2 describe-instances `
    --query "Reservations[].Instances[].[InstanceId, Tags[?Key=='Name'].Value]"

<# 出力例
- - i-1234567890AAAAAAA
  - - TEST-SERVER01
- - i-1234567890BBBBBBB
  - - TEST-SERVER02
- - i-1234567890CCCCCCC
  - - TEST-SERVER03
#>


# --- コマンド 4 ------
# コマンド 1~3 の項目 を全て取得する
aws ec2 describe-instances `
    --query "Reservations[].Instances[].[InstanceId, ImageId, BlockDeviceMappings[].Ebs.VolumeId, Tags[?Key=='Name'].Value]"

<# 出力例
- - i-1234567890AAAAAAA
  - ami-1234567890aaaaaaa
  - - vol-1234567890AAAAA01
  - - TEST-SERVER01
- - i-1234567890BBBBBBB
  - ami-1234567890bbbbbbb
  - - vol-1234567890BBBBB01
  - - vol-1234567890BBBBB02
  - TEST-SERVER02
- - i-1234567890CCCCCCC
  - ami-1234567890ccccccc
  - - vol-1234567890CCCCC01
  - - TEST-SERVER03
#>


6. 抽出した項目にラベルを付ける

識別子の命名ルール
- 英大小文字、数字、記号は_のみだと思われる - 先頭に数字はNG

# --- コマンド 1 ------
# 階層が同じ、EC2インスタンスID と AMI-ID を取得して、ラベル付与
aws ec2 describe-instances `
    --query "Reservations[].Instances[].[{EC2_ID:InstanceId}, {AMI_ID:ImageId}]"

<# 出力例
- EC2_ID: i-1234567890AAAAAAA
  AMI_ID: ami-1234567890aaaaaaa
- EC2_ID: i-1234567890BBBBBBB
  AMI_ID: ami-1234567890bbbbbbb
- EC2_ID: i-1234567890CCCCCCC
  AMI_ID: ami-1234567890ccccccc
#>


# --- コマンド 2 ------
# 階層が異なる、EC2インスタンスID と EBSボリュームID(更に深い辞書) 取得して、ラベル付与
aws ec2 describe-instances `
    --query "Reservations[].Instances[].[{EC2_ID:InstanceId}, BlockDeviceMappings[].Ebs.{Volume_ID:VolumeId}]"

<# 出力例
- - EC2_ID: i-1234567890AAAAAAA
  - - Volume_ID: vol-1234567890AAAAA01
- - EC2_ID: i-1234567890BBBBBBB
  - - Volume_ID: vol-1234567890BBBBB01
  - - Volume_ID: vol-1234567890BBBBB02
- - EC2_ID: i-1234567890CCCCCCC
  - - Volume_ID: vol-1234567890CCCCC01
#>


# --- コマンド 3 ------
# 階層が異なる、EC2インスタンスID と Nameタグ(更に深いリスト) 取得して、ラベル付与
aws ec2 describe-instances `
    --query "Reservations[].Instances[].[{EC2_ID:InstanceId}, Tags[?Key=='Name'].{Name_Tag:Value}]"

<# 出力例
- - EC2_ID: i-1234567890AAAAAAA
  - - Name_Tag: TEST-SERVER01
- - EC2_ID: i-1234567890BBBBBBB
  - - Name_Tag: TEST-SERVER02
- - EC2_ID: i-1234567890CCCCCCC
  - - Name_Tag: TEST-SERVER03
#>


# --- コマンド 4 ------
# コマンド 1~3 の項目 を全て取得して、ラベル付与
aws ec2 describe-instances `
    --query "Reservations[].Instances[].[{EC2_ID:InstanceId}, {AMI_ID:ImageId}, BlockDeviceMappings[].Ebs.{Volume_ID:VolumeId}, Tags[?Key=='Name'].{Name_Tag:Value}]"

<# 出力例
- - EC2_ID: i-1234567890AAAAAAA
  - AMI_ID: ami-1234567890aaaaaaa
  - - Volume_ID: vol-1234567890AAAAA01
  - - Name_Tag: TEST-SERVER01
- - EC2_ID: i-1234567890BBBBBBB
  - AMI_ID: ami-1234567890bbbbbbb
  - - Volume_ID: vol-1234567890BBBBB01
  - - Volume_ID: vol-1234567890BBBBB02
  - Name_Tag: TEST-SERVER02
- - EC2_ID: i-1234567890CCCCCCC
  - AMI_ID: ami-1234567890ccccccc
  - - Volume_ID: vol-1234567890CCCCC01
  - - Name_Tag: TEST-SERVER03
#>


7. ソート

7.1. 昇順ソート(若番から、古いものから)

# --- 使用例 1 ------
# EBSボリュームで、作成した日付が古いものから表示する
aws ec2 describe-volumes `
    --query "sort_by(Volumes, &CreateTime)[]"


# --- 使用例 2 ------
# Snapshotを「古いものから5件だけ」表示する
aws ec2 describe-snapshots `
    --owner-ids self `
    --query "sort_by(Snapshots, &StartTime)[] | [:5]"


7.2. 降順ソート(老番から、新しいものから)

# --- 使用例 1 ------
# AmazonのNATインスタンス AMIで、最新から10個までの名前を表示する
aws ec2 describe-images `
    --owners amazon `
    --filters 'Name=name,Values=amzn-ami-vpc-nat-*' `
    --query "reverse(sort_by(Images, &CreationDate))[:10].Name"

<# 出力例
- amzn-ami-vpc-nat-2018.03.0.20211015.1-x86_64-ebs
- amzn-ami-vpc-nat-2018.03.0.20211001.0-x86_64-ebs
- amzn-ami-vpc-nat-2018.03.0.20210721.0-x86_64-ebs
- amzn-ami-vpc-nat-2018.03.0.20210521.1-x86_64-ebs
- amzn-ami-vpc-nat-2018.03.0.20210408.0-x86_64-ebs
- amzn-ami-vpc-nat-2018.03.0.20210319.0-x86_64-ebs
- amzn-ami-vpc-nat-2018.03.0.20210224.0-x86_64-ebs
- amzn-ami-vpc-nat-2018.03.0.20210126.1-x86_64-ebs
- amzn-ami-vpc-nat-2018.03.0.20201209.1-x86_64-ebs
- amzn-ami-vpc-nat-2018.03.0.20201028.0-x86_64-ebs
#>


# --- 使用例 2 ------
# AmazonのNATインスタンス AMIで、最新のAMI-IDだけ表示する
aws ec2 describe-images `
    --owners amazon `
    --filters 'Name=name,Values=amzn-ami-vpc-nat-*' `
    --query "reverse(sort_by(Images, &CreationDate))[0].ImageId" `
    --output text

<# 出力例
ami-0a855f2e323191702
#>


# --- 使用例 3 ------
# Snapshotを「2021-10-30 00:00(UTC)以降」AND「最新から5件だけ」表示する
aws ec2 describe-snapshots `
    --owner-ids self `
    --query "reverse(sort_by(Snapshots, &StartTime)[?StartTime>='2021-10-30T00:00']) | [:5]"


9. query を fileters 代わりに使用する

9.1. filters と query の違いについて

  • filters
    • オプションコマンド --filter , --filters
    • AWSサーバー側でのフィルタリング
      • サーバ側からフィルターしたデータのみを受け取る
    • コマンド毎にフィルター出来る項目が異なる(共通で使わ回せない)
  • query
    • オプションコマンド --query
    • クライアント側でのフィルタリング
      • サーバ側からコマンド結果を全部受け取ってからクライアント側でフィルターするから重い
    • コマンド毎に使い勝手が変わらない
    • 組み込みの JSON ベース

サーバ側 or クライアント側のどちらでフィルターするかによる速度の違いは以下のコマンドで実感できます。
どちらも ImageId: ami-0701e21c502689c31 のフィルターで同じ結果が出ますが、速度が違います。

# --- PowerShell --- ※変数部分は自分用に修正してください。

${image_id} = "ami-0701e21c502689c31"
aws ec2 describe-images --filters "Name=image-id, Values='${image_id}'"
aws ec2 describe-images --query "Images[?ImageId=='${image_id}']"

aws ec2 describe-images は単体で打ってみると解るのですが出力量が多く重たいコマンドです。
filterはサーバ側からフィルターされたデータが来るので早いですが、queryは全部の出力を受け取ってから処理する遅いです。 queryが悪いというわけではなく、filterとqueryの使い分けが必要です。


9.2. query を fileters 代わりにするコマンド

# --- PowerShell --- ※変数部分は自分用に修正してください。

# --- コマンド 1 ------
# インスタンスID でフィルターする。
${ec2_id} = "i-1234567890AAAAAAA"
aws ec2 describe-instances `
    --query "Reservations[].Instances[?InstanceId=='${ec2_id}'][]"

<# aws ec2 describe-instances のどこを見ているか
Reservations:
- Groups: []
  Instances:
  - AmiLaunchIndex: 0
    InstanceId: i-1234567890AAAAAAA  ← ここをフィルター対象にしている
#>


# --- コマンド 2 ------
# インスタンスNameタグ でフィルターする。(Nested Listsに有効)
${ec2_name} = "EC2-TAGSNAME"
aws ec2 describe-instances `
    --query "Reservations[].Instances[?contains(Tags[?Key=='Name'].Value, '${ec2_name}')]"

<# aws ec2 describe-instances のどこを見ているか
Reservations:
- Groups: []
  Instances:
  - AmiLaunchIndex: 0
    Tags:
    - Key: Name            ← タグKeyが`Name`であることを条件にしている
      Value: EC2-TAGSNAME  ← ここをフィルター対象にしている
#>


# --- コマンド 3 ------
# 付与されているIAMロール名 でフィルターする。(Nested Dictionaryに有効)
${iamrole_name} = "iam-role-name"
aws ec2 describe-instances `
    --query "Reservations[].Instances[?contains(IamInstanceProfile.Arn, '${iamrole_name}')][]"

<# aws ec2 describe-instances のどこを見ているか
Reservations:
- Groups: []
  Instances:
  - AmiLaunchIndex: 0
    IamInstanceProfile:
      Arn: arn:aws:iam::123456789012:instance-profile/iam-role-name  ← ここをフィルター対象にしている
      Id: ABCDEFGHIJKLMNOPQR
#>


# --- コマンド 4 ------
# EC2インスタンスの起動状態 でフィルターする。(Nested Dictionaryに有効)
${ec2_state} = "running"
aws ec2 describe-instances `
    --query "Reservations[].Instances[?contains(State.Name, '${ec2_state}')][]"

<# aws ec2 describe-instances のどこを見ているか
Reservations:
- Groups: []
  Instances:
  - AmiLaunchIndex: 0
    State:
      Code: 16
      Name: running  ← ここをフィルター対象にしている
#>


参考リンク