初日から間に合わない事になってしまって反省してます。一人アドベントカレンダー的更新第1日目行ってみたいと思います。どこかの誰かのネタとかぶっている可能性もありますが、最近やったことを書いていきます。
今日のネタはAWS LambdaのScheduled Eventをつかって日次の費用レポートをSlackにPostするというものです。
まずはLambda Functionを実装します。CloudWatchからBilling NamespaceのEstimatedChargesメトリクスを前日の6:00〜本日の6:00までの費用計とさらに1日前の費用計とで取得して差分を取るという簡易的な日次費用取得となっています。end_timeなどを6時にしているのと、一回で2日分まとめて取得していないのは2日前の0時からから本日の0時までだとうまくいかなかったからというネガティブな理由です。。。
この例ではConsolidatedBillingの親アカウントから複数アカウント分の費用を取得するようにしています。
# -*- coding: utf-8 -*- import sys, traceback import boto3 import json import datetime, pytz import slackweb def billing_info(cloudwatch, account_no, start_time, end_time): billing_info = cloudwatch.get_metric_statistics( Namespace = 'AWS/Billing', MetricName = 'EstimatedCharges', Dimensions = [{'Name': 'LinkedAccount', 'Value': account_no},{'Name': 'Currency', 'Value': 'USD'},], StartTime = start_time, EndTime = end_time, Period = 86400, Statistics = ["Maximum"], ) return billing_info def latest_bill(billing_info): bill = 0 for b in billing_info['Datapoints']: if bill < b['Maximum']: bill = b['Maximum'] return bill def lambda_handler(event, context): hook_url = "https://hooks.slack.com/services/xxxxxxxxx/yyyyyyyyy/zzzzzzzzzzzzzzzzzzzzzzzz" username = "billing_bot" channel = "#report" icon_emoji = ":money_with_wings:" try: tokyo_tz = pytz.timezone('Asia/Tokyo') d = datetime.datetime.now() todays_end_time = datetime.datetime(d.year, d.month, d.day, 6, 0, 0, 0, tokyo_tz) todays_start_time = todays_end_time - datetime.timedelta(days=1) yesterdays_end_time = todays_start_time yesterdays_start_time = yesterdays_end_time - datetime.timedelta(days=1) yesterday = yesterdays_end_time.strftime("%Y-%m-%d") cloudwatch = boto3.client('cloudwatch', region_name='us-east-1') post_message = "" for account_name, account_no in {'Account1':'000000000000', 'Account2':'111111111111'}.items(): todays_billing_info = billing_info(cloudwatch, account_no, todays_start_time, todays_end_time) yesterdays_billing_info = billing_info(cloudwatch, account_no, yesterdays_start_time, yesterdays_end_time) todays_bill = latest_bill(todays_billing_info) yesterdays_bill = latest_bill(yesterdays_billing_info) one_day_bill = todays_bill - yesterdays_bill if post_message != "": post_message += "\n" post_message += "%s: %f USD" % (account_name, one_day_bill) post_message = "昨日(%s)1日分のAWS費用は以下のとおりでした。\n```\n%s\n```\n今日もコスト意識を持って行きましょう。" % (yesterday, post_message) slack = slackweb.Slack(url=hook_url) slack.notify(text=post_message, channel=channel, username=username, icon_emoji=icon_emoji) except: print traceback.format_exc() else: print('finished.')
これをこんな感じでZIPファイルにする。
$mkdir billing_bot $ vi lambda_function.py (上のPythonコード) $ vi requirements.txt slackweb pytz $ pip -r requirements.txt -t ./ $ zip -r ~/myLambdaFunction.zip *
Management ConsoleからLambdaのメニューに進んで、次のように設定していく。(例は朝9:10 JSTにスケジュールしています)
- Create a Lambda Dunction
- lambda-canary
- Configure Event Source
- Event source type: Scheduled Event
- Name: billing_bot
- Description: daily billing report
- Schedule Expression: cron(10 0 * * ? *)
- Configure function
- Name: billing_bot
- Runtime: Python2.7
- Lambda function code
- Upload a .ZIP file: 先ほど作成したZIPファイルを選択
- Lambda function handler and role
- lambda_function.lambda_handler (lambda functionのメインファイル名.handler関数名)
- Role: lambda_billing_bot (※roleのPolicyは後述)
- Advanced settings
- Memory: 128MB
- Timeout: 1min
- Enable nowをチェック
- Create function
Lambda functionに設定するRoleのPolicyはこんな感じ。
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "logs:CreateLogGroup", "logs:CreateLogStream", "logs:PutLogEvents" ], "Resource": "arn:aws:logs:*:*:*" }, { "Effect": "Allow", "Action": [ "cloudwatch:GetMetricStatistics" ], "Resource": "*" } ] }
Lambda Functionが作成できたら、「Test」ボタンを押してテスト実行をしてみます。するとSlackにこんな感じでPostされます。
はい、うまくいきましたね。
かなりはしょった感は否めませんが、なんとなく雰囲気は伝わったと思います。突っ込みどころは多くあると思いますので、優しく教えていただければと思います。
では、1日坊主にならないことを祈って、初日はこの辺で。