やりたいこと
CloudWatch アラームが ALARM 状態になったら Redmine のチケットに起票したい。
なお、Redmine チケットは次のようにしたい。
- プロジェクト: test_project
- トラッカー: Alarm
- チケットの優先度
- CloudWatch アラーム名に Critical が含まれる場合は、優先度 Urgent とする
- CloudWatch アラーム名に Error が含まれる場合は、優先度 High とする
どうやるか?
Redmine API が提供されているので、これを参考に行う。
構成図
実際は、CloudWatch metrics のアクションとして、Lambda を指定しますが、今回は Lamda からテストイベントを作成し実行させるので、CloudWatch metrics の作成は手順から省いています。
手順
Lightsail で Redmine を作成する
本題は CloudWatch アラームが ALARM 状態になったら Redmine のチケットに起票するという点ですので、Redmine の作成において今回は簡易的に Lightsail で作成しておきます。
ドキュメントを参考に「ステップ 1 〜 4」を行う
繰り返しになりますが、本題は CloudWatch アラームが ALARM 状態になったら Redmine のチケットに起票するという点ですので、「ステップ 5 〜 8」は今回は飛ばします。
プロジェクト,トラッカーの作成、チケットの優先度の確認
チケットの優先度: Urgent と High は既に存在することだけを確認します。
ユーザーと API キーの作成
Redmine の REST API を有効にするため、「RESTによるWebサービスを有効にする」にチェックを入れ、「保存」します。
新しく作成した aws のアカウントでログインし、「個人設定」から API アクセスキーを確認し控えます。
プロジェクト,トラッカー,チケットの優先度の id の確認
プロジェクトの test_project の id: 1 を確認。
<projects total_count="1" offset="0" limit="25" type="array">
<project>
<id>1</id>
<name>test_project</name>
<identifier>test_project</identifier>
<description/>
<homepage/>
<status>1</status>
<is_public>true</is_public>
<inherit_members>false</inherit_members>
<created_on>2024-05-08T02:40:49Z</created_on>
<updated_on>2024-05-08T02:40:49Z</updated_on>
</project>
</projects>
トラッカーの Alarm の id: 6 を確認。
<trackers type="array">
<tracker>
<id>1</id>
<name>Bug</name>
<default_status id="1" name="New"/>
<description/>
<enabled_standard_fields type="array">
<field>assigned_to_id</field>
<field>category_id</field>
<field>fixed_version_id</field>
<field>parent_issue_id</field>
<field>start_date</field>
<field>due_date</field>
<field>estimated_hours</field>
<field>done_ratio</field>
<field>description</field>
<field>priority_id</field>
</enabled_standard_fields>
</tracker>
<tracker>
<id>2</id>
<name>Feature</name>
<default_status id="1" name="New"/>
<description/>
<enabled_standard_fields type="array">
<field>assigned_to_id</field>
<field>category_id</field>
<field>fixed_version_id</field>
<field>parent_issue_id</field>
<field>start_date</field>
<field>due_date</field>
<field>estimated_hours</field>
<field>done_ratio</field>
<field>description</field>
<field>priority_id</field>
</enabled_standard_fields>
</tracker>
<tracker>
<id>3</id>
<name>Support</name>
<default_status id="1" name="New"/>
<description/>
<enabled_standard_fields type="array">
<field>assigned_to_id</field>
<field>category_id</field>
<field>fixed_version_id</field>
<field>parent_issue_id</field>
<field>start_date</field>
<field>due_date</field>
<field>estimated_hours</field>
<field>done_ratio</field>
<field>description</field>
<field>priority_id</field>
</enabled_standard_fields>
</tracker>
<tracker>
<id>6</id>
<name>Alarm</name>
<default_status id="1" name="New"/>
<description/>
<enabled_standard_fields type="array">
<field>assigned_to_id</field>
<field>category_id</field>
<field>fixed_version_id</field>
<field>parent_issue_id</field>
<field>start_date</field>
<field>due_date</field>
<field>estimated_hours</field>
<field>done_ratio</field>
<field>description</field>
<field>priority_id</field>
</enabled_standard_fields>
</tracker>
</trackers>
チケットの優先度の
Urgent の id: 4
High の id: 3
Normal の id: 2
を確認。
<issue_priorities type="array">
<issue_priority>
<id>5</id>
<name>Immediate</name>
<is_default>false</is_default>
<active>true</active>
</issue_priority>
<issue_priority>
<id>4</id>
<name>Urgent</name>
<is_default>false</is_default>
<active>true</active>
</issue_priority>
<issue_priority>
<id>3</id>
<name>High</name>
<is_default>false</is_default>
<active>true</active>
</issue_priority>
<issue_priority>
<id>1</id>
<name>Low</name>
<is_default>false</is_default>
<active>true</active>
</issue_priority>
<issue_priority>
<id>2</id>
<name>Normal</name>
<is_default>false</is_default>
<active>true</active>
</issue_priority>
</issue_priorities>
Lambda の実装
## になっている箇所を自身の環境に合わせて修正し、Lambda 関数を作成します。
import json
import urllib.request
import ssl
def lambda_handler(event, context):
# CloudWatchからのイベント情報を取得
alarm_name = event['detail']['alarmName']
alarm_description = event['detail']['alarmDescription']
new_state = event['detail']['newState']['stateValue']
# new_stateがALARMの場合のみRedmineのチケットに登録
if new_state != 'ALARM':
print('CloudWatchアラームはALARM状態ではありません。Redmineチケットへの登録をスキップします。')
return
# 優先度のデフォルト設定
priority_id = 2 ## enumerations/issue_priorities.xml で確認した Normal id
# alarm_nameに応じて優先度を設定
if 'Error' in alarm_name:
priority_id = 3 ## enumerations/issue_priorities.xml で確認した High id
elif 'Critical' in alarm_name:
priority_id = 4 ## enumerations/issue_priorities.xml で確認した Urgent id
# RedmineのAPIエンドポイントと認証情報
redmine_url = 'https://X.X.X.X/issues.json' ## redmine のip
redmine_api_key = 'abcdefghi1234567' ## redmine aws ユーザーの API アクセスキー
# SSL証明書の検証を無効にする
ssl_context = ssl.create_default_context()
ssl_context.check_hostname = False
ssl_context.verify_mode = ssl.CERT_NONE
# CloudWatchアラームの情報をRedmineチケットに記録するためのデータを準備
ticket_data = {
'issue': {
'project_id': 1, ## projects.xml で確認した test_project id
'tracker_id': 6, ## trackers.xml で確認した Alarm id
'priority_id': priority_id,
'subject': f'Alarm: {alarm_name} is now {new_state}',
'description': f'{alarm_description}\n\nCloudWatch Alarm: {alarm_name} is now {new_state}'
}
}
# RedmineにPOSTリクエストを送信してチケットを作成
req = urllib.request.Request(
redmine_url,
data=json.dumps(ticket_data).encode('utf-8'),
headers={'Content-Type': 'application/json', 'X-Redmine-API-Key': redmine_api_key})
try:
with urllib.request.urlopen(req, context=ssl_context) as response:
response_data = response.read().decode('utf-8')
print(response_data)
print('Redmineチケットが正常に登録されました。')
except urllib.error.HTTPError as e:
print('Redmineチケットの作成中にエラーが発生しました。')
print(e)
動作確認
Critical_test
CloudWatch アラーム名に Critical が含まれる場合は、優先度 Urgent とするを行いたいと思います。
次のようなテストイベントを作成し、Lambda を実行させます。
{
"version": "0",
"id": "b80d68ad-1c0b-4b9e-8c1e-092fe533b4a5",
"detail-type": "CloudWatch Alarm State Change",
"source": "aws.cloudwatch",
"account": "123456789012",
"time": "2024-05-08T12:34:56Z",
"region": "ap-northeast-1.",
"resources": [
"arn:aws:cloudwatch:ap-northeast-1:123456789012:alarm:Critical_test"
],
"detail": {
"alarmName": "Critical_test",
"alarmDescription": "これは緊急テストです。",
"oldState": {
"stateValue": "OK",
"stateReason": "Threshold Crossed: 1 out of the last 1 datapoints [1.0 (08/05/24 12:32:00)] was greater than the threshold (0.0)."
},
"newState": {
"stateValue": "ALARM",
"stateReason": "Threshold Crossed: 1 out of the last 1 datapoints [1.0 (08/05/24 12:32:00)] was greater than the threshold (0.0).",
"stateReasonData": {
"version": "1.0",
"queryDate": "2024-05-08T12:34:56.005Z",
"startDate": "2024-05-08T12:32:00.000Z",
"statistic": "Sum",
"period": 60,
"recentDatapoints": [
1
]
}
}
}
}
実行結果は以下の通りです。
{
"issue": {
"id": 32,
"project": {
"id": 1,
"name": "test_project"
},
"tracker": {
"id": 6,
"name": "Alarm"
},
"status": {
"id": 1,
"name": "New",
"is_closed": false
},
"priority": {
"id": 4,
"name": "Urgent"
},
"author": {
"id": 6,
"name": "aws aws"
},
"subject": "Alarm: Critical_test is now ALARM",
"description": "これは緊急テストです。\r\n\r\nCloudWatch Alarm: Critical_test is now ALARM",
"start_date": "2024-05-09",
"due_date": null,
"done_ratio": 0,
"is_private": false,
"estimated_hours": null,
"total_estimated_hours": null,
"created_on": "2024-05-09T05:39:31Z",
"updated_on": "2024-05-09T05:39:31Z",
"closed_on": null
}
}
Error_test
CloudWatch アラーム名に Error が含まれる場合は、優先度 High とするを行いたいと思います。
次のようなテストイベントを作成し、Lambda を実行させます。
{
"version": "0",
"id": "b80d68ad-1c0b-4b9e-8c1e-092fe533b4a5",
"detail-type": "CloudWatch Alarm State Change",
"source": "aws.cloudwatch",
"account": "123456789012",
"time": "2024-05-08T12:34:56Z",
"region": "ap-northeast-1.",
"resources": [
"arn:aws:cloudwatch:ap-northeast-1:123456789012:alarm:Error_test"
],
"detail": {
"alarmName": "Error_test",
"alarmDescription": "これはエラーテストです。",
"oldState": {
"stateValue": "OK",
"stateReason": "Threshold Crossed: 1 out of the last 1 datapoints [1.0 (08/05/24 12:32:00)] was greater than the threshold (0.0)."
},
"newState": {
"stateValue": "ALARM",
"stateReason": "Threshold Crossed: 1 out of the last 1 datapoints [1.0 (08/05/24 12:32:00)] was greater than the threshold (0.0).",
"stateReasonData": {
"version": "1.0",
"queryDate": "2024-05-08T12:34:56.005Z",
"startDate": "2024-05-08T12:32:00.000Z",
"statistic": "Sum",
"period": 60,
"recentDatapoints": [
1
]
}
}
}
}
実行結果は以下の通りです。
{
"issue": {
"id": 33,
"project": {
"id": 1,
"name": "test_project"
},
"tracker": {
"id": 6,
"name": "Alarm"
},
"status": {
"id": 1,
"name": "New",
"is_closed": false
},
"priority": {
"id": 3,
"name": "High"
},
"author": {
"id": 6,
"name": "aws aws"
},
"subject": "Alarm: Error_test is now ALARM",
"description": "これはエラーテストです。\r\n\r\nCloudWatch Alarm: Error_test is now ALARM",
"start_date": "2024-05-09",
"due_date": null,
"done_ratio": 0,
"is_private": false,
"estimated_hours": null,
"total_estimated_hours": null,
"created_on": "2024-05-09T05:40:02Z",
"updated_on": "2024-05-09T05:40:02Z",
"closed_on": null
}
}