Amazon ConnectでCloudWatchのアラームを電話通知してみた

はじめに

CX事業本部の高橋雄大です。

AWSリソースの監視にはCloudWatchを利用することが多いですが、通知の手段は多岐にわたります。今回はAmazon Connectを活用した電話通知の方法を紹介します。

Amazon Connectとは?

Amazon Connectはクラウド型のコンタクトセンター(コールセンター)です。単純な電話の受発信だけでなく、問い合わせフロー設計による対応の自動化や、他のAWSサービスとの連携が簡単にできます。

料金は電話番号の利用料と分単位の従量課金のみで初期費用は不要です。

Amazon Connect(クラウドベースのコンタクトセンター)| AWS

実装してみた

Amazon ConnectはLambda関数から実行することが可能です。CloudWatchのアラームをSNSからLambdaで受け取り、Lambda関数からAWS SDKを利用してAmazon Connectの問い合わせフローを実行します。

CloudWatchのアラームをAmazon Connectで電話通知するAWS構成図

コンタクトセンターの準備

Amazon ConnectはCloudFormationに対応していないため、AWSのマネージメントコンソールから環境を構築する必要があります。以下の記事を参考に、インスタンスの作成から電話番号の取得まで行います。

【東京リージョン】Amazon Connectでコンタクトセンター構築はじめの一歩

問い合わせフローの作成

Amazon Connectの問い合わせフローを作成します。以下の図が問い合わせフローの完成形になります。

Amazon Connectの問い合わせフロー

CloudWatchアラームで検知したエラー内容を通知したいので、「音声の設定」と「プロンプトの再生」を設定します。プロンプトの再生では、どのような形式でメッセージを受け取るのか選択します。

Lambdaが送信したテキストを再生する場合は、以下のようにプロンプトの再生を設定をしてください。

  • プロンプト: テキスト読み上げ機能 (アドホック)&動的に入力する
  • タイプ: ユーザー定義
  • 属性: message
  • 解釈する: テキスト

運用監視の実装

環境

以下の環境で実装をしています。

  • Lambdaランタイム: Python3.7
  • Region: ap-northeast-1
  • AWS CLI: 1.16.170

ディレクトリ構成

srcディレクトリにLambda関数のソースコードを配置しています。

├─ src
│   └─ lambda
│       ├─ call_error_message.py
│       └─ run_error.py
└─ template.yaml

Lambda関数

SNSからメッセージを受け取り、Amazon Connectの問い合わせフローを実行するLambda関数です。

import boto3
import os
import json

DESTINATION_PHONE_NUMBER = os.getenv('DestinationPhoneNumber')
SOURCE_PHONE_NUMBER = os.getenv('SourcePhoneNumber')
INSTANCE_ID = os.getenv('InstanceId')
CONTACT_FLOW_ID = os.getenv('ContactFlowId')

connect = boto3.client('connect')

def lambda_handler(event: dict, context: dict) -> None:
    message = get_message(event)
    call_message(message)


def get_message(payload: dict) -> str:
    messages = json.loads(payload['Records'][0]['Sns']['Message'])
    aws_account_id = messages['AWSAccountId']

    message = f'Lambda関数でエラーが発生しました。'
    message += f'対象のAWSアカウントIDは {aws_account_id} です。'

    return message


def call_message(message: str) -> None:
    connect.start_outbound_voice_contact(
        DestinationPhoneNumber=DESTINATION_PHONE_NUMBER,
        ContactFlowId=CONTACT_FLOW_ID,
        InstanceId=INSTANCE_ID,
        SourcePhoneNumber=SOURCE_PHONE_NUMBER,
        Attributes={
            'message': message
        }
    )

意図的に例外を返却して、CloudWatchでアラームを発生させるLambda関数です。

def lambda_handler(event: dict, context: dict) -> None:
    raise Exception('Error!')

テンプレート

Lambda関数とCloudWatchとSNSをCloudFormationでデプロイするためのテンプレートです。CloudWatchのアラームは、RunErrorFunctionで1分間に1回以上のエラーが発生した場合に検知するよう設定しています。

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Parameters:
  ProjectName:
    Type: String
  DestinationPhoneNumber:
    Type: String
  SourcePhoneNumber:
    Type: String
  InstanceId:
    Type: String
  ContactFlowId:
    Type: String
Globals:
  Function:
    Runtime: python3.7
    MemorySize: 128
    Timeout: 10
    Environment:
      Variables:
        TZ: Asia/Tokyo
