[アップデート] Amazon Connectの「問い合わせフロー」がCloudFormationで作成できるようになりました

使いどころは限られるかもしれませんが、今後が期待できるアップデートです。
2021.12.27

みなさん、こんにちは!
福岡オフィスの青柳です。

Amazon Connectの「問い合わせフロー」および「問い合わせフローモジュール」がCloudFromationで作成できるようになりました。

Amazon Connect が問い合わせフローおよび問い合わせフローモジュールリソースへの AWS CloudFormation のサポートを提供開始

「問い合わせフロー」は、Amazon Connectをお使いになったことがある方であればご存じかと思います。

一方の「問い合わせフローモジュール」とは何ぞや?と思われる方もいらっしゃるかもしれません。
「問い合わせフローモジュール」とは、先月発表されたAmazon Connectの新機能です。

Amazon Connect が繰り返しロジックを簡単化する問い合わせフローモジュールのサポートを開始

問い合わせフローモジュールは、文字通りフローをモジュール化したものであり、特定の処理を行うフロー部分を再利用できるようにしたり、巨大で複雑なフローをモジュール分割して見通しを良くすることができるものです。

問い合わせフローモジュール (コンタクトフローモジュール) については、こちらの記事で紹介されていますので、参考にしてください。

使い方

それでは、CloudFormationを使って問い合わせフローを作成する手順を説明します。

CloudFormationテンプレートの書式

AWSドキュメントのリファレンスから抜粋します。

AWS::Connect::ContactFlow - AWS CloudFormation

Type: AWS::Connect::ContactFlow
Properties: 
  Name: String
  Description: String
  InstanceArn: String
  Type: String
  Content: String
  State: String
  Tags: 
    - Tag

各プロパティの説明は以下の通りです。

プロパティ 説明 備考
Name フローの名前 必須
Description フローの説明 省略可能
InstanceArn フローを作成する「Amazon Connectインスタンス」のARN 必須
Type 問い合わせフローの種類 (下表参照) 必須 (※1)
Content フロー定義JSON文字列 (「Amazon Connect Flow言語」で記述) 必須
State 「ACTIVE」または「ARCHIVED」 現時点では省略することを推奨 (※2)
Tags リソースタグの設定 (Key-Valueの組から成る配列) 省略可能

「Type」の設定値は以下の通りです。

フローの種類 「Type」の設定値
コンタクトフロー (インバウンド) CONTACT_FLOW
顧客キューフロー CUSTOMER_QUEUE
顧客保留フロー CUSTOMER_HOLD
顧客ウィスパーフロー CUSTOMER_WHISPER
発信ウィスパーフロー OUTBOUND_WHISPER
エージェント保留フロー AGENT_HOLD
エージェントウィスパーフロー AGENT_WHISPER
エージェントへの転送フロー AGENT_TRANSFER
キューへの転送フロー QUEUE_TRANSFER

※1: リファレンスでは「省略可能 (Required: No)」と記載されていますが、省略したところエラーになりました。実際は省略不可の項目であると思われます。

※2: この項目に関してはリファレンスに詳細な説明がありません。「State: ARCHIVED」と記述してCloudFormationスタックを実行したところ、実際に作成されたリソースは「State: ACTIVE」の状態になりました。どうやらフローの状態を「有効」「無効 (アーカイブ)」と切り替えるもののようなのですが、現時点ではGUI (Amazon Connectコンソール) で変更することもできませんので、この項目は無視した方が無難かと思います。

「Amazon Connect Flow言語」について

問い合わせフローは、通常、GUI (Amazon Connectコンソール) を使って作成しますが、これをJSON形式で記述するために使用するのが「Amazon Connect Flow言語」です。

Amazon Connect Flow 言語 - Amazon Connect

Amazon Connect Flow言語を利用する際のポイントは以下の通りです。

  • 仕様に従ってテキストエディタで記述することが可能
  • 現実的な手法は、GUIで作成したフローからAWS CLIで「フロー定義JSON」を抽出するという方法
  • いずれの方法であっても、CloudFormationで適用する際にはいくつか留意事項がある
  • Amazon Connectコンソールで「エクスポート」したファイルは使用できない (形式が異なる)

テキストエディタで記述する

Amazon Connect Flow言語を使って記述したフロー定義の一部を抜粋すると、以下のような感じです。

