AWSTemplateFormatVersion: "2010-09-09" Description: "This sets up the Subtitle and Translate Video blog environment" Parameters: S3CodeBucket: Type: "String" AllowedPattern: "^[0-9a-zA-Z]+([0-9a-zA-Z-]*[0-9a-zA-Z])*$" Description: "The bucket that has already been created that has the code for the blog post" Resources: ############## # S3 Buckets # ############## stvblogLogBucket: Type: "AWS::S3::Bucket" Properties: PublicAccessBlockConfiguration: BlockPublicAcls: true BlockPublicPolicy: true IgnorePublicAcls: true RestrictPublicBuckets: true BucketEncryption: ServerSideEncryptionConfiguration: - ServerSideEncryptionByDefault: SSEAlgorithm: "AES256" BucketKeyEnabled: false AccessControl: "LogDeliveryWrite" stvblogBucket: Type: "AWS::S3::Bucket" Properties: CorsConfiguration: CorsRules: - AllowedHeaders: - "*" AllowedMethods: - "HEAD" - "GET" - "PUT" - "POST" - "DELETE" AllowedOrigins: - "*" PublicAccessBlockConfiguration: BlockPublicAcls: true BlockPublicPolicy: true IgnorePublicAcls: true RestrictPublicBuckets: true LoggingConfiguration: DestinationBucketName: !Ref stvblogLogBucket LogFilePrefix: "S3Logs" BucketEncryption: ServerSideEncryptionConfiguration: - ServerSideEncryptionByDefault: SSEAlgorithm: "AES256" BucketKeyEnabled: false stvblogAppBucket: Type: "AWS::S3::Bucket" Properties: CorsConfiguration: CorsRules: - AllowedHeaders: - "*" AllowedMethods: - "HEAD" - "GET" - "PUT" - "POST" - "DELETE" AllowedOrigins: - "*" PublicAccessBlockConfiguration: BlockPublicAcls: true BlockPublicPolicy: true IgnorePublicAcls: true RestrictPublicBuckets: true LoggingConfiguration: DestinationBucketName: !Ref stvblogLogBucket LogFilePrefix: "S3Logs" BucketEncryption: ServerSideEncryptionConfiguration: - ServerSideEncryptionByDefault: SSEAlgorithm: "AES256" BucketKeyEnabled: false stvblogInBucket: Type: "AWS::S3::Bucket" Properties: CorsConfiguration: CorsRules: - AllowedHeaders: - "*" AllowedMethods: - "HEAD" - "GET" - "PUT" - "POST" - "DELETE" AllowedOrigins: - "*" PublicAccessBlockConfiguration: BlockPublicAcls: true BlockPublicPolicy: true IgnorePublicAcls: true RestrictPublicBuckets: true LoggingConfiguration: DestinationBucketName: !Ref stvblogLogBucket LogFilePrefix: "S3Logs" BucketEncryption: ServerSideEncryptionConfiguration: - ServerSideEncryptionByDefault: SSEAlgorithm: "AES256" BucketKeyEnabled: false ###################### # S3 Bucket Policies # ###################### stvblogBucketS3BucketPolicy: Type: "AWS::S3::BucketPolicy" DependsOn: stvblogCloudFrontOriginAccessIdentity Properties: Bucket: !Ref stvblogBucket PolicyDocument: Version: "2008-10-17" Id: "PolicyForCloudFrontPrivateContent" Statement: - Sid: "1" Effect: "Allow" Principal: AWS: !Sub "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity ${stvblogCloudFrontOriginAccessIdentity}" Action: - "s3:GetObject" Resource: !Sub "${stvblogBucket.Arn}/*" stvblogAppBucketS3BucketPolicy: Type: "AWS::S3::BucketPolicy" DependsOn: stvblogCloudFrontOriginAccessIdentity Properties: Bucket: !Ref stvblogAppBucket PolicyDocument: Version: "2008-10-17" Id: "PolicyForCloudFrontPrivateContent" Statement: - Sid: "1" Effect: "Allow" Principal: AWS: !Sub "arn:aws:iam::cloudfront:user/CloudFront Origin Access Identity ${stvblogCloudFrontOriginAccessIdentity}" Action: "s3:GetObject" Resource: !Sub "${stvblogAppBucket.Arn}/*" ################ # IAM Policies # ################ stvblogAllowLoggingPolicy: Type: "AWS::IAM::ManagedPolicy" Properties: Path: "/" PolicyDocument: | { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "logs:CreateLogDelivery", "logs:GetLogDelivery", "logs:UpdateLogDelivery", "logs:DeleteLogDelivery", "logs:ListLogDeliveries", "logs:PutResourcePolicy", "logs:DescribeResourcePolicies", "logs:DescribeLogGroups" ], "Resource": "*" } ] } stvblogCloudWatchLogsDeliveryFullAccessPolicy: Type: "AWS::IAM::ManagedPolicy" Properties: Path: "/" PolicyDocument: | { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "logs:CreateLogDelivery", "logs:GetLogDelivery", "logs:UpdateLogDelivery", "logs:DeleteLogDelivery", "logs:ListLogDeliveries", "logs:PutResourcePolicy", "logs:DescribeResourcePolicies", "logs:DescribeLogGroups" ], "Resource": "*" } ] } stvblogLambdaExecutionPolicy: Type: "AWS::IAM::ManagedPolicy" Properties: Path: "/" PolicyDocument: !Sub | { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "polly:SynthesizeSpeech", "translate:ListTextTranslationJobs", "dynamodb:ListTables", "mediaconvert:ListQueues", "mediaconvert:DescribeEndpoints", "mediaconvert:ListPresets", "transcribe:ListTranscriptionJobs", "polly:ListSpeechSynthesisTasks", "translate:DescribeTextTranslationJob", "mediaconvert:CreatePreset", "transcribe:GetTranscriptionJob", "polly:StartSpeechSynthesisTask", "ssm:DescribeParameters", "polly:GetSpeechSynthesisTask", "transcribe:StartTranscriptionJob", "mediaconvert:DisassociateCertificate", "mediaconvert:CreateQueue", "translate:StartTextTranslationJob", "mediaconvert:AssociateCertificate", "translate:TranslateText", "mediaconvert:ListJobTemplates" ], "Resource": "*" }, { "Action": [ "logs:*" ], "Effect": "Allow", "Resource": "*" }, { "Effect": "Allow", "Action": [ "iam:PassRole" ], "Resource": "*", "Condition": { "StringLike": { "iam:PassedToService": [ "mediaconvert.amazonaws.com" ] } } }, { "Effect": "Allow", "Action": [ "dynamodb:CreateTable", "s3:GetObject", "mediaconvert:*", "dynamodb:PutItem", "lambda:InvokeFunction", "dynamodb:GetItem", "dynamodb:UpdateItem", "logs:CreateLogGroup", "dynamodb:UpdateTable", "dynamodb:Scan" ], "Resource": [ "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogPrepareProcess", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogTranscribeMedia", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCheckTranscribeJobStatus", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCreatePhrasesFromTranscription", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogTranslateTranscript", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCheckTranslationJobStatus", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCombinePhrasesAndTranslation", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCreatePollyAudio", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCreateSSML", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCreatePollyAudio", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCopyResultsToFinalInput", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCreateMediaConvertJob", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCheckMediaConvertJobStatus", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogFinalizeProcess", "arn:aws:mediaconvert:${AWS::Region}:${AWS::AccountId}:queues/*", "arn:aws:mediaconvert:${AWS::Region}:${AWS::AccountId}:jobs/*", "arn:aws:s3:::${stvblogBucket}/*", "arn:aws:s3:::${stvblogBucket}", "arn:aws:s3:::${stvblogInBucket}/*", "arn:aws:s3:::${stvblogInBucket}", "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:*", "arn:aws:dynamodb:*:${AWS::AccountId}:table/stvblog" ] }, { "Effect": "Allow", "Action": [ "logs:CreateLogStream", "lambda:InvokeFunction", "s3:ListBucket", "logs:PutLogEvents" ], "Resource": [ "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogPrepareProcess:*", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogTranscribeMedia:*", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCheckTranscribeJobStatus:*", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCreatePhrasesFromTranscription:*", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogTranslateTranscript:*", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCheckTranslationJobStatus:*", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCombinePhrasesAndTranslation:*", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCreatePollyAudio:*", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCreateSSML:*", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCopyResultsToFinalInput:*", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCreateMediaConvertJob:*", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCheckMediaConvertJobStatus:*", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogFinalizeProcess:*", "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda:*", "arn:aws:s3:::${stvblogBucket}/*", "arn:aws:s3:::${stvblogBucket}", "arn:aws:s3:::${stvblogInBucket}/*", "arn:aws:s3:::${stvblogInBucket}" ] }, { "Effect": "Allow", "Action": "s3:PutObject", "Resource": [ "arn:aws:s3:::${stvblogBucket}/*", "arn:aws:s3:::${stvblogInBucket}/*" ] }, { "Effect": "Allow", "Action": [ "ssm:PutParameter", "ssm:GetParameters", "ssm:GetParameter" ], "Resource": "arn:aws:ssm:*:${AWS::AccountId}:parameter/stvblog/*" }, { "Effect": "Allow", "Action": [ "iam:GetRole", "iam:PassRole" ], "Resource": "arn:aws:iam::${AWS::AccountId}:role/${stvblogTranslateS3BucketPermissionsRole}" } ] } stvblogLambdaInvokeScopedAccessPolicy: Type: "AWS::IAM::ManagedPolicy" Properties: Path: "/" PolicyDocument: !Sub | { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "lambda:InvokeFunction" ], "Resource": [ "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCombinePhrasesAndTranslation:*", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCreateSSML:*", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogFinalizeProcess:*", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCheckTranslationJobStatus:*", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCheckMediaConvertJobStatus:*", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogPrepareProcess:*", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogTranscribeMedia:*", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCopyResultsToFinalInput:*", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCreateMediaConvertJob:*", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCheckTranscribeJobStatus:*", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCreatePhrasesFromTranscription:*", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCreatePollyAudio:*", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCreateSRT:*", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogTranslateTranscript:*" ] }, { "Effect": "Allow", "Action": [ "lambda:InvokeFunction" ], "Resource": [ "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCombinePhrasesAndTranslation", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCreateSSML", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogFinalizeProcess", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCheckTranslationJobStatus", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCheckMediaConvertJobStatus", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogPrepareProcess", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogTranscribeMedia", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCopyResultsToFinalInput", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCreateMediaConvertJob", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCheckTranscribeJobStatus", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCreatePhrasesFromTranscription", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCreatePollyAudio", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCreateSRT", "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogTranslateTranscript" ] } ] } stvblogS3BucketPolicy: Type: "AWS::IAM::ManagedPolicy" Properties: Path: "/" PolicyDocument: !Sub | { "Version": "2012-10-17", "Statement": [ { "Action": [ "s3:GetObject" ], "Resource": [ "arn:aws:s3:::${stvblogBucket}", "arn:aws:s3:::${stvblogBucket}/*" ], "Effect": "Allow" }, { "Action": [ "s3:ListBucket" ], "Resource": [ "arn:aws:s3:::${stvblogBucket}" ], "Effect": "Allow" }, { "Action": [ "s3:PutObject" ], "Resource": [ "arn:aws:s3:::${stvblogBucket}/*" ], "Effect": "Allow" } ] } stvblogStepFunctionPolicy: Type: "AWS::IAM::ManagedPolicy" Properties: Path: "/" PolicyDocument: !Sub | { "Version": "2012-10-17", "Statement": [ { "Sid": "ExecuteStateMachine", "Effect": "Allow", "Action": "states:StartExecution", "Resource": [ "arn:aws:states:${AWS::Region}:${AWS::AccountId}:stateMachine:stvblogProcessMedia" ] } ] } stvblogXRayAccessPolicy: Type: "AWS::IAM::ManagedPolicy" Properties: Path: "/" PolicyDocument: | { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "xray:PutTraceSegments", "xray:PutTelemetryRecords", "xray:GetSamplingRules", "xray:GetSamplingTargets" ], "Resource": [ "*" ] } ] } ############# # IAM Roles # ############# stvblogElementalMediaConvertRole: Type: "AWS::IAM::Role" Properties: Path: "/" AssumeRolePolicyDocument: !Sub "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":[\"mediaconvert.${AWS::Region}.amazonaws.com\",\"mediaconvert.amazonaws.com\"]},\"Action\":\"sts:AssumeRole\"}]}" MaxSessionDuration: 3600 ManagedPolicyArns: - !Ref stvblogS3BucketPolicy Description: "Allows MediaConvert service to call S3 APIs and API Gateway on your behalf." stvblogLambaExecuteRole: Type: "AWS::IAM::Role" Properties: Path: "/" AssumeRolePolicyDocument: "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"lambda.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}" MaxSessionDuration: 3600 ManagedPolicyArns: - !Ref stvblogAllowLoggingPolicy - !Ref stvblogLambdaExecutionPolicy Description: "Allows Lambda functions to call AWS services on your behalf." stvblogStepFunctionExecuteRole: Type: "AWS::IAM::Role" Properties: Path: "/" AssumeRolePolicyDocument: "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"apigateway.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}" MaxSessionDuration: 3600 ManagedPolicyArns: - "arn:aws:iam::aws:policy/service-role/AmazonAPIGatewayPushToCloudWatchLogs" - !Ref stvblogStepFunctionPolicy Description: "Allows API Gateway to push logs to CloudWatch Logs." stvblogProcessMediaRole: Type: "AWS::IAM::Role" Properties: Path: "/" AssumeRolePolicyDocument: "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Sid\":\"\",\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"states.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}" MaxSessionDuration: 3600 ManagedPolicyArns: - !Ref stvblogAllowLoggingPolicy - !Ref stvblogLambdaInvokeScopedAccessPolicy - !Ref stvblogXRayAccessPolicy Description: "Allows Step Functions to access AWS resources on your behalf." stvblogTranslateS3BucketPermissionsRole: Type: "AWS::IAM::Role" Properties: Path: "/" AssumeRolePolicyDocument: "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"Service\":\"translate.amazonaws.com\"},\"Action\":\"sts:AssumeRole\"}]}" MaxSessionDuration: 3600 ManagedPolicyArns: - !Ref stvblogS3BucketPolicy Description: "Allows EC2 instances to call AWS services on your behalf." ################# # Lambda Layers # ################# stvblogBoto3LambdaLayer: Type: "AWS::Lambda::LayerVersion" Properties: Description: "boto3" CompatibleRuntimes: - "python3.8" Content: S3Bucket: !Ref S3CodeBucket S3Key: "layers/boto3.zip" stvblogFfmpegLambdaLayer: Type: "AWS::Lambda::LayerVersion" Properties: Description: "ffmpeg" CompatibleRuntimes: - "python3.8" Content: S3Bucket: !Ref S3CodeBucket S3Key: "layers/ffmpeg.zip" stvblogPydubLambdaLayer: Type: "AWS::Lambda::LayerVersion" Properties: Description: "pydub" CompatibleRuntimes: - "python3.8" Content: S3Bucket: !Ref S3CodeBucket S3Key: "layers/pydublayer.zip" stvblogStvblogLambdaLayer: Type: "AWS::Lambda::LayerVersion" Properties: Description: "stvblog" CompatibleRuntimes: - "python3.8" Content: S3Bucket: !Ref S3CodeBucket S3Key: "layers/stvblog.zip" #################### # Lambda Functions # #################### stvblogCreatePollyAudioLambdaFunction: Type: "AWS::Lambda::Function" DependsOn: - stvblogBoto3LambdaLayer - stvblogFfmpegLambdaLayer - stvblogPydubLambdaLayer - stvblogStvblogLambdaLayer - stvblogLambaExecuteRole Properties: Description: "" FunctionName: "stvblogCreatePollyAudio" Handler: "lambda_function.lambda_handler" Code: S3Bucket: !Ref S3CodeBucket S3Key: "lambda/stvblogCreatePollyAudio.zip" MemorySize: 1024 Role: !GetAtt stvblogLambaExecuteRole.Arn Runtime: "python3.8" Timeout: 300 ReservedConcurrentExecutions: 4 TracingConfig: Mode: "PassThrough" Layers: - !Ref stvblogBoto3LambdaLayer - !Ref stvblogStvblogLambdaLayer - !Ref stvblogPydubLambdaLayer - !Ref stvblogFfmpegLambdaLayer stvblogCreateSRTLambdaFunction: Type: "AWS::Lambda::Function" DependsOn: - stvblogBoto3LambdaLayer - stvblogStvblogLambdaLayer - stvblogLambaExecuteRole Properties: Description: "" FunctionName: "stvblogCreateSRT" Handler: "lambda_function.lambda_handler" Code: S3Bucket: !Ref S3CodeBucket S3Key: "lambda/stvblogCreateSRT.zip" MemorySize: 512 Role: !GetAtt stvblogLambaExecuteRole.Arn Runtime: "python3.8" Timeout: 60 ReservedConcurrentExecutions: 4 TracingConfig: Mode: "PassThrough" Layers: - !Ref stvblogBoto3LambdaLayer - !Ref stvblogStvblogLambdaLayer stvblogCopyResultsToFinalInputLambdaFunction: Type: "AWS::Lambda::Function" DependsOn: - stvblogBoto3LambdaLayer - stvblogStvblogLambdaLayer - stvblogLambaExecuteRole Properties: Description: "" FunctionName: "stvblogCopyResultsToFinalInput" Handler: "lambda_function.lambda_handler" Code: S3Bucket: !Ref S3CodeBucket S3Key: "lambda/stvblogCopyResultsToFinalInput.zip" MemorySize: 512 Role: !GetAtt stvblogLambaExecuteRole.Arn Runtime: "python3.8" Timeout: 60 ReservedConcurrentExecutions: 4 TracingConfig: Mode: "PassThrough" Layers: - !Ref stvblogBoto3LambdaLayer - !Ref stvblogStvblogLambdaLayer stvblogCombinePhrasesAndTranslationLambdaFunction: Type: "AWS::Lambda::Function" DependsOn: - stvblogBoto3LambdaLayer - stvblogStvblogLambdaLayer - stvblogLambaExecuteRole Properties: Description: "" FunctionName: "stvblogCombinePhrasesAndTranslation" Handler: "lambda_function.lambda_handler" Code: S3Bucket: !Ref S3CodeBucket S3Key: "lambda/stvblogCombinePhrasesAndTranslation.zip" MemorySize: 512 Role: !GetAtt stvblogLambaExecuteRole.Arn Runtime: "python3.8" Timeout: 60 ReservedConcurrentExecutions: 4 TracingConfig: Mode: "PassThrough" Layers: - !Ref stvblogBoto3LambdaLayer - !Ref stvblogStvblogLambdaLayer stvblogGetJobListLambdaFunction: Type: "AWS::Lambda::Function" DependsOn: - stvblogBoto3LambdaLayer - stvblogStvblogLambdaLayer - stvblogLambaExecuteRole Properties: Description: "" FunctionName: "stvblogGetJobList" Handler: "lambda_function.lambda_handler" Code: S3Bucket: !Ref S3CodeBucket S3Key: "lambda/stvblogGetJobList.zip" MemorySize: 512 Role: !GetAtt stvblogLambaExecuteRole.Arn Runtime: "python3.8" Timeout: 60 ReservedConcurrentExecutions: 4 TracingConfig: Mode: "PassThrough" Layers: - !Ref stvblogBoto3LambdaLayer - !Ref stvblogStvblogLambdaLayer stvblogFinalizeProcessLambdaFunction: Type: "AWS::Lambda::Function" DependsOn: - stvblogBoto3LambdaLayer - stvblogStvblogLambdaLayer - stvblogLambaExecuteRole Properties: Description: "" FunctionName: "stvblogFinalizeProcess" Handler: "lambda_function.lambda_handler" Code: S3Bucket: !Ref S3CodeBucket S3Key: "lambda/stvblogFinalizeProcess.zip" MemorySize: 512 Role: !GetAtt stvblogLambaExecuteRole.Arn Runtime: "python3.8" Timeout: 60 ReservedConcurrentExecutions: 4 TracingConfig: Mode: "PassThrough" Layers: - !Ref stvblogBoto3LambdaLayer - !Ref stvblogStvblogLambdaLayer stvblogCheckMediaConvertJobStatusLambdaFunction: Type: "AWS::Lambda::Function" DependsOn: - stvblogBoto3LambdaLayer - stvblogStvblogLambdaLayer - stvblogLambaExecuteRole Properties: Description: "" FunctionName: "stvblogCheckMediaConvertJobStatus" Handler: "lambda_function.lambda_handler" Code: S3Bucket: !Ref S3CodeBucket S3Key: "lambda/stvblogCheckMediaConvertJobStatus.zip" MemorySize: 512 Role: !GetAtt stvblogLambaExecuteRole.Arn Runtime: "python3.8" Timeout: 60 ReservedConcurrentExecutions: 4 TracingConfig: Mode: "PassThrough" Layers: - !Ref stvblogBoto3LambdaLayer - !Ref stvblogStvblogLambdaLayer stvblogTranslateTranscriptLambdaFunction: Type: "AWS::Lambda::Function" DependsOn: - stvblogBoto3LambdaLayer - stvblogStvblogLambdaLayer - stvblogLambaExecuteRole Properties: Description: "" FunctionName: "stvblogTranslateTranscript" Handler: "lambda_function.lambda_handler" Code: S3Bucket: !Ref S3CodeBucket S3Key: "lambda/stvblogTranslateTranscript.zip" MemorySize: 512 Role: !GetAtt stvblogLambaExecuteRole.Arn Runtime: "python3.8" Timeout: 60 ReservedConcurrentExecutions: 4 TracingConfig: Mode: "PassThrough" Layers: - !Ref stvblogBoto3LambdaLayer - !Ref stvblogStvblogLambdaLayer stvblogCreateMediaConvertJobLambdaFunction: Type: "AWS::Lambda::Function" DependsOn: - stvblogBoto3LambdaLayer - stvblogStvblogLambdaLayer - stvblogLambaExecuteRole Properties: Description: "" FunctionName: "stvblogCreateMediaConvertJob" Handler: "lambda_function.lambda_handler" Code: S3Bucket: !Ref S3CodeBucket S3Key: "lambda/stvblogCreateMediaConvertJob.zip" MemorySize: 512 Role: !GetAtt stvblogLambaExecuteRole.Arn Runtime: "python3.8" Timeout: 60 ReservedConcurrentExecutions: 4 TracingConfig: Mode: "PassThrough" Layers: - !Ref stvblogBoto3LambdaLayer - !Ref stvblogStvblogLambdaLayer stvblogCheckTranscribeJobStatusLambdaFunction: Type: "AWS::Lambda::Function" DependsOn: - stvblogBoto3LambdaLayer - stvblogStvblogLambdaLayer - stvblogLambaExecuteRole Properties: Description: "" FunctionName: "stvblogCheckTranscribeJobStatus" Handler: "lambda_function.lambda_handler" Code: S3Bucket: !Ref S3CodeBucket S3Key: "lambda/stvblogCheckTranscribeJobStatus.zip" MemorySize: 512 Role: !GetAtt stvblogLambaExecuteRole.Arn Runtime: "python3.8" Timeout: 60 ReservedConcurrentExecutions: 4 TracingConfig: Mode: "PassThrough" Layers: - !Ref stvblogBoto3LambdaLayer - !Ref stvblogStvblogLambdaLayer stvblogCreatePhrasesFromTranscriptionLambdaFunction: Type: "AWS::Lambda::Function" DependsOn: - stvblogBoto3LambdaLayer - stvblogStvblogLambdaLayer - stvblogLambaExecuteRole Properties: Description: "" FunctionName: "stvblogCreatePhrasesFromTranscription" Handler: "lambda_function.lambda_handler" Code: S3Bucket: !Ref S3CodeBucket S3Key: "lambda/stvblogCreatePhrasesFromTranscription.zip" MemorySize: 512 Role: !GetAtt stvblogLambaExecuteRole.Arn Runtime: "python3.8" Timeout: 60 ReservedConcurrentExecutions: 4 TracingConfig: Mode: "PassThrough" Layers: - !Ref stvblogBoto3LambdaLayer - !Ref stvblogStvblogLambdaLayer stvblogTranscribeMediaLambdaFunction: Type: "AWS::Lambda::Function" DependsOn: - stvblogBoto3LambdaLayer - stvblogStvblogLambdaLayer - stvblogLambaExecuteRole Properties: Description: "" FunctionName: "stvblogTranscribeMedia" Handler: "lambda_function.lambda_handler" Code: S3Bucket: !Ref S3CodeBucket S3Key: "lambda/stvblogTranscribeMedia.zip" MemorySize: 512 Role: !GetAtt stvblogLambaExecuteRole.Arn Runtime: "python3.8" Timeout: 60 ReservedConcurrentExecutions: 4 TracingConfig: Mode: "PassThrough" Layers: - !Ref stvblogBoto3LambdaLayer - !Ref stvblogStvblogLambdaLayer stvblogGetUploadUrlLambdaFunction: Type: "AWS::Lambda::Function" DependsOn: - stvblogBoto3LambdaLayer - stvblogStvblogLambdaLayer - stvblogLambaExecuteRole Properties: Description: "" FunctionName: "stvblogGetUploadUrl" Handler: "lambda_function.lambda_handler" Code: S3Bucket: !Ref S3CodeBucket S3Key: "lambda/stvblogGetUploadUrl.zip" MemorySize: 512 Role: !GetAtt stvblogLambaExecuteRole.Arn Runtime: "python3.8" Timeout: 60 ReservedConcurrentExecutions: 4 TracingConfig: Mode: "PassThrough" Layers: - !Ref stvblogBoto3LambdaLayer - !Ref stvblogStvblogLambdaLayer stvblogPrepareProcessLambdaFunction: Type: "AWS::Lambda::Function" DependsOn: - stvblogBoto3LambdaLayer - stvblogStvblogLambdaLayer - stvblogLambaExecuteRole Properties: Description: "" FunctionName: "stvblogPrepareProcess" Handler: "lambda_function.lambda_handler" Code: S3Bucket: !Ref S3CodeBucket S3Key: "lambda/stvblogPrepareProcess.zip" MemorySize: 512 Role: !GetAtt stvblogLambaExecuteRole.Arn Runtime: "python3.8" Timeout: 60 ReservedConcurrentExecutions: 4 TracingConfig: Mode: "PassThrough" Layers: - !Ref stvblogBoto3LambdaLayer - !Ref stvblogStvblogLambdaLayer stvblogCheckTranslationJobStatusLambdaFunction: Type: "AWS::Lambda::Function" DependsOn: - stvblogBoto3LambdaLayer - stvblogStvblogLambdaLayer - stvblogLambaExecuteRole Properties: Description: "" FunctionName: "stvblogCheckTranslationJobStatus" Handler: "lambda_function.lambda_handler" Code: S3Bucket: !Ref S3CodeBucket S3Key: "lambda/stvblogCheckTranslationJobStatus.zip" MemorySize: 512 Role: !GetAtt stvblogLambaExecuteRole.Arn Runtime: "python3.8" Timeout: 60 ReservedConcurrentExecutions: 4 TracingConfig: Mode: "PassThrough" Layers: - !Ref stvblogBoto3LambdaLayer - !Ref stvblogStvblogLambdaLayer stvblogCreateSSMLLambdaFunction: Type: "AWS::Lambda::Function" DependsOn: - stvblogBoto3LambdaLayer - stvblogStvblogLambdaLayer - stvblogLambaExecuteRole Properties: Description: "" FunctionName: "stvblogCreateSSML" Handler: "lambda_function.lambda_handler" Code: S3Bucket: !Ref S3CodeBucket S3Key: "lambda/stvblogCreateSSML.zip" MemorySize: 512 Role: !GetAtt stvblogLambaExecuteRole.Arn Runtime: "python3.8" Timeout: 60 ReservedConcurrentExecutions: 4 TracingConfig: Mode: "PassThrough" Layers: - !Ref stvblogBoto3LambdaLayer - !Ref stvblogStvblogLambdaLayer ###################### # Lambda Permissions # ###################### stvblogGetJobListLambdaPermission: Type: "AWS::Lambda::Permission" Properties: Action: "lambda:InvokeFunction" FunctionName: !GetAtt stvblogGetJobListLambdaFunction.Arn Principal: "apigateway.amazonaws.com" SourceArn: !Sub "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${stvblogJobV2Api}/*/*/job" stvblogGetUploadUrlLambdaPermission: Type: "AWS::Lambda::Permission" Properties: Action: "lambda:InvokeFunction" FunctionName: !GetAtt stvblogGetUploadUrlLambdaFunction.Arn Principal: "apigateway.amazonaws.com" SourceArn: !Sub "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${stvblogJobV2Api}/*/*/upload" ############### # API Gateway # ############### stvblogJobV2Api: Type: "AWS::ApiGatewayV2::Api" Properties: Name: "stvblogJobApi" ApiKeySelectionExpression: "$request.header.x-api-key" ProtocolType: "HTTP" RouteSelectionExpression: "$request.method $request.path" CorsConfiguration: AllowCredentials: false AllowHeaders: - "*" AllowMethods: - "*" AllowOrigins: - "*" MaxAge: 0 DisableExecuteApiEndpoint: false stvblogGetUploadUrlV2ApiIntegration: Type: "AWS::ApiGatewayV2::Integration" DependsOn: stvblogGetUploadUrlLambdaFunction Properties: ApiId: !Ref stvblogJobV2Api ConnectionType: "INTERNET" IntegrationMethod: "POST" IntegrationType: "AWS_PROXY" IntegrationUri: !GetAtt stvblogGetUploadUrlLambdaFunction.Arn TimeoutInMillis: 30000 PayloadFormatVersion: "2.0" stvblogStepFunctionStartV2ApiIntegration: Type: "AWS::ApiGatewayV2::Integration" Properties: ApiId: !Ref stvblogJobV2Api ConnectionType: "INTERNET" IntegrationType: "AWS_PROXY" IntegrationSubtype: StepFunctions-StartExecution CredentialsArn: !GetAtt stvblogStepFunctionExecuteRole.Arn RequestParameters: Input: "$request.body" StateMachineArn: !Sub "arn:aws:states:${AWS::Region}:${AWS::AccountId}:stateMachine:stvblogProcessMedia" TimeoutInMillis: 30000 PayloadFormatVersion: "1.0" stvblogGetJobListV2ApiIntegration: Type: "AWS::ApiGatewayV2::Integration" DependsOn: stvblogGetJobListLambdaFunction Properties: ApiId: !Ref stvblogJobV2Api ConnectionType: "INTERNET" IntegrationMethod: "POST" IntegrationType: "AWS_PROXY" IntegrationUri: !GetAtt stvblogGetJobListLambdaFunction.Arn TimeoutInMillis: 30000 PayloadFormatVersion: "2.0" stvblogPostJobV2ApiRoute: Type: "AWS::ApiGatewayV2::Route" Properties: ApiId: !Ref stvblogJobV2Api ApiKeyRequired: false AuthorizationType: " NONE" RouteKey: "POST /job" Target: !Sub "integrations/${stvblogStepFunctionStartV2ApiIntegration}" stvblogGetUploadV2ApiRoute: Type: "AWS::ApiGatewayV2::Route" DependsOn: stvblogGetUploadUrlV2ApiIntegration Properties: ApiId: !Ref stvblogJobV2Api ApiKeyRequired: false AuthorizationType: " NONE" RouteKey: "GET /upload" Target: !Sub "integrations/${stvblogGetUploadUrlV2ApiIntegration}" stvblogGetJobV2ApiRoute: Type: "AWS::ApiGatewayV2::Route" DependsOn: stvblogGetJobListV2ApiIntegration Properties: ApiId: !Ref stvblogJobV2Api ApiKeyRequired: false AuthorizationType: " NONE" RouteKey: "GET /job" Target: !Sub "integrations/${stvblogGetJobListV2ApiIntegration}" svtblogDefaultV2ApiStage: Type: "AWS::ApiGatewayV2::Stage" Properties: StageName: "$default" ApiId: !Ref stvblogJobV2Api AutoDeploy: true AccessLogSettings: DestinationArn: !GetAtt stvblogAPILogGroup.Arn Format: >- {"requestId":"$context.requestId", "ip": "$context.identity.sourceIp", "caller":"$context.identity.caller", "user":"$context.identity.user","requestTime":"$context.requestTime"} ############ # DynamoDB # ############ stvblogDynamoDBTable: Type: "AWS::DynamoDB::Table" Properties: AttributeDefinitions: - AttributeName: "ProcessName" AttributeType: "S" TableName: "stvblog" KeySchema: - AttributeName: "ProcessName" KeyType: "HASH" BillingMode: "PROVISIONED" ProvisionedThroughput: ReadCapacityUnits: 5 WriteCapacityUnits: 5 PointInTimeRecoverySpecification: PointInTimeRecoveryEnabled: true SSESpecification: SSEEnabled: false ################## # Step Functions # ################## stvblogProcessMediaStepFunction: Type: "AWS::StepFunctions::StateMachine" Properties: StateMachineName: "stvblogProcessMedia" DefinitionString: !Sub | { "Comment": "stvblog - Process Media to Transcribe, Translate, Subtitle, and Package it", "StartAt": "PrepareProcess", "States": { "PrepareProcess": { "Type": "Task", "Resource": "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogPrepareProcess", "ResultPath": "$", "Next": "CreateTranscriptionJob" }, "CreateTranscriptionJob": { "Type": "Task", "Resource": "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogTranscribeMedia", "ResultPath": "$", "Next": "PollWaitTime" }, "PollWaitTime": { "Type": "Wait", "Seconds": 30, "Next": "CheckTranscribeJobStatus" }, "CheckTranscribeJobStatus": { "Type": "Task", "Resource": "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCheckTranscribeJobStatus", "InputPath": "$", "ResultPath": "$", "Next": "PollStatus" }, "PollStatus": { "Type": "Choice", "Choices": [ { "Variable": "$.Outputs.transcribe.transcribeJobStatus", "StringEquals": "COMPLETED", "Next": "CreatePhrasesFromTranscription" }, { "Variable": "$.Outputs.transcribe.transcribeJobStatus", "StringEquals": "IN_PROGRESS", "Next": "PollWaitTime" } ], "Default": "PollError" }, "CreatePhrasesFromTranscription": { "Type": "Task", "Resource": "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCreatePhrasesFromTranscription", "ResultPath": "$", "Next": "SubtitleAndTranslate" }, "SubtitleAndTranslate": { "Type": "Map", "MaxConcurrency": 0, "ItemsPath": "$.Targets", "Parameters": { "input.$": "$", "item.$": "$$.Map.Item.Value", "index.$": "$$.Map.Item.Index" }, "Iterator": { "StartAt": "TranslateTranscript", "States": { "TranslateTranscript": { "Type": "Task", "Resource": "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogTranslateTranscript", "Next": "PollTranslationWaitTime" }, "PollTranslationWaitTime": { "Type": "Wait", "Seconds": 120, "Next": "CheckTranslationJobStatus" }, "CheckTranslationJobStatus": { "Type": "Task", "Resource": "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCheckTranslationJobStatus", "Next": "PollTranslationStatus" }, "PollTranslationStatus": { "Type": "Choice", "Choices": [ { "Variable": "$.item.translate.translateJobStatus", "StringEquals": "COMPLETED", "Next": "CombinePhrasesAndTranslation" }, { "Variable": "$.item.translate.translateJobStatus", "StringEquals": "IN_PROGRESS", "Next": "PollTranslationWaitTime" } ], "Default": "TranslationPollError" }, "CombinePhrasesAndTranslation": { "Type": "Task", "Resource": "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCombinePhrasesAndTranslation", "Next": "CreateSubtitles" }, "CreateSubtitles": { "Type": "Task", "Resource": "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCreateSRT", "Next": "PrepareSSML" }, "PrepareSSML": { "Type": "Task", "Resource": "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCreateSSML", "Next": "CreatePollyAudio" }, "CreatePollyAudio": { "Type": "Task", "Resource": "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCreatePollyAudio", "Next": "CopyResultsToFinal" }, "CopyResultsToFinal": { "Type": "Task", "Resource": "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCopyResultsToFinalInput", "End": true }, "TranslationPollError": { "Type": "Fail", "Cause": "Translation Polling Error" } } }, "Next": "CreateMediaConvertJob" }, "CreateMediaConvertJob": { "Type": "Task", "Resource": "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCreateMediaConvertJob", "Next": "MediaConvertWaitTime" }, "MediaConvertWaitTime": { "Type": "Wait", "Seconds": 30, "Next": "CheckMediaConvertJobStatus" }, "CheckMediaConvertJobStatus": { "Type": "Task", "Resource": "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogCheckMediaConvertJobStatus", "Next": "PollMediaConvertStatus" }, "PollMediaConvertStatus": { "Type": "Choice", "Choices": [ { "Variable": "$.Outputs.mediaConvert.mediaConvertJobStatus", "StringEquals": "COMPLETE", "Next": "Finalize" }, { "Or": [ { "Variable": "$.Outputs.mediaConvert.mediaConvertJobStatus", "StringEquals": "PROGRESSING" }, { "Variable": "$.Outputs.mediaConvert.mediaConvertJobStatus", "StringEquals": "SUBMITTED" } ], "Next": "MediaConvertWaitTime" } ], "Default": "MediaConvertPollError" }, "MediaConvertPollError": { "Type": "Fail", "Cause": "Translation Polling Error" }, "PollError": { "Type": "Fail", "Cause": "Polling Error" }, "Finalize": { "Type": "Task", "Resource": "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:stvblogFinalizeProcess", "End": true } } } RoleArn: !GetAtt stvblogProcessMediaRole.Arn StateMachineType: "STANDARD" LoggingConfiguration: Destinations: - CloudWatchLogsLogGroup: LogGroupArn: !GetAtt stvblogProcessMediaLogsLogGroup.Arn IncludeExecutionData: true Level: "ALL" ############# # Log Group # ############# stvblogProcessMediaLogsLogGroup: Type: "AWS::Logs::LogGroup" Properties: LogGroupName: !Sub "/aws/vendedlogs/states/stvblogProcessMedia-Logs" RetentionInDays: 14 stvblogAPILogGroup: Type: "AWS::Logs::LogGroup" Properties: LogGroupName: !Sub "/aws/vendedlogs/states/stvblogAPI-Logs" RetentionInDays: 14 ################### # Parameter Store # ################### stvblogCloudFrontUrlParameter: Type: "AWS::SSM::Parameter" Properties: Name: "/stvblog/CloudFrontUrl" Type: "String" Value: !GetAtt stvblogCloudFrontDistribution.DomainName DataType: "text" Tier: "Standard" stvblogbaseBucketNameParameter: Type: "AWS::SSM::Parameter" Properties: Name: "/stvblog/baseBucketName" Type: "String" Value: !Ref stvblogBucket DataType: "text" Tier: "Standard" stvblogfinalInputParameter: Type: "AWS::SSM::Parameter" Properties: Name: "/stvblog/finalInput" Type: "String" Value: "finalInput" DataType: "text" Tier: "Standard" stvblogmediaInputParameter: Type: "AWS::SSM::Parameter" Properties: Name: "/stvblog/mediaInput" Type: "String" Value: "mediaInput" DataType: "text" Tier: "Standard" stvblogpollyInputParameter: Type: "AWS::SSM::Parameter" Properties: Name: "/stvblog/pollyInput" Type: "String" Value: "pollyInput" DataType: "text" Tier: "Standard" stvblogtranscriptionInputParameter: Type: "AWS::SSM::Parameter" Properties: Name: "/stvblog/transcriptionInput" Type: "String" Value: "transcriptionInput" DataType: "text" Tier: "Standard" stvblogregionParameter: Type: "AWS::SSM::Parameter" Properties: Name: "/stvblog/region" Type: "String" Value: !Ref AWS::Region DataType: "text" Tier: "Standard" stvbloguploadFolderParameter: Type: "AWS::SSM::Parameter" Properties: Name: "/stvblog/uploadFolder" Type: "String" Value: !Ref stvblogInBucket DataType: "text" Tier: "Standard" stvblogpollyOutputParameter: Type: "AWS::SSM::Parameter" Properties: Name: "/stvblog/pollyOutput" Type: "String" Value: "pollyOutput" DataType: "text" Tier: "Standard" stvblogtranslateDataAccessRoleARNParameter: Type: "AWS::SSM::Parameter" Properties: Name: "/stvblog/translateDataAccessRoleARN" Type: "String" Value: !GetAtt stvblogTranslateS3BucketPermissionsRole.Arn DataType: "text" Tier: "Standard" stvblogtranscriptionOutputParameter: Type: "AWS::SSM::Parameter" Properties: Name: "/stvblog/transcriptionOutput" Type: "String" Value: "transcriptionOutput" DataType: "text" Tier: "Standard" stvblogMediaConvertRoleParameter: Type: "AWS::SSM::Parameter" Properties: Name: "/stvblog/MediaConvertRole" Type: "String" Value: !GetAtt stvblogElementalMediaConvertRole.Arn DataType: "text" Tier: "Standard" stvblogfinalOutputParameter: Type: "AWS::SSM::Parameter" Properties: Name: "/stvblog/finalOutput" Type: "String" Value: "finalOutput" DataType: "text" Tier: "Standard" stvblogtranslationOutputParameter: Type: "AWS::SSM::Parameter" Properties: Name: "/stvblog/translationOutput" Type: "String" Value: "translationOutput" DataType: "text" Tier: "Standard" stvblogtranslationInputParameter: Type: "AWS::SSM::Parameter" Properties: Name: "/stvblog/translationInput" Type: "String" Value: "translationInput" DataType: "text" Tier: "Standard" stvblogsubtitleOutputParameter: Type: "AWS::SSM::Parameter" Properties: Name: "/stvblog/subtitleOutput" Type: "String" Value: "subtitleOutput" DataType: "text" Tier: "Standard" stvblogsubtitleInputParameter: Type: "AWS::SSM::Parameter" Properties: Name: "/stvblog/subtitleInput" Type: "String" Value: "subtitleInput" DataType: "text" Tier: "Standard" ############## # Cloudfront # ############## stvblogCloudFrontDistribution: Type: "AWS::CloudFront::Distribution" DependsOn: stvblogLogBucket Properties: DistributionConfig: Origins: - ConnectionAttempts: 3 ConnectionTimeout: 10 DomainName: !Sub "${stvblogBucket}.s3.amazonaws.com" Id: !Sub "S3-${stvblogBucket}" OriginPath: "" S3OriginConfig: OriginAccessIdentity: !Sub "origin-access-identity/cloudfront/${stvblogCloudFrontOriginAccessIdentity}" - ConnectionAttempts: 3 ConnectionTimeout: 10 DomainName: !Sub "${stvblogAppBucket}.s3.amazonaws.com" Id: !Sub "S3-${stvblogAppBucket}" OriginPath: "" S3OriginConfig: OriginAccessIdentity: !Sub "origin-access-identity/cloudfront/${stvblogCloudFrontOriginAccessIdentity}" OriginGroups: Quantity: 0 DefaultCacheBehavior: AllowedMethods: - "HEAD" - "GET" - "OPTIONS" CachedMethods: - "HEAD" - "GET" - "OPTIONS" Compress: false CachePolicyId: "658327ea-f89d-4fab-a63d-7e88639e58f6" OriginRequestPolicyId: "88a5eaf4-2fd4-4709-b370-b4c650ea3fcf" ForwardedValues: QueryString: false Cookies: Forward: none SmoothStreaming: false TargetOriginId: !Sub "S3-${stvblogBucket}" ViewerProtocolPolicy: "allow-all" CacheBehaviors: - AllowedMethods: - "HEAD" - "DELETE" - "POST" - "GET" - "OPTIONS" - "PUT" - "PATCH" Compress: false CachePolicyId: "4135ea2d-6df8-44a3-9df3-4b5a84be39ad" OriginRequestPolicyId: "88a5eaf4-2fd4-4709-b370-b4c650ea3fcf" ForwardedValues: QueryString: false Cookies: Forward: none PathPattern: "*html" SmoothStreaming: false TargetOriginId: !Sub "S3-${stvblogAppBucket}" ViewerProtocolPolicy: "allow-all" - AllowedMethods: - "HEAD" - "GET" Compress: false CachePolicyId: "4135ea2d-6df8-44a3-9df3-4b5a84be39ad" OriginRequestPolicyId: "88a5eaf4-2fd4-4709-b370-b4c650ea3fcf" ForwardedValues: QueryString: false Cookies: Forward: none PathPattern: "*js" SmoothStreaming: false TargetOriginId: !Sub "S3-${stvblogAppBucket}" ViewerProtocolPolicy: "allow-all" Comment: "" PriceClass: "PriceClass_100" Enabled: true ViewerCertificate: CloudFrontDefaultCertificate: true MinimumProtocolVersion: "TLSv1.2_2021" Restrictions: GeoRestriction: RestrictionType: "none" HttpVersion: "http2" DefaultRootObject: "" IPV6Enabled: true Logging: Bucket: !Sub "${stvblogLogBucket}.s3.amazonaws.com" Prefix: "logs/" stvblogCloudFrontOriginAccessIdentity: Type: "AWS::CloudFront::CloudFrontOriginAccessIdentity" Properties: CloudFrontOriginAccessIdentityConfig: Comment: !Sub "access-identity-stvblog" ########### # Outputs # ########### Outputs: FrontendUrl: Description: "stvblog Front End URL" Value: !Join [ "/", [!GetAtt stvblogCloudFrontDistribution.DomainName, "index.html"]] HttpApiUrl: Description: URL of your API endpoint Value: !Sub "https://${stvblogJobV2Api}.execute-api.${AWS::Region}.${AWS::URLSuffix}" S3UploadBucketName: Description: "S3 Bucket Name for uploadBucket variable in frontend scripts.js file" Value: !Ref stvblogInBucket S3AppBucketName: Description: "S3 Bucket Name for uploading the frontend application files" Value: !Ref stvblogAppBucket