Resources:
  LambdaExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Statement:
        - Effect: Allow
          Principal:
            Service: lambda.amazonaws.com
          Action:
          - sts:AssumeRole
      Path: /
      Policies:
        - PolicyName:
            Fn::Sub: ${ProjectName}-lambda-execution-role
          PolicyDocument:
            Statement:
              - Effect: Allow
                Action:
                  - logs:CreateLogGroup
                  - logs:CreateLogStream
                  - logs:PutLogEvents
                Resource: '*'
              - Effect: Allow
                Action:
                  - sns:Publish
                Resource: '*'
              - Effect: Allow
                Action:
                  - connect:StartOutboundVoiceContact
                  - connect:StopContact
                Resource: '*'
  CallErrorMessageFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName:
        Fn::Sub: ${ProjectName}-call-error-message
      CodeUri: src/lambda
      Handler: call_error_message.lambda_handler
      Role:
        Fn::GetAtt:
          - LambdaExecutionRole
          - Arn
      Environment:
        Variables:
          DestinationPhoneNumber:
            Ref: DestinationPhoneNumber
          SourcePhoneNumber:
            Ref: SourcePhoneNumber
          InstanceId:
            Ref: InstanceId
          ContactFlowId:
            Ref: ContactFlowId
      Events:
        CloudWatchAlarmTopic:
          Type: SNS
          Properties:
            Topic:
              Ref: CloudWatchAlarmTopic
  RunErrorFunction:
    Type: AWS::Serverless::Function
    Properties:
      FunctionName:
        Fn::Sub: ${ProjectName}-run-error
      CodeUri: src/lambda
      Handler: run_error.lambda_handler
      Role:
        Fn::GetAtt:
          - LambdaExecutionRole
          - Arn
  ErrorsAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName:
        Fn::Sub: ${ProjectName}-errors
      Namespace: AWS/Lambda
      Dimensions:
        - Name: FunctionName
          Value: !Sub ${ProjectName}-run-error
      MetricName: Errors
      ComparisonOperator: GreaterThanOrEqualToThreshold
      Period: 60
      EvaluationPeriods: 1
      Statistic: Sum
      Threshold: 1
      AlarmActions:
        - Ref: CloudWatchAlarmTopic
      TreatMissingData: notBreaching
  CloudWatchAlarmTopic:
    Type: AWS::SNS::Topic
    Properties:
      DisplayName:
        Fn::Sub: ${ProjectName}-cloud-watch-alarm
      TopicName:
        Fn::Sub: ${ProjectName}-cloud-watch-alarm

環境変数の設定

デプロイで利用する環境変数を設定します。

export PROJECT_NAME='cloudwatch-amazon-connect' #プロジェクト名
export AWS_ACCOUNT_ID='' #AWSアカウントID
export REGION='ap-northeast-1' #リージョン
export DESTINATION_PHONE_NUMBER='+819000000000' #通知先の電話番号
export SOURCE_PHONE_NUMBER='+815000000000' #取得した電話番号
export INSTANCE_ID='' #インスタンスID
export CONTACT_FLOW_ID='' #コンタクトフローID

インスタンスIDとコンタクトフローIDは、Amazon Connectの問い合わせフロー作成画面から確認できます。「追加フロー情報」もしくは「URL」のArnから取得してください。

arn:aws:connect:{AWS::Region}:{AWS::AccountId}:instance/{インスタンスID}/contact-flow/{コンタクトフローID}

S3バケットの作成

パッケージ化したソースコードをアップロードするためのS3バケットを作成します。

aws s3api create-bucket \
    --bucket ${PROJECT_NAME}-artifact-${AWS_ACCOUNT_ID} \
    --region ${REGION} \
    --create-bucket-configuration LocationConstraint=${REGION}

パッケージ

ソースコードをパッケージ化します。

aws cloudformation package \
    --template-file template.yaml \
    --output-template-file template-output.yaml \
    --s3-bucket ${PROJECT_NAME}-artifact-${AWS_ACCOUNT_ID}

デプロイ

CloudFormationのテンプレートに記載した各リソースをデプロイします。

aws cloudformation deploy \
    --template-file template-output.yaml \
    --stack-name ${PROJECT_NAME}-stack \
    --parameter-overrides \
        ProjectName=${PROJECT_NAME} \
        DestinationPhoneNumber=${DESTINATION_PHONE_NUMBER} \
        SourcePhoneNumber=${SOURCE_PHONE_NUMBER} \
        InstanceId=${INSTANCE_ID} \
        ContactFlowId=${CONTACT_FLOW_ID} \
    --no-fail-on-empty-changeset \
    --capabilities CAPABILITY_NAMED_IAM

動作確認

RunErrorFunctionを実行して意図的にエラーを発生させます。

aws lambda invoke log \
    --invocation-type Event \
    --function-name ${PROJECT_NAME}-run-error \
    --region ${REGION} \
    --payload '{}'

電話通知のみ実行する場合はCallErrorMessageFunctionを実行します。

aws lambda invoke log \
    --invocation-type Event \
    --function-name ${PROJECT_NAME}-call-error-message \
    --region ${REGION} \
    --payload '{"Records": [{"Sns": {"Message": "{\"AWSAccountId\": \"12345\"}"}}]}'

通知先の電話番号に音声通知が届けば成功です。

まとめ

CloudWatchアラームの内容をLambdaから簡単に電話で通知することができました。Amazon Connectは他にも様々な機能を備えています。例えば、電話の受信者にプッシュ番号を入力してもらい、Lambda関数で番号に応じた処理を実行することも可能です。

次回はAmazon ConnectからLambda関数を実行させる方法について記事を書こうと思います。

参考