Claude Media
ClaudeでAWSのタグ別showbackレポートを作る — 未配賦の原因を月次で追う

ClaudeでAWSのタグ別showbackレポートを作る — 未配賦の原因を月次で追う

AWSのCost and Usage Reportをチーム単位のタグで集計し、タグのない未配賦コストの割合と原因をClaudeに仕分けさせる月次の手順です。集計はスクリプト、原因づけはClaudeと分けます。

showback(チーム別のコスト可視化)の月次レポートは、AWSのCost and Usage Report(CUR)をチームタグで集計し、タグのない「未配賦」がどれだけ残ったかを示す形で作れます。ここでの分担は、集計をPythonスクリプトに固定し、未配賦の原因づけと配賦ルール案の下書きをClaudeに任せる、というものです。数字を出す役はコードに、読み解く役はClaudeに置きます。

事業損益の共通費をExcelで配る話は事業別損益の共通費配賦の記事にあります。ここで扱うのはクラウドの請求データで、配る前に「そもそもタグが付いていない請求」を洗い出す作業です。

全行をClaudeに読ませず、集計結果だけを読ませる

CURは行数が多くなりがちです。リソース単位の出力(INCLUDE_RESOURCES)を有効にすると、サービスごとの合計ではなくリソースごとに行ができ、行数もファイルサイズも大きく増えます。この全行をチャットに貼っても、合計は合いません。

分け方は単純です。

  • 集計(チーム別合計、未配賦の割合、未配賦の内訳)は pandas のスクリプトで出す
  • Claudeには、集計後の小さなCSVと、未配賦の内訳だけを渡す
  • Claudeの役割は「この未配賦はなぜ生じたか」の仮説と、配賦ルール案の下書きに絞る

こうしておくと、毎月同じスクリプトを回すだけで前月と比べられます。Claudeが数字を暗算で出すことも避けられます。

先にタグの状態を確かめる

未配賦の多くは、タグの運用で決まります。集計の前に、AWS側の仕様を3つ押さえておきます。

仕様

未配賦を生むタグの仕様

  • 有効化しないと出ない

    ユーザー定義タグは、Billing and Cost Managementコンソールの「Cost allocation tags」でキーを有効化して初めて請求レポートに出ます。タグを付けてから有効化の画面に現れるまで最大24時間、有効化されるまでさらに最大24時間かかります。

  • 遡って付かない

    タグは、タグを作る前に作成されたリソースには適用されません。有効化したのが9月でも、8月分の行にはタグ列が空のまま残ります。

  • レポート上の名前

    ユーザー定義タグは、レポート上で user: の接頭辞が付いた列になります。aws: はAWS生成タグの予約接頭辞で、利用者は付けられません。

有効化の操作ができるのは、組織の管理アカウント、または組織に属さない単独アカウントだけです。メンバーアカウントがほかの組織へ移ると、有効化済みだったタグは非アクティブに戻り、新しい管理アカウントで有効化し直します。

過去月を揃えたいときは、タグのバックフィルを使います。最大12か月分まで、現在の有効化状態を過去月に適用できます。ただしリソースに過去から同じタグが付いていることが条件で、付いていなかった月に値が生えることはありません。依頼は24時間に1回までで、反映はCost Explorer、Data Exports、CURの更新サイクル(24時間に1回)を待ちます。

集計スクリプトを固定する

次のスクリプトは、チームタグ別の合計と、未配賦の内訳を2つのCSVに書き出します。列名は、書き出し方や設定で変わります。先頭の定数は、自分のCURのヘッダーを開いて合わせてください。

import pandas as pd
 
TAG = "resourceTags/user:Team"    # 配賦に使うタグの列
COST = "lineItem/UnblendedCost"   # 集計するコスト列
KIND = "lineItem/LineItemType"    # 課金の種類(Usage, Tax など)
SERVICE = "product/ProductName"   # サービス名の列。自分のCURで確認
 
df = pd.read_csv("cur-2026-09.csv", usecols=[TAG, COST, KIND, SERVICE])
 
# 空白と大文字小文字のゆれをそろえ、空のものを未配賦にする
tag = df[TAG].fillna("").str.strip().str.lower()
df["team"] = tag.where(tag != "", "(未配賦)")
 
total = df[COST].sum()
by_team = df.groupby("team")[COST].sum().sort_values(ascending=False)
summary = by_team.to_frame("cost")
summary["share"] = (summary["cost"] / total).round(4)
summary.to_csv("team_summary.csv")
 
untagged = df[df["team"] == "(未配賦)"]
untagged.groupby([KIND, SERVICE])[COST].sum() \
    .sort_values(ascending=False).head(30) \
    .to_csv("untagged_by_service.csv")
 
print(f"total={total:.2f}  untagged_share={summary.loc['(未配賦)', 'share']:.1%}")

列名の書き方には注意が要ります。CUR 2.0は列の集合を固定し、一部の列をキーと値の入れ子の列にまとめます。入れ子のキーをオプションで個別の列に展開すると、従来のCURの形に近づけられます。どちらの形式で出したかで、TAG に入れる列名は変わります。

