AWSの合計月額料金を年月指定で取得するAWS CLIを用いたシェルスクリプト(バッチ処理プログラム) ~請求ダッシュボード画面にログインしないで合計請求額を確認する方法~

2月 20, 2017AWS,AWS CLI,Billing,EC2,Programming,Shell Script

AWSを利用していて気になるのが日々従量課金される請求額です。

特に複数のIAMユーザーをAWSアカウント内に用意して使用する場合、IAMユーザーのリソースの使い方によっては数日確認しなかっただけで料金が跳ね上がってしまうこともあります。

AWSの請求額を確認する方法には主に下記の3つが挙げられます。

  • AWSマネジメントコンソールの請求ダッシュボードで請求書を確認する。
  • CloudWatchのus-east-1リージョンのメトリクスBillingで概算合計請求額「EstimatedCharges」を確認する。
  • AWSアカウント設定画面で「請求レポートを受け取る」を有効にしてS3バケットに請求レポートを出力し、確認する。
  • ※「請求レポートを受け取る」の設定は「AWS請求関連設定画面」でできます。

これらの中で「請求ダッシュボード」に毎日、毎回ログインして確認するのは意外に手間がかかり、請求額をすぐに知りたい場合や請求額をプログラムに取り込むには不向きです。

また、CloudWatchは当月の請求額が設定額を超えると通知してくれる機能があり便利な一方で、メトリクス保存期間が2週間のため、過去の請求額との比較やプログラムとの連携には不向きです。

一方、請求レポートをS3バケットに出力する方法は指定したS3バケットに出力された詳細な請求レポートが半永久的に残っていくため、過去の請求額との比較やプログラム連携ととても相性が良いです。

今回はS3バケットに出力された請求レポートを用いてAWS CLIで指定した年月の合計請求額を出力するシェルスクリプト(バッチ処理プログラム)を備忘録として記載します。

AWSの合計月額料金を年月指定で取得するAWS CLIを用いたシェルスクリプト(バッチ処理プログラム) ~請求ダッシュボード画面にログインしないで合計請求額を確認する方法~

基本方針としてはスクリプト内の変数に請求レポートが出力されているS3バケット名を記載しておき、引数で渡された年月の請求レポートをAWS CLIでダウンロードし、合計請求額を該当の行から取得するというものです。

AWSの合計月額料金を年月指定で取得するAWS CLIを用いたシェルスクリプト(バッチ処理プログラム)

処理の内容は基本方針と同じですが、AWS CLIでバケットの情報を取得する場合にEU系のリージョンではリージョン指定をする文字列が無いとエラーが発生する場合があるため、各S3バケット名からリージョン指定をする文字列を生成する関数を作成してAWS CLI実行時に付加しています。

当月の概算合計請求額には「Approximately Total Cost」と表示し、先月以前の確定合計請求額には「Committed Total Cost」と表示されるようにしています。

[magtranetwork@localhost ~]# vim check_aws_total_cost.sh
#!/bin/bash

# S3バケット名からリージョンオプション文字列を作成する関数
# \`bucket_region ${BUCKET}\`
bucket_region () {
  REGION=`aws s3api get-bucket-location --output text --bucket ${1}`
  if [ "${REGION}" = "None" -o "${REGION}" = "" ]; then
    REGION_STR=""
  else
    REGION_STR="--region ${REGION}"
  fi
  echo ${REGION_STR}
}

#請求レポートを出力しているS3バケット名
#自分の環境に応じて適宜変更してください。
BUCKET_NAME="your-billing-report"

MONTH_STR=$1

#macOSの場合はgdateコマンドで日付を扱う
if [ "$(uname)" == 'Darwin' ]; then
  DATECMD=gdate
else
  DATECMD=date
fi

#引数がない場合は当月を指定
if [ "${MONTH_STR}" = "" ]; then
  MONTH_STR=`${DATECMD} +"%Y-%m"`
else
  #引数の形式が違う場合は終了。
  IS_MONTH_STR=`echo "${MONTH_STR}" | grep '[0-9]\{4\}-[0-9]\{2\}' | wc -l | sed 's/ //g' | sed 's/\t//g' | tr -d '\n'`
  
  if [ "$IS_MONTH_STR" != "1" ]; then
    echo "Usage: $(basename $0) [MONTH_STR(ex. 2017-01)]"
    exit 1
  fi
