Claude Media
Claude CodeのSavings Plansカバレッジ試算 — 買い増し量は安定使用量で決める

Claude CodeのSavings Plansカバレッジ試算 — 買い増し量は安定使用量で決める

Cost Explorer APIのカバレッジ、利用率、購入推奨のJSONをclaude -pに渡し、Savings Plansの買い増し量を安定使用量から試算します。RIとの指標の違いと、購入前の確認点もまとめました。

Savings Plans(SP)を買い増すかどうかは、カバレッジの数字だけでは決まりません。カバレッジが低くても、増やした分が使い切れなければ余りが出ます。決め手は、時間帯が変わっても下回らない安定使用量です。

Cost Explorer APIは、カバレッジ、利用率、購入推奨の3つをJSONで返します。この3つをClaude Codeの非対話モード(claude -p)に渡せば、買い増し量の試算まで一気に進みます。算術はスクリプトに任せ、Claudeには読み解きと確認項目の洗い出しを担当させる分担にします。

買い増しの前に見る指標は3つある

指標の役割は次のとおりです。

見るものAWS CLIコマンド答える問い
カバレッジAWS CLIコマンドget-savings-plans-coverage答える問い対象になる使用量のうち、SPで賄えている割合は
利用率AWS CLIコマンドget-savings-plans-utilization答える問いすでに買ったコミットを使い切れているか
購入推奨AWS CLIコマンドget-savings-plans-purchase-recommendation答える問いAWSが示す時間あたりコミット額の目安は

カバレッジの CoveragePercentage は、SPで賄えた使用量を、SP対象の使用量全体で割った割合です。計算はSP利用分のオンデマンド換算額に基づきます。利用率の UtilizationPercentage は逆向きの見方で、買ったコミットがどれだけ消費されたかを示します。未消費分は UnusedCommitment に出ます。

買い増しの順序はこうなります。先に利用率を見て、既存コミットが余っていないか確かめます。余っていれば、買い増しではなく現状の見直しが先です。次にカバレッジで未カバーの量を把握し、最後に購入推奨と突き合わせます。

3つのJSONを取得する

取得は人間のシェルで行い、Claudeには結果のファイルだけを渡します。AWS CLIのコマンドを許可する権限設計を省けるからです。

mkdir -p sp-review && cd sp-review
 
# 月次のカバレッジ(サービス別)
aws ce get-savings-plans-coverage \
  --time-period Start=2025-10-01,End=2026-10-01 \
  --group-by Type=DIMENSION,Key=SERVICE \
  > coverage-by-service.json
 
# 日次のカバレッジ(全体。granularityとgroup-byは併用できない)
aws ce get-savings-plans-coverage \
  --time-period Start=2026-09-01,End=2026-10-01 \
  --granularity DAILY > coverage-daily.json
 
# 日次の利用率
aws ce get-savings-plans-utilization \
  --time-period Start=2026-09-01,End=2026-10-01 \
  --granularity DAILY > utilization-daily.json

2本目のコマンドで --group-by を外しているのは、get-savings-plans-coverage が「GroupByを指定した場合はGranularityを指定できない」仕様だからです。利用率のほうは、そもそもどの軸でもグループ化できません。サービス別の利用率が欲しいときも、このAPIでは出せません。

期間にも制約があります。開始日は現在から13か月以内で、終了日は現在より前でなければなりません。つまり季節性を見るために使える遡り幅は約1年です。

購入推奨は期間を変えて3回取る

購入推奨は、取得の前に生成を依頼する2段構成です。公式の説明では、先に StartSavingsPlansPurchaseRecommendationGeneration で新しい推奨を作り、その後に GetSavingsPlansPurchaseRecommendation で取り出します。生成は1つの一括請求ファミリーあたり1日3回まで更新できます。

取得コマンドの必須オプションは、SPの種類、期間、支払い方法、遡り期間の4つです。遡り期間に指定できるのは7日、30日、60日の3択です。この3択を全部取るのが、このワークフローの肝になります。

aws ce start-savings-plans-purchase-recommendation-generation
 
for d in SEVEN_DAYS THIRTY_DAYS SIXTY_DAYS; do
  aws ce get-savings-plans-purchase-recommendation \
    --savings-plans-type COMPUTE_SP \
    --term-in-years ONE_YEAR \
    --payment-option NO_UPFRONT \
    --lookback-period-in-days "$d" > "rec-$d.json"
done

--savings-plans-type には COMPUTE_SP、EC2_INSTANCE_SP、SAGEMAKER_SP、DATABASE_SP が指定できます。ここでは例として COMPUTE_SP を使っています。

推奨は生成時点の使用量と契約済みSPの在庫を反映して作り直されます。取得ファイルには、いつ生成して取ったかを自分でファイル名や記録に残しておくと、後で突き合わせられます。

