この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
こんにちは、森田です。
Amazon Connect では、プレビュー版APIがあるものの、CloudFormationでは提供されていません。
そこで、CloudFormation のカスタムリソースを用いてAmazon Connect インスタンス・ユーザ作成をやってみました。
CloudFormationテンプレート
では、早速ですが、以下が CloudFormationテンプレートとなります。
launch.yml
AWSTemplateFormatVersion: "2010-09-09"
Parameters:
InstanceAlias:
Type: String
Description: New Instance Alias
UserName:
Type: String
Default: admin
Description: New User Name
Password:
Type: String
Default: admin
Description: New User Password
NoEcho: true
FirstName:
Type: String
Description: New User first name
LastName:
Type: String
Description: New User last name
Mail:
Type: String
Description: New User mail
Resources:
UserPasswordParameter:
Type: AWS::SSM::Parameter
Properties:
Name: !Sub 'ConnectMasterPassword-${AWS::StackName}'
Type: String
Value: !Ref Password
Description: "MasterUserPassword for Connect"
ConnectInstanceHandler:
Type: Custom::ConnectInstanceHandler
Properties:
ServiceToken: !GetAtt "LambdaFunction.Arn"
LambdaFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName : !Join ['-', [!Sub '${AWS::StackName}-Lambda', !Select [0, !Split ['-', !Select [2, !Split ['/', !Ref 'AWS::StackId' ]]]]]]
Role: !GetAtt "LambdaExecutionRole.Arn"
Runtime: "python3.8"
Handler: index.lambda_handler
Timeout: "300"
Environment:
Variables:
InstanceAlias : !Ref InstanceAlias
FirstName : !Ref FirstName
LastName : !Ref LastName
Mail : !Ref Mail
UserName : !Ref UserName
Password : !Ref UserPasswordParameter
Code:
ZipFile: |
import cfnresponse
import sys
import os
import boto3
import time
def lambda_handler(event, context):
if 'RequestType' not in event:
return "Overwrite"
if event['RequestType'] == 'Create':
create_connect(context)
cfnresponse.send(event, context, cfnresponse.SUCCESS,
{'Response': 'Success'})
if event['RequestType'] == 'Delete':
print('Delete')
delete_connect()
cfnresponse.send(event, context, cfnresponse.SUCCESS,
{'Response': 'Success'})
if event['RequestType'] == 'Update':
print('Update')
cfnresponse.send(event, context, cfnresponse.SUCCESS,
{'Response': 'Success'})
def get_ssm(key):
ssm = boto3.client('ssm', "ap-northeast-1")
response = ssm.get_parameter(
Name=key,
WithDecryption=True,
)
try:
value = response['Parameter']['Value']
except:
value = None
return value
def create_connect(context):
client = boto3.client('connect')
instance_alias = os.getenv('InstanceAlias')
user_name = os.getenv('UserName')
passwd = get_ssm(os.getenv('Password'))
f_n = os.getenv('FirstName')
l_n = os.getenv('LastName')
mail= os.getenv('Mail')
res = client.create_instance(
IdentityManagementType='CONNECT_MANAGED',
InstanceAlias=instance_alias,
InboundCallsEnabled=True,
OutboundCallsEnabled=True
)
assert res['ResponseMetadata']['HTTPStatusCode'] == 200, 'Connect インスタンスが正常に作成できませんでした'
InstanceId = res["Id"]
# Instance ID Save
update_environ(
context.invoked_function_arn,
InstanceId
)
# インスタンスがACTIVEになるまで待つ
status = ''
while status != 'ACTIVE':
time.sleep(5)
res= client.describe_instance(
InstanceId=InstanceId
)
status = res['Instance'] ['InstanceStatus']
# RoutingProfileIdを取得する
res = client.list_routing_profiles(
InstanceId=InstanceId
)
RoutingProfileId = res['RoutingProfileSummaryList'][0]['Id']
# SecurityProfileIdsを取得する
res = client.list_security_profiles(
InstanceId=InstanceId
)
SecurityProfileAdminId = list(filter(lambda x: x['Name']=='Admin', res['SecurityProfileSummaryList']))[0]['Id']
res = client.create_user(
Username=user_name,
Password=passwd,
IdentityInfo={
'FirstName': f_n,
'LastName': l_n,
'Email': mail
},
PhoneConfig={
'PhoneType': 'SOFT_PHONE'
},
SecurityProfileIds=[
SecurityProfileAdminId,
],
RoutingProfileId=RoutingProfileId,
InstanceId=InstanceId
)
assert res['ResponseMetadata']['HTTPStatusCode'] == 200, 'Connect ユーザが正常に作成できませんでした'
print('Amazon Connect Setup Done!!')
print('https://{}.my.connect.aws'.format(instance_alias))
def delete_connect():
client = boto3.client('connect')
InstanceId = os.getenv('InstanceId')
response = client.delete_instance(
InstanceId=InstanceId
)
def update_environ(function_arn, InstanceId):
client = boto3.client('lambda')
response = client.update_function_configuration(
FunctionName=function_arn,
Environment={
'Variables': {
'InstanceId': InstanceId
}
}
)
LambdaExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
Path: /
Policies:
- PolicyName: sample-lambda-policy
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource: arn:aws:logs:*:*:*
- Effect: Allow
Action:
- ssm:GetParameter
Resource: "*"
- Effect: Allow
Action:
- lambda:UpdateFunctionConfiguration
Resource: "*"
ManagedPolicyArns:
- arn:aws:iam::aws:policy/AmazonConnect_FullAccess
作成時と削除時のアクション
作成時
スタック作成時には、create_connect関数を実行します。こちらのインスタンス・ユーザ作成のコードについては、以下をご参照ください。
上記の記事との変更点は、パスワードをそのまま環境変数で渡さず、Parameter Storeで渡すように変更しています。
削除時
削除時は、delete_connect関数を実行します。Lambdaの環境変数より Connect インスタンスID を読み込み、Boto3で Connect インスタンス の削除を行っております。
試してみる
スタックの作成
上記のテンプレートを実際に流してみます。
パラメータでは、ユーザ名、パスワード等が求められますので、入力します。
実行後、しばらく待つと以下のようにコネクトインスタンスが確認できます。
スタックの削除
スタックの削除も行ってみます。スタックの削除後、コネクトインスタンスも削除されていることが確認できます。
最後に
以前作成したPythonスクリプトをCloudFormationとしてカプセル化することで、より扱いやすくなりました。
ただ、他のCloudFormationとして動作させるための構文は追加しているので、コードが長くなっています。
このまま CloudFormationのテンプレート を再利用するには、大変ですので、近いうちにモジュールとしてCloudFormationレジストリ登録もやってみたいと思います。