📲

【10分レシピ👩‍🍳】あなたのAWSコストをLINE通知しよう 💰️📲

に公開

AWS利用者の必須設定です。
さぁ。あなたは10分でできるかな^^

手順

LINE側

1. LINE公式アカウントを作成する

LINE公式ドキュメントを見ながら公式アカウントを開設してください

https://developers.line.biz/ja/docs/messaging-api/getting-started/#create-oa

2. LINE公式アカウントでMessaging APIを有効にする

公式ドキュメントを見ながらMessaging APIを有効化してください。

https://developers.line.biz/ja/docs/messaging-api/getting-started/#using-oa-manager

3. チャネルアクセストークン(APIキー)を取得する
  1. LINE Developerコンソールにアクセスして、2で作成したプロバイダを選択してください

  2. チャネル詳細へアクセスします

  3. Messaging API設定からチャネルアクセストークンを取得します

4. あなたのLINEユーザーIDを取得
  1. 先ほどと同じLINE Developer画面のMessaging API設定のタブを開きます

  2. スマホでQRコードを読みとって公式アカウントを開き、友達追加します

  3. 公式LINEアカウント管理画面を開き、今回作成したアカウントを選択します

  4. 設定へ移動

  5. 応答設定へ

  6. WebhookをONにして、Messaging APIを開きます

Webhookの入力欄が表示されることを確認

  1. Webhook URLを取得したいので、このサイトを開きます

https://webhook.site

  1. Webhook URLのYour unique URLをコピーします

  2. コピーしたURLをMessaging APIのWebhook URLに貼り付けて保存します

  3. あなたのLINEアプリから、今回作成した公式ラインアカウントに何でもよいのでメッセージを送ります

  4. すると8の https://webhook.site にて、先ほど保存したWebhookにメッセージが届いているのが分かります。LINEユーザーIDをメモしておいてください。

これでLINE側の作業は完了です!
次はAWS側で作業します!

AWS側

1. AWSでCost Explorer を有効にする

AWS公式ドキュメントを見て有効化します。

https://docs.aws.amazon.com/ja_jp/cost-management/latest/userguide/ce-enable.html

2. Lambda関数用のIAMポリシー作成

AWSコンソールにログインします。
IAMポリシーポリシーの作成からJSONで以下のポリシーを作成します。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "NotifyAwsCostToLineLambdaPolicy",
            "Effect": "Allow",
            "Action": [
                "logs:CreateLogGroup",
                "logs:CreateLogStream",
                "logs:PutLogEvents",
                "ce:GetCostAndUsage"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}
3. ↑のIAMポリシーをアタッチしたIAMロール作成

次に IAMロールロールを作成からロールを作成します。

先ほど作成したポリシーをアタッチしてください。

4. Lambda関数作成

次に Lambda関数の作成 でLambda関数を作成します。

基本設定

環境変数の設定
LINE_CHANNEL_ACCESS_TOKENにはLINE側の作業で取得したチャネルアクセストークンを
LINE_USER_IDにはLINEユーザーIDをそれぞれ設定してください。

コード追加

import os
import boto3
from datetime import datetime, timedelta, date
import json
import urllib.request

# 環境変数からLINE BotのチャネルアクセストークンとユーザーIDを取得
LINE_CHANNEL_ACCESS_TOKEN = os.environ['LINE_CHANNEL_ACCESS_TOKEN']
LINE_USER_ID = os.environ['LINE_USER_ID']

def lambda_handler(event, context) -> None:
    client = boto3.client('ce', region_name='us-east-1')

    # 合計の請求額を取得する
    total_billing = get_total_billing(client)

    # Line用のメッセージを作成して投げる
    message = get_message(total_billing)
    post_line(message)


def get_total_billing(client) -> dict:
    (start_date, end_date) = get_cost_date_range()

    response = client.get_cost_and_usage(
        TimePeriod={
            'Start': start_date,
            'End': end_date
        },
        Granularity='MONTHLY',
        Metrics=[
            'AmortizedCost'
        ]
    )

    return {
        'start': response['ResultsByTime'][0]['TimePeriod']['Start'],
        'end': response['ResultsByTime'][0]['TimePeriod']['End'],
        'billing': response['ResultsByTime'][0]['Total']['AmortizedCost']['Amount'],
    }

def get_message(total_billing: dict) -> (str):
    start = datetime.strptime(total_billing['start'], '%Y-%m-%d').strftime('%m/%d')

    # Endの日付は結果に含まないため、表示上は前日にしておく
    end_today = datetime.strptime(total_billing['end'], '%Y-%m-%d')
    end_yesterday = (end_today - timedelta(days=1)).strftime('%m/%d')

    total = round(float(total_billing['billing']), 2)

    title = f'{start}{end_yesterday}の請求額は、{total:.2f} USDです。'

    return title

def post_line(title: str) -> None:
    url = 'https://api.line.me/v2/bot/message/push'
    headers = {
        'Content-Type': 'application/json',
        'Authorization': 'Bearer ' + LINE_CHANNEL_ACCESS_TOKEN
    }
    body = {
        'to': LINE_USER_ID,
        'messages': [
            {
                "type": "text",
                "text": title,
            }
        ]
    }

    try:
        req = urllib.request.Request(url, data=json.dumps(body).encode(), method='POST', headers=headers)
        urllib.request.urlopen(req)
            
        return {
            'statusCode': 200,
        }
    except urllib.request.exceptions.RequestException as e:
        print(e)
    else:
        print(response.status_code)


def get_cost_date_range() -> (str, str):
    start_date = get_first_day_of_month()
    end_date = get_today()

    # get_cost_and_usage()のstartとendに同じ日付は指定不可のため、
    # 「今日が1日」なら、「先月1日から今月1日(今日)」までの範囲にする
    if start_date == end_date:
        end_of_month = datetime.strptime(start_date, '%Y-%m-%d') + timedelta(days=-1)
        begin_of_month = end_of_month.replace(day=1)
        return begin_of_month.date().isoformat(), end_date
    return start_date, end_date

def get_first_day_of_month() -> str:
    return date.today().replace(day=1).isoformat()

def get_today() -> str:
    return date.today().isoformat()
5. 通知スケジュール作成

Amazon EventBridgeスケジュールスケジュールを作成

スケジュールの設定

実行するLambda関数を設定

これでおしまい!
あとは寝て翌朝を待ちましょう。
起床後、、

キタ━━━━(゚∀゚)━━━━!!

これで前日からの増分も分かるようになり、不要なリソースの消し忘れや急激なコスト増にすぐ気付けます!

お片付け (忘れずに⚠️

LINE側作業の4-6で有効化したWebhookは不要と思うのでOFFにしておきましょう!

終わりに

LINEのチャネルアクセストークンやユーザーIDの取得が思いのほか大変だったので手順として記事にしました。
Slackとかならwebhook URLを発行するだけなのでそれと比べてしまう。

ただ、LINEという日に何度も確認するアプリに通知ができるのは通知効果が高く、リマインダーやアラート系とは相性がよいので予定を覚えるのが苦手な人には便利な使い方です。

今度は「朝イチで今日のGoogleスケジュールを送ってくれるLINE通知」を作ろうと思います!
生成AIと組み合わせたりしたら楽しそう^^

LINEは使い方次第でかなりいろんなことができるので希望に胸が膨らみます!!

参考記事

お世話になりました。Pythonコードなどはそのまま使用させて頂きました🙇
https://techblog.ap-com.co.jp/entry/notify-aws-cost-to-line

Discussion