AWS Chatbot で予算アラートを Slack に通知する
はじめに
AWSをはじめ、クラウドサービスってほんとに便利ですよね。個人開発者にも優しい無料枠もたくさんあって嬉しい限り。ですが従量課金ゆえ、気をつけないといつだってクラウド破産と隣り合わせです。
今回はそんなクラウド破産を防ぐべく、設定した予算に到達したら通知をSlackに送ってもらえる予算アラートをAWS CDKを使って組んでいきたいと思います。
対象読者
- AWSを利用している方
- AWSコストの監視をしたい方
- Slackを利用している方
- AWS CDKでインフラを管理したい方
- Typescriptを使ったことがある方
技術スタック
- AWS CDK(Typescript)
- aws-cdk(v2.141.0)
- node(v20.0.0)
- AWS Budget
- AWS Chatbot
- AWS SNS
概要
全体フローは以下になっています。
-
AWS Budget
に予算アラートを設定 - 1で設定した閾値に到達したら
AWS SNS
トピックに通知が発行される - 2を
AWS Chatbot
で取得し、Slackのチャンネルに送信する
事前準備
前提としてAWSとSlackの連携が必要なため、解説していきます。
AWS ChatbotにSlackチャットクライアントを設定
AWS Chatbotにまず使用している自分のSlackワークスペースを連携しなければいけません。
すでに連携済みの方はスキップしてください。
前提条件としてAWSコンソールとSlackにログイン済みとします。
AWSコンソールでAWS Chatbot
を開くと、右上に設定プルダウンがあるのでそちらからSlackを選択して、クライアントを設定ボタンを押してください。
その後Slackにリダイレクトします。Slackにawsのボットが入っていれば設定完了です。
通知したいSlackチャンネルを作成
今回の予算アラート通知を受信するためのチャンネルを作成します。DMでもいいですが、今回はチームで共有することも多いだろうということでプライベートのチャンネルを作成します。
チャンネル作成後先ほど現れたawsボットをチャンネルに招待します。
チャンネルで@aws
と送信して、招待するかどうか聞かれるので招待しましょう。
これで事前準備は完了です。
コード解説
CDKで以下のリソースを作成していきます。
- AWS Chatbot
- AWS SNS
- AWS Budget
ソースはGithubに公開しています。ワンコマンドでデプロイ可能です。
今回はわかりやすいように一つのスタックですべて作成します。
実際の運用ではスタックをご自身で分けるほうがおすすめです。
作成するリソースごとに関数にしていますので追って説明します。
全体フロー
Stackのインスタンスを作成します。
SlackのワークスペースIDとチャンネルIDはハードコーディングを避けたいのでコンテキストで引数で受け取ります。
SNSトピック作成->チャットボット作成->予算作成の順序になっています。
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
const slackChannelId = scope.node.tryGetContext("slackChannelId") as string;
const slackWorkspaceId = scope.node.tryGetContext("slackWorkspaceId") as string;
const idPrefix = `aws-chatbot-cost-notification-slack-${slackChannelId}`;
const topic = this.createSnsTopic(idPrefix);
this.createChatbot(idPrefix, topic, slackWorkspaceId, slackChannelId);
this.createBudget(idPrefix, topic);
}
createSnsTopic
AWS SNSトピックを作成します。
principalsにbudgets.amazonaws.com
、actionにsns:Publish
を設定することで、
AWS Budget
側から予算アラートメッセージをSNSに発行できるロールをアタッチします。
private createSnsTopic(idPrefix: string) {
const topic = new aws_sns.Topic(this, `${idPrefix}-topic`, {
displayName: `${idPrefix}-topic`,
topicName: `${idPrefix}-topic`,
});
topic.addToResourcePolicy(
new aws_iam.PolicyStatement({
effect: aws_iam.Effect.ALLOW,
actions: ["sns:Publish"],
principals: [new aws_iam.ServicePrincipal("budgets.amazonaws.com")],
resources: [topic.topicArn],
})
);
return topic;
}
createChatbot
AWS Chatbot
を作成します。
AWS Chatbot
は外部チャットツールのチャンネルと連携する設定を、チャットクライアントごとにチャネルという単位で持つことができます。
今回はSlackを通知先として利用するのでaws_chatbot.SlackChannelConfiguration
クラスを使い、コンテキストから受け取ったSlackのワークスペースとチャンネルを設定します。
また先ほど作成したSNSトピックから通知を受信するので、arnを設定します。
通知を受信し、Slackに転送するためには権限が必要なので、principalでchatbot.amazonaws.com
を、actionでsns:Subscribe
をロールに付与し、リソースは上記のトピックのarnを追加します。
private createChatbot(
idPrefix: string,
topic: cdk.aws_sns.Topic,
slackWorkspaceId: string,
slackChannelId: string
) {
const chatbotRole = new aws_iam.Role(this, `${idPrefix}-role`, {
assumedBy: new aws_iam.ServicePrincipal("chatbot.amazonaws.com"),
});
chatbotRole.addToPolicy(
new aws_iam.PolicyStatement({
actions: ["sns:Subscribe"],
resources: [topic.topicArn],
})
);
new aws_chatbot.SlackChannelConfiguration(this, `${idPrefix}-chatbot`, {
slackChannelConfigurationName: `${idPrefix}-chatbot`,
slackWorkspaceId,
slackChannelId,
notificationTopics: [topic],
role: chatbotRole,
});
}
createBudget
AWS予算アラートを作成します。
今回は100ドル
を上限として設定し、そのうち50%
に到達した際にアラートが設定されるようにしています。
つまり請求額が50ドル
に到達したらアラートが飛ぶことになります。
タイムラグもあるので余裕をもった上限と閾値を設定することをおすすめします。
通知先を設定しないと、アラート状態になるだけで通知がとんでこないので、先ほど作成したSNSトピックを通知先として設定します。
private createBudget(idPrefix: string, topic: cdk.aws_sns.Topic) {
new aws_budgets.CfnBudget(this, `${idPrefix}-budget`, {
budget: {
budgetLimit: {
amount: 100, // お好きな金額を
unit: "USD",
},
budgetName: `${idPrefix}-budget`,
budgetType: "COST",
timeUnit: "MONTHLY",
},
notificationsWithSubscribers: [
{
notification: {
notificationType: "ACTUAL",
comparisonOperator: "GREATER_THAN",
threshold: 50, // お好きな閾値を
thresholdType: "PERCENTAGE",
},
subscribers: [
{
subscriptionType: "SNS",
address: topic.topicArn,
},
],
},
],
});
}
デプロイ
スタックを作成したらさっそくデプロイしてみます。
SlackのワークスペースIDとチャンネルIDをコンテキスト引数として渡す必要があるので取得しましょう。
ワークスペースID
AWSコンソールからAWS Chatbot
を開いて、事前準備で作成したSlackのチャットクライアントページの上部にワークスペースIDが載っているのでコピーします。
チャンネルID
事前準備で作成したSlackのチャンネルの設定画面からコピーできます。
コマンド
cdk deploy AwsChatbotCostNotificationSlackStack -c slackWorkspaceId=[ワークスペースID] -c slackChannelId=[チャンネルID] --profile [プロファイル名]
動作確認
デプロイが完了したらAWSコンソールからBudgets
を開き、確認してみます。
作成できてますね!
閾値を変更
ちゃんと通知が送信されるかどうかテストしてみます。
コンソール上から一度予算の閾値を0にしてみましょう。
- 作成した予算を選択
- すべてのアラートを表示
- 画面下のアラートの編集をクリック
- 閾値を0にする
上記の設定をしたら保存してください。
テストのために一時的に0にするだけなので、もとの閾値に戻すことをお忘れなく。
Slackを確認
通知が届きました!
おわり
いかがだったでしょうか。
運用でAWSを使ってるチームはもちろん設定済みではあると思いますが、意外と個人開発者だとやってない方もいらっしゃると思います。
高額請求でなんとかなった系の記事もちょこちょこみますが、精神的によろしくないのでコスト管理はしっかりやってきたいですね。
Discussion