はじめに

この記事は、以下のような課題をお持ちの方を対象としています。

  • 数百、数千規模のAWSリソースを管理している
  • 棚卸しや監査のため、AWS CLIを使ってリソース情報を定期的に取得したい
  • しかし、単純なループ処理ではAPIのレートリミット(スロットリング)に頻繁に遭遇し、一部リソース情報の取得が欠損するなどでスクリプトが安定しない

AWS環境の規模が大きくなるにつれて、多数のリソース情報取得は実行時間と信頼性の両面で大きな課題となります。
本記事では、この課題を解決するために、AWSのリソースを網羅的に取得するための3つのアプローチを「セットアップの手間」「コスト」「効率」「リソースの網羅性」の観点から徹底比較します。さらに、APIスロットリングを回避するための実践的なシェルスクリプト設計について、例を交えながら解説します。

比較:AWSリソース情報の取得、3つのアプローチ

AWS環境のリソース情報を網羅的に取得するには、いくつかの代表的な方法があります。ここでは3つのアプローチを取り上げ、それぞれの長所と短所を見ていきましょう。

1:各サービスのAPIをループで実行する

これは、例えばまずaws ec2 describe-instancesで全インスタンスIDを取得し、その各IDに対してaws ec2 describe-instance-attributeのようなリソースの詳細情報を取得するコマンドを、ループ実行する方法です。あるいは、S3バケット名のリストに対してaws s3api get-bucket-encryptionを一つずつ実行していくような、最も直感的でシンプルなアプローチを指します。

  • セットアップ
    不要です。AWS CLIがインストールされ、認証情報が設定されていればすぐに実行できます。
  • コスト
    describelistといったリソース情報を読み取るAPIの呼び出し自体は無料です。
  • 効率
    悪いと言わざるを得ません。リソース数が増えるほどAPIのコール数が増大し、大規模な環境となるとAPIスロットリングが発生することもあるでしょう。数十のリソースを対象とする小規模な環境や、一度きりの使い捨てスクリプトには向いていますが、大規模な環境での定期的な棚卸しなどには不向きです。
  • リソースの網羅性
    スクリプトの作り込み次第で網羅的な取得は可能ですが、APIスロットリングによるエラーを適切にハンドリングしないと、情報取得が欠損するリスクが伴います。

2:AWS Config のAPIや高度なクエリを活用する

