Serverlessで日々のAWSコストを算出してSlackへデイリー通知する

最近、Cost Explorer APIというものが新しくリリースされ、請求ダッシュボードから見られるコストが取得できるようになりました。
「[Introducing the AWS Cost Explorer API](https://aws.amazon.com/jp/about-aws/whats-new/2017/11/introducing-the-aws-cost-explorer-api/)」

これを使用し、Serverless FrameworkでEC2やCloudFrontなど各サービス毎のコストを算出してみます。

## serverless.yml

```yaml:serverless.yml
service: cost-checker

provider:
  name: aws
  runtime: nodejs6.10
  region: us-east-1

  iamRoleStatements:
    - Effect: Allow
      Action:
        - ce:GetCostAndUsage
      Resource: "*"

functions:
  costCheck:
    handler: cost.check
    events:
      - schedule: cron(0 1 * * ? *)
    timeout: 300
```

`Cost Explorer API` はバージニア北部でしか使えないため `region: us-east-1` を指定しdeployします。
cronはUTC表記のため、今回は日本時間の朝10時にCloudWatchEventsでLambdaが実行されます。


## Cost Explorer API

```javascript:cost.js
const AWS = require('aws-sdk');

const costexplorer = new AWS.CostExplorer();

module.exports.check = () => {
  const params = {
    Granularity: 'DAILY',
    Metrics: [ 'UnblendedCost' ],
    GroupBy: [{
      Type: 'DIMENSION',
      Key: 'SERVICE',
    }],
    TimePeriod: {
      Start: '2017-12-07',
      End: '2017-12-08',
    },
  };

  costexplorer.getCostAndUsage(params, (err, data) => {
    if (err) {
      console.error(err, err.stack);
      return;
    }

    console.log(data);    
  });
};
```

これだけで`2017-12-07`の各コストが取得できます。
細かいAPIの使い方はこちら
http://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/CostExplorer.html



## 動的に日付を取得する

これだけでは、`2017-12-07`の分しか取得できないため、TimePeriodを動的に指定できるようにします。

```javascript
const _ = require('lodash');

const now = new Date();
const start = new Date(now.getFullYear(), now.getMonth(), now.getDate()　- 1);
const end = new Date(now.getFullYear(), now.getMonth(), now.getDate());

const TimePeriod = {
  Start: `${start.getFullYear()}-${_.padStart(start.getMonth() + 1, 2, 0)}-${_.padStart(start.getDate(), 2, 0)}`,
  End: `${end.getFullYear()}-${_.padStart(end.getMonth() + 1, 2, 0)}-${_.padStart(end.getDate(), 2, 0)}`,
};
```

`now.getDate()　- 1`で実行日の前日を取得、この方法であれば月をまたいだときも日付を自動で生成してくれます。
また、lodashを使い`_.padStart()`で１桁の場合でも簡単に`0`で埋めることができます。


## Slackに送る

かなり雑ですが、`costexplorer.getCostAndUsage()`で取得した値を、Slackのmessage apiのattachments.fieldsに詰めて送信しています。

```javascript
const costs = [];
_.each(data.ResultsByTime, (item) => {
  _.each(item.Groups, (group) => {
    costs.push({
      serviceName: _.head(group.Keys),
      value: _.round(group.Metrics.UnblendedCost.Amount * 120, 2),
    });
  });
});

const message = {
  username: 'コストさん',
  channel: '#cost',
  text: `${TimePeriod.Start}のコストです。:money_with_wings:`,
  attachments: [
    {
      fields: _.map(costs, (item) => {
        return {
          title: item.serviceName,
          value: item.value,
          short: true,
        }
      });,
    }
  ],
};

slack.post(message);
```
金額はドルで返ってくるので、１ドル120円換算にして、小数点２位まで表示

Slack上では、こんな感じで投稿されて便利です。
![sc.png](https://qiita-image-store.s3.amazonaws.com/0/7132/c11d8bea-d490-2877-005d-86f0ba6e8924.png)


## おわり
実際のところ、デイリーでコストを出しても毎日のアクセス数の上下で変わってくるので、よくわからないｗ
でも、一気にコストを削ったときにこうして見られるのは楽しい。一時的に使ったサーバーを消し忘れたとか、設定のミスとかもこういったレポートで、すぐに気がつけるんじゃないかなーと思います。
何よりメンバーにコスト感が生まれるし、どのAWSサービス使ってるかわかって良い。

Cost Explorer APIは１コール$0.01と結構お高い額を請求されます。
基本的に乱打するものではないと思いますが注意！