LambdaでAWS利用料金をSlackへDM送信する方法

# やりたいこと
最近、AWSのAI系サービスを検証しており、あまり詳しくないサービスを触っているうちに、知らないうちにAWS料金が大量にかかってしまうリスクを感じています。

毎日マネジメントコンソールで料金を確認するのが面倒なので、AWS利用料金を定期的にSlackに送信する仕組みを作ってみました。

こんなことをやりたいです↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/ea37ed93-6cdc-4e3d-8930-2a600bf2e75b.png)

# ①Slackアプリの作成
以下のURLにアクセスして、Slackアプリを作ります
https://api.slack.com/apps?new_app=1

「From a manifest」で作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/4086c886-77cd-9f6c-b2e4-d3ae8bb0671b.png)

ワークスペースを選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/892db475-27fb-4e0e-5057-1b87391498de.png)

manifestを設定。アプリ名を決めて、その他は全部デフォルトで大丈夫。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/13536d96-f7fd-7094-af45-5a906198da7c.png)

設定内容をレビューして、「Create」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/9104e105-6247-cde5-8ef5-c779d535a14c.png)

# ②SlackのIncoming　Webhooksを設定
アプリ作成できたら、このような画面に遷移されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/4430c6f2-6eb4-f3a1-ac08-a9d174849354.png)


左メニューの「Incoming　Webhooks」をクリックし、設定を「On」に
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/5ed55c75-185b-5d76-3a80-514d9358f57c.png)

少し下スクロールして、「Add New Webhook to Workspace」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/b4fe40dc-55bd-202c-13fe-0ac362c1fe9e.png)

チャンネル検索のところに、自分自身の名前を検索し設定し、「許可」
（特定のチャンネルへ投稿する場合はチャンネル名で設定しますが、今回は自分へDMを送りたいため名前で検索します。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/6ecf4b2b-182f-f219-d4b6-61b544e8226c.png)

すると、Webhook URLが作成されました。いったんコピーしておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/2429507c-f61c-f716-7520-609b5659ff36.png)

# ③Parameter Storeの設定
AWSの「System Manager」を開き、左メニューの「パラメータストア」をクリックし、「パラメータの作成」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/1ef75e6e-62a7-ae67-992f-62de324b708f.png)

パラメータ名を入力し、「標準」利用枠にします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/f355167f-5f34-dbc7-aa47-07f2a2968752.png)

タイプを「文字列」にし、タイプ型を「text」にし、値を先ほどコピーしたSlackのWebhookで入力し、「パラメータを作成」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/0f401cb6-1523-dba1-6b7a-2f457303313d.png)

作成に成功しました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/b50ceb32-79bd-e06f-0c96-4a9e86be48d2.png)

# ④Lambdaの作成
AWSのLambdaを開き、「関数を作成」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/83bd1aee-7584-7fb5-6d90-ccff444d85c2.png)

「一から作成」を選択し、関数名を入力します。今回はPython3.13で作ります。「関数の作成」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/163063fe-56dd-7ea8-3390-c8df78bde4da.png)

作成完了後、この画面に遷移
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/564a9fab-450f-f3be-09ee-e94bb436550e.png)

以下のソースでデプロイします。
```
import base64
import urllib3
import json
import boto3
import datetime
 
http = urllib3.PoolManager()

def lambda_handler(event, context):
    # SSMクライアントを作成
    ssm_client = boto3.client('ssm')

    # パラメータを取得
    parameter_name = 'slack-url'  # Parameter Storeに保存されているパラメータ名
    response = ssm_client.get_parameter(Name=parameter_name, WithDecryption=True)
    
    # URLを取得
    slack_endpoint = response['Parameter']['Value']
    print(f"Retrieved URL: {slack_endpoint}")

    # Cost Explorer クライアントの作成
    ce_client = boto3.client('ce')
    
    # 今日の日付と月初の日付を取得
    today = datetime.date.today()
    start_of_month = today.replace(day=1)

    try:
        response = ce_client.get_cost_and_usage(
            TimePeriod={
                'Start': start_of_month.strftime('%Y-%m-%d'),
                'End': today.strftime('%Y-%m-%d')
            },
            Granularity='MONTHLY',
            Metrics=['UnblendedCost']
        )
        
        # 合計料金を取得
        total_cost = response['ResultsByTime'][0]['Total']['UnblendedCost']['Amount']
        currency = response['ResultsByTime'][0]['Total']['UnblendedCost']['Unit']

        msg = {
            "text": f"今月のAWS料金：{total_cost} {currency}",
        }
        encoded_msg = json.dumps(msg).encode('utf-8')
        resp = http.request('POST', slack_endpoint, body=encoded_msg)

        print(resp)

        return {
            "statusCode": 200,
            "body": json.dumps("Message sent successfully!")
        }
    except Exception as e:
        return {
            "statusCode": 500,
            "body": json.dumps(str(e))
        }

```
### ソースの実施内容
SSMからSlackのWebhook URLを取得
↓
Cost Exploreから今月のAWS利用料金を取得
↓
SlackのDMで自分へ送る

