AWS 料金を定期的に Slack に通知したい

AWS の料金、気づいたら予想以上に使ってたって事ないでしょうか。

サービスもどんどん増え、サービスの料金も複雑で結局いくらかかるのか不安!

こんな場合は、AWS の料金を定期的にチェックする事をおすすめします。

今回は、 AWS 利用料金を定期的に Slack に通知する設定方法をご紹介します。

設定の概要 

今回設定する仕組みは以下のとおりです。

  • Billing の予算機能を利用
  • 予算の値をチェックし、Slack へ通知する Lambda を用意する
  • CloudWatch イベントで定期的に予算チェック Lambda を実行する

今回の設定はコチラの記事を参考にさせていただきました。

@isobecky74 さんの記事と違うのは値を Billing の予算から取ってきているところだけです。

Budgets を作成する

取得するデータの基となるBudgets( 予算 ) を作成します。

請求ダッシュボードを開きます。

メニューの「予算」をクリックし、[ 予算を作成 ] ボタンをクリックします。

予算のタイプの選択と予算の名前、予算額を決めます。

予算額は $10 にしました。個人アカウントなので 1500 円くらいで。

その他はデフォルトで。

メールでも通知したい場合は、この画面でアラート設定でいます。

現在の利用料金と、予測がすぐに表示されます。

今月はこのまま利用すると予算より 132% オーバーしそうです。

Lambda が実行する IAM Role を設定する

Lambda が Billing の予算情報を取得できるように IAM Role を設定します。

IAM 画面を開きます。

ロールを選択します。

ロールを使用するサービスの選択で Lmanbda を選びます。

 

ポリシーフィルターに Billing と入力して Billing ポリシーをアタッチします。

ロールの名前をつけます。

ロール ARN を Lambda をデプロイするときに使うのでメモっておいてください。

Slack の Webhook URL を取得する

Slack へメッセージを通知するための URL を取得します。以下の URL にアクセスします。

https://slack.com/services/new/incoming-webhook

投稿したいチャンネルを選択して [ 新着 Web フック インテグレーションの追加 ] をクリックします。

次の画面で Webhook URL が発行されますのでコピーしておきます。

また、この画面最下部で、通知する際のボットの名前やアイコンも変更できます。

Lambda を作る

AWS Lambda のコンソールを開いて関数を作成します。

関数名を awscost_to_slack にしました。

ランタイムは Python 3.6 にしてください。

ロールは先程作成したロールを指定しましょう。

プログラムを用意する

以下のファイルを用意してください。

awscost_to_slack% tree                                                                                                                                                                                                 
├── lambda.json
├── lambda_function.py
└── requirements.txt

lambda.json

{
  "name": "awscost_to_slack",
  "description": "function to notify Slack of AWS cost",
  "region": "ap-northeast-1",
  "handler": "lambda_function.lambda_handler",
  "role": "作成したロールのARN",
  "timeout": 300,
  "memory": 128,
  "variables":
    {
      "slackPostURL":"取得したSlackのWebfookURL",
      "slackChannel":"投稿するSlackチャンネル",
      "budgetName":"作成した予算名",
      "accountId":"AWSアカウントID"
    }
}

lambda_function.py

#!/usr/bin/env python
# encoding: utf-8

import json
import datetime
import requests
import pytz
from dateutil import parser
import boto3
import os
import logging

logger = logging.getLogger()
logger.setLevel(logging.INFO)

# Slack の設定
SLACK_POST_URL = os.environ['slackPostURL']
SLACK_CHANNEL = os.environ['slackChannel']

# Budgets の設定
BUDGET_NAME = os.environ['budgetName']
ACCOUNTID = os.environ['accountId']

client = boto3.client('budgets')
responce = client.describe_budget(
        AccountId=ACCOUNTID,
        BudgetName=BUDGET_NAME
)

#予算
budget = responce['Budget']['BudgetLimit']['Amount']
#現行
cost = responce['Budget']['CalculatedSpend']['ActualSpend']['Amount']
#予測
predicted = responce['Budget']['CalculatedSpend']['ForecastedSpend']['Amount']

utcdate = responce['ResponseMetadata']['HTTPHeaders']['date']
jst_date = parser.parse(utcdate).astimezone(pytz.timezone('Asia/Tokyo'))

cost = round(float(cost))
predicted = round(float(predicted))

date = jst_date.strftime('%Y-%m-%d')

def build_message(cost, predicted):
    if float(cost) >= budget:
        color = "#ff0000" #red
        emotion = "予算を超えてます!不要なリソースは削除しましょう!"

    elif float(predicted) > budget:
        color = "warning" #yellow
        emotion = "予算を超えそうなので気をつけましょう。"
    else:
        color = "good"    #green
        emotion = "予算内におさまってます。"

    text = " %sまでのAWSの料金は、$%sです。今月の予測料金は$%sになりそうです。" % (date, cost, predicted)
    text = text + emotion

    atachements = {"text":text,"color":color}
    return atachements

def lambda_handler(event, context):
    content = build_message(cost,predicted)

    # SlackにPOSTする内容をセット
    slack_message = {
        'channel': SLACK_CHANNEL,
        "attachments": [content],
    }

    # SlackにPOST
    try:
        req = requests.post(SLACK_POST_URL, data=json.dumps(slack_message))
        logger.info("Message posted to %s", slack_message['channel'])
    except requests.exceptions.RequestException as e:
        logger.error("Request failed: %s", e)

requirements.txt

requests
pytz

 

作成したファイルを AWS にアップロードします。Lambda へコードをアップロードするには一度、 zip で圧縮して AWS コンソールか、 AWS CLI でアップロードします。

ただ、それだとめんどくさいので、 lambda-uploader でアップロードします。インストールしてない場合は、 pip install しましょう。

$ pip install lambda-uploadera

 

アップロード前に credential の設定をしておきましょう。私は direnv を使って作業フォルダごとに credential を設定してます。

credential の設定がただしいか、念の為 AWS CLI で先程作成した Lambda 関数の情報を取得してみましょう。

% aws lambda list-functions

 

FunctionName に先ほど作成した関数名が表示されてたら OK です。

では、コードをデプロイしましょう。

% lambda-uploader                                                                                                                                                                                      
λ Building Package
λ Uploading Package
λ Fin

 

Fin がでると成功です。

テスト実行してみる

コードのデプロイができたのでテスト実行してみます。

Lambda の画面で [ テスト ] をクリックします。

今回の Lambda に受け渡すイベント値はないのでそのまま、適当な名前をつけて [保存] をクリックします。

[ テスト ] ボタンを押して実行します。

成功すると上記の様に表示されます。

Slack にはこの様に表示されます。

CloudWatch Events から定期実行する

Lambda を実行するタイミングを設定します。今回は CloudWatch Events から毎朝 11 時に実行するようにします。

CloudWatch Event 画面を開いて [ ルールの作成 ] をクリックします。

 

[ スケジュール ] を選択し、 Cron 式で設定します。

0 2 ? * * *

実行時間が GMT なので日本時間からマイナス 9 時間した値を設定します。今回は 11 時に実行したいので 2 時で設定します。

ターゲットに作成した Lambda を選択して、 [ 設定の詳細 ] をクリックします。

ルールの名前と説明を入力して [ ルールの作成 ] ボタンをクリックして終了です。

編集後記

スクリンショットとって編集するのめんどいのでどうにかしたい。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です