FinOps: 次の25億ドルのAWS請求書が届く前にTerraformで予算アラートを
7月17日のCost Explorer障害では数十億ドル規模の誤った請求見積もりが表示された。Terraformのaws_budgets_budgetで予算アラートをコードとして整備する入門記事。

7月17日、AWS Cost Explorer は推定請求額として数百万ドル、数十億ドル、一部のスクリーンショットでは数兆ドルという数字を表示した。数字は誤りで、実際に課金された人はおらず、AWS は1日以内にデータを修正した。ここで覚えておく価値があるのは、最初に異変に気づいたのが請求アラートを設定していた人たちだった、という点だ。本記事では Terraform の aws_budgets_budget を使った予算アラート設定の基礎を、単純な月次コストアラートから使用量ベースの予算、Savings Plan の利用率トラッキングまで順に解説する。
あの朝 LinkedIn や X を開いた人は、みな同じフィードを目にした。一部の国家予算より桁数の多い AWS 請求見積もりのスクリーンショットだ。前月の支払いが 0.19 ドルだったユーザーには約 25 億ドルの見積もりが表示され、最大 2.5 兆ドルという数字を投稿した人もいた。
何が起きたのか
太平洋時間の午前1時33分、AWS は Health Dashboard に、Cost Explorer が "reflecting inaccurate estimated billing data"(不正確な推定請求データを表示している)と投稿した。根本原因は AWS の言葉を借りれば "an issue with unit pricing within the estimated billing computation subsystem"(推定請求計算サブシステム内の単価に関する問題)だった。要点は次のとおり。
- 数字は推定値であり、実際の使用量や請求額ではなかった。
- 課金された顧客はおらず、顧客側の対応も不要だった。
- AWS がデータをバックフィルし、7月18日には数字は正常に戻った。
興味深いのはここからだ。多くの人はこのバグを請求しきい値超過のメールで知った。アラートを設定していたアカウントは数分以内に異常を検知した。設定していなかったアカウントはソーシャルメディアで知るか、あるいは気づかないままだった。
今回は誤報だった。しかし、消し忘れた NAT Gateway、リトライループに陥った Lambda、漏洩したアクセスキーが生む請求は紛れもなく本物だ。だからこそ今週は請求アラートを整備する良い機会であり、コンソールのクリックではなくコードでやるべきだ。
なぜコンソールではなく IaC なのか
コンソールで作ったアラートは、ひとつのアカウントと誰かひとりの記憶の中にしか存在しない。Terraform なら次のようになる。
- バージョン管理下に置かれる。誰がいつ何を変えたかが見える。
- 新しいアカウントができたら、同じモジュールを適用して標準を維持すればよい。
- しきい値の変更は、レビューを通るプルリクエストになる。
実際の作業を担うリソースは、AWS Budgets をラップした aws_budgets_budget だ。
1. メールアラート付きの月次コスト予算
実際の支出が 80% を超えたらメールで知らせる、月額 100 ドルの予算。
resource "aws_budgets_budget" "monthly_cost" {
name = "monthly-cost-budget"
budget_type = "COST"
limit_amount = "100"
limit_unit = "USD"
time_unit = "MONTHLY"
notification {
comparison_operator = "GREATER_THAN"
threshold = 80
threshold_type = "PERCENTAGE"
notification_type = "ACTUAL"
subscriber_email_addresses = ["ops@example.com"]
}
}2. 予測ベースの早期警告
notification_type = "FORECASTED" を使うと、月末時点の予測が予算を超過する見込みになった段階、つまりお金が実際に出ていく前に AWS が警告してくれる。ACTUAL 通知と組み合わせれば両方向をカバーできる。
resource "aws_budgets_budget" "with_forecast" {
name = "monthly-cost-budget"
budget_type = "COST"
limit_amount = "100"
limit_unit = "USD"
time_unit = "MONTHLY"
# 予測が予算を超過しそうな場合に早期警告
notification {
comparison_operator = "GREATER_THAN"
threshold = 100
threshold_type = "PERCENTAGE"
notification_type = "FORECASTED"
subscriber_email_addresses = ["ops@example.com"]
}
# 実際の支出が 90% を超えたら再度警告
notification {
comparison_operator = "GREATER_THAN"
threshold = 90
threshold_type = "PERCENTAGE"
notification_type = "ACTUAL"
subscriber_email_addresses = ["ops@example.com"]
}
}3. SNS 経由で Slack や PagerDuty へ
メールは埋もれる。代わりにアラートを SNS トピックへ送り、そのトピックを Slack の Webhook や PagerDuty につなぐ。
resource "aws_sns_topic" "billing_alerts" {
name = "billing-alerts"
}
resource "aws_budgets_budget" "sns_alert" {
name = "monthly-cost-budget-sns"
budget_type = "COST"
limit_amount = "100"
limit_unit = "USD"
time_unit = "MONTHLY"
notification {
comparison_operator = "GREATER_THAN"
threshold = 80
threshold_type = "PERCENTAGE"
notification_type = "ACTUAL"
subscriber_sns_topic_arns = [aws_sns_topic.billing_alerts.arn]
}
}ここでの落とし穴: トピックポリシーで budgets.amazonaws.com サービスプリンシパルに SNS:Publish を許可しておく必要がある。これがないと Budgets は配信できず、アラートは音もなく消える。予算側にはエラーが出ない。
4. サービス単位の予算
特定のサービス、たとえば EC2 だけを監視するには cost_filter を追加する。
resource "aws_budgets_budget" "ec2_only" {
name = "ec2-monthly-budget"
budget_type = "COST"
limit_amount = "50"
limit_unit = "USD"
time_unit = "MONTHLY"
cost_filter {
name = "Service"
values = ["Amazon Elastic Compute Cloud - Compute"]
}
notification {
comparison_operator = "GREATER_THAN"
threshold = 80
threshold_type = "PERCENTAGE"
notification_type = "ACTUAL"
subscriber_email_addresses = ["ops@example.com"]
}
}5. 使用量予算: S3 を 3 GB まで
FinOps の仕事はドルの追跡だけではない。消費量の追跡もその一部だ。budget_type = "USAGE" を使うと、予算を通貨ではなく使用量の単位で定義できる。S3 ストレージを 3 GB に抑える予算は次のとおり。
resource "aws_budgets_budget" "s3" {
name = "s3-3GB-limit"
budget_type = "USAGE"
limit_amount = "3"
limit_unit = "GB"
time_unit = "MONTHLY"
notification {
comparison_operator = "GREATER_THAN"
threshold = 80
threshold_type = "PERCENTAGE"
notification_type = "ACTUAL"
subscriber_email_addresses = ["ops@example.com"]
}
}無料枠に収めたい個人プロジェクトや、「なぜこのバケットは増え続けているのか」を請求明細の項目になる前に捕まえる用途に向いている。
6. Savings Plan 利用率の予算
FinOps のもう半分。使いすぎを捕まえることは大事だが、前払いした割引を無駄にしないことも同じくらい大事だ。Savings Plan を購入したのに利用率が低ければ、ワークロードが別の場所でオンデマンドで動いている間、コミットメントはお金を燃やし続けている。SAVINGS_PLANS_UTILIZATION 予算タイプはまさにそれを追跡する。
resource "aws_budgets_budget" "savings_plan_utilization" {
name = "savings-plan-utilization"
budget_type = "SAVINGS_PLANS_UTILIZATION"
limit_amount = "100.0"
limit_unit = "PERCENTAGE"
time_unit = "MONTHLY"
cost_types {
include_credit = false
include_discount = false
include_other_subscription = false
include_recurring = false
include_refund = false
include_subscription = true
include_support = false
include_tax = false
include_upfront = false
use_blended = false
}
notification {
comparison_operator = "LESS_THAN"
threshold = 90
threshold_type = "PERCENTAGE"
notification_type = "ACTUAL"
subscriber_email_addresses = ["finops@example.com"]
}
}演算子が逆向きになっている点に注意。comparison_operator = "LESS_THAN" だ。このアラートは支出が増えたときではなく、利用率が 90% を下回ったときに発火する。同じパターンは RI_UTILIZATION を使って Reserved Instance にも適用できる。
まとめ
7月17日の兆ドル級スクリーンショットは表示上のバグだった。誰もその金額を払っておらず、AWS は1日以内にデータを修正した。それでも教訓は変わらない。請求の異常は、タイムラインではなく自分のアラート経由で届くべきだ。
aws_budgets_budgetで月次予算を定義する。FORECASTEDとACTUALの通知を組み合わせて二段構えの警告にする。- アラートは SNS 経由で、チームが実際に見ているチャンネルへ流す。
- 使用量予算(S3 の 3 GB)と Savings Plan 利用率のトラッキングで全体像を仕上げる。
次に「AWS の請求額が爆発した」がトレンド入りしたら、コーヒー片手に眺めていればいい。今回のインシデントの背景は The Register と TechCrunch が発生当時から報じている。
次に読む
- When the Cloud Sneezes, the World Catches a Cold - Lessons from the us-east-1 Meltdown: 業界全体が AWS の障害をリアルタイムで見守ったもうひとつの日と、そこから学んだブラストラディウスの話。
- Connect your AWS to GCP with Terraform via IPSec Site-to-Site VPN: コンソールのウィザードではなくコードに置くべきインフラの、もうひとつの例。
- Agents on Call, Part 2. The Foundation: Terraform Before Tokens(ercan.ai): 同じ Terraform ファーストの規律を AI エージェントのインフラに適用した記事。
参考資料
Ercan の他のサイト
同じ著者、別の領域のサイトが2つ。