マルチアカウント環境のAWS BudgetsをTerraformでいい感じに管理する
はじめに
皆様こんにちは、あかいけです。
マルチアカウント環境を運用していると、アカウントごとのコスト管理が地味に大変ですよね?
特にサンドボックスや検証用アカウントなど、開発者が自由にリソースを触れる環境では、うっかり高額なリソースが立ち上がったまま放置されてしまうことも珍しくありません。
そんな事態を防ぐために各アカウントに予算を設定してアラートを飛ばす必要があります。
ただいざ実装しようとすると、「各アカウントでそれぞれ設定する?管理アカウントから一括で設定できる?」と迷う場面があると思います。(私は迷いました)
なので今回は、管理アカウントからマルチアカウントのAWS BudgetsをTerraformで一元管理する方法をまとめます。
アカウント個別のAWS Budgetsを設定する方法について
AWS Budgetsでアカウント個別の予算を設定する方法は、大きく分けて2つあります。
①.各アカウントで設定する方法
各アカウントのAWS Budgets画面からそれぞれ設定する方法です。
一番シンプルなやり方ですが以下のようなデメリットがあり、アカウント数が増えるほど運用コストが跳ね上がります…。
- デメリット
- アカウントごとにログインして設定する必要がある
- アカウントが増えるたびに設定作業が発生する
- 設定の統一が難しく、アカウントによってばらつきが出やすい
- SCP等で制御しないでAdmin権限を付与した場合、アカウント側で設定を変更・削除可能
②.管理アカウントで設定する方法
AWS Organizationsを使っている場合は、管理アカウントからAWS Budgetsを設定して「連結アカウント(Linked Account)」フィルターで対象アカウントを絞り込む方法が使えます。
なのでOrganizationsを使っているのであれば、こちらの方式で一元管理するのがおすすめです。
-
前提条件
- AWS Organizationsを設定していること
- 管理アカウントで操作すること
-
メリット
- 管理アカウント1箇所からすべてのアカウントのAWS Budgetsを管理できる
- Terraformで設定をコード化しやすい
- 設定を統一できる
なお実際の設定画面は以下のような感じです。
フィルターとして「リンクされたアカウント」を選択すると、各アカウントを指定できます。

今回はこの②の方式をTerraformで実装していきます。
料金とクォータについて
実装の前に、AWS Budgetsの料金とクォータを確認しておきましょう。
料金
今回実装するのは通知のみのAWS Budgets(メール通知)なので、費用は無料です。
アクション(IAMポリシーのアタッチやEC2の停止など)は使わないので、アカウント数が増えてもコストが発生しません。
| 項目 | 料金 |
|---|---|
| 通知のみのAWS Budgets(アクションなし) | 無料(個数制限なし) |
| アクション付きAWS Budgets(最初の2つ) | 無料 |
| アクション付きAWS Budgets(3つ目以降) | $0.10/日 |
| Budget Reports | $0.01/レポート |
クォータ
上限は管理アカウントあたり20,000個なので、よほど大規模な組織でない限り上限に達することはないかと思います。
| 項目 | 上限 |
|---|---|
| 管理アカウントあたりの総AWS Budgets数 | 20,000 |
またこの上限は変更不可のようです。