{
  "Version": "2019-10-30",
  "StartAction": "SetVoice",
  "Actions": [
    {
      "Identifier": "SetVoice",
      "Type": "UpdateContactTextToSpeechVoice",
      "Parameters": {
        "TextToSpeechVoice": "Mizuki"
      },
      "Transitions": {
        "NextAction": "PlayPrompt"
      }
    },
    {
      "Identifier": "PlayPrompt",
      ...

なんとなく想像が付くかと思いますが、上記は「音声の設定」ブロックで「Mizuki」を指定しているところです。

「Type」で指定している「UpdateContactTextToSpeechVoice」は「音声の設定」ブロックを意味します。
「Parameters」でブロックのパラメーターを指定しています。
ブロックからブロックへの遷移は「Transitions」という項目で記述します。

このように「Actions」配下にブロック単位でフロー構造を順に記述していきます。

また、GUI上でブロックが配置される時の座標データなどについては、「Actions」とは別に「メタデータ」として記述します。

このようにして、JSON形式のテキストとしてフロー定義を記述することは可能です。

ただし、「ブロック毎に一意なIDを付与して、遷移の記述でIDを適切に指定しなければならない」「座標データを記述しなければならない」などの点で、Amazon Connect Flow言語を使ってフロー定義を一から記述するのは大変なのではないかと思います。

GUIで作成したフローからAWS CLIで「フロー定義JSON」を抽出する

現実的な手法として、一度GUI (Amazon Connectコンソール) でフローを作成してから、AWS CLIを使って「フロー定義JSON」を抽出する方法をご紹介します。

フローの内容を表示するには、AWS CLIのコマンドaws connect describe-contact-flowを以下のように実行します。

$ aws connect describe-contact-flow \
    --instance-id xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx \
    --contact-flow-id yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy

「インスタンスID」と「フローID」はフロー画面に表示される「フローARN」から確認することができます。

フローARN:
arn:aws:connect:<リージョン>:<AWSアカウントID>:instance/<インスタンスID>/contact-flow/<フローID>

コマンドの実行結果は以下のようになります。

{
    "ContactFlow": {
        "Arn": "arn:aws:connect:ap-northeast-1:123456789012:instance/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/contact-flow/yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy",
        "Id": "yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy",
        "Name": "AmazonConnectExampleContactFlow",
        "Type": "CONTACT_FLOW",
        "State": "ACTIVE",
        "Description": "Example Contact Flow",
        "Content": <フロー定義JSON文字列>,
        "Tags": {
            "System": "example",
            "Environment": "dev"
        }
    }
}

ここで「Content」に出力される「フロー定義JSON文字列」はエスケープされているため、そのままでは利用し辛いと思います。

そこで、--queryjqコマンドを使ってJSON形式の文字列を取り出します。

$ aws connect describe-contact-flow \
  --instance-id xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx \
  --contact-flow-id yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy \
  --query "ContactFlow.Content" \
  | jq -r '. | fromjson'

コマンドの実行結果は以下のようになります。

{
  "Version": "2019-10-30",
  "StartAction": "zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz",
  "Metadata": {
    "entryPointPosition": {
      "x": 20,
      "y": 20
    },
    "snapToGrid": false,
    "ActionMetadata": {
      "zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz": {
        "position": {
          "x": 181,
          "y": 81
        },
        "overrideConsoleVoice": false,
        "defaultVoice": "Standard"
      },
      ...

これでフロー定義JSON文字列を取り出すことができました。

CloudFormationテンプレートの記述例

取得したフロー定義JSON文字列をプロパティContentに埋め込みます。

---
AWSTemplateFormatVersion: "2010-09-09"
Description: "Amazon Connect - Contact Flow"

Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: "General Information"
        Parameters:
          - SystemName
          - EnvironmentName
      - Label:
          default: "Amazon Connect Information"
        Parameters:
          - InstanceID

Parameters:
  SystemName:
    Type: String
  EnvironmentName:
    Type: String
    AllowedValues: [prd, stg, dev]
  InstanceID:
    Type: String
    Description: "Enter the Amazon Connect Instance ID (e.g. 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx')"

Resources:
  ContactFlow:
    Type: 'AWS::Connect::ContactFlow'
    Properties:
      Name: ExampleContactFlow
      Description: Example contact flow
      InstanceArn: !Sub "arn:aws:connect:${AWS::Region}:${AWS::AccountId}:instance/${InstanceID}"
      Type: CONTACT_FLOW
      Content: |
        {
          "Version": "2019-10-30",
          "StartAction": "54ed49aa-5e2a-426c-bca5-36ed7c8ef6a8",
          "Metadata": {
            "entryPointPosition": {
              "x": 20,
              "y": 19
            },
            "snapToGrid": false,
            "ActionMetadata": {
              "54ed49aa-5e2a-426c-bca5-36ed7c8ef6a8": {
                "position": {
                  "x": 181,
                  "y": 80
                },
                "overrideConsoleVoice": false,
                "defaultVoice": "Standard"
              },
              "a90c0a7f-7956-4b1f-a6e3-96a6a48ca192": {
                "position": {
                  "x": 421,
                  "y": 81
                },
                "useDynamic": false
              },
              "91471703-9511-4888-8055-24019fe390ab": {
                "position": {
                  "x": 660,
                  "y": 259
                }
              }
            }
          },
          "Actions": [
            {
              "Identifier": "54ed49aa-5e2a-426c-bca5-36ed7c8ef6a8",
              "Parameters": {
                "TextToSpeechVoice": "Mizuki"
              },
              "Transitions": {
                "NextAction": "a90c0a7f-7956-4b1f-a6e3-96a6a48ca192",
                "Errors": [],
                "Conditions": []
              },
              "Type": "UpdateContactTextToSpeechVoice"
            },
            {
              "Identifier": "a90c0a7f-7956-4b1f-a6e3-96a6a48ca192",
              "Parameters": {
                "Text": "お電話ありがとうございます"
              },
              "Transitions": {
                "NextAction": "91471703-9511-4888-8055-24019fe390ab",
                "Errors": [],
                "Conditions": []
              },
              "Type": "MessageParticipant"
            },
            {
              "Identifier": "91471703-9511-4888-8055-24019fe390ab",
              "Type": "DisconnectParticipant",
              "Parameters": {},
              "Transitions": {}
            }
          ]
        }
      Tags:
        - Key: System
          Value: !Ref SystemName
        - Key: Environment
          Value: !Ref EnvironmentName

このテンプレートを使ってCloudFormationスタックを作成・実行します。

このように問い合わせフローが作成されました。

補足・留意事項

「さあ、これで問い合わせフローの作成がCloudFormationでガンガン自動化できるぞ!」と言いたいところですが、いくつか留意すべき点があります。

ブロックに付与される固有ID

フロー定義JSONにおいて、各ブロック (Amazon Connect Flow言語では「Action」と呼ばれます) にはIdentifierという項目の指定が必須となっています。

  "Actions": [
    {
      "Identifier": "SetVoice",
      "Type": "UpdateContactTextToSpeechVoice",
      "Parameters": {
        "TextToSpeechVoice": "Mizuki"
      },
      ...

Identifierは以下の制約を満たしている必要があります。

  • フロー内で一意であること
  • 最大50文字の任意の文字が使用可能 (Unicodeやスペースも可能)

一からフロー定義JSONを記述する場合は、上記の例のように分かり易い (かつ重複しない) 文字列を指定すれば問題ありません。

GUI (Amazon Connectコンソール) でフローを作成した場合は、IdentifierにはランダムなUUID文字列が設定されます。

なお、別のフロー間であればActionのIdentifierが重複していても問題ありません。
したがって、GUIで作成したフローから抽出したフロー定義JSONを使って別のフローを作成する際にIdentifierの重複を気にする必要はありません。

Amazon Connectインスタンスに依存するリソース固有IDの取扱い

フローにブロックを配置する際、以下のようにインスタンス内で定義しているリソースをパラメーターとして指定する場合があると思います。

  • 「プロンプトの再生」ブロックで指定する「プロンプト」(保留音や音声メッセージのwevファイル)
  • 「作業キューの設定」ブロックで指定する「キュー」
  • 「フローへの転送」ブロックで指定する「問い合わせフロー」
  • 「オペレーション時間を確認する」ブロックで指定する「オペレーション時間」
  • ・・・など

これらのリソースは内部的に「ID」で管理されており、原則として、異なるインスタンス間ではIDも別のものになります。

例えば、「プロンプトの再生」ブロックで、デフォルトで用意されている「Beep.wav」を再生するように指定したとします。

フロー定義JSONの該当部分を抜き出すと以下のようになります。

    {
      "Identifier": "a90c0a7f-7956-4b1f-a6e3-96a6a48ca192",
      "Parameters": {
        "PromptId": "arn:aws:connect:ap-northeast-1:123456789012:instance/67abb772-96bc-49ff-8750-cea7ecd910e8/prompt/42b6bb3f-5aa4-4ac2-b95c-ae465316eddb"
      },
      "Transitions": {
        "NextAction": "636ae941-09de-48ed-8728-c566938acd69",
        "Errors": [],
        "Conditions": []
      },
      "Type": "MessageParticipant"
    }

別のインスタンスで同様に「プロンプトの再生」で「Beep.wav」を指定したものについて、フロー定義JSONの該当部分を抜き出します。

    {
      "Identifier": "a90c0a7f-7956-4b1f-a6e3-96a6a48ca192",
      "Parameters": {
        "PromptId": "arn:aws:connect:ap-northeast-1:123456789012:instance/e7df23b8-8f58-44e8-82bc-bef87b60e78a/prompt/fe2f8b10-40a6-42e9-ae07-fa1f56d769cb"
      },
      "Transitions": {
        "NextAction": "91471703-9511-4888-8055-24019fe390ab",
        "Errors": [],
        "Conditions": []
      },
      "Type": "MessageParticipant"
    }

PromptIdで指定されているIDが以下のように異なることが分かります。

  • arn:aws:connect:ap-northeast-1:123456789012:instance/67abb772-96bc-49ff-8750-cea7ecd910e8/prompt/42b6bb3f-5aa4-4ac2-b95c-ae465316eddb
  • arn:aws:connect:ap-northeast-1:123456789012:instance/e7df23b8-8f58-44e8-82bc-bef87b60e78a/prompt/fe2f8b10-40a6-42e9-ae07-fa1f56d769cb

インスタンスIDの部分が異なるのは当然ですが、/prompt/に続くプロンプトIDについても、全く異なったものになっています。

これによって何が起こるかと言うと、「あるインスタンスで作成したフローのフロー定義JSONを抽出して、そのまま別インスタンスのフロー作成で使おうとするとエラーになる」ということです。

これを回避するためには、投入先のインスタンスでプロンプトなどのリソースのIDを調べた上で、ID部分の置換が必要になりますす。
(IDはAmazon Connectコンソール上で確認できるものもあれば、場合によってはAWS CLI等を使って調べる必要があるものもあります)

なお、同一インスタンス内でフロー定義JSONを使い回すのであれば、リソースのIDは変わりませんので問題は起こりません。

しかし、例えば「開発環境のインスタンスで作成したフローを使って、本番環境のインスタンスにフローを構築したい」という場合に、インスタンスに依存したリソースIDを置換する作業が必要となります。

AWSドキュメントにも下記ページで言及されていますが、現時点ではID置換を行う移行ツールの類は提供されておらず、プログラム/スクリプト等の開発によりID置換/マッピングを行うことになるかと思います。
問い合わせフローを別のインスタンスに移行する - Amazon Connect

フローの「エクスポート/インポート」機能の利用も検討

GUI (Amazon Connectコンソール) の問い合わせフロー編集画面で、画面右上の「保存」メニューから「フローのエクスポート」「フローのインポート」が選択できます。

(ここからエクスポートしたファイルは、テキストエディタで開くとJSON形式となっており、一見「Amazon Connect Flow言語か?」と思ってしまいますが、書式が全く異なるため互換性はありません)

エクスポート/インポート機能も、Amazon Connect Flow言語と同様に「インスタンスに依存するリソースIDの問題」は存在します。

ただし、エクスポート/インポート機能の場合、別インスタンスからエクスポートしたフローのファイルであっても、インポートすることが可能です。(インポート時にエラーとはなりません)

インポートした後、インスタンスに依存するリソースIDを含んでいるブロックは、下図のように警告マークが付きます。

その上で、デフォルトで用意されているリソースであれば、自動的にIDを置き換えてくれます。

独自に作成したリソースの場合は自動で差し替えることができませんので、未設定状態となります。(ブロックのプロパティを開いて、改めて設定する必要があります)

このように、「エクスポート/インポート」機能では、手作業が発生するもののインポートした後に修正することが可能であるため、小規模な移行の場合であれば、Amazon Connect Flow言語でID置換を行うよりも現実的だと思います。

おわりに

Amazon Connectの環境構築において、恐らく最も手間がかかり、作業の大部分を占めると思われる「問い合わせフローの作成」ですが、CloudFormationなどのIaC (Infrastructure as Code) によって自動化できれば構築コストが大幅に削減可能と思われます。

そういう意味で待望されていた「CloudFormationによる問い合わせフロー作成のサポート」ですが、現時点では残念ながら限定的な用途での利用になってしまうと思います。

それでも、大規模な開発であれば、移行プログラム/スクリプトの開発を伴う前提ではあるものの、構築の自動化が行える可能性が出てきたというのは画期的なことではないでしょうか。

また、Amazon Connect Flow言語やCloudFormationサポートの改善・改良によって、将来的にはもっと柔軟にIaCによる構築自動化が行えるようになるかもしれません。
(あるいは、CloudFormationではなくCDKやTerraformなどによる自動構築という選択肢も考えられます)

今後に期待しつつ、Amazon Connectの環境構築自動化についてウォッチしていきたいと思います。