AWS利用金額と予測請求金額を日次で自動通知する

## はじめに
今回はLambdaで**get_cost_and_usage()**,**get_cost_forecast()**
のメソッドを使ってAWS利用金額と予測請求金額を取得しSNSで通知するLambdaを作成しました。

各メソッドは以下のように動作します。

- get_cost_and_usage()
AWSの利用額を取得するメソッドです。
2019年8月24日に8月に利用した金額を知りたい場合は、
STARTを2019年8月1日にしてENDを2019年8月24日にします。
すると2019年8月1日 00:00 ~ 2019年8月24日 00:00 (8月23日 24:00)の間に取得した金額が取得できます。
なので2019年8月全体の金額を知りたい場合は、STARTを2019年8月1日にしてENDを2019年9月1日

- get_cost_forecast()
AWSの予測金額を取得するメソッドです。
2019年8月24日に2019年8月の予測金額を知りたい場合は、
STARTを2019年8月25日にしてENDを2019年9月1日(8月31日 24:00)にします。
すると2019年8月の予測金額を取得できます。
注意として予測なのでSTARTは明日以降の日付しか設定できません。
本日より前の日付を指定すると以下のようなエラーメッセージが返されます。

メソッドの詳細は[こちら](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/ce.html)の公式サイト

```python
[ERROR] ClientError: An error occurred (ValidationException) when calling the GetCostForecast operation: Earliest supported Start is 2019-08-25
```

## APIで取得可能な請求情報(通知する金額)
1日・・・前月の金額の通知と。当月の予測金額
最終日・・・それまでに利用した金額(最終日は明日が無いので当月予測金額の取得ができない)
その間・・・当月の利用金額と当月の予測請求金額

表にすると以下のような形(逆に解り難いかな???)

|日程|get_cost_and_usage()の範囲|get_cost_forecast()の範囲|
|:--|:--|:--|
|1日|前月の1日から当月の1日(前月最終の 24:00)で前月の用金額|明日から来月初め(月末24:00)まで利用した場合の予測金額|
|最終日|当月の1日から本日までの利用金額|月末最終日=当月の明日が無いので使わない(使えない)|
|その間|当月の1日から本日までの利用金額|明日から来月初め(月末24:00)まで利用した場合の予測金額|

## 事前準備
#### 事前準備1. boto3のバージョンアップ  

get_cost_forecast()を使うにはLambdaデフォルトのランタイムPython3.7のboto3のバージョンを上げる必要があります。
今回はLambda Layerを用いてboto3のバージョンを上げた状態で実行します。
※上げないとこのようなメソッド対応してないので使えませんエラーが出るなのでboto3のバージョンを上げます。

```python
  "errorMessage": "'CostExplorer' object has no attribute 'get_cost_forecast'",
```



1. Cloud9(が楽だったので)で以下のコマンドを実行しzipファイルをローカルにダウンロード

```
$ mkdir python
$ pip install -t ./python boto3
$ zip -r boto3.zip python
```
<img width="858" alt="002.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322881/d3ab60ff-a7fc-bb68-5625-b224906c36ea.png">
2. Lambdaコンソールの画面でLayersを押下
<img width="859" alt="003.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322881/bd1e33c0-b51b-2aa8-d160-2cda44e29655.png">
3. レイヤーの作成を押下
<img width="930" alt="004.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322881/52cc91c4-d88d-0755-8152-09b805897acf.png">
4. 名前の入力/先ほどCloud9からダウンロードしたzipファイルをアップロード/ランタイムを入力し作成を押下
<img width="520" alt="005.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322881/a8f713f7-5517-e6e9-5ba4-f880d2a56a65.png">
5. 作成されたことを確認
<img width="905" alt="006.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322881/f49b491b-94ca-3d1c-6629-1dd5d1e0936a.png">
6. LambdaのLayersを選択しレイヤーの追加を押下
<img width="902" alt="007.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322881/26e7ea81-13f8-35e6-f1ac-c1312cf4df75.png">
7. 先ほど作成したレイヤーを選択し追加を押下
<img width="570" alt="008.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322881/d5629b69-88fa-54ee-7206-e4c93b09fd31.png">

