AWSTemplateFormatVersion: "2010-09-09" Description: This template deploys the Amazon Rekognition PPE Demo (uksb-1qpqs27gu) Transform: AWS::Serverless-2016-10-31 Globals: Function: Runtime: nodejs16.x MemorySize: 128 Timeout: 120 Environment: Variables: REGION: !Ref AWS::Region TOPIC_ARN: !Ref TopicArn VERSION: "1.1" Api: EndpointConfiguration: REGIONAL Cors: AllowMethods: "'*'" AllowHeaders: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'" AllowOrigin: "'*'" Auth: DefaultAuthorizer: CognitoAuthorizer Authorizers: CognitoAuthorizer: UserPoolArn: !GetAtt CognitoUserPool.Arn AddDefaultAuthorizerToCorsPreflight: False GatewayResponses: DEFAULT_4XX: ResponseTemplates: "application/json": '{ "Message": $context.error.messageString }' ResponseParameters: Headers: Access-Control-Allow-Methods: "'*'" Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'" Access-Control-Allow-Origin: "'*'" DEFAULT_5XX: ResponseTemplates: "application/json": '{ "Message": $context.error.messageString }' ResponseParameters: Headers: Access-Control-Allow-Methods: "'*'" Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'" Access-Control-Allow-Origin: "'*'" BAD_REQUEST_BODY: StatusCode: 422 ResponseTemplates: "application/json": '{ "Message": $context.error.messageString }' ResponseParameters: Headers: Access-Control-Allow-Methods: "'*'" Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'" Access-Control-Allow-Origin: "'*'" BAD_REQUEST_PARAMETERS: StatusCode: 422 ResponseTemplates: "application/json": '{ "Message": $context.error.messageString }' ResponseParameters: Headers: Access-Control-Allow-Methods: "'*'" Access-Control-Allow-Headers: "'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token'" Access-Control-Allow-Origin: "'*'" Parameters: AdminEmail: Description: Creates a username to be used for Authentication. It needs to be an e-mail address. Type: String AllowedPattern: ^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$ ResourcePrefix: Description: AWS Resources are named based on the value of this parameter. You must customise this if you are launching more than one instance of the stack within the same account. Type: String Default: PPEDemo AllowedPattern: ^[a-zA-Z0-9_]*$ CreateCloudFrontDistribution: Description: Creates a CloudFront distribution for accessing the web interface of the demo. This must be enabled if S3 Block Public Access is enabled at an account level. Type: String Default: "true" AllowedValues: - "true" - "false" TopicArn: Description: When an SNS Topic Arn is provided, SNS notifications will be sent for each body part detected. The SNS Topic and the Demo need to be located in the same AWS Region. Type: String Default: "false" Conditions: WithCloudFront: !Equals [!Ref CreateCloudFrontDistribution, "true"] WithSNS: !Not [!Equals [!Ref TopicArn, "false"]] Outputs: api: Value: !Sub https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/ Description: PPE Demo API url: Value: !If - WithCloudFront - !Sub "https://${CloudFrontDistribution.DomainName}" - !Sub "https://${WebUIBucket.RegionalDomainName}/index.html" Description: PPE Demo URL Resources: SetupRekognitionAndWebUI: Type: Custom::Setup Properties: ServiceToken: !GetAtt LambdaSetup.Arn Region: !Ref AWS::Region WebUIBucket: Type: AWS::S3::Bucket Properties: VersioningConfiguration: Status: Enabled BucketEncryption: ServerSideEncryptionConfiguration: - ServerSideEncryptionByDefault: SSEAlgorithm: AES256 CorsConfiguration: CorsRules: - AllowedHeaders: ["*"] AllowedMethods: [GET] AllowedOrigins: ["*"] Id: RekogCorsRule MaxAge: 3600 WebUIBucketReadPolicy: Type: AWS::S3::BucketPolicy Condition: WithCloudFront Properties: Bucket: !Ref WebUIBucket PolicyDocument: Statement: - Action: s3:GetObject Effect: Allow Resource: !Sub arn:aws:s3:::${WebUIBucket}/* Principal: CanonicalUser: !GetAtt CloudFrontOriginAccessIdentity.S3CanonicalUserId CognitoIdentityPool: Type: AWS::Cognito::IdentityPool Properties: IdentityPoolName: !Sub RekogIdentityPool${ResourcePrefix} AllowUnauthenticatedIdentities: false CognitoIdentityProviders: - ClientId: !Ref CognitoUserPoolClient ProviderName: !GetAtt CognitoUserPool.ProviderName CognitoIdentityPoolRole: Type: AWS::Cognito::IdentityPoolRoleAttachment Properties: IdentityPoolId: !Ref CognitoIdentityPool Roles: authenticated: !GetAtt ApiGatewayInvokeRole.Arn unauthenticated: !GetAtt ApiGatewayInvokeRole.Arn CognitoUserPool: Type: AWS::Cognito::UserPool Properties: UserPoolName: !Sub ${ResourcePrefix}RekogUsersPool AdminCreateUserConfig: AllowAdminCreateUserOnly: true InviteMessageTemplate: EmailMessage: !Sub - 'Your Amazon Rekognition PPE Demo username is {username} and the temporary password is {####}

Click here to access the web interface: ${UIUrl}' - { UIUrl: !If [ WithCloudFront, !Sub "https://${CloudFrontDistribution.DomainName}", !Sub "https://${WebUIBucket.RegionalDomainName}/index.html", ], } EmailSubject: "Your temporary password for Amazon Rekognition PPE Demo" SMSMessage: "Your Amazon Rekognition PPE Demo username is {username} and the temporary password is {####}" AutoVerifiedAttributes: - email Policies: PasswordPolicy: MinimumLength: 8 RequireLowercase: false RequireNumbers: false RequireSymbols: false RequireUppercase: false CognitoUserPoolClient: Type: AWS::Cognito::UserPoolClient Properties: UserPoolId: !Ref CognitoUserPool ClientName: !Sub ${ResourcePrefix}RekogUsersPoolClient GenerateSecret: false RefreshTokenValidity: 1 CognitoUserPoolUser: Type: AWS::Cognito::UserPoolUser DependsOn: SetupRekognitionAndWebUI Properties: Username: !Ref AdminEmail UserPoolId: !Ref CognitoUserPool DesiredDeliveryMediums: - EMAIL UserAttributes: - Name: email Value: !Ref AdminEmail - Name: email_verified Value: "true" ApiGatewayInvokeRole: Type: AWS::IAM::Role Properties: ManagedPolicyArns: - arn:aws:iam::aws:policy/AmazonAPIGatewayInvokeFullAccess AssumeRolePolicyDocument: Statement: - Effect: Allow Principal: Federated: - cognito-identity.amazonaws.com Action: sts:AssumeRoleWithWebIdentity Condition: StringEquals: "cognito-identity.amazonaws.com:aud": !Ref CognitoIdentityPool Path: "/" Policies: - PolicyName: WebServiceExecutionPolicy PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: rekognition:DetectProtectiveEquipment Resource: "*" # Lambdas ProcessImageFunction: Type: AWS::Serverless::Function Properties: Handler: index.processHandler CodeUri: ../backend/functions/detect/ Events: Get: Type: Api Properties: Path: /process Method: post Policies: - RekognitionDetectOnlyPolicy: {} - Version: "2012-10-17" Statement: - Effect: Allow Action: - rekognition:DetectProtectiveEquipment Resource: "*" - !If - WithSNS - Effect: Allow Resource: !Ref TopicArn Action: sns:Publish - !Ref AWS::NoValue LambdaSetup: Type: AWS::Serverless::Function Properties: Handler: index.handler CodeUri: ../backend/functions/setup/ Description: Custom Lambda resource for the PPE Demo Cloudformation Stack Environment: Variables: API_GATEWAY: !Sub https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/ COGNITO_IDENTITY_POOL: !Ref CognitoIdentityPool COGNITO_USERPOOL_ID: !Ref CognitoUserPool COGNITO_USERPOOLCLIENT_ID: !Ref CognitoUserPoolClient COLLECTION_ID: !Ref ResourcePrefix CREATE_CLOUDFRONT_DISTRIBUTION: !Ref CreateCloudFrontDistribution FROM_BUCKET: !Sub solution-builders-${AWS::Region} TO_BUCKET: !Ref WebUIBucket Timeout: 900 Policies: - Statement: - Effect: Allow Action: - s3:PutObject - s3:PutObjectAcl - s3:DeleteObject - s3:ListBucket Resource: - !Sub arn:aws:s3:::${WebUIBucket} - !Sub arn:aws:s3:::${WebUIBucket}/* - Effect: Allow Action: s3:GetObject Resource: !Sub arn:aws:s3:::solution-builders-${AWS::Region}/* CloudFrontOriginAccessIdentity: Type: AWS::CloudFront::CloudFrontOriginAccessIdentity Condition: WithCloudFront Properties: CloudFrontOriginAccessIdentityConfig: Comment: !Ref WebUIBucket CloudFrontDistribution: Type: AWS::CloudFront::Distribution Condition: WithCloudFront Properties: DistributionConfig: Origins: - DomainName: !GetAtt WebUIBucket.RegionalDomainName Id: !Sub myS3Origin-${ResourcePrefix} S3OriginConfig: OriginAccessIdentity: !Sub origin-access-identity/cloudfront/${CloudFrontOriginAccessIdentity} Enabled: true HttpVersion: http2 Comment: The Distribution for the Rekognition Meter Web UI DefaultRootObject: index.html DefaultCacheBehavior: AllowedMethods: - HEAD - GET - OPTIONS TargetOriginId: !Sub myS3Origin-${ResourcePrefix} ForwardedValues: QueryString: false Cookies: Forward: none ViewerProtocolPolicy: redirect-to-https PriceClass: PriceClass_All ViewerCertificate: CloudFrontDefaultCertificate: true