【この記事だけで完結】Lambda+SNSでAWS利用料金をメール通知する【python】

2月 12, 2022

どもども、AWS学習に個人アカウントを使用することにビビり続けるsaisaiです。


みなさん、AWS利用料金は日々モニタリングしておりますでしょうか。AWSは使った分だけ料金がかかるというコスト上の特性から思わぬ高額請求につながりかねない事例が多数あります。


起動したリソースの削除し忘れ、設定ミスによる予測金額超過、アクセスキーとシークレットアクセスキーの漏洩による攻撃被害などなど、利便性の裏には常に危険が付き纏います。


そこで今回は日々のAWS予測金額を毎日メール通知する仕組みをAWS Lambda, Amazon Cloudwatch Event, Amazon SNSを使用してAWSコンソール画面の操作のみで実現してみたいと思います。


SNSで料金を通知するならAWS請求アラートを利用したら良いのでは?と思われる方もいると思いますが、事前に設定した料金を超過したタイミングでアラートが出るという挙動が僕はあまり好きではありません…。料金の変化がなくても日々の料金を見て安心したいのです。


また、今時メール?と思われる方もいると思いますが、今回あえてメール通知を採用した大きな理由は以下の2つ

・企業によってはSlackやLINE通知が使用できない。
・そもそもAPIキーの取得や管理がめんどくさい。

を考慮する方々をターゲットとしているためです。


それでは、やってみまっしょう!

SNSのトピックとサブスクリプションを作成

まずはAmazon SNSのトピックとサブスクリプションを作成します。


Amazon SNSとはAmazon Simple Nortification Serviceの略でアプリケーションなどのトリガーを利用してSMS、モバイルプッシュ、E メールなどを介して、ユーザーに対しメッセージを送信するサービスです。


今回はSNSを利用し、取得したAWS利用コストをメールで通知したいと思います。


まずはAWSコンソール画面にログインし、Amazon SNSのページに移行してください。左側メニューよりトピックをクリック、右上の"トピックを作成"を作成してください。


設定は下記のようにし、それ以外はデフォルトの設定で問題ありません!

・タイプはスタンダード
・名前はトピックの名前です、お好みで!(aws-cost-check-topicなど)
・SMSに通知する場合は表示名もお好みで設定してください

上記設定が完了したら右下"トピックの作成"をクリックしてください。これでSNSトピックの作成は完了です!

画面が変わったら作成されたトピックの設定が表示されますので、その中の"ARN"を控えておいてください。"arn:aws:sns:リージョン:AWSアカウントID:トピック名"となっている部分になります。


続いてこのままではメッセージの送信先が指定されていないので設定しましょう。左側メニューよりサブスクリプションをクリックしてください。右上の"サブスクリプションを作成"をクリックしましょう。


下記の設定以外はデフォルトのままで問題ないので"サブスクリプションの作成"をクリックして設定を完了してください。

・トピックARNには先ほど控えたARNを入力してください
・プロトコルにはEメールを選択しましょう
・エンドポイントには料金を通知するメールアドレスを指定します


これでSNSトピックに何かしらのアクションがあるとサブスクリプションを通して対象メールアドレスに通知する仕組みが出来上がりました。


サブスクリプションが正しく作成されていれば、登録したメールアドレス宛にAWSから承認メールが届くのでConfirm subscriptionのリンクをクリックしましょう。これでSNSからメール送信が承認され実際にメールが届くようになります。


あとはAWS料金を取得してとのSNSトピックに渡してやればOKですね!

Lambdaの基本設定

それでは肝心のAWS料金を取得する部分を作成しましょう。今回はAWS Lambdaで1日分の最大コストを取得してSNSに渡す関数を作成したいと思います。


まずはLambdaの基本設定をしましょう。設定は下記の通りです。

・一から作成を選択
・関数名はお好みで!(aws-cost-check-funcなど)
・ランタイムにはpython3.8か3.9を選択してください
・デフォルトの実行ロールは"基本的な Lambda アクセス権限で新しいロールを作成"で新しく作成しましょう

上記設定が完了したら"関数の作成"をクリックしてください。


関数が作成されると作成したLambda関数の詳細設定画面に変遷するかと思います。ここで注意なのはLambdaの実行ロールです。


“設定"タブをクリックすると実行ロール名が表示されるはずなので、実行ロール名をクリックして中身を確認してみましょう。見ての通りAWSLambdaBasicExecutionRoleというLambdaを実行するための基本的なポリシーのみアタッチされていますね。


こちらのポリシーではLambdaからAWSのコストを取得するためにCloudWatchメトリクスにアクセスしたり、結果を伝えるためにSNSに通知したりするための権限が足りませんので付与しましょう。


“ポリシーをアタッチします"をクリックし検索バーにsnsと入力してください。AmazonSNSFullAccessというポリシーが出てきますので選択して右下の"ポリシーのアタッチ"をクリックしましょう。これでLambdaからSNSに接続する権限を付与できました。


