はじめに
監視ツールからのアラート対応は、運用チームにとって大きな負担です。アラートの確認、原因調査、チケット起票、関係者への報告——これらを手作業で行うと、対応の遅延やヒューマンエラーが発生しがちです。
本記事では、Zabbix(監視)、Zendesk(チケット管理)、AWS DevOps Agent(AI根本原因分析)を連携させ、アラート発生からRCA(Root Cause Analysis)結果のチケット反映までを自動化する仕組みを構築した手順を紹介します。
全体アーキテクチャ

前提条件
- AWS DevOps Agent のAgent Spaceが作成済みであること
- Zabbixサーバーが構築済みであること(監視対象のホストが登録済み。監視内容はデフォルトのままで問題なし。)
- Zendeskアカウントがセットアップ済みであり、API トークンが発行済みであること
- AWS Organizations でSecurity Hub Findings集約済み(SecurityHub連携を動作検証する場合のみ)
注意: 本記事内のコマンドやコードに含まれる
{AccountId}、{AgentSpaceId}、{your-subdomain}等のプレースホルダーは、ご自身の環境の値に置き換えて実行してください。
1. Zabbixの設定
Zabbixには公式のZendesk連携テンプレート(Webhookメディアタイプ)が用意されています。このテンプレートをインポートすることで、アラート発生時にZendeskチケットを自動作成できます。
テンプレートソース: https://git.zabbix.com/projects/ZBX/repos/zabbix/browse/templates/media/zendesk?at=release/7.4
1-1. グローバルマクロの設定
- Administration → General → ドロップダウンから Macros を選択
- マクロ
{$ZABBIX.URL}を追加し、ZabbixフロントエンドのURLを設定(例:http://192.168.7.123:8081) - Update をクリック
1-2. Zendeskメディアタイプのインポート
- Administration → Media types → Import をクリック
- テンプレートファイル
media_zendesk.yamlを選択して Import - インポートされた Zendesk メディアタイプを開き、以下のパラメータを設定:
| パラメータ | 値 | 説明 |
|---|---|---|
zendesk_url |
https://{your-subdomain}.zendesk.com |
ZendeskのURL |
zendesk_token |
{email}/token:{APIトークン} |
認証情報(email/token:トークンの形式) |
zendesk_type |
incident |
チケットタイプ(question/incident/problems/task) |
instance_id |
{INVENTORY.TAG} |
ホストインベントリのタグ(EC2インスタンスID等) |
region |
{INVENTORY.LOCATION} |
ホストインベントリのロケーション(AWSリージョン等) |
instance_idとregionはZabbixのホストインベントリから自動取得されます。ホストインベントリとは、Zabbixが各監視対象ホストに対して保持できる資産管理情報(デバイスタイプ、シリアル番号、ロケーション等)です。本構成では、ホストインベントリの Tag フィールドにEC2インスタンスID(例:i-0abc123def456)、Location フィールドにAWSリージョン(例:ap-northeast-1)をあらかじめ登録しておきます。これにより、アラート発生時にどのAWSリソースを調査すべきかをDevOps Agentに自動で伝達できます。カスタムフィールド: Zendeskにカスタムフィールドがある場合、
customfield_{フィールドID}の形式でパラメータを追加できます(text/number/date型のみ対応)。
- スクリプトの修正: デフォルトのスクリプトを以下のように修正し、チケット作成時にインベントリ情報をタグとして付与します。
修正ポイント(スクリプト内のタグ生成部分):
// Build tags string with inventory info var tagsStr = [ params.event_tags, 'zabbix', 'instance:' + params.instance_id, 'region:' + params.region ].join(' ');
この修正により、Zendeskチケットのタグに instance:{インスタンスID} と region:{リージョン} が自動付与されます。後続の convertZendeskDevOps Lambda がこのタグを解析し、DevOps Agentに調査対象リソースとして渡します。
このテンプレートは以下のイベントに対応しています:
| Zabbixイベント | 動作 |
|---|---|
| Problem(障害発生) | Zendeskチケットを新規作成 |
| Problem recovery(障害復旧) | 既存チケットを更新 |
| Problem update(障害更新) | 既存チケットを更新 |
| Discovery(ディスカバリ) | Zendeskチケットを新規作成 |
| Autoregistration(自動登録) | Zendeskチケットを新規作成 |
| Internal problem(内部障害) | Zendeskチケットを新規作成 |
注意: タグ・優先度・ステータスはチケット作成時のみ設定され、更新時には上書きされません。カスタムフィールドとsubjectフィールドは更新時にも反映されます。
1-3. アラート通知用ユーザーの作成
- Administration → Users → Create user
- ユーザー名を「Zendesk User」等に設定
- Groups で、監視対象ホストへの読み取り権限を持つグループを選択
- Media タブ → Add:
- Type: Zendesk
- Send to: 任意のテキスト(使用されないが必須)
- Enabled: チェック
- Add で保存
1-4. アクションの設定
Alerts → Actions → Trigger actions → Create action
| 項目 | 値 |
|---|---|
| Name | Zendesk Ticket Creation |
| Conditions | Trigger severity >= Warning |
Operations:
| 項目 | 値 |
|---|---|
| Send to users | Zendesk User(上記で作成したユーザー) |
| Send only to | Zendesk |
2. Zendeskの設定
2-1. Zendesk → DevOps Agent 連携用Webhookの作成
Zendeskでチケットが作成された際に、AWS DevOps Agentに調査を依頼するためのWebhookを設定します。
Admin Center → Apps and integrations → Webhooks → Create webhook
| 項目 | 値 |
|---|---|
| Name | AWS DevOps Agent |
| Endpoint URL | 後述 3-1. convertZendeskDevOps の Lambda Function URL |
| Request method | POST |
| Request format | JSON |
Webhook Body(JSON テンプレート):
{ "id": "{{ticket.id}}", "subject": "{{ticket.title}}", "description": "{{ticket.description}}", "priority": "{{ticket.priority}}", "tags": "{{ticket.tags}}" }
| フィールド | Zendeskプレースホルダー | 用途 |
|---|---|---|
id |
{{ticket.id}} |
チケットID(DevOps Agentの referenceId に使用) |
subject |
{{ticket.title}} |
チケット件名(DevOps Agentの調査タイトル) |
description |
{{ticket.description}} |
チケット本文(調査の詳細情報) |
priority |
{{ticket.priority}} |
優先度(DevOps Agentの優先度にマッピング) |
tags |
{{ticket.tags}} |
タグ(instance: region: を含む) |
2-2. トリガーの作成
Admin Center → Objects and rules → Business rules → Triggers → Create trigger
| 項目 | 値 |
|---|---|
| Name | Send to DevOps Agent |
| Conditions (ALL) | Ticket is Created, Tag contains zabbix |
| Actions | Notify webhook → AWS DevOps Agent |
3. Lambda関数の構築
共通: boto3 Lambda Layer
AWS DevOps Agent のAPIを利用するには、最新のboto3が必要です。Lambda組み込みのboto3にはdevops-agentサービスモデルが含まれていないため、Layerとして最新版を追加します。
# レイヤー作成 mkdir -p /tmp/boto3-layer/python pip3 install boto3 -t /tmp/boto3-layer/python --quiet cd /tmp/boto3-layer && zip -r /tmp/boto3-layer.zip python -q aws lambda publish-layer-version \ --layer-name boto3-latest \ --zip-file fileb:///tmp/boto3-layer.zip \ --compatible-runtimes python3.14 \ --region us-east-1
ランタイムバージョンについて: 本記事では執筆時点での最新バージョン
python3.14を指定しています。環境に合わせてpython3.12等の利用可能なバージョンに適宜変更してください。注意: Lambda組み込みのboto3が優先読み込みされる問題を回避するため、各Lambda関数の先頭で以下のコードが必要です。以降のLambda関数のコードにはすべてこの処理を含めていますが、仕組みは共通です。
import sys sys.path.insert(0, '/opt/python') for mod_name in list(sys.modules): if mod_name == 'boto3' or mod_name.startswith('boto3.') or \ mod_name == 'botocore' or mod_name.startswith('botocore.'): del sys.modules[mod_name] import boto3 # レイヤーの最新版が読み込まれる
3-1. convertZendeskDevOps(Zendesk → DevOps Agent Webhook転送)
ZendeskからのWebhookを受け取り、DevOps AgentのGeneric Webhookフォーマットに変換して転送するLambda関数です。
なぜLambda Function URLでラッピングするのか:
ZendeskのWebhookはHTTPリクエストを送信するだけのシンプルな仕組みであり、DevOps AgentのGeneric Webhookが要求するHMAC-SHA256署名ヘッダー(x-amzn-event-signature)を付与する機能がありません。また、Zendeskのチケットデータ構造とDevOps Agentが期待するペイロード構造(eventType, incidentId, affectedResources 等)も異なります。そのため、間にLambda Function URLを挟み、以下の変換を行います:
- Zendeskのチケットデータ → DevOps Agent Webhookペイロードへの構造変換
- HMAC-SHA256署名の生成・付与
- チケットタグからAWSリソース情報(インスタンスID、リージョン)の抽出
Lambda設定:
| 項目 | 値 |
|---|---|
| 関数名 | convertZendeskDevOps |
| Runtime | python3.14 |
| Timeout | 3秒 |
| Memory | 128MB |
| トリガー | Function URL(認証なし) |
セキュリティに関する注意: 本記事では検証目的で認証なし(
AuthType: NONE)のFunction URLを使用しています。URLが漏洩すると第三者からリクエストを送信される可能性があるため、本番環境では認証付きFunction URL(AuthType: AWS_IAM)の利用や、Lambda関数内でZendesk側のWebhook署名を検証する仕組みの導入を推奨します。
環境変数:
| 変数名 | 説明 |
|---|---|
DEVOPS_AGENT_WEBHOOK_URL |
後述の手順で取得したWebhook URL |
DEVOPS_AGENT_WEBHOOK_SECRET |
後述の手順で取得したHMACシークレット |
DevOps Agent Generic Webhookの発行手順
convertZendeskDevOps がDevOps Agentに調査を依頼するには、Generic WebhookのURLとHMACシークレットが必要です。以下の手順で発行します。
- AWS マネジメントコンソールで AWS DevOps Agent コンソールを開く
- 対象の Agent Space を選択
- Capabilities タブ → Webhook セクション → Configure をクリック
- Generate webhook をクリック
- 生成された Webhook URL と HMAC Secret を控える
重要: HMACシークレットは生成時にしか表示されません。必ずこの時点で安全な場所に保存してください。再表示はできません。
- 控えた値をLambdaの環境変数に設定:
DEVOPS_AGENT_WEBHOOK_URL→ Webhook URL(例:https://event-ai.us-east-1.api.aws/webhook/generic/{webhook_id})DEVOPS_AGENT_WEBHOOK_SECRET→ HMAC Secret
参考: Invoking DevOps Agent through Webhook(AWS公式ドキュメント)
コード:
import json import hmac import hashlib import base64 import os from datetime import datetime, timezone from urllib.request import Request, urlopen WEBHOOK_URL = os.environ['DEVOPS_AGENT_WEBHOOK_URL'] WEBHOOK_SECRET = os.environ['DEVOPS_AGENT_WEBHOOK_SECRET'] PRIORITY_MAP = {'urgent': 'CRITICAL', 'high': 'HIGH', 'normal': 'MEDIUM', 'low': 'LOW'} def lambda_handler(event, context): if 'body' in event: ticket = json.loads(event['body']) else: ticket = event tags = ticket.get('tags', '').split() instance_id = '' region = 'ap-northeast-1' for tag in tags: if tag.startswith('instance:'): instance_id = tag.split(':', 1)[1] elif tag.startswith('region:'): region = tag.split(':', 1)[1] timestamp = datetime.now(timezone.utc).strftime('%Y-%m-%dT%H:%M:%S.000Z') payload = json.dumps({ 'eventType': 'incident', 'incidentId': f"zendesk-{ticket['id']}", 'action': 'created', 'priority': PRIORITY_MAP.get(ticket.get('priority', ''), 'MEDIUM'), 'title': ticket.get('subject', ''), 'description': ticket.get('description', ''), 'timestamp': timestamp, 'service': 'ZabbixMonitoring', 'data': { 'metadata': { 'region': region, 'environment': 'production', 'source': 'zendesk', 'zendesk_ticket_id': str(ticket['id']) } }, 'affectedResources': [instance_id] }) sig = base64.b64encode( hmac.new( WEBHOOK_SECRET.encode(), f"{timestamp}:{payload}".encode(), hashlib.sha256 ).digest() ).decode() req = Request(WEBHOOK_URL, data=payload.encode(), method='POST', headers={ 'Content-Type': 'application/json', 'x-amzn-event-timestamp': timestamp, 'x-amzn-event-signature': sig }) resp = urlopen(req) return {'statusCode': resp.status, 'body': resp.read().decode()}
ポイント:
- ZendeskチケットのタグからEC2インスタンスIDやリージョンを抽出し、DevOps Agentに渡す
- incidentId に zendesk-{ticket_id} を設定することで、後続のRCA結果をチケットに紐付け可能にする
- HMAC-SHA256署名でWebhookの認証を行う
Zendesk → DevOps Agent Webhookのフィールドマッピング:
| DevOps Agent Webhookフィールド | 値の取得元 | 説明 |
|---|---|---|
eventType |
固定値 incident |
イベント種別 |
incidentId |
zendesk-{ticket.id} |
Zendeskチケットとの紐付けID |
priority |
ticket.priority |
urgent→CRITICAL, high→HIGH, normal→MEDIUM, low→LOW |
title |
ticket.subject |
チケット件名 |
description |
ticket.description |
チケット本文 |
affectedResources |
タグ instance:{id} |
調査対象のAWSリソース |
data.metadata.region |
タグ region:{region} |
調査対象のAWSリージョン |
3-2. createZendeskTicket(SecurityHub/CloudWatch → Zendesk + DevOps Agent)
SecurityHub FindingsやCloudWatch Alarmから直接Zendeskチケットを作成し、同時にDevOps AgentにRCA調査を依頼するLambda関数です。
Lambda設定:
| 項目 | 値 |
|---|---|
| 関数名 | createZendeskTicket |
| Runtime | python3.14 |
| Timeout | 30秒 |
| Memory | 128MB |
| Layer | boto3-latest:1 |
環境変数:
| 変数名 | 説明 |
|---|---|
ZENDESK_URL |
ZendeskのURL |
ZENDESK_USER |
{email}/token |
ZENDESK_TOKEN |
Zendesk APIトークン |
AGENT_SPACE_ID |
DevOps Agent Space ID |
ASSOCIATION_ID |
Event Channel Association ID |
AGENT_SPACE_ID と ASSOCIATION_ID の確認方法:
AWS CLI で以下のコマンドを実行し、それぞれのIDを取得します。
# Agent Space ID の確認
aws devops-agent list-agent-spaces --region us-east-1
# Association ID の確認(Event Channel の associationId を使用)
aws devops-agent list-associations \
--agent-space-id {AgentSpaceId} --region us-east-1
list-associations の結果から、"configuration": {"eventChannel": {}} を持つエントリの associationId を使用します。
IAMポリシー(インライン):
{ "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Action": ["aidevops:CreateBacklogTask"], "Resource": "arn:aws:aidevops:us-east-1:{AccountId}:agentspace/{AgentSpaceId}" }] }
上記に加え、AWS管理ポリシー
AWSLambdaBasicExecutionRole(CloudWatch Logsへのログ出力権限)のアタッチも必要です。以降のLambda関数も同様です。
コード:
import sys import os sys.path.insert(0, '/opt/python') for mod_name in list(sys.modules): if mod_name == 'boto3' or mod_name.startswith('boto3.') or \ mod_name == 'botocore' or mod_name.startswith('botocore.'): del sys.modules[mod_name] import json import base64 import uuid import boto3 from urllib.request import Request, urlopen ZENDESK_URL = os.environ['ZENDESK_URL'] ZENDESK_USER = os.environ['ZENDESK_USER'] ZENDESK_TOKEN = os.environ['ZENDESK_TOKEN'] AGENT_SPACE_ID = os.environ['AGENT_SPACE_ID'] ASSOCIATION_ID = os.environ['ASSOCIATION_ID'] devops_client = boto3.client('devops-agent') SEVERITY_MAP = { 'CRITICAL': 'urgent', 'HIGH': 'high', 'MEDIUM': 'normal', 'LOW': 'low', 'INFORMATIONAL': 'low' } PRIORITY_MAP = { 'CRITICAL': 'CRITICAL', 'HIGH': 'HIGH', 'MEDIUM': 'MEDIUM', 'LOW': 'LOW', 'INFORMATIONAL': 'MINIMAL' } def create_zendesk_ticket(subject, body, priority='normal'): auth = base64.b64encode(f'{ZENDESK_USER}:{ZENDESK_TOKEN}'.encode()).decode() payload = json.dumps({ 'ticket': { 'subject': subject, 'comment': {'body': body, 'public': False}, 'priority': priority } }) req = Request( f'{ZENDESK_URL}/api/v2/tickets.json', data=payload.encode(), method='POST', headers={'Content-Type': 'application/json', 'Authorization': f'Basic {auth}'} ) resp = urlopen(req) result = json.loads(resp.read().decode()) return result['ticket']['id'] def create_devops_task(title, description, priority, ticket_id): resp = devops_client.create_backlog_task( agentSpaceId=AGENT_SPACE_ID, taskType='INVESTIGATION', title=title[:400], description=description[:10000], priority=priority, reference={ 'system': 'Event Channel', 'title': f'EventChannel-zendesk-{ticket_id}', 'referenceId': f'zendesk-{ticket_id}', 'referenceUrl': f'{ZENDESK_URL}/agent/tickets/{ticket_id}', 'associationId': ASSOCIATION_ID }, clientToken=str(uuid.uuid4()) ) return resp['task']['taskId'] def lambda_handler(event, context): source = event.get('source', '') detail_type = event.get('detail-type', '') if source == 'aws.securityhub': f = event['detail']['findings'][0] severity = f.get('Severity', {}).get('Label', 'MEDIUM') subject = f'[SecurityHub][{severity}] {f.get("Title", "")}' body = f'Severity: {severity}\nAccount: {f.get("AwsAccountId", "")}\n' \ f'Region: {f.get("Region", "")}\n{f.get("Description", "")}' ticket_id = create_zendesk_ticket(subject, body, SEVERITY_MAP.get(severity, 'normal')) create_devops_task(subject, body, PRIORITY_MAP.get(severity, 'MEDIUM'), ticket_id) elif source == 'aws.cloudwatch': detail = event['detail'] alarm_name = detail.get('alarmName', '') reason = detail.get('state', {}).get('reason', '') subject = f'[CloudWatch Alarm] {alarm_name}' body = f'Alarm: {alarm_name}\nReason: {reason}' ticket_id = create_zendesk_ticket(subject, body, 'high') create_devops_task(subject, body, 'HIGH', ticket_id) return {'statusCode': 200}
3-3. sendBackToZendesk(DevOps Agent RCA結果 → Zendesk)
DevOps AgentのRCA完了後、結果をZendeskチケットにコメントとして投稿するLambda関数です。
Lambda設定:
| 項目 | 値 |
|---|---|
| 関数名 | sendBackToZendesk |
| Runtime | python3.14 |
| Timeout | 30秒 |
| Memory | 128MB |
| Layer | boto3-latest:1 |
環境変数:
| 変数名 | 説明 |
|---|---|
ZENDESK_URL |
ZendeskのURL |
ZENDESK_USER |
{email}/token |
ZENDESK_TOKEN |
Zendesk APIトークン |
IAMポリシー(インライン):
{ "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Action": ["aidevops:GetBacklogTask", "aidevops:ListJournalRecords"], "Resource": "arn:aws:aidevops:us-east-1:{AccountId}:agentspace/{AgentSpaceId}" }] }
リソースは Agent Space レベルで指定しています。DevOps Agent APIではタスクIDやレコードID単位でのリソース指定ができないため、Agent Space ARN が最小のリソーススコープとなります。
コード:
import sys import os sys.path.insert(0, '/opt/python') for mod_name in list(sys.modules): if mod_name == 'boto3' or mod_name.startswith('boto3.') or \ mod_name == 'botocore' or mod_name.startswith('botocore.'): del sys.modules[mod_name] import json import base64 import boto3 from urllib.request import Request, urlopen ZENDESK_URL = os.environ['ZENDESK_URL'] ZENDESK_USER = os.environ['ZENDESK_USER'] ZENDESK_TOKEN = os.environ['ZENDESK_TOKEN'] devops_client = boto3.client('devops-agent') def get_rca_summary(agent_space_id, execution_id): try: records = devops_client.list_journal_records( agentSpaceId=agent_space_id, executionId=execution_id, recordType='investigation_summary_md' ).get('records', []) if records: content = records[0].get('content', '') return content if isinstance(content, str) else json.dumps(content) except Exception as e: print(f'Failed to get journal records: {e}') return '' def post_to_zendesk(ticket_id, comment): auth = base64.b64encode(f'{ZENDESK_USER}:{ZENDESK_TOKEN}'.encode()).decode() payload = json.dumps({'ticket': {'comment': {'body': comment, 'public': False}}}) req = Request( f'{ZENDESK_URL}/api/v2/tickets/{ticket_id}.json', data=payload.encode(), method='PUT', headers={'Content-Type': 'application/json', 'Authorization': f'Basic {auth}'} ) return urlopen(req).status def lambda_handler(event, context): detail = event.get('detail', {}) detail_type = event.get('detail-type', '') metadata = detail.get('metadata', {}) data = detail.get('data', {}) agent_space_id = metadata.get('agent_space_id') task_id = metadata.get('task_id') execution_id = metadata.get('execution_id') # タスク情報からZendeskチケットIDを取得 task = devops_client.get_backlog_task( agentSpaceId=agent_space_id, taskId=task_id ).get('task', {}) reference_id = task.get('reference', {}).get('referenceId', '') if not reference_id.startswith('zendesk-'): return {'statusCode': 200, 'body': 'Not a Zendesk task'} ticket_id = reference_id.replace('zendesk-', '') rca_execution_id = execution_id # Investigation Linked の場合、親タスクのRCAを取得 if detail_type == 'Investigation Linked': primary_task_id = task.get('primaryTaskId') if primary_task_id: primary_task = devops_client.get_backlog_task( agentSpaceId=agent_space_id, taskId=primary_task_id ).get('task', {}) rca_execution_id = primary_task.get('executionId') # RCAサマリーを取得 rca_body = get_rca_summary(agent_space_id, rca_execution_id) if rca_execution_id else '' # Zendeskにコメント投稿 if rca_body: status_reason = task.get('statusReason', '') comment = f'=== AWS DevOps Agent RCA Report ===\nTask: {task_id}\n' if detail_type == 'Investigation Linked' and status_reason: comment += f'Note: {status_reason}\n' comment += f'Priority: {data.get("priority", "N/A")}\n\n{rca_body}' else: comment = f'=== AWS DevOps Agent Report ===\nTask: {task_id}\n' \ f'Status: {data.get("status", "N/A")}\n' \ 'RCA summary not yet available.' post_to_zendesk(ticket_id, comment) return {'statusCode': 200}
ポイント:
- Investigation Completed イベントでは自身の executionId からRCA本文を取得
- Investigation Linked イベントでは primaryTaskId で親タスクを辿り、親のRCA結果を取得
- RCA本文は list_journal_records API の investigation_summary_md レコードタイプで取得
補足: 本記事の
get_rca_summary関数ではlist_journal_recordsのページネーション処理を省略しています。レコード数が多い場合はNextTokenを使ったループ処理が必要になる場合があります。3つのLambda関数の連携について:
createZendeskTicketがZendeskチケット作成とDevOps Agentへの調査依頼を同時に行い、convertZendeskDevOpsはZabbix経由のZendeskチケットに対して同様の調査依頼を行います。どちらの経路でも、DevOps Agentが調査を完了すると EventBridge にInvestigation Completed/Investigation Linkedイベントが発行され、sendBackToZendeskがRCA結果を元のZendeskチケットに書き戻します。この紐付けは、すべてのLambdaが共通してzendesk-{ticket_id}をreferenceIdとして使用することで実現しています。
4. EventBridgeルールの設定
本構成で使用するEventBridgeイベントは以下の通りです。
| ソース | detail-type | 説明 | トリガー先 |
|---|---|---|---|
aws.securityhub |
Security Hub Findings - Imported |
SecurityHub Findingsの検出 | createZendeskTicket |
aws.cloudwatch |
CloudWatch Alarm State Change |
CloudWatchアラームの状態変更 | createZendeskTicket |
aws.aidevops |
Investigation Completed |
DevOps Agent RCA完了 | sendBackToZendesk |
aws.aidevops |
Investigation Linked |
既存調査へのリンク | sendBackToZendesk |
4-1. sentBackToZendesk(DevOps Agent → sendBackToZendesk)
命名について: EventBridgeルール名は
sentBackToZendesk(過去形: 送り返された)、Lambda関数名はsendBackToZendesk(現在形: 送り返す)としています。ルール名はイベントの発生(=調査が完了した)を表し、Lambda関数名は実行するアクション(=Zendeskに送る)を表しています。
aws events put-rule \
--name sentBackToZendesk \
--event-pattern '{
"source": ["aws.aidevops"],
"detail-type": ["Investigation Completed", "Investigation Linked"]
}' \
--region us-east-1
aws events put-targets \
--rule sentBackToZendesk \
--targets '[{"Id":"sendBackToZendesk","Arn":"arn:aws:lambda:us-east-1:{AccountId}:function:sendBackToZendesk"}]' \
--region us-east-1
4-2. securityHubToZendesk(SecurityHub → createZendeskTicket)
aws events put-rule \
--name securityHubToZendesk \
--event-pattern '{
"source": ["aws.securityhub"],
"detail-type": ["Security Hub Findings - Imported"],
"detail": {
"findings": {
"Severity": {"Label": ["CRITICAL", "HIGH"]},
"Workflow": {"Status": ["NEW"]}
}
}
}' \
--region us-east-1
aws events put-targets \
--rule securityHubToZendesk \
--targets '[{"Id":"createZendeskTicket","Arn":"arn:aws:lambda:us-east-1:{AccountId}:function:createZendeskTicket"}]' \
--region us-east-1
4-3. cloudwatchAlarmToZendesk(CloudWatch Alarm → createZendeskTicket)
aws events put-rule \
--name cloudwatchAlarmToZendesk \
--event-pattern '{
"source": ["aws.cloudwatch"],
"detail-type": ["CloudWatch Alarm State Change"],
"detail": {"state": {"value": ["ALARM"]}}
}' \
--region us-east-1
aws events put-targets \
--rule cloudwatchAlarmToZendesk \
--targets '[{"Id":"createZendeskTicket","Arn":"arn:aws:lambda:us-east-1:{AccountId}:function:createZendeskTicket"}]' \
--region us-east-1
4-4. Lambdaリソースベースポリシーの追加
EventBridgeからLambdaを呼び出すには、リソースベースポリシーが必要です。これがないとEventBridgeの FailedInvocations になります。
# sendBackToZendesk
aws lambda add-permission \
--function-name sendBackToZendesk \
--statement-id EventBridgeInvoke \
--action lambda:InvokeFunction \
--principal events.amazonaws.com \
--source-arn arn:aws:events:us-east-1:{AccountId}:rule/sentBackToZendesk \
--region us-east-1
# createZendeskTicket(SecurityHub用)
aws lambda add-permission \
--function-name createZendeskTicket \
--statement-id SecurityHubEventBridge \
--action lambda:InvokeFunction \
--principal events.amazonaws.com \
--source-arn arn:aws:events:us-east-1:{AccountId}:rule/securityHubToZendesk \
--region us-east-1
# createZendeskTicket(CloudWatch Alarm用)
aws lambda add-permission \
--function-name createZendeskTicket \
--statement-id CloudWatchAlarmEventBridge \
--action lambda:InvokeFunction \
--principal events.amazonaws.com \
--source-arn arn:aws:events:us-east-1:{AccountId}:rule/cloudwatchAlarmToZendesk \
--region us-east-1
注意: EventBridgeターゲットにLambdaを指定する場合、
RoleArnは不要です。Lambda呼び出しはリソースベースポリシーで認可されます。RoleArnを設定するとAssumeRole失敗でFailedInvocationsになる場合があります。
5. ハマりどころと対策
5-1. Lambda組み込みboto3にdevops-agentサービスモデルがない
Lambda Runtime の組み込みboto3は古いバージョンのため、devops-agent サービスが含まれていません。Lambda Layerで最新のboto3を追加し、sys.path.insert(0, '/opt/python') で優先読み込みする必要があります。
5-2. EventBridgeからLambdaが呼び出せない(FailedInvocations)
EventBridgeルールを作成しても、Lambda側にリソースベースポリシーがないと呼び出しが失敗します。CloudWatch Logsにもログが残らないため、原因特定が困難です。aws lambda get-policy でポリシーの有無を確認してください。
5-3. DevOps Agentの「Investigation Linked」ではRCAサマリーが生成されない
DevOps Agentは、同一ホスト・同一原因と判断したアラートを既存の調査にリンクする機能を持っています。この場合、リンクされた側のタスクは Investigation Linked ステータスとなり、独自のRCA調査は実行されません。RCAサマリーは親タスク(リンク先)の executionId にのみ生成されます。
そのため、sendBackToZendesk では Investigation Linked イベントを受けた際に、タスクの primaryTaskId フィールドで親タスクを辿り、親タスクの executionId からRCAサマリーを取得する実装が必要です。
6. 動作確認
実際にZabbixで監視しているEC2インスタンス上でCPU負荷を発生させ、アラートからRCA結果のチケット反映までの一連の流れを確認します。
6-1. CPU負荷の発生
Zabbixエージェントが稼働しているEC2インスタンスにSSHで接続し、以下のコマンドでCPUを意図的に高騰させます。
yes > /dev/null & yes > /dev/null & yes > /dev/null & yes > /dev/null &
6-2. Zabbixアラートの発火 → Zendeskチケット作成
ZabbixがCPU使用率の閾値超過を検知し、トリガーが発火します。設定済みのZendesk Webhookメディアタイプにより、Zendeskにインシデントチケットが自動作成されます。
チケットにはZabbixのアラート情報に加え、タグとして zabbix、instance:{インスタンスID}、region:{リージョン} が付与されます。


