サイトアイコン 協栄情報ブログ

想像と違った”AMI Usage” ~運用で使えるようにスクリプト化してみた~

AMIの棚卸し、最後にいつやりましたか?

 

環境が大きくなるほど、どのAMIがどこで使われているのかを追うのは大変です。実際不要リソースのチェックでAMIやスナップショットを棚卸しますが、「これ消しても大丈夫なやつ?」ってなることがあります。

 

そんな中で「AMIの使用状況を追跡できる」という"AMI Usage"が提供されたので、「運用負荷を下げるのでは?」という期待を込めて試してみました。結果として、概念としてはとても良い一方で、運用現場で“一覧から未使用を一気にあぶり出す”用途にはそのままだと足りないと感じました。

 

そこで今回の記事では、

 

 

AMI Usageとは

AMIは増え続けます。検証用、ロールバック用、派生AMIなどなど、気づけば「消していいのか判断に迷うAMI」が山積みです。根拠なく消すのは怖いし、根拠を集めるのは面倒ですよね。このジレンマを解消したい、それが今回紹介する「AMI Usage」のできることです。

 

ドキュメントの内容をまとめると、

 

ドキュメント読むと、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 ]をクリックすると、どれくらい使用されているかが確認できます。

 

 

 

■触ってみてわかったこと

機能としてはかゆいところに手が届く便利な機能ですよね。

 

【良かった点】

 

【惜しかった点】

 

 

想像していたAMI Usageとは違った

私は当初、「AMIの一覧をポンと出すと、各AMIの参照数(インスタンス数等)が並ぶ」イメージでした。

 

しかし実際は、AMIを一つずつ指定して参照状況を確認する前提なんですよね。Whats Newの「特定のAMIに依存しているアカウント内のリソースを特定」という説明の通り、対象を絞って深掘りする設計でした。

 

一方、運用の現場で求めたかったのは、“棚卸し視点”の逆引きです。

 

「リージョン内のAMIを一括で走査 → 参照なし&外部利用なし=削除候補」

 

この流れを“手でクリック”ではなく、定期実行できる形にしたかったです。

 

 

■AMI Usageで削除候補AMIを抽出

というわけで、削除候補となるAMI一覧を一括取得できることを目的として、以下の流れで処理するスクリプトを用意してみました。

 

  1. AMIの一覧を取得
  2. 社内参照チェック
  3. 共有状況チェック
  4. 外部利用チェック(共有している場合のみ)
  5. 削除候補判定(出力)

 

#!/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をアカウント間で共有する方法(齊藤弘樹)

 

モバイルバージョンを終了