lineItem/LineItemType を集計の軸に入れているのは、未配賦の中身を切り分けるためです。この列には Usage、Tax、Credit、Refund、Fee、SavingsPlanCoveredUsage、SavingsPlanNegation などが入ります。税やクレジットはリソースに紐づかないので、タグがなくて当然の行です。

合計が請求書と合うかを先に見る

集計の直後に、total をBillsページの合計と突き合わせます。コスト配分レポートはタグ付きとタグなしの両方を含み、請求サイクルの終わりには、その合計がBillsページなどほかの請求レポートの合計と一致する仕様です。ここが合わないなら、使っているコスト列の選び方か、月の取り違えを疑います。割引がある月は、割引後の lineItem/NetUnblendedCost が出力に加わります。どの列で請求書と合わせるかを決めて、毎月同じ列で回します。

未配賦の原因づけをClaudeに任せる

集計ができたら、team_summary.csv と untagged_by_service.csv の2枚だけをClaudeに渡します。Claude Codeを使うなら、2枚を置いた summary/ フォルダだけを読める状態にします。元のCURは、作業ディレクトリに置かないのが無難です。

summary/team_summary.csv と summary/untagged_by_service.csv を読んで。
1. 未配賦の割合と、上位の費目を3行で要約する
2. LineItemType と サービス名から、未配賦の原因を次の4分類に振る
   (a)タグの付け忘れ (b)タグ有効化前の月
   (c)リソースに紐づかない課金 (d)対応が不明
3. 各分類に、根拠にした行を引用する
4. (d)は推測で埋めず、人が調べる項目として列挙する
数値はCSVにある値だけを使い、再計算した値には「再計算」と書く。

4つ目の指示が要です。原因が分からない行を推測で埋めさせると、レポートが説明のつかない数字を断定します。「不明」を出口として用意しておけば、人が調べる行だけが残ります。

返ってくる内容の形は、たとえば次のようになります。数値は形を示すための架空の値です。

分類根拠の行割合(未配賦内)次に誰が動くか
(c)紐づかない課金根拠の行Tax、Credit割合(未配賦内)22%次に誰が動くか配賦ルールで決める
(a)付け忘れ根拠の行Usage、EC2、S3割合(未配賦内)55%次に誰が動くか各チームにタグ付けを依頼
(b)有効化前根拠の行8月分の Usage 全般割合(未配賦内)15%次に誰が動くかバックフィルを検討
(d)不明根拠の行共通のネットワーク系割合(未配賦内)8%次に誰が動くか基盤チームに確認

この分類は、Claudeの出力を鵜呑みにするためのものではありません。根拠に引いた行を、実際のCSVで1つずつ開いて確かめる作業が残ります。

落とし穴

Marketplaceの行は、元のEC2のタグを引き継ぐ

AWS Marketplaceの課金行には、関連するAmazon EC2インスタンスのタグが付きます。EC2側のタグが抜けていると、Marketplaceの購入分もまとめて未配賦に出ます。分類が(a)でも原因はMarketplaceの行そのものでなく、EC2のタグ漏れです。

配賦ルール案を作らせる

未配賦を「ゼロにする」ことはまず現実的ではありません。税、サポート、共通ネットワークのように、特定のチームに紐づかない費用が残るためです。そこで、配賦の方針をルールとして文章に落とし、人が承認する流れにします。

Claudeには次の3種類の中から、費目ごとに案を出させます。

ルール向く費目注意点
直接配賦向く費目タグが付いたリソース注意点タグの値のゆれ(大文字小文字、綴り)を先にそろえる
比例配賦向く費目税、クレジット注意点タグ付き分の比率で按分する。比率の根拠は月ごとに変わる
共通基盤として別枠向く費目共有ネットワーク、監視注意点配らずに別枠で見せる選択肢もある

比例配賦は、各チームの金額を「タグ付きの合計」で割った比率で未配賦を按分する方法です。計算はスクリプトに足します。

tagged = summary.drop(index="(未配賦)")
ratio = tagged["cost"] / tagged["cost"].sum()
allocated = tagged["cost"] + ratio * summary.loc["(未配賦)", "cost"]

この計算で、全チームの配賦後の合計は元の total に一致します。一致しなければ、スクリプトのどこかで行が落ちています。

コンテナ上でEC2を共有している場合は、CUR 2.0の設定 INCLUDE_SPLIT_COST_ALLOCATION_DATA を有効にすると、1台のインスタンスの費用をコンテナ単位に割り当てるデータ(split_line_item_* の列)が加わります。タグだけでは分けられない共有インスタンスは、この機能で分けられるかを先に調べると、人手の按分が減ります。

Claude APIで集計も任せる場合

スクリプトを自分で回す代わりに、Claude APIのコード実行ツールにCSVを渡して、集計から任せる方法もあります。ファイルをFiles APIにアップロードし、container_upload ブロックで参照します。

