毎月のアカウント利用料を通知する機能をCloudFormationで作ってみた!
はじめに
みなさんこんにちは。最近某コンビニのルイボスティーが大好きなhiropyです。
今回は、「毎月のアカウント利用料を自動でSlackに通知する機能」をCloudFormationを使って実装したのでこちらをご紹介します。
前提
今回は、ControlTower管理下のOU内のアカウントに対してCloudFormationを使ってリソースをデプロイすることを想定しています。
ただし、単一のアカウントに対してスタックでデプロイする場合はControlTowerの設定は必要ありません。
今回は以下のアーキテクチャで実装していこうと思います。
やってみる
早速実装していきましょう!
1. Slackに通知するためのbotを作成する
まずはSlackに通知するためのbotを作成します。
以下のリンクから「Slack API」のページを開き、「Create an app」をクリックします。
https://api.slack.com/
モーダルが表示されるので「From scratch」を選択します。
次の画面でbot名の入力とワークスペースの選択をしてアプリを作成します。
最初の画面で「Permissions」をクリックします。
下にスクロールし、「Scopes」の「Bot token scopes」で「Add an OAuth Scopes」をクリックします。
以下3つの権限を追加してください。
- chat:write
- chat:write.public
- chat:write.customize
次に左メニューの「App Home」からDisplaynameの編集を行います。
画像の「Edit」ボタンをクリックして、DisplayName(Slackで表示されるbot名)、DefaultName(半角英数字)を設定します。
あとはお好みで「Basic Information」からbotの画像などを設定します。
サイドバーで「OAuth & Permissions」を選択し、「Install your Workspace」をクリックしてワークスペースにbotを導入します。
画面にトークンが表示されるのでコピーしておきましょう。
2. 通知するためのStackSetsを作成する
ControlTowerの管理アカウントでStackSetsを作成していきます。
まずは以下のファイルをダウンロードし、s3内の任意のバケットに保存します。
(今回はap-northeast-1
に作成してください。CTのデフォルトリージョンが異なる場合は、Lambdaコードのリージョンを指定している箇所を変更してください。)
requests.zip (5.7 MB)
こちらはLambdaで使用するためのレイヤーファイルとなっています。
続いてyamlファイルの内容からコードをコピーし、yamlファイルを作成します。
- yamlファイルの内容
-
AWSTemplateFormatVersion: 2010-09-09 Parameters: Token: Type: String Resources: LambdaLayer: Type: AWS::Lambda::LayerVersion Properties: CompatibleArchitectures: - arm64 - x86_64 CompatibleRuntimes: - python3.9 Content: S3Bucket: <任意のバケット名> S3Key: requests.zip LayerName: send-cost-layer LambdaFunction: Type: AWS::Lambda::Function Properties: Handler: index.lambda_handler Code: ZipFile: !Sub | import os from slack_sdk import WebClient import boto3 from datetime import datetime, timedelta, date def get_total_billing() -> dict: ce = boto3.client('ce', region_name='ap-northeast-1') start_date = date.today().replace(day=1).isoformat() end_date = date.today().isoformat() response = ce.get_cost_and_usage( TimePeriod={ 'Start': start_date, 'End': end_date }, Granularity='MONTHLY', Metrics=[ 'AmortizedCost' ] ) billing = '{:.2f}'.format(float(response['ResultsByTime'][0]['Total']['AmortizedCost']['Amount'])) return billing def lambda_handler(event, context): # Web API クライアントを初期化します client = WebClient(os.environ["SLACK_BOT_TOKEN"]) amount = get_total_billing() try: # chat.postMessage API を呼び出します response = client.chat_postMessage( channel=f"<任意のチャンネル名>", text=f"今月のサンドボックス利用料をお知らせします :hatching_chick: \n今月の利用料は{amount}USDです。", ) print("送信に成功しました") except Exception as e: raise e return FunctionName: SendMonthlyCostFunction Runtime: python3.9 Environment: Variables: SLACK_BOT_TOKEN : !Sub ${Token} Layers: - !Ref LambdaLayer Role: !GetAtt - LambdaExecutionRole - Arn LambdaExecutionRole: Type: AWS::IAM::Role Properties: RoleName: send-cost-function-execution-role AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: sts:AssumeRole Policies: - PolicyName: !Sub send-cost-function-policy PolicyDocument: Version: 2012-10-17 Statement: - Resource: '*' Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents - logs:CreateExportTask - s3:* - ce:GetCostAndUsage EventBridgeRule: Type: AWS::Events::Rule Properties: EventBusName: default Name: send-cost-eventbridge-rule ScheduleExpression: cron(0 1 L * ? *) State: ENABLED Targets: - Arn: !GetAtt LambdaFunction.Arn Id: LambdaFunction PermissionForEventsToInvokeLambda: Type: AWS::Lambda::Permission Properties: FunctionName: !Ref LambdaFunction Action: lambda:InvokeFunction Principal: events.amazonaws.com SourceArn: !GetAtt 'EventBridgeRule.Arn'
今回は「毎月末の10:00に通知する」ような設定にしています。
<任意のバケット名>と書かれているところには、先ほど作成したバケット名を入力します。
<任意のチャンネル名>と書かれているところには、通知を飛ばしたいチャンネル名を設定します(例:#general)。
それではStackSetsをデプロイしていきます。
ControlTowerの管理アカウントからCloudFormationコンソールを開き、StackSetsタブから「Stacksetの作成」をクリックします。
「テンプレートの指定」で先ほど作成したyamlファイルをアップロードします。
Stackset名、説明は任意のものを入力して、パラメータの「Token」でSlack APIのトークンを貼り付けます。
デプロイオプションの設定では、デプロイ先のアカウントIDまたはOU IDを設定し、任意のリージョンを指定します。(今回はap-northeast-1です)
レビュー画面に進み、送信ボタンを押せばOKです!
デプロイが完了するのを待ちましょう。
3. 確認
cronを修正するなどして、今日通知が来るようにすると、画像のようにその月の1日から今日までのアカウント利用料を通知してくれます。
最後に
いかがでしたでしょうか?
サンドボックス環境などを運用していると、その月の利用料は気になってくるところだと思うのでぜひ参考にしていただけますと幸いです!
参考
- 【2021年版】slackAPIでメッセージ投稿するボットアプリ作成・設定(スコープ権限を詳細解説)
- Slack Python SDK でチャンネルにメッセージを投稿しよう
- 【AWS】EventBridgeを使って毎日の課金額をSlackに通知する
- AWS::Lambda::LayerVersion
- CFNでLambdaレイヤー作成
- AWS CloudFormationを触ってみる #2 EventBridge Lambda SNS