[こちら](https://qiita.com/hayao_k/items/b9750cc8fa69d0ce91b0)の記事を参考にさせていただきました。すごくわかりやすかったです。


#### 事前準備2. Lambdaにロールの作成と付与
1. Lambda関数でロールを表示を押下
<img width="430" alt="009.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322881/ca2f3725-39b1-5b14-dfcc-14ab0f513cd2.png">

2. ポリシーを押下
<img width="426" alt="012.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322881/98657d25-5963-9f0f-48f9-a7ed86e60caa.png">

3. ポリシーの作成を押下
<img width="772" alt="013.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322881/f17f1c7b-0182-fcb3-d9d8-7c3f42f1b888.png">

4. Cost Explorer Service と SNSのポリシーを選択し以下の設定でポリシーの確認を押下
<img width="524" alt="020.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322881/231bc586-ab19-f373-8f1b-1e688e74d178.png">

5. 名前を入力しポリシーの作成を押下
<img width="455" alt="021.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322881/eed5ab9f-bfaf-682b-b804-09f516b76bde.png">

6. 最初の画面でポリシーをアタッチしますを押下
<img width="444" alt="016.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322881/6a8540c1-4833-8d55-cf89-5b9db7816be5.png">

7. 先ほど作成したポリシーをチェックしポリシーのアタッチを押下
<img width="745" alt="017.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322881/8d53351b-bc54-eb13-e76f-12767fff17a9.png">

8. policyが付与されていることを確認
<img width="403" alt="018.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322881/1b4a7b07-1b8e-e9fe-d372-b1102df6a4e9.png">

## LambdaのソースコードとCloudWatchのスケジュール
Lambdaのコードの中身は以下のようになっております。

```python
import boto3
from datetime import date
from dateutil.relativedelta import relativedelta
ce = boto3.client('ce')
sns = boto3.client('sns')

def lambda_handler(event, context):
    today = date.today() #今日
    tomorrow = today + relativedelta(days=1) #明日
    lastmonth_first = today + relativedelta(day=1, months=-1) #先月1日
    lastmonth_end = today + relativedelta(day=1, days=-1) #先月最終
    thismonth_first = today + relativedelta(day=1) #今月1日
    thismonth_end = today + relativedelta(day=1,months=1, days=-1) #今月最後
    nextmonth_first = today + relativedelta(day=1, months=1) #来月最初

    cost_end = str(today)
    forecast_start = str(tomorrow)
    forecast_end = str(nextmonth_first)

    if today == thismonth_first:#1日
        cost_start = str(lastmonth_first)
        message_cost = get_cost(cost_start,cost_end)
        message_forecast = get_forecast(forecast_start,forecast_end)
        message = ('先月の利用金額は $'+message_cost+' で今月の利用予測金額は $'+message_forecast+' です!!')
    
    elif today == thismonth_end:#最終日
        cost_start = str(thismonth_first)
        message_cost = get_cost(cost_start,cost_end)
        message = ('今月の現在までの利用金額は $'+message_cost+' です!!')

    else:#他の日
        cost_start = str(thismonth_first)
        message_cost = get_cost(cost_start,cost_end)
        message_forecast = get_forecast(forecast_start,forecast_end)
        message = ('今月の現在までの利用金額は $'+message_cost+' で今月の利用予測金額は $'+message_forecast+' です!!')

    topic = 'arn:aws:sns:ap-northeast-1:xxxxxxxxxxxx:Myaddress' #送付したいARNを入力
    subject = 'AWS利用料金'
    region = '{ap-northeast-1}'
    sns_response = sns.publish(
        TopicArn=topic,
        Message=message,
        Subject=subject,
        MessageStructure='raw'
    )
    
def get_cost(cost_start,cost_end):
    cost = ce.get_cost_and_usage(
        TimePeriod={
            'Start': cost_start,
            'End': cost_end
        },
        Granularity='MONTHLY',
        Metrics=['UnblendedCost',],
    )
    format_cost = round(float(cost['ResultsByTime'][0]['Total']['UnblendedCost']['Amount']),2)
    return str(format_cost)
    
def get_forecast(forecast_start,forecast_end):
    forecast = ce.get_cost_forecast(
        TimePeriod={
            'Start': forecast_start,
            'End': forecast_end
        },
        Granularity='MONTHLY',
        Metric='UNBLENDED_COST',
    )
    format_forecast = round(float(forecast['Total']['Amount']),2)
    return str(format_forecast)

```

CloudWatchは以下のような設定です。
CloudWatch EventはUTCのため日次で日本時間のAM09:00に通知が来ます。
<img width="979" alt="019.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322881/c5d508ab-da7c-de95-5911-2ebd57199377.png">

## 届いたメール
届いたメールはこんな感じです。
####1日
<img width="482" alt="1日.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322881/8e9a6e25-884f-b195-de04-bb77128c1f12.png">

####最終日
<img width="460" alt="月末.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322881/b0d37a78-30a2-a3fa-e259-7e18ba178154.png">
####その間
<img width="488" alt="その間.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/322881/181e921c-7fda-ac78-91c4-acb564215355.png">