遡り期間ごとの差が「安定度」を教えてくれる

購入推奨の各要素には、次の値が入っています。

  • HourlyCommitmentToPurchase: 推奨される時間あたりのコミット額
  • EstimatedAverageUtilization: 推奨どおり買った場合の推定利用率
  • CurrentMinimumHourlyOnDemandSpend / CurrentAverageHourlyOnDemandSpend / CurrentMaximumHourlyOnDemandSpend: 遡り期間内の時間あたりオンデマンド支出の最小、平均、最大

3つの遡り期間で HourlyCommitmentToPurchase がほぼ同じなら、使用量が安定している目安になります。7日だけ突出しているなら、直近の一時的な負荷が推奨を押し上げている可能性があります。最小と平均が離れている場合も、時間帯による山谷が大きい状態です。

次の jq で3本を1表に並べます。

for d in SEVEN_DAYS THIRTY_DAYS SIXTY_DAYS; do
  jq -r --arg d "$d" '
    .SavingsPlansPurchaseRecommendation
      .SavingsPlansPurchaseRecommendationDetails[]
    | [$d, .HourlyCommitmentToPurchase, .EstimatedAverageUtilization,
       .CurrentMinimumHourlyOnDemandSpend,
       .CurrentAverageHourlyOnDemandSpend,
       .CurrentMaximumHourlyOnDemandSpend] | @tsv' "rec-$d.json"
done > rec-compare.tsv

出力は次のような形になります(数値は説明用の架空の値です)。

SEVEN_DAYS     6.20  97.1  4.10  7.80  15.30
THIRTY_DAYS    4.80  98.4  3.90  6.10  12.00
SIXTY_DAYS     4.70  98.6  3.80  5.90  11.50

この架空の例なら、30日と60日はほぼ一致し、7日だけ上振れしています。直近1週間の負荷が一時的に高かったと読み、30〜60日側の約4.7〜4.8ドル/時を候補にして、買い増し量の検討を進める形になります。

Claudeの非対話モードに読ませる

整理したファイルをClaudeに渡します。標準入力で渡せば、ファイルを読ませるための権限設定が不要です。公式ドキュメントでも、差分を標準入力に流すとBashの許可がいらなくなると説明されています。標準入力の上限は10MBなので、サービス別の月次JSONが大きいときはjqで必要な項目に絞ってから渡します。

出力はスキーマで固定します。--output-format json に --json-schema を組み合わせると、結果が structured_output フィールドに入ります。

{
  echo "## utilization"; jq -c '.Total' utilization-daily.json
  echo "## coverage-daily"
  jq -c '.SavingsPlansCoverages[]
    | {d: .TimePeriod.Start, p: .Coverage.CoveragePercentage}' coverage-daily.json
  echo "## recommendations"; cat rec-compare.tsv
} | claude -p "上の3種類の指標から、買い増し前に確認すべき点を挙げる。\
金額の計算はしない。計算が要る箇所は式だけ書く" \
  --output-format json \
  --json-schema '{"type":"object","properties":{"commitment_stable":{"type":"boolean"},"unused_risk":{"type":"string"},"checks_before_purchase":{"type":"array","items":{"type":"string"}}},"required":["commitment_stable","unused_risk","checks_before_purchase"]}' \
  | jq '.structured_output'

プロンプトで「金額の計算はしない」と縛っているのは、CLIの出力値が文字列で返るためです。ドキュメント上、HourlyCommitmentToPurchase や UtilizationPercentage はいずれも文字列型です。小数の掛け算を言語モデルに任せると桁がずれる余地があるので、数値の合成は jq かスクリプトで行い、Claudeには「何を確かめるべきか」の抽出を頼みます。

公式ドキュメントによれば、スキーマに合う結果が得られなかった場合は structured_output フィールド自体が付きません。スクリプトに組み込むときは、フィールドの存在を先に確認してください。

買い増し量は「下回らない時間額」から逆算する

コミットは時間あたりの金額で買います。使い切れない時間帯があれば、その分は未消費のコミットになります。ここから、試算は次の順序になります。

  1. 安定使用量を決める。遡り期間3本の最小オンデマンド支出と平均を見て、下回らない水準を見積もる
  2. 既存コミット額を引く。get-savings-plans-utilization の TotalCommitment が現在のコミット額で、買い増しはその上積みになる
  3. 候補額を推奨値と比べる。HourlyCommitmentToPurchase と自分の見積もりの差が大きければ、前提(対象サービスや期間)がずれていないか確かめる
  4. 推奨額との差で利用率の下がり方を見る。EstimatedAverageUtilization は推奨額で買った場合の推定値で、自分の候補額に対する値ではない。候補額が推奨額より大きいときは、この値より利用率が下がる側に振れるので、差額分を最小オンデマンド支出の範囲で消費できるか確かめる

