AWSTemplateFormatVersion: 2010-09-09 Description: A primary template which creates a Lex Bot and integrate it with a Kendra Index with S3 bucket as a document respository. (qs-1qu380l7l) Metadata: QuickStartDocumentation: EntrypointName: "Parameters for launching stack creation" AWS::CloudFormation::Interface: ParameterGroups: - Label: default: "Kendra resource configuration" Parameters: - KendraIndexName - KendraIndexEdition - KendraS3BucketName - KendraDataSourceName - KendraFAQName - KendraFAQFileKey - Label: default: "Lex bot configuration" Parameters: - LexBotJSONKey - Label: default: "Cross account role configuration" Parameters: - AssumingAccountID - ExternalID - Label: default: "AWS Quick Start configuration" Parameters: - QSS3BucketName - QSS3BucketRegion - QSS3KeyPrefix ParameterLabels: QSS3BucketName: default: Your configured S3 bucket for the Quick Start QSS3BucketRegion: default: Region of Quick Start bucket QSS3KeyPrefix: default: S3 prefix where you want to sync the Git repo LexBotJSONKey: default: S3 key of JSON configuration of the Lex bot KendraS3BucketName: default: S3 bucket with documents KendraIndexName: default: Kendra index name KendraIndexEdition: default: Kendra index edition KendraDataSourceName: default: Data source name for Kendra index KendraFAQName: default: FAQ name for Kendra KendraFAQFileKey: default: S3 key for FAQs AssumingAccountID: default: Assuming account ID ExternalID: default: Organization's ID Parameters: QSS3BucketName: Type: String Description: >- The S3 bucket created for your copy of Quick Start assets. Default: aws-quickstart AllowedPattern: ^[0-9a-zA-Z]+([0-9a-zA-Z-]*[0-9a-zA-Z])*$ QSS3BucketRegion: Default: 'us-east-1' Description: 'The AWS Region where the Quick Start S3 bucket (QSS3BucketName) is hosted.' Type: String QSS3KeyPrefix: Type: String Description: The S3 key name prefix used for your copy of Quick Start assets. Default: quickstart-quantiphi-lex-kendra-backend/ AllowedPattern: ^[0-9a-zA-Z-/]*$ KendraS3BucketName: AllowedPattern: ^[a-z0-9][a-z0-9-.]*$ Description: The name of the S3 bucket the contains the Kendra docs for syncing. Type: String KendraIndexName: Description: The name of the Kendra index. Type: String Default: Lex-Kendra-Bot-Index KendraIndexEdition: Description: Kendra index edition (DEVELOPER_EDITION or ENTERPRISE_EDITION). Type: String AllowedValues: - DEVELOPER_EDITION - ENTERPRISE_EDITION Default: DEVELOPER_EDITION KendraDataSourceName: Description: The name of data source for the Kendra index. Type: String Default: lex-kendra-bot-data-source KendraFAQName: Description: The name of FAQs for the Kendra index. Type: String Default: lex-kendra-bot-faqs KendraFAQFileKey: AllowedPattern: ^.*.csv$ Description: The file where FAQs are stored for the Kendra index. Type: String Default: COVID_FAQ.csv LexBotJSONKey: AllowedPattern: ^.*.json$ Description: JSON configuration of the Lex bot. Type: String Default: assets/lex-bot-template/covid_bot_Export.json AssumingAccountID: Description: Account ID of the AWS account that assumes the IAM role to invoke Lex chatbot. Type: String ExternalID: Description: The organization's ID. Type: String Conditions: UsingDefaultBucket: !Equals [!Ref QSS3BucketName, 'aws-quickstart'] Resources: LambdaFunctionIAMRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - lambda.amazonaws.com - kendra.amazonaws.com - lex.amazonaws.com - s3.amazonaws.com Action: - sts:AssumeRole Path: "/" LambdaFunctionIAMPolicy: Type: AWS::IAM::Policy Properties: PolicyName: !Join - '' - - !Ref 'LambdaFunctionIAMRole' - _policy Roles: - Ref: LambdaFunctionIAMRole PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - "logs:CreateLogGroup" - "logs:CreateLogStream" - "logs:PutLogEvents" Resource: - !Sub "arn:${AWS::Partition}:logs:*:*:*" - Effect: Allow Action: - "s3:GetObject" - "s3:GetObjectTagging" - "s3:PutObject" - "s3:PutObjectTagging" - "s3:DeleteObject" Resource: - !Join - '' - - !Sub 'arn:${AWS::Partition}:s3:::' - !Ref RegionalArtifactBucket - !Join - '' - - !Sub 'arn:${AWS::Partition}:s3:::' - !Ref RegionalArtifactBucket - '/*' - !Join - '' - - !Sub 'arn:${AWS::Partition}:s3:::' - !Ref QSS3BucketName - !Join - '' - - !Sub 'arn:${AWS::Partition}:s3:::' - !Ref QSS3BucketName - '/*' - Effect: Allow Action: - "iam:GetRole" - "iam:PassRole" Resource: - !GetAtt LambdaFunctionIAMRole.Arn - Effect: Allow Action: - lambda:InvokeFunction Resource: - !Sub "arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:*" RegionalArtifactBucket: Type: AWS::S3::Bucket CopyZipsFunction: DependsOn: - LambdaFunctionIAMPolicy Type: AWS::Lambda::Function Properties: Description: Copies objects from a source S3 bucket to a destination. Handler: index.handler Runtime: python3.6 Role: !GetAtt LambdaFunctionIAMRole.Arn Timeout: 240 Code: ZipFile: !Join - "\n" - - import json - import logging - import threading - import boto3 - import cfnresponse - '' - 'def copy_objects(source_bucket, dest_bucket, prefix, objects):' - ' s3 = boto3.client(''s3'')' - ' for o in objects:' - ' key = prefix + o' - ' copy_source = {' - ' ''Bucket'': source_bucket,' - ' ''Key'': key' - ' }' - ' print((''copy_source: %s'' % copy_source))' - ' print((''dest_bucket = %s''%dest_bucket))' - ' print((''key = %s'' %key))' - ' s3.copy_object(CopySource=copy_source, Bucket=dest_bucket,' - ' Key=key)' - '' - 'def delete_objects(bucket, prefix, objects):' - ' s3 = boto3.client(''s3'')' - ' objects = {''Objects'': [{''Key'': prefix + o} for o in objects]}' - ' s3.delete_objects(Bucket=bucket, Delete=objects)' - '' - 'def timeout(event, context):' - ' logging.error(''Execution is about to time out, sending failure response to CloudFormation'')' - ' cfnresponse.send(event, context, cfnresponse.FAILED, {}, None)' - '' - 'def handler(event, context):' - ' # make sure we send a failure to CloudFormation if the function' - ' # is going to timeout' - ' timer = threading.Timer((context.get_remaining_time_in_millis()' - ' / 1000.00) - 0.5, timeout, args=[event, context])' - ' timer.start()' - ' print((''Received event: %s'' % json.dumps(event)))' - ' status = cfnresponse.SUCCESS' - ' try:' - ' source_bucket = event[''ResourceProperties''][''SourceBucket'']' - ' dest_bucket = event[''ResourceProperties''][''DestBucket'']' - ' prefix = event[''ResourceProperties''][''Prefix'']' - ' objects = event[''ResourceProperties''][''Objects'']' - ' if event[''RequestType''] == ''Delete'':' - ' delete_objects(dest_bucket, prefix, objects)' - ' else:' - ' copy_objects(source_bucket, dest_bucket, prefix, objects)' - ' except Exception as e:' - ' logging.error(''Exception: %s'' % e, exc_info=True)' - ' status = cfnresponse.FAILED' - ' finally:' - ' timer.cancel()' - ' cfnresponse.send(event, context, status, {}, None)' - '' CopyZips: Type: Custom::CopyZips Properties: ServiceToken: !GetAtt 'CopyZipsFunction.Arn' DestBucket: !Ref 'RegionalArtifactBucket' SourceBucket: !Ref 'QSS3BucketName' Prefix: !Ref 'QSS3KeyPrefix' Objects: - "templates/lex_bot_resource.template.yaml" - "templates/lambda_kendra_search.template.yaml" - "templates/kendra_resource.template.yaml" - "functions/packages/lambda_layers/lambda_layers.zip" - "functions/packages/kendra_search_intent_handler_lambda/kendra_search_intent_handler_lambda.zip" - !Ref LexBotJSONKey - "functions/packages/lex_custom_resource/lex_custom_resource.zip" - "functions/packages/kendra_custom_resource/kendra_custom_resource.zip" KendraIndexStack: Type: 'AWS::CloudFormation::Stack' DependsOn: CopyZips Properties: TemplateURL: !Sub - 'https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QSS3KeyPrefix}templates/kendra_resource.template.yaml' - S3Region: !If [UsingDefaultBucket, !Ref 'AWS::Region', !Ref QSS3BucketRegion] S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref RegionalArtifactBucket] Parameters: QSS3KeyPrefix: !Ref QSS3KeyPrefix KendraS3BucketName: !Ref KendraS3BucketName KendraIndexName: !Ref KendraIndexName KendraIndexEdition: !Ref KendraIndexEdition KendraDataSourceName: !Ref KendraDataSourceName KendraFAQName: !Ref KendraFAQName KendraFAQFileKey: !Ref KendraFAQFileKey ArtifactsS3BucketName: !Ref 'RegionalArtifactBucket' KendraSearchIntentStack: Type: 'AWS::CloudFormation::Stack' DependsOn: KendraIndexStack Properties: TemplateURL: !Sub - 'https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QSS3KeyPrefix}templates/lambda_kendra_search.template.yaml' - S3Region: !If [UsingDefaultBucket, !Ref 'AWS::Region', !Ref QSS3BucketRegion] S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref RegionalArtifactBucket] Parameters: QSS3KeyPrefix: !Ref QSS3KeyPrefix ArtifactsS3BucketName: !Ref 'RegionalArtifactBucket' KendraS3BucketName: !Ref KendraS3BucketName LexBotStack: Type: 'AWS::CloudFormation::Stack' Properties: TemplateURL: !Sub - 'https://${S3Bucket}.s3.${S3Region}.${AWS::URLSuffix}/${QSS3KeyPrefix}templates/lex_bot_resource.template.yaml' - S3Region: !If [UsingDefaultBucket, !Ref 'AWS::Region', !Ref QSS3BucketRegion] S3Bucket: !If [UsingDefaultBucket, !Sub '${QSS3BucketName}-${AWS::Region}', !Ref RegionalArtifactBucket] Parameters: QSS3KeyPrefix: !Ref QSS3KeyPrefix ArtifactsS3BucketName: !Ref 'RegionalArtifactBucket' LambdaFunctionARN: !GetAtt KendraSearchIntentStack.Outputs.LambdaFunctionARN LexBotJSONKey: !Ref LexBotJSONKey AssumingAccountID: !Ref AssumingAccountID ExternalID: !Ref ExternalID Outputs: AssumeIAMRoleARN: Description: Amazon Resource Name (ARN) of IAM role used for cross-account integration. Value: !GetAtt LexBotStack.Outputs.AssumeIAMRoleARN KendraIndexName: Description: Name of the Kendra Index Value: !Ref KendraIndexName KendraSearchIntentLambdaFunctionName: Description: Name of AWS Lambda function that handles Kendra search intent. Value: !GetAtt KendraSearchIntentStack.Outputs.LambdaFunctionARN LexBotName: Description: Lex Bot Name Value: !GetAtt LexBotStack.Outputs.LexBotName