AWS Configを有効化しているAWS環境であれば、記録されたリソースの構成情報に対して、クエリで高速に検索をかけることができます。

  • セットアップ
    必須です。事前にAWS Configを有効化し、棚卸しの対象としたいリソースタイプを記録する設定が完了している必要があります。
  • コスト
    有料です。AWS Configは記録した構成情報の数に応じて課金されます。東京リージョンでは、記録された構成アイテム1つあたり (※) $0.003 の料金が発生します。※連続的な記録を選択した場合
    例えば、10,000個のリソースを記録対象としており、1ヶ月の間にリソースの作成や設定変更によって合計30,000個の構成情報の変更が記録された場合、月額料金は以下のようになります。
    30,000 * $0.003 = $90/月
    構成情報はリソースが作成された時だけでなく、設定が変更されるたびに記録されるため、数はリソース数よりも多くなる傾向にあります。そのため、大規模かつ変更が頻繁な環境では相応のコストがかかることを想定し、費用対効果を検討する必要があります。(参考: AWS Config の料金
  • 効率
    高いです。特に高度なクエリは、レコーダーによって記録済みのデータベースに対して検索をかけるため高速です。
  • リソースの網羅性
    記録対象に依存: Configが取得できるのは、レコーダーで記録を有効にしているリソースタイプのみです。記録対象外のリソースは取得できません。
  • APIによる取得情報の違い:
    • batch-get-resource-config: 1回で100個のリソース情報を取得でき一括取得に適しています。(参考:BatchGetResourceConfig)
    • get-resource-config-history: 履歴やタグも含めて詳細な情報を取得できますが、一度に1リソースずつしか取得できません。全量取得に利用するとAPIのRateLimitにかかる可能性があるため、一括取得には不向きです。(参考:GetResourceConfigHistory)

クエリの実行例

クエリ例1:特定のインスタンスタイプ(t2.micro)を持つEC2インスタンスをすべて検索する

SELECT
  resourceId,
  resourceName,
  configuration.instanceType,
  awsRegion
WHERE
  resourceType = 'AWS::EC2::Instance'
  AND configuration.instanceType = 't2.micro'

このクエリをAWS CLIから実行するには、以下のコマンドを使用します。

aws configservice select-resource-config \
--expression "SELECT resourceId, resourceName, configuration.instanceType, awsRegion WHERE resourceType = 'AWS::EC2::Instance' AND configuration.instanceType = 't3.micro'" \
| jq '.Results[] | fromjson'

クエリ例2:パブリック読み取りアクセスがブロックされていないS3バケットを検索する

SELECT
  resourceId,
  resourceName,
  awsRegion
WHERE
  resourceType = 'AWS::S3::Bucket'
  AND configuration.publicAccessBlockConfiguration.blockPublicAcls = 'false'

(参考: AWS Config ドキュメント – 高度なクエリ)

3: Resource Groups Tagging API を活用する

aws resourcegroupstaggingapi get-resourcesコマンドを利用すると、EC2インスタンス、S3バケット、RDSデータベースといった異なる種類のAWSサービスにまたがってリソースを一度に検索できます。

  • セットアップ
    不要です。
  • コスト:
    無料です。このAPIの呼び出し自体に追加料金は発生しません。
  • 効率
    高いです。一度のAPIコールで、リージョン内の複数のサービスにまたがるリソース情報を最大100件までまとめて取得できるため、効率的です。
  • リソースの網羅性
    低い。このAPIは網羅的なリソース棚卸しには向いていません。以下の弱点を理解しておく必要があります。
    • 設定情報が取得できない: 取得できるのはARNとタグのみです。暗号化設定などの詳細情報は、別途各サービスのAPIで取得し直す必要があります。
    • タグの無いリソースは取得できない: このAPIの最大の弱点です。タグが一切付与されたことがないリソースは、検索結果から完全に漏れてしまいます。全リソースの棚卸しという要件では致命的です。

(参考: Resource Groups Tagging API User Guide – get-resources)

比較表

アプローチ セットアップ コスト 効率(速度/スロットリング体制) リソースの網羅性 備考
1. 各APIのループ実行 不要 基本無料 低い スクリプトに依存する スロットリングによる情報欠損リスクあり
2. AWS Config 必要 有料 非常に高い 記録対象に限る 継続的なリソース管理や監査に最適
3. Tagging API 不要 基本無料 高い 低い タグ無しリソースが取得できず、網羅性に欠ける

実践:APIスロットリングを回避するシェルスクリプトの工夫

上記を踏まえ、各アプローチの良し悪しが見えてきました。AWS Configが強力な選択肢である一方、コストや柔軟性の観点からAWS CLIでの実装が最適なケースもあります。そこで以降からは、単純なループ実行の課題を克服し、CLIでリソース取得スクリプトを構築するためのテクニックを5つ紹介します。

テクニック1: 一括取得系APIの活用

APIスロットリングを回避するための重要な考え方は、そもそもAPIを呼び出す回数を最小限に抑えることです。リソースIDを一つひとつ指定してAPIをコールするのではなく、aws ec2 describe-instancesのように、フィルター条件を指定することで一度に多数のリソース情報をまとめて取得できる「一括取得系」のAPIを可能な限り利用しましょう。これにより、APIコール数を削減できます。

テクニック2: 処理フェーズの分離(応用)

リソース情報を取得するだけでなく、その結果を使ってリソースの状態を変更したい(例:特定のタグを一括で割り当てる)というニーズも多いかと思います。そのような場合に、スクリプトの堅牢性を高めるための応用テクニックが「処理フェーズの分離」です。

  • リソース情報取得フェーズ: まず、処理対象となるリソースのIDやARNを全て取得し、一時ファイルに保存します。
  • 実行フェーズ: 次に、その一時ファイルを読み込み、各リソースに対して詳細情報の取得や設定変更といったAPIコールを実行します。

このような設計にすることで、もし実行フェーズの途中でスクリプトが失敗しても、コストや処理時間のかかるリソース情報取得を再実行することなく、中断した箇所から処理を再開できます。デバッグも容易になり、スクリプトの信頼性が向上します。

テクニック3: xargsによるバッチ処理

テクニック2で作成した一時ファイルを効率的に処理するのに最適なのがxargsコマンドです。xargsは、ファイルや標準入力から受け取った大量のデータを、後続のコマンドが処理しやすいように分割して渡してくれます。
例えば、volume_ids.txtというファイルに100個のIDが保存されている場合、以下のようにxargsと組み合わせることで、「20個ずつ」の小さなバッチに分割してAPIを実行できます。

cat volume_ids.txt | xargs -n 20 aws ec2 describe-volumes --volume-ids

これにより、短時間に大量のAPIコールが集中するのを防ぎ、APIスロットリングのリスクを低減します。(参考:xargs コマンド

テクニック4: sleepによる待機処理

シンプルですが、非常に効果的な方法です。ループ処理やバッチ処理の合間にsleep 1のような意図的な待機時間を挿入することで、単位時間あたりのAPIコール数を物理的に抑制します。これにより、APIレートリミットの閾値を超える可能性をさらに下げることができます。

テクニック5: AWS CLIのリトライ設定

AWS CLIには、APIエラー発生時に自動でリトライ処理を行う機能が組み込まれています。特にadaptiveモードは、スロットリングエラーを検知すると、後続のAPIリクエストレートを自動的に調整してくれる優れものです。スクリプトの冒頭で環境変数を設定するだけで、スクリプトの堅牢性を大きく向上させることができます。
(参考:AWS CLI User Guide – Retries)

コード例:情報取得スクリプト

これらのテクニックを組み合わせた、「全EC2インスタンスにアタッチされているEBSボリュームの暗号化状態をCSVに出力する」スクリプトのサンプルです。

#!/bin/bash
# EBSボリュームの暗号化状態を安全に取得するスクリプト例

# --- 設定 ---
TARGET_REGION="ap-northeast-1"
OUTPUT_FILE="ebs_encryption_status.csv"
TMP_FILE="volume_ids.tmp"

# --- テクニック5: AWS CLIのリトライ設定 ---
export AWS_RETRY_MODE=adaptive
export AWS_MAX_ATTEMPTS=10

echo "調査を開始します..."
echo '"InstanceId","VolumeId","Encrypted"' > "$OUTPUT_FILE"

# --- テクニック1 & 2: IDリスト取得フェーズ ---
echo "IDリスト取得フェーズ: 対象ボリュームIDを一時ファイルに収集中..."
aws ec2 describe-instances \
  --region "$TARGET_REGION" \
  --query "Reservations[].Instances[].BlockDeviceMappings[].Ebs.VolumeId" \
  --output text > "$TMP_FILE"

if [ ! -s "$TMP_FILE" ]; then
  echo "調査対象のボリュームが見つかりませんでした。"
  exit 0
fi

# --- テクニック2, 3 & 4: 詳細情報取得フェーズ ---
echo "詳細情報取得フェーズ: 各ボリュームの暗号化状態を確認中..."
cat "$TMP_FILE" | xargs -n 20 | while read -r batch; do
  echo -n "."
  aws ec2 describe-volumes \
    --region "$TARGET_REGION" \
    --volume-ids $batch \
    --query "Volumes[].[Attachments[0].InstanceId, VolumeId, Encrypted]" \
    --output text | while read -r instance_id volume_id encrypted_status; do
      echo "\"$instance_id\",\"$volume_id\",\"$encrypted_status\"" >> "$OUTPUT_FILE"
    done
  # テクニック4: 各バッチ処理の間に待機を入れる
  sleep 1
done

echo ""
echo "調査が完了しました。結果は $OUTPUT_FILE を確認してください。"

発展:取得したリストを状態変更に使う

このフェーズを分離する設計の強みは、IDリスト取得フェーズで作成した一時ファイル(volume_ids.tmp)を再利用できることです。 例えば、棚卸しの結果、暗号化されていないボリューム全てに特定のタグを付けたくなったとします。その場合、以下のような「状態変更スクリプト」を別途用意するだけで、安全にタグ付け処理(実行フェーズ)を行えます。

#!/bin/bash
# volume_ids.tmpリストの全ボリュームに一括でタグを付けるスクリプト例

# --- 設定 ---
TARGET_REGION="ap-northeast-1"
TMP_FILE="volume_ids.tmp"
TAG_KEY="Needs-Encryption"
TAG_VALUE="true"

# リトライ設定は同様に有効化
export AWS_RETRY_MODE=adaptive
export AWS_MAX_ATTEMPTS=10

if [ ! -s "$TMP_FILE" ]; then
  echo "エラー: 対象リストファイル $TMP_FILE が存在しません。"
  exit 1
fi

echo "状態変更: 対象ボリュームにタグを付与中..."
cat "$TMP_FILE" | xargs -n 20 | while read -r batch; do
  echo -n "."
  # create-tagsは冪等性を持つため、すでにタグが存在してもエラーにならずに再実行できます
  aws ec2 create-tags \
    --region "$TARGET_REGION" \
    --resources $batch \
    --tags Key=$TAG_KEY,Value=$TAG_VALUE
  sleep 1
done

echo ""
echo "タグ付与が完了しました"

このように、「何をするか(状態変更)」のロジックを「何に対して行うか(変更対象リソースの取得)」のロジックから完全に分離することで、スクリプトの見通しが良くなり、再利用性も格段に向上します。

まとめ

AWSリソースの状態取得には複数のアプローチがあり、あらゆる要件に対応できる万能な解決策はありません。

  • AWS Configは、セットアップとコストというハードルをクリアできれば、リソースの網羅性という観点で信頼性の高い選択肢です。継続的なリソース構成管理や監査対応が求められる環境では、第一の候補となるでしょう。
  • 一方、AWS CLIで柔軟に実装する場合は、APIの特性を深く理解することが不可欠です。特に大規模環境においては、本記事で紹介したような一括取得系APIの活用、xargsによるバッチ処理、sleepによる待機処理、そしてCLIのリトライ設定といった地道な工夫の組み合わせが、スクリプトの安定稼働を実現する鍵となります。

この記事が、皆さまの環境や要件に合わせた最適なアプローチを選択するための一助となれば幸いです。