【コスト削減】すべてのリージョンに存在する指定した日付以前の、自己所有AMIをスクリプトで特定してみる

記事タイトルとURLをコピーする

こんにちは!エンタープライズクラウド部技術2課の日高です。
皆さんAmazon Machine Images(今後はAMIと表記)をきちんと管理していますか??かなり前に作ったAMIが残っている方もいるのではないでしょうか。

AMI自体には料金はかかりませんが、AMIに紐づいているスナップショットに料金がかかるので不要なAMIを削除することでコスト削減につながります!
ということで、AWSの複数のリージョンに存在する自己所有のAmazon Machine Images(AMI)を検索し、指定された日付より前に作成されたAMIを探すスクリプトを作成してみました!

スクリプトを利用するだけの場合は「作成スクリプトとその使い方」を、スクリプトの詳細を知りたい方は「スクリプトの詳細」をご覧ください。

※2023/07/21に、下記2点ができるようにスクリプトを修正しました。
①AMI名も追加で出力できるようにする ②出力ファイルをマークダウン記法の表形式で出力する

作成スクリプトとその使い方

前提

  • AWS CLIがインストールされている
  • Python3がインストールされている
  • スイッチロールできる設定が済まされている(AWS CLI の設定 (~/.aws/config ファイル) にて、ロールの情報が設定されている)
  • 実行環境のOSがUnix系

※スイッチロールではなく、デフォルトに設定されているアカウントでスクリプトを実行したい場合は、各スクリプトの「PROFILE="myprofile"」と「--profile $PROFILE」を削除して利用してください。

作成したスクリプト

このスクリプトはAWSの複数のリージョンに存在する自己所有のAMIを検索し、指定された日付より前に作成されたAMIを探します。
そして、その結果をテキストファイルに保存します。

#!/bin/bash -eu

# プロファイルの定義
PROFILE="myprofile"

# 出力ファイルの定義
OUTPUT_FILE="unused_ami.txt"

# 指定日付の設定
YEAR_AGO="20××-××-××"

# ファイル出力の初期化
echo "Region, ImageId, CreationDate" > $OUTPUT_FILE

# すべてのリージョンでループ処理
for region in $(aws ec2 describe-regions --output text --query 'Regions[].RegionName' --profile $PROFILE)
do
    echo "Processing region: $region"

    # 自己所有のAMIを取得
    ami_infos=$(aws ec2 describe-images --owners self --region $region --query 'Images[?CreationDate<=`'$YEAR_AGO'`].[ImageId, CreationDate]' --output text --profile $PROFILE)

    # 各AMIの情報をファイルに出力
    while read -r ami_info; do
        echo "$region, $ami_info" >> $OUTPUT_FILE
    done <<< "$ami_infos"
done

利用方法

準備

  • 上記のスクリプトを実行するフォルダ配下に作成する。

※この際"myprofile""unused_ami.txt""20××-××-××"はご自身が実行したいものに書き換えてください。

実行コマンド

  • 下記コマンドを実行します。
  • bash <保存時のスクリプト名>
    • 私は「get_ami.sh」で保存しているのでbash get_ami.shが実行コマンドになります。

スクリプトの実行結果

スクリプトの実行画面は以下のようになります。
スイッチロールする際に、MFAを強制している方は、MFAの入力が求められます。

スクリプトの実行結果はこのように指定した日付以前(私はYEAR_AGO="2023-06-15"としています)に作られた自己所有のAMI IDと作成された日付、そのAMI IDが存在するリージョンが「unused_ami.txt」(出力ファイルをOUTPUT_FILE="unused_ami.txt"にしているため)に出力されます。

スクリプトの詳細

シェルスクリプトの部分とAWS CLIの部分に分けて解説します。

#!/bin/bash -eu

# プロファイルの定義
PROFILE="myprofile"

# 出力ファイルの定義
OUTPUT_FILE="unused_ami.txt"

# 指定日付の設定
YEAR_AGO="20××-××-××"

# ファイル出力の初期化
echo "Region, ImageId, CreationDate" > $OUTPUT_FILE

