はじめに
この記事では、LINEのチャネルを通じてAWSの料金情報を確認するシステムを構築する方法を詳しく解説します。このシステムは、AWS API Gateway、Lambda、そしてLINE Messaging APIを組み合わせて実現します。この機能はLINEでお手軽にAWSのコンソールにログインしないで情報を確認できるので非常に便利です。
本記事で作成するもの
このシステムでは、LINEのチャットボットを通じて以下の機能を提供します:
「今月の詳細」「先月の詳細」と入力すれば、下記のように、項目ごとの料金詳細表示
「先月」「今月」と入力すれば、単純に費用のみを表示します
LINE APIの準備
1. LINE Messaging APIの設定
- LINEチャネルの作成
- LINE Developer Consoleにアクセスし、新しいMessaging APIチャネルを作成します。
- チャネルシークレットとチャネルアクセストークンを取得しておきます。ここでは皆さんはすでに、こちらの記事で、大体やり方はご存じであるという前提で説明していきます
作成出来たら、右端の絵「設定」をクリックして、左側の「Messaging API」をクリックします
設定完了すると下記のように チャネルIDとシークレットが表示されます。Webhook URLはあとでAWSのAPIにつかうURLを入力します.
AWSサービスの設定
IAMロールの作成
今回はlambda関数に、コストとcloudwatchへのアクセスを許可しますので、ロールを作成します。
「AWSのサービス」をクリック
ユースケースはLambda を栗㏍して 「次へ」
ポリシーは適切なものを許可します。
ポリシーは下記の2つを許可します
AWSBillingReadOnlyAccess
AWSLambdaBasicExecutionRole
また、カスタムポリシーも許可してください。下記のポリシーをJSONで書いて保存してください
JSONは下記になります
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "ce:GetCostAndUsage",
"Resource": "*"
}
]
}
ポリシーの信頼関係は「信頼されたエンティティ」で下記を付与してください
JSONはこちらになります
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
こちらは何も設定せず、「次」をクリック
ロール名をつけてください
タグは任意です。「ロールを作成」をクリックしてください
問題なければ下記のようにロールが作成されます。
AWS Lambdaの設定
ここでは、APIゲートウェイで受け取ったリクエストを処理するコードを書きます。
AWSのLambdaから関数を作成します
ロールは先ほど作ったものを利用します
作成出来たら、コードのタブからコードを追加します
下記がコードとなります
import os
import json
from datetime import datetime, timedelta
import boto3
from linebot import LineBotApi, WebhookHandler
from linebot.exceptions import LineBotApiError
from linebot.models import TextSendMessage
# 環境変数からLINE Messaging APIのアクセストークンとシークレットを取得
LINE_ACCESS_TOKEN = os.getenv('YOUR_CHANNEL_ACCESS_TOKEN')
LINE_CHANNEL_SECRET = os.getenv('YOUR_CHANNEL_SECRET')
# アクセストークンとシークレットが環境変数に設定されていない場合のエラーハンドリング
if not LINE_ACCESS_TOKEN or not LINE_CHANNEL_SECRET:
raise ValueError("LINEアクセストークンまたはシークレットが環境変数に設定されていません。")
line_bot_api = LineBotApi(LINE_ACCESS_TOKEN)
handler = WebhookHandler(LINE_CHANNEL_SECRET)
def get_billing_info(message):
"""
AWS料金情報を取得する。
- メッセージに応じて期間や表示形式を切り替える。
"""
client = boto3.client('ce', region_name='us-east-1')
today = datetime.today()
# 料金期間と出力形式を設定
if message == "今月":
start_date = today.replace(day=1).strftime('%Y-%m-%d')
end_date = (today + timedelta(days=1)).strftime('%Y-%m-%d')
detail = False
elif message == "先月":
first_day_last_month = (today.replace(day=1) - timedelta(days=1)).replace(day=1)
start_date = first_day_last_month.strftime('%Y-%m-%d')
end_date = today.replace(day=1).strftime('%Y-%m-%d')
detail = False
elif message == "今月の詳細":
start_date = today.replace(day=1).strftime('%Y-%m-%d')
end_date = (today + timedelta(days=1)).strftime('%Y-%m-%d')
detail = True
elif message == "先月の詳細":
first_day_last_month = (today.replace(day=1) - timedelta(days=1)).replace(day=1)
start_date = first_day_last_month.strftime('%Y-%m-%d')
end_date = today.replace(day=1).strftime('%Y-%m-%d')
detail = True
else:
return "サポートされていないコマンドです。"
try:
# AWSの料金情報を取得
response = client.get_cost_and_usage(
TimePeriod={'Start': start_date, 'End': end_date},
Granularity='MONTHLY',
Metrics=['AmortizedCost'],
GroupBy=[{'Type': 'DIMENSION', 'Key': 'SERVICE'}]
)
if detail:
# サービスごとの詳細情報を整形
details = [
f"{group['Keys'][0]}: {group['Metrics']['AmortizedCost']['Amount']} USD"
for group in response['ResultsByTime'][0]['Groups']
]
return f"{start_date}から{end_date}までのサービスごとの料金詳細:\n" + "\n".join(details)
else:
# 合計料金を取得
total_billing = response['ResultsByTime'][0]['Total']['AmortizedCost']['Amount']
return f"{start_date}から{end_date}までの総料金: {total_billing} USD"
except boto3.exceptions.Boto3Error as e:
# AWSサービス関連のエラー処理
print(f"AWS Error: {str(e)}")
return "料金情報の取得に失敗しました。AWSサービスに接続できませんでした。"
except Exception as e:
# その他の予期しないエラー処理
print(f"Unexpected Error: {str(e)}")
return "料金情報の取得に失敗しました。予期しないエラーが発生しました。"
def lambda_handler(event, context):
"""
Lambda関数のエントリーポイント。
- LINEからのWebhookイベントを処理し、返信を送信。
"""
try:
# イベントデータのログ出力(デバッグ用)
print("Event received:", event)
# イベントのボディをパース
body = json.loads(event['body'])
message_text = body['events'][0]['message']['text']
reply_token = body['events'][0]['replyToken']
# 無効なリプライトークンの場合、ログ出力して終了
if reply_token == "dummyReplyToken":
print("Dummy reply token detected. Skipping reply.")
return {'statusCode': 200, 'body': 'Dummy token, no reply sent'}
# メッセージに基づいてAWS料金情報を取得
billing_info = get_billing_info(message_text)
# LINE Messaging APIで返信
line_bot_api.reply_message(
reply_token,
TextSendMessage(text=billing_info)
)
return {'statusCode': 200, 'body': 'Success'}
except LineBotApiError as e:
# LINE Messaging APIのエラー処理
print(f"LineBotApiError: {e.status_code}, {e.error.message}")
return {'statusCode': 400, 'body': 'LineBotApiError occurred'}
except Exception as e:
# その他の例外処理
print(f"Unexpected Error: {str(e)}")
return {'statusCode': 500, 'body': 'Internal Server Error'}
チャネルアクセストークンと、シークレットを環境変数へ登録
YOUR_CHANNEL_ACCESS_TOKEN
YOUR_CHANNEL_SECRET
については環境変数として作成します。
チャネルアクセストークンとシークレットを取得
チャネルのアクセストークンは、「Messaging API 設定」のタブの下側にあります
「発行」ボタンをクリックして
発行された トークンをコピペしておいてください
環境変数に追加
「設定」タブから「環境変数」で追加します
チャネルのアクセストークンは,「チャネルの基本設定」の下側にあります
設定を完了したら「保存」ボタンを押してください
これが完了すれば「テスト」でOKとなるはずです
レイヤーの追加
最後にlinebot のレイヤーを追加します。コードの画面から下に向かってスクロールダウンして「レイヤー」の「レイヤーの追加」をクリック
arn:aws:lambda:us-east-1:590184098214:layer:linebot:1 を追加できるので ARNを指定して追加します 「追加」ボタンを教えて、保存します
これでlambdaの設定は完了です
AWS API Gateway
- API GatewayでREST APIの作成
REST API を構築します
新しいAPI
エンドポイントタイプはリージョンで問題ありません。「APIの作成」をクリックします
リソースを作成
CORSを有効にして、「リソースを作成」をクリック
作成されたらCORSを有効にします
メソッドを作成でPOSTを作成します
POSTを選択して、先ほど作成した Lambda 関数をリンクします
ほかの箇所は変更せずに、APIメソッドを作成 ボタンを押します
作成出来たら「メソッドレスポンス」を編集して
下記の3つを追加して、保存します
下記のようになればOKです
この状態でAPIをデプロイします
モーダル画面でステージを設定して、デプロイします
デプロイが成功すれば、URLを呼び出す の箇所にURLが出てきますので、これをコピペします
テストとデプロイメント
先ほど作成したLINEチャネルで上記のAPIゲートウェイのデプロイで取得した URLを下記のWebhook URLに登録します
LINEからテストしてみれば成功するはずです
最後に
いかがでしたでしょうか?LINEを使えば無料でこのようなツールが作れるので面白いかとおもいます。次回はGAS(Google App Script)とLINEを連携したいと思います