TerraformでAWS Budgetsを管理する
ファイル構成
今回のファイル構成は以下の通りです。
budgets.tfでAWS Budgetsリソースを定義し、config/budgets_config.yamlにアカウントごとの設定を書くシンプルな構成です。
.
├── provider.tf
├── budgets.tf
└── config/
└── budgets_config.yaml
budgets_config.yaml
これは各アカウントのAWS Budgetsの設定値を記載したファイルです。
以下のブログでも紹介しましたが、個人的にfor_eachでいっぱい作るリソースやエンジニア以外の方が触れることが想定される場合はYAMLで設定を定義するのが好きなので、今回もそうしています。
# 共通設定
default:
limit_amount: "100" # USD/月
thresholds: [50, 80, 100] # 通知閾値(%)
admin_notification_email: "example+admin@gmail.com" # 管理者通知先
# アカウントごとの設定
accounts:
- account_id: "XXXXXXXXXXXX" # target account id
notification_emails:
- "example+user@gmail.com" # サンドボックス利用者
limit_amount: "200" # 省略時はdefaultの値を使用
thresholds: [70, 90, 100] # 省略時はdefaultの値を使用
admin_notification_email: "example+admin00@gmail.com" # 省略時はdefaultの値を使用
defaultセクションに全アカウント共通の設定を書き、accountsセクションにアカウントごとの設定を追加します。
notification_emailsはアカウント利用者への通知先なので、各アカウントで必ず設定します。
limit_amount、thresholds、admin_notification_emailはアカウントごとに省略でき、省略した場合はdefaultの値が適用されます。
budgets.tf
これはbudgets_config.yamlを読み込んで、リソースを作成するファイルです。
locals {
# YAML読み込み
budgets_config = yamldecode(file("${path.module}/config/budgets_config.yaml"))
# AWS Budgets設定
budget_accounts = try(local.budgets_config.accounts, [])
budget_default = local.budgets_config.default
}
# 管理アカウントにAWS Budgetsを作成し、リンクされたアカウントでフィルターする
resource "aws_budgets_budget" "sandbox" {
for_each = { for account in local.budget_accounts : account.account_id => account }
name = "monthly-budget-${each.value.account_id}"
budget_type = "COST"
time_unit = "MONTHLY"
limit_amount = try(each.value.limit_amount, local.budget_default.limit_amount)
limit_unit = "USD"
cost_filter {
name = "LinkedAccount"
values = [each.value.account_id]
}
cost_types {
include_credit = false
include_discount = true
include_other_subscription = true
include_recurring = true
include_refund = false
include_subscription = true
include_support = true
include_tax = true
include_upfront = true
use_blended = false
}
dynamic "notification" {
for_each = try(each.value.thresholds, local.budget_default.thresholds)
content {
comparison_operator = "GREATER_THAN"
threshold = notification.value
threshold_type = "PERCENTAGE"
notification_type = "ACTUAL"
subscriber_email_addresses = concat(
[try(each.value.admin_notification_email, local.budget_default.admin_notification_email)],
each.value.notification_emails
)
}
}
tags = {
ManagedBy = "terraform"
}
}
ポイントをいくつか解説します。
for_eachでアカウントごとにAWS Budgetsを作成する
for_each = { for account in local.budget_accounts : account.account_id => account }
YAMLのaccountsリストをアカウントIDをキーとするマップに変換してfor_eachに渡しています。
これによって、YAMLにアカウントを追加するだけで自動的にAWS Budgetsが作成されます。
cost_filterでアカウントを絞り込む
cost_filter {
name = "LinkedAccount"
values = [each.value.account_id]
}
LinkedAccountフィルターで各アカウントのコストのみに絞り込んでいます。
このフィルターを設定することで、管理アカウントから見た場合でも特定のアカウントのコストのみを対象にできます。
try()でデフォルト値にフォールバックする
limit_amount = try(each.value.limit_amount, local.budget_default.limit_amount)
Terraformのtry()関数を使って、アカウントごとの設定が省略された場合にdefaultの値を使うようにしています。
これにより、アカウントごとに細かくカスタマイズしつつ、省略した場合は共通設定が適用されるシンプルな設計になっています。
dynamic "notification"で閾値ごとに通知を生成する
dynamic "notification" {
for_each = try(each.value.thresholds, local.budget_default.thresholds)
content {
threshold = notification.value
subscriber_email_addresses = concat(
[try(each.value.admin_notification_email, local.budget_default.admin_notification_email)],
each.value.notification_emails
)
}
}
閾値のリスト(例: [50, 80, 100])をループして、それぞれの閾値に対応した通知設定を動的に生成しています。
通知先は管理者メールとアカウント利用者のメールをconcat()で結合しているので、アカウントごとに異なる通知先を柔軟に設定できます。
provider.tf
管理アカウントのCredentialsでTerraformを実行する必要があります。
それ以外の注意点は特にないです。
terraform {
required_version = ">= 1.10"
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}
provider "aws" {
region = "ap-northeast-1"
}
実際に適用してみる
例えば以下のような設定でapplyすると、3アカウント分のAWS Budgetsが作成されます。
アカウントXXXXXXXXXXXX(1つ目)は個別に設定を上書きしており、残り2つはdefaultの設定を使います。
# 共通設定
default:
limit_amount: "100" # USD/月
thresholds: [50, 80, 100] # 通知閾値(%)
admin_notification_email: "example+admin@gmail.com" # 管理者通知先
# アカウントごとの設定
accounts:
- account_id: "XXXXXXXXXXXX" # target account id
notification_emails:
- "example+user00@gmail.com" # サンドボックス利用者
limit_amount: "200" # 省略時はdefaultの値を使用
thresholds: [70, 90, 100] # 省略時はdefaultの値を使用
admin_notification_email: "example+admin00@gmail.com" # 省略時はdefaultの値を使用
- account_id: "XXXXXXXXXXXX" # target account id
notification_emails:
- "example+user01@gmail.com" # サンドボックス利用者
- account_id: "XXXXXXXXXXXX" # target account id
notification_emails:
- "example+user02@gmail.com" # サンドボックス利用者
以下のように3つのAWS Budgetsが作成されます。いい感じですね。

新しいアカウントを追加したいときは、budgets_config.yaml の accounts にエントリを追加してapplyするだけです。
さいごに
以上、管理アカウントからマルチアカウントのAWS BudgetsをTerraformで一元管理する方法でした。
AWS OrganizationsのLinkedAccountフィルターを使うことで、管理アカウント1箇所からすべてのアカウントの予算管理ができます。
各アカウントにログインして回る必要がないのはもちろん、アカウント側で設定を変更・削除できない点も管理者としては安心ですね。
またTerraformに関しては、今回のようにYAMLで設定を外出しにすることでアカウントが増えてもコードの部分を触らずに対応できるので、よければ合わせて取り入れてみてください。
AWS Organizationsでマルチアカウントのコスト管理に悩んでいる方の参考になれば幸いです。







