AWSの今月の使用料を毎日slackに通知する

# はじめに
AWSには「CostExplorer」というAWSのコスト関連を管理&分析してくれるサービスがあります。
このサービスで現在のAWS使用料も把握することもできます。
私はプライベートで色々試すことが多いので、常に使用料を把握しておきたいのですが、確認の度にAWSにログインするのがとても面倒でした。
なので、毎日slackに使用料を通知するようにしました。

# やりたいこと
LambdaとEventBridgeを使って、月初からその日までの合計使用料を毎日0時にslackに通知する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/975289/3926b024-34c6-5f32-343a-055adcf06734.png)

# 事前準備
- 通知するslackチャンネルを作成しておく
- Incoming Webhookの設定を行い、URLを取得しておく  
[【2020年度版】Slack通知はSlack AppのIncoming Webhooksを使おう！やり方を解説](https://keyama.hatenablog.com/entry/2020/04/21/181451)

# やってみる
### 1. Lambda関数を作成
「CostExplorerから今月の使用料を取得して、slackにそのデータを通知する」という内容でLambda関数で作成します。

「名前」と「利用する言語」を入力して、Lambda関数を作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/975289/1ef3e60d-b45c-e7aa-b9d7-c29ae3d949b4.png)



以下のように関数のコードを編集

```python
from datetime import datetime
import json
import os
import urllib

import boto3
SLACK_WEBHOOK_URL = "https://hooks.slack.com/services/YYYYYY/XXXXXXXX"		# slackのwebhook url
SLACK_CHANNEL_NAME = "#notice_aws_cost"		# 通知先のslackチャンネル名


def lambda_handler(event, context):
    today = datetime.now()  # UTC時間

    # Cost Explorerから今月の使用料を取得
    client = boto3.client('ce')
    response = client.get_cost_and_usage(
        TimePeriod={
            'Start': str(today.strftime('%Y-%m')) + '-01',
            'End': today.strftime('%Y-%m-%d')
        },
        Granularity='MONTHLY',
        Metrics=[
            'AmortizedCost'
        ]
    )
    billing_dollers = response["ResultsByTime"][0]["Total"]["AmortizedCost"]["Amount"]
    msg = f"""{today.strftime("%Y/%m/%d")}時点での今月の合計請求額をお知らせします。
合計請求額：{billing_dollers} USD"""
    post_slack(msg)


def post_slack(msg):
	# メッセージデータを作成
    post_data = {
        "username": "noticeBot",				#ユーザー名
        "icon_emoji": ":money_with_wings:",		#アイコン
        "text": msg,							#メッセージ（※メンションを付与してる）
        "channel": SLACK_CHANNEL_NAME			#チャンネル
    }
    encode_post_data = ("payload=" + json.dumps(post_data)).encode('utf-8')
	# Request(post)を作成
    request = urllib.request.Request(
        SLACK_WEBHOOK_URL,
        data=encode_post_data,
        method="POST"
    )
    # 送信
    urllib.request.urlopen(request)
```
  
以下、コード内容の補足説明

```python
# Cost Explorerから今月の使用料を取得
client = boto3.client('ce')
response = client.get_cost_and_usage(
    TimePeriod={
        'Start': str(today.strftime('%Y-%m')) + '-01',
        'End': today.strftime('%Y-%m-%d')
    },
    Granularity='MONTHLY',
    Metrics=[
        'AmortizedCost'
    ]
)
billing_dollers = response["ResultsByTime"][0]["Total"]["AmortizedCost"]["Amount"]
```
CostExplorerのGetCostAndUsageというAPIを利用して、今月の1日から今日までの合計使用料を取得してます。
今回は月の使用料を取得してますが、日ごとの使用料も取得できます。また、サービスの合計ではなく各々のサービス使用料なども取得することができます。
[GetCostAndUsage - AWS Cost Explorer Service](https://docs.aws.amazon.com/ja_jp/aws-cost-management/latest/APIReference/API_GetCostAndUsage.html)
[Serverlessで日々のAWSコストを算出する](https://qiita.com/narikei/items/3f772c12591afdc5262d)

```python
def post_slack(msg):
	# メッセージデータを作成
    post_data = {
        "username": "noticeBot",		#ユーザー名
        "icon_emoji": ":money:",		#アイコン
        "text": msg,					#メッセージ（※メンションを付与してる）
        "channel": SLACK_CHANNEL_NAME	#チャンネル
    }
    encode_post_data = ("payload=" + json.dumps(send_data)).encode('utf-8')
	# Request(post)を作成
    request = urllib.request.Request(
        SLACK_WEBHOOK_URL,
        data=encode_post_data,
        method="POST"
    )
    # 送信
    urllib.request.urlopen(request)
```
slackにメッセージを送信する関数です。
`SLACK_CHANNEL_NAME`、`SLACK_WEBHOOK_URL`変数には、各自適した値を代入してください。


### 2. IAM Roleにポリシーを追加
Lambdaを作成すると自動でIAM Roleも一緒に作成されます。
そのIAM Roleには、Cost ExplorerのGetCostAndUsageの利用権限が付与されていません。
今の状態でLambdaを実行するとエラーになりますので、権限を付与します。

Lambdaのアクセス権限タブを開き、実行ロールのリンクにアクセス
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/975289/92371c71-8b4a-ef68-bb06-83c0f210ebc0.png)
インラインポリシーの追加
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/975289/c8fb9a8e-331e-f911-df3c-552e8d0f862a.png)
以下のようにJSONを記述し、名前をつけて作成

```json
{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Sid": "VisualEditor0",
			"Effect": "Allow",
			"Action": "ce:GetCostAndUsage",
			"Resource": "*"
		}
	]
}
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/975289/e1d2902b-b773-d6c5-a999-a4a86261bd72.png)

### 3. EventBridgeルールを作成
Lambda関数を作成したことで、使用料をslackに通知できるようになりました。
しかし、Lambdaには定期実行という仕組みがないので、毎日0時に実行することはできません。
それを実現するためにEventBridge ルールを作成します。
EventBridgeとは簡単に言うと、何かイベントが発火したらそれをトリガーにして、予め設定してるAWSリソースを実行してくれるサービスです。
　[Amazon EventBridge とは - Amazon EventBridge](https://docs.aws.amazon.com/ja_jp/eventbridge/latest/userguide/what-is-amazon-eventbridge.html)

EventBridge ルールを以下の設定で作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/975289/11d03ff7-8902-b9c2-e33b-9cc4e2e8dbf7.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/975289/71c00de8-4e27-473a-1891-6a57619408b5.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/975289/040c4c6c-c3b7-f178-c824-63e65fad8c24.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/975289/3d4c0e48-370e-fb61-6926-329eec8fe575.png)
これで毎日0時にAWSの使用料がslackに通知されるようになりました。
注意
　cron式に「0 0 * * ? *」ではなく「0 15 * * ? *」と指定してるのは、cronが東京時間（UTC+9）ではなく標準時間（UTC+0）で動くので帳尻を合わせてます。

# 最後に
実はこのプログラム、実行されるたびに$0.01課金されます。
（CostExplorerのAPIを叩くときに$0.01のコストが発生）
無料で似たような事をしたいという方は、Budgetsを調べてみてください。