# すべてのリージョンでループ処理
for region in $(aws ec2 describe-regions --output text --query 'Regions[].RegionName' --profile $PROFILE)
do
    echo "Processing region: $region"

    # 自己所有のAMIを取得
    ami_infos=$(aws ec2 describe-images --owners self --region $region --query 'Images[?CreationDate<=`'$YEAR_AGO'`].[ImageId, CreationDate]' --output text --profile $PROFILE)

    # 各AMIの情報をファイルに出力
    while read -r ami_info; do
        echo "$region, $ami_info" >> $OUTPUT_FILE
    done <<< "$ami_infos"
done

シェルスクリプト

  • #!/bin/bash -eu
    • これはスクリプトの始まりで、スクリプトがbashシェルを使って解釈されるべきことをOSに指示します。
    • -eはエラーが起きた時にスクリプトの実行を停止させ、-uは未定義の変数が使われた場合にエラーを出力します。
  • PROFILE="myprofile"
    • この行はPROFILEという変数にmyprofileという値を格納します。
    • この変数は後でAWS CLIのコマンドで--profileオプションとして利用されます。
  • OUTPUT_FILE="unused_ami.txt"
    • この行はOUTPUT_FILEという変数にunused_ami.txtという値を格納します。
    • この変数は後でAMIの情報を出力するファイル名として利用されます。
  • YEAR_AGO="20××-××-××"
    • この行はYEAR_AGOという変数に20××-××-××という値を格納します。
    • この変数は後でAMIの作成日をフィルタするための基準として利用されます。
  • echo "Region, ImageId, CreationDate" > $OUTPUT_FILE
    • この行は出力ファイルの先頭にヘッダーライン(Region, ImageId, CreationDate)を書き込みます。
  • for region in $(aws ec2 describe-regions --output text --query 'Regions[].RegionName' --profile $PROFILE)
    • この行は全てのAWSリージョンに対してループを行います。
    • ループの各回では、そのリージョンの名前がregion変数に格納されます。
  • ami_infos=$(aws ec2 describe-images --owners self --region $region --query 'Images[?CreationDate<='$YEAR_AGO'].[ImageId, CreationDate]' --output text --profile $PROFILE)
    • この行はそのリージョンの自己所有のAMI情報を取得し、その結果をami_infos変数に格納します。
  • while read -r ami_info; doとdone <<< "$ami_infos"
    • この部分はami_infos変数に格納された各AMI情報に対してループを行います。
    • ループの各回では、そのAMIの情報がami_info変数に格納されます。
  • cho "$region, $ami_info" >> $OUTPUT_FILE
    • この行はそのAMIの情報(リージョン名とAMI情報)を出力ファイルに追記します。

AWS CLI

aws ec2 describe-regions --output text --query 'Regions[].RegionName' --profile $PROFILE

  • aws
    • AWS CLIの実行コマンドです。
  • ec2 describe-regions
    • このコマンドは利用可能な全てのAWSリージョンの情報を取得します。
  • --output text
    • 出力フォーマットをテキスト形式に指定します。
  • --query 'Regions[].RegionName'
    • 取得したリージョン情報から特定のデータを取り出します。
    • このクエリは全リージョン情報からRegionName(リージョン名)のみを取り出します。
  • --profile $PROFILE
    • AWS CLIのプロファイルを指定します。
    • プロファイルは、特定のAWSアカウントとリージョン、出力フォーマットの設定をまとめたもので、AWS CLIのコマンドを実行する際の認証情報を提供します。
    • ここでは変数$PROFILEに設定されたプロファイルを使用しています。

※詳しくは以下をご覧ください

docs.aws.amazon.com

