概要
使っていないインスタンスが起動しっぱなしだったせいで、AWSの利用料がいつの間にか大きくなっていたことがきっかけです。
一方毎日CostExplorerを見に行くのも手間が多いので、slackに通知するようにしたいと思い作ってみました。
また環境構築が面倒なので、docker化してどこでも実行できるようにしました。
成果物
今回作ったものはこちら。
環境
- Ubuntu 16.04
- chrominum 62.0.3202.94
- chromedriver 2.33.506092
- python3
前準備
BillingというAWSのManaged Policyを持ったIAMユーザを作っておいてください。
でないとログインしても情報を閲覧する権限がありません。
説明
AWSの前日コストをSlackに通知してみた。概算でなくて正確な値で。 - Qiitaの記事をほぼ流用する形で実装しています。
一部環境変数化していたり、見たい情報が違ったのでその辺をいじったりしています。
importからchromeの準備
#!/usr/bin/python3 # coding: UTF-8 import os import datetime import slackweb from time import sleep from selenium import webdriver from selenium.webdriver.chrome.options import Options from collections import OrderedDict # headless chrome CHROME_BIN = "/usr/bin/chromium-browser" CHROME_DRIVER = os.path.expanduser("/usr/bin/chromedriver") options = Options() options.binary_location = CHROME_BIN options.add_argument("--headless") options.add_argument("--no-sandbox") options.add_argument("--window-size=1280,3000") driver = webdriver.Chrome(CHROME_DRIVER, chrome_options=options)
CHROME_BIN
やCHROME_DRIVER
は実行環境に依って異なります。
例えばmacOSであれば
CHROME_BIN = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
という感じになります。
options.add_argument("--no-sandbox")
を入れているのは、dockerコンテナがrootから実行しているため、付けないと
Running as root without --no-sandbox is not supported
と怒られるためです。
ログイン
# login print('login') account_id = os.environ.get("ACCOUNT_ID") username = os.environ.get("USERNAME") password = os.environ.get("PASSWORD") login_url = "https://%s.signin.aws.amazon.com/console" % account_id driver.get(login_url) driver.find_element_by_id('username').send_keys(username) driver.find_element_by_id('password').send_keys(password) driver.find_element_by_id('signin_button').click() sleep(3)
環境変数を代入するようにしています。すぐ遷移できるわけではないので、sleep()
を入れています。
ダッシュボード
print('move cost report dashboard') driver.get("https://console.aws.amazon.com/cost-reports/home?#/savedReports") sleep(3) print('move saved report') driver.find_element_by_link_text('Monthly costs by service').click() sleep(3) # set period driver.find_element_by_xpath('//*[@class="picker-dropdown"]').click() # target_at target_date = os.getenv("TARGET_DATE", "1") target_at = datetime.date.today() - datetime.timedelta(int(target_date)) target_at_str = target_at.strftime('%m/%d/%Y') # from elem = driver.find_element_by_xpath('//label[text()="From"]/following::input') elem.clear() elem.send_keys(target_at_str) sleep(1) # to elem = driver.find_element_by_xpath('//label[text()="To"]/following::input') elem.clear() elem.send_keys(target_at_str) sleep(1) # apply elem = driver.find_element_by_xpath('//div[text()="Apply"]').click() sleep(1) # change granularity elem = driver.find_element_by_xpath('//granularity//div[@class="ui-dropdown"]').click() elem = driver.find_element_by_link_text('Daily').click() sleep(1)
xpath
を使いながらゴリゴリ進んでいます。
要件が使ってるサービス毎の料金が分かるようにしたいだったので、デフォルトで用意されているダッシュボードの内Monthly costs by serviceというものを使うようにしています。
Webコンソールだと以下のように分かりやすく棒グラフで表示されます。
要件として毎日の料金を取得したいというのがあったので、これをDaily単位で表示しています。
データの抽出とslack送信
# for slack report slack_link = driver.current_url # get cost costs = OrderedDict() for row in range(1, 7): title_xpath = '//div[@class="left-container"]//tr[%d]/td' % row title = driver.find_element_by_xpath(title_xpath).text value_xpath = '//div[contains(@class, "right-container")]//tr[%d]/td[1]' % row value = driver.find_element_by_xpath(value_xpath).text costs[title] = value # post slack webhook_url = os.environ.get("WEBHOOK_URL") slack = slackweb.Slack(url=webhook_url) # create message text = target_at.strftime('%Y/%m/%d') + "\n" text += "```\n" for k, v in costs.items(): text += k + ":\t" + v +"\n" text += "```\n" text += "<"+slack_link+"|cost explorer>" slack.notify(text=text) driver.quit()
Webコンソールだとデフォルトでコストがかかっている順にソートしてくれているので、Totalを含めた上位5件をループで取得するようにしています。
後はslackに送るようテキストを整形しています。
incoming webhookの用意
ググるといくらでも情報が出てくるのでそちらを参考に。。
動作検証
ビルドします。
$ docker build -t aws-cost-explorer .
実行します。
$ docker run --name cost_explorer \ -e ACCOUNT_ID=wwwwwwwwww \ -e USERNAME=xxxxxxxxxxxx \ -e PASSWORD=yyyyyyyyyyyy \ -e TARGET_DATE=1 \ -e WEBHOOK_URL=https://hooks.slack.com/services/xxxxxxx/yyyyyyyyyy/zzzzzzzzzzzzzzzzzzzzzzzzz \ aws-cost-explorer
README.mdにも書いてありますが、パラメータは以下の通りです。
環境変数 | 値 |
---|---|
ACCOUNT_ID | AWSのアカウントID |
USERNAME | Billingの権限持ったIAMユーザ |
PASSWORD | パスワード |
TARGET_DATE | 何日前のレポートにするか |
WEBHOOK_URL | slackに飛ばすwebhookのURL |
すると以下の様に通知が飛んできます。
リンク先にはCostExplorerのページがあります。
週や月比較したい時はプルダウンをちょっといじればすぐできます。
まとめ
先日CostExplorerのAPIが提供されました。なのでそちらを使うのも選択肢としてはもちろんありだと思います。
僕の場合は後からリンクで週比較・月比較とかもサクッと見れたら嬉しかったので、今回のようにWebコンソールをスクレイピングする形にしました。