import anthropic
from pathlib import Path
 
client = anthropic.Anthropic()
up = client.files.upload(file=Path("cur-daily-2026-09.csv"))
 
resp = client.messages.create(
    model="claude-opus-5-5",
    max_tokens=4096,
    messages=[{
        "role": "user",
        "content": [
            {"type": "text", "text": "チームタグ別に合計し、未配賦の割合を出して"},
            {"type": "container_upload", "file_id": up.id},
        ],
    }],
    tools=[{"type": "code_execution_20250825", "name": "code_execution"}],
)

コードはAnthropic側のサンドボックスで実行され、pandas と openpyxl などがあらかじめ入っています。選ぶ前に、次の条件を見比べてください。

観点ローカルのスクリプト + Claude Codeコード実行ツール
請求データの置き場ローカルのスクリプト + Claude Code手元に残るコード実行ツールFiles APIとコンテナに置かれる
保持期間ローカルのスクリプト + Claude Code自分で決めるコード実行ツールコンテナのデータは最長30日
ZDR(ゼロデータ保持)ローカルのスクリプト + Claude Code該当しないコード実行ツール対象外
実行環境の制約ローカルのスクリプト + Claude Code手元のマシン次第コード実行ツールメモリ5GiB、ディスク5GiB、CPU 1つ。外部通信なし
料金ローカルのスクリプト + Claude Codeトークン代のみコード実行ツール実行時間課金(月1,550時間まで無料、超過は1コンテナ1時間0.05ドル、最小5分)

CURにはアカウントIDやリソースIDが含まれます。コード実行ツールに渡すなら、日次に集計したうえで、アカウントIDを仮名に置き換えたCSVにするのが安全です。ZDRの対象外という条件は、請求データを社外に出さない社内規程があるチームでは、選ぶ段階で効いてきます。

もう1点、料金の細部があります。ファイルをリクエストに含めると、ツールを呼ばなくてもファイルがコンテナに事前読み込みされるため、実行時間は課金されます。毎月1回の定例なら無料枠に収まる量ですが、確認用に何度も回すときは気にしておく点です。

毎月の手順

手順

月次showbackの流れ

  1. 1

    CURを出す

    前月分が確定してから出力します。タグを有効化したばかりの月は、バックフィルの結果が反映されるまで24時間ほど待ちます。

  2. 2

    スクリプトで集計する

    team_summary.csv と untagged_by_service.csv を作り、total をBillsページと照合します。

  3. 3

    Claudeで原因づけする

    2枚のCSVだけを渡し、4分類と根拠の行を出させます。(d)の行は人が調べます。

  4. 4

    ルール案を承認する

    比例配賦の対象費目と別枠にする費目を、財務と各チームの責任者で決めます。

  5. 5

    タグの穴を塞ぐ

    付け忘れの上位リソースに、各チームがタグを付けます。

未配賦の割合は、毎月同じ表に並べると傾向が見えます。何%までなら許容するかは、自社で決める閾値です。ここに汎用の目安はありません。

使われなくなったタグ名は、コンソールの「Last used month」列で見つかります。タグキーが最後に使われた月で、現在どのリソースにも付いていないキーは - と表示されます。チームの統廃合のあとに、古いキーを整理する材料になります。

タグとCUR周りの落とし穴

  • タグに機密情報を入れない。タグはレポートに列として載ります
  • 同じキーを1つのリソースに2回付けることはできず、リクエストは拒否されます。大文字小文字違いの別キー(Team と team)を作ると、別の列に分かれて集計が割れます
  • Credit や Refund は、請求の確定後に追加されることがあります。確定後に月をまたいで数字が動くのは、このためです
  • Cost ExplorerのCSVで使用タイプまで掘る作業は、別の手順です。急増の原因調査はCost ExplorerのCSVを使用タイプまで分解する手順で扱っています
  • 予算と実績の差異をExcelで追うなら、月次予実対比レポートの差異分析の流れが使えます

まとめ

showbackの精度は、Claudeの賢さより、タグの有効化とタグ付けの運用で決まります。集計はスクリプトに固定し、請求書との合計照合を毎月の最初の検算にします。Claudeには未配賦の分類と、根拠の行の引用、そして「不明」を不明のまま返す仕事を任せます。配賦ルールの最終判断は、費用を負担するチームの責任者が下します。

参照した一次ソース

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

  1. platform.claude.com/docs/en/agents-and-tools/tool-use/code-execution-tool
  2. docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/cost-alloc-tags.html
  3. docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/activating-tags.html
  4. docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/custom-tags.html
  5. docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/cost-allocation-backfill.…
  6. docs.aws.amazon.com/awsaccountbilling/latest/aboutv2/cost-allocation-tags-time…
  7. docs.aws.amazon.com/cur/latest/userguide/table-dictionary-cur2.html
  8. docs.aws.amazon.com/cur/latest/userguide/Lineitem-columns.html
この記事を共有:XはてブLinkedIn