aws ec2 describe-images --owners self --region $region --query 'Images[?CreationDate<='$YEAR_AGO'].[ImageId, CreationDate]' --output text --profile $PROFILE

  • aws
    • AWS CLIの実行コマンドです。
  • ec2 describe-images
    • このコマンドはEC2インスタンスの起動に使用できるAMIの情報を取得します。
  • --owners self
    • describe-imagesコマンドのオプションで、自己所有のAMIのみを取得します。
  • --region $region
    • 処理対象のAWSリージョンを指定します。
    • $regionは前述(シェルスクリプトの解説部分)のforループで設定された変数です。
  • --query 'Images[?CreationDate<='$YEAR_AGO'].[ImageId, CreationDate]'
    • 取得したAMI情報から特定のデータを取り出します。
    • このクエリは指定した日付($YEAR_AGO)以前に作成されたAMIのみをフィルタリングし、それぞれのAMIからImageIdとCreationDateの情報を取り出します。
  • --output text
    • 出力フォーマットをテキスト形式に指定します。
  • --profile $PROFILE
    • AWS CLIのプロファイルを指定します。
    • プロファイルは、特定のAWSアカウントとリージョン、出力フォーマットの設定をまとめたもので、AWS CLIのコマンドを実行する際の認証情報を提供します。
    • ここでは変数$PROFILEに設定されたプロファイルを使用しています。

※詳しくは以下をご覧ください。

docs.aws.amazon.com

コスト削減効果

冒頭にも記載しましたが、AMI自体には料金はかかりません。
ただ、AMIに紐づいているスナップショットに料金がかかります。

現状(2023年6月15日)かかるスナップショットのストレージ料金と以下になります。

  • スタンダード 0.05USD/1 か月あたりの GB
  • アーカイブ 0.0125USD/1 か月あたりの GB

例えば、スタンダードの200GiBのスナップショットが10個あり削除すると下記の計算式のようになります。 10個×200GiB×0.05USD×1ヶ月 = 100USD/月

つまり、スタンダードの200GiBのスナップショットが10個を削除すると1か月当たり100USDほどのコスト削減効果があります。

※ Elastic IPの料金については、詳しくは以下をご覧ください

aws.amazon.com

まとめ

スタンダードのスナップショットの保存料金「0.05USD/1 か月あたりの GB」とみるとそこまで料金がかからないように見えますが、容量が大きいスナップショットが紐づいているAMIがあると無駄なコストが増えてしまうことが分かりました。

本記事が誰かの助けになれば幸いです。

他にもコスト削減に役立つスクリプトを書いているのでよかったらご覧ください!

blog.serverworks.co.jp

blog.serverworks.co.jp

修正したスクリプト(※2023/07/21追記)

①Nameタグがある場合はAMI名を出力 ②出力ファイルをマークダウン記法の表形式で出力する の2つができるようにスクリプトを修正しました。
使用方法等は追記前と変更はありません。

  • スクリプト
#!/bin/bash -eu

# プロファイルの定義
PROFILE="指定してください"

# 出力ファイルの定義
OUTPUT_FILE="unused_ami.md"

# 指定日付の設定
YEAR_AGO="20××-××-××"

# ファイル出力の初期化
echo "|Region|AMI ID|AMI名|作成日時|" > $OUTPUT_FILE
echo "| --- | --- | --- | --- |" >> $OUTPUT_FILE

# すべてのリージョンでループ処理
for region in $(aws ec2 describe-regions --output text --query 'Regions[].RegionName' --profile $PROFILE)
do
    echo "Processing region: $region"

    # 自己所有のAMIを取得
    ami_infos=$(aws ec2 describe-images --owners self --region $region --query 'Images[?CreationDate<=`'$YEAR_AGO'`].[ImageId, CreationDate, Name]' --output text --profile $PROFILE)

    # 各AMIの情報をファイルに出力
    while read -r ami_info; do
        ami_id=$(echo $ami_info | awk '{print $1}')
        ami_date=$(echo $ami_info | awk '{print $2}')
        ami_name=$(echo $ami_info | awk '{print $3}')
        echo "|$region|$ami_id|$ami_name|$ami_date|" >> $OUTPUT_FILE
    done <<< "$ami_infos"
done
  • 出力結果

下記のようにAMI名と、出力形式を表形式のマークダウンファイルで出力されるようになりました。
※見えやすいように、マークダウン記法のプレビュー画面で表示しています。