注意点は金額の基準です。カバレッジの計算はオンデマンド換算額に基づきます。オンデマンド支出の最小値と、コミット額が同じ基準の金額なのかは、出力項目の説明だけでは判断できません。両者を直接引き算せず、自作の見積もりは推奨値の妥当性を確かめる用途に留めます。

RIは指標が時間ベースで、見方が少し違う

リザーブドインスタンス(RI)も同じ型で確認できます。違いは指標の単位です。

項目Savings Plansリザーブドインスタンス
カバレッジSavings Plansget-savings-plans-coverage(金額ベース)リザーブドインスタンスget-reservation-coverage(--metrics にHour、Unit、Cost)
利用率Savings Plansget-savings-plans-utilizationリザーブドインスタンスget-reservation-utilization
余りの量Savings PlansUnusedCommitmentリザーブドインスタンスUnusedHours
購入推奨Savings PlansSPの種類・期間・支払い方法を指定リザーブドインスタンスget-reservation-purchase-recommendation(--service が必須)

RIの利用率には、購入した時間(PurchasedHours)と使用した時間(TotalActualHours)、未使用時間(UnusedHours)が含まれます。純粋な節約額は NetRISavings で、OnDemandCostOfRIHoursUsed から TotalAmortizedFee を引いた値です。RI側のカバレッジは対象がEC2、ElastiCache、RDS、Redshiftに限られます。

なお get-reservation-utilization が受け付けるグループ化は SUBSCRIPTION_ID のみです。個々の予約ごとの余りを見たいときは、この軸で分けて取ります。

購入前に確かめる6項目

試算結果をそのまま発注に回さず、次の6点を確認します。

発注前のチェック

  • 既存コミットの利用率が100%近くで、余りが出ていない
  • 遡り期間7日、30日、60日の推奨額が大きくずれていない
  • 日次カバレッジで、平日と休日の落差が想定内に収まっている
  • 13か月のカバレッジに、季節的な落ち込みの月がない
  • 近いうちに止める予定のワークロードが、対象サービスに含まれていない
  • 期間(1年か3年)と支払い方法の選択を、購入担当と合意している

推奨の遡り期間は最長60日です。つまり購入推奨だけでは、年に一度の閑散期は見えません。そこで13か月のカバレッジを補助線にします。月次カバレッジを --group-by なしで取り、最小の月に当たる使用量が推奨の前提を下回っていないかを、Claudeに表の形で列挙させる使い方が向いています。

落とし穴

推奨額をそのまま買うと、閑散期に余る

遡り期間の推奨は直近60日以内の使用量から作られます。年末年始や夏季など、60日より外の閑散期は推奨に反映されません。13か月の月次カバレッジで落ち込み月を確認し、その月の使用量でも消費できる額かを見てから決めます。

定期実行にするときの境界

月1回のレビューに組み込むなら、取得スクリプトと claude -p を同じシェルにまとめます。AWSの認証は専用の読み取りプロファイルに絞ります。

  • 許可するのは参照系の ce:Get* と、推奨の生成(ce:StartSavingsPlansPurchaseRecommendationGeneration)だけにする
  • 生成コマンド(start-savings-plans-purchase-recommendation-generation)は1日3回までの更新制限があるため、リトライ処理に入れない
  • 購入は人が行い、スクリプトには含めない

請求データをMCP経由でClaudeに直接読ませる構成は、AWS Billing MCPサーバーの手順にまとめています。今回のようにJSONを前処理して渡す構成と比べると、MCPはClaudeが質問のたびにAPIを呼ぶ点が違います。急増の原因を分解したい場合はCSV起点の手順が向いています。

claude -p の基本オプションはClaude Codeのヘッドレスモードで整理しています。ログなどの大きな入力を渡すときの上限の扱いはclaude -pの10MB上限を参照してください。

まとめ

買い増しの判断は、カバレッジを上げることではなく、安定使用量にコミットを合わせることです。そのために、利用率で既存の余りを確かめ、遡り期間の違う3つの推奨で安定度を測り、13か月のカバレッジで閑散期を確認します。

Claudeに任せるのは、出力の読み解きと確認項目の洗い出しまでです。金額の合成は jq に寄せ、購入の最終判断は人が持ちます。この線引きなら、数字の取り違えと意図しない購入の両方を避けられます。

参照した一次ソース

この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。

  1. code.claude.com/docs/en/headless
  2. docs.aws.amazon.com/cli/latest/reference/ce/get-savings-plans-coverage.html
  3. docs.aws.amazon.com/cli/latest/reference/ce/get-savings-plans-utilization.html
  4. docs.aws.amazon.com/cli/latest/reference/ce/get-savings-plans-purchase-recomme…
  5. docs.aws.amazon.com/cli/latest/reference/ce/get-reservation-utilization.html
この記事を共有:XはてブLinkedIn