fi

#請求レポート名をAWS CLIで取得
FILE_NAME=`aws s3 \`bucket_region ${BUCKET_NAME}\` ls s3://${BUCKET_NAME}/ | grep "aws-billing-csv-${MONTH_STR}.csv" | awk '{print $NF}'`

#請求レポートが存在しなければ終了。
if [ "${FILE_NAME}" = "" ]; then
  echo "Target Billing is Not Existed."
  exit 1
fi

#実際に請求レポートをダウンロード
aws s3 `bucket_region ${BUCKET_NAME}` cp s3://${BUCKET_NAME}/${FILE_NAME} /tmp/ >/dev/null 2>&1

#確定合計請求額を抽出
TOTAL_COST=`cat /tmp/${FILE_NAME} | sed -e 's/,/ /g' | awk 'END{print $NF}' | sed -e 's/"//g'`

if [ "${TOTAL_COST}" = "" ]; then
  #確定合計請求額がない場合は概算合計請求額と請求レポート作成日付を抽出し、結果出力。
  TOTAL_COST=`cat /tmp/${FILE_NAME} | sed -e 's/,/ /g' | grep '"StatementTotal"' | awk 'END{print $NF}' | sed -e 's/"//g'`
  DESCRIPTION=`cat /tmp/${FILE_NAME} | sed -e 's/,/ /g' | awk 'END{print}' | sed -e 's/"//g' | sed -e 's/EstimatedDisclaimer//g' | sed -e 's/  //g' | sed -e 's/This report reflects your estimated monthly bill for activity through approximately //g'`
  echo "{\"StatementTotal\": \"\$${TOTAL_COST}\", \"Description\": \"Approximately Total Cost at ${DESCRIPTION}(UTC)\"}"
else
  #確定合計請求額がある場合は請求レポート作成日付を抽出し、結果出力。
  DESCRIPTION=`aws s3 \`bucket_region ${BUCKET_NAME}\` ls s3://${BUCKET_NAME}/ | grep ${FILE_NAME} | awk '{print $1 " " $2}' | sed -e 's/-/\//g'`
  if [ "$(uname)" == 'Darwin' ]; then
    ZONE=`${DATECMD} | awk '{print $((NF-1))}'`
  else
    ZONE=`${DATECMD} | awk '{print $NF}'`
  fi
  echo "{\"StatementTotal\": \"\$${TOTAL_COST}\", \"Description\": \"Committed Total Cost at ${DESCRIPTION}(${ZONE})\"}"
fi

#ダウンロードした請求レポートを削除。
rm -f /tmp/${FILE_NAME}

AWSの合計月額料金を年月指定で取得するシェルスクリプト(バッチ処理プログラム)の実行例

引数が何もない場合は当月とみなして処理を行います。
例えば2017年2月中に実行した場合は引数無しの場合と「2017-02」を指定した場合は同じ結果となります。

[magtranetwork@localhost ~]# chmod 755 check_aws_total_cost.sh
[magtranetwork@localhost ~]# ./check_aws_total_cost.sh
{"StatementTotal": "$9.21", "Description": "Approximately Total Cost at 2017/02/09 14:57:58(UTC)"}
[magtranetwork@localhost ~]# /check_aws_total_cost.sh 2017-02
{"StatementTotal": "$9.21", "Description": "Approximately Total Cost at 2017/02/09 14:57:58(UTC)"}
[magtranetwork@localhost ~]# /check_aws_total_cost.sh 2017-01
{"StatementTotal": "$278.85", "Description": "Committed Total Cost at 2017/02/03 18:16:04(UTC)"}

応用の可能性

S3バケットに出力されるS3バケットには明細が記載された請求レポート、「リソースとタグを含む詳細な請求レポート」を有効にした場合に出力されるリソースのタグ毎の請求明細など詳細な請求額の分析ができる形式で請求レポートが出力されています。

S3バケットから請求レポートをダンロードし、数値を解析するという方法は、チーム毎のタグで請求額を集計したり、請求額をバッチ連携して先月と比較して通知したりなど、様々なケースで請求額の分析に応用できます。

Reference: Tech Blog citing related sources