serverless frameworkを使って毎日AWSのコストをSlackに飛ばすlambda関数を作ってみる（実用コードの記載）

## 概要

serverless frameworkを使って毎日AWSのコストをSlackに飛ばすlambda関数のコードを実際に動作するコードに書き換える。
今まではコスト取得期間がハードコーディングされていたが、今回から動的に情報を取得しSlackに通知を投げる部分も作成する。

## 前提

下記の内容が完了していること。

https://qiita.com/miriwo/items/bf274fa6af25a396f05d

https://qiita.com/miriwo/items/d4aee0c6dc020e134145

## 方法

- 下記の内容を参考にSlackAppを作ってWebhook URLを発行し、メモする

https://qiita.com/miriwo/items/cbb4b37b3f5abecc835f

- 下記をプロジェクトディレクトリで実行してaxiosをインストール

    ```terminal
    npm install axios
    ```

- aws-cost-notify/handler.jsを下記のように書き換える

    ```aws-cost-notify/handler.js
    const AWS = require('aws-sdk');
    const costExplorer = new AWS.CostExplorer({ region: 'us-east-1' });
    const axios = require('axios');
    
    exports.notify = async (event, context) => {
      try {
        console.log('コスト通知イベントを受信しました:', JSON.stringify(event));
    
        const todayDate = new Date(); // lambdaのタイムゾーンはUTC
        const todayYear = String(todayDate.getFullYear());
        // padStartを使って1桁の場合0埋め
        const todayMonth = String(todayDate.getMonth() + 1).padStart(2, '0'); // 月は1月が0で返るので+1
        const todayDay = String(todayDate.getDate()).padStart(2, '0');
        const todayString = todayYear + '-' + todayMonth + '-' + todayDay;
        const firstDayOfMonthString = todayYear + '-' + todayMonth + '-01';
    
        const isFirstDayOfMonth = firstDayOfMonthString === todayString;
        const defaultDayOfMonthAWSCost = 0; // マジックナンバーを避けるためにあえて定数を定義
        let monthlyAWSCost = defaultDayOfMonthAWSCost;

        if (!isFirstDayOfMonth) {
          const params = {
            TimePeriod: {
              Start: firstDayOfMonthString, // 取得開始日
              End: todayString, // 取得終了日
            },
            Granularity: 'MONTHLY',
            Metrics: ['UnblendedCost']
          };
          const costExplorerData = await costExplorer.getCostAndUsage(params).promise();
          monthlyAWSCost = costExplorerData.ResultsByTime[0].Total.UnblendedCost.Amount;
        }
        const slackMessageText = '今月のAWS使用量は' + monthlyAWSCost + ' ドルです';
    
        await sendSlackMessage(slackMessageText);
    
        message = 'コスト通知が正常に処理されました';
        return ok(message);
      } catch (error) {
        console.error('エラーが発生しました:', error);
        return internalServerError();
      }
    };
    
    // Slackにメッセージを送信
    async function sendSlackMessage(slackMessageText) {
      const webhookURL = '皆さんのSlack AppのWebhook URL';
      const slackMessage = {
        text: slackMessageText,
      };
  
      try {
        const response = await axios.post(webhookURL, slackMessage);
        console.log('Slack通知のレスポンス:', response.data);
      } catch (error) {
        console.error('Slack通知のエラー:', error);
      }
    }
    
    // 200を返す
    function ok(message = 'OK') {
      return {
        statusCode: 200,
        body: JSON.stringify({ message: message })
      };
    }
    
    // 500を返す
    function internalServerError(message = 'Internal Server Error') {
      return {
        statusCode: 500,
        body: JSON.stringify({ message: message })
      };
    }
    ```

- 下記をプロジェクトディレクトリで実行

    ```terminal
    serverless invoke local --function notify --data '{"source":"aws.cost","detail-type":"AWS Cost and Usage Report","detail":{"key1":"value1"}}'
    ```

- 下記のように指定したSlackチャンネルに料金が投稿されたら一旦完了

    ![CleanShot 2024-09-03 at 00.11.28@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/3b81567a-8dc6-4452-14b1-e6327e0a8107.png)