# ⑤Lambdaのレイヤー設定
しかし、Lambdaでパラメータストアからパラメータを取りたい場合、aws-sdkだけでは足りないです。レイヤーの設定が必要となります。

画面を少し下スクロールし。「レイヤー」の「レイヤーを追加」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/fc275c3f-2419-27ac-f701-0cf74fd9d66a.png)

「AWSレイヤー」を選択し、「AWS-Parameters-and-Secrets-Lambda-Extension」の最新バージョンを選択し、「追加」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/1fcfab56-0f29-0e7f-64d9-a26f421407d1.png)

# ⑥LambdaのIAMロール設定
Lambdaに「SSM」と「Cost Explore」へのアクセス権限を付与する必要があります。

「設定」＞「アクセス権限」をクリックします。デフォルトで作成されたIAMロールはCloudWatch関連の権限しかないはずですので、ロールをクリックし編集します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/f7cd6a77-3a22-c262-3637-cbdff5054e15.png)


IAMの編集画面へ遷移します。ポリシーをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/9d1cf0a0-2c32-9394-f631-154f3b928ec5.png)

「編集」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/c79380ac-564c-d654-b8f2-54fad18e3a09.png)

「ビジュアル」タブをクリックし、「許可をさらに追加」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/2c72a82c-3383-a51a-a334-a1df63c4aafe.png)

Cost Explorer Serviceに関する以下の権限を与えます。
ce:GetCostAndUsage
ce:DescribeReport

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/7deb6a00-1f6f-3ede-6dd6-f5e5766b36a5.png)

Systems Managerに関する以下の権限を与えます。
ssm:GetParameter
リソースは作ったパラメータのARNにします
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/07bdb578-f3cf-52ef-aa54-ef8d6b510cc8.png)

「次へ」＞「変更を保存」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/f656926f-4dcc-457e-b9cf-6b005a2f013d.png)

# ⑦Lambdaのタイムアウト設定
もう一度Lambdaに戻ります。

デフォルトのタイムアウト時間が足りない恐れがありますので、「設定」＞「一般設定」＞「編集」

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/a03fddf5-be5e-8384-0dfa-d444f7d0062b.png)

10秒まで増やして「保存」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/1b9f2943-faa0-f245-40f7-1a9fc157aac3.png)

# ⑧Lambdaのトリガー設定
毎朝九時に、定期的にAWS料金を送ってもらいたいので、定期実行のトリガーを設定します。

「関数の概要」＞「トリガーを追加」をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/568f6802-4937-fd84-96a3-8ac96b1154d1.png)


トリガーの設定に「Event Bridge」を検索
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/f02515fc-bf3f-2283-4f7a-11f00d313cdb.png)

「新規ルールを作成」をクリックし、ルール名を入力し、「追加」
「スケジュール式」を選択し、「cron(0 0 * * ? *)」を入力します。（UTC時間の朝九時の意味）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/1e8af785-6637-b398-c6aa-056ae0273fa4.png)

すると、トリガーが追加されました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/9d71434d-486d-5ee9-4355-b0755998071c.png)

「Event Bridge」を確認すると、イベントスケジュール の内容がこのように表示され、以後10回のトリガー時間が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/a22f598f-dc9b-841e-57ba-a1e922aa7479.png)

# ⑨SlackのDMを待つ
明日の朝九時まで待つのは面倒くさいので、いったんトリガーのスケジュールを「cron(40 2 * * ? *)」（朝11:45）に変えてみて、結果を見ましょう！

すると、時間になったらメッセージが送られてきました！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/936708ad-c08f-9682-a688-aad37f260ff2.png)

メッセージ内の料金は、請求内容とは一致しています！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3538617/73a68158-6e8a-29bb-99b6-4dc5b87c87a4.png)

# 参考サイト
https://www.softbank.jp/biz/blog/cloud-technology/articles/202301/deploy-a-slack-using-lambda/

https://qiita.com/y-dobashi/items/64ca17795a9e6a278f97