AMIの棚卸し、最後にいつやりましたか?
環境が大きくなるほど、どのAMIがどこで使われているのかを追うのは大変です。実際不要リソースのチェックでAMIやスナップショットを棚卸しますが、「これ消しても大丈夫なやつ?」ってなることがあります。
そんな中で「AMIの使用状況を追跡できる」という"AMI Usage"が提供されたので、「運用負荷を下げるのでは?」という期待を込めて試してみました。結果として、概念としてはとても良い一方で、運用現場で“一覧から未使用を一気にあぶり出す”用途にはそのままだと足りないと感じました。
そこで今回の記事では、
- AMI Usageで何ができるのか
- 私が期待していた使い方との違い
- 不足分をスクリプトで補い、日々の棚卸しに使える形にした話
を共有します。
AMI Usageとは
AMIは増え続けます。検証用、ロールバック用、派生AMIなどなど、気づけば「消していいのか判断に迷うAMI」が山積みです。根拠なく消すのは怖いし、根拠を集めるのは面倒ですよね。このジレンマを解消したい、それが今回紹介する「AMI Usage」のできることです。
ドキュメントの内容をまとめると、
- AMIの参照チェック
⇒指定したAMIを参照しているリソース(インスタンス)を特定できる - AMIの使用状況レポート
⇒最も使用されているAMIを特定したり、共有したAMIの他アカウントでの利用状況をレポートできる - 最後に使用された追跡
⇒最後に使われた時刻が確認できる
ドキュメント読むと、AMI 単位での確認が前提で、ピンポイント調査は強いが、棚卸しの“一括可視化”は自分で仕組み化が必要という印象ですね。
■AMI Usage使ってみた
AMI Usageを実際に使ってみます。まずはAMIの参照チェックを試します。
↓EC2のダッシュボードから[ AMI ]にアクセスします。
↓調査用にAMIを二つ用意しました。一つは参照リソースがあるAMI(used-ami)、もうひとつが参照リソースがないAMI(unused-ami)です。
↓調べたいAMIを選択し、[ Actions ]から[ AMI usage ] ⇒ [ View referenced resources ]をクリックします。
↓[ Resource types ]から[ Instance ]を選択し、[ View resources ]をクリックします。
↓
↓そうすると、指定したAMIを参照しているEC2インスタンスが表示されました。
つづいて、使用状況レポートを出力してみましょう。
↓AMIの一覧からAMIを選択し、[ My AMI usage – new ]タブをクリックします。
↓[ Create my AMI usage report ]をクリックします。
↓[ Resource types ]から[ Instance ]を選択します。
↓[ Account IDs ]で[
Include all accounts ]を選択し、[ Create my AMI usage report ]をクリックします。
↓[ Status ]が[ Complete ]になれば作成完了です。
↓作成したレポートにあります[ View report ]をクリックすると、どれくらい使用されているかが確認できます。
↓
■触ってみてわかったこと
機能としてはかゆいところに手が届く便利な機能ですよね。
【良かった点】
- GUIからも使えるためその場で直感的な調査が楽
- 共有AMIの外部利用を「数」で把握できる
【惜しかった点】
- AMI一覧 → 集計ビューがないため、棚卸しには手間
- レポートがAMI指定前提で、全体の未使用候補を一気に抽出する用途には向かない
想像していたAMI Usageとは違った
私は当初、「AMIの一覧をポンと出すと、各AMIの参照数(インスタンス数等)が並ぶ」イメージでした。
しかし実際は、AMIを一つずつ指定して参照状況を確認する前提なんですよね。Whats Newの「特定のAMIに依存しているアカウント内のリソースを特定」という説明の通り、対象を絞って深掘りする設計でした。
一方、運用の現場で求めたかったのは、“棚卸し視点”の逆引きです。
「リージョン内のAMIを一括で走査 → 参照なし&外部利用なし=削除候補」
この流れを“手でクリック”ではなく、定期実行できる形にしたかったです。
■AMI Usageで削除候補AMIを抽出
というわけで、削除候補となるAMI一覧を一括取得できることを目的として、以下の流れで処理するスクリプトを用意してみました。
- AMIの一覧を取得
- 社内参照チェック
- 共有状況チェック
- 外部利用チェック(共有している場合のみ)
- 削除候補判定(出力)
#!/usr/bin/env bash
set -euo pipefail
# ---------- 設定 ----------
REGION="${1:-ap-southeast-1}" # 第1引数があればそれをリージョンとして使用
POLL_INTERVAL=5 # usage report のポーリング間隔(秒)
POLL_TIMEOUT=180 # usage report のタイムアウト(秒)
# -------------------------
echo "Scanning AMIs in ${REGION} ..."
AMIS=$(aws ec2 describe-images \
--region "$REGION" \
--owners self \
--query 'Images[].ImageId' \
--output text)
echo "Unused-candidate AMIs:"
for AMI in $AMIS; do
# --- 1) 社内参照チェック ---
INTERNAL_REFS="NA"
if aws ec2 help 2>&1 | grep -q describe-image-references; then
set +e
INTERNAL_REFS=$(aws ec2 describe-image-references \
--region "$REGION" \
--image-ids "$AMI" \
--include-all-resource-types \
--query 'length(ImageReferenceSet[])' \
--output text 2>/dev/null)
rc=$?
set -e
if [[ $rc -ne 0 || "$INTERNAL_REFS" == "None" ]]; then
INTERNAL_REFS="NA"
fi
fi
if [[ "$INTERNAL_REFS" == "NA" ]]; then
INTERNAL_REFS=$(aws ec2 describe-instances \
--region "$REGION" \
--filters "Name=image-id,Values=${AMI}" \
--query 'length(Reservations[].Instances[])' \
--output text)
fi
# --- 2) 共有状況チェック ---
set +e
SHARED_LEN=$(aws ec2 describe-image-attribute \
--region "$REGION" \
--image-id "$AMI" \
--attribute launchPermission \
--query 'length(LaunchPermissions[])' \
--output text 2>/dev/null)
set -e
[[ "$SHARED_LEN" == "None" ]] && SHARED_LEN=0
IS_SHARED=$(( SHARED_LEN > 0 ? 1 : 0 ))
# --- 3) 外部利用状況チェック(共有している場合のみ) ---
EXTERNAL_USAGE_TOTAL=0
if [[ $IS_SHARED -eq 1 ]]; then
REPORT_ID=$(aws ec2 create-image-usage-report \
--region "$REGION" \
--image-id "$AMI" \
--resource-types \
"ResourceType=ec2:Instance" \
"ResourceType=ec2:LaunchTemplate,ResourceTypeOptions=[{OptionName=version-depth,OptionValues=[100]}]" \
--query 'ReportId' --output text)
START=$(date +%s)
while :; do
STATE=$(aws ec2 describe-image-usage-reports \
--region "$REGION" \
--report-ids "$REPORT_ID" \
--query 'ImageUsageReports[0].State' \
--output text)
[[ "$STATE" == "available" ]] && break
NOW=$(date +%s); ELAPSED=$((NOW-START))
[[ $ELAPSED -ge $POLL_TIMEOUT ]] && break
sleep $POLL_INTERVAL
done
COUNTS=$(aws ec2 describe-image-usage-report-entries \
--region "$REGION" \
--report-ids "$REPORT_ID" \
--query 'ImageUsageReportEntries[].UsageCount' \
--output text 2>/dev/null || true)
if [[ -n "$COUNTS" ]]; then
EXTERNAL_USAGE_TOTAL=$(awk 'BEGIN{s=0} {for(i=1;i<=NF;i++) s+=$i} END{print s}' <<< "$COUNTS")
fi
fi
# --- 4) 判定 ---
if [[ "$INTERNAL_REFS" == "0" && ( $IS_SHARED -eq 0 || "$EXTERNAL_USAGE_TOTAL" == "0" ) ]]; then
echo "$AMI"
fi
done
アカウント内のシンガポールリージョンには、参照しているAMIが一つと参照していないAMIが一つあります。スクリプトを実行することで、参照していない、次のAMIが出力されればOKですね。
実際に動かしてみます。
$ ./find_unused_amis.sh ap-southeast-1
Scanning AMIs in ap-southeast-1 ...
Unused-candidate AMIs:
ami-0dacf2edef7fde743
一つだけ出力されました。
まとめ
新機能である"AMI Usage"は“深掘り調査”に強い機能です。一方、棚卸しの“一括可視化”は自前で補う必要がありそうです。
スクリプト化により、未使用候補の抽出が数分で完了し、削除の根拠が明確化でき、結果としてコスト削減に効く運用が実現できます。
定期実行させることで、月次で簡単にAMIの棚卸も可能ですね。
参考リンク:AWS公式ドキュメント
↓ほかの協栄情報メンバーもAmazon EC2についての記事を公開しています。ぜひ参考にしてみてください。
■KMSで暗号化されたAMIを他アカウントで起動してみる(齊藤弘樹)
■AMIをアカウント間で共有する方法(齊藤弘樹)