API Gateway×Lambda×DynamoDB×Amazon Connectで障害発生時の承認フローを作成

# 特定のエラー発生時にメール通知と電話連絡を行う承認フローの構築
特定のエラー発生時にメールで通知し、対応担当者が承認を行うことで「対応完了」とし、一定時間内に承認がない場合はAmazon Connectで電話連絡を行うワークフローを、API Gateway、Lambda、DynamoDB、Amazon Connectを使用して構築します。

# システム構成

![con00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523652/f862ae1a-4a9c-4cd1-9a7e-1f6f4c01ce06.png)


* エラーや特定条件の発生時にLambda関数を実行し、SNS経由でメール通知を送信するとともに、DynamoDBに承認ステータス「pending」のレコードを登録します。

* 対応者はメール内のリンクをクリックして、DynamoDB内のステータスを「approved」に変更できます。

* Amazon EventBridge を使って定期的に DynamoDB を監視し、承認が行われていない「pending」ステータスが存在する場合、Amazon Connect を通じて電話連絡を行います。


# 前提条件

以下の前提で構築を進めます。

* Amazon Connect でコンタクトフローが作成済みであること
* SNSトピックが作成済みであること
* 以下の IAM ポリシーが付与された Lambda 実行ロールが用意されていること
AmazonConnect_FullAccess
AmazonDynamoDBFullAccess
AmazonSNSFullAccess

# 設定

## DynamoDBの設定
テーブル名`ApprovalRequests`、パーティションキー`RequestId`でDynamoDBのテーブルを作成します。

![con01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523652/6278da5e-1d7d-4641-a899-62543e6313ce.png)


## メール通知用Lambda関数の作成
このLambda関数は、メール通知の送信と、DynamoDBへの「pending」状態の書き込みを行います。承認リンクは後でAPI Gateway作成後に修正します。

```python
import boto3
import uuid
from datetime import datetime, timezone

# 初期化
sns = boto3.client('sns')
dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('ApprovalRequests')

SNS_TOPIC_ARN = 'SNSトピックのARNを入力'

def lambda_handler(event, context):
    request_id = str(uuid.uuid4())

    # 承認リンク（後でAPI Gateyway作成後変更）
    approval_link = f"https://***.***.amazonaws.com/prod/approve?id={request_id}"

    subject = "【要対応】承認依頼"
    message = f"""以下の内容について承認をお願いします。

承認リンク: {approval_link}
"""
    sns.publish(
        TopicArn=SNS_TOPIC_ARN,
        Message=message,
        Subject=subject
    )

    table.put_item(
        Item={
            'RequestId': request_id,
            'Status': 'pending',
            'CreatedAt': datetime.now(timezone.utc).isoformat()
        }
    )

    return {
        "statusCode": 200,
        "body": f"SNS経由で承認依頼を送信しました。Request ID: {request_id}"
    }
```

## 承認処理用Lambda関数の作成
DynamoDBの「Status」を「approved」に更新する処理です。API Gatewayから呼び出されます。

```python
import boto3
import os
import json

dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table("ApprovalRequests")

def lambda_handler(event, context):
    request_id = event.get('queryStringParameters', {}).get('id')

    table.update_item(
        Key={'RequestId': request_id},
        UpdateExpression="SET #s = :approved",
        ExpressionAttributeNames={'#s': 'Status'},
        ExpressionAttributeValues={':approved': 'approved'}
    )

    return {
        "statusCode": 200,
        "headers": {"Content-Type": "text/html"},
        "body": "<h3>It's been approved. Thank you.</h3>"
    }
```

## API Gatewayの作成

REST APIを作成し、パス /approve に GET メソッドを追加します。Lambda プロキシ統合を有効化し、上記の承認処理用 Lambda を統合します。

![con02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523652/16c5660d-2e18-42fb-9972-6bdb320ee49f.png)


![con03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523652/681dcbd8-e903-4c56-a4a5-3a672bbca44e.png)

API Gateway作成後、メール通知用Lambda関数内の承認リンクURLを更新します。

## 状態監視用Lambda関数の作成
承認状態が「pending」のレコードを検出し、Amazon Connectで電話をかける処理を行います。

```python
import boto3

def lambda_handler(event, context):
    dynamodb = boto3.resource('dynamodb')
    table = dynamodb.Table('ApprovalRequests')

    result = table.scan(
        FilterExpression="#s = :pending",
        ExpressionAttributeNames={"#s": "Status"},
        ExpressionAttributeValues={":pending": "pending"}
    )

    if result.get("Items"):
        item = result["Items"][0]
        
        client = boto3.client('connect')
        response = client.start_outbound_voice_contact(
            ContactFlowId='フローIDを入力',
            DestinationPhoneNumber='発信元電話番号を入力',
            InstanceId='インスタンスIDを入力',
            SourcePhoneNumber='送信元電話番号を入力'
        )

        return {"statusCode": 200}

    else:
        return {"statusCode": 204
        
```

このLambdaをAmazon EventBridgeにスケジュール登録し、定期的に呼び出すことで監視を行います。

# 動作確認

## メール通知の確認

アラート発生時に以下のようなメールが送信されます。

![con04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523652/1ed99668-12e5-44b0-964d-9ab7553af55f.png)


## DynamoDBの状態確認
「pending」ステータスのレコードが作成されています。

![con05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523652/4c655639-2b36-4e32-86c4-c840ab38085b.png)

## 承認リンクによる状態変更
メール内の承認リンクをクリックすると以下のような画面が表示され、DynamoDBのステータスが「approved」に更新されます。

![con06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2523652/4ebb5409-dac5-4779-8760-4d0a41585370.png)

## Amazon Connectによる電話発信
承認リンクがクリックされないまま EventBridge のスケジュール時間を迎えると、Amazon Connect 経由で電話が発信されます。