Contents
AWS Budgets の基本と二段階アラート設定
アラートしきい値の種類
| 種類 | 目的 | 設定例 |
|---|---|---|
| 相対パーセンテージ(予測コスト) | コストが急上昇したときに早期警告を出す | 80 % 超過時に通知 |
| 絶対金額(実績コスト) | 予算上限に達した瞬間で確実に検知 | 100 %(上限)超過時に通知 |
ポイント:二段階アラートを組み合わせることで、早期警告 と 最終的な上限突破 の両方を網羅できます。
作成手順(コンソール)
- AWS マネジメントコンソール → Billing → Budgets
- 「Create budget」 → Cost budget を選択
- 予算期間(Monthly / Annual)と上限金額を入力(例:月額
150,000 円) -
「Add alert threshold」ボタンで以下を追加
-
80 %(相対) → 通知先は後述の SNS トピック
-
100 %(絶対) → 同じく SNS
-
設定が完了したら「Create budget」をクリック。画面に二つのしきい値が表示されます。
予算アクション(Budget Action)と UI 確認ポイント
2026 年時点の UI 状況
- 「予算アクション」 の画面は、2026 年現在もコンソールに残っており、「Add action」 ボタンから直接 SNS トピックや Lambda 関数を紐付けられます。
- AWS は UI 改訂の可能性を常に保持しているため、「Budget Action」タブが見当たらない場合は、コンソール左側メニューの「Actions」セクションを確認してください。
アクション設定フロー(概要)
| 手順 | 操作内容 |
|---|---|
| 1 | 予算詳細画面で対象しきい値横の Add action をクリック |
| 2 | SNS topic または AWS Lambda function を選択 |
| 3 | 必要に応じて IAM role for Budgets(例:budgets-action-role)を作成し、最小権限 sns:Publish / lambda:InvokeFunction を付与 |
| 4 | 「Add action」ボタンで保存。画面右上に「Action configured」のマークが表示されます |
注意:IAM ロールは 予算アクション専用 に作成し、他のリソースへの権限は付与しないよう徹底してください。
SNS トピックの作成・メール/Slack への配信手順
1. SNS トピックを作成する
| 項目 | 設定例 |
|---|---|
| 名前 | budget-alerts |
| タイプ | Standard |
| 暗号化 | デフォルトの AWS‑managed KMS キー(自動暗号化) |
コンソール: SNS → Topics → Create topic
2. メール購読を追加
- 作成したトピックの「Create subscription」
- Protocol:
Email、Endpoint: 通知受信者メールアドレス - 送られてくる確認メールのリンクをクリックしてサブスクリプションを有効化
3. Slack(Incoming Webhook)への配信設定
Slack に直接 SNS がメッセージを投げることはできませんが、HTTPS プロトコル のサブスクリプションとして Incoming Webhook URL を指定すれば実現できます。
手順詳細
| ステップ | 内容 |
|---|---|
| a. Slack で Incoming Webhook を作成 (App → Incoming Webhooks → Add to Workspace) |
発行された URL(例:https://hooks.slack.com/services/AAA/BBB/CCC)をコピー |
| b. SNS コンソールでトピックの「Create subscription」を選択 | |
c. Protocol: HTTPS Endpoint: 先ほど取得した Webhook URL を貼り付け |
|
| d. Enable raw message delivery にチェック(※推奨) これにより SNS が送る JSON ペイロードがそのまま Slack に渡ります |
|
| e. Delivery status logging(オプション)を有効化し、失敗時の再試行や CloudWatch Logs への出力を設定 |
注意点
- HTTPS エンドポイントは TLS 1.2 以上 が必須です。Slack の Webhook は常に TLS 1.2 で提供されますので問題ありません。
- SNS が Slack に送信できない場合、IAM ポリシーや VPC エンドポイントの設定 を再確認してください(インターネットアクセスが遮断されていると失敗します)。
4. 最小権限ポリシー例
|
1 2 3 4 5 6 7 8 9 10 11 |
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "sns:Publish", "Resource": "arn:aws:sns:*:*:budget-alerts" } ] } |
このポリシーを BudgetsSNSPublishRole にアタッチし、予算アクション設定画面でロールを選択します。
Lambda 関数で実装する自動対処フロー(EC2・RDS 停止)
前提条件
| 条件 | 内容 |
|---|---|
| Lambda ランタイム | Python 3.9 以上(本稿は 3.9) |
| IAM ロール | ec2:StopInstances、rds:StopDBInstance、rds:ListTagsForResource、rds:DescribeDBInstances を最小権限で付与 |
| 対象リソースのタグ | AutoStop=true(文字列は小文字でも OK) |
重要:RDS の停止はエンジンに依存します。Aurora、MySQL、PostgreSQL、MariaDB、Oracle がサポート対象です。SQL Server など一部のエンジンでは
StopDBInstanceAPI が利用できません。その旨をコード内でコメントし、実行前に対象インスタンスが対応エンジンか必ず確認してください。
完全サンプルコード
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
import json import logging import boto3 from botocore.exceptions import ClientError # ロガー設定 logger = logging.getLogger() logger.setLevel(logging.INFO) ec2 = boto3.client('ec2') rds = boto3.client('rds') def lambda_handler(event, context): """ SNS メッセージ(Budgets アクション)を受信し、予算超過時に AutoStop タグが付いた EC2 / 対応 RDS インスタンスを停止する。 """ try: # 1. SNS ペイロード取得 message = json.loads(event['Records'][0]['Sns']['Message']) logger.info("Received Budgets event: %s", message) # 2. しきい値が 100%(=上限超過)かどうか判定 threshold = float(message.get('newThreshold', 0)) if threshold < 100: logger.info("Threshold %.2f%% (<100) – no action taken.", threshold) return # 3. タグ条件で EC2 を停止 stop_ec2_instances() # 4. 対応エンジンの RDS を停止(非対応はスキップ) stop_rds_instances() except Exception as e: logger.error("Error in lambda_handler: %s", e, exc_info=True) raise def stop_ec2_instances(): """ AutoStop=true タグが付いた EC2 インスタンスだけを停止 """ filters = [{'Name': 'tag:AutoStop', 'Values': ['true']}] try: resp = ec2.describe_instances(Filters=filters) ids = [ inst['InstanceId'] for reservation in resp['Reservations'] for inst in reservation['Instances'] if inst['State']['Name'] == 'running' ] if not ids: logger.info("No running EC2 instances matched the AutoStop tag.") return ec2.stop_instances(InstanceIds=ids) logger.info("Stopped EC2 instances: %s", ids) except ClientError as err: logger.error("Failed to stop EC2 instances: %s", err) def stop_rds_instances(): """ 対応エンジンかつ AutoStop=true タグが付いた RDS を停止 """ try: dbs = rds.describe_db_instances()['DBInstances'] stopped = [] for db in dbs: # エンジン対応チェック if db['Engine'] not in ('aurora', 'aurora-mysql', 'aurora-postgresql', 'mysql', 'postgres', 'mariadb', 'oracle-ee'): continue # 非対応エンジンはスキップ tags = rds.list_tags_for_resource( ResourceName=db['DBInstanceArn'] )['TagList'] auto_stop = any(t['Key'] == 'AutoStop' and t['Value'].lower() == 'true' for t in tags) if auto_stop and db['DBInstanceStatus'] == 'available': rds.stop_db_instance(DBInstanceIdentifier=db['DBInstanceIdentifier']) stopped.append(db['DBInstanceIdentifier']) if stopped: logger.info("Stopped RDS instances: %s", stopped) else: logger.info("No eligible RDS instances to stop.") except ClientError as err: logger.error("Failed to stop RDS instances: %s", err) |
重要ポイントまとめ
| 項目 | 内容 |
|---|---|
| エンジン制限 | StopDBInstance が利用できない SQL Server 系は除外(コード内でフィルタ) |
| タグ名の統一 | AutoStop=true(大文字・小文字は問わないが、統一して運用) |
| ログ出力 | CloudWatch Logs に情報とエラーを残すことで、失敗時に再通知や手動復旧が容易になる |
| テスト方法 | 予算のしきい値を 80 % のみ設定し、実際に SNS が届くか確認。その後閾値を 100 % に上げて Lambda が正常に起動することを検証 |
CloudWatch Billing Alarm との比較と選択基準
| 項目 | AWS Budgets | CloudWatch Billing Alarm |
|---|---|---|
| 対象範囲 | コスト・使用量・予算(タグ別・サービス別) | 主に月次請求額のみ |
| アラート方式 | 相対%・絶対金額・予測ベースの二段階設定が可能 | 静的な閾値(例:$100 超過) |
| 通知先 | SNS、Lambda、Chatbot、AWS Chatbot (Slack) など多彩 | SNS のみ(カスタムロジックは別途実装必要) |
| UI/UX | 「Add alert threshold」→「Add action」で一括設定可能 | CloudWatch コンソールで別々に作成する手間がある |
| 自動対処 | Budgets → Lambda で即時実行可 | Alarm → SNS → 別途 Lambda を呼び出す構成が必要 |
| 推奨シーン | 部門・タグ単位で細かく管理したい大規模環境 | 小規模アカウントや単純な請求額監視 |
選択基準
- 多様な通知先や自動処理が必須 → AWS Budgets が最適。
- シンプルに月次請求だけをモニタリングしたい → CloudWatch Billing Alarm でも十分。
ベストプラクティスチェックリスト
| カテゴリ | 確認項目 |
|---|---|
| リージョン・サービス整合性 | Budgets は全リージョン対象。SNS と Lambda が同一リージョンか、クロスリージョンの場合は適切な VPC エンドポイントを設定 |
| 予算・アラートの重複回避 | 同一金額・期間で複数予算が作成されていないか、コンソールの Budget list で一覧確認 |
| IAM 権限最小化 | Budgets → SNS: sns:Publish のみ、Lambda 起動は lambda:InvokeFunction のみ付与。RDS 停止ロールはエンジン制限を考慮し、rds:StopDBInstance と rds:ListTagsForResource に限定 |
| SNS 暗号化 | デフォルト KMS キーで暗号化し、外部公開しない。必要に応じてカスタム CMK を使用 |
| タグ戦略の統一 | コストセンター・環境(dev/prod)用タグは事前に定義し、未タグ付リソースには自動で CostCenter=Unassigned タグを付与する Lambda も併せて運用 |
| テスト実施 | 小額予算で「80 %」アラートだけ発火させ、SNS と Lambda の連携が正しく機能するか検証。その後「100 %」に変更し自動停止処理を確認 |
| モニタリング・ログ保管 | Lambda の CloudWatch Logs を永続化(Retention 30 日以上)し、失敗時は別途 SNS に再通知設定 |
| 予算の定期レビュー | 四半期ごとに Cost Explorer と照らし合わせて実績を確認。閾値・タグの見直しや不要なリソースの除外を実施 |
| Slack 連携の安全性 | HTTPS エンドポイントは TLS 1.2+ を必須とし、SNS の Raw Message Delivery を有効化して JSON ペイロードがそのまま Slack に届くことを確認 |
まとめ
- 二段階アラート(相対%+絶対金額)で早期警告と確実な上限突破検知を同時に実現。
- 予算アクション UI は 2026 年も有効で、SNS または Lambda を直接紐付けられる点が大きなメリット。
- Slack 通知は HTTPS プロトコルのサブスクリプションとして設定し、TLS と Raw Message Delivery の要件を満たすことが必須。
- RDS 停止はエンジン依存であるため、コード内に除外ロジックを実装し、対象外エンジンへの呼び出しを防止。
- 最小権限 IAM ロールと暗号化設定でセキュリティリスクを低減しつつ、CloudWatch Logs による可観測性を確保。
上記手順・ベストプラクティスをそのままテンプレート化すれば、数分で AWS Budgets + SNS + Lambda の自動コストアラート基盤が構築できます。ぜひ実装し、予算超過リスクを根本から排除してください。