しかしまだ足りません。CloudWatchメトリクスからAWSコストを取得するための権限をさらに付与しましょう。


今回は非常に限定的な範囲の権限(CloudWatchメトリクスへのGetのみ)なのでインラインポリシーで対応します。ロール設定画面から"ポリシーをアタッチします"の隣の"インラインポリシーの追加"をクリックしてください。


下記のように設定してください。

・サービスはCloudWatchを選択
・アクセスレベルは"読み込み"から"GetMetricStatistics"を選択してください

上記設定が完了したら"ポリシーの確認"をクリックしましょう。名前にはポリシー名をお好みで(GetAWSCostMetricPolicyなど)設定し、"ポリシーの作成"をクリックしてください。


これでLamdaからCloudWatchメトリクスのデータを読み込めるようになりました!


必要な権限が揃いましたね!

Lambda関数を書く

それでは実際にAWSコストを取得しSNSに渡すLambda関数を書いていきましょう。


今回作成したのは以下のようなコードです。

import boto3
import datetime

def get_cost():

    now = datetime.datetime.now()

    cloudwatch = boto3.resource('cloudwatch', region_name='us-east-1')
    metric = cloudwatch.Metric('AWS/Billing', 'EstimatedCharges')
    response = metric.get_statistics(
        Dimensions=[
            {
                'Name': 'Currency',
                'Value': 'USD'
            },
        ],
        StartTime=now - datetime.timedelta(days=1),
        EndTime=now,
        Period=86400,
        Statistics=['Maximum']
    )

    return response

def lambda_handler(event, context):
    client = boto3.client('sns')
    cost = get_cost()['Datapoints'][0]['Maximum']
    date = (get_cost()['Datapoints'][0]['Timestamp'] + datetime.timedelta(days=1)).strftime(
        '%Y年%m月%d日')

    msg = f'''
    AWS利用料金をお知らせします!
    ${cost}'''
    subject = f'{date} AWS利用料金'
    TopicArn = u'SNSトピックのARN'

    print(msg,subject,TopicArn)

    response = client.publish(
        TopicArn = TopicArn,
        Message = msg,
        Subject = subject
    )

    return response

Lambdaのコードソースページに最初から存在する"lambda_function.py"の中のコードを全て削除し、上記コードをコピペしましょう。コード内の変数TopicArnには今回作成したSNSトピックのARN(序盤のほうで控えたARNですね!)を指定しておいてください。


コピペとコードの変更が完了したら、"Deploy"をクリックしてください。これで実行可能なLambda関数となりました!


では、正しく昨日するかテストしてみましょう。"Test"ボタンをクリックしてください。テストイベントの設定が表示されますが、イベント名をお好みで指定し他は何も変更せずに"作成"をクリックしてください。


コードソースページに戻るはずなのでもう一度"Test"をクリックしましょう。うまくいけば、登録されたメールアドレス宛にAWSの利用料金が通知されているはずです!

CloudWatchEventを設定し毎日通知するように設定

これでAWSコストを取得してメールに通知するところまで実装できました。


あとは作成したLambdaが自動で定期的に実行されれば追い求めていた仕組みが完成します。


Lambdaの自動実行設定はCloudWatchEventで行います。作成したLambdaのページより"設定"を選択し、"トリガー"をクリック、"トリガーを追加"をクリックしましょう。


トリガーの選択から"EventBridge(CloudWatchEvent)"を選択しましょう。"新規ルール作成"を選択し下記設定を行います。

・ルール名とルールの説明をお好みで入力
・ルールタイプは"スケジュール式"
・今回は"rate(1 day)"と入力し毎日実行されるように設定します。
※明確に実行したい時間帯がある場合はcron設定に挑戦してみてください!

右下の"追加"をクリックしトリガーを追加しましょう。


これで毎日Lambdaが自動で実行される設定も完了しました!


翌日、自動でAWSコスト通知メールが届けば晴れて完了となります!お疲れ様でした!

さいごに

今回はAWSのコストを通知する仕組みをコンソール画面上の設定のみで作成してみました。


もちろん可能であればAWS SAMやAWS CDKなどを使用してリソースをコード管理するべき構成だと思います。しかし、AWSを導入したばかりの企業様やAWSを学び始めた方々が安心してAWSを操作できる助けになればと思い今回はGUIベースでの操作を選びました。


Lambda+SNSの基礎が詰まった内容なのではないかと思っていますので、是非ともチャレンジしてみてください!


今回の記事を作成するにあたり、下記の内容を参考にさせていただきました。
https://qiita.com/sotoiwa/items/fa3070f5c84f4538e774
https://qiita.com/tsumita7/items/bbafba094db5794d0374
もし分かりにくい部分がありましたら上記記事もご覧になっていただければと思います。


最後まで読んでいただきありがとうございました!


↓オススメ教材

AWS

Posted by CY