6-3. DevOps Agentによる調査の開始
Zendeskのトリガーが zabbix タグを検知し、convertZendeskDevOps Lambda経由でDevOps AgentのGeneric Webhookにイベントが送信されます。DevOps Agentが自動的にInvestigationを開始し、CloudWatchメトリクス、CloudTrailログ、EC2の状態などを横断的に分析します。

6-4. RCA結果の特定とチケットへの反映
DevOps AgentがRCAを完了すると、EventBridgeに Investigation Completed イベントが発行されます。sendBackToZendesk Lambdaがこのイベントをキャッチし、list_journal_records APIでRCAサマリーを取得、Zendeskチケットにプライベートコメントとして投稿します。
RCAレポートには以下のような内容が含まれます:
- Symptoms(症状): CPU使用率の急上昇とアラートの発火タイミング
- Findings(調査結果): 原因となったプロセスの特定、CloudTrailでの操作履歴、インスタンスの状態変化


日本語訳も掲載しておきます。

運用チームはZendeskチケットを開くだけで、AIが分析した根本原因と経緯を即座に確認できます。
6-5. CloudWatch アラームからの連携
前回記事のLambdaを利用して、AWS側で発生したCloudWatch アラームを起点とする調査も検証します。 blog.serverworks.co.jp
下記のコマンドでLambdaを実行すると、Lambda内で意図的にエラーを発生させ、CloudWatch アラームが発火します。 アラームのアクションとして、createZendeskTicketが実行されます。
# シナリオを指定して実行
aws lambda invoke \
--function-name AWS-DevOpsAgent-test-lambda \
--cli-binary-format raw-in-base64-out \
--payload '{"scenario":"db-timeout"}' \
response.json \
--region us-east-1
こちらもチケット起票と、RCA完了後の結果の追記がされました。

まとめ
本記事では、以下の自動化パイプラインを構築しました。
| 経路 | フロー |
|---|---|
| Zabbix経由 | Zabbix → Zendesk(チケット作成)→ convertZendeskDevOps → DevOps Agent(RCA)→ sendBackToZendesk → Zendesk(RCA結果投稿) |
| SecurityHub経由 | SecurityHub → EventBridge → createZendeskTicket → Zendesk + DevOps Agent → sendBackToZendesk → Zendesk |
| CloudWatch経由 | CloudWatch Alarm → EventBridge → createZendeskTicket → Zendesk + DevOps Agent → sendBackToZendesk → Zendesk |
これにより、アラート発生からRCA結果のチケット反映まで、人手を介さずに自動で完了します。運用チームはZendeskチケットを確認するだけで、AIによる根本原因分析の結果を即座に把握できます。