一覧に戻る
    AWSコスト監視ダッシュボードの値が、コンソールと2倍違っていた話 — 「リセラー配下アカウント」の仕様に行き着くまで
    開発ラボ
    PRこの記事には広告が含まれています

    AWSコスト監視ダッシュボードの値が、コンソールと2倍違っていた話 — 「リセラー配下アカウント」の仕様に行き着くまで

    (更新日: 2026/5/8)
    28 分で読める

    はじめに

    以前の記事で、ce:GetCostForecast を使った AWS コスト予測の話を書きました。

    そこで紹介した仕組みを使って、社内ではいくつかの AWS アカウントの月次コストを Jenkins のモーニングチェックジョブで毎朝集計し、Teams にレポートする運用を続けています。「当月実績」「月末予測」「前月比」をダッシュボードで一目で見られる、という比較的ありふれた構成です。

    ところが先日、新しい AWS アカウントを監視対象に追加したところ、思わぬ場面で足を止めることになりました。

    「レポートに出ている値と、AWSコンソールに出ている値が、約2倍違う」

    この記事はその違和感を出発点に、最終的に 「対象アカウントが AWSリセラー配下である」 という構造的な事実に行き着くまでの調査ログです。

    結論だけを知りたい方には少し遠回りに感じるかもしれませんが、この種の問題は 「最初に立てた仮説をどう潰していくか」 がそのまま再現性のある知見になるので、検証プロセスもなるべく残しました。


    違和感の発見

    新しい AWS アカウント(以下「アカウントA」)を追加した直後のレポートはこうなっていました。

    • 当社レポートの「当月実績」: $404.60
    • AWS コンソールの「当月」表示: $807.20

    ほぼぴったり 2 倍です。一目見て「単位ミスかな?」と思えるくらいきれいな倍率で、これがかえって違和感を強くしました。

    最初の仮説はシンプルでした。

    「複数リージョンの金額が考慮されていないんじゃないか」

    確かに、対象アカウントは複数リージョンを使っています。ところが、コードを見直してみるとこれはすぐに違うとわかりました。

    response = ce_client.get_cost_and_usage(
        TimePeriod=time_period,
        Granularity="DAILY",
        Metrics=["UnblendedCost"],
    )

    Cost Explorer の GetCostAndUsageそもそもアカウント単位の集計 API で、リージョンは Filter を明示しない限り全リージョン合算です。boto3 セッションのリージョン指定は API エンドポイント選択にしか使われておらず、取得値の絞り込みには影響しません。

    ここで一度、仮説リストを書き出して整理することにしました。

    「2倍ピッタリ」を素直に説明できそうなのは、仮説2(Savings Plans 周り)と 仮説3(Linked Account のロールアップ)の2つに絞られました。


    仮説検証 — Savings Plans の影響を疑う

    最初に疑ったのは Savings Plans(SP)と Reserved Instance(RI)の見え方です。

    当社レポートは UnblendedCost を使っており、コンソールは表示モードによって AmortizedCost を含む値を返すことがあります。SP / RI を購入していると、この差が大きく出るケースがあります。

    そこで、Cost Explorer API に直接同条件のクエリを投げて確かめました。

    # レポートと同条件
    aws ce get-cost-and-usage \
      --time-period Start=2026-05-01,End=2026-05-08 \
      --granularity DAILY \
      --metrics UnblendedCost
    
    # AmortizedCost で同期間
    aws ce get-cost-and-usage \
      --time-period Start=2026-05-01,End=2026-05-08 \
      --granularity DAILY \
      --metrics AmortizedCost

    結果は意外なものでした。UnblendedCostAmortizedCost が完全一致

    つまり SP / RI の影響を疑った瞬間にこの仮説は潰れます。両者が一致するということは、SP の前払い按分による表現差は存在せず、SP/RI が原因ではないと確定的に否定できます。

    念のため RECORD_TYPE 別にブレイクダウンしてみたところ、こうでした。

    RECORD_TYPEUnblendedCostAmortizedCost
    Usage$243.48$243.48
    SavingsPlanCoveredUsage$113.60$95.88
    SavingsPlanNegation-$113.60$0.00
    SavingsPlanRecurringFee$112.73$16.85
    Tax$64.50$64.50
    合計$420.70$420.70

    SavingsPlanCoveredUsage (+113.60)SavingsPlanNegation (-113.60)UnblendedCost で相殺されており、Amortized でも按分後の合計が一致するため、当然合計は一致します。Savings Plans があってもなくても、CE API から見える値は $420.70 で揺るがない ということがはっきりしました。

    ここで、ひとつ重要な発見もありました。日次の合計($420.70)は当社レポートの $404.60 とほぼ整合しています(差分は集計対象日の1日ずれ分)。当社のレポート値は Cost Explorer の生データと完全に一致している。つまり、こちらの計算は壊れていない。


    Linked Account のロールアップを疑う

    次に Linked Account を疑いました。AWS Organizations を使っている場合、コンソールの「Billing Home」トップが org payer 視点だと、対象アカウントに他の連結アカウントが合算される表示になることがあります。

    これも CE API で LINKED_ACCOUNT 別に集計してみました。

    aws ce get-cost-and-usage \
      --time-period Start=2026-05-01,End=2026-05-08 \
      --granularity MONTHLY \
      --metrics UnblendedCost \
      --group-by Type=DIMENSION,Key=LINKED_ACCOUNT

    結果は アカウントA 単独で $420.70、他の連結アカウントは含まれず。コンソールが見せている $807.20 を作り出せる別アカウントの混入はありません。

    ここで一度立ち止まりました。

    「数学的に該当する解釈が見当たらない」

    これは正直、けっこう焦る瞬間です。仮説をいくつか潰した時点で、自分の中の "これだろう" の在庫が尽きるような感覚があります。

    整理してみると、コンソールが「当月 $807.20」と表示している裏側で、API ベースの値はこうなっていました。

    比較対象API 値コンソール表示整合
    月末予測$1881.69(4月実績ベース)$1881.69(▼4%)
    過去月(Mar/Apr)$2014 / $1968グラフは ~$2000
    当月(5/1-5/7)$420.70$807.20❌ 約 1.92 倍
    4月同期(4/1-4/7)$578.90$578.90

    おもしろいのは、過去月や月末予測は完全に整合しているのに、「当月 MTD」だけがコンソールで高く出ている という点でした。

    「当月の表示だけ別ロジックで動いている」と仮定すると、こちらは API では再現できないことになります。ここで初めて、 「コンソールが見ている世界そのものが、こちら側からは見えていない」可能性 が頭をよぎりました。


    決定打 — Organization 構造を見にいく

    行き詰まりかけたところで、ふと思いついたのが aws organizations describe-organization でした。これは「このアカウントがどの Organization の配下にあるか」を返す API です。

    aws organizations describe-organization

    返ってきたレスポンスを見て、視界が一気に変わりました。

    {
      "Organization": {
        "Id": "o-xxxxxxxxxx",
        "MasterAccountId": "<別の事業者のアカウントID>",
        "MasterAccountEmail": "<...@example-reseller.example>",
        ...
      }
    }

    対象アカウントは、当社が AWS と直接契約しているアカウントではなく、ある AWSリセラー(再販パートナー)のメンバーズプログラム経由で発行された "メンバーアカウント" だった。

    つまり、課金フローはこうなっていたわけです。

    メンバーアカウント側の CE API は、AWS から見た「このアカウントへの実課金原価」を返します。これが $420.70。

    一方、AWS コンソールの「コストと使用状況」ウィジェットは、payer 側で計算された集約値、おそらくはディスカウント適用前の list price 相当を「当月」表示として参照している。これが $807.20。

    割引前 vs 割引後の関係としてみたとき、1.92 倍という比率はリセラーのマークアップ・割引構造として違和感のない数字でした。


    仮説の確定 — 他アカウントとの相関で裏取り

    ここまでで「リセラー配下のアカウントだから」という仮説が立ちましたが、1 件だけだと "そういうこともあるかもね" 止まりです。社内には他にも本番アカウントが複数あったので、それぞれを確認してもらいました。

    アカウントコンソール乖離Master Account同一 Org か
    アカウントAありリセラーの payer (Account-X)リセラー配下
    アカウントBあり同上リセラー配下
    アカウントCなし(別 / 直契約相当)

    「乖離あり = リセラー payer 配下」 という相関がきれいに成立しました。これで仮説は確定です。

    【追記:2026-05-08】 この結論は、後日の検証で「半分しか正しくなかった」ことが判明しました。実際にはアカウントCもリセラー配下で、リセラー配下でも乖離しないアカウントが多数存在することがわかり、判定軸を Savings Plans 適用の有無 に再設計しています。経緯は続編記事を参照してください:「リセラー配下が原因」は半分正しかった — 続編で覆った前提と、Savings Plans 検出への再設計

    ここまできて、ようやくコードや設定の側の疑いが完全に晴れました。当社モニタリングのコードは、UnblendedCost を使ってアカウント単位の実課金原価を取得しており、これは AWS から見た正規の実コストです。


    「コンソールと同じ値を取れる経路はないのか?」

    「壊れていなかった」とわかったときの安堵と、ちょっとした寂しさが同時にきました。原因を作ったのは自分のコードじゃない、けれど、原因がここにある以上、もうここから先は技術ではどうにもならない領域に入ることが見えたからです。

    ここから先は半分以上「お作法の話」になります。報告を受け取る側からすれば、当然こう聞きたくなります。

    「じゃあ、コンソールと同じ値を取得する方法はないの?」

    正直に言うと、メンバーアカウント側の API から、当月の Console 値を確実に取得する経路は見つけられませんでした。 検討した経路と、それぞれが行き止まりだった理由を残しておきます。

    経路A: Cost Explorer API で他のメトリクスを試す

    UnblendedCost 以外に AmortizedCost / BlendedCost / NetUnblendedCost / NetAmortizedCost がありますが、すべて同一値($420.70)を返しました。

    RECORD_TYPE / USAGE_TYPE / SERVICE どの粒度で集計してもコンソール値には届きません。payer 側集約や list price 換算の影響は CE API には露出しない ので、この方向では再現不可能です。

    経路B: AWS Invoicing API

    2024 年に追加された aws invoicing list-invoice-summaries で確定済みインボイスを取得できることを思い出し、これに賭けました。

    aws invoicing list-invoice-summaries \
      --selector ResourceType=ACCOUNT_ID,Value=<account-id> \
      --filter '{"BillingPeriod":{"Year":"2026","Month":"5"}}'

    ところが結論を先に書くと、当月分は仕様上取得できません

    • AWS のインボイスは billing period 終了後に発行される
    • 月次インボイスが finalized されるのは翌月初頭(1〜3 日頃)
    • 当月分はクエリしても結果なし(空配列が返る)

    つまりコンソールが見せている当月値は インボイスではなく、コンソール独自のリアルタイム算出値 です。

    さらに踏み込むと、リセラー配下では 「メンバーアカウントから見える AWS インボイス」自体が存在しない可能性が高い ということもわかりました。AWS から発行されるインボイスの受領者は payer であって、メンバーではないからです。

    経路C: Cost and Usage Report (CUR)

    pricing/publicOnDemandRate × lineItem/UsageAmount から on-demand-equivalent を再計算でき、コンソール表示に近い値が得られる可能性があります。ただしこちらにも壁があります。

    • CUR の有効化は payer 側の操作
    • メンバーアカウントから enable できない
    • リセラー側にエクスポートをリクエストする必要があり、Jenkins ジョブの自動化コストに見合わない

    経路D: リセラー独自の管理 API

    最後に残るのが、リセラーが提供している独自の管理コンソール / API です。ここから「インボイス相当値」が取れる可能性はありますが、AWS 標準 API の範疇外であり、認証も別系統。Jenkins ジョブで自動取得したい場合は、リセラー側の連携可否と仕様確認が前提になります。


    何を「コスト監視」と呼ぶかの整理

    ここまでの調査を踏まえて、「では何を監視値とすべきか」をチームで話す段になりました。

    ポイントは、 コンソール値と API 値は "どちらが正しいか" の話ではなく、"何を意味しているか" が違う という点です。

    意味用途
    CE API の UnblendedCostAWS が当該アカウントに計上する実課金原価監視・予測・前月比比較
    コンソールの「当月」表示payer 集約後の参考値(list price 相当の可能性)何を意味しているかが特定できない
    リセラーの月次インボイスリセラーが当社に請求する金額(マークアップ・割引込)経理・支払金額

    監視ジョブの目的が「コストの異常な急増・急減を検知すること」「来月の予測を出すこと」であれば、CE API の値で十分機能します。実際、月末予測や過去月の bar chart はコンソール側と完全に整合していました。

    一方で、「リセラーに支払う額そのものを追跡したい」となると、それはもう CE API では取れない領域なので、別経路でインボイス連携を構築するしかありません。

    ここに気づいたとき、ようやく「これはコードのバグではなく、概念整理の問題だった」と腹に落ちました。


    知見をどこに残すか — troubleshooting への追記

    調査が終わってまず気になったのは、「次に同じ疑問にぶつかった人(未来の自分含む)が、どこを見れば辿り着けるか」 でした。

    そこで、運用ドキュメントの troubleshooting.md に「AWS コストチェック関連」セクションを新設し、以下を記載しました。

    • 現象: ダッシュボードの当月値とコンソール「コストと使用状況」当月値の乖離
    • 原因: 対象アカウントがリセラー配下である場合に発生する仕様
    • 判別方法: aws organizations describe-organizationMasterAccountId を確認
    • 確認済みアカウント表: 当社運用中のアカウントのうち、どれが該当するか
    • 対処: コードや設定の修正は不要。CE API 値が実課金原価で正
    • 切り分けクエリ: UnblendedCostAmortizedCost の比較で SP/RI 影響を確認するコマンド

    さらに「コンソール値を API で取れる方法はあるか」という小見出しも追加し、上記4経路(CE API / Invoicing / CUR / リセラー独自 API)をそれぞれ「なぜ取得できないか」とセットで残しました。

    未来の自分が同じ疑問を抱いたとき、「2 時間で同じ仮説を全部潰し直す」のではなく、「30 分で結論まで読める」状態にしておきたかったからです。


    振り返り — 仮説を潰す順序と「コードの外」に答えがあること

    この調査を振り返ると、いくつかの学びが残りました。

    仮説を「潰しやすいもの」から潰す順序の効用

    最初に複数リージョンや SP/RI を疑ったのは、いずれも CE API のメトリクス変更だけで検証できる軽い仮説だったからです。コードや設定変更を伴わないので、間違っていてもダメージが小さい。重い仮説(アーキテクチャ / 契約構造)を疑う前に、軽い仮説をスキャンしてしまうのは、回り道のようでいて結果的に近道になりました。

    「コードの外側」に答えがあるパターン

    コード・設定が同じパターンで動いている既存アカウントが他にもある中で、新アカウントだけで現象が出る。となれば、コードを疑う前にまず 「アカウントそのものの素性が違うのでは?」 を疑う、というのは今回の経験で身につけた感覚です。今後同種の問題が出たときには、aws organizations describe-organization を最初の数手のうちに叩く、という手筋が増えました。

    「壊れていなかった」と確認することの価値

    調査の半分くらいは「自分たちのコードは壊れていない」ことの検証に使われました。バグを見つけて直すよりも地味な作業ですが、 「正しく動いていることを根拠つきで言える」状態にすること自体が運用の価値 だと改めて感じます。バグが無かったとわかった瞬間に少し寂しさを感じたのは、このプロセスの裏返しでもありました。

    「これは仕様だ」で終わらせないドキュメンテーション

    最初に troubleshooting に書いた1段落だけでは、「コンソールと違うのは仕様です」で終わってしまい、次の人が「じゃあコンソールと同じ値を取る方法は?」と問い直したときに行き止まります。問いかけの分岐ごとに「どこまで調べたか」「なぜ取れないか」を残すと、ドキュメントが「結論の倉庫」ではなく「思考の地図」になる。今回はそれを意識して書きました。


    おわりに

    「2 倍違う」という違和感は、最初は単純なバグの匂いがしました。けれども最後まで追いかけてみると、コードでもなければ設定でもなく、「そのアカウントが、誰と AWS の契約を結んでいるか」 という、もっと手前の構造の話でした。

    技術的な問題が技術の外側にしか答えを持っていないことは、ときどき起こります。そのとき、コードを開く前に一度立ち止まって「この前提、本当に同じか?」と問い直せるかどうかは、運用の練度にかなり関わってくる気がしています。

    今後は、新しい AWS アカウントを監視対象に追加するときは「それがリセラー配下か直契約か」を最初に確認するフローをチェックリストに加えました。違和感をきっかけに増えたチェック項目は、それ自体がこの調査の副産物として一番役に立つかもしれません。


    関連書籍

    今回のような AWS 運用・監視ダッシュボード設計・ドキュメンテーションについて、さらに学びたい方におすすめの書籍です。

    この記事は役に立ちましたか?

    Coffee cup

    この記事が、何かの整理につながったら

    コーヒー1杯分の応援をもらえると嬉しいです。

    ※ これは応援とは別の話ですが、

    同じようなテーマを自分の文脈で整理したい場合は、 (文章だけだと詰まりやすい人向けに) 思考整理の壁打ちという形で対話の時間も取っています。

    対話の時間について