import json
import boto3
import urllib.request
import urllib.parse # URLエンコード用
from datetime import datetime, timezone, timedelta # timedeltaをインポート
# SNSクライアントを初期化(メール通知用)
sns_client = boto3.client('sns')
# SNSトピックのARNを指定してください
SNS_TOPIC_ARN = 'arn:aws:sns:your-region:your-account-id:your-topic-name'
# ワークフロー(TeamsのWebhookなど)のURLを設定してください
WORKFLOW_URL = 'https://your-workflow-url'
def extract_metrics(configuration):
"""
メトリクス情報を抽出して文字列にフォーマットします
"""
metrics = configuration.get('metrics', [])
metric_list = []
for metric in metrics:
metric_info = metric.get('metricStat', {}).get('metric', {})
namespace = metric_info.get('namespace', '')
metric_name = metric_info.get('name', '')
dimensions = metric_info.get('dimensions', {})
dimensions_str = ', '.join([f"{k}={v}" for k, v in dimensions.items()])
metric_list.append(f"{namespace}/{metric_name} ({dimensions_str})")
return '; '.join(metric_list)
def format_timestamp(timestamp_str):
"""
タイムスタンプ文字列を日本標準時(JST)にフォーマットします
"""
if timestamp_str:
try:
# タイムスタンプを解析(タイムゾーン情報を含む)
timestamp = datetime.strptime(timestamp_str, '%Y-%m-%dT%H:%M:%S.%f%z')
# 日本標準時(UTC+9)のタイムゾーンを作成
jst_timezone = timezone(timedelta(hours=9))
# タイムゾーンを日本標準時に変換
jst_time = timestamp.astimezone(jst_timezone)
return jst_time.strftime('%Y-%m-%d %H:%M:%S %Z')
except ValueError as e:
print(f"タイムスタンプの解析エラー: {e}")
return 'N/A'
else:
return 'N/A'
def send_email(message, subject):
"""
SNSを使用してメールを送信します
"""
response = sns_client.publish(
TopicArn=SNS_TOPIC_ARN,
Message=message,
Subject=subject
)
print('メールを送信しました。メッセージID:')
print(response['MessageId'])
def send_to_workflow(card_content):
"""
ワークフローにデータを送信します
"""
headers = {
'Content-Type': 'application/json'
}
request = urllib.request.Request(WORKFLOW_URL, data=json.dumps(card_content).encode('utf-8'), headers=headers)
try:
with urllib.request.urlopen(request) as response:
response_body = response.read()
print('ワークフローへのデータ送信に成功しました。')
except Exception as e:
print(f"ワークフローへのデータ送信エラー: {e}")
def lambda_handler(event, context):
"""
AWS Lambdaのエントリポイント
"""
# デバッグ用にイベントを出力
print("Received event: " + json.dumps(event))
# CloudWatch Alarmからのイベントを解析
alarm_data = event.get('alarmData', {})
alarm_name = alarm_data.get('alarmName', '')
state = alarm_data.get('state', {})
reason = state.get('reason', '')
timestamp = state.get('timestamp', '')
region = event.get('region', '')
configuration = alarm_data.get('configuration', {})
metrics = extract_metrics(configuration)
# タイムスタンプをフォーマット
occurrence_time = format_timestamp(timestamp)
# アラームのコンソールリンクを作成
alarm_url = create_alarm_url(region, alarm_name)
# 小文字に変換して比較を容易にする
reason_lower = reason.lower()
alarm_name_lower = alarm_name.lower()
# メッセージの初期化
subject = ''
message = ''
workflow_data = {}
# メンションするユーザーの情報を設定(必要に応じて)
mention_name = 'lihao' # 要提案のユーザーの表示名
mention_id = 'user-id' # 要提案のユーザーのID(AAD Object IDなど)
# 第一段階のフィルタリング:reasonに特定のキーワードが含まれるかチェック
if ('no datapoints' in reason_lower) or ('insufficient data' in reason_lower) or ('unknown' in reason_lower):
# 固定形式のメールAを送信 - データ不明
subject = '【データ不明】アラームが発生しました'
message = f'''発生時間:{occurrence_time}
発生アラーム名:{alarm_name}
発生メトリクス:{metrics}
理由:
{reason}
詳細はdevops環境のAWS Cloudwatchコンソールでご確認ください。
リンク:{alarm_url}'''
# Adaptive Cardを作成(メンションを含む)
workflow_data = create_adaptive_card_with_mention(subject, occurrence_time, alarm_name, metrics, reason, alarm_url, mention_name, mention_id)
else:
# 第二段階のフィルタリング:alarmNameに特定のキーワードが含まれるかチェック
if 'warning' in alarm_name_lower:
# 固定形式のメールBを送信 - 警告
subject = '【警告】アラームが発生しました'
elif 'danger' in alarm_name_lower:
# 固定形式のメールCを送信 - 危険
subject = '【危険】重大なアラームが発生しました'
else:
# 固定形式のメールDを送信 - アラーム状態
subject = '【アラーム状態】アラームが発生しました'
message = f'''発生時間:{occurrence_time}
発生アラーム名:{alarm_name}
発生メトリクス:{metrics}
理由:
{reason}
詳細はdevops環境のAWS Cloudwatchコンソールでご確認ください。
リンク:{alarm_url}'''
# Adaptive Cardを作成(メンションを含む)
workflow_data = create_adaptive_card_with_mention(subject, occurrence_time, alarm_name, metrics, reason, alarm_url, mention_name, mention_id)
# SNSメールを送信
send_email(message, subject)
# ワークフローにデータを送信
send_to_workflow(workflow_data)
def create_alarm_url(region, alarm_name):
"""
CloudWatchアラームのコンソールURLを生成します
"""
# アラーム名をURLエンコード
encoded_alarm_name = urllib.parse.quote(alarm_name)
# コンソールのURLを作成
url = f'https://{region}.console.aws.amazon.com/cloudwatch/home?region={region}#alarmsV2:alarm/{encoded_alarm_name}'
return url
def create_adaptive_card_with_mention(title, occurrence_time, alarm_name, metrics, reason, alarm_url, mention_name, mention_id):
"""
Adaptive Cardを作成し、メンションを含めます
"""
mention_text = f"<at>{mention_name}</at>"
card = {
"type": "message",
"attachments": [
{
"contentType": "application/vnd.microsoft.card.adaptive",
"contentUrl": None,
"content": {
"$schema": "http://adaptivecards.io/schemas/adaptive-card.json",
"type": "AdaptiveCard",
"version": "1.4",
"msteams": {
"entities": [
{
"type": "mention",
"text": mention_text,
"mentioned": {
"id": mention_id,
"name": mention_name
}
}
]
},
"body": [
{
"type": "TextBlock",
"text": f"{mention_text} {title}",
"size": "Large",
"weight": "Bolder",
"color": "Attention",
"wrap": True
},
{
"type": "FactSet",
"facts": [
{
"title": "発生時間:",
"value": occurrence_time
},
{
"title": "発生アラーム名:",
"value": alarm_name
},
{
"title": "発生メトリクス:",
"value": metrics
}
]
},
{
"type": "TextBlock",
"text": "理由:",
"weight": "Bolder",
"wrap": True
},
{
"type": "TextBlock",
"text": reason,
"wrap": True
},
{
"type": "TextBlock",
"text": "詳細はdevops環境のAWS Cloudwatchコンソールでご確認ください。",
"wrap": True
},
{
"type": "TextBlock",
"text": f"[リンクはこちら]({alarm_url})",
"wrap": True
}
]
}
}
]
}
return card
Register as a new user and use Qiita more conveniently
- You get articles that match your needs
- You can efficiently read back useful information
- You can use dark theme