どうも、AWS勉強中のとがみんです。
仕事のための勉強ついでに、自分のAWSのアカウントを発行したのですが、従量課金のサービスが多いので、気づかぬ間に使いすぎて、多額の請求がこないか?と少し不安になったりします。。。
なので、コスト監視の仕組みを構築し、手間なくコストを確認できるようにしようかと思います。
そのためにLamdaを活用して、Slackに定期的に通知を送るように設定します。
今回やりたいこと
今回は、指定した日時になるとLamdaを起動し、LamdaがAWSのCostExplorerからその月にかかったコストを取得し、Slackに通知するという仕組みを構築していきます。
Serverless Frameworkを活用し、EventBridgeとLamdaを作成し、Lamda関数からCost Explorerにアクセスしその月にかかった費用の合計金額をSlackに通知します。
Cost Explorerへのアクセスは1回につき、0.01$かかるので、もし毎日通知するようであれば月約30円ほどかかるので注意が必要です。
今回作成したサンプルコードはGitHubにあげています。
作業手順概要
Slackへ通知するための設定をし、Lamda関数にて、CostExplorerへアクセスし、合計金額を取得して通知する仕組みを構築していきます。
- SlackでWebhook URL取得のための設定
- プロジェクトの作成とSlackへメッセージを通知するまで
- CostExplorerの設定
- CostExplorerへのアクセスと合計金額の通知するまで
作業手順詳細
SlackでWebhook URLの取得のための設定と、プロジェクトの作成からSlackへメッセージを通知するまでは下記の記事に記載しているので参考にしてください。
Cost Explorerの設定
AWS Cost Explorerとは、AWSのリソースの使用状況と、コストを可視化して分析できるサービスです。このCost ExplorerにLamda関数からアクセスし、AWSの利用金額を取得します。
Cost Explorerで時間単位、リソース単位で情報が取得できるので有効化しておくと良さそうです。
CostExplorerへのアクセスと合計金額の通知
Lamda関数にアップロードするソースコードを作成します。
serverless.yml
serverless.ymlの全体ソースコードは「aws-cost-alert/serverless.yml」に記載しています。
functions
1 2 3 4 5 6 7 | functions: PostSlackFunctions: handler: handler.post_slack role: !GetAtt BillingIamRole.Arn events: - eventBridge: schedule: cron(00 23 ? * SAT *) |
呼び出す関数の指定と起動タイミングに関して記載しています。
起動タイミングは日曜日の午前8時に設定しています。
Amazon EventBridgeでCron式で設定するイベント時刻はタイムゾーンがUTC(協定世界時)になっているため、日本標準時(JST)との時差を考慮する必要があります。
cron(00 23 ? * SAT *)は土曜日の23時に起動するような設定になっていますが、日本時間だと+9時間の時差の考慮をする必要があります。
そのため上記の設定で日曜日の午前8時に起動するような設定になっています。
BillingIamRole.ArnのIAMロールは次に記載するresourcesにロールを設定しています。
resources
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | resources: Resources: BillingIamRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: lambda.amazonaws.com Action: "sts:AssumeRole" Policies: - PolicyName: "NotifySlackToBillingLambdaPolicy" PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - "logs:CreateLogGroup" - "logs:CreateLogStream" - "logs:PutLogEvents" - "ce:GetCostAndUsage" Resource: "*" |
ポリシーにはCloudWatchへのログの出力のための権限と、Cost Explorerからデータを取得するためのアクションを付与したポリシーをアタッチしています。
handler.py
handler.pyの全体ソースコードは「aws-cost-alert/handler.py」に記載しています。
メインメソッド
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | def post_slack(event, context) -> None: # CostExplorer client = boto3.client('ce', region_name='us-east-1') # 対象月にかかったAWSの合計金額の算出 total_billing = get_total_billing(client) # Slack通知内容の作成 payload = { 'attachments': [ { 'color': '#36a64f', 'pretext': '今月のAWS利用費用の合計金額', 'text': '期間:' + total_billing['start'] + '〜' + total_billing['end'] + ' \n' + '合計金額:' + str(round(float(total_billing['billing']), 2)) + '$' } ] } # Slackへの通知 try: response = requests.post(SLACK_WEBHOOK_URL, data=json.dumps(payload)) except requests.exceptions.RequestException as e: print(e) else: print(response.status_code) |
メインメソッドのpost_slackには、Cost Explorerのインスタンス化、合計金額の取得、Slackへの通知内容を作成して通知する処理が記載されています。
対象月にかかったAWSの合計金額の算出
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | # 対象月にかかったAWSの合計金額の算出 def get_total_billing(client) -> dict: #コスト集計範囲の取得 (start_date, end_date) = get_monthly_cost_date_range() #コスト集計範囲に対する合計コストの取得 response = client.get_cost_and_usage( TimePeriod={ 'Start': start_date, 'End': end_date }, Granularity='MONTHLY', Metrics=[ 'AmortizedCost' ] ) #取得したデータの返却 return { 'start': response['ResultsByTime'][0]['TimePeriod']['Start'], 'end': response['ResultsByTime'][0]['TimePeriod']['End'], 'billing': response['ResultsByTime'][0]['Total']['AmortizedCost']['Amount'], } |
対象月にかかったAWSの合計金額を算出する関数です。
コスト集計範囲を取得し、その期間にかかったコストを取得し返却する関数です。
対象月のコスト算出対象の初日と当日の日付を取得する
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | # 対象月のコスト算出対象の初日と当日の日付を取得する def get_monthly_cost_date_range()->(str,str): # Costを算出する期間を設定する start_date = date.today().replace(day=1).isoformat() end_date = date.today().isoformat() # 「start_date」と「end_date」が同じ場合「start_date」は先月の月初の値を取得する。 if start_date == end_date: end_of_month = datetime.strptime(start_date, '%Y-%m-%d') + timedelta(days=-1) begin_of_month = end_of_month.replace(day=1) return begin_of_month.date().isoformat(), end_date return start_date, end_date |
対象月のコスト算出対象の初日と当日の日付を取得する関数です。
「start_date」と「end_date」が同じ場合「start_date」は先月の月初の値を取得するようにしています。
デプロイと動作確認
serverless deployを実行して、AWSの環境に各種リソースを構築します。
指定した時間になったらEventBrideが起動して実行されます。
まとめ
AWSの対象月の費用を定期的にSlackへ通知する仕組みを構築しました。今回は合計金額ですが、サービス毎の金額の出力も追加していこうかと思います。
参考
AWSサービス毎の請求額を毎日Slackに通知してみた
【この記事だけで完結】Lambda+SNSでAWS利用料金をメール通知する【python】