LoginSignup
11
12

More than 5 years have passed since last update.

AWSの利用料金をPythonからSlackに投稿する

Last updated at Posted at 2019-01-03

Pythonのお勉強のため、AWSの利用料金をSlackに投稿するようにしたメモ。

参考リンク

以下の先人の記事を参考に作成。

動作イメージ

image.png

Slackの準備

何かと使えそうだし個人用Slackを作ることにする。

Webhook URLの取得

以下を参考にWebhook URLを取得する。

curlでテスト

curlで投稿をテストする。

CHANNEL="#sandbox"
WEBHOOK_URL="https://hooks.slack.com/services/hogehoge"
MESSAGE="テストメッセージ"
curl -X POST ${WEBHOOK_URL} \
  --data-urlencode "payload=
  {\"channel\": \"${CHANNEL}\",
   \"text\": \"${MESSAGE}\"}"

以下のように投稿されればOK。

image.png

AWS CLI

Pythonの前にAWS CLIで料金を取得してみる。

AWS CLIをセットアップする。

pip3 install awscli
aws configure

料金を確認する。

コマンド
aws cloudwatch get-metric-statistics \
  --region us-east-1 \
  --namespace "AWS/Billing" \
  --metric-name "EstimatedCharges" \
  --start-time "2019-01-01T00:00:00Z" \
  --end-time "2019-01-02T00:00:00Z" \
  --period 86400 \
  --statistics Maximum \
  --dimensions Name=Currency,Value=USD
実行例
$ aws cloudwatch get-metric-statistics \
>   --region us-east-1 \
>   --namespace "AWS/Billing" \
>   --metric-name "EstimatedCharges" \
>   --start-time "2019-01-01T00:00:00Z" \
>   --end-time "2019-01-02T00:00:00Z" \
>   --period 86400 \
>   --statistics Maximum \
>   --dimensions Name=Currency,Value=USD
EstimatedCharges
DATAPOINTS  0.45    2019-01-01T00:00:00Z    None
$

Python + AWS Lambda

AWS Lambda上のPythonからSlackに投稿する。

IAM Roleの作成

IAMコンソールでRoleを作成し、AWSLambdaBasicExecutionRoleCloudWatchReadOnlyAccessのポリシーをアタッチする。

image.png

作成したRoleのARNを確認しておく。

コード作成

作成するファイルは以下の3つ。→GitHub

.
├── lambda.json
├── lambda_function.py
└── requirements.txt

lambda.jsonはlambda-uploderが使用するJSON形式のLambda関数の定義ファイル。先ほど作成したRoleのARN、Slack Webhook URL、Slackチャンネル名を指定する。lambda-uploaderの使い方と、jsonの書き方は以下を参照。

lambda.json
{
  "name": "aws_cost_checker",
  "description": "My AWS cost checker.",
  "region": "ap-northeast-1",
  "runtime": "python3.7",
  "handler": "lambda_function.lambda_handler",
  "role": "arn:aws:iam::hogehoge",
  "timeout": 300,
  "memory": 128,
  "variables":
    {
      "SLACK_WEBHOOK_URL": "https://hooks.slack.com/services/hogehoge",
      "SLACK_CHANNEL": "#hogehoge"
    }
}

lambda_function.pyが関数本体。以下を参考に作成。Low-Level ClientではなくResourceクラスを使ってみる。

lambda_function.py
import datetime
import json
import os

import boto3
import pytz
import requests

SLACK_WEBHOOK_URL = os.environ['SLACK_WEBHOOK_URL']
SLACK_CHANNEL = os.environ['SLACK_CHANNEL']


def get_cost():
    """boto3を使って直近の1日の最大の予想請求額を取得する

    :return: response
    """

    now = datetime.datetime.now()

    # Low-Level Clientの場合
    #
    # client = boto3.client('cloudwatch', region_name='us-east-1')
    # response = client.get_metric_statistics(
    #     Namespace='AWS/Billing',
    #     MetricName='EstimatedCharges',
    #     Dimensions=[
    #         {
    #             'Name': 'Currency',
    #             'Value': 'USD'
    #         }
    #     ],
    #     StartTime = now - datetime.timedelta(days=1),
    #     EndTime = now,
    #     Period=86400,
    #     Statistics=['Maximum']
    # )

    cloudwatch = boto3.resource('cloudwatch', region_name='us-east-1')
    metric = cloudwatch.Metric('AWS/Billing', 'EstimatedCharges')
    response = metric.get_statistics(
        Dimensions=[
            {
                'Name': 'Currency',
                'Value': 'USD'
            },
        ],
        StartTime=now - datetime.timedelta(days=1),
        EndTime=now,
        Period=86400,
        Statistics=['Maximum']
    )

    return response


def build_message(response):
    """SlackにPOSTするメッセージボディを作成する

    :param response:
    :return: message
    """

    cost = response['Datapoints'][0]['Maximum']
    timestamp = (response['Datapoints'][0]['Timestamp'] + datetime.timedelta(days=1)).astimezone(
        pytz.timezone('Asia/Tokyo')).strftime(
        '%Y年%m月%d日%H時%M分')

    text = '{}までのAWSの料金'.format(timestamp)
    attachment_text = '${}'.format(cost)

    if float(cost) >= 100.0:
        color = 'danger'   # red
    elif float(cost) >= 10.0:
        color = 'warning'  # yellow
    else:
        color = 'good'     # green

    atachements = {'text': attachment_text, 'color': color}

    message = {
        'text': text,
        'channel': SLACK_CHANNEL,
        'attachments': [atachements],
    }

    return message


def post_message(message):
    """SlackにPOSTする

    :param message:
    :return:
    """

    response = requests.post(SLACK_WEBHOOK_URL, data=json.dumps(message))
    response.raise_for_status()


def lambda_handler(event, context):

    response = get_cost()

    message = build_message(response)

    post_message(message)

requirements.txtは使用するモジュールの定義。

requirements.txt
boto3
pytz
requests

アップロード

lambda-uploaderをインストールする。

pip3 install lambda-uploader 

アップロードする。

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

スケジュール実行

lambda-uploaderによってLambda関数が作成されるので、LambdaコンソールからトリガーとしてCloudWatch Eventsを追加する。

image.png

スケジュールを設定する。とりあえずcron(* * * * ? *)毎分実行するようにする。

image.png

Slackに投稿されることを確認する。

image.png

上手く投稿できなかったら、CloudWatch Logsでログを確認する。上手く投稿が確認できたら、スケジュールをcron(0 15 * * ? *)(日本時間の0時)に変更する。

image.png

11
12
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
12