AWSのコストレポートを日次でメール通知する仕組みを AWS Serverless Application Model(AWS SAM)で構築する

 友人から「AWSのコストレポートをメールで流すのって簡単にできる?」と聞かれ、「(コンソールの設定でできたかなー、と思い、)調べるから待っててねー。」と言ったのですが、それをすっかり忘れてしまい3週間ほど放置してしまいました。その贖罪として(:))、SAM でコストレポートをメールする仕組みを作ったので共有します(結局、コンソールで簡単な設定はありませんでした。)

 IAM ロールの設定と、Lambda ファンクションの定義だけでできるので(後述しますが、SESの初期設定は Lambda ファンクション内でやっています。)、それほど難しくなく、SAM の練習にはもってこいだと思います。

 また、実は SES をあまり使ったことがなく、その勉強を兼ねています。いったん、Gmail のアドレスを From にも To にも設定する形にしていますが、From 側を独自ドメインのものを使う場合は、別途設定が必要だと思っています。こちらについてもどこかで確認したいと思っています。

ソースコード

 こちらで公開しています。

github.com

Lambda ファンクション(billing_mail.py

 メールアドレスは環境変数から取得します。

import os
import boto3
import datetime

MAIL_FROM = os.environ['MAIL_FROM']
MAIL_TO   = os.environ['MAIL_TO']

cloud_watch_client = boto3.client('cloudwatch', region_name='us-east-1')
ses_client = boto3.client('ses', region_name='us-east-1')

def lambda_handler(event, context):

    if not is_registerd_email_address():
        register_email_address()
        return

    billing = getBilling()
    send_email(billing)
    return

def is_registerd_email_address():
    return len(ses_client.list_verified_email_addresses()["VerifiedEmailAddresses"]) == 1

def register_email_address():
    return ses_client.verify_email_address(
        EmailAddress=MAIL_FROM
    )

def send_email(billing):

    return ses_client.send_email(
        Source=MAIL_FROM,
        Destination={
            'ToAddresses': [
                MAIL_TO,
            ]
        },
        Message={
            'Subject': {
                'Data': '【システムメール】現在のAWS利用料金',
            },
            'Body': {
                'Text': {
                    'Data': '現在のAWS利用料は、$' + str(billing) + ' です。' ,
                },
            }
        }
    )

def getBilling():
    start_time = datetime.datetime.today() - datetime.timedelta(days=1)
    end_time   = datetime.datetime.today()

    statistics = cloud_watch_client.get_metric_statistics(
                        Namespace='AWS/Billing',
                        MetricName='EstimatedCharges',
                        Dimensions=[
                            {
                                'Name': 'Currency',
                                'Value': 'USD'
                            }
                        ],
                        StartTime=start_time,
                        EndTime=end_time,
                        Period=3600*24,
                        Statistics=['Maximum']
                        )

    return statistics['Datapoints'][0]['Maximum']

template.yaml

 MAIL_FROM, MAIL_TO については適宜修正してください。

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: Send AWS billing information.

# SAM Globals
Globals:
  Function:
    Environment:
      Variables:
        MAIL_FROM: '***@gmail.com' # Fix
        MAIL_TO:   '***@gmail.com' # Fix

# SAM Resources
Resources:
  SamSampleLambda:
    Type: AWS::Serverless::Function
    Properties:
      CodeUri: functions/billing_mail/billing_mail.zip
      Handler: billing_mail.lambda_handler
      Runtime: python3.6
      Role: !GetAtt BillingIamRole.Arn
      Timeout: 30
      Events:
        Timer:
          Type: Schedule
          Properties:
            Schedule: cron(0 0 * * ? *) # JST AM9:00
  BillingIamRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: "sts:AssumeRole"
      Policies:
        -
          PolicyName: "billing_mail_for_lambda"
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              -
                Effect: "Allow"
                Action: "cloudwatch:*"
                Resource: "*"
              -
                Effect: "Allow"
                Action: "ses:*"
                Resource: "*"

導入方法

 README.md にも書いていますが、SAM の初期設定は完了している前提となります。もし、設定が完了していない場合は、こちらも参考にしてみてください。

www.ketancho.net

  1. template.yamlMAIL_FROM , MAIL_TO を変更してください。(Gmailでしか動作確認しておりません。)
  2. sh deploy.sh コマンドを実行してください。
  3. 初回実行時にメールアドレスの verify が必要です。構築された Lambda Function を実行してください。2. で設定したメールアドレスに、verifyメールが送信されるので、承認してください。
  4. 以上で設定完了です。日次でコストレポートが送信されます。

イマイチなところ

 最初のメールアドレスの verify 部分はライフサイクルが他と異なるのでどうしたものかと思ったのですが、いったん一つの Lambda ファンクションでやってしまっています。SAM(Cfn)ではできなそうに見える?(調べが甘いかもしれません。) deploy.sh の中で AWS CLI を使って verify する方がまだマシな気もしています。うーむ。

まとめ

 少々イマイチなところは残りましたが、当初の目的は達成しました。SAM についてはこちらでもまとめていますので、気になる方はチェックしてみてください。

www.ketancho.net