Azure MonitorのアラートをMicrosoft Teamsに通知する方法
はじめに
この記事ではAzure MonitorのアラートをMicrosoft Teamsに通知する方法を紹介します。
Azure Monitorとは
Azure Monitorは、Azureのサービスの正常性やアプリケーションのログなどを監視することができるサービスです。
以下に、Azure Monitorの機能を紹介します。
- アプリケーション監視 (Application Insights): アプリケーションのパフォーマンス、可用性、ログなどをリアルタイムで監視します。
- インフラストラクチャ監視 (VM Insights, Container Insights): 仮想マシンやコンテナなどのインフラストラクチャのパフォーマンスと状態を監視します。
- ネットワーク監視: ネットワークパフォーマンス、接続状況、トラフィック分析を提供します。
- ログ分析 (Log Analytics): ログデータを収集、検索、分析するためのツールを提供します。
Azure Monitorのアラートと通知
Azure Monitorでは、アラートルールを設定することで、アラートルールの条件に一致する問題が発生した場合にアラートを通知することができます。
アラートルールの設定後、アクショングループという通知方法の設定すると、指定した通知方法で通知されます。
通知方法の種類として、Eメール、SMS、Webhook、Azure Automation Runbook、Azure Functions、Azure Logic Appsなどがあります。
共通アラートスキーマ
Azure Monitorでは共通アラート スキーマ(Common Alert Schema)という、Azureでのアラート通知の標準化されたフォーマットを使用することができます。
このスキーマを使用することで、Application InsightsやLog Analytics、コストアラートなど異なるサービスからのアラートを共通のフォーマットで受け取ることができます。
異なるソースからのアラートを効率的に処理することを目的としています。
共通アラート スキーマには以下のような情報が含まれます。
-
essentials: 基本情報
- alertId: ユニークなアラート識別子。
- firedDateTime: アラートが生成された時刻。
- monitoringService: アラートを検出したAzure Monitorのサービス。
- description: アラートの説明。
-
alertContext: アラートに関連する詳細な情報やメトリクス。
- アラートの内容や原因など情報。
コストアラートの共通アラートスキーマ
コストアラートは、Azureのコストが設定した予算額を超過した場合に通知するアラートです。
コストアラートの共通アラートスキーマは以下のようになります。
{
"schemaId": "azureMonitorCommonAlertSchema",
"data": {
"essentials": {
"monitoringService": "CostAlerts",
"firedDateTime": "2023-12-05T12:02:54.657Z",
"description": "Your spend for budget Test_actual_cost_budget is now $11,111.00 exceeding your specified threshold $25.00.",
"essentialsVersion": "1.0",
"alertContextVersion": "1.0",
"alertId": "/subscriptions/11111111-1111-1111-1111-111111111111/providers/Microsoft.CostManagement/alerts/Test_Alert",
"alertRule": null,
"severity": null,
"signalType": null,
"monitorCondition": null,
"alertTargetIDs": null,
"configurationItems": [
"budgets"
],
"originAlertId": null
},
"alertContext": {
"AlertCategory": "budgets",
"AlertData": {
"Scope": "/subscriptions/11111111-1111-1111-1111-111111111111/",
"ThresholdType": "Actual",
"BudgetType": "Cost",
"BudgetThreshold": "$50.00",
"NotificationThresholdAmount": "$25.00",
"BudgetName": "Test_actual_cost_budget",
"BudgetId": "/subscriptions/11111111-1111-1111-1111-111111111111/providers/Microsoft.Consumption/budgets/Test_actual_cost_budget",
"BudgetStartDate": "2022-11-01",
"BudgetCreator": "test@sample.test",
"Unit": "USD",
"SpentAmount": "$11,111.00"
}
}
}
}
Log Alerts V2の共通アラートスキーマ
Azure MonitorのLog Alerts V2は、Azure Monitor Log Analyticsのデータに基づいてアラートを作成し管理するための強化されたアラートシステムです。
Log Alerts V2は、Log Analyticsのクエリ機能を使用してログデータを定期的に評価し、特定の条件が満たされた場合にアラートを生成します。
また、Application Insightでアプリケーションログのアラートを設定した際も、Log Alerts V2のスキーマが使用されます。
Application Insightsは、アプリケーションのパフォーマンスや可用性を監視するサービスです。
JSONの項目のうち、linkToSearchResultsAPIとlinkToFilteredSearchResultsAPIのURLを実行すると、Application Insightsで対象のクエリの実行結果の表示が可能です。
{
"schemaId": "azureMonitorCommonAlertSchema",
"data": {
"essentials": {
"alertId": "/subscriptions/11111111-1111-1111-1111-111111111111/providers/Microsoft.AlertsManagement/alerts/11111111-1111-1111-1111-111111111111",
"alertRule": "ar-test",
"severity": "Sev1",
"signalType": "Log",
"monitorCondition": "Fired",
"monitoringService": "Log Alerts V2",
"alertTargetIDs": [
"/subscriptions/11111111-1111-1111-1111-111111111111/resourcegroups/rg-test-dev-001/providers/microsoft.insights/components/func-test-dev-eastus-001"
],
"configurationItems": [
"/subscriptions/11111111-1111-1111-1111-111111111111/resourceGroups/rg-test-dev-001/providers/microsoft.insights/components/func-test-dev-eastus-001"
],
"originAlertId": "11111111-1111-1111-1111-111111111111",
"firedDateTime": "2023-12-20T01:43:16.4557405Z",
"description": "Testアラート",
"essentialsVersion": "1.0",
"alertContextVersion": "1.0"
},
"alertContext": {
"properties": {
"includeSearchResults": "True"
},
"conditionType": "LogQueryCriteria",
"condition": {
"windowSize": "PT5M",
"allOf": [
{
"searchQuery": "traces \r\n| where severityLevel > 2\r\n| project timestamp, message, severityLevel, operation_Id, appId, appName\r\n",
"metricMeasureColumn": null,
"targetResourceTypes": "['microsoft.insights/components']",
"operator": "GreaterThanOrEqual",
"threshold": "1",
"timeAggregation": "Count",
"dimensions": [
],
"metricValue": 4.0,
"failingPeriods": {
"numberOfEvaluationPeriods": 1,
"minFailingPeriodsToAlert": 1
},
"linkToSearchResultsUI": "https://portal.azure.com#@11111111-1111-1111-1111-111111111111/blade/Microsoft_Azure_Monitoring_Logs/LogsBlade/source/Alerts.EmailLinks/scope/%7B%22resources%22%3A%5B%7B%22resourceId%22%3A%22%2Fsubscriptions%2F11111111-1111-1111-1111-111111111111%2FresourceGroups%2Frg-test-dev-001%2Fproviders%2Fmicrosoft.insights%2Fcomponents%2Ffunc-test-dev-eastus-001%22%7D%5D%7D/q/hoge%2Fhoge%3D%3D/prettify/1/timespan/2023-12-20T01%3a37%3a44.0000000Z%2f2023-12-20T01%3a42%3a44.0000000Z",
"linkToFilteredSearchResultsUI": "https://portal.azure.com#@11111111-1111-1111-1111-111111111111/blade/Microsoft_Azure_Monitoring_Logs/LogsBlade/source/Alerts.EmailLinks/scope/%7B%22resources%22%3A%5B%7B%22resourceId%22%3A%22%2Fsubscriptions%2F11111111-1111-1111-1111-111111111111%2FresourceGroups%2Frg-test-dev-001%2Fproviders%2Fmicrosoft.insights%2Fcomponents%2Ffunc-test-dev-eastus-001%22%7D%5D%7D/q/hoge%2Fhoge%3D%3D/prettify/1/timespan/2023-12-20T01%3a37%3a44.0000000Z%2f2023-12-20T01%3a42%3a44.0000000Z",
"linkToSearchResultsAPI": "https://api.applicationinsights.io/v1/apps/11111111-1111-1111-1111-111111111111/query?query=traces%20%0D%0A%7C%20where%20severityLevel%20%3E%202%0D%0A%7C%20project%20timestamp%2C%20message%2C%20severityLevel%2C%20operation_Id%2C%20appId%2C%20appName×pan=2023-12-20T01%3a37%3a44.0000000Z%2f2023-12-20T01%3a42%3a44.0000000Z",
"linkToFilteredSearchResultsAPI": "https://api.applicationinsights.io/v1/apps/11111111-1111-1111-1111-111111111111/query?query=traces%20%0D%0A%7C%20where%20severityLevel%20%3E%202%0D%0A%7C%20project%20timestamp%2C%20message%2C%20severityLevel%2C%20operation_Id%2C%20appId%2C%20appName×pan=2023-12-20T01%3a37%3a44.0000000Z%2f2023-12-20T01%3a42%3a44.0000000Z"
}
],
"windowStartTime": "2023-12-20T01:37:44Z",
"windowEndTime": "2023-12-20T01:42:44Z"
}
},
"customProperties": {
"includeSearchResults": "True"
}
}
}
Application InsightsのAPI実行結果
linkToFilteredSearchResultsAPIを実行すると以下のレスポンスをJSONで取得できる。
"linkToFilteredSearchResultsAPI": https://api.applicationinsights.io/v1/apps/11111111-1111-1111-1111-111111111111/query?query=traces
| where severityLevel > 2
| project timestamp, message, severityLevel, operation_Id, appId, appName×pan=2023-12-20T01:37:44.0000000Z/2023-12-20T01:42:44.0000000Z
{
"tables": [
{
"name": "PrimaryResult",
"columns": [
{
"name": "timestamp",
"type": "datetime"
},
{
"name": "message",
"type": "string"
},
{
"name": "severityLevel",
"type": "int"
},
{
"name": "operation_Id",
"type": "string"
},
{
"name": "appId",
"type": "string"
},
{
"name": "appName",
"type": "string"
}
],
"rows": [
[
"2024-01-08T02:13:47.6814847Z",
"Executed 'Functions.TeamsHttpTrigger' (Failed, Id=hoge, Duration=301ms)",
3,
"hoge",
"hoge",
"/subscriptions/hoge/resourcegroups/rg-001/providers/microsoft.insights/components/func-001"
],
[
"2024-01-08T02:13:48.0240301Z",
"Executed 'Functions.TeamsHttpTrigger' (Failed, Id=hoge, Duration=112ms)",
3,
"hoge",
"hoge",
"/subscriptions/hoge/resourcegroups/rg-001/providers/microsoft.insights/components/func-igichatvision-dev-eastus-001"
],
[
"2024-01-08T02:13:49.6339781Z",
"Executed 'Functions.TeamsHttpTrigger' (Failed, Id=hoge, Duration=104ms)",
3,
"hoge",
"hoge",
"/subscriptions/hoge/resourcegroups/rg-dev-001/providers/microsoft.insights/components/func-001"
]
]
}
]
}
Microsoft Teamsに通知する際のアーキテクチャ構成
Azure MonitorのアラートをMicrosoft Teamsに通知する場合の構成は、以下の図になります。
- Azure Monitorのアラートルールでアラートを検出
- Azure MonitorのアクショングループでWebhookで通知用アプリ(Azure FunctionsやLogic Appsなど)に通知
- 通知アプリで共通アラートスキーマからlinkToFilteredSearchResultsAPIのURLを取得し、Application Insightにリクエストを実行し、エラーログを取得
- 通知用アプリからWebhookでTeamsに通知
アクショングループのWebhookではなく、通知用のアプリを間に設定する理由として、アクショングループの通知する場合はAzure Monitorの共通JSONアラートスキーマとして通知されるので、TeamsのWebhookへの直接の通知はサポート対象外になるため、通知用のアプリで共通JSONアラートスキーマをTeamsに通知する形式に加工してから、Webhookで通知しています。
上記のアーキテクチャは以下の手順で構築します。
- Azure Monitorのアラートルールでアラートを通知する条件を設定
- Azure Monitorのアクショングループで通知方法を設定 (WebhookでAzure Functions、Logic Appsなど通知用のアプリを設定)
- Microsoft Teamsの通知対象チャネルで受信用のWebhookを作成 : 受信用のWebhookを作成
- アクショングループのwebhookに指定した通知用のアプリにWebhookを設定
PythonでTeamsのチャネルに通知
PythonからTeamsのチャネルにWebhookで通知する場合、pymsteamsというライブラリを使用することで、通知ができるようになります。
https://pypi.org/project/pymsteams/
以下に、Azure FunctionsのHTTPトリガーでpyteamsを使ってTeamsのチャネルに通知するコードを記載しました。
import json
import logging
import traceback
from datetime import datetime
import azure.functions as func
import pymsteams
import requests
import os
TEAMS_WEBHOOK_URL = os.environ['TEAMS_WEBHOOK_URL']
APPLICATION_INSIGHTS_API_KEY = os.environ['APPLICATION_INSIGHTS_API_KEY']
def main(req: func.HttpRequest) -> func.HttpResponse:
logging.info('Python HTTP trigger function to starting')
body = req.get_body()
headers = req.headers
params = req.params
response = ""
status_code = 200
try:
logging.debug(f"Headers: {headers}")
logging.debug(f"Body: {type(body)}{body}")
body_decode = body.decode('utf-8')
body_json = json.loads(body_decode)
logging.info(f"Body json: {body_json}")
data_json = body_json['data']
alert_context_json = data_json['alertContext']
condition_json = alert_context_json['condition']
allOf_json = condition_json['allOf']
# Application Insights APIに認証ヘッダーを付けてリクエストを送信する
headers = {'x-api-key': APPLICATION_INSIGHTS_API_KEY}
filtered_search_results_api_link = allOf_json[0]['linkToFilteredSearchResultsAPI']
filtered_search_response_response = requests.get(filtered_search_results_api_link, headers=headers)
filtered_search_response_response_json = filtered_search_response_response.json()
logging.info(f"Response linkToFilteredSearch: {type(filtered_search_response_response_json)}: {filtered_search_response_response_json}")
tables = filtered_search_response_response_json['tables']
rows = tables[0]['rows']
sections = {}
for row in rows:
sections["timestamp"] = row[0]
sections["message"] = row[1]
sections["severity_level"] = row[2]
sections["operation_id"] = row[3]
sections["app_id"] = row[4]
sections["app_name"] = row[5]
app_name = os.path.basename(sections["app_name"])
title = f"{app_name} Application Insights Alert"
timestamp = datetime.strptime(sections["timestamp"], '%Y-%m-%dT%H:%M:%S.%fZ')
body = f"{timestamp} Send alert from {app_name} Application Insights"
post_teams(TEAMS_WEBHOOK_URL, title, body, sections)
logging.info(f"row: {sections}")
response = f"alert count : {str(len(rows))} {str(rows)}"
except Exception as e:
status_code = 500
response = traceback.format_exc()
logging.error(f"Error: {e}", exc_info=True)
return func.HttpResponse(response, status_code=status_code)
def post_teams(incoming_webhook_url, send_message_title, send_message_body, sections, link_url=None):
"""Teamsへメッセージを送信
Args:
send_message_title (str): メッセージのタイトル
send_message_body (str): メッセージの本文
sections (dict): セクション
link_url (str): リンク先のURL
Returns:
None
"""
logging.info(f"post_teams: {incoming_webhook_url}")
if incoming_webhook_url:
logging.info(f"post_teams: {send_message_title}")
# メッセージを送信
teams = pymsteams.connectorcard(incoming_webhook_url)
teams.title(send_message_title)
teams.text(send_message_body)
if link_url:
teams.addLinkButton("リンク", link_url)
card_section = pymsteams.cardsection()
for key in sections.keys():
card_section.addFact(key, sections[key])
teams.addSection(card_section)
teams.send()
おわりに
この記事ではAzure MonitorのアラートをMicrosoft Teamsに通知する方法を紹介しました。
この記事がエンジニアの方の参考になれば、幸いです。