AWSTemplateFormatVersion: "2010-09-09" Description: Media2Cloud WebApp Stack - create frontend resources such as Amazon API Gateway, Amazon Cognito User Pool, demo portal, and etc. Version %%VERSION%% Mappings: Workflow: WebApp: Package: "%%PKG_WEBAPP%%" Api: Name: api Package: "%%PKG_API%%" APIGW: Stage: Name: demo Node: Runtime: Version: nodejs14.x Cognito: Group: Viewer: viewer Creator: creator Admin: admin Parameters: S3Bucket: Type: String Description: S3Bucket KeyPrefix: Type: String Description: KeyPrefix SolutionId: Type: String Description: SolutionId SolutionLowerCaseId: Type: String Description: SolutionLowerCaseId ResourcePrefix: Type: String Description: ResourcePrefix CustomUserAgent: Type: String Description: CustomUserAgent AnonymousUsage: Type: String Description: AnonymousUsage CustomResourcesLambdaArn: Type: String Description: CustomResourcesLambdaArn CustomResourcesRoleName: Type: String Description: CustomResourcesRoleName AwsSdkLayer: Type: String Description: AwsSdkLayer CoreLibLayer: Type: String Description: CoreLibLayer SolutionUuid: Type: String Description: SolutionUuid IotHost: Type: String Description: IotHost IotTopic: Type: String Description: IotTopic IotThingPolicy: Type: String Description: IotThingPolicy IngestBucket: Type: String Description: IngestBucket ProxyBucket: Type: String Description: ProxyBucket WebBucket: Type: String Description: WebBucket CloudFrontDistributionId: Type: String Description: CloudFrontDistributionId CloudFrontDistributionDomainName: Type: String Description: CloudFrontDistributionDomainName OpenSearchDomainName: Type: String Description: OpenSearchDomainName OpenSearchDomainEndpoint: Type: String Description: OpenSearchDomainEndpoint DefaultAIOptions: Type: String Description: DefaultAIOptions DefaultMinConfidence: Type: String Description: DefaultMinConfidence AIOptionsS3Key: Type: String Description: AIOptionsS3Key Resources: ################################################################################ # # APIGW CloudWatch Logs # ################################################################################ APIGWLogRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Principal: Service: apigateway.amazonaws.com Action: sts:AssumeRole Path: / ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs APIGWAccount: Type: AWS::ApiGateway::Account Properties: CloudWatchRoleArn: !GetAtt APIGWLogRole.Arn ################################################################################ # # REST API resources # ################################################################################ ApiLogGroup: Metadata: cfn_nag: rules_to_suppress: - id: W84 reason: Use default encryption. Disable additional KMS encryption requirement. https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/encrypt-log-data-kms.html Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub - /aws/lambda/${ResourcePrefix}-${name} - name: !FindInMap - Workflow - Api - Name RetentionInDays: 7 ApiRole: Type: AWS::IAM::Role Metadata: cfn_nag: rules_to_suppress: - id: W11 reason: Resources require wildcard Properties: AssumeRolePolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: sts:AssumeRole Principal: Service: lambda.amazonaws.com Path: !Sub /${ResourcePrefix}/ ManagedPolicyArns: - arn:aws:iam::aws:policy/AWSXRayDaemonWriteAccess Policies: - PolicyName: !Sub ${ResourcePrefix}-api-logs PolicyDocument: Version: "2012-10-17" Statement: # CloudWatch Logs - Effect: Allow Action: - logs:CreateLogGroup - logs:CreateLogStream - logs:PutLogEvents Resource: !GetAtt ApiLogGroup.Arn # IoT - allows API to attach policy to IoT core - Effect: Allow Action: iot:AttachPolicy # Attaching Cognito Identity Id requires wildcard character # cert/* and thinggroup/* resources won't work. # See details on https://docs.aws.amazon.com/IAM/latest/UserGuide/list_awsiot.html Resource: "*" # Rekognition - allows access to Face Collection - Effect: Allow Action: - rekognition:ListCollections - rekognition:DescribeCollection - rekognition:CreateCollection - rekognition:DeleteCollection - rekognition:ListFaces - rekognition:IndexFaces - rekognition:DeleteFaces Resource: !Sub arn:aws:rekognition:${AWS::Region}:${AWS::AccountId}:collection/* # Rekognition - allows access to Custom Labels projects - Effect: Allow Action: rekognition:DescribeProjects Resource: "*" - Effect: Allow Action: rekognition:DescribeProjectVersions Resource: !Sub arn:aws:rekognition:${AWS::Region}:${AWS::AccountId}:project/*/* # Comprehend - list custom entity recognizers - Effect: Allow Action: comprehend:ListEntityRecognizers Resource: "*" # Transcribe - list custom language models and vocabularies - Effect: Allow Action: - transcribe:ListLanguageModels - transcribe:ListVocabularies Resource: "*" # S3 - check object exists on ingest bucket - Effect: Allow Action: s3:GetObject Resource: !Sub arn:aws:s3:::${IngestBucket}/* # S3 - list object on proxy bucket - Effect: Allow Action: s3:ListBucket Resource: !Sub arn:aws:s3:::${ProxyBucket} # S3 - read/write/delete on proxy bucket - Effect: Allow Action: - s3:GetObject - s3:PutObject - s3:DeleteObject Resource: !Sub arn:aws:s3:::${ProxyBucket}/* # OpenSearch - Effect: Allow Action: - es:ESHttpGet - es:ESHttpHead - es:ESHttpPost - es:ESHttpPut - es:ESHttpDelete Resource: - !Sub arn:aws:es:${AWS::Region}:${AWS::AccountId}:domain/${OpenSearchDomainName} - !Sub arn:aws:es:${AWS::Region}:${AWS::AccountId}:domain/${OpenSearchDomainName}/* # Note: Attach Backend resources such as Step Functions and DynamoDB in the main stack ApiLambda: Type: AWS::Lambda::Function Metadata: cfn_nag: rules_to_suppress: - id: W89 reason: Workflow not using VPC - id: W92 reason: Workflow not limiting simultaneous executions Properties: FunctionName: !Sub - ${ResourcePrefix}-${name} - name: !FindInMap - Workflow - Api - Name Description: !Sub (${SolutionLowerCaseId}) Handle GET, POST, OPTIONS requests Runtime: !FindInMap - Node - Runtime - Version MemorySize: 128 Timeout: 300 Handler: index.handler Role: !GetAtt ApiRole.Arn Code: S3Bucket: !Ref S3Bucket S3Key: !Sub - ${KeyPrefix}/${package} - package: !FindInMap - Workflow - Api - Package Layers: - !Ref AwsSdkLayer - !Ref CoreLibLayer TracingConfig: Mode: Active Environment: Variables: ENV_EXPECTED_BUCKET_OWNER: !Ref AWS::AccountId ENV_CUSTOM_USER_AGENT: !Ref CustomUserAgent ENV_SOLUTION_ID: !Ref SolutionId ENV_RESOURCE_PREFIX: !Ref ResourcePrefix ENV_ANONYMOUS_USAGE: !Ref AnonymousUsage ENV_SOLUTION_UUID: !Ref SolutionUuid ENV_IOT_HOST: !Ref IotHost ENV_IOT_TOPIC: !Ref IotTopic ENV_IOT_THING_POLICY_NAME: !Ref IotThingPolicy ENV_INGEST_BUCKET: !Ref IngestBucket ENV_PROXY_BUCKET: !Ref ProxyBucket ENV_ES_DOMAIN_ENDPOINT: !Ref OpenSearchDomainEndpoint ENV_DEFAULT_AI_OPTIONS: !Ref DefaultAIOptions ENV_DEFAULT_MINCONFIDENCE: !Ref DefaultMinConfidence ENV_USER_POOL_ID: !Ref CognitoUserPool ENV_AI_OPTIONS_S3KEY: !Ref AIOptionsS3Key Tags: - Key: SolutionId Value: !Ref SolutionId RestApi: Type: AWS::ApiGateway::RestApi Properties: Description: !Sub (${SolutionLowerCaseId}) Rest API for webapp Body: swagger: 2.0 info: version: "2018-08-03T20:13:00Z" title: !Sub ${ResourcePrefix}-api basePath: !Sub - /${stageName} - stageName: !FindInMap - APIGW - Stage - Name schemes: - https paths: /{operation}: get: produces: - application/json parameters: - name: operation in: path required: true type: string response: "200": description: 200 response schema: $ref: "#/definitions/Empty" security: - sigv4: [] x-amazon-apigateway-integration: uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ApiLambda.Arn}/invocations responses: default: statusCode: "200" passthroughBehavior: when_no_match httpMethod: POST contentHandling: CONVERT_TO_TEXT type: aws_proxy delete: produces: - application/json parameters: - name: operation in: path required: true type: string response: "200": description: 200 response schema: $ref: "#/definitions/Empty" security: - sigv4: [] x-amazon-apigateway-integration: uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ApiLambda.Arn}/invocations responses: default: statusCode: "200" passthroughBehavior: when_no_match httpMethod: POST contentHandling: CONVERT_TO_TEXT type: aws_proxy post: produces: - application/json parameters: - name: operation in: path required: true type: string response: "200": description: 200 response schema: $ref: "#/definitions/Empty" security: - sigv4: [] x-amazon-apigateway-integration: uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ApiLambda.Arn}/invocations responses: default: statusCode: "200" passthroughBehavior: when_no_match httpMethod: POST contentHandling: CONVERT_TO_TEXT type: aws_proxy options: produces: - application/json parameters: - name: operation in: path required: true type: string response: "200": description: 200 response schema: $ref: "#/definitions/Empty" headers: Access-Control-Allow-Origin: type: string Access-Control-Allow-Methods: type: string Access-Control-Allow-Headers: type: string x-amazon-apigateway-integration: uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ApiLambda.Arn}/invocations responses: default: statusCode: "200" passthroughBehavior: when_no_match httpMethod: POST contentHandling: CONVERT_TO_TEXT type: aws_proxy /{operation}/{uuid+}: get: produces: - application/json parameters: - name: uuid in: path required: true type: string - name: operation in: path required: true type: string response: "200": description: 200 response schema: $ref: "#/definitions/Empty" security: - sigv4: [] x-amazon-apigateway-integration: uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ApiLambda.Arn}/invocations responses: default: statusCode: "200" passthroughBehavior: when_no_match httpMethod: POST contentHandling: CONVERT_TO_TEXT type: aws_proxy delete: produces: - application/json parameters: - name: uuid in: path required: true type: string - name: operation in: path required: true type: string response: "200": description: 200 response schema: $ref: "#/definitions/Empty" security: - sigv4: [] x-amazon-apigateway-integration: uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ApiLambda.Arn}/invocations responses: default: statusCode: "200" passthroughBehavior: when_no_match httpMethod: POST contentHandling: CONVERT_TO_TEXT type: aws_proxy post: produces: - application/json parameters: - name: uuid in: path required: true type: string - name: operation in: path required: true type: string response: "200": description: 200 response schema: $ref: "#/definitions/Empty" security: - sigv4: [] x-amazon-apigateway-integration: uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ApiLambda.Arn}/invocations responses: default: statusCode: "200" passthroughBehavior: when_no_match httpMethod: POST contentHandling: CONVERT_TO_TEXT type: aws_proxy options: produces: - application/json parameters: - name: uuid in: path required: true type: string - name: operation in: path required: true type: string response: "200": description: 200 response schema: $ref: "#/definitions/Empty" headers: Access-Control-Allow-Origin: type: string Access-Control-Allow-Methods: type: string Access-Control-Allow-Headers: type: string x-amazon-apigateway-integration: uri: !Sub arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${ApiLambda.Arn}/invocations responses: default: statusCode: "200" passthroughBehavior: when_no_match httpMethod: POST contentHandling: CONVERT_TO_TEXT type: aws_proxy securityDefinitions: sigv4: type: apiKey name: Authorization in: header x-amazon-apigateway-authtype: awsSigv4 definitions: Empty: type: object title: Empty Schema APIGWLogGroup: Metadata: cfn_nag: rules_to_suppress: - id: W84 reason: Use default encryption. Disable additional KMS encryption requirement. https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/encrypt-log-data-kms.html Type: AWS::Logs::LogGroup Properties: LogGroupName: !Sub /apigateway/media2cloud/${ResourcePrefix} RetentionInDays: 7 Deployment: Type: AWS::ApiGateway::Deployment Metadata: cfn_nag: rules_to_suppress: - id: W68 reason: Supress UsagePlan requirement Properties: Description: !Sub (${SolutionLowerCaseId}) created by ${ResourcePrefix} RestApiId: !Ref RestApi StageName: !FindInMap - APIGW - Stage - Name StageDescription: Description: !Sub (${SolutionLowerCaseId}) created by ${ResourcePrefix} LoggingLevel: ERROR AccessLogSetting: DestinationArn: !GetAtt APIGWLogGroup.Arn Format: $context.identity.sourceIp $context.identity.caller $context.identity.user [$context.requestTime] \"$context.httpMethod $context.resourcePath $context.protocol\" $context.status $context.responseLength $context.requestId MethodSettings: - ResourcePath: /~1{operation} HttpMethod: GET DataTraceEnabled: false LoggingLevel: ERROR - ResourcePath: /~1{operation} HttpMethod: POST DataTraceEnabled: false LoggingLevel: ERROR - ResourcePath: /~1{operation} HttpMethod: DELETE DataTraceEnabled: false LoggingLevel: ERROR - ResourcePath: /~1{operation}~1{uuid+} HttpMethod: GET DataTraceEnabled: false LoggingLevel: ERROR - ResourcePath: /~1{operation}~1{uuid+} HttpMethod: POST DataTraceEnabled: false LoggingLevel: ERROR - ResourcePath: /~1{operation}~1{uuid+} HttpMethod: DELETE DataTraceEnabled: false LoggingLevel: ERROR DELETEOperation: Type: AWS::Lambda::Permission Properties: Action: lambda:InvokeFunction FunctionName: !GetAtt ApiLambda.Arn Principal: apigateway.amazonaws.com SourceArn: !Sub - arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${RestApi}/${stageName}/DELETE/* - stageName: !FindInMap - APIGW - Stage - Name GETOperation: Type: AWS::Lambda::Permission Properties: Action: lambda:InvokeFunction FunctionName: !GetAtt ApiLambda.Arn Principal: apigateway.amazonaws.com SourceArn: !Sub - arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${RestApi}/${stageName}/GET/* - stageName: !FindInMap - APIGW - Stage - Name OPTIONSOperation: Type: AWS::Lambda::Permission Properties: Action: lambda:InvokeFunction FunctionName: !GetAtt ApiLambda.Arn Principal: apigateway.amazonaws.com SourceArn: !Sub - arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${RestApi}/${stageName}/OPTIONS/* - stageName: !FindInMap - APIGW - Stage - Name POSTOperation: Type: AWS::Lambda::Permission Properties: Action: lambda:InvokeFunction FunctionName: !GetAtt ApiLambda.Arn Principal: apigateway.amazonaws.com SourceArn: !Sub - arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${RestApi}/${stageName}/POST/* - stageName: !FindInMap - APIGW - Stage - Name DELETEUuid: Type: AWS::Lambda::Permission Properties: Action: lambda:InvokeFunction FunctionName: !GetAtt ApiLambda.Arn Principal: apigateway.amazonaws.com SourceArn: !Sub - arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${RestApi}/${stageName}/DELETE/*/* - stageName: !FindInMap - APIGW - Stage - Name GETUuid: Type: AWS::Lambda::Permission Properties: Action: lambda:InvokeFunction FunctionName: !GetAtt ApiLambda.Arn Principal: apigateway.amazonaws.com SourceArn: !Sub - arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${RestApi}/${stageName}/GET/*/* - stageName: !FindInMap - APIGW - Stage - Name OPTIONSUuid: Type: AWS::Lambda::Permission Properties: Action: lambda:InvokeFunction FunctionName: !GetAtt ApiLambda.Arn Principal: apigateway.amazonaws.com SourceArn: !Sub - arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${RestApi}/${stageName}/OPTIONS/*/* - stageName: !FindInMap - APIGW - Stage - Name POSTUuid: Type: AWS::Lambda::Permission Properties: Action: lambda:InvokeFunction FunctionName: !GetAtt ApiLambda.Arn Principal: apigateway.amazonaws.com SourceArn: !Sub - arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${RestApi}/${stageName}/POST/*/* - stageName: !FindInMap - APIGW - Stage - Name ################################################################################ # # Cognito resources # * UserPool, IdentityPool, AppClient, & Authenticated role # * UserGroups (viewer, creator, & admin) & IAM roles and policies mapped to UserGroup # * Add Cognito Admin APIs permission to ApiRole # ################################################################################ CognitoUserPool: Type: AWS::Cognito::UserPool Metadata: cfn_nag: rules_to_suppress: - id: F78 reason: Disable MFA check Properties: AdminCreateUserConfig: AllowAdminCreateUserOnly: true InviteMessageTemplate: EmailSubject: You are invited to AWS Media2Cloud demo portal EmailMessage: !Sub - |-
You will need this user name and temporary password to log in the first time.
User name: {username}
Temporary password: {####}
(After you log in with your temporary password, you will be prompted to create a new one.)
Open the link to log in:
${url}
S3 Bucket where content is ingested:
${ingest}
S3 Bucket where proxies and metadata are stored:
${proxy}
Team AWS Media2Cloud