# MIT No Attribution # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # Permission is hereby granted, free of charge, to any person obtaining a copy of this # software and associated documentation files (the "Software"), to deal in the Software # without restriction, including without limitation the rights to use, copy, modify, # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. AWSTemplateFormatVersion: 2010-09-09 Description: > "CloudFormation for re:Invent 2022 - BIZ310 - Real-time churn prediction in AI powered contact centres - Amazon Connect Queues, Routing Profile, QuickConnect, and Retention User" Parameters: AmazonConnectInstanceARN: Default: "**** Enter the Full Amazon Connect Instance ARN ****" Description: The Amazon Connect Instance ARN Type: String NewEmailForNotifications: Default: "**** Enter an email address for Retention Specialist ****" Description: Email for New Contact Lens Notification Type: String # Basic and Retention queue ARNs Outputs: RetentionQueueARN: Description: The ARN of the Retention Queue Value: !GetAtt LambdainvokeQueues.Retention Export: Name: !Sub '${AWS::StackName}-RetentionQueue' BasicQueueARN: Description: The ARN of the Basic Queue Value: !GetAtt LambdaInvokeConnectInfo.Basic Export: Name: !Sub '${AWS::StackName}-BasicQueue' Resources: # IAM Role for lambda functions to create Connect Components CoreLambdasIAMRole: Type: "AWS::IAM::Role" Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com Action: - "sts:AssumeRole" Description: A role to allow all the functions to access Connect Policies: - PolicyName: BuildConnectComponents PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - connect:createQueue - connect:createRoutingProfile - connect:list* - connect:associate* Resource: - !Sub ${AmazonConnectInstanceARN}/* - !Ref AmazonConnectInstanceARN - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: "*" # Lambda function to get BasicQueueArn, Hours of Operation, Security Profiles, and QueueTransferFlowARN LambdaInfoFromConnect: Type: AWS::Lambda::Function Properties: Description: Function to get Connect BasicQueue, HoursOfOperation, Security Profiles, and QueueTransferFlow ARNs Environment: Variables: INSTANCEARN: !Ref AmazonConnectInstanceARN Code: ZipFile: | # MIT No Attribution # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # Permission is hereby granted, free of charge, to any person obtaining a copy of this # software and associated documentation files (the "Software"), to deal in the Software # without restriction, including without limitation the rights to use, copy, modify, # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import json import boto3 import os import cfnresponse client = boto3.client('connect') InstanceArn= os.environ['INSTANCEARN'] # This Lambda retrieves the details required to create a Quick Connect to trasnsfer to the Basic Queue def lambda_handler(event, context): if event['RequestType'] == 'Create': # List Hours of Opertaion, Queues, and Security Profiles try: InstanceId = InstanceArn[-36:] # List Hours of Operation responseListHoO = client.list_hours_of_operations( InstanceId=InstanceId, MaxResults=50 ) HoOlist = responseListHoO ["HoursOfOperationSummaryList"] for x in HoOlist: if x['Name'] == "Basic Hours": BasicHoursId=x['Id'] print("BasicHoursId= " + BasicHoursId) BasicHoursArn=x['Arn'] print("BasicHoursArn= " + BasicHoursArn) # List Queues responseListQueues = client.list_queues( InstanceId=InstanceId, QueueTypes=[ 'STANDARD', ], MaxResults=50 ) QueueList = responseListQueues ["QueueSummaryList"] queues = [] for x in QueueList: if x['Name'] == "BasicQueue": BasicQueueId=x['Id'] print("BasicQueueId= " + BasicQueueId) BasicQueueArn=x['Arn'] print("BasicQueueArn= " + BasicQueueArn) # List Queue Transfer Contact Flows responseTransferQCF = client.list_contact_flows( InstanceId=InstanceId, ContactFlowTypes = ['QUEUE_TRANSFER'], MaxResults=50 ) QTransferCFlist = responseTransferQCF ["ContactFlowSummaryList"] for x in QTransferCFlist: if x['Name'] == "Default queue transfer": QueueTransferFlowArn=x['Arn'] print("QueueTransferFlowArn= " + QueueTransferFlowArn) # List Security Profiles responseListSecProfiles = client.list_security_profiles( InstanceId=InstanceId, MaxResults=50 ) SecProfilesList = responseListSecProfiles ["SecurityProfileSummaryList"] for x in SecProfilesList: if x['Name'] == "Admin": AdminSecProfileArn=x['Arn'] print("AdminSecProfileArn= " + AdminSecProfileArn) responseData = {} responseData['Basic'] = BasicQueueArn responseData['QueueTransfer'] = QueueTransferFlowArn responseData['BasicHours'] = BasicHoursArn responseData['SecProfile'] = AdminSecProfileArn cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, "CustomResourcePhysicalID") print(responseData) except Exception as a: print (a) return { 'message': 'Failed listing Queues and ContactFlows' } else: responseData = {} responseData['Data'] = "Delete" cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, "CustomResourcePhysicalID") Handler: index.lambda_handler Role: !GetAtt CoreLambdasIAMRole.Arn Runtime: python3.9 # Run Lambda to Get Connect information LambdaInvokeConnectInfo: Type: AWS::CloudFormation::CustomResource DependsOn: - LambdaInfoFromConnect Version: "1.0" Properties: ServiceToken: !GetAtt LambdaInfoFromConnect.Arn # Quick Connect to transfer to Retention Queue RetentionQueueQuickConnect: Type: AWS::Connect::QuickConnect DependsOn: - LambdainvokeQueues Properties: Description: Quick Connect to transfer to the Retention Queue InstanceArn: !Ref AmazonConnectInstanceARN Name: "RetentionQueue" QuickConnectConfig: QuickConnectType: QUEUE QueueConfig: ContactFlowArn: !GetAtt LambdaInvokeConnectInfo.QueueTransfer QueueArn: !GetAtt LambdainvokeQueues.Retention # Lambda function to create queue for customer retention LambdacreateQueues: Type: AWS::Lambda::Function Properties: Description: Function to create Retention Queue in Connect Environment: Variables: INSTANCEARN: !Ref AmazonConnectInstanceARN HOURSOPERATIONARN: !GetAtt LambdaInvokeConnectInfo.BasicHours Code: ZipFile: | # MIT No Attribution # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # Permission is hereby granted, free of charge, to any person obtaining a copy of this # software and associated documentation files (the "Software"), to deal in the Software # without restriction, including without limitation the rights to use, copy, modify, # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import json import boto3 import os import cfnresponse client = boto3.client('connect') InstanceArn= os.environ['INSTANCEARN'] HoursOperationArn= os.environ['HOURSOPERATIONARN'] # This Lambda creates the queues required for the workshop def lambda_handler(event, context): if event['RequestType'] == 'Create': InstanceId = InstanceArn[-36:] hoursOfOperationId = HoursOperationArn[-36:] # Hours of Operation, PhoneNumber and OutboundWhisper Contact Flow try: responseNumber = client.list_phone_numbers( InstanceId=InstanceId, PhoneNumberTypes = ['DID'], MaxResults=1 ) phoneNumberId = responseNumber ["PhoneNumberSummaryList"][0]["Id"] print("PhoneNumberId= " + phoneNumberId) responseOutboundWhisper = client.list_contact_flows( InstanceId=InstanceId, ContactFlowTypes = ['OUTBOUND_WHISPER'], MaxResults=1 ) OutboundFlowId = responseOutboundWhisper ["ContactFlowSummaryList"][0]["Id"] print("OutboundFlowId= " + OutboundFlowId) except Exception as a: print (a) return { 'message': 'Failed listing PhoneNumber or OutboundWhisper' } try: responseQueueRetention = client.create_queue( InstanceId=InstanceId, Name='Retention Queue', Description='Queue for Customer Retention Specialists', OutboundCallerConfig={ 'OutboundCallerIdNumberId': phoneNumberId, 'OutboundFlowId': OutboundFlowId }, HoursOfOperationId=hoursOfOperationId ) print(responseQueueRetention) QueueRetentionArn=responseQueueRetention['QueueArn'] print(QueueRetentionArn) responseData = {} responseData['Retention'] = QueueRetentionArn cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, "CustomResourcePhysicalID") except Exception as e: print (e) return { 'message': 'Failure Creating Queues' } else: responseData = {} responseData['Data'] = "Delete" cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, "CustomResourcePhysicalID") Handler: index.lambda_handler Role: !GetAtt CoreLambdasIAMRole.Arn Runtime: python3.9 # Run Lambda to create Queues LambdainvokeQueues: Type: AWS::CloudFormation::CustomResource DependsOn: - LambdacreateQueues Version: "1.0" Properties: ServiceToken: !GetAtt LambdacreateQueues.Arn # Lambda Function to create Routing Profile LambdacreateRoutingProfiles: Type: AWS::Lambda::Function DependsOn: - LambdacreateQueues Properties: Description: Function to create Retention Routing Profile Environment: Variables: INSTANCEARN: !Ref AmazonConnectInstanceARN RetentionQUEUE: !GetAtt LambdainvokeQueues.Retention BasicQUEUE: !GetAtt LambdaInvokeConnectInfo.Basic QuickConnectARN: !Ref RetentionQueueQuickConnect Code: ZipFile: | # MIT No Attribution # Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. # Permission is hereby granted, free of charge, to any person obtaining a copy of this # software and associated documentation files (the "Software"), to deal in the Software # without restriction, including without limitation the rights to use, copy, modify, # merge, publish, distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, # INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A # PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT # HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import json import boto3 import os import cfnresponse client = boto3.client('connect') InstanceArn= os.environ['INSTANCEARN'] RetentionQueueArn= os.environ['RetentionQUEUE'] BasicQueueArn= os.environ['BasicQUEUE'] QuickConnectArn= os.environ['QuickConnectARN'] # This Lambda creates the routing profile required for the retention specialists and update Basic Queue Quick Connect def lambda_handler(event, context): if event['RequestType'] == 'Create': # Setup Data for Routing Profile try: InstanceId = InstanceArn[-36:] RetentionQueueId = RetentionQueueArn[-36:] BasicQueueId = BasicQueueArn[-36:] QuickConnectId = QuickConnectArn[-36:] print("QuickConnectId= " + QuickConnectId) print("RetentionQueueId= " + RetentionQueueId) queues = [ {'QueueReference': { 'QueueId': RetentionQueueId, 'Channel': 'VOICE' },'Priority': 1,'Delay': 0 }, {'QueueReference': { 'QueueId': RetentionQueueId, 'Channel': 'CHAT' },'Priority': 1,'Delay': 0 }, {'QueueReference': { 'QueueId': RetentionQueueId, 'Channel': 'TASK' },'Priority': 1,'Delay': 0 } ] print(queues) except Exception as a: print (a) return { 'message': 'Failed Setting up data for Routing Profile' } # Create Routing Profile and Update Basic Queue try: responseRouting = client.create_routing_profile( InstanceId=InstanceId, Name='Retention Specialists', Description='Routing profile for Retention Specialists', DefaultOutboundQueueId=RetentionQueueId, QueueConfigs=queues, MediaConcurrencies=[ { 'Channel': 'VOICE', 'Concurrency': 1 }, { 'Channel': 'CHAT', 'Concurrency': 3 }, { 'Channel': 'TASK', 'Concurrency': 3 }, ] ) RoutingProfileARN = responseRouting ["RoutingProfileArn"] # Associate QuickConnect with Basic Queue responseAssociate = client.associate_queue_quick_connects( InstanceId=InstanceId, QueueId=BasicQueueId, QuickConnectIds=[ QuickConnectId ] ) print(responseAssociate) responseData = {} responseData['RoutingProfile'] = RoutingProfileARN cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, "CustomResourcePhysicalID") except Exception as e: print (e) return { 'message': 'Failed Creating Routing Profile' } else: responseData = {} responseData['Data'] = "Delete" cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, "CustomResourcePhysicalID") #FunctionName: WorkshopCreateRoutingProfiles Handler: index.lambda_handler Role: !GetAtt CoreLambdasIAMRole.Arn Runtime: python3.9 # Run Lambda to create Routing Profile LambdainvokeRouting: Type: AWS::CloudFormation::CustomResource DependsOn: - LambdacreateRoutingProfiles Version: "1.0" Properties: ServiceToken: !GetAtt LambdacreateRoutingProfiles.Arn # Create a new Retention Specialist User RetentionSpecialistUser: Type: 'AWS::Connect::User' DependsOn: - LambdainvokeRouting Properties: IdentityInfo: FirstName: 'Retention1' LastName: 'Specialist' Email: !Ref NewEmailForNotifications PhoneConfig: PhoneType: 'SOFT_PHONE' Username: 'retention1' InstanceArn: !Ref AmazonConnectInstanceARN RoutingProfileArn: !GetAtt LambdainvokeRouting.RoutingProfile SecurityProfileArns: [!GetAtt LambdaInvokeConnectInfo.SecProfile] Password: "reInvent2022"