この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
1 はじめに
CX事業本部の平内(SIN)です。
Amazon CloudWatch(以下CloudWatch)のアラートは、その要件に合わせて、メールやSlackなど、様々な形で通知可能です。しかし、受け取る人の環境(状況)に合わせて、「確実に気づくことができる仕組み」となると、ちょっと検討が必要です。
あくまで、状況によりますが、電話の着信は、様々な場面で確実に気付くため、比較的有効な手段の一つと言えるかもしれません。
今回は、Amazon Connect(以下、Connect)を使用して、Cloudwatchのアラート発生時に電話で通知する仕組みを作ってみました。
なお、着信に気付かなかった場合や、留守番電話への着信となってしまった場合、「履歴だけが残っている」という状況になってしまいます。 これで要件が満たせるのであれば、それで良いのですが、今回は、このような場合に再試行を設定できる仕組みについても検討してみました。
2 フロー
全体のフローは、図のようなものです。
電話がコールされ、ユーザーが電話に出た時、キー入力(番号や#)を求め、入力が確認できた場合に「アラート確認済み」を記録します。
電話発信後は、「タイムアウト」で設定した時間まで待機し、「アラート確認済み」の記録が確認できない場合、「電話発信」からの工程を繰り返します。
「着信記録」が残れば十分であるというような場合は、「再試行回数」を「0」に設定すれば良いでしょう。
3 構成図
構成は、以下のとおりです。
ClowddWatchのAlarmは、Amazon Event Bridge(以下、Event Bridge) で AWS Step Functions(以下、Step Functions) に繋ぎました。
Step Functionsでは、ConnectのAPI(StartOutboundVoiceContact)で発信を行います。ユーザーが電話に出ると、Connectのコンタクトフローでキー入力を求め、入力を確認できた場合に、Amazon DynamoDB(以下、DynamoDB)に記録します。
再試行は、Step Functionsで行っています。タイムアウトで設定された時間後にDynamoDBに記録がない場合は、指定された再試行回数まで発信を繰り返します。
4 Event Bridge
Event Bridgeでは、CloudWatch の CloudWatch Alarm State Change をStep Functionsへ繋いでいます。
イベントソース
ターゲット
この時、Event Bridgeからの入力は、以下のようになっています。
{
・・・略・・・
"id": "c4c1c1c9-6542-e61b-6ef0-8c4d36933a92",
"detail-type": "CloudWatch Alarm State Change",
"source": "aws.cloudwatch",
"account": "123456789012",
・・・略・・・
"detail": {
・・・略・・・
"state": {
・・・略・・・
"reason": "XXXX",
"value": "ALARM"
}
}
}
ここで、入力トランスフォーマーを使用して、必要なパラメータのみを切り出し、併せてStep Functionsで使用するパラメータ(タイムアウト値、再試行回数、電話番号)を設定しています。
入力パス
{
"detail-state-value": "$.detail.state.value",
"id": "$.id"
}
入力テンプレート
{
"id": <id>,
"state": <detail-state-value>,
"waitSecinds":60,
"maxRetries":2,
"phoneNumber":"+8190XXXXXXXX"
}
5 Step Functions
Step Functionsは、以下のような内容です。
ConfiramationParamsでは、入力パラメータをチェックしています。stateは、アラーム状態か正常かのステータスなので、ALARMの時だけ、後続の処理が動作するようになっています。
StartOutboundVoiceContactで発信し、WaitOutboundCallで、設定したタイムアウトまで待機したのち、GetIdFromDynamoDBで、「アラート確認済み」の記録がDynamoDBに存在するかどうかを確認しています。
CheckContinue及び、ChoiceToopOrStopは、再試行回数だけループする処理です。
6 Connect
Connectに設置されたコンタクトフローは、以下の通りです。
このフローは、ユーザが、電話を取った時に発動します。
内容としては、入力を促すアナウンスの後、キー入力を待ち、キー入力があった場合は、AWS Lambda(以下、Lambda)でDynamoDBにidを記録します。
なお、このコンタクトフローは、留守番電話に着信した場合も動作しますが、留守番電話では、キー入力を返す事は無いので、Lambdaは、起動せず、DynamoDBへの記録も行われません。
また、顧客の入力を取得するというブロックでは、入力したキーの種類で、事後の処理を分岐できますが、指定以外の入力があった場合、デフォルトに分岐します。ここでは、#の入力を求めていますが、#以外のキー入力があっても、正常系(キー入力があったって事は、誰かが操作しているはず)として処理しています。
7 DynamoDB
DynamoDBのテーブルは、id(string) がパーティションキーとなっています。また、ttlが設定されており、24時間で自動的に削除されるようになっています。
8 Lambda
(1) AlarmCall_Iterator
AlarmCall_Iteratorは、Step Functionsのループ処理で使われています。 counterをインクリメントし、ループ継続か、終わりかをisContinueで返しています。
def lambda_handler(event, _context):
counter = int(event["iterator"]["counter"])
max_retries = int(event["maxRetries"])
counter += 1
return {
"counter": counter,
"isContinue": (counter < max_retries)
}
(2) AlarmCall_SaveId
AlarmCall_SaveIdは、Connectの中で、DynamoDBへのid記録で使われています。
import boto3
from datetime import datetime
from datetime import timedelta
def lambda_handler(event, _context):
try:
id = event["Details"]["ContactData"]["Attributes"]["id"]
table_name = 'AlarmCall_Table'
ttl = datetime.now() + timedelta(days=1) # 1日後に削除
item = {
"id": id,
"ttl":int(ttl.timestamp())
}
dynamo = boto3.resource('dynamodb')
dynamo_table = dynamo.Table(table_name)
dynamo_table.put_item(Item=item)
print("Success: set id ({})".format(id))
except Exception as e:
print("Error: {}".format(e))
return {}
9 設置
以下は、設置の手順です。CDKに対応していない、一部のConnectの設定以外は、CDKでデプロイできるようになっています。 興味が湧きましたら、お試しいただければ嬉しいです。
Connectで発信するためには、予め電話番号の取得が必要ですが、こちらについては、下記のドキュメントをご参照ください。
参考:Amazon Connect の電話番号をアジアパシフィック (東京) リージョンで取得する
(1) Download
Githubから、コード等をダウンロードします。
$ git clone https://github.com/furuya02/AlarmCall.git
$ cd AlarmCall
(2) 共通スタック
AlarmCallCommonStackをdeployし、共通のリソースを作成します。
$ yarn install
$ tsc
$ cdk deploy AlarmCallCommonStack
作成されるリソースは、以下の通りです。
- Lambda(AlarmCall_SaveId) Connectのフローの中で使用され、キー入力があった場合にidを保存する
- Lambda(AlarmCall_Iterator) Step Functionsの中で使用され、カウンターをインクリメントして、ループを継続するかどうかを返す
- DynamoDB(AlarmCall_Table) Connectのフローの中で使用され、キー入力があった場合にidを保存する
- Step Functions(AlarmCall_StateMachine)
(3) Connect
Connectのインスタンスで、共通スタックで作成されたLambdaを使用可能にします
コンタクトフローを作成します
※ 下記のフォルダにサンプルがあるので、インポートして利用して下さい。
AlarmCall/CallFlow/AlarmCallFlow
Lambda関数を紐付けます
(4) 共通スタック(更新)
Connectの各種IDをcdk.jsonに転記します。
※ 各IDは、URL等から取得可能です
[Amazon Connect] StartOutboundVoiceContactや、GetCurrentMetricDataで必要となる各種IDの取得方法(小ネタ)
例)
cdk.json
{
・・・略・・・
"context": {
・・・略・・・
"contactFlowId": "xxxxxxxx-cec9-431c-a71b-xxxxxxxxxxxx",
"instanceId": "xxxxxxxx-6260-42bf-b4b1-xxxxxxxxxxxx",
"queueId": "xxxxxxxx-f501-4995-9eef-xxxxxxxxxxxx"
}
}
Connect と連携させるため、共通スタックを再度デプロイ(更新)します
$ tsc
$ cdk deploy AlarmCallCommonStack
(5) 個別スタック
個別スタックは、指定したアラームをEvent BridgeでStep Functionsに繋ぎます。 個別スタックは、アラーム毎にデプロイします。
- name: 個別名(スタックを区別するために命名します「個人名等」)
- phoneNumber: コールする電話番号
- maxRetries: 着信が確認できなかった時に繰り返す回数
- waitSecinds: 繰り返すまでに待機する時間(秒)
- alarmArn: トリガーするアラームのarn
AlarmCallStack-{name}のnameは、-c name=で指定したものと一致している必要があります。
例)testAlarmが、ARAM状態になると、+8190xxxxxxxx宛にタイムアウト60秒、再試行2回で発信します。
% cdk deploy AlarmCallStack-JohnSmith -c name=JohnSmith -c phoneNumber=+8190xxxxxxxx -c waitSecinds=60 -c maxRetries=2 -c alarmArn=arn:aws:cloudwatch:ap-northeast-1:xxxxxxxxxxxx:alarm:testAlarm
例)sampleAlarmが、ARAM状態になると、+8190xxxxxxxx宛にタイムアウト120秒、再試行3回で発信します。
% cdk deploy AlarmCallStack-YamadaTaro -c name=YamadaTaro -c phoneNumber=+8190xxxxxxxx -c waitSecinds=120 -c maxRetries=3 -c alarmArn=arn:aws:cloudwatch:ap-northeast-1:xxxxxxxxxxxx:alarm:sampleAlarm
10 最後に
今回は、Connectを使用して、Cloudwatchのアラート発生を電話を通知する仕組みを作ってみました。
電話の呼び出しは、好き嫌いがあると思いますが・・・もし、必要な場面があれば、参考にしていただければ嬉しいです。
コード https://github.com/furuya02/AlarmCall.git
11 参考リンク
Amazon Conect コールバック キャンペーン
Amazon Connect StartOutboundVoiceContact API を使用して、顧客に通話の発信を行うにはどうすればよいですか?
[Amazon Connect] StartOutboundVoiceContactや、GetCurrentMetricDataで必要となる各種IDの取得方法(小ネタ)