diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ab34fb7..57b6e5ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,29 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [6.0.0] - 2022-10-17 +### Added +- Added cdk infrastructure in source/cdk directory +- Defined resources for cdk stack in source/cdk/lib/vod-stack.ts +- Added snapshot test to source/cdk/test directory +- Added SolutionId tag to resources +- Added cdk nag rule suppressions +- Added Service Catalog AppRegistry configuration + +### Changed +- Removed CloudFormation template video-on-demand-on-aws.yaml +- Upgrade path from old versions require a delete and re-deploy since moving to CDK +- Use CachePolicy instead of ForwardedValues(deprecated) for cloudfront distribution +- Use @aws-solutions-constructs/aws-cloudfront-s3 construct for cloudfront distribution and destination bucket origin +- Updated deployment/build-s3-dist.sh to output cdk nag errors +- Updated to uuid v9 + +### Fixed +- Fixed mergeSettingsWithDefault arguments. Closes this issue (https://github.com/aws-solutions/video-on-demand-on-aws/issues/137) + +### Contributors +* @sandimciin + ## [5.3.1] - 2021-12-16 ### Fixed - Fixed spelling errors in CloudFormation template. diff --git a/README.md b/README.md index 766e5d5c..cfce61a8 100644 --- a/README.md +++ b/README.md @@ -145,7 +145,7 @@ For more detail please see [Accelerated Transcoding](https://docs.aws.amazon.com ## Source code -### Node.js 12 +### Node.js 14 * **archive-source:** Lambda function to tag the source video in s3 to enable the Glacier lifecycle policy. * **custom-resource:** Lambda backed CloudFormation custom resource to deploy MediaConvert templates configure S3 event notifications. * **dynamo:** Lambda function to Update DynamoDB. @@ -157,7 +157,7 @@ For more detail please see [Accelerated Transcoding](https://docs.aws.amazon.com * **profiler:** Lambda function used to send publish and/or error notifications. * **step-functions:** Lambda function to trigger AWS Step Functions. -### Python 3.7 +### Python 3.8 * **mediainfo:** Lambda function to run [mediainfo](https://mediaarea.net/en/MediaInfo) on an S3 signed url. > ./source/mediainfo/bin/mediainfo must be made executable before deploying to lambda. @@ -168,7 +168,7 @@ To make changes to the solution, download or clone this repo, update the source ### Prerequisites: * [AWS Command Line Interface](https://aws.amazon.com/cli/) -* Node.js 12.x or later +* Node.js 14.x or later * Python 3.8 or later ### 1. Running unit tests for customization @@ -226,7 +226,9 @@ aws s3api head-bucket --bucket my-bucket-us-east-1 --expected-bucket-owner / +aws s3 sync ./global-s3-assets/ s3://my-bucket-us-east-1/video-on-demand-on-aws// ``` ### 5. Launch the CloudFormation template. diff --git a/deployment/build-s3-dist.sh b/deployment/build-s3-dist.sh index e545a1ce..1481a65f 100755 --- a/deployment/build-s3-dist.sh +++ b/deployment/build-s3-dist.sh @@ -1,63 +1,64 @@ #!/bin/bash # -# This assumes all of the OS-level configuration has been completed and git repo has already been cloned +# This script will perform the following tasks: +# 1. Remove any old dist files from previous runs. +# 2. Install dependencies for the cdk-solution-helper; responsible for +# converting standard 'cdk synth' output into solution assets. +# 3. Build and synthesize your CDK project. +# 4. Run the cdk-solution-helper on template outputs and organize +# those outputs into the /global-s3-assets folder. +# 5. Organize source code artifacts into the /regional-s3-assets folder. +# 6. Remove any temporary files used for staging. # # This script should be run from the repo's deployment directory # cd deployment -# ./build-s3-dist.sh source-bucket-base-name trademarked-solution-name version-code +# ./build-s3-dist.sh source-bucket-base-name solution-name version-code template-bucket-name # -# Paramenters: +# Parameters: # - source-bucket-base-name: Name for the S3 bucket location where the template will source the Lambda # code from. The template will append '-[region_name]' to this bucket name. # For example: ./build-s3-dist.sh solutions my-solution v1.0.0 # The template will then expect the source code to be located in the solutions-[region_name] bucket -# -# - trademarked-solution-name: name of the solution for consistency -# +# - solution-name: name of the solution for consistency # - version-code: version of the package +[ "$DEBUG" == 'true' ] && set -x +set -e # Check to see if input has been provided: if [ -z "$1" ] || [ -z "$2" ] || [ -z "$3" ]; then - echo "Please provide the base source bucket name, trademark approved solution name and version where the lambda code will eventually reside." + echo "Please provide all required parameters for the build script" echo "For example: ./build-s3-dist.sh solutions trademarked-solution-name v1.0.0" exit 1 fi -set -e +bucket_name="$1" +solution_name="$2" +solution_version="$3" # Get reference for all important folders template_dir="$PWD" +staging_dist_dir="$template_dir/staging" template_dist_dir="$template_dir/global-s3-assets" build_dist_dir="$template_dir/regional-s3-assets" source_dir="$template_dir/../source" echo "------------------------------------------------------------------------------" -echo "Rebuild distribution" +echo "[Init] Remove any old dist files from previous runs" echo "------------------------------------------------------------------------------" rm -rf $template_dist_dir mkdir -p $template_dist_dir + rm -rf $build_dist_dir mkdir -p $build_dist_dir +rm -rf $staging_dist_dir +mkdir -p $staging_dist_dir + echo "------------------------------------------------------------------------------" -echo "CloudFormation Template" +echo "[Init] Install dependencies for the cdk-solution-helper" echo "------------------------------------------------------------------------------" -cp $template_dir/video-on-demand-on-aws.yaml $template_dist_dir/video-on-demand-on-aws.template - -replace="s/%%BUCKET_NAME%%/$1/g" -echo "sed -i -e $replace" -sed -i -e $replace $template_dist_dir/video-on-demand-on-aws.template - -replace="s/%%SOLUTION_NAME%%/$2/g" -echo "sed -i -e $replace" -sed -i -e $replace $template_dist_dir/video-on-demand-on-aws.template - -replace="s/%%VERSION%%/$3/g" -echo "sed -i -e $replace" -sed -i -e $replace $template_dist_dir/video-on-demand-on-aws.template -sed -i -e $replace $template_dir/../README.md - -cp $template_dist_dir/video-on-demand-on-aws.template $build_dist_dir/ +cd $template_dir/cdk-solution-helper +npm install --production echo "------------------------------------------------------------------------------" echo "Download mediainfo binary for AWS Lambda" @@ -70,34 +71,77 @@ mv LICENSE bin/ chmod +x ./bin/mediainfo rm -r MediaInfo_CLI_20.09_Lambda.zip -cd $source_dir/ echo "------------------------------------------------------------------------------" -echo "Lambda Functions" +echo "[Synth] CDK Project" +echo "------------------------------------------------------------------------------" +# Make sure user has the newest CDK version +npm uninstall -g aws-cdk && npm install -g aws-cdk@2 + +cd $source_dir/cdk +npm install +cdk synth --output=$staging_dist_dir +if [ $? -ne 0 ] +then + echo "******************************************************************************" + echo "cdk-nag found errors" + echo "******************************************************************************" + exit 1 +fi + +cd $staging_dist_dir +rm tree.json manifest.json cdk.out + +echo "------------------------------------------------------------------------------" +echo "Run Cdk Helper and update template placeholders" echo "------------------------------------------------------------------------------" +mv VideoOnDemand.template.json $template_dist_dir/$solution_name.template -for folder in */ ; do - cd "$folder" +node $template_dir/cdk-solution-helper/index - function_name=${PWD##*/} - zip_path="$build_dist_dir/$function_name.zip" +for file in $template_dist_dir/*.template +do + replace="s/%%BUCKET_NAME%%/$bucket_name/g" + sed -i -e $replace $file - echo "Creating deployment package for $function_name at $zip_path" + replace="s/%%SOLUTION_NAME%%/$solution_name/g" + sed -i -e $replace $file - if [ -e "package.json" ]; then - rm -rf node_modules/ - npm i --production + replace="s/%%VERSION%%/$solution_version/g" + sed -i -e $replace $file +done - zip -q -r9 $zip_path . - elif [ -e "setup.py" ]; then - # If you're running this command on macOS and Python3 has been installed using Homebrew, you might see this issue: - # DistutilsOptionError: must supply either home or prefix/exec-prefix - # Please follow the workaround suggested on this StackOverflow answer: https://stackoverflow.com/a/4472877 - python3 setup.py build_pkg --zip-path=$zip_path +echo "------------------------------------------------------------------------------" +echo "[Packing] Source code artifacts" +echo "------------------------------------------------------------------------------" +# ... For each asset.* source code artifact in the temporary /staging folder... +cd $staging_dist_dir +for d in `find . -mindepth 1 -maxdepth 1 -type d`; do + # Rename the artifact, removing the period for handler compatibility + pfname="$(basename -- $d)" + fname="$(echo $pfname | sed -e 's/\.//g')" + mv $d $fname + + # Zip artifacts from asset folder + cd $fname + rm -rf node_modules/ + #rm -rf coverage/ + if [ -f "package.json" ] + then + npm ci --production fi - + zip -rq ../$fname.zip * cd .. + + # Copy the zipped artifact from /staging to /regional-s3-assets + mv $fname.zip $build_dist_dir done +echo "------------------------------------------------------------------------------" +echo "[Cleanup] Remove temporary files" +echo "------------------------------------------------------------------------------" +rm -rf $staging_dist_dir +rm -f $template_dist_dir/*.template-e + echo "------------------------------------------------------------------------------" echo "S3 Packaging Complete" echo "------------------------------------------------------------------------------" diff --git a/deployment/cdk-solution-helper/README.md b/deployment/cdk-solution-helper/README.md new file mode 100644 index 00000000..39c1c7ed --- /dev/null +++ b/deployment/cdk-solution-helper/README.md @@ -0,0 +1,152 @@ +# cdk-solution-helper + +A lightweight helper function that cleans-up synthesized templates from the AWS Cloud Development Kit (CDK) and prepares +them for use with the AWS Solutions publishing pipeline. This function performs the following tasks: + +#### Lambda function preparation + +Replaces the AssetParameter-style properties that identify source code for Lambda functions with the common variables +used by the AWS Solutions publishing pipeline. + +- `Code.S3Bucket` is assigned the `%%BUCKET_NAME%%` placeholder value. +- `Code.S3Key` is assigned the `%%SOLUTION_NAME%%`/`%%VERSION%%` placeholder value. +- `Handler` is given a prefix identical to the artifact hash, enabling the Lambda function to properly find the handler in the extracted source code package. + +These placeholders are then replaced with the appropriate values using the default find/replace operation run by the pipeline. + +Before: +``` +"examplefunction67F55935": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": { + "Ref": "AssetParametersd513e93e266931de36e1c7e79c27b196f84ab928fce63d364d9152ca501551f7S3Bucket54E71A95" + }, + "S3Key": { + "Fn::Join": [ + "", + [ + { + "Fn::Select": [ + 0, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParametersd513e93e266931de36e1c7e79c27b196f84ab928fce63d364d9152ca501551f7S3VersionKeyC789D8B1" + } + ] + } + ] + }, + { + "Fn::Select": [ + 1, + { + "Fn::Split": [ + "||", + { + "Ref": "AssetParametersd513e93e266931de36e1c7e79c27b196f84ab928fce63d364d9152ca501551f7S3VersionKeyC789D8B1" + } + ] + } + ] + } + ] + ] + } + }, ... + Handler: "index.handler", ... +``` + +After helper function run: +``` +"examplefunction67F55935": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "%%BUCKET_NAME%%", + "S3Key": "%%SOLUTION_NAME%%/%%VERSION%%/assetd513e93e266931de36e1c7e79c27b196f84ab928fce63d364d9152ca501551f7.zip" + }, ... + "Handler": "assetd513e93e266931de36e1c7e79c27b196f84ab928fce63d364d9152ca501551f7/index.handler" +``` + +After build script run: +``` +"examplefunction67F55935": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "solutions", + "S3Key": "trademarked-solution-name/v1.0.0/asset.d513e93e266931de36e1c7e79c27b196f84ab928fce63d364d9152ca501551f7.zip" + }, ... + "Handler": "assetd513e93e266931de36e1c7e79c27b196f84ab928fce63d364d9152ca501551f7/index.handler" +``` + +After CloudFormation deployment: +``` +"examplefunction67F55935": { + "Type": "AWS::Lambda::Function", + "Properties": { + "Code": { + "S3Bucket": "solutions-us-east-1", + "S3Key": "trademarked-solution-name/v1.0.0/asset.d513e93e266931de36e1c7e79c27b196f84ab928fce63d364d9152ca501551f7.zip" + }, ... + "Handler": "assetd513e93e266931de36e1c7e79c27b196f84ab928fce63d364d9152ca501551f7/index.handler" +``` + +#### Template cleanup + +Cleans-up the parameters section and improves readability by removing the AssetParameter-style fields that would have +been used to specify Lambda source code properties. This allows solution-specific parameters to be highlighted and +removes unnecessary clutter. + +Before: +``` +"Parameters": { + "AssetParametersd513e93e266931de36e1c7e79c27b196f84ab928fce63d364d9152ca501551f7S3Bucket54E71A95": { + "Type": "String", + "Description": "S3 bucket for asset \"d513e93e266931de36e1c7e79c27b196f84ab928fce63d364d9152ca501551f7\"" + }, + "AssetParametersd513e93e266931de36e1c7e79c27b196f84ab928fce63d364d9152ca501551f7S3VersionKeyC789D8B1": { + "Type": "String", + "Description": "S3 key for asset version \"d513e93e266931de36e1c7e79c27b196f84ab928fce63d364d9152ca501551f7\"" + }, + "AssetParametersd513e93e266931de36e1c7e79c27b196f84ab928fce63d364d9152ca501551f7ArtifactHash7AA751FE": { + "Type": "String", + "Description": "Artifact hash for asset \"d513e93e266931de36e1c7e79c27b196f84ab928fce63d364d9152ca501551f7\"" + }, + "CorsEnabled" : { + "Description" : "Would you like to enable Cross-Origin Resource Sharing (CORS) for the image handler API? Select 'Yes' if so.", + "Default" : "No", + "Type" : "String", + "AllowedValues" : [ "Yes", "No" ] + }, + "CorsOrigin" : { + "Description" : "If you selected 'Yes' above, please specify an origin value here. A wildcard (*) value will support any origin.", + "Default" : "*", + "Type" : "String" + } + } + ``` + +After: +``` +"Parameters": { + "CorsEnabled" : { + "Description" : "Would you like to enable Cross-Origin Resource Sharing (CORS) for the image handler API? Select 'Yes' if so.", + "Default" : "No", + "Type" : "String", + "AllowedValues" : [ "Yes", "No" ] + }, + "CorsOrigin" : { + "Description" : "If you selected 'Yes' above, please specify an origin value here. A wildcard (*) value will support any origin.", + "Default" : "*", + "Type" : "String" + } + } + ``` + +*** +© Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. \ No newline at end of file diff --git a/deployment/cdk-solution-helper/index.js b/deployment/cdk-solution-helper/index.js new file mode 100644 index 00000000..9423f942 --- /dev/null +++ b/deployment/cdk-solution-helper/index.js @@ -0,0 +1,68 @@ +/** + * Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance + * with the License. A copy of the License is located at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions + * and limitations under the License. + */ + +// Imports +const fs = require('fs'); + +// Paths +const global_s3_assets = '../global-s3-assets'; + +// For each template in global_s3_assets ... +fs.readdirSync(global_s3_assets).forEach(file => { + // Import and parse template file + const raw_template = fs.readFileSync(`${global_s3_assets}/${file}`); + let template = JSON.parse(raw_template); + + // Clean-up Lambda function code dependencies + const resources = (template.Resources) ? template.Resources : {}; + const lambdaFunctions = Object.keys(resources).filter(function (key) { + return resources[key].Type === 'AWS::Lambda::Function'; + }); + + lambdaFunctions.forEach(function (f) { + const fn = template.Resources[f]; + let prop; + if (fn.Properties.hasOwnProperty('Code')) { + prop = fn.Properties.Code; + } else if (fn.Properties.hasOwnProperty('Content')) { + prop = fn.Properties.Content; + } + + if (prop.hasOwnProperty('S3Bucket')) { + // Set the S3 key reference + let artifactHash = Object.assign(prop.S3Key); + const assetPath = `asset${artifactHash}`; + prop.S3Key = `%%SOLUTION_NAME%%/%%VERSION%%/${assetPath}`; + + // Set the S3 bucket reference + prop.S3Bucket = { + 'Fn::Sub': '%%BUCKET_NAME%%-${AWS::Region}' + }; + } else { + console.warn(`No S3Bucket Property found for ${JSON.stringify(prop)}`); + } + }); + + // Clean-up parameters section + const parameters = (template.Parameters) ? template.Parameters : {}; + const assetParameters = Object.keys(parameters).filter(function (key) { + return key.includes('AssetParameters'); + }); + assetParameters.forEach(function (a) { + template.Parameters[a] = undefined; + }); + + // Output modified template file + const output_template = JSON.stringify(template, null, 2); + fs.writeFileSync(`${global_s3_assets}/${file}`, output_template); +}); \ No newline at end of file diff --git a/deployment/cdk-solution-helper/package.json b/deployment/cdk-solution-helper/package.json new file mode 100644 index 00000000..1831a48c --- /dev/null +++ b/deployment/cdk-solution-helper/package.json @@ -0,0 +1,7 @@ +{ + "name": "cdk-solution-helper", + "version": "0.1.0", + "dependencies": { + "fs": "0.0.1-security" + } +} diff --git a/deployment/run-unit-tests.sh b/deployment/run-unit-tests.sh index c8d40e4a..82742544 100755 --- a/deployment/run-unit-tests.sh +++ b/deployment/run-unit-tests.sh @@ -1,43 +1,101 @@ #!/bin/bash +[ "$DEBUG" == 'true' ] && set -x set -e -cd ../source/archive-source -npm test +prepare_jest_coverage_report() { + local component_name=$1 -cd ../custom-resource -npm test + if [ ! -d "coverage" ]; then + echo "ValidationError: Missing required directory coverage after running unit tests" + exit 129 + fi -cd ../dynamo -npm test + # prepare coverage reports + rm -fr coverage/lcov-report + mkdir -p $coverage_reports_top_path/jest + coverage_report_path=$coverage_reports_top_path/jest/$component_name + rm -fr $coverage_report_path + mv coverage $coverage_report_path +} -cd ../encode -npm test +run_javascript_test() { + local component_path=$1 + local component_name=$2 -cd ../error-handler -npm test + echo "------------------------------------------------------------------------------" + echo "[Test] Run javascript unit test with coverage for $component_name" + echo "------------------------------------------------------------------------------" + echo "cd $component_path" + cd $component_path -cd ../media-package-assets -npm test + # install dependencies + npm install --silent + # run unit tests + npm test -cd ../input-validate -npm test + # prepare coverage reports + prepare_jest_coverage_report $component_name +} -cd ../profiler -npm test +# Get reference for all important folders +template_dir="$PWD" +source_dir="$template_dir/../source" +coverage_reports_top_path=$source_dir/test/coverage-reports -cd ../sns-notification -npm test +# Test the attached Lambda function +declare -a lambda_packages=( + "cdk" + "custom-resource" + "archive-source" + "dynamo" + "encode" + "error-handler" + "input-validate" + "media-package-assets" + "output-validate" + "profiler" + "sns-notification" + "sqs-publish" + "step-functions" +) -cd ../step-functions -npm test +for lambda_package in "${lambda_packages[@]}" +do + rm -rf $source_dir/$lambda_package/coverage + mkdir $source_dir/$lambda_package/coverage + run_javascript_test $source_dir/$lambda_package $lambda_package + # Check the result of the test and exit if a failure is identified + if [ $? -eq 0 ] + then + echo "Test for $lambda_package passed" + else + echo "******************************************************************************" + echo "Lambda test FAILED for $lambda_package" + echo "******************************************************************************" + exit 1 + fi + +done + + +echo "------------------------------------------------------------------------------" +echo "[Test] Run python unit test with coverage for mediainfo" +echo "------------------------------------------------------------------------------" +echo "cd $source_dir/mediainfo" # If you're running these commands on macOS and Python3 has been installed using Homebrew, you might see this issue: # DistutilsOptionError: must supply either home or prefix/exec-prefix # Please follow the workaround suggested on this StackOverflow answer: https://stackoverflow.com/a/44728772 cd ../mediainfo -rm -rf ./pytests && mkdir ./pytests -cp lambda_function.py ./test*.py ./pytests -pip3 install boto3 -t ./pytests -python3 -m unittest discover -s ./pytests -v -rm -rf ./pytests +pip3 install boto3 -t ./pytests --quiet +pip3 install pytest --quiet +pip3 install pytest-cov --quiet + +rm -rf coverage +pytest --cov=. --cov-report xml:coverage/coverage.xml +# fix source file path +sed -i -- 's/filename\=\"/filename\=\"source\/mediainfo\//g' coverage/coverage.xml +mkdir -p $coverage_reports_top_path/pytest +mv coverage $coverage_reports_top_path/pytest/mediainfo +rm -rf pytests \ No newline at end of file diff --git a/deployment/video-on-demand-on-aws.yaml b/deployment/video-on-demand-on-aws.yaml deleted file mode 100644 index 93d5ae8b..00000000 --- a/deployment/video-on-demand-on-aws.yaml +++ /dev/null @@ -1,1806 +0,0 @@ -Description: "(SO0021) - Video On Demand workflow with AWS Step Functions, MediaConvert, MediaPackage, S3, CloudFront and DynamoDB. Version %%VERSION%%" - -Parameters: - - AdminEmail: - Description: Email address for SNS notifications (subscribed users will receive ingest, publishing, and error notifications) - Type: String - AllowedPattern: "^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$" - - WorkflowTrigger: - Description: How the workflow will be triggered (source video upload to S3 or source metadata file upload) - Type: String - Default: VideoFile - AllowedValues: - - VideoFile - - MetadataFile - - Glacier: - Description: If enabled, source assets will be tagged for archiving to Glacier or Glacier Deep Archive once the workflow is complete - Type: String - Default: DISABLED - AllowedValues: - - DISABLED - - GLACIER - - DEEP_ARCHIVE - - FrameCapture: - Description: If enabled, frame capture is added to the job submitted to MediaConvert - Type: String - Default: No - AllowedValues: - - Yes - - No - - EnableMediaPackage: - Description: If enabled, MediaPackage VOD will be included in the workflow - Type: String - Default: No - AllowedValues: - - Yes - - No - - EnableSns: - Description: Enable Ingest and Publish email notifications, error messages are not affected by this parameter. - Type: String - Default: Yes - AllowedValues: - - Yes - - No - - EnableSqs: - Description: Publish the workflow results to an SQS queue to ingest upstream - Type: String - Default: Yes - AllowedValues: - - Yes - - No - - AcceleratedTranscoding: - Description: Enable accelerated transcoding in AWS Elemental MediaConvert. PREFERRED will only use acceleration if the input files are supported. ENABLED acceleration is applied to all files (this will fail for unsupported file types) see MediaConvert Documentation for more detail https://docs.aws.amazon.com/mediaconvert/latest/ug/accelerated-transcoding.html - Type: String - Default: PREFERRED - AllowedValues: - - ENABLED - - DISABLED - - PREFERRED - -Metadata: - AWS::CloudFormation::Interface: - ParameterGroups: - - - Label: - default: "Workflow" - Parameters: - - AdminEmail - - WorkflowTrigger - - Glacier - - EnableSns - - EnableSqs - - - Label: - default: "AWS Elemental MediaConvert" - Parameters: - - FrameCapture - - AcceleratedTranscoding - - - Label: - default: "AWS Elemental MediaPackage" - Parameters: - - EnableMediaPackage - ParameterLabels: - AdminEmail: - default: Notification email address - Glacier: - default: Archive source content - WorkflowTrigger: - default: Workflow trigger - FrameCapture: - default: Enable Frame Capture - EnableMediaPackage: - default: Enable MediaPackage - AcceleratedTranscoding: - default: Accelerated Transcoding - EnableSns: - default: Enable SNS Notifications - EnableSqs: - default: Enable SQS Messaging - -Mappings: - SourceCode: - General: - S3Bucket: "%%BUCKET_NAME%%" - KeyPrefix: "%%SOLUTION_NAME%%/%%VERSION%%" - AnonymousData: - SendAnonymousData: - Data: Yes - -Conditions: - Metrics: !Equals [!FindInMap [AnonymousData, SendAnonymousData, Data], Yes] - IsMediaPackageEnabled: !Equals [!Ref EnableMediaPackage, Yes] - -Resources: - CustomResourceRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Version: 2012-10-17 - Statement: - - Effect: Allow - Principal: - Service: - - lambda.amazonaws.com - Action: - - sts:AssumeRole - Path: "/" - Policies: - - PolicyName: !Sub "${AWS::StackName}-custom-resource" - PolicyDocument: - Statement: - - Effect: Allow - Action: - - logs:CreateLogGroup - - logs:CreateLogStream - - logs:PutLogEvents - Resource: - - !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/*" - - Effect: Allow - Action: - - s3:PutBucketNotification - - s3:PutObject - - s3:PutObjectAcl - Resource: - - !GetAtt Source.Arn - - Effect: Allow - Action: - - mediaconvert:CreatePreset - - mediaconvert:CreateJobTemplate - - mediaconvert:DeletePreset - - mediaconvert:DeleteJobTemplate - - mediaconvert:DescribeEndpoints - - mediaconvert:ListJobTemplates - Resource: - - !Sub "arn:${AWS::Partition}:mediaconvert:${AWS::Region}:${AWS::AccountId}:*" - - Effect: Allow - Action: - - mediapackage-vod:DeleteAsset - - mediapackage-vod:DeletePackagingConfiguration - Resource: - - !Sub "arn:${AWS::Partition}:mediapackage-vod:${AWS::Region}:${AWS::AccountId}:assets/*" - - !Sub "arn:${AWS::Partition}:mediapackage-vod:${AWS::Region}:${AWS::AccountId}:packaging-configurations/packaging-config-*" - - Effect: Allow - Action: - - mediapackage-vod:DescribePackagingGroup - - mediapackage-vod:DeletePackagingGroup - Resource: - - !Sub "arn:${AWS::Partition}:mediapackage-vod:${AWS::Region}:${AWS::AccountId}:packaging-groups/${AWS::StackName}-packaging-group" - - Effect: Allow - Action: - - mediapackage-vod:CreatePackagingConfiguration - - mediapackage-vod:CreatePackagingGroup - - mediapackage-vod:ListAssets - - mediapackage-vod:ListPackagingConfigurations - - mediapackage-vod:ListPackagingGroups - Resource: "*" - - Effect: Allow - Action: - - cloudfront:GetDistributionConfig - - cloudfront:UpdateDistribution - Resource: - - !Sub "arn:${AWS::Partition}:cloudfront::${AWS::AccountId}:distribution/${CloudFront}" - Metadata: - cfn_nag: - rules_to_suppress: - - id: W11 - reason: "* is required to create CloudWatch logs and interact with MediaConvert / MediaPackage actions that do not support resource level permissions" - - id: W76 - reason: "All policies are required by the custom resource." - - StepFunctionsServiceRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Version: 2012-10-17 - Statement: - - Effect: Allow - Principal: - Service: - - !Sub "states.${AWS::Region}.amazonaws.com" - Action: - - sts:AssumeRole - Policies: - - PolicyName: !Sub "${AWS::StackName}-stepfunctions-service-role" - PolicyDocument: - Statement: - - Effect: Allow - Action: - - lambda:InvokeFunction - Resource: - - !Sub "arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:*" - Metadata: - cfn_nag: - rules_to_suppress: - - id: W11 - reason: "The * resource is required since the functions need to be created before the state machine" - - MediaConvertRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Version: 2012-10-17 - Statement: - - Effect: Allow - Principal: - Service: - - mediaconvert.amazonaws.com - Action: - - sts:AssumeRole - Policies: - - PolicyName: !Sub "${AWS::StackName}-mediatranscode-policy" - PolicyDocument: - Statement: - - Effect: Allow - Action: - - s3:GetObject - - s3:PutObject - Resource: - - !Sub "${Source.Arn}/*" - - !Sub "${Destination.Arn}/*" - - Effect: Allow - Action: - - execute-api:Invoke - Resource: - - !Sub "arn:${AWS::Partition}:execute-api:${AWS::Region}:${AWS::AccountId}:*" - Metadata: - cfn_nag: - rules_to_suppress: - - id: W11 - reason: "/* required to get/put objects to S3" - - MediaPackageVodRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Version: 2012-10-17 - Statement: - - Effect: Allow - Principal: - Service: - - mediapackage.amazonaws.com - Action: - - sts:AssumeRole - Policies: - - PolicyName: !Sub "${AWS::StackName}-mediapackagevod-policy" - PolicyDocument: - Statement: - - Effect: Allow - Action: - - s3:GetObject - - s3:GetBucketLocation - - s3:GetBucketRequestPayment - Resource: - - !Sub "${Destination.Arn}" - - !Sub "${Destination.Arn}/*" - Metadata: - cfn_nag: - rules_to_suppress: - - id: W11 - reason: "* is required to get objects from S3" - - S3LambdaInvokeVideo: - Type: AWS::Lambda::Permission - Properties: - FunctionName: !GetAtt StepFunctions.Arn - Action: lambda:InvokeFunction - Principal: s3.amazonaws.com - SourceAccount: !Ref AWS::AccountId - - CloudWatchLambdaInvokeErrors: - Type: AWS::Lambda::Permission - Properties: - FunctionName: !GetAtt ErrorHandler.Arn - Action: lambda:InvokeFunction - Principal: events.amazonaws.com - SourceArn: !GetAtt EncodeErrorRule.Arn - - CloudWatchLambdaInvokeCompletes: - Type: AWS::Lambda::Permission - Properties: - FunctionName: !GetAtt StepFunctions.Arn - Action: lambda:InvokeFunction - Principal: events.amazonaws.com - SourceArn: !GetAtt EncodeCompleteRule.Arn - - DestBucketPolicy: - Type: AWS::S3::BucketPolicy - Properties: - Bucket: !Ref Destination - PolicyDocument: - Statement: - - Effect: Allow - Action: - - s3:GetObject - Resource: !Sub "arn:${AWS::Partition}:s3:::${Destination}/*" - Principal: - CanonicalUser: !GetAtt DestinationOriginAccessIdentity.S3CanonicalUserId - - EncodeCompleteRule: - Type: AWS::Events::Rule - Properties: - Name: !Sub "${AWS::StackName}-EncodeComplete" - Description: MediaConvert Completed event rule - EventPattern: - source: - - aws.mediaconvert - detail: - status: - - COMPLETE - userMetadata: - workflow: - - !Ref AWS::StackName - Targets: - - Arn: !GetAtt StepFunctions.Arn - Id: !Sub "${AWS::StackName}-StepFunctions" - - EncodeErrorRule: - Type: AWS::Events::Rule - Properties: - Name: !Sub "${AWS::StackName}-EncodeError" - Description: MediaConvert Error event rule - EventPattern: - source: - - aws.mediaconvert - detail: - status: - - ERROR - userMetadata: - workflow: - - !Ref AWS::StackName - Targets: - - Arn: !GetAtt ErrorHandler.Arn - Id: !Sub "${AWS::StackName}-EncodeError" - - DynamoDBTable: - DeletionPolicy: Retain - UpdateReplacePolicy: Retain - Type: AWS::DynamoDB::Table - Properties: - BillingMode: PAY_PER_REQUEST - PointInTimeRecoverySpecification: - PointInTimeRecoveryEnabled: true - AttributeDefinitions: - - AttributeName: guid - AttributeType: S - - AttributeName: srcBucket - AttributeType: S - - AttributeName: startTime - AttributeType: S - KeySchema: - - AttributeName: guid - KeyType: HASH - GlobalSecondaryIndexes: - - IndexName: srcBucket-startTime-index - KeySchema: - - AttributeName: srcBucket - KeyType: HASH - - AttributeName: startTime - KeyType: RANGE - Projection: - ProjectionType: ALL - TableName: !Ref AWS::StackName - Metadata: - cfn_nag: - rules_to_suppress: - - id: W28 - reason: "Table name is set to the stack name" - - id: W74 - reason: " The DynamoDB table is configured to use the default encryption" - - Source: - DeletionPolicy: Retain - UpdateReplacePolicy: Retain - Type: AWS::S3::Bucket - Properties: - LoggingConfiguration: - DestinationBucketName: !Ref Logs - LogFilePrefix: s3-access/ - LifecycleConfiguration: - Rules: - - Id: !Sub "${AWS::StackName}-source-archive" - TagFilters: - - Key: !Ref AWS::StackName - Value: GLACIER - Status: Enabled - Transitions: - - TransitionInDays: 1 - StorageClass: GLACIER - - Id: !Sub "${AWS::StackName}-source-deep-archive" - TagFilters: - - Key: !Ref AWS::StackName - Value: DEEP_ARCHIVE - Status: Enabled - Transitions: - - TransitionInDays: 1 - StorageClass: DEEP_ARCHIVE - BucketEncryption: - ServerSideEncryptionConfiguration: - - ServerSideEncryptionByDefault: - SSEAlgorithm: AES256 - PublicAccessBlockConfiguration: - BlockPublicAcls: true - BlockPublicPolicy: true - IgnorePublicAcls: true - RestrictPublicBuckets: true - Metadata: - cfn_nag: - rules_to_suppress: - - id: W51 - reason: "Bucket does not need a bucket policy" - - Destination: - DeletionPolicy: Retain - UpdateReplacePolicy: Retain - Type: AWS::S3::Bucket - Properties: - LoggingConfiguration: - DestinationBucketName: !Ref Logs - LogFilePrefix: s3-access/ - CorsConfiguration: - CorsRules: - - AllowedMethods: [GET] - AllowedOrigins: ['*'] - AllowedHeaders: ['*'] - MaxAge: 3000 - PublicAccessBlockConfiguration: - BlockPublicAcls: true - BlockPublicPolicy: true - IgnorePublicAcls: true - RestrictPublicBuckets: true - BucketEncryption: - ServerSideEncryptionConfiguration: - - ServerSideEncryptionByDefault: - SSEAlgorithm: AES256 - - Logs: - DeletionPolicy: Retain - UpdateReplacePolicy: Retain - Type: AWS::S3::Bucket - Properties: - AccessControl: LogDeliveryWrite - BucketEncryption: - ServerSideEncryptionConfiguration: - - ServerSideEncryptionByDefault: - SSEAlgorithm: AES256 - PublicAccessBlockConfiguration: - BlockPublicAcls: true - BlockPublicPolicy: true - IgnorePublicAcls: true - RestrictPublicBuckets: true - Metadata: - cfn_nag: - rules_to_suppress: - - id: W35 - reason: "Used to store access logs for other buckets" - - id: W51 - reason: "Bucket does not need a bucket policy" - - SnsTopic: - Type: AWS::SNS::Topic - Properties: - DisplayName: !Sub "${AWS::StackName}-Notifications" - KmsMasterKeyId: alias/aws/sns - Subscription: - - Endpoint: !Ref AdminEmail - Protocol: email - - SqsQueue: - Type: AWS::SQS::Queue - Properties: - VisibilityTimeout: 120 - QueueName: !Sub ${AWS::StackName} - RedrivePolicy: - deadLetterTargetArn: !Sub ${SqsQueueDlq.Arn} - maxReceiveCount: 1 - KmsDataKeyReusePeriodSeconds: 300 - KmsMasterKeyId: alias/aws/sqs - - SqsQueueDlq: - Type: AWS::SQS::Queue - Properties: - VisibilityTimeout: 120 - QueueName: !Sub ${AWS::StackName}-dlq - KmsDataKeyReusePeriodSeconds: 300 - KmsMasterKeyId: alias/aws/sqs - - DestinationOriginAccessIdentity: - Type: AWS::CloudFront::CloudFrontOriginAccessIdentity - Properties: - CloudFrontOriginAccessIdentityConfig: - Comment: !Sub "access-identity-${Destination}" - - CloudFront: - Type: AWS::CloudFront::Distribution - Properties: - DistributionConfig: - Origins: - - DomainName: !Sub "${Destination}.s3.${AWS::Region}.amazonaws.com" - Id: vodS3Origin - S3OriginConfig: - OriginAccessIdentity: !Sub "origin-access-identity/cloudfront/${DestinationOriginAccessIdentity}" - Enabled: true - Logging: - IncludeCookies: false - Bucket: !GetAtt Logs.DomainName - Prefix: cloudfront-logs/ - DefaultCacheBehavior: - AllowedMethods: - - GET - - HEAD - Compress: true - MaxTTL: 0 - MinTTL: 0 - ViewerProtocolPolicy: "redirect-to-https" - DefaultTTL: 0 - TargetOriginId: vodS3Origin - ForwardedValues: - QueryString: false - Cookies: - Forward: none - Headers: - - Origin - - Access-Control-Request-Method - - Access-Control-Request-Headers - PriceClass: PriceClass_100 - ViewerCertificate: - CloudFrontDefaultCertificate: true - Metadata: - cfn_nag: - rules_to_suppress: - - id: W70 - reason: "CloudFront automatically sets the security policy to TLSv1 when the distribution uses the CloudFront domain name (CloudFrontDefaultCertificate=true)" - - S3Config: - DependsOn: CloudFront - Type: Custom::S3 - Properties: - ServiceToken: !GetAtt CustomResource.Arn - Source: !Ref Source - IngestArn: !GetAtt StepFunctions.Arn - Resource: S3Notification - WorkflowTrigger: !Ref WorkflowTrigger - - MediaConvertEndPoint: - Type: Custom::LoadLambda - Properties: - ServiceToken: !GetAtt CustomResource.Arn - Resource: EndPoint - - MediaConvertTemplates: - Type: Custom::LoadLambda - Properties: - ServiceToken: !GetAtt CustomResource.Arn - Resource: MediaConvertTemplates - StackName: !Ref AWS::StackName - EndPoint: !GetAtt MediaConvertEndPoint.EndpointUrl - EnableMediaPackage: !Ref EnableMediaPackage - EnableNewTemplates: true - - MediaPackageVod: - Type: Custom::LoadLambda - Properties: - ServiceToken: !GetAtt CustomResource.Arn - Resource: MediaPackageVod - StackName: !Ref AWS::StackName - GroupId: !Sub "${AWS::StackName}-packaging-group" - PackagingConfigurations: "HLS,DASH,MSS,CMAF" - DistributionId: !Ref CloudFront - EnableMediaPackage: !Ref EnableMediaPackage - - CustomResource: - Type: AWS::Lambda::Function - Properties: - FunctionName: !Sub "${AWS::StackName}-custom-resource" - Description: Used to deploy resources not supported by CloudFormation - Handler: index.handler - Role: !GetAtt CustomResourceRole.Arn - Code: - S3Bucket: !Join ["-", [!FindInMap ["SourceCode", "General", "S3Bucket"], Ref: "AWS::Region"]] - S3Key: !Join ["/", [!FindInMap ["SourceCode", "General", "KeyPrefix"], "custom-resource.zip"]] - Runtime: nodejs12.x - Timeout: 180 - Environment: - Variables: - AWS_NODEJS_CONNECTION_REUSE_ENABLED: "1" - SOLUTION_IDENTIFIER: "AwsSolution/SO0021/%%VERSION%%" - Metadata: - cfn_nag: - rules_to_suppress: - - id: W89 - reason: "Lambda functions do not need a VPC" - - id: W92 - reason: "Lambda do not need ReservedConcurrentExecutions in this case" - - StepFunctionsRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Version: 2012-10-17 - Statement: - - Effect: Allow - Principal: - Service: - - lambda.amazonaws.com - Action: - - sts:AssumeRole - Policies: - - PolicyName: !Sub "${AWS::StackName}-step-functions-role" - PolicyDocument: - Statement: - - Effect: Allow - Action: - - states:StartExecution - Resource: - - !Sub "arn:${AWS::Partition}:states:${AWS::Region}:${AWS::AccountId}:stateMachine:${AWS::StackName}-ingest" - - !Sub "arn:${AWS::Partition}:states:${AWS::Region}:${AWS::AccountId}:stateMachine:${AWS::StackName}-process" - - !Sub "arn:${AWS::Partition}:states:${AWS::Region}:${AWS::AccountId}:stateMachine:${AWS::StackName}-publish" - - Effect: Allow - Action: - - lambda:InvokeFunction - Resource: - - !Sub "arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:${AWS::StackName}-error-handler" - - Effect: Allow - Action: - - logs:CreateLogGroup - - logs:CreateLogStream - - logs:PutLogEvents - Resource: - - !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/*" - Metadata: - cfn_nag: - rules_to_suppress: - - id: W11 - reason: "* is used so that the Lambda function can create log groups" - - StepFunctions: - Type: AWS::Lambda::Function - Properties: - FunctionName: !Sub "${AWS::StackName}-step-functions" - Description: Creates a unique identifer (GUID) and executes the Ingest StateMachine - Handler: index.handler - Role: !GetAtt StepFunctionsRole.Arn - Code: - S3Bucket: !Join ["-", [!FindInMap ["SourceCode", "General", "S3Bucket"], Ref: "AWS::Region"]] - S3Key: !Join ["/", [!FindInMap ["SourceCode", "General", "KeyPrefix"], "step-functions.zip"]] - Runtime: nodejs12.x - Timeout: 120 - Environment: - Variables: - AWS_NODEJS_CONNECTION_REUSE_ENABLED: "1" - IngestWorkflow: !Sub "arn:${AWS::Partition}:states:${AWS::Region}:${AWS::AccountId}:stateMachine:${AWS::StackName}-ingest" - ProcessWorkflow: !Sub "arn:${AWS::Partition}:states:${AWS::Region}:${AWS::AccountId}:stateMachine:${AWS::StackName}-process" - PublishWorkflow: !Sub "arn:${AWS::Partition}:states:${AWS::Region}:${AWS::AccountId}:stateMachine:${AWS::StackName}-publish" - ErrorHandler: !GetAtt ErrorHandler.Arn - SOLUTION_IDENTIFIER: "AwsSolution/SO0021/%%VERSION%%" - Metadata: - cfn_nag: - rules_to_suppress: - - id: W89 - reason: "Lambda functions do not need a VPC" - - id: W92 - reason: "Lambda do not need ReservedConcurrentExecutions in this case" - - InputValidateRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Version: 2012-10-17 - Statement: - - Effect: Allow - Principal: - Service: - - lambda.amazonaws.com - Action: - - sts:AssumeRole - Policies: - - PolicyName: !Sub "${AWS::StackName}-input-validate-role" - PolicyDocument: - Statement: - - Effect: Allow - Action: - - s3:GetObject - Resource: - - !Sub "${Source.Arn}/*" - - Effect: Allow - Action: - - lambda:InvokeFunction - Resource: - - !Sub "arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:${AWS::StackName}-error-handler" - - Effect: Allow - Action: - - logs:CreateLogGroup - - logs:CreateLogStream - - logs:PutLogEvents - Resource: - - !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/*" - Metadata: - cfn_nag: - rules_to_suppress: - - id: W11 - reason: "* is used so that the Lambda function can create log groups; S3 action is limited to one bucket" - - InputValidate: - Type: AWS::Lambda::Function - Properties: - FunctionName: !Sub "${AWS::StackName}-input-validate" - Description: Validates the input given to the workflow - Handler: index.handler - Role: !GetAtt InputValidateRole.Arn - Code: - S3Bucket: !Join ["-", [!FindInMap ["SourceCode", "General", "S3Bucket"], Ref: "AWS::Region"]] - S3Key: !Join ["/", [!FindInMap ["SourceCode", "General", "KeyPrefix"], "input-validate.zip"]] - Runtime: nodejs12.x - Timeout: 120 - Environment: - Variables: - AWS_NODEJS_CONNECTION_REUSE_ENABLED: "1" - ErrorHandler: !GetAtt ErrorHandler.Arn - WorkflowName: !Ref AWS::StackName - Source: !Ref Source - Destination: !Ref Destination - FrameCapture: !Ref FrameCapture - ArchiveSource: !Ref Glacier - MediaConvert_Template_2160p: - !If - - IsMediaPackageEnabled - - !Sub "${AWS::StackName}_Ott_2160p_Avc_Aac_16x9_mvod_no_preset" - - !Sub "${AWS::StackName}_Ott_2160p_Avc_Aac_16x9_qvbr_no_preset" - MediaConvert_Template_1080p: - !If - - IsMediaPackageEnabled - - !Sub "${AWS::StackName}_Ott_1080p_Avc_Aac_16x9_mvod_no_preset" - - !Sub "${AWS::StackName}_Ott_1080p_Avc_Aac_16x9_qvbr_no_preset" - MediaConvert_Template_720p: - !If - - IsMediaPackageEnabled - - !Sub "${AWS::StackName}_Ott_720p_Avc_Aac_16x9_mvod_no_preset" - - !Sub "${AWS::StackName}_Ott_720p_Avc_Aac_16x9_qvbr_no_preset" - CloudFront: !GetAtt CloudFront.DomainName - EnableMediaPackage: !Ref EnableMediaPackage - InputRotate: DEGREE_0 - EnableSns: !Ref EnableSns - EnableSqs: !Ref EnableSqs - AcceleratedTranscoding: !Ref AcceleratedTranscoding - SOLUTION_IDENTIFIER: "AwsSolution/SO0021/%%VERSION%%" - Metadata: - cfn_nag: - rules_to_suppress: - - id: W89 - reason: "Lambda functions do not need a VPC" - - id: W92 - reason: "Lambda do not need ReservedConcurrentExecutions in this case" - - MediainfoRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Version: 2012-10-17 - Statement: - - Effect: Allow - Principal: - Service: - - lambda.amazonaws.com - Action: - - sts:AssumeRole - Policies: - - PolicyName: !Sub "${AWS::StackName}-mediainfo-role" - PolicyDocument: - Statement: - - Effect: Allow - Action: - - s3:GetObject - Resource: - - !Sub "${Source.Arn}/*" - - Effect: Allow - Action: - - lambda:InvokeFunction - Resource: - - !Sub "arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:${AWS::StackName}-error-handler" - - Effect: Allow - Action: - - logs:CreateLogGroup - - logs:CreateLogStream - - logs:PutLogEvents - Resource: - - !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/*" - Metadata: - cfn_nag: - rules_to_suppress: - - id: W11 - reason: "* is limited to one S3 bucket" - - - Mediainfo: - Type: AWS::Lambda::Function - Properties: - FunctionName: !Sub "${AWS::StackName}-mediainfo" - Description: Runs mediainfo on a pre-signed S3 URL - Handler: lambda_function.lambda_handler - Role: !GetAtt MediainfoRole.Arn - Code: - S3Bucket: !Join ["-", [!FindInMap ["SourceCode", "General", "S3Bucket"], Ref: "AWS::Region"]] - S3Key: !Join ["/", [!FindInMap ["SourceCode", "General", "KeyPrefix"], "mediainfo.zip"]] - Runtime: python3.7 - Timeout: 120 - Environment: - Variables: - ErrorHandler: !GetAtt ErrorHandler.Arn - SOLUTION_IDENTIFIER: "AwsSolution/SO0021/%%VERSION%%" - Metadata: - cfn_nag: - rules_to_suppress: - - id: W89 - reason: "Lambda functions do not need a VPC" - - id: W92 - reason: "Lambda do not need ReservedConcurrentExecutions in this case" - - DynamoUpdateRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Version: 2012-10-17 - Statement: - - Effect: Allow - Principal: - Service: - - lambda.amazonaws.com - Action: - - sts:AssumeRole - Policies: - - PolicyName: !Sub "${AWS::StackName}-dynamo-role" - PolicyDocument: - Statement: - - Effect: Allow - Action: - - lambda:InvokeFunction - Resource: - - !Sub "arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:${AWS::StackName}-error-handler" - - Effect: Allow - Action: - - dynamodb:UpdateItem - Resource: - - !Sub "arn:${AWS::Partition}:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${DynamoDBTable}" - - Effect: Allow - Action: - - logs:CreateLogGroup - - logs:CreateLogStream - - logs:PutLogEvents - Resource: - - !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/*" - Metadata: - cfn_nag: - rules_to_suppress: - - id: W11 - reason: "* is used so that the Lambda function can create log groups" - - DynamodbUpdate: - Type: AWS::Lambda::Function - Properties: - FunctionName: !Sub "${AWS::StackName}-dynamo" - Description: Updates DynamoDB with event data - Handler: index.handler - Role: !GetAtt DynamoUpdateRole.Arn - Code: - S3Bucket: !Join ["-", [!FindInMap ["SourceCode", "General", "S3Bucket"], Ref: "AWS::Region"]] - S3Key: !Join ["/", [!FindInMap ["SourceCode", "General", "KeyPrefix"], "dynamo.zip"]] - Runtime: nodejs12.x - Timeout: 120 - Environment: - Variables: - AWS_NODEJS_CONNECTION_REUSE_ENABLED: "1" - DynamoDBTable: !Ref DynamoDBTable - ErrorHandler: !GetAtt ErrorHandler.Arn - SOLUTION_IDENTIFIER: "AwsSolution/SO0021/%%VERSION%%" - Metadata: - cfn_nag: - rules_to_suppress: - - id: W89 - reason: "Lambda functions do not need a VPC" - - id: W92 - reason: "Lambda do not need ReservedConcurrentExecutions in this case" - - ProfilerRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Version: 2012-10-17 - Statement: - - Effect: Allow - Principal: - Service: - - lambda.amazonaws.com - Action: - - sts:AssumeRole - Policies: - - PolicyName: !Sub "${AWS::StackName}-profiler-role" - PolicyDocument: - Statement: - - Effect: Allow - Action: - - lambda:InvokeFunction - Resource: - - !Sub "arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:${AWS::StackName}-error-handler" - - Effect: Allow - Action: - - dynamodb:GetItem - Resource: - - !Sub "arn:${AWS::Partition}:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${DynamoDBTable}" - - Effect: Allow - Action: - - logs:CreateLogGroup - - logs:CreateLogStream - - logs:PutLogEvents - Resource: - - !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/*" - Metadata: - cfn_nag: - rules_to_suppress: - - id: W11 - reason: "* is used so that the Lambda function can create log groups" - - Profiler: - Type: AWS::Lambda::Function - Properties: - FunctionName: !Sub "${AWS::StackName}-profiler" - Description: Sets an EncodeProfile based on mediainfo output - Handler: index.handler - Role: !GetAtt ProfilerRole.Arn - Code: - S3Bucket: !Join ["-", [!FindInMap ["SourceCode", "General", "S3Bucket"], Ref: "AWS::Region"]] - S3Key: !Join ["/", [!FindInMap ["SourceCode", "General", "KeyPrefix"], "profiler.zip"]] - Runtime: nodejs12.x - Timeout: 120 - Environment: - Variables: - AWS_NODEJS_CONNECTION_REUSE_ENABLED: "1" - DynamoDBTable: !Ref DynamoDBTable - ErrorHandler: !GetAtt ErrorHandler.Arn - SOLUTION_IDENTIFIER: "AwsSolution/SO0021/%%VERSION%%" - Metadata: - cfn_nag: - rules_to_suppress: - - id: W89 - reason: "Lambda functions do not need a VPC" - - id: W92 - reason: "Lambda do not need ReservedConcurrentExecutions in this case" - - EncodeRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Version: 2012-10-17 - Statement: - - Effect: Allow - Principal: - Service: - - lambda.amazonaws.com - Action: - - sts:AssumeRole - Policies: - - PolicyName: !Sub "${AWS::StackName}-encode-role" - PolicyDocument: - Statement: - - Effect: Allow - Action: - - lambda:InvokeFunction - Resource: - - !Sub "arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:${AWS::StackName}-error-handler" - - Effect: Allow - Action: - - mediaconvert:CreateJob - - mediaconvert:GetJobTemplate - Resource: - - !Sub "arn:${AWS::Partition}:mediaconvert:${AWS::Region}:${AWS::AccountId}:*" - - Effect: Allow - Action: - - iam:PassRole - Resource: - - !GetAtt MediaConvertRole.Arn - - Effect: Allow - Action: - - logs:CreateLogGroup - - logs:CreateLogStream - - logs:PutLogEvents - Resource: - - !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/*" - Metadata: - cfn_nag: - rules_to_suppress: - - id: W11 - reason: "* is used so that the Lambda function can create log groups; MediaConvert job ids are randomly generated" - - Encode: - Type: AWS::Lambda::Function - Properties: - FunctionName: !Sub "${AWS::StackName}-encode" - Description: Creates a MediaConvert encode job - Handler: index.handler - Role: !GetAtt EncodeRole.Arn - Code: - S3Bucket: !Join ["-", [!FindInMap ["SourceCode", "General", "S3Bucket"], Ref: "AWS::Region"]] - S3Key: !Join ["/", [!FindInMap ["SourceCode", "General", "KeyPrefix"], "encode.zip"]] - Runtime: nodejs12.x - Timeout: 120 - Environment: - Variables: - AWS_NODEJS_CONNECTION_REUSE_ENABLED: "1" - ErrorHandler: !GetAtt ErrorHandler.Arn - MediaConvertRole: !GetAtt MediaConvertRole.Arn - EndPoint: !GetAtt MediaConvertEndPoint.EndpointUrl - SOLUTION_IDENTIFIER: "AwsSolution/SO0021/%%VERSION%%" - Metadata: - cfn_nag: - rules_to_suppress: - - id: W89 - reason: "Lambda functions do not need a VPC" - - id: W92 - reason: "Lambda do not need ReservedConcurrentExecutions in this case" - - OutputValidateRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Version: 2012-10-17 - Statement: - - Effect: Allow - Principal: - Service: - - lambda.amazonaws.com - Action: - - sts:AssumeRole - Policies: - - PolicyName: !Sub "${AWS::StackName}-output-validate-role" - PolicyDocument: - Statement: - - Effect: Allow - Action: - - lambda:InvokeFunction - Resource: - - !Sub "arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:${AWS::StackName}-error-handler" - - Effect: Allow - Action: - - dynamodb:GetItem - Resource: - - !Sub "arn:${AWS::Partition}:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${DynamoDBTable}" - - Effect: Allow - Action: - - s3:ListBucket - Resource: - - !GetAtt Destination.Arn - - Effect: Allow - Action: - - logs:CreateLogGroup - - logs:CreateLogStream - - logs:PutLogEvents - Resource: - - !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/*" - Metadata: - cfn_nag: - rules_to_suppress: - - id: W11 - reason: "* is used so that the Lambda function can create log groups" - - OutputValidate: - Type: AWS::Lambda::Function - Properties: - FunctionName: !Sub "${AWS::StackName}-output-validate" - Description: Parses MediaConvert job output - Handler: index.handler - Role: !GetAtt OutputValidateRole.Arn - Code: - S3Bucket: !Join ["-", [!FindInMap ["SourceCode", "General", "S3Bucket"], Ref: "AWS::Region"]] - S3Key: !Join ["/", [!FindInMap ["SourceCode", "General", "KeyPrefix"], "output-validate.zip"]] - Runtime: nodejs12.x - Timeout: 120 - Environment: - Variables: - AWS_NODEJS_CONNECTION_REUSE_ENABLED: "1" - DynamoDBTable: !Ref DynamoDBTable - ErrorHandler: !GetAtt ErrorHandler.Arn - EndPoint: !GetAtt MediaConvertEndPoint.EndpointUrl - SOLUTION_IDENTIFIER: "AwsSolution/SO0021/%%VERSION%%" - Metadata: - cfn_nag: - rules_to_suppress: - - id: W89 - reason: "Lambda functions do not need a VPC" - - id: W92 - reason: "Lambda do not need ReservedConcurrentExecutions in this case" - - ArchiveSourceRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Version: 2012-10-17 - Statement: - - Effect: Allow - Principal: - Service: - - lambda.amazonaws.com - Action: - - sts:AssumeRole - Policies: - - PolicyName: !Sub "${AWS::StackName}-archive-source-role" - PolicyDocument: - Statement: - - Effect: Allow - Action: - - s3:PutObjectTagging - Resource: - - !Sub "${Source.Arn}/*" - - Effect: Allow - Action: - - lambda:InvokeFunction - Resource: - - !Sub "arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:${AWS::StackName}-error-handler" - - Effect: Allow - Action: - - logs:CreateLogGroup - - logs:CreateLogStream - - logs:PutLogEvents - Resource: - - !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/*" - Metadata: - cfn_nag: - rules_to_suppress: - - id: W11 - reason: "* is used so that the Lambda function can create log groups; S3 action is limited to one bucket" - - ArchiveSource: - Type: AWS::Lambda::Function - Properties: - FunctionName: !Sub "${AWS::StackName}-archive-source" - Description: Updates tags on source files to enable Glacier - Handler: index.handler - Role: !GetAtt ArchiveSourceRole.Arn - Code: - S3Bucket: !Join ["-", [!FindInMap ["SourceCode", "General", "S3Bucket"], Ref: "AWS::Region"]] - S3Key: !Join ["/", [!FindInMap ["SourceCode", "General", "KeyPrefix"], "archive-source.zip"]] - Runtime: nodejs12.x - Timeout: 120 - Environment: - Variables: - AWS_NODEJS_CONNECTION_REUSE_ENABLED: "1" - ErrorHandler: !GetAtt ErrorHandler.Arn - SOLUTION_IDENTIFIER: "AwsSolution/SO0021/%%VERSION%%" - Metadata: - cfn_nag: - rules_to_suppress: - - id: W89 - reason: "Lambda functions do not need a VPC" - - id: W92 - reason: "Lambda do not need ReservedConcurrentExecutions in this case" - - SqsSendMessageRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Version: 2012-10-17 - Statement: - - - Effect: Allow - Principal: - Service: - - lambda.amazonaws.com - Action: - - sts:AssumeRole - Policies: - - - PolicyName: !Sub "${AWS::StackName}-sqs-publish-role" - PolicyDocument: - Statement: - - - Effect: Allow - Action: - - lambda:InvokeFunction - Resource: - - !Sub "arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:${AWS::StackName}-error-handler" - - - Effect: Allow - Action: - - sqs:SendMessage - Resource: - - !GetAtt SqsQueue.Arn - Condition: - Bool: - 'aws:SecureTransport': 'true' - - - Effect: Allow - Action: - - logs:CreateLogGroup - - logs:CreateLogStream - - logs:PutLogEvents - Resource: - - !Sub "arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/*" - Metadata: - cfn_nag: - rules_to_suppress: - - id: W11 - reason: "* is used so that the Lambda function can create log groups" - - SqsSendMessage: - Type: AWS::Lambda::Function - Properties: - FunctionName: !Sub ${AWS::StackName}-sqs-publish - Description: Publish the workflow results to an SQS queue - Handler: index.handler - Role: !GetAtt SqsSendMessageRole.Arn - Code: - S3Bucket: !Join ["-", [!FindInMap ["SourceCode", "General", "S3Bucket"], Ref: "AWS::Region"]] - S3Key: !Join ["/", [!FindInMap ["SourceCode", "General", "KeyPrefix"], "sqs-publish.zip"]] - Runtime: nodejs12.x - Timeout: 120 - Environment: - Variables: - AWS_NODEJS_CONNECTION_REUSE_ENABLED: "1" - ErrorHandler: !GetAtt ErrorHandler.Arn - SqsQueue: !Ref SqsQueue - SOLUTION_IDENTIFIER: "AwsSolution/SO0021/%%VERSION%%" - Metadata: - cfn_nag: - rules_to_suppress: - - id: W89 - reason: "Lambda functions do not need a VPC" - - id: W92 - reason: "Lambda do not need ReservedConcurrentExecutions in this case" - - SnsNotificationRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Version: 2012-10-17 - Statement: - - - Effect: Allow - Principal: - Service: - - lambda.amazonaws.com - Action: - - sts:AssumeRole - Policies: - - PolicyName: !Sub "${AWS::StackName}-sns-notification-role" - PolicyDocument: - Statement: - - Effect: Allow - Action: - - lambda:InvokeFunction - Resource: - - !Sub "arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:${AWS::StackName}-error-handler" - - Effect: Allow - Action: - - sns:Publish - Resource: - - !Ref SnsTopic - Condition: - Bool: - 'aws:SecureTransport': 'true' - - Effect: Allow - Action: - - logs:CreateLogGroup - - logs:CreateLogStream - - logs:PutLogEvents - Resource: - - !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/*" - Metadata: - cfn_nag: - rules_to_suppress: - - id: W11 - reason: "* is used so that the Lambda function can create log groups" - - SnsNotification: - Type: AWS::Lambda::Function - Properties: - FunctionName: !Sub "${AWS::StackName}-sns-notification" - Description: Sends a notification when the encode job is completed - Handler: index.handler - Role: !GetAtt SnsNotificationRole.Arn - Code: - S3Bucket: !Join ["-", [!FindInMap ["SourceCode", "General", "S3Bucket"], Ref: "AWS::Region"]] - S3Key: !Join ["/", [!FindInMap ["SourceCode", "General", "KeyPrefix"], "sns-notification.zip"]] - Runtime: nodejs12.x - Timeout: 120 - Environment: - Variables: - AWS_NODEJS_CONNECTION_REUSE_ENABLED: "1" - ErrorHandler: !GetAtt ErrorHandler.Arn - SnsTopic: !Ref SnsTopic - SOLUTION_IDENTIFIER: "AwsSolution/SO0021/%%VERSION%%" - Metadata: - cfn_nag: - rules_to_suppress: - - id: W89 - reason: "Lambda functions do not need a VPC" - - id: W92 - reason: "Lambda do not need ReservedConcurrentExecutions in this case" - - MediaPackageAssetsRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Version: 2012-10-17 - Statement: - - Effect: Allow - Principal: - Service: - - lambda.amazonaws.com - Action: - - sts:AssumeRole - Policies: - - PolicyName: !Sub "${AWS::StackName}-media-package-assets-role" - PolicyDocument: - Statement: - - Effect: Allow - Action: - - lambda:InvokeFunction - Resource: - - !Sub "arn:${AWS::Partition}:lambda:${AWS::Region}:${AWS::AccountId}:function:${AWS::StackName}-error-handler" - - Effect: Allow - Action: - - iam:PassRole - Resource: - - !GetAtt MediaPackageVodRole.Arn - - Effect: Allow - Action: - - logs:CreateLogGroup - - logs:CreateLogStream - - logs:PutLogEvents - Resource: - - !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/*" - - Effect: Allow - Action: - - mediapackage-vod:CreateAsset - Resource: "*" - Metadata: - cfn_nag: - rules_to_suppress: - - id: W11 - reason: "* is used so that the Lambda function can create log groups; MediaPackage action that does not support resource level permissions" - - MediaPackageAssets: - Type: AWS::Lambda::Function - Properties: - FunctionName: !Sub "${AWS::StackName}-media-package-assets" - Description: Ingests an asset into MediaPackage-VOD - Handler: index.handler - Role: !GetAtt MediaPackageAssetsRole.Arn - Code: - S3Bucket: !Join ["-", [!FindInMap ["SourceCode", "General", "S3Bucket"], Ref: "AWS::Region"]] - S3Key: !Join ["/", [!FindInMap ["SourceCode", "General", "KeyPrefix"], "media-package-assets.zip"]] - Runtime: nodejs12.x - Timeout: 300 - Environment: - Variables: - AWS_NODEJS_CONNECTION_REUSE_ENABLED: "1" - ErrorHandler: !GetAtt ErrorHandler.Arn - GroupId: !GetAtt MediaPackageVod.GroupId - GroupDomainName: !GetAtt MediaPackageVod.GroupDomainName - MediaPackageVodRole: !GetAtt MediaPackageVodRole.Arn - SOLUTION_IDENTIFIER: "AwsSolution/SO0021/%%VERSION%%" - Metadata: - cfn_nag: - rules_to_suppress: - - id: W89 - reason: "Lambda functions do not need a VPC" - - id: W92 - reason: "Lambda do not need ReservedConcurrentExecutions in this case" - - ErrorHandlerRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Version: 2012-10-17 - Statement: - - Effect: Allow - Principal: - Service: - - lambda.amazonaws.com - Action: - - sts:AssumeRole - Policies: - - PolicyName: !Sub "${AWS::StackName}-error-handler-role" - PolicyDocument: - Statement: - - Effect: Allow - Action: - - sns:Publish - Resource: - - !Ref SnsTopic - Condition: - Bool: - 'aws:SecureTransport': 'true' - - Effect: Allow - Action: - - dynamodb:UpdateItem - Resource: - - !Sub "arn:${AWS::Partition}:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${DynamoDBTable}" - - Effect: Allow - Action: - - logs:CreateLogGroup - - logs:CreateLogStream - - logs:PutLogEvents - Resource: - - !Sub "arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/*" - Metadata: - cfn_nag: - rules_to_suppress: - - id: W11 - reason: "* is used so that the Lambda function can create log groups" - - ErrorHandler: - Type: AWS::Lambda::Function - Properties: - FunctionName: !Sub "${AWS::StackName}-error-handler" - Description: Captures and processes workflow errors - Handler: index.handler - Role: !GetAtt ErrorHandlerRole.Arn - Code: - S3Bucket: !Join ["-", [!FindInMap ["SourceCode", "General", "S3Bucket"], Ref: "AWS::Region"]] - S3Key: !Join ["/", [!FindInMap ["SourceCode", "General", "KeyPrefix"], "error-handler.zip"]] - Runtime: nodejs12.x - Timeout: 120 - Environment: - Variables: - AWS_NODEJS_CONNECTION_REUSE_ENABLED: "1" - DynamoDBTable: !Ref DynamoDBTable - SnsTopic: !Ref SnsTopic - SOLUTION_IDENTIFIER: "AwsSolution/SO0021/%%VERSION%%" - Metadata: - cfn_nag: - rules_to_suppress: - - id: W89 - reason: "Lambda functions do not need a VPC" - - id: W92 - reason: "Lambda do not need ReservedConcurrentExecutions in this case" - - IngestWorkflow: - Type: AWS::StepFunctions::StateMachine - Properties: - StateMachineName: !Sub "${AWS::StackName}-ingest" - DefinitionString: !Sub | - { - "StartAt": "Input Validate", - "States": { - "Input Validate": { - "Type": "Task", - "Resource": "${InputValidate.Arn}", - "Next": "Mediainfo" - }, - "Mediainfo": { - "Type": "Task", - "Resource": "${Mediainfo.Arn}", - "Next": "DynamoDB Update" - }, - "DynamoDB Update": { - "Type": "Task", - "Resource": "${DynamodbUpdate.Arn}", - "Next": "SNS Choice" - }, - "SNS Choice": { - "Type": "Choice", - "Choices": [ - { - "Variable": "$.enableSns", - "BooleanEquals": true, - "Next": "SNS Notification" - } - ], - "Default": "Process Execute" - }, - "SNS Notification": { - "Type": "Task", - "Resource": "${SnsNotification.Arn}", - "Next": "Process Execute" - }, - "Process Execute": { - "Type": "Task", - "Resource": "${StepFunctions.Arn}", - "End": true - } - } - } - RoleArn: !GetAtt StepFunctionsServiceRole.Arn - - ProcessWorkflow: - Type: AWS::StepFunctions::StateMachine - Properties: - StateMachineName: !Sub "${AWS::StackName}-process" - DefinitionString: !Sub | - { - "Comment": "Process StateMachine to create MediaConvert Encoding Jobs", - "StartAt": "Profiler", - "States": { - "Profiler": { - "Type": "Task", - "Resource": "${Profiler.Arn}", - "Next": "Encoding Profile Check" - }, - "Encoding Profile Check": { - "Type": "Choice", - "Choices": [ - { - "Variable": "$.isCustomTemplate", - "BooleanEquals": true, - "Next": "Custom jobTemplate" - }, - { - "Variable": "$.encodingProfile ", - "NumericEquals": 2160, - "Next": "jobTemplate 2160p" - }, - { - "Variable": "$.encodingProfile ", - "NumericEquals": 1080, - "Next": "jobTemplate 1080p" - }, - { - "Variable": "$.encodingProfile ", - "NumericEquals": 720, - "Next": "jobTemplate 720p" - } - ] - }, - "jobTemplate 2160p": { - "Type": "Pass", - "Next": "Accelerated Transcoding Check" - }, - "jobTemplate 1080p": { - "Type": "Pass", - "Next": "Accelerated Transcoding Check" - }, - "jobTemplate 720p": { - "Type": "Pass", - "Next": "Accelerated Transcoding Check" - }, - "Custom jobTemplate": { - "Type": "Pass", - "Next": "Accelerated Transcoding Check" - }, - "Accelerated Transcoding Check": { - "Type": "Choice", - "Choices": [ - { - "Variable": "$.acceleratedTranscoding", - "StringEquals": "ENABLED", - "Next": "Enabled" - }, - { - "Variable": "$.acceleratedTranscoding", - "StringEquals": "PREFERRED", - "Next": "Preferred" - }, - { - "Variable": "$.acceleratedTranscoding", - "StringEquals": "DISABLED", - "Next": "Disabled" - } - ] - }, - "Enabled": { - "Type": "Pass", - "Next": "Frame Capture Check" - }, - "Preferred": { - "Type": "Pass", - "Next": "Frame Capture Check" - }, - "Disabled": { - "Type": "Pass", - "Next": "Frame Capture Check" - }, - "Frame Capture Check": { - "Type": "Choice", - "Choices": [ - { - "Variable": "$.frameCapture", - "BooleanEquals": true, - "Next": "Frame Capture" - }, - { - "Variable": "$.frameCapture", - "BooleanEquals": false, - "Next": "No Frame Capture" - } - ] - }, - "Frame Capture": { - "Type": "Pass", - "Next": "Encode Job Submit" - }, - "No Frame Capture": { - "Type": "Pass", - "Next": "Encode Job Submit" - }, - "Encode Job Submit": { - "Type": "Task", - "Resource": "${Encode.Arn}", - "Next": "DynamoDB Update" - }, - "DynamoDB Update": { - "Type": "Task", - "Resource": "${DynamodbUpdate.Arn}", - "End": true - } - } - } - RoleArn: !GetAtt StepFunctionsServiceRole.Arn - - PublishWorkflow: - Type: AWS::StepFunctions::StateMachine - Properties: - StateMachineName: !Sub "${AWS::StackName}-publish" - DefinitionString: !Sub | - { - "StartAt": "Validate Encoding Outputs", - "States": { - "Validate Encoding Outputs": { - "Type": "Task", - "Resource": "${OutputValidate.Arn}", - "Next": "Archive Source Choice" - }, - "Archive Source Choice": { - "Type": "Choice", - "Choices": [ - { - "Variable": "$.archiveSource", - "StringEquals": "GLACIER", - "Next": "Archive" - }, - { - "Variable": "$.archiveSource", - "StringEquals": "DEEP_ARCHIVE", - "Next": "Deep Archive" - } - ], - "Default": "MediaPackage Choice" - }, - "Archive": { - "Type": "Task", - "Resource": "${ArchiveSource.Arn}", - "Next": "MediaPackage Choice" - }, - "Deep Archive": { - "Type": "Task", - "Resource": "${ArchiveSource.Arn}", - "Next": "MediaPackage Choice" - }, - "MediaPackage Choice": { - "Type": "Choice", - "Choices": [ - { - "Variable": "$.enableMediaPackage", - "BooleanEquals": true, - "Next": "MediaPackage Assets" - } - ], - "Default": "DynamoDB Update" - }, - "MediaPackage Assets": { - "Type": "Task", - "Resource": "${MediaPackageAssets.Arn}", - "Next": "DynamoDB Update" - }, - "DynamoDB Update": { - "Type": "Task", - "Resource": "${DynamodbUpdate.Arn}", - "Next": "SQS Choice" - }, - "SQS Choice": { - "Type": "Choice", - "Choices": [ - { - "Variable": "$.enableSqs", - "BooleanEquals": true, - "Next": "SQS Send Message" - } - ], - "Default": "SNS Choice" - }, - "SQS Send Message": { - "Type": "Task", - "Resource": "${SqsSendMessage.Arn}", - "Next": "SNS Choice" - }, - "SNS Choice": { - "Type": "Choice", - "Choices": [ - { - "Variable": "$.enableSns", - "BooleanEquals": true, - "Next": "SNS Notification" - } - ], - "Default": "Complete" - }, - "SNS Notification": { - "Type": "Task", - "Resource": "${SnsNotification.Arn}", - "Next": "Complete" - }, - "Complete": { - "Type": "Pass", - "End": true - } - } - } - RoleArn: !GetAtt StepFunctionsServiceRole.Arn - - Uuid: - Condition: Metrics - Type: Custom::UUID - Properties: - ServiceToken: !GetAtt CustomResource.Arn - Resource: UUID - - AnonymousMetric: - Condition: Metrics - Type: Custom::LoadLambda - Properties: - ServiceToken: !GetAtt CustomResource.Arn - SolutionId: SO0021 - UUID: !GetAtt Uuid.UUID - Version: "%%VERSION%%" - Transcoder: MediaConvert - WorkflowTrigger: !Ref WorkflowTrigger - Glacier: !Ref Glacier - FrameCapture: !Ref FrameCapture - Resource: AnonymousMetric - EnableMediaPackage: !Ref EnableMediaPackage - -Outputs: - - DynamoDBTable: - Description: DynamoDB Table - Value: !Ref DynamoDBTable - Export: - Name: !Join [":", [!Ref "AWS::StackName", DynamoDBTable]] - - Source: - Description: Source Bucket - Value: !Ref Source - Export: - Name: !Join [":", [!Ref "AWS::StackName", Source]] - - Destination: - Description: Destination Bucket - Value: !Ref Destination - Export: - Name: !Join [":", [!Ref "AWS::StackName", Destination]] - - CloudFront: - Description: CloudFront Domain Name - Value: !GetAtt CloudFront.DomainName - Export: - Name: !Join [":", [!Ref "AWS::StackName", CloudFront]] - - UUID: - Condition: Metrics - Description: AnonymousMetric UUID - Value: !GetAtt Uuid.UUID - Export: - Name: !Join [":", [!Ref "AWS::StackName", UUID]] - - SnsTopic: - Description: SNS Notification Topic - Value: !Ref SnsTopic - Export: - Name: !Join [":", [!Ref "AWS::StackName", SnsTopic]] - - SqsURL: - Description: AmazonSQS Queue URL - Value: !Ref SqsQueue - Export: - Name: !Join [":", [!Ref "AWS::StackName", SqsQueue]] - - SqsARN: - Description: AmazonSQS Queue ARN - Value: - !Sub ${SqsQueue.Arn} \ No newline at end of file diff --git a/source/archive-source/jest.config.js b/source/archive-source/jest.config.js new file mode 100644 index 00000000..967fc50a --- /dev/null +++ b/source/archive-source/jest.config.js @@ -0,0 +1,6 @@ +module.exports = { + roots: ['/lib'], + testMatch: ['**/*.spec.js'], + coveragePathIgnorePatterns: ['/lib/utils.test.js'], + coverageReporters: [['lcov', { projectRoot: '../' }], 'text'] + }; \ No newline at end of file diff --git a/source/archive-source/package.json b/source/archive-source/package.json index b395cac7..b049af4e 100644 --- a/source/archive-source/package.json +++ b/source/archive-source/package.json @@ -8,14 +8,15 @@ "main": "index.js", "scripts": { "pretest": "npm i --quiet", - "test": "mocha lib/*.spec.js" + "test": "jest --coverage" }, "dependencies": {}, "devDependencies": { "aws-sdk": "^2.609.0", "aws-sdk-mock": "^5.0.0", "chai": "^4.2.0", - "mocha": "^7.0.0" + "mocha": "^7.0.0", + "jest": "^29.2.2" }, "private": true, "author": { diff --git a/source/cdk/.gitignore b/source/cdk/.gitignore new file mode 100644 index 00000000..cb6c7593 --- /dev/null +++ b/source/cdk/.gitignore @@ -0,0 +1,13 @@ +.DS_Store +vscode/ +*.js +!jest.config.js +*.d.ts +node_modules + +# CDK asset staging directory +.cdk.staging +cdk.out + +# Parcel default cache directory +.parcel-cache \ No newline at end of file diff --git a/source/cdk/.npmignore b/source/cdk/.npmignore new file mode 100644 index 00000000..c1d6d45d --- /dev/null +++ b/source/cdk/.npmignore @@ -0,0 +1,6 @@ +*.ts +!*.d.ts + +# CDK asset staging directory +.cdk.staging +cdk.out diff --git a/source/cdk/bin/vod.ts b/source/cdk/bin/vod.ts new file mode 100644 index 00000000..705a9258 --- /dev/null +++ b/source/cdk/bin/vod.ts @@ -0,0 +1,24 @@ +#!/usr/bin/env node +/********************************************************************************************************************* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * + * * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance * + * with the License. A copy of the License is located at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES * + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions * + * and limitations under the License. * + *********************************************************************************************************************/ + +import 'source-map-support/register'; +import * as cdk from 'aws-cdk-lib'; +import { VideoOnDemand } from '../lib/vod-stack'; +import { AwsSolutionsChecks } from 'cdk-nag'; + +const app = new cdk.App(); +new VideoOnDemand(app, 'VideoOnDemand'); // NOSONAR + +//cdk nag +cdk.Aspects.of(app).add(new AwsSolutionsChecks({ verbose: true })); \ No newline at end of file diff --git a/source/cdk/cdk.json b/source/cdk/cdk.json new file mode 100644 index 00000000..c88eaa72 --- /dev/null +++ b/source/cdk/cdk.json @@ -0,0 +1,7 @@ +{ + "app": "npx ts-node bin/vod.ts", + "context": { + "aws-cdk:enableDiffNoFail": "true", + "@aws-cdk/core:stackRelativeExports": "true" + } +} diff --git a/source/cdk/jest.config.js b/source/cdk/jest.config.js new file mode 100644 index 00000000..08263b89 --- /dev/null +++ b/source/cdk/jest.config.js @@ -0,0 +1,8 @@ +module.exports = { + testEnvironment: 'node', + roots: ['/test'], + testMatch: ['**/*.test.ts'], + transform: { + '^.+\\.tsx?$': 'ts-jest' + } +}; diff --git a/source/cdk/lib/vod-stack.ts b/source/cdk/lib/vod-stack.ts new file mode 100644 index 00000000..4973f4b5 --- /dev/null +++ b/source/cdk/lib/vod-stack.ts @@ -0,0 +1,2380 @@ +/********************************************************************************************************************* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * + * * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance * + * with the License. A copy of the License is located at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES * + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions * + * and limitations under the License. * + *********************************************************************************************************************/ + +import * as cdk from 'aws-cdk-lib'; +import { Construct } from 'constructs'; +import * as s3 from 'aws-cdk-lib/aws-s3'; +import * as iam from 'aws-cdk-lib/aws-iam'; +import * as cloudfront from 'aws-cdk-lib/aws-cloudfront'; +import * as lambda from 'aws-cdk-lib/aws-lambda'; +import * as sns from 'aws-cdk-lib/aws-sns'; +import * as sqs from 'aws-cdk-lib/aws-sqs'; +import * as subscriptions from 'aws-cdk-lib/aws-sns-subscriptions'; +import * as dynamodb from 'aws-cdk-lib/aws-dynamodb'; +import * as sfn from 'aws-cdk-lib/aws-stepfunctions'; +import * as tasks from 'aws-cdk-lib/aws-stepfunctions-tasks'; +import * as events from 'aws-cdk-lib/aws-events'; +import * as targets from 'aws-cdk-lib/aws-events-targets'; +import * as appreg from '@aws-cdk/aws-servicecatalogappregistry-alpha'; +import * as appinsights from 'aws-cdk-lib/aws-applicationinsights'; +import { CloudFrontToS3 } from '@aws-solutions-constructs/aws-cloudfront-s3'; +import { NagSuppressions } from 'cdk-nag'; + +export class VideoOnDemand extends cdk.Stack { + constructor(scope: Construct, id: string, props?: cdk.StackProps) { + super(scope, id, props); + + /** + * CloudFormation Template Descrption + */ + const solutionId = 'SO0021' + const solutionName = 'Video on Demand on AWS' + this.templateOptions.description = `(${solutionId}) - ${solutionName} workflow with AWS Step Functions, MediaConvert, MediaPackage, S3, CloudFront and DynamoDB. Version %%VERSION%%`; + /** + * Cfn Parameters + */ + const adminEmail = new cdk.CfnParameter(this, 'AdminEmail', { + type: 'String', + description: 'Email address for SNS notifications (subscribed users will receive ingest, publishing, and error notifications)', + allowedPattern: '^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$' + }); + const workflowTrigger = new cdk.CfnParameter(this, 'WorkflowTrigger', { + type: 'String', + description: 'How the workflow will be triggered (source video upload to S3 or source metadata file upload)', + default: 'VideoFile', + allowedValues: ['VideoFile', 'MetadataFile'] + }); + const glacier = new cdk.CfnParameter(this, 'Glacier', { + type: 'String', + description: 'If enabled, source assets will be tagged for archiving to Glacier or Glacier Deep Archive once the workflow is complete', + default: 'DISABLED', + allowedValues: ['DISABLED', 'GLACIER', 'DEEP_ARCHIVE'] + + }); + const frameCapture = new cdk.CfnParameter(this, 'FrameCapture', { + type: 'String', + description: 'If enabled, frame capture is added to the job submitted to MediaConvert', + default: 'No', + allowedValues: ['Yes', 'No'] + }); + const enableMediaPackage = new cdk.CfnParameter(this, 'EnableMediaPackage', { + type: 'String', + description: 'If enabled, MediaPackage VOD will be included in the workflow', + default: 'No', + allowedValues: ['Yes', 'No'] + }); + const enableSns = new cdk.CfnParameter(this, 'EnableSns', { + type: 'String', + description: 'Enable Ingest and Publish email notifications, error messages are not affected by this parameter.', + default: 'Yes', + allowedValues: ['Yes', 'No'] + }); + const enableSqs = new cdk.CfnParameter(this, 'EnableSqs', { + type: 'String', + description: 'Publish the workflow results to an SQS queue to injest upstream', + default: 'Yes', + allowedValues: ['Yes', 'No'] + }); + const acceleratedTranscoding = new cdk.CfnParameter(this, 'AcceleratedTranscoding', { + type: 'String', + description: 'Enable accelerated transcoding in AWS Elemental MediaConvert. PREFERRED will only use acceleration if the input files is supported. ENABLED accleration is applied to all files (this will fail for unsupported file types) see MediaConvert Documentation for more detail https://docs.aws.amazon.com/mediaconvert/latest/ug/accelerated-transcoding.html', + default: 'PREFERRED', + allowedValues: ['ENABLED', 'DISABLED', 'PREFERRED'] + }); + /** + * Template metadata + */ + this.templateOptions.metadata = { + 'AWS::CloudFormation::Interface': { + ParameterGroups: [ + { + Label: { default: 'Workflow' }, + Parameters: [ + adminEmail.logicalId, + workflowTrigger.logicalId, + glacier.logicalId, + enableSns.logicalId, + enableSqs.logicalId + ] + }, + { + Label: { default: 'AWS Elemental MediaConvert' }, + Parameters: [ + frameCapture.logicalId, + acceleratedTranscoding.logicalId + ] + }, + { + Label: { default: 'AWS Elemental MediaPackage' }, + Parameters: [enableMediaPackage.logicalId] + } + ], + ParameterLabels: { + AdminEmail: { + default: 'Notification email address' + }, + Glacier: { + default: 'Archive source content' + }, + WorkflowTrigger: { + default: 'Workflow trigger' + }, + FrameCapture: { + default: 'Enable Frame Capture' + }, + EnableMediaPackage: { + default: 'Enable MediaPackage' + }, + AcceleratedTranscoding: { + default: 'Accelerated Transcoding' + }, + EnableSns: { + default: 'Enable SNS Notifications' + }, + EnableSqs: { + default: 'Enable SQS Messaging' + } + } + } + }; + /** + * Mapping for sending anonymous metrics to AWS Solution Builders API + */ + new cdk.CfnMapping(this, 'AnonymousData', { // NOSONAR + mapping: { + SendAnonymousData: { + Data: 'Yes' + } + } + }); + /** + * Conditions + */ + const conditionEnableMediaPackage = new cdk.CfnCondition(this, 'EnableMediaPackageCondition', { + expression: cdk.Fn.conditionEquals(enableMediaPackage.valueAsString, 'Yes') + }); + const conditionFrameCapture = new cdk.CfnCondition(this, 'FrameCaptureCondition', { + expression: cdk.Fn.conditionEquals(frameCapture.valueAsString, 'Yes') + }); + const conditionEnableSns = new cdk.CfnCondition(this, 'EnableSnsCondition', { + expression: cdk.Fn.conditionEquals(enableSns.valueAsString, 'Yes') + }); + const conditionEnableSqs = new cdk.CfnCondition(this, 'EnableSqsCondition', { + expression: cdk.Fn.conditionEquals(enableSqs.valueAsString, 'Yes') + }); + + + /** + * Resources + */ + + /** + * Logging bucket for S3 and CloudFront + */ + const logsBucket = new s3.Bucket(this, 'Logs', { + accessControl: s3.BucketAccessControl.LOG_DELIVERY_WRITE, + blockPublicAccess: new s3.BlockPublicAccess({ + blockPublicAcls: true, + blockPublicPolicy: true, + ignorePublicAcls: true, + restrictPublicBuckets: true + }), + encryption: s3.BucketEncryption.S3_MANAGED, + versioned: false + }); + const cfnLogsBucket = logsBucket.node.findChild('Resource') as s3.CfnBucket; + cfnLogsBucket.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.RETAIN; + cfnLogsBucket.cfnOptions.updateReplacePolicy = cdk.CfnDeletionPolicy.RETAIN; + + //cfn_nag + cfnLogsBucket.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W35', + reason: 'Used to store access logs for other buckets' + }, { + id: 'W51', + reason: 'Bucket does not need a bucket policy' + } + ] + } + }; + //cdk_nag + NagSuppressions.addResourceSuppressions( + logsBucket, + [ + { + id: 'AwsSolutions-S1', //same as cfn_nag rule W35 + reason: 'Used to store access logs for other buckets' + }, { + id: 'AwsSolutions-S10', + reason: 'Bucket is private and is not using HTTP' + } + ] + ); + + /** + * Source bucket for source video and jobsettings JSON files + */ + const source = new s3.Bucket(this, 'Source', { + serverAccessLogsBucket: logsBucket, + serverAccessLogsPrefix: 'source-bucket-logs/', + blockPublicAccess: new s3.BlockPublicAccess({ + blockPublicAcls: true, + blockPublicPolicy: true, + ignorePublicAcls: true, + restrictPublicBuckets: true + }), + encryption: s3.BucketEncryption.S3_MANAGED, + lifecycleRules: [ + { + id: `${cdk.Aws.STACK_NAME}-soure-archive`, + tagFilters: { + [cdk.Aws.STACK_NAME]: 'GLACIER' + }, + transitions: [ + { + storageClass: s3.StorageClass.GLACIER, + transitionAfter: cdk.Duration.days(1) + } + ] + }, { + id: `${cdk.Aws.STACK_NAME}-source-deep-archive`, + tagFilters: { + [cdk.Aws.STACK_NAME]: 'DEEP_ARCHIVE' + }, + transitions: [ + { + storageClass: s3.StorageClass.DEEP_ARCHIVE, + transitionAfter: cdk.Duration.days(1) + } + ] + } + ], + versioned: false + }); + const cfnSource = source.node.findChild('Resource') as s3.CfnBucket; + cfnSource.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.RETAIN; + cfnSource.cfnOptions.updateReplacePolicy = cdk.CfnDeletionPolicy.RETAIN; + + //cfn_nag + cfnSource.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W51', + reason: 'Bucket does not need a bucket policy' + } + ] + } + }; + //cdk_nag + NagSuppressions.addResourceSuppressions( + source, + [ + { + id: 'AwsSolutions-S10', + reason: 'Bucket is private and is not using HTTP' + } + ] + ); + + /** + * Destination bucket for workflow outputs + */ + const destination = new s3.Bucket(this, 'Destination', { + serverAccessLogsBucket: logsBucket, + serverAccessLogsPrefix: 'destination-bucket-logs/', + blockPublicAccess: new s3.BlockPublicAccess({ + blockPublicAcls: true, + blockPublicPolicy: true, + ignorePublicAcls: true, + restrictPublicBuckets: true + }), + encryption: s3.BucketEncryption.S3_MANAGED, + cors: [ + { + allowedMethods: [s3.HttpMethods.GET], + allowedOrigins: ['*'], + allowedHeaders: ['*'], + maxAge: 3000 + } + ], + versioned: false + }); + const cfnDestination = destination.node.findChild('Resource') as s3.CfnBucket; + cfnDestination.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.RETAIN; + cfnDestination.cfnOptions.updateReplacePolicy = cdk.CfnDeletionPolicy.RETAIN; + + //cdk_nag + NagSuppressions.addResourceSuppressions( + destination, + [ + { + id: 'AwsSolutions-S10', + reason: 'Bucket is private and is not using HTTP' + } + ] + ); + + /** + * CloudFront distribution. + * AWS Solutions Construct. + * Construct includes a logs bucket for the CloudFront distribution and a CloudFront + * OriginAccessIdentity which is used to restrict access to S3 from CloudFront. + */ + const cachePolicy = new cloudfront.CachePolicy(this, 'CachePolicy', { + cookieBehavior: cloudfront.CacheCookieBehavior.none(), + headerBehavior: cloudfront.CacheHeaderBehavior.allowList( + 'Origin', + 'Access-Control-Request-Method', + 'Access-Control-Request-Headers' + ), + maxTtl: cdk.Duration.seconds(0) + }); + const distribution = new CloudFrontToS3(this, 'CloudFrontToS3', { + existingBucketObj: destination, + cloudFrontDistributionProps: { + defaultBehavior: { + allowedMethods: cloudfront.AllowedMethods.ALLOW_GET_HEAD, + cachePolicy: cachePolicy, + viewerProtocolPolicy: cloudfront.ViewerProtocolPolicy.REDIRECT_TO_HTTPS + }, + priceClass: cloudfront.PriceClass.PRICE_CLASS_100, + logBucket: logsBucket, + logFilePrefix: 'cloudfront-logs/' + }, + insertHttpSecurityHeaders: false, + logS3AccessLogs: false + }); + + //cdk_nag + NagSuppressions.addResourceSuppressions( + destination.policy!, + [ + { + id: 'AwsSolutions-S10', + reason: 'Bucket is private and is not using HTTP' + } + ] + ); + NagSuppressions.addResourceSuppressions( + distribution.cloudFrontWebDistribution, + [ + { + id: 'AwsSolutions-CFR1', + reason: 'Use case does not warrant CloudFront Geo restriction' + }, { + id: 'AwsSolutions-CFR2', + reason: 'Use case does not warrant CloudFront integration with AWS WAF' + }, { + id: 'AwsSolutions-CFR4', //same as cfn_nag rule W70 + reason: 'CloudFront automatically sets the security policy to TLSv1 when the distribution uses the CloudFront domain name' + } + ] + ); + + /** + * Custom Resource lambda, role, and policy. + * Creates custom resources + */ + const customResourceRole = new iam.Role(this, 'CustomResourceRole', { + assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com') + }); + const customResourcePolicy = new iam.Policy(this, 'CustomResourcePolicy', { + statements: [ + new iam.PolicyStatement({ + resources: [`arn:${cdk.Aws.PARTITION}:logs:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:log-group:/aws/lambda/*`], + actions: [ + 'logs:CreateLogGroup', + 'logs:CreateLogStream', + 'logs:PutLogEvents' + ] + }), + new iam.PolicyStatement({ + resources: [source.bucketArn], + actions: [ + 's3:PutBucketNotification', + 's3:PutObject', + 's3:PutObjectAcl' + ] + }), + new iam.PolicyStatement({ + resources: [`arn:${cdk.Aws.PARTITION}:mediaconvert:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:*`], + actions: [ + 'mediaconvert:CreatePreset', + 'mediaconvert:CreateJobTemplate', + 'mediaconvert:DeletePreset', + 'mediaconvert:DeleteJobTemplate', + 'mediaconvert:DescribeEndpoints', + 'mediaconvert:ListJobTemplates', + 'mediaconvert:TagResource', + 'mediaconvert:UntagResource' + ] + }), + new iam.PolicyStatement({ + resources: [ + `arn:${cdk.Aws.PARTITION}:mediapackage-vod:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:assets/*`, + `arn:${cdk.Aws.PARTITION}:mediapackage-vod:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:packaging-configurations/packaging-config-*` + ], + actions: [ + 'mediapackage-vod:DeleteAsset', + 'mediapackage-vod:DeletePackagingConfiguration' + ] + }), + new iam.PolicyStatement({ + resources: [`arn:${cdk.Aws.PARTITION}:mediapackage-vod:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:packaging-groups/${cdk.Aws.STACK_NAME}-packaging-group`], + actions: [ + 'mediapackage-vod:DescribePackagingGroup', + 'mediapackage-vod:DeletePackagingGroup' + ] + }), + new iam.PolicyStatement({ + resources: ['*'], + actions: [ + 'mediapackage-vod:CreatePackagingConfiguration', + 'mediapackage-vod:CreatePackagingGroup', + 'mediapackage-vod:ListAssets', + 'mediapackage-vod:ListPackagingConfigurations', + 'mediapackage-vod:ListPackagingGroups', + 'mediapackage-vod:TagResource', + 'mediapackage-vod:UntagResource' + ] + }), + new iam.PolicyStatement({ + resources: [`arn:${cdk.Aws.PARTITION}:cloudfront::${cdk.Aws.ACCOUNT_ID}:distribution/${distribution.cloudFrontWebDistribution.distributionId}`], + actions: [ + 'cloudfront:GetDistributionConfig', + 'cloudfront:UpdateDistribution' + ] + }) + ] + }); + customResourcePolicy.attachToRole(customResourceRole); + + //cfn_nag + const cfnCustomResourceRole = customResourceRole.node.findChild('Resource') as iam.CfnRole; + cfnCustomResourceRole.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W11', + reason: '* is required to create CloudWatch logs and interact with MediaConvert / MediaPackage actions that do not support resource level permissions' + }, { + id: 'W76', + reason: 'All policies are required by the custom resource.' + } + ] + } + }; + const cfnCustomResourcePolicy = customResourcePolicy.node.findChild('Resource') as iam.CfnPolicy; + cfnCustomResourcePolicy.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W12', + reason: '* is required to create CloudWatch logs and interact with MediaConvert / MediaPackage actions that do not support resource level permissions' + }, { + id: 'W76', + reason: 'High complexity due to number of policy statements needed for creating all custom resources' + } + ] + } + }; + //cdk_nag + NagSuppressions.addResourceSuppressions( + customResourcePolicy, + [ + { + id: 'AwsSolutions-IAM5', + reason: 'Resource ARNs are not generated at the time of policy creation' + } + ] + ); + + const customResourceLambda = new lambda.Function(this, 'CustomResource', { + runtime: lambda.Runtime.NODEJS_16_X, + handler: 'index.handler', + description: 'Used to deploy resources not supported by CloudFormation', + environment: { + SOLUTION_IDENTIFIER: `AwsSolution/${solutionId}/%%VERSION%%` + }, + functionName: `${cdk.Aws.STACK_NAME}-custom-resource`, + role: customResourceRole, + code: lambda.Code.fromAsset('../custom-resource'), + timeout: cdk.Duration.seconds(30) + }); + customResourceLambda.node.addDependency(customResourceRole); + customResourceLambda.node.addDependency(customResourcePolicy); + + //cfn_nag + const cfnCustomResourceLambda = customResourceLambda.node.findChild('Resource') as lambda.CfnFunction; + cfnCustomResourceLambda.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W58', + reason: 'Invalid warning: function has access to cloudwatch' + }, { + id: 'W89', + reason: 'This CustomResource does not need to be deployed inside a VPC' + }, { + id: 'W92', + reason: 'This CustomResource does not need to define ReservedConcurrentExecutions to reserve simultaneous executions' + } + ] + } + }; + + /** + * Custom Resource: MediaConvert Endpoint + */ + const mediaConvertEndpoint = new cdk.CustomResource(this, 'MediaConvertEndPoint', { + serviceToken: customResourceLambda.functionArn, + properties: { + Resource: 'EndPoint' + } + }); + + /** + * Custom Resource: MediaConvert Templates + */ + const mediaConvertTemplates = new cdk.CustomResource(this, 'MediaConvertTemplates', { // NOSONAR + serviceToken: customResourceLambda.functionArn, + properties: { + Resource: 'MediaConvertTemplates', + StackName: cdk.Aws.STACK_NAME, + EndPoint: mediaConvertEndpoint.getAtt('EndpointUrl'), + EnableMediaPackage: cdk.Fn.conditionIf(conditionEnableMediaPackage.logicalId, 'true', 'false'), + EnableNewTemplates: true + } + }); + + /** + * Custom Resource: MediaPackage VOD + */ + const mediaPackageVod = new cdk.CustomResource(this, 'MediaPackageVod', { + serviceToken: customResourceLambda.functionArn, + properties: { + Resource: 'MediaPackageVod', + StackName: cdk.Aws.STACK_NAME, + GroupId: `${cdk.Aws.STACK_NAME}-packaging-group`, + PackagingConfigurations: 'HLS,DASH,MSS,CMAF', + DistributionId: distribution.cloudFrontWebDistribution.distributionId, + EnableMediaPackage: cdk.Fn.conditionIf(conditionEnableMediaPackage.logicalId, 'true', 'false') + } + }); + + /** + * MediaConvert role + */ + const mediaConvertRole = new iam.Role(this, 'MediaConvertRole', { + assumedBy: new iam.ServicePrincipal('mediaconvert.amazonaws.com') + }); + + const mediaConvertPolicy = new iam.Policy(this, 'MediaConvertPolicy', { + policyName: `${cdk.Aws.STACK_NAME}-mediatranscode-policy`, + statements: [ + new iam.PolicyStatement({ + resources: [ + `${source.bucketArn}/*`, + `${destination.bucketArn}/*` + ], + actions: [ + 's3:GetObject', + 's3:PutObject' + ] + }), + new iam.PolicyStatement({ + resources: [`arn:${cdk.Aws.PARTITION}:execute-api:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:*`], + actions: ['execute-api:Invoke'] + }) + ] + }); + mediaConvertPolicy.attachToRole(mediaConvertRole); + + //cfn_nag + const cfnMediaConvertRole = mediaConvertRole.node.findChild('Resource') as iam.CfnRole; + cfnMediaConvertRole.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W11', + reason: '/* required to get/put objects to S3' + } + ] + } + }; + //cdk_nag + NagSuppressions.addResourceSuppressions( + mediaConvertPolicy, + [ + { + id: 'AwsSolutions-IAM5', + reason: '/* required to get/put objects to S3' + } + ] + ); + + /** + * MediaPackageVod role + */ + const mediaPackageVodRole = new iam.Role(this, 'MediaPackageVodRole', { + assumedBy: new iam.ServicePrincipal('mediapackage.amazonaws.com') + }); + + const mediaPackageVodPolicy = new iam.Policy(this, 'MediaPackageVodPolicy', { + policyName: `${cdk.Aws.STACK_NAME}-mediapackagevod-policy`, + statements: [ + new iam.PolicyStatement({ + resources: [ + destination.bucketArn, + `${destination.bucketArn}/*` + ], + actions: [ + 's3:GetObject', + 's3:GetBucketLocation', + 's3:GetBucketRequestPayment' + ] + }) + ] + }); + mediaPackageVodPolicy.attachToRole(mediaPackageVodRole); + + //cfn_nag + const cfnMediaPackageVodRole = mediaPackageVodRole.node.findChild('Resource') as iam.CfnRole; + cfnMediaPackageVodRole.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W11', + reason: '* is required to get objects from S3' + } + ] + } + }; + //cdk_nag + NagSuppressions.addResourceSuppressions( + mediaPackageVodPolicy, + [ + { + id: 'AwsSolutions-IAM5', + reason: '/* required to get/put objects to S3' + } + ] + ); + + + /** + * SNS Topic + */ + const snsTopic = new sns.Topic(this, 'SnsTopic', { + displayName: `${cdk.Aws.STACK_NAME}-Notifications` + }); + snsTopic.addSubscription(new subscriptions.EmailSubscription(adminEmail.valueAsString)); + const cfnSnsTopic = snsTopic.node.findChild('Resource') as sns.CfnTopic; + cfnSnsTopic.kmsMasterKeyId = 'alias/aws/sns'; + + /** + * SQS Queue + */ + const sqsDlq = new sqs.Queue(this, 'SqsQueueDlq', { + queueName: `${cdk.Aws.STACK_NAME}-dlq`, + visibilityTimeout: cdk.Duration.seconds(120), + enforceSSL: true + }); + const cfnSqsDlq = sqsDlq.node.findChild('Resource') as sqs.CfnQueue; + cfnSqsDlq.kmsMasterKeyId = 'alias/aws/sqs'; + cfnSqsDlq.kmsDataKeyReusePeriodSeconds = 300; + + const sqsQueue = new sqs.Queue(this, 'SqsQueue', { + queueName: `${cdk.Aws.STACK_NAME}`, + visibilityTimeout: cdk.Duration.seconds(120), + deadLetterQueue: { + queue: sqsDlq, + maxReceiveCount: 1 + }, + enforceSSL: true + }); + const cfnSqsQueue = sqsQueue.node.findChild('Resource') as sqs.CfnQueue; + cfnSqsQueue.kmsMasterKeyId = 'alias/aws/sqs'; + cfnSqsQueue.kmsDataKeyReusePeriodSeconds = 300; + + //cdk_nag + NagSuppressions.addResourceSuppressions( + sqsDlq, + [ + { + id: 'AwsSolutions-SQS3', + reason: 'This resource is a DLQ' + } + ] + ); + + + /** + * DynamoDB Table + */ + const dynamoDBTable = new dynamodb.Table(this, 'DynamoDBTable', { + partitionKey: { + name: 'guid', + type: dynamodb.AttributeType.STRING + }, + tableName: cdk.Aws.STACK_NAME, + billingMode: dynamodb.BillingMode.PAY_PER_REQUEST, + pointInTimeRecovery: true + }); + dynamoDBTable.addGlobalSecondaryIndex({ + indexName: 'srcBucket-startTime-index', + partitionKey: { + name: 'srcBucket', + type: dynamodb.AttributeType.STRING + }, + sortKey: { + name: 'startTime', + type: dynamodb.AttributeType.STRING + } + }); + + const cfnDynamoDB = dynamoDBTable.node.findChild('Resource') as dynamodb.CfnTable; + cfnDynamoDB.cfnOptions.deletionPolicy = cdk.CfnDeletionPolicy.RETAIN; + cfnDynamoDB.cfnOptions.updateReplacePolicy = cdk.CfnDeletionPolicy.RETAIN; + + //cfn_nag + cfnDynamoDB.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W28', + reason: 'Table name is set to the stack name' + }, { + id: 'W74', + reason: 'The DynamoDB table is configured to use the default encryption' + } + ] + } + }; + + /** + * Error Handler role and lambda + */ + const errorHandlerRole = new iam.Role(this, 'ErrorHandlerRole', { + assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com') + }); + + const errorHandlerPolicy = new iam.Policy(this, 'ErrorHandlerPolicy', { + policyName: `${cdk.Aws.STACK_NAME}-error-handler-role`, + statements: [ + new iam.PolicyStatement({ + resources: [snsTopic.topicArn], + actions: ['sns:Publish'], + conditions: { + 'Bool': { + 'aws:SecureTransport': 'true' + } + } + }), + new iam.PolicyStatement({ + resources: [dynamoDBTable.tableArn], + actions: ['dynamodb:UpdateItem'] + }), + new iam.PolicyStatement({ + resources: [`arn:${cdk.Aws.PARTITION}:logs:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:log-group:/aws/lambda/*`], + actions: [ + 'logs:CreateLogGroup', + 'logs:CreateLogStream', + 'logs:PutLogEvents' + ] + }) + ] + }); + errorHandlerPolicy.attachToRole(errorHandlerRole); + + //cfn_nag + const cfnErrorHandlerRole = errorHandlerRole.node.findChild('Resource') as iam.CfnRole; + cfnErrorHandlerRole.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W11', + reason: '* is used so that the Lambda function can create log groups' + } + ] + } + }; + //cdk_nag + NagSuppressions.addResourceSuppressions( + errorHandlerPolicy, + [ + { + id: 'AwsSolutions-IAM5', + reason: '* is used so that the Lambda function can create log groups' + } + ] + ); + + const errorHandlerLambda = new lambda.Function(this, 'ErrorHandlerLambda', { + runtime: lambda.Runtime.NODEJS_16_X, + handler: 'index.handler', + functionName: `${cdk.Aws.STACK_NAME}-error-handler`, + description: 'Captures and processes workflow errors', + environment: { + SOLUTION_IDENTIFIER: `AwsSolution/${solutionId}/%%VERSION%%`, + AWS_NODEJS_CONNECTION_REUSE_ENABLED: '1', + DynamoDBTable: dynamoDBTable.tableName, + SnsTopic: snsTopic.topicArn + }, + role: errorHandlerRole, + code: lambda.Code.fromAsset('../error-handler'), + timeout: cdk.Duration.seconds(120) + }); + errorHandlerLambda.node.addDependency(errorHandlerRole); + errorHandlerLambda.node.addDependency(errorHandlerPolicy); + + //cfn_nag + const cfnErrorHandlerLambda = errorHandlerLambda.node.findChild('Resource') as lambda.CfnFunction; + cfnErrorHandlerLambda.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W58', + reason: 'Invalid warning: function has access to cloudwatch' + }, { + id: 'W89', + reason: 'This resource does not need to be deployed inside a VPC' + }, { + id: 'W92', + reason: 'This resource does not need to define ReservedConcurrentExecutions to reserve simultaneous executions' + } + ] + } + }; + + const encodeErrorRule = new events.Rule(this, 'EncodeErrorRule', { + ruleName: `${cdk.Aws.STACK_NAME}-EncodeError`, + description: 'MediaConvert Error event rule', + eventPattern: { + source: ['aws.mediaconvert'], + detail: { + status: ['ERROR'], + userMetadata: { + workflow: [cdk.Aws.STACK_NAME] + } + } + }, + targets: [new targets.LambdaFunction(errorHandlerLambda)] + }); + errorHandlerLambda.addPermission('CloudWatchLambdaInvokeErrors', { + principal: new iam.ServicePrincipal('events.amazonaws.com'), + action: 'lambda:InvokeFunction', + sourceArn: encodeErrorRule.ruleArn + }); + + + /** + * Input Validate role and lambda + */ + const inputValidateRole = new iam.Role(this, 'InputValidateRole', { + assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com') + }); + const inputValidatePolicy = new iam.Policy(this, 'InputValidatePolicy', { + policyName: `${cdk.Aws.STACK_NAME}-input-validate-role`, + statements: [ + new iam.PolicyStatement({ + resources: [`${source.bucketArn}/*`], + actions: ['s3:GetObject'] + }), + new iam.PolicyStatement({ + resources: [errorHandlerLambda.functionArn], + actions: ['lambda:InvokeFunction'] + }), + new iam.PolicyStatement({ + resources: [`arn:${cdk.Aws.PARTITION}:logs:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:log-group:/aws/lambda/*`], + actions: [ + 'logs:CreateLogGroup', + 'logs:CreateLogStream', + 'logs:PutLogEvents' + ] + }) + ] + }); + inputValidatePolicy.attachToRole(inputValidateRole); + + //cfn_nag + const cfnInputValidateRole = inputValidateRole.node.findChild('Resource') as iam.CfnRole; + cfnInputValidateRole.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W11', + reason: '* is used so that the Lambda function can create log groups' + } + ] + } + }; + //cdk_nag + NagSuppressions.addResourceSuppressions( + inputValidatePolicy, + [ + { + id: 'AwsSolutions-IAM5', + reason: '* is used so that the Lambda function can create log groups' + } + ] + ); + + const inputValidateLambda = new lambda.Function(this, 'InputValidateLambda', { + runtime: lambda.Runtime.NODEJS_16_X, + handler: 'index.handler', + functionName: `${cdk.Aws.STACK_NAME}-input-validate`, + description: 'Validates the input given to the workflow', + environment: { + SOLUTION_IDENTIFIER: `AwsSolution/${solutionId}/%%VERSION%%`, + AWS_NODEJS_CONNECTION_REUSE_ENABLED: '1', + ErrorHandler: errorHandlerLambda.functionArn, + WorkflowName: cdk.Aws.STACK_NAME, + Source: source.bucketName, + Destination: destination.bucketName, + FrameCapture: `${cdk.Fn.conditionIf(conditionFrameCapture.logicalId, 'true', 'false')}`, + ArchiveSource: glacier.valueAsString, + MediaConvert_Template_2160p: `${cdk.Fn.conditionIf( + conditionEnableMediaPackage.logicalId, + `${cdk.Aws.STACK_NAME}_Ott_2160p_Avc_Aac_16x9_mvod_no_preset`, + `${cdk.Aws.STACK_NAME}_Ott_2160p_Avc_Aac_16x9_qvbr_no_preset` + )}`, + MediaConvert_Template_1080p: `${cdk.Fn.conditionIf( + conditionEnableMediaPackage.logicalId, + `${cdk.Aws.STACK_NAME}_Ott_1080p_Avc_Aac_16x9_mvod_no_preset`, + `${cdk.Aws.STACK_NAME}_Ott_1080p_Avc_Aac_16x9_qvbr_no_preset` + )}`, + MediaConvert_Template_720p: `${cdk.Fn.conditionIf( + conditionEnableMediaPackage.logicalId, + `${cdk.Aws.STACK_NAME}_Ott_720p_Avc_Aac_16x9_mvod_no_preset`, + `${cdk.Aws.STACK_NAME}_Ott_720p_Avc_Aac_16x9_qvbr_no_preset` + )}`, + CloudFront: distribution.cloudFrontWebDistribution.domainName, + EnableMediaPackage: `${cdk.Fn.conditionIf(conditionEnableMediaPackage.logicalId, 'true', 'false')}`, + InputRotate: 'DEGREE_0', + EnableSns: `${cdk.Fn.conditionIf(conditionEnableSns.logicalId, 'true', 'false')}`, + EnableSqs: `${cdk.Fn.conditionIf(conditionEnableSqs.logicalId, 'true', 'false')}`, + AcceleratedTranscoding: acceleratedTranscoding.valueAsString + }, + role: inputValidateRole, + code: lambda.Code.fromAsset('../input-validate'), + timeout: cdk.Duration.seconds(120) + }); + inputValidateLambda.node.addDependency(inputValidateRole); + inputValidateLambda.node.addDependency(inputValidatePolicy); + + //cfn_nag + const cfnInputValidateLambda = inputValidateLambda.node.findChild('Resource') as lambda.CfnFunction; + cfnInputValidateLambda.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W89', + reason: 'Lambda functions do not need a VPC' + }, { + id: 'W92', + reason: 'Lambda do not need ReservedConcurrentExecutions in this case' + }, { + id: 'W58', + reason: 'Invalid warning: function has access to cloudwatch' + } + ] + } + }; + + /** + * MediaInfo role and lambda + */ + const mediaInfoRole = new iam.Role(this, 'MediaInfoRole', { + assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com') + }); + const mediaInfoPolicy = new iam.Policy(this, 'MediaInfoPolicy', { + policyName: `${cdk.Aws.STACK_NAME}-mediainfo-role`, + statements: [ + new iam.PolicyStatement({ + resources: [`${source.bucketArn}/*`], + actions: ['s3:GetObject'] + }), + new iam.PolicyStatement({ + resources: [errorHandlerLambda.functionArn], + actions: ['lambda:InvokeFunction'] + }), + new iam.PolicyStatement({ + resources: [`arn:${cdk.Aws.PARTITION}:logs:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:log-group:/aws/lambda/*`], + actions: [ + 'logs:CreateLogGroup', + 'logs:CreateLogStream', + 'logs:PutLogEvents' + ] + }) + ] + }); + mediaInfoPolicy.attachToRole(mediaInfoRole); + + //cfn_nag + const cfnMediaInfoRole = mediaInfoRole.node.findChild('Resource') as iam.CfnRole; + cfnMediaInfoRole.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W11', + reason: '* is used so that the Lambda function can create log groups' + } + ] + } + }; + //cdk_nag + NagSuppressions.addResourceSuppressions( + mediaInfoPolicy, + [ + { + id: 'AwsSolutions-IAM5', + reason: '* is used so that the Lambda function can create log groups' + } + ] + ); + + const mediaInfoLambda = new lambda.Function(this, 'MediaInfoLambda', { + runtime: lambda.Runtime.PYTHON_3_9, + handler: 'lambda_function.lambda_handler', + functionName: `${cdk.Aws.STACK_NAME}-mediainfo`, + description: 'Runs mediainfo on a pre-signed S3 URL', + environment: { + SOLUTION_IDENTIFIER: `AwsSolution/${solutionId}/%%VERSION%%`, + ErrorHandler: errorHandlerLambda.functionArn + }, + role: mediaInfoRole, + code: lambda.Code.fromAsset('../mediainfo'), + timeout: cdk.Duration.seconds(120) + }); + mediaInfoLambda.node.addDependency(mediaInfoRole); + mediaInfoLambda.node.addDependency(mediaInfoPolicy); + + //cfn_nag + const cfnMediaInfoLambda = mediaInfoLambda.node.findChild('Resource') as lambda.CfnFunction; + cfnMediaInfoLambda.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W89', + reason: 'Lambda functions do not need a VPC' + }, { + id: 'W92', + reason: 'Lambda do not need ReservedConcurrentExecutions in this case' + }, { + id: 'W58', + reason: 'Invalid warning: function has access to cloudwatch' + } + ] + } + }; + + /** + * DynamoUpdate role and lambda + */ + const dynamoUpdateRole = new iam.Role(this, 'DynamoUpdateRole', { + assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com') + }); + const dynamoUpdatePolicy = new iam.Policy(this, 'DynamoUpdatePolicy', { + policyName: `${cdk.Aws.STACK_NAME}-dynamo-role`, + statements: [ + new iam.PolicyStatement({ + resources: [dynamoDBTable.tableArn], + actions: ['dynamodb:UpdateItem'] + }), + new iam.PolicyStatement({ + resources: [errorHandlerLambda.functionArn], + actions: ['lambda:InvokeFunction'] + }), + new iam.PolicyStatement({ + resources: [`arn:${cdk.Aws.PARTITION}:logs:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:log-group:/aws/lambda/*`], + actions: [ + 'logs:CreateLogGroup', + 'logs:CreateLogStream', + 'logs:PutLogEvents' + ] + }) + ] + }); + dynamoUpdatePolicy.attachToRole(dynamoUpdateRole); + + //cfn_nag + const cfnDynamoUpdateRole = dynamoUpdateRole.node.findChild('Resource') as iam.CfnRole; + cfnDynamoUpdateRole.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W11', + reason: '* is used so that the Lambda function can create log groups' + } + ] + } + }; + //cdk_nag + NagSuppressions.addResourceSuppressions( + dynamoUpdatePolicy, + [ + { + id: 'AwsSolutions-IAM5', + reason: '* is used so that the Lambda function can create log groups' + } + ] + ); + + const dynamoUpdateLambda = new lambda.Function(this, 'DynamoUpdateLambda', { + runtime: lambda.Runtime.NODEJS_16_X, + handler: 'index.handler', + functionName: `${cdk.Aws.STACK_NAME}-dynamo`, + description: 'Updates DynamoDB with event data', + environment: { + SOLUTION_IDENTIFIER: `AwsSolution/${solutionId}/%%VERSION%%`, + AWS_NODEJS_CONNECTION_REUSE_ENABLED: '1', + ErrorHandler: errorHandlerLambda.functionArn, + DynamoDBTable: dynamoDBTable.tableName + }, + role: dynamoUpdateRole, + code: lambda.Code.fromAsset('../dynamo'), + timeout: cdk.Duration.seconds(120) + }); + dynamoUpdateLambda.node.addDependency(dynamoUpdateRole); + dynamoUpdateLambda.node.addDependency(dynamoUpdatePolicy); + + //cfn_nag + const cfnDynamoUpdateLambda = dynamoUpdateLambda.node.findChild('Resource') as lambda.CfnFunction; + cfnDynamoUpdateLambda.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W89', + reason: 'Lambda functions do not need a VPC' + }, { + id: 'W92', + reason: 'Lambda do not need ReservedConcurrentExecutions in this case' + }, { + id: 'W58', + reason: 'Invalid warning: function has access to cloudwatch' + } + ] + } + }; + + /** + * Profiler role and lambda + */ + const profilerRole = new iam.Role(this, 'ProfilerRole', { + assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com') + }); + const profilerPolicy = new iam.Policy(this, 'ProfilerPolicy', { + policyName: `${cdk.Aws.STACK_NAME}-profiler-role`, + statements: [ + new iam.PolicyStatement({ + resources: [dynamoDBTable.tableArn], + actions: ['dynamodb:GetItem'] + }), + new iam.PolicyStatement({ + resources: [errorHandlerLambda.functionArn], + actions: ['lambda:InvokeFunction'] + }), + new iam.PolicyStatement({ + resources: [`arn:${cdk.Aws.PARTITION}:logs:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:log-group:/aws/lambda/*`], + actions: [ + 'logs:CreateLogGroup', + 'logs:CreateLogStream', + 'logs:PutLogEvents' + ] + }) + ] + }); + profilerPolicy.attachToRole(profilerRole); + + //cfn_nag + const cfnProfilerRole = profilerRole.node.findChild('Resource') as iam.CfnRole; + cfnProfilerRole.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W11', + reason: '* is used so that the Lambda function can create log groups' + } + ] + } + }; + //cdk_nag + NagSuppressions.addResourceSuppressions( + profilerPolicy, + [ + { + id: 'AwsSolutions-IAM5', + reason: '* is used so that the Lambda function can create log groups' + } + ] + ); + + const profilerLambda = new lambda.Function(this, 'ProfilerLambda', { + runtime: lambda.Runtime.NODEJS_16_X, + handler: 'index.handler', + functionName: `${cdk.Aws.STACK_NAME}-profiler`, + description: 'Sets an EncodeProfile based on mediainfo output', + environment: { + SOLUTION_IDENTIFIER: `AwsSolution/${solutionId}/%%VERSION%%`, + AWS_NODEJS_CONNECTION_REUSE_ENABLED: '1', + ErrorHandler: errorHandlerLambda.functionArn, + DynamoDBTable: dynamoDBTable.tableName + }, + role: profilerRole, + code: lambda.Code.fromAsset('../profiler'), + timeout: cdk.Duration.seconds(120) + }); + profilerLambda.node.addDependency(profilerRole); + profilerLambda.node.addDependency(profilerPolicy); + + //cfn_nag + const cfnProfilerLambda = profilerLambda.node.findChild('Resource') as lambda.CfnFunction; + cfnProfilerLambda.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W89', + reason: 'Lambda functions do not need a VPC' + }, { + id: 'W92', + reason: 'Lambda do not need ReservedConcurrentExecutions in this case' + }, { + id: 'W58', + reason: 'Invalid warning: function has access to cloudwatch' + } + ] + } + }; + + /** + * Encode role and lambda + */ + const encodeRole = new iam.Role(this, 'EncodeRole', { + assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com') + }); + const encodePolicy = new iam.Policy(this, 'EncodePolicy', { + policyName: `${cdk.Aws.STACK_NAME}-encode-role`, + statements: [ + new iam.PolicyStatement({ + resources: [`arn:${cdk.Aws.PARTITION}:mediaconvert:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:*`], + actions: [ + 'mediaconvert:CreateJob', + 'mediaconvert:GetJobTemplate', + 'mediaconvert:TagResource', + 'mediaconvert:UntagResource' + ] + }), + new iam.PolicyStatement({ + resources: [mediaConvertRole.roleArn], + actions: ['iam:PassRole'] + }), + new iam.PolicyStatement({ + resources: [errorHandlerLambda.functionArn], + actions: ['lambda:InvokeFunction'] + }), + new iam.PolicyStatement({ + resources: [`arn:${cdk.Aws.PARTITION}:logs:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:log-group:/aws/lambda/*`], + actions: [ + 'logs:CreateLogGroup', + 'logs:CreateLogStream', + 'logs:PutLogEvents' + ] + }) + ] + }); + encodePolicy.attachToRole(encodeRole); + + //cfn_nag + const cfnEncodeRole = encodeRole.node.findChild('Resource') as iam.CfnRole; + cfnEncodeRole.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W11', + reason: '* is used so that the Lambda function can create log groups' + } + ] + } + }; + //cdk_nag + NagSuppressions.addResourceSuppressions( + encodePolicy, + [ + { + id: 'AwsSolutions-IAM5', + reason: '* is used so that the Lambda function can create log groups' + } + ] + ); + + const encodeLambda = new lambda.Function(this, 'EncodeLambda', { + runtime: lambda.Runtime.NODEJS_16_X, + handler: 'index.handler', + functionName: `${cdk.Aws.STACK_NAME}-encode`, + description: 'Creates a MediaConvert encode job', + environment: { + SOLUTION_IDENTIFIER: `AwsSolution/${solutionId}/%%VERSION%%`, + AWS_NODEJS_CONNECTION_REUSE_ENABLED: '1', + ErrorHandler: errorHandlerLambda.functionArn, + MediaConvertRole: mediaConvertRole.roleArn, + EndPoint: mediaConvertEndpoint.getAttString('EndpointUrl') + }, + role: encodeRole, + code: lambda.Code.fromAsset('../encode'), + timeout: cdk.Duration.seconds(120) + }); + encodeLambda.node.addDependency(encodeRole); + encodeLambda.node.addDependency(encodePolicy); + + //cfn_nag + const cfnEncodeLambda = encodeLambda.node.findChild('Resource') as lambda.CfnFunction; + cfnEncodeLambda.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W89', + reason: 'Lambda functions do not need a VPC' + }, { + id: 'W92', + reason: 'Lambda do not need ReservedConcurrentExecutions in this case' + }, { + id: 'W58', + reason: 'Invalid warning: function has access to cloudwatch' + } + ] + } + }; + + /** + * OutputValidate role and lambda + */ + const outputValidateRole = new iam.Role(this, 'OutputValidateRole', { + assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com') + }); + const outputValidatePolicy = new iam.Policy(this, 'OutputValidatePolicy', { + policyName: `${cdk.Aws.STACK_NAME}-output-validate-role`, + statements: [ + new iam.PolicyStatement({ + resources: [dynamoDBTable.tableArn], + actions: ['dynamodb:GetItem'] + }), + new iam.PolicyStatement({ + resources: [destination.bucketArn], + actions: ['s3:ListBucket'] + }), + new iam.PolicyStatement({ + resources: [errorHandlerLambda.functionArn], + actions: ['lambda:InvokeFunction'] + }), + new iam.PolicyStatement({ + resources: [`arn:${cdk.Aws.PARTITION}:logs:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:log-group:/aws/lambda/*`], + actions: [ + 'logs:CreateLogGroup', + 'logs:CreateLogStream', + 'logs:PutLogEvents' + ] + }) + ] + }); + outputValidatePolicy.attachToRole(outputValidateRole); + + //cfn_nag + const cfnOutputValidateRole = outputValidateRole.node.findChild('Resource') as iam.CfnRole; + cfnOutputValidateRole.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W11', + reason: '* is used so that the Lambda function can create log groups' + } + ] + } + }; + //cdk_nag + NagSuppressions.addResourceSuppressions( + outputValidatePolicy, + [ + { + id: 'AwsSolutions-IAM5', + reason: '* is used so that the Lambda function can create log groups' + } + ] + ); + + const outputValidateLambda = new lambda.Function(this, 'OutputValidateLambda', { + runtime: lambda.Runtime.NODEJS_16_X, + handler: 'index.handler', + functionName: `${cdk.Aws.STACK_NAME}-output-validate`, + description: 'Parses MediaConvert job output', + environment: { + SOLUTION_IDENTIFIER: `AwsSolution/${solutionId}/%%VERSION%%`, + AWS_NODEJS_CONNECTION_REUSE_ENABLED: '1', + ErrorHandler: errorHandlerLambda.functionArn, + DynamoDBTable: dynamoDBTable.tableName, + EndPoint: mediaConvertEndpoint.getAttString('EndpointUrl') + }, + role: outputValidateRole, + code: lambda.Code.fromAsset('../output-validate'), + timeout: cdk.Duration.seconds(120) + }); + outputValidateLambda.node.addDependency(outputValidateRole); + outputValidateLambda.node.addDependency(outputValidatePolicy); + + //cfn_nag + const cfnOutputValidateLambda = outputValidateLambda.node.findChild('Resource') as lambda.CfnFunction; + cfnOutputValidateLambda.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W89', + reason: 'Lambda functions do not need a VPC' + }, { + id: 'W92', + reason: 'Lambda do not need ReservedConcurrentExecutions in this case' + }, { + id: 'W58', + reason: 'Invalid warning: function has access to cloudwatch' + } + ] + } + }; + + /** + * ArchiveSource role and lambda + */ + const archiveSourceRole = new iam.Role(this, 'ArchiveSourceRole', { + assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com') + }); + const archiveSourcePolicy = new iam.Policy(this, 'ArchiveSourcePolicy', { + policyName: `${cdk.Aws.STACK_NAME}-archive-source-role`, + statements: [ + new iam.PolicyStatement({ + resources: [`${source.bucketArn}/*`], + actions: ['s3:PutObjectTagging'] + }), + new iam.PolicyStatement({ + resources: [errorHandlerLambda.functionArn], + actions: ['lambda:InvokeFunction'] + }), + new iam.PolicyStatement({ + resources: [`arn:${cdk.Aws.PARTITION}:logs:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:log-group:/aws/lambda/*`], + actions: [ + 'logs:CreateLogGroup', + 'logs:CreateLogStream', + 'logs:PutLogEvents' + ] + }) + ] + }); + archiveSourcePolicy.attachToRole(archiveSourceRole); + + //cfn_nag + const cfnArchiveSourceRole = archiveSourceRole.node.findChild('Resource') as iam.CfnRole; + cfnArchiveSourceRole.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W11', + reason: '* is used so that the Lambda function can create log groups' + } + ] + } + }; + //cdk_nag + NagSuppressions.addResourceSuppressions( + archiveSourcePolicy, + [ + { + id: 'AwsSolutions-IAM5', + reason: '* is used so that the Lambda function can create log groups' + } + ] + ); + + const archiveSourceLambda = new lambda.Function(this, 'ArchiveSourceLambda', { + runtime: lambda.Runtime.NODEJS_16_X, + handler: 'index.handler', + functionName: `${cdk.Aws.STACK_NAME}-archive-source`, + description: 'Updates tags on source files to enable Glacier', + environment: { + SOLUTION_IDENTIFIER: `AwsSolution/${solutionId}/%%VERSION%%`, + AWS_NODEJS_CONNECTION_REUSE_ENABLED: '1', + ErrorHandler: errorHandlerLambda.functionArn + }, + role: archiveSourceRole, + code: lambda.Code.fromAsset('../archive-source'), + timeout: cdk.Duration.seconds(120) + }); + archiveSourceLambda.node.addDependency(archiveSourceRole); + archiveSourceLambda.node.addDependency(archiveSourcePolicy); + + //cfn_nag + const cfnArchiveSourceLambda = archiveSourceLambda.node.findChild('Resource') as lambda.CfnFunction; + cfnArchiveSourceLambda.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W89', + reason: 'Lambda functions do not need a VPC' + }, { + id: 'W92', + reason: 'Lambda do not need ReservedConcurrentExecutions in this case' + }, { + id: 'W58', + reason: 'Invalid warning: function has access to cloudwatch' + } + ] + } + }; + + /** + * SqsSendMessage role and lambda + */ + const sqsSendMessageRole = new iam.Role(this, 'SqsSendMessageRole', { + assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com') + }); + const sqsSendMessagePolicy = new iam.Policy(this, 'SqsSendMessagePolicy', { + policyName: `${cdk.Aws.STACK_NAME}-sqs-publish-role`, + statements: [ + new iam.PolicyStatement({ + resources: [sqsQueue.queueArn], + actions: ['sqs:SendMessage'], + conditions: { + 'Bool': { + 'aws:SecureTransport': 'true' + } + } + }), + new iam.PolicyStatement({ + resources: [errorHandlerLambda.functionArn], + actions: ['lambda:InvokeFunction'] + }), + new iam.PolicyStatement({ + resources: [`arn:${cdk.Aws.PARTITION}:logs:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:log-group:/aws/lambda/*`], + actions: [ + 'logs:CreateLogGroup', + 'logs:CreateLogStream', + 'logs:PutLogEvents' + ] + }) + ] + }); + sqsSendMessagePolicy.attachToRole(sqsSendMessageRole); + + //cfn_nag + const cfnSqsSendMessageRole = sqsSendMessageRole.node.findChild('Resource') as iam.CfnRole; + cfnSqsSendMessageRole.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W11', + reason: '* is used so that the Lambda function can create log groups' + } + ] + } + }; + //cdk_nag + NagSuppressions.addResourceSuppressions( + sqsSendMessagePolicy, + [ + { + id: 'AwsSolutions-IAM5', + reason: '* is used so that the Lambda function can create log groups' + } + ] + ); + + const sqsSendMessageLambda = new lambda.Function(this, 'SqsSendMessageLambda', { + runtime: lambda.Runtime.NODEJS_16_X, + handler: 'index.handler', + functionName: `${cdk.Aws.STACK_NAME}-sqs-publish`, + description: 'Publish the workflow results to an SQS queue', + environment: { + SOLUTION_IDENTIFIER: `AwsSolution/${solutionId}/%%VERSION%%`, + AWS_NODEJS_CONNECTION_REUSE_ENABLED: '1', + ErrorHandler: errorHandlerLambda.functionArn, + SqsQueue: sqsQueue.queueUrl + }, + role: sqsSendMessageRole, + code: lambda.Code.fromAsset('../sqs-publish'), + timeout: cdk.Duration.seconds(120) + }); + sqsSendMessageLambda.node.addDependency(sqsSendMessageRole); + sqsSendMessageLambda.node.addDependency(sqsSendMessagePolicy); + + //cfn_nag + const cfnSqsSendMessageLambda = sqsSendMessageLambda.node.findChild('Resource') as lambda.CfnFunction; + cfnSqsSendMessageLambda.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W89', + reason: 'Lambda functions do not need a VPC' + }, { + id: 'W92', + reason: 'Lambda do not need ReservedConcurrentExecutions in this case' + }, { + id: 'W58', + reason: 'Invalid warning: function has access to cloudwatch' + } + ] + } + }; + + /** + * SnsNotification role and lambda + */ + const snsNotificationRole = new iam.Role(this, 'SnsNotificationRole', { + assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com') + }); + const snsNotificationPolicy = new iam.Policy(this, 'SnsNotificationPolicy', { + policyName: `${cdk.Aws.STACK_NAME}-sns-notification-role`, + statements: [ + new iam.PolicyStatement({ + resources: [snsTopic.topicArn], + actions: ['sns:Publish'], + conditions: { + 'Bool': { + 'aws:SecureTransport': 'true' + } + } + }), + new iam.PolicyStatement({ + resources: [errorHandlerLambda.functionArn], + actions: ['lambda:InvokeFunction'] + }), + new iam.PolicyStatement({ + resources: [`arn:${cdk.Aws.PARTITION}:logs:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:log-group:/aws/lambda/*`], + actions: [ + 'logs:CreateLogGroup', + 'logs:CreateLogStream', + 'logs:PutLogEvents' + ] + }) + ] + }); + snsNotificationPolicy.attachToRole(snsNotificationRole); + + //cfn_nag + const cfnSnsNotificationRole = snsNotificationRole.node.findChild('Resource') as iam.CfnRole; + cfnSnsNotificationRole.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W11', + reason: '* is used so that the Lambda function can create log groups' + } + ] + } + }; + //cdk_nag + NagSuppressions.addResourceSuppressions( + snsNotificationPolicy, + [ + { + id: 'AwsSolutions-IAM5', + reason: '* is used so that the Lambda function can create log groups' + } + ] + ); + + const snsNotificationLambda = new lambda.Function(this, 'SnsNotificationLambda', { + runtime: lambda.Runtime.NODEJS_16_X, + handler: 'index.handler', + functionName: `${cdk.Aws.STACK_NAME}-sns-notification`, + description: 'Sends a notification when the encode job is completed', + environment: { + SOLUTION_IDENTIFIER: `AwsSolution/${solutionId}/%%VERSION%%`, + AWS_NODEJS_CONNECTION_REUSE_ENABLED: '1', + ErrorHandler: errorHandlerLambda.functionArn, + SnsTopic: snsTopic.topicArn + }, + role: snsNotificationRole, + code: lambda.Code.fromAsset('../sns-notification'), + timeout: cdk.Duration.seconds(120) + }); + snsNotificationLambda.node.addDependency(snsNotificationRole); + snsNotificationLambda.node.addDependency(snsNotificationPolicy); + + //cfn_nag + const cfnSnsNotificationLambda = snsNotificationLambda.node.findChild('Resource') as lambda.CfnFunction; + cfnSnsNotificationLambda.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W89', + reason: 'Lambda functions do not need a VPC' + }, { + id: 'W92', + reason: 'Lambda do not need ReservedConcurrentExecutions in this case' + }, { + id: 'W58', + reason: 'Invalid warning: function has access to cloudwatch' + } + ] + } + }; + + /** + * MediaPackageAssets role and lambda + */ + const mediaPackageAssetsRole = new iam.Role(this, 'MediaPackageAssetsRole', { + assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com') + }); + const mediaPackageAssetsPolicy = new iam.Policy(this, 'MediaPackageAssetsPolicy', { + policyName: `${cdk.Aws.STACK_NAME}-media-package-assets-role`, + statements: [ + new iam.PolicyStatement({ + resources: [mediaPackageVodRole.roleArn], + actions: ['iam:PassRole'] + }), + new iam.PolicyStatement({ + resources: ['*'], + actions: [ + 'mediapackage-vod:CreateAsset', + 'mediapackage-vod:TagResource', + 'mediapackage-vod:UntagResource' + ] + }), + new iam.PolicyStatement({ + resources: [errorHandlerLambda.functionArn], + actions: ['lambda:InvokeFunction'] + }), + new iam.PolicyStatement({ + resources: [`arn:${cdk.Aws.PARTITION}:logs:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:log-group:/aws/lambda/*`], + actions: [ + 'logs:CreateLogGroup', + 'logs:CreateLogStream', + 'logs:PutLogEvents' + ] + }) + ] + }); + mediaPackageAssetsPolicy.attachToRole(mediaPackageAssetsRole); + + //cfn_nag + const cfnMediaPackageAssetsRole = mediaPackageAssetsRole.node.findChild('Resource') as iam.CfnRole; + cfnMediaPackageAssetsRole.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W11', + reason: '* is used so that the Lambda function can create log groups' + } + ] + } + }; + const cfnMediaPackageAssetsPolicy = mediaPackageAssetsPolicy.node.findChild('Resource') as iam.CfnPolicy; + cfnMediaPackageAssetsPolicy.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W12', + reason: '* is used so that the Lambda function can create log groups' + } + ] + } + }; + //cdk_nag + NagSuppressions.addResourceSuppressions( + mediaPackageAssetsPolicy, + [ + { + id: 'AwsSolutions-IAM5', + reason: '* is used so that the Lambda function can create log groups' + } + ] + ); + + const mediaPackageAssetsLambda = new lambda.Function(this, 'MediaPackageAssetsLambda', { + runtime: lambda.Runtime.NODEJS_16_X, + handler: 'index.handler', + functionName: `${cdk.Aws.STACK_NAME}-media-package-assets`, + description: 'Ingests an asset into MediaPackage-VOD', + environment: { + SOLUTION_IDENTIFIER: `AwsSolution/${solutionId}/%%VERSION%%`, + AWS_NODEJS_CONNECTION_REUSE_ENABLED: '1', + ErrorHandler: errorHandlerLambda.functionArn, + GroupId: mediaPackageVod.getAttString('GroupId'), + GroupDomainName: mediaPackageVod.getAttString('GroupDomainName'), + MediaPackageVodRole: mediaPackageVodRole.roleArn + }, + role: mediaPackageAssetsRole, + code: lambda.Code.fromAsset('../media-package-assets'), + timeout: cdk.Duration.seconds(120) + }); + mediaPackageAssetsLambda.node.addDependency(mediaPackageAssetsRole); + mediaPackageAssetsLambda.node.addDependency(mediaPackageAssetsPolicy); + + //cfn_nag + const cfnMediaPackageAssetsLambda = mediaPackageAssetsLambda.node.findChild('Resource') as lambda.CfnFunction; + cfnMediaPackageAssetsLambda.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W89', + reason: 'Lambda functions do not need a VPC' + }, { + id: 'W92', + reason: 'Lambda do not need ReservedConcurrentExecutions in this case' + }, { + id: 'W58', + reason: 'Invalid warning: function has access to cloudwatch' + } + ] + } + }; + + /** + * Step Functions role and lambda + */ + const stepFunctionsRole = new iam.Role(this, 'StepFunctionsRole', { + assumedBy: new iam.ServicePrincipal('lambda.amazonaws.com') + }); + + const stepFunctionsPolicy = new iam.Policy(this, 'StepFunctionsPolicy', { + policyName: `${cdk.Aws.STACK_NAME}-step-functions-role`, + statements: [ + new iam.PolicyStatement({ + resources: [ + `arn:${cdk.Aws.PARTITION}:states:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:stateMachine:${cdk.Aws.STACK_NAME}-ingest`, + `arn:${cdk.Aws.PARTITION}:states:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:stateMachine:${cdk.Aws.STACK_NAME}-process`, + `arn:${cdk.Aws.PARTITION}:states:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:stateMachine:${cdk.Aws.STACK_NAME}-publish` + ], + actions: ['states:StartExecution'] + }), + new iam.PolicyStatement({ + resources: [errorHandlerLambda.functionArn], + actions: ['lambda:InvokeFunction'] + }), + new iam.PolicyStatement({ + resources: [`arn:${cdk.Aws.PARTITION}:logs:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:log-group:/aws/lambda/*`], + actions: [ + 'logs:CreateLogGroup', + 'logs:CreateLogStream', + 'logs:PutLogEvents' + ] + }) + ] + }); + stepFunctionsPolicy.attachToRole(stepFunctionsRole); + + //cfn_nag + const cfnStepFunctionsRole = stepFunctionsRole.node.findChild('Resource') as iam.CfnRole; + cfnStepFunctionsRole.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W11', + reason: '* is used so that the Lambda function can create log groups' + } + ] + } + }; + //cdk_nag + NagSuppressions.addResourceSuppressions( + stepFunctionsPolicy, + [ + { + id: 'AwsSolutions-IAM5', + reason: '* is used so that the Lambda function can create log groups' + } + ] + ); + + const stepFunctionsLambda = new lambda.Function(this, 'StepFunctionsLambda', { + runtime: lambda.Runtime.NODEJS_16_X, + handler: 'index.handler', + functionName: `${cdk.Aws.STACK_NAME}-step-functions`, + description: 'Creates a unique identifer (GUID) and executes the Ingest StateMachine', + environment: { + SOLUTION_IDENTIFIER: `AwsSolution/${solutionId}/%%VERSION%%`, + AWS_NODEJS_CONNECTION_REUSE_ENABLED: '1', + IngestWorkflow: `arn:${cdk.Aws.PARTITION}:states:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:stateMachine:${cdk.Aws.STACK_NAME}-ingest`, + ProcessWorkflow: `arn:${cdk.Aws.PARTITION}:states:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:stateMachine:${cdk.Aws.STACK_NAME}-process`, + PublishWorkflow: `arn:${cdk.Aws.PARTITION}:states:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:stateMachine:${cdk.Aws.STACK_NAME}-publish`, + ErrorHandler: errorHandlerLambda.functionArn + }, + role: stepFunctionsRole, + code: lambda.Code.fromAsset('../step-functions'), + timeout: cdk.Duration.seconds(120) + }); + stepFunctionsLambda.node.addDependency(stepFunctionsRole); + stepFunctionsLambda.node.addDependency(stepFunctionsPolicy); + + //cfn_nag + const cfnStepFunctionsLambda = stepFunctionsLambda.node.findChild('Resource') as lambda.CfnFunction; + cfnStepFunctionsLambda.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W58', + reason: 'Invalid warning: function has access to cloudwatch' + }, { + id: 'W89', + reason: 'This resource does not need to be deployed inside a VPC' + }, { + id: 'W92', + reason: 'This resource does not need to define ReservedConcurrentExecutions to reserve simultaneous executions' + } + ] + } + }; + + const encodeCompleteRule = new events.Rule(this, 'EncodeCompleteRule', { + ruleName: `${cdk.Aws.STACK_NAME}-EncodeComplete`, + description: 'MediaConvert Completed event rule', + eventPattern: { + source: ['aws.mediaconvert'], + detail: { + status: ['COMPLETE'], + userMetadata: { + workflow: [cdk.Aws.STACK_NAME] + } + } + }, + targets: [new targets.LambdaFunction(stepFunctionsLambda)] + }); + stepFunctionsLambda.addPermission('S3LambdaInvokeVideo', { + principal: new iam.ServicePrincipal('s3.amazonaws.com'), + action: 'lambda:InvokeFunction', + sourceAccount: cdk.Aws.ACCOUNT_ID + }); + stepFunctionsLambda.addPermission('CloudWatchLambdaInvokeCompletes', { + principal: new iam.ServicePrincipal('events.amazonaws.com'), + action: 'lambda:InvokeFunction', + sourceArn: encodeCompleteRule.ruleArn + }); + + /** + * Custom Resource: S3Config + */ + const s3Config = new cdk.CustomResource(this, 'S3Config', { + serviceToken: customResourceLambda.functionArn, + properties: { + Resource: 'S3Notification', + Source: source.bucketName, + IngestArn: stepFunctionsLambda.functionArn, + WorkflowTrigger: workflowTrigger.valueAsString + } + }); + s3Config.node.addDependency(stepFunctionsLambda); + + /** + * StepFunctionsService role + */ + const stepFunctionsServiceRole = new iam.Role(this, 'StepFunctionsServiceRole', { + assumedBy: new iam.ServicePrincipal(`states.${cdk.Aws.REGION}.amazonaws.com`) + }); + const stepFunctionsServicePolicy = new iam.Policy(this, 'StepFunctionsServicePolicy', { + policyName: `${cdk.Aws.STACK_NAME}-stepfunctions-service-role`, + statements: [ + new iam.PolicyStatement({ + resources: [`arn:${cdk.Aws.PARTITION}:lambda:${cdk.Aws.REGION}:${cdk.Aws.ACCOUNT_ID}:function:*`], + actions: ['lambda:InvokeFunction'] + }) + ] + }); + stepFunctionsServicePolicy.attachToRole(stepFunctionsServiceRole); + + //cfn_nag + const cfnStepFunctionsServiceRole = stepFunctionsServiceRole.node.findChild('Resource') as iam.CfnRole; + cfnStepFunctionsServiceRole.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W11', + reason: 'The * resource is required since the functions need to be created before the state machine' + } + ] + } + }; + //cdk_nag + NagSuppressions.addResourceSuppressions( + stepFunctionsServicePolicy, + [ + { + id: 'AwsSolutions-IAM5', + reason: '* is used so that the Lambda function can create log groups' + } + ] + ); + + + /** + * StepFunction States + */ + const inputValidateTask = new tasks.LambdaInvoke(this, 'Input Validate', { + lambdaFunction: inputValidateLambda, + payloadResponseOnly: true + }); + const mediaInfoTask = new tasks.LambdaInvoke(this, 'MediaInfo', { + lambdaFunction: mediaInfoLambda, + payloadResponseOnly: true + }); + const dynamodbUpdateTaskIngest = new tasks.LambdaInvoke(this, 'DynamoDB Update (Ingest)', { + lambdaFunction: dynamoUpdateLambda, + payloadResponseOnly: true + }); + const dynamodbUpdateTaskProcess = new tasks.LambdaInvoke(this, 'DynamoDB Update (Process)', { + lambdaFunction: dynamoUpdateLambda, + payloadResponseOnly: true + }); + const dynamodbUpdateTaskPublish = new tasks.LambdaInvoke(this, 'DynamoDB Update (Publish)', { + lambdaFunction: dynamoUpdateLambda, + payloadResponseOnly: true + }); + const snsNotificationTaskIngest = new tasks.LambdaInvoke(this, 'SNS Notification (Ingest)', { + lambdaFunction: snsNotificationLambda, + payloadResponseOnly: true + }); + const snsNotificationTaskPublish = new tasks.LambdaInvoke(this, 'SNS Notification (Publish)', { + lambdaFunction: snsNotificationLambda, + payloadResponseOnly: true + }); + const processExecuteTask = new tasks.LambdaInvoke(this, 'Process Execute', { + lambdaFunction: stepFunctionsLambda, + payloadResponseOnly: true + }); + const profilerTask = new tasks.LambdaInvoke(this, 'Profiler', { + lambdaFunction: profilerLambda, + payloadResponseOnly: true + }); + const encodeTask = new tasks.LambdaInvoke(this, 'Encode Job Submit', { + lambdaFunction: encodeLambda, + payloadResponseOnly: true + }); + const outputValidateTask = new tasks.LambdaInvoke(this, 'Validate Encoding Outputs', { + lambdaFunction: outputValidateLambda, + payloadResponseOnly: true + }); + const archiveTask = new tasks.LambdaInvoke(this, 'Archive', { + lambdaFunction: archiveSourceLambda, + payloadResponseOnly: true + }); + const deepArchiveTask = new tasks.LambdaInvoke(this, 'Deep Archive', { + lambdaFunction: archiveSourceLambda, + payloadResponseOnly: true + }); + const mediaPackageAssetsTask = new tasks.LambdaInvoke(this, 'MediaPackage Assets', { + lambdaFunction: mediaPackageAssetsLambda, + payloadResponseOnly: true + }); + const sqsSendMessageTask = new tasks.LambdaInvoke(this, 'SQS Send Message', { + lambdaFunction: sqsSendMessageLambda, + payloadResponseOnly: true + }); + const completeState = new sfn.Pass(this, 'Complete'); + + /** + * IngestWorkflow state machine + * 1: Input Validate + * 2: MediaInfo + * 3: DynamoDB Update + * 4: SNS Choice + * 5: SNS Notification + * 6: Process Execute + */ + snsNotificationTaskIngest.next(processExecuteTask); + const ingestWorkflowDefinition = inputValidateTask + .next(mediaInfoTask) + .next(dynamodbUpdateTaskIngest) + .next(new sfn.Choice(this, 'SNS Choice (Ingest)') + .when(sfn.Condition.booleanEquals('$.enableSns', true), snsNotificationTaskIngest) + .otherwise(processExecuteTask)); + + const ingestWorkflow = new sfn.StateMachine(this, 'IngestWorkflow', { + stateMachineName: `${cdk.Aws.STACK_NAME}-ingest`, + role: stepFunctionsServiceRole, + definition: ingestWorkflowDefinition + }); + + //cdk_nag + NagSuppressions.addResourceSuppressions( + ingestWorkflow, + [ + { + id: 'AwsSolutions-SF1', + reason: 'Logging handled by DynamoDB Update step and Error Handler lambda' + }, { + id: 'AwsSolutions-SF2', + reason: 'Optional configuration for this solution' + } + ] + ); + + /** + * ProcessWorkflow state machine + * 1: Profiler + * 2: Encoding Profile Check + * 3: Custom jobTemplate OR + * jobTemplate 2160p OR + * jobTemplate 1080p OR + * jobTemplate 720p + * 4: Accelerated Transcoding Check + * 5: Enabled OR + * Preferred OR + * Disabled + * 6: Frame Capture Check + * 7: Frame Capture OR + * No Frame Capture + * 8: Encode Job Submit + * 9: DynamoDB Update + */ + const processWorkflowDefinition = profilerTask + .next(new sfn.Choice(this, 'Encoding Profile Check') + .when(sfn.Condition.booleanEquals('$.isCustomTemplate', true), new sfn.Pass(this, 'Custom jobTemplate')) + .when(sfn.Condition.numberEquals('$.encodingProfile', 2160), new sfn.Pass(this, 'jobTemplate 2160p')) + .when(sfn.Condition.numberEquals('$.encodingProfile', 1080), new sfn.Pass(this, 'jobTemplate 1080p')) + .when(sfn.Condition.numberEquals('$.encodingProfile', 720), new sfn.Pass(this, 'jobTemplate 720p')) + .afterwards()) + .next(new sfn.Choice(this, 'Accelerated Transcoding Check') + .when(sfn.Condition.stringEquals('$.acceleratedTranscoding', 'ENABLED'), new sfn.Pass(this, 'Enabled')) + .when(sfn.Condition.stringEquals('$.acceleratedTranscoding', 'PREFERRED'), new sfn.Pass(this, 'Preferred')) + .when(sfn.Condition.stringEquals('$.acceleratedTranscoding', 'DISABLED'), new sfn.Pass(this, 'Disabled')) + .afterwards()) + .next(new sfn.Choice(this, 'Frame Capture Check') + .when(sfn.Condition.booleanEquals('$.frameCapture', true), new sfn.Pass(this, 'Frame Capture')) + .when(sfn.Condition.booleanEquals('$.frameCapture', false), new sfn.Pass(this, 'No Frame Capture')) + .afterwards()) + .next(encodeTask) + .next(dynamodbUpdateTaskProcess); + + const processWorkflow = new sfn.StateMachine(this, 'ProcessWorkflow', { + stateMachineName: `${cdk.Aws.STACK_NAME}-process`, + role: stepFunctionsServiceRole, + definition: processWorkflowDefinition + }); + + //cdk_nag + NagSuppressions.addResourceSuppressions( + processWorkflow, + [ + { + id: 'AwsSolutions-SF1', + reason: 'Logging handled by DynamoDB Update step and Error Handler lambda' + }, { + id: 'AwsSolutions-SF2', + reason: 'Optional configuration for this solution' + } + ] + ); + + /** + * PublishWorkflow state machine + * 1: Validate Encoding Outputs + * 2: Archive Source Choice + * 3: Archive OR + * Deep Archive + * 4: MediaPackage Choice + * 5: MediaPackage Assets + * 6: DynamoDB Update + * 7: SQS Choice + * 8: SQS Send Message + * 9: SNS Choice + * 10: SNS Notification + * 11: Complete + */ + snsNotificationTaskPublish.next(completeState); + const snsChoicePublish = new sfn.Choice(this, 'SNS Choice (Publish)'); + snsChoicePublish + .when(sfn.Condition.booleanEquals('$.enableSns', true), snsNotificationTaskPublish) + .otherwise(completeState); + + sqsSendMessageTask.next(snsChoicePublish); + const sqsChoice = new sfn.Choice(this, 'SQS Choice'); + sqsChoice + .when(sfn.Condition.booleanEquals('$.enableSqs', true), sqsSendMessageTask) + .otherwise(snsChoicePublish); + + const dynamoChain = sfn.Chain + .start(dynamodbUpdateTaskPublish) + .next(sqsChoice); + + mediaPackageAssetsTask.next(dynamoChain); + const mediaPackageChoice = new sfn.Choice(this, 'MediaPackage Choice'); + mediaPackageChoice + .when(sfn.Condition.booleanEquals('$.enableMediaPackage', true), mediaPackageAssetsTask) + .otherwise(dynamoChain); + + archiveTask.next(mediaPackageChoice); + deepArchiveTask.next(mediaPackageChoice); + const archiveSourceChoice = new sfn.Choice(this, 'Archive Source Choice') + .when(sfn.Condition.stringEquals('$.archiveSource', 'GLACIER'), archiveTask) + .when(sfn.Condition.stringEquals('$.archiveSource', 'DEEP_ARCHIVE'), deepArchiveTask) + .otherwise(mediaPackageChoice); + + const publishWorkflowDefinition = outputValidateTask + .next(archiveSourceChoice); + + const publishWorkflow = new sfn.StateMachine(this, 'PublishWorkflow', { + stateMachineName: `${cdk.Aws.STACK_NAME}-publish`, + role: stepFunctionsServiceRole, + definition: publishWorkflowDefinition + }); + + //cfn_nag + const stateMachineDefaultPolicy = stepFunctionsServiceRole.node.findChild('DefaultPolicy') as iam.Policy; + const cfnDefaultPolicy = stateMachineDefaultPolicy.node.defaultChild as cdk.CfnResource; + cfnDefaultPolicy.cfnOptions.metadata = { + cfn_nag: { + rules_to_suppress: [ + { + id: 'W76', + reason: 'testestesteslsdkfjsdlkfjlskdfklsdljf' + } + ] + } + }; + //cdk_nag + NagSuppressions.addResourceSuppressions( + publishWorkflow, + [ + { + id: 'AwsSolutions-SF1', + reason: 'Logging handled by DynamoDB Update step and Error Handler lambda' + }, { + id: 'AwsSolutions-SF2', + reason: 'Optional configuration for this solution' + } + ] + ); + NagSuppressions.addResourceSuppressions( + cfnDefaultPolicy, + [ + { + id: 'AwsSolutions-IAM5', + reason: 'Statements added to default policy by aws-stepfunctions.StateMachine class' + } + ] + ); + + + /** + * Custom Resource: UUID + */ + const uuid = new cdk.CustomResource(this, 'UUID', { + serviceToken: customResourceLambda.functionArn, + properties: { + Resource: 'UUID' + } + }); + + /** + * Custom Resource: Anonymouse Metric + */ + new cdk.CustomResource(this, 'AnonymousMetric', { // NOSONAR + serviceToken: customResourceLambda.functionArn, + properties: { + Resource: 'AnonymousMetric', + SolutionId: solutionId, + UUID: uuid.getAttString('UUID'), + Version: '%%VERSION%%', + Transcoder: 'MediaConvert', + WorkflowTrigger: workflowTrigger.valueAsString, + Glacier: glacier.valueAsString, + FrameCapture: frameCapture.valueAsString, + EnableMediaPackage: enableMediaPackage.valueAsString + } + }); + + /** + * AppRegistry + */ + const applicationName = `video-on-demand-on-aws-${cdk.Aws.STACK_NAME}`; + const attributeGroup = new appreg.AttributeGroup(this, 'AppRegistryAttributeGroup', { + attributeGroupName: cdk.Aws.STACK_NAME, + description: 'Attribute group for solution information', + attributes: { + ApplicationType: 'AWS-Solutions', + SolutionVersion: '%%VERSION%%', + SolutionID: solutionId, + SolutionName: solutionName + } + }); + const appRegistry = new appreg.Application(this, 'AppRegistryApp', { + applicationName: applicationName, + description: `Service Catalog application to track and manage all your resources. The SolutionId is ${solutionId} and SolutionVersion is %%VERSION%%.` + }); + appRegistry.associateStack(this); + cdk.Tags.of(appRegistry).add('solutionId', solutionId); + cdk.Tags.of(appRegistry).add('SolutionName', solutionName); + cdk.Tags.of(appRegistry).add('SolutionDomain', 'CloudFoundations'); + cdk.Tags.of(appRegistry).add('SolutionVersion', '%%VERSION%%'); + cdk.Tags.of(appRegistry).add('ApplicationType', 'AWS-Solutions'); + + appRegistry.node.addDependency(attributeGroup); + appRegistry.associateAttributeGroup(attributeGroup); + + + /** + * Outputs + */ + new cdk.CfnOutput(this, 'DynamoDBTableName', { // NOSONAR + value: dynamoDBTable.tableName, + description: 'DynamoDB Table', + exportName: `${cdk.Aws.STACK_NAME}:DynamoDBTable` + }); + new cdk.CfnOutput(this, 'SourceBucketName', { // NOSONAR + value: source.bucketName, + description: 'Source Bucket', + exportName: `${cdk.Aws.STACK_NAME}:Source` + }); + new cdk.CfnOutput(this, 'DestinationBucketName', { // NOSONAR + value: destination.bucketName, + description: 'Destination Bucket', + exportName: `${cdk.Aws.STACK_NAME}:Destination` + }); + new cdk.CfnOutput(this, 'CloudFrontDomainName', { // NOSONAR + value: distribution.cloudFrontWebDistribution.domainName, + description: 'CloudFront Domain Name', + exportName: `${cdk.Aws.STACK_NAME}:CloudFront` + }); + new cdk.CfnOutput(this, 'AnonymousMetricUUID', { // NOSONAR + value: uuid.getAttString('UUID'), + description: 'AnonymousMetric UUID', + exportName: `${cdk.Aws.STACK_NAME}:UUID` + }); + new cdk.CfnOutput(this, 'SnsTopicName', { // NOSONAR + value: snsTopic.topicName, + description: 'SNS Topic', + exportName: `${cdk.Aws.STACK_NAME}:SnsTopic` + }); + new cdk.CfnOutput(this, 'SqsUrl', { // NOSONAR + value: sqsQueue.queueUrl, + description: 'SQS Queue URL', + exportName: `${cdk.Aws.STACK_NAME}:SqsQueue` + }); + new cdk.CfnOutput(this, 'SqsArn', { // NOSONAR + value: sqsQueue.queueArn, + description: 'SQS Queue ARN', + exportName: `${cdk.Aws.STACK_NAME}:SqsQueueArn` + }); + new cdk.CfnOutput(this, 'AppRegistryConsole', { // NOSONAR + description: 'AppRegistry', + value: `https://${cdk.Aws.REGION}.console.aws.amazon.com/servicecatalog/home?#applications/${appRegistry.applicationId}`, + exportName: `${cdk.Aws.STACK_NAME}-AppRegistry` + }); + + /** + * Tag all resources with Solution Id + */ + cdk.Tags.of(this).add('SolutionId', solutionId); + + } +} diff --git a/source/cdk/package.json b/source/cdk/package.json new file mode 100644 index 00000000..487de9de --- /dev/null +++ b/source/cdk/package.json @@ -0,0 +1,32 @@ +{ + "name": "video-on-demand-on-aws", + "version": "6.0.0", + "bin": { + "cdk": "bin/vod.js" + }, + "scripts": { + "build": "tsc", + "watch": "tsc -w", + "test": "jest --coverage --updateSnapshot", + "cdk": "cdk" + }, + "devDependencies": { + "@aws-cdk/assert": "2.50.0", + "@types/jest": "^29.2.1", + "@types/node": "18.11.9", + "@types/prettier": "2.7.1", + "jest": "^29.2.2", + "ts-jest": "^29.0.3", + "aws-cdk": "2.50.0", + "ts-node": "^10.9.1", + "typescript": "~4.8.4" + }, + "dependencies": { + "aws-cdk-lib": "2.50.0", + "@aws-solutions-constructs/aws-cloudfront-s3": "2.27.0", + "@aws-cdk/aws-servicecatalogappregistry-alpha": "2.50.0-alpha.0", + "constructs": "^10.1.147", + "source-map-support": "^0.5.21", + "cdk-nag": "^2.19.10" + } +} diff --git a/source/cdk/test/__snapshots__/vod.test.ts.snap b/source/cdk/test/__snapshots__/vod.test.ts.snap new file mode 100644 index 00000000..d6d72592 --- /dev/null +++ b/source/cdk/test/__snapshots__/vod.test.ts.snap @@ -0,0 +1,5417 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`VideoOnDemand Stack Test 1`] = ` +{ + Conditions: { + EnableMediaPackageCondition: { + Fn::Equals: [ + { + Ref: EnableMediaPackage, + }, + Yes, + ], + }, + EnableSnsCondition: { + Fn::Equals: [ + { + Ref: EnableSns, + }, + Yes, + ], + }, + EnableSqsCondition: { + Fn::Equals: [ + { + Ref: EnableSqs, + }, + Yes, + ], + }, + FrameCaptureCondition: { + Fn::Equals: [ + { + Ref: FrameCapture, + }, + Yes, + ], + }, + }, + Description: (SO0021) - Video on Demand on AWS workflow with AWS Step Functions, MediaConvert, MediaPackage, S3, CloudFront and DynamoDB. Version %%VERSION%%, + Mappings: { + AnonymousData: { + SendAnonymousData: { + Data: Yes, + }, + }, + }, + Metadata: { + AWS::CloudFormation::Interface: { + ParameterGroups: [ + { + Label: { + default: Workflow, + }, + Parameters: [ + AdminEmail, + WorkflowTrigger, + Glacier, + EnableSns, + EnableSqs, + ], + }, + { + Label: { + default: AWS Elemental MediaConvert, + }, + Parameters: [ + FrameCapture, + AcceleratedTranscoding, + ], + }, + { + Label: { + default: AWS Elemental MediaPackage, + }, + Parameters: [ + EnableMediaPackage, + ], + }, + ], + ParameterLabels: { + AcceleratedTranscoding: { + default: Accelerated Transcoding, + }, + AdminEmail: { + default: Notification email address, + }, + EnableMediaPackage: { + default: Enable MediaPackage, + }, + EnableSns: { + default: Enable SNS Notifications, + }, + EnableSqs: { + default: Enable SQS Messaging, + }, + FrameCapture: { + default: Enable Frame Capture, + }, + Glacier: { + default: Archive source content, + }, + WorkflowTrigger: { + default: Workflow trigger, + }, + }, + }, + }, + Outputs: { + AnonymousMetricUUID: { + Description: AnonymousMetric UUID, + Export: { + Name: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + :UUID, + ], + ], + }, + }, + Value: { + Fn::GetAtt: [ + UUID, + UUID, + ], + }, + }, + AppRegistryConsole: { + Description: AppRegistry, + Export: { + Name: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -AppRegistry, + ], + ], + }, + }, + Value: { + Fn::Join: [ + , + [ + https://, + { + Ref: AWS::Region, + }, + .console.aws.amazon.com/servicecatalog/home?#applications/, + { + Fn::GetAtt: [ + AppRegistryApp5349BE86, + Id, + ], + }, + ], + ], + }, + }, + CloudFrontDomainName: { + Description: CloudFront Domain Name, + Export: { + Name: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + :CloudFront, + ], + ], + }, + }, + Value: { + Fn::GetAtt: [ + CloudFrontToS3CloudFrontDistribution241D9866, + DomainName, + ], + }, + }, + DestinationBucketName: { + Description: Destination Bucket, + Export: { + Name: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + :Destination, + ], + ], + }, + }, + Value: { + Ref: Destination920A3C57, + }, + }, + DynamoDBTableName: { + Description: DynamoDB Table, + Export: { + Name: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + :DynamoDBTable, + ], + ], + }, + }, + Value: { + Ref: DynamoDBTable59784FC0, + }, + }, + SnsTopicName: { + Description: SNS Topic, + Export: { + Name: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + :SnsTopic, + ], + ], + }, + }, + Value: { + Fn::GetAtt: [ + SnsTopic2C1570A4, + TopicName, + ], + }, + }, + SourceBucketName: { + Description: Source Bucket, + Export: { + Name: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + :Source, + ], + ], + }, + }, + Value: { + Ref: Source71E471F1, + }, + }, + SqsArn: { + Description: SQS Queue ARN, + Export: { + Name: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + :SqsQueueArn, + ], + ], + }, + }, + Value: { + Fn::GetAtt: [ + SqsQueue13597403, + Arn, + ], + }, + }, + SqsUrl: { + Description: SQS Queue URL, + Export: { + Name: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + :SqsQueue, + ], + ], + }, + }, + Value: { + Ref: SqsQueue13597403, + }, + }, + }, + Parameters: { + AcceleratedTranscoding: { + AllowedValues: [ + ENABLED, + DISABLED, + PREFERRED, + ], + Default: PREFERRED, + Description: Enable accelerated transcoding in AWS Elemental MediaConvert. PREFERRED will only use acceleration if the input files is supported. ENABLED accleration is applied to all files (this will fail for unsupported file types) see MediaConvert Documentation for more detail https://docs.aws.amazon.com/mediaconvert/latest/ug/accelerated-transcoding.html, + Type: String, + }, + AdminEmail: { + AllowedPattern: ^[_A-Za-z0-9-\\+]+(\\.[_A-Za-z0-9-]+)*@[A-Za-z0-9-]+(\\.[A-Za-z0-9]+)*(\\.[A-Za-z]{2,})$, + Description: Email address for SNS notifications (subscribed users will receive ingest, publishing, and error notifications), + Type: String, + }, + EnableMediaPackage: { + AllowedValues: [ + Yes, + No, + ], + Default: No, + Description: If enabled, MediaPackage VOD will be included in the workflow, + Type: String, + }, + EnableSns: { + AllowedValues: [ + Yes, + No, + ], + Default: Yes, + Description: Enable Ingest and Publish email notifications, error messages are not affected by this parameter., + Type: String, + }, + EnableSqs: { + AllowedValues: [ + Yes, + No, + ], + Default: Yes, + Description: Publish the workflow results to an SQS queue to injest upstream, + Type: String, + }, + FrameCapture: { + AllowedValues: [ + Yes, + No, + ], + Default: No, + Description: If enabled, frame capture is added to the job submitted to MediaConvert, + Type: String, + }, + Glacier: { + AllowedValues: [ + DISABLED, + GLACIER, + DEEP_ARCHIVE, + ], + Default: DISABLED, + Description: If enabled, source assets will be tagged for archiving to Glacier or Glacier Deep Archive once the workflow is complete, + Type: String, + }, + WorkflowTrigger: { + AllowedValues: [ + VideoFile, + MetadataFile, + ], + Default: VideoFile, + Description: How the workflow will be triggered (source video upload to S3 or source metadata file upload), + Type: String, + }, + }, + Resources: { + AnonymousMetric: { + DeletionPolicy: Delete, + Properties: { + EnableMediaPackage: { + Ref: EnableMediaPackage, + }, + FrameCapture: { + Ref: FrameCapture, + }, + Glacier: { + Ref: Glacier, + }, + Resource: AnonymousMetric, + ServiceToken: { + Fn::GetAtt: [ + CustomResource8CDCD7A7, + Arn, + ], + }, + SolutionId: SO0021, + Transcoder: MediaConvert, + UUID: { + Fn::GetAtt: [ + UUID, + UUID, + ], + }, + Version: %%VERSION%%, + WorkflowTrigger: { + Ref: WorkflowTrigger, + }, + }, + Type: AWS::CloudFormation::CustomResource, + UpdateReplacePolicy: Delete, + }, + AppRegistryApp5349BE86: { + DependsOn: [ + AppRegistryAttributeGroup7AF07446, + ], + Properties: { + Description: Service Catalog application to track and manage all your resources. The SolutionId is SO0021 and SolutionVersion is %%VERSION%%., + Name: { + Fn::Join: [ + , + [ + video-on-demand-on-aws-, + { + Ref: AWS::StackName, + }, + ], + ], + }, + Tags: { + ApplicationType: AWS-Solutions, + SolutionDomain: CloudFoundations, + SolutionId: SO0021, + SolutionName: Video on Demand on AWS, + SolutionVersion: %%VERSION%%, + solutionId: SO0021, + }, + }, + Type: AWS::ServiceCatalogAppRegistry::Application, + }, + AppRegistryAppAttributeGroupAssociation89f88b663a927A6EC94A: { + DependsOn: [ + AppRegistryAttributeGroup7AF07446, + ], + Properties: { + Application: { + Fn::GetAtt: [ + AppRegistryApp5349BE86, + Id, + ], + }, + AttributeGroup: { + Fn::GetAtt: [ + AppRegistryAttributeGroup7AF07446, + Id, + ], + }, + }, + Type: AWS::ServiceCatalogAppRegistry::AttributeGroupAssociation, + }, + AppRegistryAppResourceAssociationa70e26ed0b0e5F5D9D9F: { + DependsOn: [ + AppRegistryAttributeGroup7AF07446, + ], + Properties: { + Application: { + Fn::GetAtt: [ + AppRegistryApp5349BE86, + Id, + ], + }, + Resource: { + Ref: AWS::StackId, + }, + ResourceType: CFN_STACK, + }, + Type: AWS::ServiceCatalogAppRegistry::ResourceAssociation, + }, + AppRegistryAttributeGroup7AF07446: { + Properties: { + Attributes: { + ApplicationType: AWS-Solutions, + SolutionID: SO0021, + SolutionName: Video on Demand on AWS, + SolutionVersion: %%VERSION%%, + }, + Description: Attribute group for solution information, + Name: { + Ref: AWS::StackName, + }, + Tags: { + SolutionId: SO0021, + }, + }, + Type: AWS::ServiceCatalogAppRegistry::AttributeGroup, + }, + ArchiveSourceLambda320F09D9: { + DependsOn: [ + ArchiveSourcePolicy1D98A4F5, + ArchiveSourceRole49DA53ED, + ], + Metadata: { + cfn_nag: { + rules_to_suppress: [ + { + id: W89, + reason: Lambda functions do not need a VPC, + }, + { + id: W92, + reason: Lambda do not need ReservedConcurrentExecutions in this case, + }, + { + id: W58, + reason: Invalid warning: function has access to cloudwatch, + }, + ], + }, + }, + Properties: { + Code: { + S3Bucket: { + Fn::Sub: cdk-hnb659fds-assets-\${AWS::AccountId}-\${AWS::Region}, + }, + S3Key: [HASH REMOVED].zip, + }, + Description: Updates tags on source files to enable Glacier, + Environment: { + Variables: { + AWS_NODEJS_CONNECTION_REUSE_ENABLED: 1, + ErrorHandler: { + Fn::GetAtt: [ + ErrorHandlerLambdaFC10367C, + Arn, + ], + }, + SOLUTION_IDENTIFIER: AwsSolution/SO0021/%%VERSION%%, + }, + }, + FunctionName: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -archive-source, + ], + ], + }, + Handler: index.handler, + Role: { + Fn::GetAtt: [ + ArchiveSourceRole49DA53ED, + Arn, + ], + }, + Runtime: nodejs16.x, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + Timeout: 120, + }, + Type: AWS::Lambda::Function, + }, + ArchiveSourcePolicy1D98A4F5: { + Metadata: { + cdk_nag: { + rules_to_suppress: [ + { + id: AwsSolutions-IAM5, + reason: * is used so that the Lambda function can create log groups, + }, + ], + }, + }, + Properties: { + PolicyDocument: { + Statement: [ + { + Action: s3:PutObjectTagging, + Effect: Allow, + Resource: { + Fn::Join: [ + , + [ + { + Fn::GetAtt: [ + Source71E471F1, + Arn, + ], + }, + /*, + ], + ], + }, + }, + { + Action: lambda:InvokeFunction, + Effect: Allow, + Resource: { + Fn::GetAtt: [ + ErrorHandlerLambdaFC10367C, + Arn, + ], + }, + }, + { + Action: [ + logs:CreateLogGroup, + logs:CreateLogStream, + logs:PutLogEvents, + ], + Effect: Allow, + Resource: { + Fn::Join: [ + , + [ + arn:, + { + Ref: AWS::Partition, + }, + :logs:, + { + Ref: AWS::Region, + }, + :, + { + Ref: AWS::AccountId, + }, + :log-group:/aws/lambda/*, + ], + ], + }, + }, + ], + Version: 2012-10-17, + }, + PolicyName: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -archive-source-role, + ], + ], + }, + Roles: [ + { + Ref: ArchiveSourceRole49DA53ED, + }, + ], + }, + Type: AWS::IAM::Policy, + }, + ArchiveSourceRole49DA53ED: { + Metadata: { + cfn_nag: { + rules_to_suppress: [ + { + id: W11, + reason: * is used so that the Lambda function can create log groups, + }, + ], + }, + }, + Properties: { + AssumeRolePolicyDocument: { + Statement: [ + { + Action: sts:AssumeRole, + Effect: Allow, + Principal: { + Service: lambda.amazonaws.com, + }, + }, + ], + Version: 2012-10-17, + }, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + }, + Type: AWS::IAM::Role, + }, + CachePolicy26D8A535: { + Properties: { + CachePolicyConfig: { + DefaultTTL: 86400, + MaxTTL: 86400, + MinTTL: 0, + Name: { + Fn::Join: [ + , + [ + VideoOnDemandCachePolicy7E8194AA-, + { + Ref: AWS::Region, + }, + ], + ], + }, + ParametersInCacheKeyAndForwardedToOrigin: { + CookiesConfig: { + CookieBehavior: none, + }, + EnableAcceptEncodingBrotli: false, + EnableAcceptEncodingGzip: false, + HeadersConfig: { + HeaderBehavior: whitelist, + Headers: [ + Origin, + Access-Control-Request-Method, + Access-Control-Request-Headers, + ], + }, + QueryStringsConfig: { + QueryStringBehavior: none, + }, + }, + }, + }, + Type: AWS::CloudFront::CachePolicy, + }, + CloudFrontToS3CloudFrontDistribution241D9866: { + Metadata: { + cdk_nag: { + rules_to_suppress: [ + { + id: AwsSolutions-CFR1, + reason: Use case does not warrant CloudFront Geo restriction, + }, + { + id: AwsSolutions-CFR2, + reason: Use case does not warrant CloudFront integration with AWS WAF, + }, + { + id: AwsSolutions-CFR4, + reason: CloudFront automatically sets the security policy to TLSv1 when the distribution uses the CloudFront domain name, + }, + ], + }, + cfn_nag: { + rules_to_suppress: [ + { + id: W70, + reason: Since the distribution uses the CloudFront domain name, CloudFront automatically sets the security policy to TLSv1 regardless of the value of MinimumProtocolVersion, + }, + ], + }, + }, + Properties: { + DistributionConfig: { + DefaultCacheBehavior: { + AllowedMethods: [ + GET, + HEAD, + ], + CachePolicyId: { + Ref: CachePolicy26D8A535, + }, + Compress: true, + TargetOriginId: VideoOnDemandCloudFrontToS3CloudFrontDistributionOrigin1CC6EEFFD, + ViewerProtocolPolicy: redirect-to-https, + }, + DefaultRootObject: index.html, + Enabled: true, + HttpVersion: http2, + IPV6Enabled: true, + Logging: { + Bucket: { + Fn::GetAtt: [ + Logs6819BB44, + RegionalDomainName, + ], + }, + Prefix: cloudfront-logs/, + }, + Origins: [ + { + DomainName: { + Fn::GetAtt: [ + Destination920A3C57, + RegionalDomainName, + ], + }, + Id: VideoOnDemandCloudFrontToS3CloudFrontDistributionOrigin1CC6EEFFD, + S3OriginConfig: { + OriginAccessIdentity: { + Fn::Join: [ + , + [ + origin-access-identity/cloudfront/, + { + Ref: CloudFrontToS3CloudFrontDistributionOrigin1S3OriginB0637B8F, + }, + ], + ], + }, + }, + }, + ], + PriceClass: PriceClass_100, + }, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + }, + Type: AWS::CloudFront::Distribution, + }, + CloudFrontToS3CloudFrontDistributionOrigin1S3OriginB0637B8F: { + Properties: { + CloudFrontOriginAccessIdentityConfig: { + Comment: Identity for VideoOnDemandCloudFrontToS3CloudFrontDistributionOrigin1CC6EEFFD, + }, + }, + Type: AWS::CloudFront::CloudFrontOriginAccessIdentity, + }, + CustomResource8CDCD7A7: { + DependsOn: [ + CustomResourcePolicy79526710, + CustomResourceRoleAB1EF463, + ], + Metadata: { + cfn_nag: { + rules_to_suppress: [ + { + id: W58, + reason: Invalid warning: function has access to cloudwatch, + }, + { + id: W89, + reason: This CustomResource does not need to be deployed inside a VPC, + }, + { + id: W92, + reason: This CustomResource does not need to define ReservedConcurrentExecutions to reserve simultaneous executions, + }, + ], + }, + }, + Properties: { + Code: { + S3Bucket: { + Fn::Sub: cdk-hnb659fds-assets-\${AWS::AccountId}-\${AWS::Region}, + }, + S3Key: [HASH REMOVED].zip, + }, + Description: Used to deploy resources not supported by CloudFormation, + Environment: { + Variables: { + SOLUTION_IDENTIFIER: AwsSolution/SO0021/%%VERSION%%, + }, + }, + FunctionName: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -custom-resource, + ], + ], + }, + Handler: index.handler, + Role: { + Fn::GetAtt: [ + CustomResourceRoleAB1EF463, + Arn, + ], + }, + Runtime: nodejs16.x, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + Timeout: 30, + }, + Type: AWS::Lambda::Function, + }, + CustomResourcePolicy79526710: { + Metadata: { + cdk_nag: { + rules_to_suppress: [ + { + id: AwsSolutions-IAM5, + reason: Resource ARNs are not generated at the time of policy creation, + }, + ], + }, + cfn_nag: { + rules_to_suppress: [ + { + id: W12, + reason: * is required to create CloudWatch logs and interact with MediaConvert / MediaPackage actions that do not support resource level permissions, + }, + { + id: W76, + reason: High complexity due to number of policy statements needed for creating all custom resources, + }, + ], + }, + }, + Properties: { + PolicyDocument: { + Statement: [ + { + Action: [ + logs:CreateLogGroup, + logs:CreateLogStream, + logs:PutLogEvents, + ], + Effect: Allow, + Resource: { + Fn::Join: [ + , + [ + arn:, + { + Ref: AWS::Partition, + }, + :logs:, + { + Ref: AWS::Region, + }, + :, + { + Ref: AWS::AccountId, + }, + :log-group:/aws/lambda/*, + ], + ], + }, + }, + { + Action: [ + s3:PutBucketNotification, + s3:PutObject, + s3:PutObjectAcl, + ], + Effect: Allow, + Resource: { + Fn::GetAtt: [ + Source71E471F1, + Arn, + ], + }, + }, + { + Action: [ + mediaconvert:CreatePreset, + mediaconvert:CreateJobTemplate, + mediaconvert:DeletePreset, + mediaconvert:DeleteJobTemplate, + mediaconvert:DescribeEndpoints, + mediaconvert:ListJobTemplates, + mediaconvert:TagResource, + mediaconvert:UntagResource, + ], + Effect: Allow, + Resource: { + Fn::Join: [ + , + [ + arn:, + { + Ref: AWS::Partition, + }, + :mediaconvert:, + { + Ref: AWS::Region, + }, + :, + { + Ref: AWS::AccountId, + }, + :*, + ], + ], + }, + }, + { + Action: [ + mediapackage-vod:DeleteAsset, + mediapackage-vod:DeletePackagingConfiguration, + ], + Effect: Allow, + Resource: [ + { + Fn::Join: [ + , + [ + arn:, + { + Ref: AWS::Partition, + }, + :mediapackage-vod:, + { + Ref: AWS::Region, + }, + :, + { + Ref: AWS::AccountId, + }, + :assets/*, + ], + ], + }, + { + Fn::Join: [ + , + [ + arn:, + { + Ref: AWS::Partition, + }, + :mediapackage-vod:, + { + Ref: AWS::Region, + }, + :, + { + Ref: AWS::AccountId, + }, + :packaging-configurations/packaging-config-*, + ], + ], + }, + ], + }, + { + Action: [ + mediapackage-vod:DescribePackagingGroup, + mediapackage-vod:DeletePackagingGroup, + ], + Effect: Allow, + Resource: { + Fn::Join: [ + , + [ + arn:, + { + Ref: AWS::Partition, + }, + :mediapackage-vod:, + { + Ref: AWS::Region, + }, + :, + { + Ref: AWS::AccountId, + }, + :packaging-groups/, + { + Ref: AWS::StackName, + }, + -packaging-group, + ], + ], + }, + }, + { + Action: [ + mediapackage-vod:CreatePackagingConfiguration, + mediapackage-vod:CreatePackagingGroup, + mediapackage-vod:ListAssets, + mediapackage-vod:ListPackagingConfigurations, + mediapackage-vod:ListPackagingGroups, + mediapackage-vod:TagResource, + mediapackage-vod:UntagResource, + ], + Effect: Allow, + Resource: *, + }, + { + Action: [ + cloudfront:GetDistributionConfig, + cloudfront:UpdateDistribution, + ], + Effect: Allow, + Resource: { + Fn::Join: [ + , + [ + arn:, + { + Ref: AWS::Partition, + }, + :cloudfront::, + { + Ref: AWS::AccountId, + }, + :distribution/, + { + Ref: CloudFrontToS3CloudFrontDistribution241D9866, + }, + ], + ], + }, + }, + ], + Version: 2012-10-17, + }, + PolicyName: CustomResourcePolicy79526710, + Roles: [ + { + Ref: CustomResourceRoleAB1EF463, + }, + ], + }, + Type: AWS::IAM::Policy, + }, + CustomResourceRoleAB1EF463: { + Metadata: { + cfn_nag: { + rules_to_suppress: [ + { + id: W11, + reason: * is required to create CloudWatch logs and interact with MediaConvert / MediaPackage actions that do not support resource level permissions, + }, + { + id: W76, + reason: All policies are required by the custom resource., + }, + ], + }, + }, + Properties: { + AssumeRolePolicyDocument: { + Statement: [ + { + Action: sts:AssumeRole, + Effect: Allow, + Principal: { + Service: lambda.amazonaws.com, + }, + }, + ], + Version: 2012-10-17, + }, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + }, + Type: AWS::IAM::Role, + }, + Destination920A3C57: { + DeletionPolicy: Retain, + Metadata: { + cdk_nag: { + rules_to_suppress: [ + { + id: AwsSolutions-S10, + reason: Bucket is private and is not using HTTP, + }, + ], + }, + }, + Properties: { + BucketEncryption: { + ServerSideEncryptionConfiguration: [ + { + ServerSideEncryptionByDefault: { + SSEAlgorithm: AES256, + }, + }, + ], + }, + CorsConfiguration: { + CorsRules: [ + { + AllowedHeaders: [ + *, + ], + AllowedMethods: [ + GET, + ], + AllowedOrigins: [ + *, + ], + MaxAge: 3000, + }, + ], + }, + LoggingConfiguration: { + DestinationBucketName: { + Ref: Logs6819BB44, + }, + LogFilePrefix: destination-bucket-logs/, + }, + PublicAccessBlockConfiguration: { + BlockPublicAcls: true, + BlockPublicPolicy: true, + IgnorePublicAcls: true, + RestrictPublicBuckets: true, + }, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + }, + Type: AWS::S3::Bucket, + UpdateReplacePolicy: Retain, + }, + DestinationPolicy7982387E: { + Metadata: { + cdk_nag: { + rules_to_suppress: [ + { + id: AwsSolutions-S10, + reason: Bucket is private and is not using HTTP, + }, + ], + }, + cfn_nag: { + rules_to_suppress: [ + { + id: F16, + reason: Public website bucket policy requires a wildcard principal, + }, + ], + }, + }, + Properties: { + Bucket: { + Ref: Destination920A3C57, + }, + PolicyDocument: { + Statement: [ + { + Action: s3:GetObject, + Effect: Allow, + Principal: { + CanonicalUser: { + Fn::GetAtt: [ + CloudFrontToS3CloudFrontDistributionOrigin1S3OriginB0637B8F, + S3CanonicalUserId, + ], + }, + }, + Resource: { + Fn::Join: [ + , + [ + { + Fn::GetAtt: [ + Destination920A3C57, + Arn, + ], + }, + /*, + ], + ], + }, + }, + ], + Version: 2012-10-17, + }, + }, + Type: AWS::S3::BucketPolicy, + }, + DynamoDBTable59784FC0: { + DeletionPolicy: Retain, + Metadata: { + cfn_nag: { + rules_to_suppress: [ + { + id: W28, + reason: Table name is set to the stack name, + }, + { + id: W74, + reason: The DynamoDB table is configured to use the default encryption, + }, + ], + }, + }, + Properties: { + AttributeDefinitions: [ + { + AttributeName: guid, + AttributeType: S, + }, + { + AttributeName: srcBucket, + AttributeType: S, + }, + { + AttributeName: startTime, + AttributeType: S, + }, + ], + BillingMode: PAY_PER_REQUEST, + GlobalSecondaryIndexes: [ + { + IndexName: srcBucket-startTime-index, + KeySchema: [ + { + AttributeName: srcBucket, + KeyType: HASH, + }, + { + AttributeName: startTime, + KeyType: RANGE, + }, + ], + Projection: { + ProjectionType: ALL, + }, + }, + ], + KeySchema: [ + { + AttributeName: guid, + KeyType: HASH, + }, + ], + PointInTimeRecoverySpecification: { + PointInTimeRecoveryEnabled: true, + }, + TableName: { + Ref: AWS::StackName, + }, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + }, + Type: AWS::DynamoDB::Table, + UpdateReplacePolicy: Retain, + }, + DynamoUpdateLambda0DF14C26: { + DependsOn: [ + DynamoUpdatePolicy7828BC4E, + DynamoUpdateRoleB73E97DD, + ], + Metadata: { + cfn_nag: { + rules_to_suppress: [ + { + id: W89, + reason: Lambda functions do not need a VPC, + }, + { + id: W92, + reason: Lambda do not need ReservedConcurrentExecutions in this case, + }, + { + id: W58, + reason: Invalid warning: function has access to cloudwatch, + }, + ], + }, + }, + Properties: { + Code: { + S3Bucket: { + Fn::Sub: cdk-hnb659fds-assets-\${AWS::AccountId}-\${AWS::Region}, + }, + S3Key: [HASH REMOVED].zip, + }, + Description: Updates DynamoDB with event data, + Environment: { + Variables: { + AWS_NODEJS_CONNECTION_REUSE_ENABLED: 1, + DynamoDBTable: { + Ref: DynamoDBTable59784FC0, + }, + ErrorHandler: { + Fn::GetAtt: [ + ErrorHandlerLambdaFC10367C, + Arn, + ], + }, + SOLUTION_IDENTIFIER: AwsSolution/SO0021/%%VERSION%%, + }, + }, + FunctionName: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -dynamo, + ], + ], + }, + Handler: index.handler, + Role: { + Fn::GetAtt: [ + DynamoUpdateRoleB73E97DD, + Arn, + ], + }, + Runtime: nodejs16.x, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + Timeout: 120, + }, + Type: AWS::Lambda::Function, + }, + DynamoUpdatePolicy7828BC4E: { + Metadata: { + cdk_nag: { + rules_to_suppress: [ + { + id: AwsSolutions-IAM5, + reason: * is used so that the Lambda function can create log groups, + }, + ], + }, + }, + Properties: { + PolicyDocument: { + Statement: [ + { + Action: dynamodb:UpdateItem, + Effect: Allow, + Resource: { + Fn::GetAtt: [ + DynamoDBTable59784FC0, + Arn, + ], + }, + }, + { + Action: lambda:InvokeFunction, + Effect: Allow, + Resource: { + Fn::GetAtt: [ + ErrorHandlerLambdaFC10367C, + Arn, + ], + }, + }, + { + Action: [ + logs:CreateLogGroup, + logs:CreateLogStream, + logs:PutLogEvents, + ], + Effect: Allow, + Resource: { + Fn::Join: [ + , + [ + arn:, + { + Ref: AWS::Partition, + }, + :logs:, + { + Ref: AWS::Region, + }, + :, + { + Ref: AWS::AccountId, + }, + :log-group:/aws/lambda/*, + ], + ], + }, + }, + ], + Version: 2012-10-17, + }, + PolicyName: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -dynamo-role, + ], + ], + }, + Roles: [ + { + Ref: DynamoUpdateRoleB73E97DD, + }, + ], + }, + Type: AWS::IAM::Policy, + }, + DynamoUpdateRoleB73E97DD: { + Metadata: { + cfn_nag: { + rules_to_suppress: [ + { + id: W11, + reason: * is used so that the Lambda function can create log groups, + }, + ], + }, + }, + Properties: { + AssumeRolePolicyDocument: { + Statement: [ + { + Action: sts:AssumeRole, + Effect: Allow, + Principal: { + Service: lambda.amazonaws.com, + }, + }, + ], + Version: 2012-10-17, + }, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + }, + Type: AWS::IAM::Role, + }, + EncodeCompleteRuleAllowEventRuleVideoOnDemandStepFunctionsLambda0CB4E0F5435F3721: { + Properties: { + Action: lambda:InvokeFunction, + FunctionName: { + Fn::GetAtt: [ + StepFunctionsLambda8B4F69C7, + Arn, + ], + }, + Principal: events.amazonaws.com, + SourceArn: { + Fn::GetAtt: [ + EncodeCompleteRuleE2F74999, + Arn, + ], + }, + }, + Type: AWS::Lambda::Permission, + }, + EncodeCompleteRuleE2F74999: { + Properties: { + Description: MediaConvert Completed event rule, + EventPattern: { + detail: { + status: [ + COMPLETE, + ], + userMetadata: { + workflow: [ + { + Ref: AWS::StackName, + }, + ], + }, + }, + source: [ + aws.mediaconvert, + ], + }, + Name: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -EncodeComplete, + ], + ], + }, + State: ENABLED, + Targets: [ + { + Arn: { + Fn::GetAtt: [ + StepFunctionsLambda8B4F69C7, + Arn, + ], + }, + Id: Target0, + }, + ], + }, + Type: AWS::Events::Rule, + }, + EncodeErrorRule4CB53BA6: { + Properties: { + Description: MediaConvert Error event rule, + EventPattern: { + detail: { + status: [ + ERROR, + ], + userMetadata: { + workflow: [ + { + Ref: AWS::StackName, + }, + ], + }, + }, + source: [ + aws.mediaconvert, + ], + }, + Name: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -EncodeError, + ], + ], + }, + State: ENABLED, + Targets: [ + { + Arn: { + Fn::GetAtt: [ + ErrorHandlerLambdaFC10367C, + Arn, + ], + }, + Id: Target0, + }, + ], + }, + Type: AWS::Events::Rule, + }, + EncodeErrorRuleAllowEventRuleVideoOnDemandErrorHandlerLambda7A429D30DEEF909F: { + Properties: { + Action: lambda:InvokeFunction, + FunctionName: { + Fn::GetAtt: [ + ErrorHandlerLambdaFC10367C, + Arn, + ], + }, + Principal: events.amazonaws.com, + SourceArn: { + Fn::GetAtt: [ + EncodeErrorRule4CB53BA6, + Arn, + ], + }, + }, + Type: AWS::Lambda::Permission, + }, + EncodeLambdaDADCB2BB: { + DependsOn: [ + EncodePolicy89CB6B7C, + EncodeRole36198881, + ], + Metadata: { + cfn_nag: { + rules_to_suppress: [ + { + id: W89, + reason: Lambda functions do not need a VPC, + }, + { + id: W92, + reason: Lambda do not need ReservedConcurrentExecutions in this case, + }, + { + id: W58, + reason: Invalid warning: function has access to cloudwatch, + }, + ], + }, + }, + Properties: { + Code: { + S3Bucket: { + Fn::Sub: cdk-hnb659fds-assets-\${AWS::AccountId}-\${AWS::Region}, + }, + S3Key: [HASH REMOVED].zip, + }, + Description: Creates a MediaConvert encode job, + Environment: { + Variables: { + AWS_NODEJS_CONNECTION_REUSE_ENABLED: 1, + EndPoint: { + Fn::GetAtt: [ + MediaConvertEndPoint, + EndpointUrl, + ], + }, + ErrorHandler: { + Fn::GetAtt: [ + ErrorHandlerLambdaFC10367C, + Arn, + ], + }, + MediaConvertRole: { + Fn::GetAtt: [ + MediaConvertRole031A64A9, + Arn, + ], + }, + SOLUTION_IDENTIFIER: AwsSolution/SO0021/%%VERSION%%, + }, + }, + FunctionName: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -encode, + ], + ], + }, + Handler: index.handler, + Role: { + Fn::GetAtt: [ + EncodeRole36198881, + Arn, + ], + }, + Runtime: nodejs16.x, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + Timeout: 120, + }, + Type: AWS::Lambda::Function, + }, + EncodePolicy89CB6B7C: { + Metadata: { + cdk_nag: { + rules_to_suppress: [ + { + id: AwsSolutions-IAM5, + reason: * is used so that the Lambda function can create log groups, + }, + ], + }, + }, + Properties: { + PolicyDocument: { + Statement: [ + { + Action: [ + mediaconvert:CreateJob, + mediaconvert:GetJobTemplate, + mediaconvert:TagResource, + mediaconvert:UntagResource, + ], + Effect: Allow, + Resource: { + Fn::Join: [ + , + [ + arn:, + { + Ref: AWS::Partition, + }, + :mediaconvert:, + { + Ref: AWS::Region, + }, + :, + { + Ref: AWS::AccountId, + }, + :*, + ], + ], + }, + }, + { + Action: iam:PassRole, + Effect: Allow, + Resource: { + Fn::GetAtt: [ + MediaConvertRole031A64A9, + Arn, + ], + }, + }, + { + Action: lambda:InvokeFunction, + Effect: Allow, + Resource: { + Fn::GetAtt: [ + ErrorHandlerLambdaFC10367C, + Arn, + ], + }, + }, + { + Action: [ + logs:CreateLogGroup, + logs:CreateLogStream, + logs:PutLogEvents, + ], + Effect: Allow, + Resource: { + Fn::Join: [ + , + [ + arn:, + { + Ref: AWS::Partition, + }, + :logs:, + { + Ref: AWS::Region, + }, + :, + { + Ref: AWS::AccountId, + }, + :log-group:/aws/lambda/*, + ], + ], + }, + }, + ], + Version: 2012-10-17, + }, + PolicyName: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -encode-role, + ], + ], + }, + Roles: [ + { + Ref: EncodeRole36198881, + }, + ], + }, + Type: AWS::IAM::Policy, + }, + EncodeRole36198881: { + Metadata: { + cfn_nag: { + rules_to_suppress: [ + { + id: W11, + reason: * is used so that the Lambda function can create log groups, + }, + ], + }, + }, + Properties: { + AssumeRolePolicyDocument: { + Statement: [ + { + Action: sts:AssumeRole, + Effect: Allow, + Principal: { + Service: lambda.amazonaws.com, + }, + }, + ], + Version: 2012-10-17, + }, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + }, + Type: AWS::IAM::Role, + }, + ErrorHandlerLambdaCloudWatchLambdaInvokeErrors73673741: { + DependsOn: [ + ErrorHandlerPolicyF920541C, + ErrorHandlerRole361CFEB7, + ], + Properties: { + Action: lambda:InvokeFunction, + FunctionName: { + Fn::GetAtt: [ + ErrorHandlerLambdaFC10367C, + Arn, + ], + }, + Principal: events.amazonaws.com, + SourceArn: { + Fn::GetAtt: [ + EncodeErrorRule4CB53BA6, + Arn, + ], + }, + }, + Type: AWS::Lambda::Permission, + }, + ErrorHandlerLambdaFC10367C: { + DependsOn: [ + ErrorHandlerPolicyF920541C, + ErrorHandlerRole361CFEB7, + ], + Metadata: { + cfn_nag: { + rules_to_suppress: [ + { + id: W58, + reason: Invalid warning: function has access to cloudwatch, + }, + { + id: W89, + reason: This resource does not need to be deployed inside a VPC, + }, + { + id: W92, + reason: This resource does not need to define ReservedConcurrentExecutions to reserve simultaneous executions, + }, + ], + }, + }, + Properties: { + Code: { + S3Bucket: { + Fn::Sub: cdk-hnb659fds-assets-\${AWS::AccountId}-\${AWS::Region}, + }, + S3Key: [HASH REMOVED].zip, + }, + Description: Captures and processes workflow errors, + Environment: { + Variables: { + AWS_NODEJS_CONNECTION_REUSE_ENABLED: 1, + DynamoDBTable: { + Ref: DynamoDBTable59784FC0, + }, + SOLUTION_IDENTIFIER: AwsSolution/SO0021/%%VERSION%%, + SnsTopic: { + Ref: SnsTopic2C1570A4, + }, + }, + }, + FunctionName: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -error-handler, + ], + ], + }, + Handler: index.handler, + Role: { + Fn::GetAtt: [ + ErrorHandlerRole361CFEB7, + Arn, + ], + }, + Runtime: nodejs16.x, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + Timeout: 120, + }, + Type: AWS::Lambda::Function, + }, + ErrorHandlerPolicyF920541C: { + Metadata: { + cdk_nag: { + rules_to_suppress: [ + { + id: AwsSolutions-IAM5, + reason: * is used so that the Lambda function can create log groups, + }, + ], + }, + }, + Properties: { + PolicyDocument: { + Statement: [ + { + Action: sns:Publish, + Condition: { + Bool: { + aws:SecureTransport: true, + }, + }, + Effect: Allow, + Resource: { + Ref: SnsTopic2C1570A4, + }, + }, + { + Action: dynamodb:UpdateItem, + Effect: Allow, + Resource: { + Fn::GetAtt: [ + DynamoDBTable59784FC0, + Arn, + ], + }, + }, + { + Action: [ + logs:CreateLogGroup, + logs:CreateLogStream, + logs:PutLogEvents, + ], + Effect: Allow, + Resource: { + Fn::Join: [ + , + [ + arn:, + { + Ref: AWS::Partition, + }, + :logs:, + { + Ref: AWS::Region, + }, + :, + { + Ref: AWS::AccountId, + }, + :log-group:/aws/lambda/*, + ], + ], + }, + }, + ], + Version: 2012-10-17, + }, + PolicyName: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -error-handler-role, + ], + ], + }, + Roles: [ + { + Ref: ErrorHandlerRole361CFEB7, + }, + ], + }, + Type: AWS::IAM::Policy, + }, + ErrorHandlerRole361CFEB7: { + Metadata: { + cfn_nag: { + rules_to_suppress: [ + { + id: W11, + reason: * is used so that the Lambda function can create log groups, + }, + ], + }, + }, + Properties: { + AssumeRolePolicyDocument: { + Statement: [ + { + Action: sts:AssumeRole, + Effect: Allow, + Principal: { + Service: lambda.amazonaws.com, + }, + }, + ], + Version: 2012-10-17, + }, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + }, + Type: AWS::IAM::Role, + }, + IngestWorkflow58F2BCD4: { + DependsOn: [ + StepFunctionsServiceRoleDefaultPolicy6AD67B91, + StepFunctionsServiceRole2A83B843, + ], + Metadata: { + cdk_nag: { + rules_to_suppress: [ + { + id: AwsSolutions-SF1, + reason: Logging handled by DynamoDB Update step and Error Handler lambda, + }, + { + id: AwsSolutions-SF2, + reason: Optional configuration for this solution, + }, + ], + }, + }, + Properties: { + DefinitionString: { + Fn::Join: [ + , + [ + {"StartAt":"Input Validate","States":{"Input Validate":{"Next":"MediaInfo","Retry":[{"ErrorEquals":["Lambda.ServiceException","Lambda.AWSLambdaException","Lambda.SdkClientException"],"IntervalSeconds":2,"MaxAttempts":6,"BackoffRate":2}],"Type":"Task","Resource":", + { + Fn::GetAtt: [ + InputValidateLambdaA739FF97, + Arn, + ], + }, + "},"MediaInfo":{"Next":"DynamoDB Update (Ingest)","Retry":[{"ErrorEquals":["Lambda.ServiceException","Lambda.AWSLambdaException","Lambda.SdkClientException"],"IntervalSeconds":2,"MaxAttempts":6,"BackoffRate":2}],"Type":"Task","Resource":", + { + Fn::GetAtt: [ + MediaInfoLambda172F634B, + Arn, + ], + }, + "},"DynamoDB Update (Ingest)":{"Next":"SNS Choice (Ingest)","Retry":[{"ErrorEquals":["Lambda.ServiceException","Lambda.AWSLambdaException","Lambda.SdkClientException"],"IntervalSeconds":2,"MaxAttempts":6,"BackoffRate":2}],"Type":"Task","Resource":", + { + Fn::GetAtt: [ + DynamoUpdateLambda0DF14C26, + Arn, + ], + }, + "},"SNS Choice (Ingest)":{"Type":"Choice","Choices":[{"Variable":"$.enableSns","BooleanEquals":true,"Next":"SNS Notification (Ingest)"}],"Default":"Process Execute"},"Process Execute":{"End":true,"Retry":[{"ErrorEquals":["Lambda.ServiceException","Lambda.AWSLambdaException","Lambda.SdkClientException"],"IntervalSeconds":2,"MaxAttempts":6,"BackoffRate":2}],"Type":"Task","Resource":", + { + Fn::GetAtt: [ + StepFunctionsLambda8B4F69C7, + Arn, + ], + }, + "},"SNS Notification (Ingest)":{"Next":"Process Execute","Retry":[{"ErrorEquals":["Lambda.ServiceException","Lambda.AWSLambdaException","Lambda.SdkClientException"],"IntervalSeconds":2,"MaxAttempts":6,"BackoffRate":2}],"Type":"Task","Resource":", + { + Fn::GetAtt: [ + SnsNotificationLambda1EA4A474, + Arn, + ], + }, + "}}}, + ], + ], + }, + RoleArn: { + Fn::GetAtt: [ + StepFunctionsServiceRole2A83B843, + Arn, + ], + }, + StateMachineName: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -ingest, + ], + ], + }, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + }, + Type: AWS::StepFunctions::StateMachine, + }, + InputValidateLambdaA739FF97: { + DependsOn: [ + InputValidatePolicyAC31247E, + InputValidateRole862FC6A2, + ], + Metadata: { + cfn_nag: { + rules_to_suppress: [ + { + id: W89, + reason: Lambda functions do not need a VPC, + }, + { + id: W92, + reason: Lambda do not need ReservedConcurrentExecutions in this case, + }, + { + id: W58, + reason: Invalid warning: function has access to cloudwatch, + }, + ], + }, + }, + Properties: { + Code: { + S3Bucket: { + Fn::Sub: cdk-hnb659fds-assets-\${AWS::AccountId}-\${AWS::Region}, + }, + S3Key: [HASH REMOVED].zip, + }, + Description: Validates the input given to the workflow, + Environment: { + Variables: { + AWS_NODEJS_CONNECTION_REUSE_ENABLED: 1, + AcceleratedTranscoding: { + Ref: AcceleratedTranscoding, + }, + ArchiveSource: { + Ref: Glacier, + }, + CloudFront: { + Fn::GetAtt: [ + CloudFrontToS3CloudFrontDistribution241D9866, + DomainName, + ], + }, + Destination: { + Ref: Destination920A3C57, + }, + EnableMediaPackage: { + Fn::If: [ + EnableMediaPackageCondition, + true, + false, + ], + }, + EnableSns: { + Fn::If: [ + EnableSnsCondition, + true, + false, + ], + }, + EnableSqs: { + Fn::If: [ + EnableSqsCondition, + true, + false, + ], + }, + ErrorHandler: { + Fn::GetAtt: [ + ErrorHandlerLambdaFC10367C, + Arn, + ], + }, + FrameCapture: { + Fn::If: [ + FrameCaptureCondition, + true, + false, + ], + }, + InputRotate: DEGREE_0, + MediaConvert_Template_1080p: { + Fn::If: [ + EnableMediaPackageCondition, + { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + _Ott_1080p_Avc_Aac_16x9_mvod_no_preset, + ], + ], + }, + { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + _Ott_1080p_Avc_Aac_16x9_qvbr_no_preset, + ], + ], + }, + ], + }, + MediaConvert_Template_2160p: { + Fn::If: [ + EnableMediaPackageCondition, + { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + _Ott_2160p_Avc_Aac_16x9_mvod_no_preset, + ], + ], + }, + { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + _Ott_2160p_Avc_Aac_16x9_qvbr_no_preset, + ], + ], + }, + ], + }, + MediaConvert_Template_720p: { + Fn::If: [ + EnableMediaPackageCondition, + { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + _Ott_720p_Avc_Aac_16x9_mvod_no_preset, + ], + ], + }, + { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + _Ott_720p_Avc_Aac_16x9_qvbr_no_preset, + ], + ], + }, + ], + }, + SOLUTION_IDENTIFIER: AwsSolution/SO0021/%%VERSION%%, + Source: { + Ref: Source71E471F1, + }, + WorkflowName: { + Ref: AWS::StackName, + }, + }, + }, + FunctionName: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -input-validate, + ], + ], + }, + Handler: index.handler, + Role: { + Fn::GetAtt: [ + InputValidateRole862FC6A2, + Arn, + ], + }, + Runtime: nodejs16.x, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + Timeout: 120, + }, + Type: AWS::Lambda::Function, + }, + InputValidatePolicyAC31247E: { + Metadata: { + cdk_nag: { + rules_to_suppress: [ + { + id: AwsSolutions-IAM5, + reason: * is used so that the Lambda function can create log groups, + }, + ], + }, + }, + Properties: { + PolicyDocument: { + Statement: [ + { + Action: s3:GetObject, + Effect: Allow, + Resource: { + Fn::Join: [ + , + [ + { + Fn::GetAtt: [ + Source71E471F1, + Arn, + ], + }, + /*, + ], + ], + }, + }, + { + Action: lambda:InvokeFunction, + Effect: Allow, + Resource: { + Fn::GetAtt: [ + ErrorHandlerLambdaFC10367C, + Arn, + ], + }, + }, + { + Action: [ + logs:CreateLogGroup, + logs:CreateLogStream, + logs:PutLogEvents, + ], + Effect: Allow, + Resource: { + Fn::Join: [ + , + [ + arn:, + { + Ref: AWS::Partition, + }, + :logs:, + { + Ref: AWS::Region, + }, + :, + { + Ref: AWS::AccountId, + }, + :log-group:/aws/lambda/*, + ], + ], + }, + }, + ], + Version: 2012-10-17, + }, + PolicyName: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -input-validate-role, + ], + ], + }, + Roles: [ + { + Ref: InputValidateRole862FC6A2, + }, + ], + }, + Type: AWS::IAM::Policy, + }, + InputValidateRole862FC6A2: { + Metadata: { + cfn_nag: { + rules_to_suppress: [ + { + id: W11, + reason: * is used so that the Lambda function can create log groups, + }, + ], + }, + }, + Properties: { + AssumeRolePolicyDocument: { + Statement: [ + { + Action: sts:AssumeRole, + Effect: Allow, + Principal: { + Service: lambda.amazonaws.com, + }, + }, + ], + Version: 2012-10-17, + }, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + }, + Type: AWS::IAM::Role, + }, + Logs6819BB44: { + DeletionPolicy: Retain, + Metadata: { + cdk_nag: { + rules_to_suppress: [ + { + id: AwsSolutions-S1, + reason: Used to store access logs for other buckets, + }, + { + id: AwsSolutions-S10, + reason: Bucket is private and is not using HTTP, + }, + ], + }, + cfn_nag: { + rules_to_suppress: [ + { + id: W35, + reason: Used to store access logs for other buckets, + }, + { + id: W51, + reason: Bucket does not need a bucket policy, + }, + ], + }, + }, + Properties: { + AccessControl: LogDeliveryWrite, + BucketEncryption: { + ServerSideEncryptionConfiguration: [ + { + ServerSideEncryptionByDefault: { + SSEAlgorithm: AES256, + }, + }, + ], + }, + PublicAccessBlockConfiguration: { + BlockPublicAcls: true, + BlockPublicPolicy: true, + IgnorePublicAcls: true, + RestrictPublicBuckets: true, + }, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + }, + Type: AWS::S3::Bucket, + UpdateReplacePolicy: Retain, + }, + MediaConvertEndPoint: { + DeletionPolicy: Delete, + Properties: { + Resource: EndPoint, + ServiceToken: { + Fn::GetAtt: [ + CustomResource8CDCD7A7, + Arn, + ], + }, + }, + Type: AWS::CloudFormation::CustomResource, + UpdateReplacePolicy: Delete, + }, + MediaConvertPolicy91CC58C0: { + Metadata: { + cdk_nag: { + rules_to_suppress: [ + { + id: AwsSolutions-IAM5, + reason: /* required to get/put objects to S3, + }, + ], + }, + }, + Properties: { + PolicyDocument: { + Statement: [ + { + Action: [ + s3:GetObject, + s3:PutObject, + ], + Effect: Allow, + Resource: [ + { + Fn::Join: [ + , + [ + { + Fn::GetAtt: [ + Source71E471F1, + Arn, + ], + }, + /*, + ], + ], + }, + { + Fn::Join: [ + , + [ + { + Fn::GetAtt: [ + Destination920A3C57, + Arn, + ], + }, + /*, + ], + ], + }, + ], + }, + { + Action: execute-api:Invoke, + Effect: Allow, + Resource: { + Fn::Join: [ + , + [ + arn:, + { + Ref: AWS::Partition, + }, + :execute-api:, + { + Ref: AWS::Region, + }, + :, + { + Ref: AWS::AccountId, + }, + :*, + ], + ], + }, + }, + ], + Version: 2012-10-17, + }, + PolicyName: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -mediatranscode-policy, + ], + ], + }, + Roles: [ + { + Ref: MediaConvertRole031A64A9, + }, + ], + }, + Type: AWS::IAM::Policy, + }, + MediaConvertRole031A64A9: { + Metadata: { + cfn_nag: { + rules_to_suppress: [ + { + id: W11, + reason: /* required to get/put objects to S3, + }, + ], + }, + }, + Properties: { + AssumeRolePolicyDocument: { + Statement: [ + { + Action: sts:AssumeRole, + Effect: Allow, + Principal: { + Service: mediaconvert.amazonaws.com, + }, + }, + ], + Version: 2012-10-17, + }, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + }, + Type: AWS::IAM::Role, + }, + MediaConvertTemplates: { + DeletionPolicy: Delete, + Properties: { + EnableMediaPackage: { + Fn::If: [ + EnableMediaPackageCondition, + true, + false, + ], + }, + EnableNewTemplates: true, + EndPoint: { + Fn::GetAtt: [ + MediaConvertEndPoint, + EndpointUrl, + ], + }, + Resource: MediaConvertTemplates, + ServiceToken: { + Fn::GetAtt: [ + CustomResource8CDCD7A7, + Arn, + ], + }, + StackName: { + Ref: AWS::StackName, + }, + }, + Type: AWS::CloudFormation::CustomResource, + UpdateReplacePolicy: Delete, + }, + MediaInfoLambda172F634B: { + DependsOn: [ + MediaInfoPolicy1D8C0D8D, + MediaInfoRole16C46683, + ], + Metadata: { + cfn_nag: { + rules_to_suppress: [ + { + id: W89, + reason: Lambda functions do not need a VPC, + }, + { + id: W92, + reason: Lambda do not need ReservedConcurrentExecutions in this case, + }, + { + id: W58, + reason: Invalid warning: function has access to cloudwatch, + }, + ], + }, + }, + Properties: { + Code: { + S3Bucket: { + Fn::Sub: cdk-hnb659fds-assets-\${AWS::AccountId}-\${AWS::Region}, + }, + S3Key: [HASH REMOVED].zip, + }, + Description: Runs mediainfo on a pre-signed S3 URL, + Environment: { + Variables: { + ErrorHandler: { + Fn::GetAtt: [ + ErrorHandlerLambdaFC10367C, + Arn, + ], + }, + SOLUTION_IDENTIFIER: AwsSolution/SO0021/%%VERSION%%, + }, + }, + FunctionName: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -mediainfo, + ], + ], + }, + Handler: lambda_function.lambda_handler, + Role: { + Fn::GetAtt: [ + MediaInfoRole16C46683, + Arn, + ], + }, + Runtime: python3.9, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + Timeout: 120, + }, + Type: AWS::Lambda::Function, + }, + MediaInfoPolicy1D8C0D8D: { + Metadata: { + cdk_nag: { + rules_to_suppress: [ + { + id: AwsSolutions-IAM5, + reason: * is used so that the Lambda function can create log groups, + }, + ], + }, + }, + Properties: { + PolicyDocument: { + Statement: [ + { + Action: s3:GetObject, + Effect: Allow, + Resource: { + Fn::Join: [ + , + [ + { + Fn::GetAtt: [ + Source71E471F1, + Arn, + ], + }, + /*, + ], + ], + }, + }, + { + Action: lambda:InvokeFunction, + Effect: Allow, + Resource: { + Fn::GetAtt: [ + ErrorHandlerLambdaFC10367C, + Arn, + ], + }, + }, + { + Action: [ + logs:CreateLogGroup, + logs:CreateLogStream, + logs:PutLogEvents, + ], + Effect: Allow, + Resource: { + Fn::Join: [ + , + [ + arn:, + { + Ref: AWS::Partition, + }, + :logs:, + { + Ref: AWS::Region, + }, + :, + { + Ref: AWS::AccountId, + }, + :log-group:/aws/lambda/*, + ], + ], + }, + }, + ], + Version: 2012-10-17, + }, + PolicyName: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -mediainfo-role, + ], + ], + }, + Roles: [ + { + Ref: MediaInfoRole16C46683, + }, + ], + }, + Type: AWS::IAM::Policy, + }, + MediaInfoRole16C46683: { + Metadata: { + cfn_nag: { + rules_to_suppress: [ + { + id: W11, + reason: * is used so that the Lambda function can create log groups, + }, + ], + }, + }, + Properties: { + AssumeRolePolicyDocument: { + Statement: [ + { + Action: sts:AssumeRole, + Effect: Allow, + Principal: { + Service: lambda.amazonaws.com, + }, + }, + ], + Version: 2012-10-17, + }, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + }, + Type: AWS::IAM::Role, + }, + MediaPackageAssetsLambda63EB0986: { + DependsOn: [ + MediaPackageAssetsPolicy84101CE2, + MediaPackageAssetsRole5B26B67C, + ], + Metadata: { + cfn_nag: { + rules_to_suppress: [ + { + id: W89, + reason: Lambda functions do not need a VPC, + }, + { + id: W92, + reason: Lambda do not need ReservedConcurrentExecutions in this case, + }, + { + id: W58, + reason: Invalid warning: function has access to cloudwatch, + }, + ], + }, + }, + Properties: { + Code: { + S3Bucket: { + Fn::Sub: cdk-hnb659fds-assets-\${AWS::AccountId}-\${AWS::Region}, + }, + S3Key: [HASH REMOVED].zip, + }, + Description: Ingests an asset into MediaPackage-VOD, + Environment: { + Variables: { + AWS_NODEJS_CONNECTION_REUSE_ENABLED: 1, + ErrorHandler: { + Fn::GetAtt: [ + ErrorHandlerLambdaFC10367C, + Arn, + ], + }, + GroupDomainName: { + Fn::GetAtt: [ + MediaPackageVod, + GroupDomainName, + ], + }, + GroupId: { + Fn::GetAtt: [ + MediaPackageVod, + GroupId, + ], + }, + MediaPackageVodRole: { + Fn::GetAtt: [ + MediaPackageVodRole931E8163, + Arn, + ], + }, + SOLUTION_IDENTIFIER: AwsSolution/SO0021/%%VERSION%%, + }, + }, + FunctionName: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -media-package-assets, + ], + ], + }, + Handler: index.handler, + Role: { + Fn::GetAtt: [ + MediaPackageAssetsRole5B26B67C, + Arn, + ], + }, + Runtime: nodejs16.x, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + Timeout: 120, + }, + Type: AWS::Lambda::Function, + }, + MediaPackageAssetsPolicy84101CE2: { + Metadata: { + cdk_nag: { + rules_to_suppress: [ + { + id: AwsSolutions-IAM5, + reason: * is used so that the Lambda function can create log groups, + }, + ], + }, + cfn_nag: { + rules_to_suppress: [ + { + id: W12, + reason: * is used so that the Lambda function can create log groups, + }, + ], + }, + }, + Properties: { + PolicyDocument: { + Statement: [ + { + Action: iam:PassRole, + Effect: Allow, + Resource: { + Fn::GetAtt: [ + MediaPackageVodRole931E8163, + Arn, + ], + }, + }, + { + Action: [ + mediapackage-vod:CreateAsset, + mediapackage-vod:TagResource, + mediapackage-vod:UntagResource, + ], + Effect: Allow, + Resource: *, + }, + { + Action: lambda:InvokeFunction, + Effect: Allow, + Resource: { + Fn::GetAtt: [ + ErrorHandlerLambdaFC10367C, + Arn, + ], + }, + }, + { + Action: [ + logs:CreateLogGroup, + logs:CreateLogStream, + logs:PutLogEvents, + ], + Effect: Allow, + Resource: { + Fn::Join: [ + , + [ + arn:, + { + Ref: AWS::Partition, + }, + :logs:, + { + Ref: AWS::Region, + }, + :, + { + Ref: AWS::AccountId, + }, + :log-group:/aws/lambda/*, + ], + ], + }, + }, + ], + Version: 2012-10-17, + }, + PolicyName: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -media-package-assets-role, + ], + ], + }, + Roles: [ + { + Ref: MediaPackageAssetsRole5B26B67C, + }, + ], + }, + Type: AWS::IAM::Policy, + }, + MediaPackageAssetsRole5B26B67C: { + Metadata: { + cfn_nag: { + rules_to_suppress: [ + { + id: W11, + reason: * is used so that the Lambda function can create log groups, + }, + ], + }, + }, + Properties: { + AssumeRolePolicyDocument: { + Statement: [ + { + Action: sts:AssumeRole, + Effect: Allow, + Principal: { + Service: lambda.amazonaws.com, + }, + }, + ], + Version: 2012-10-17, + }, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + }, + Type: AWS::IAM::Role, + }, + MediaPackageVod: { + DeletionPolicy: Delete, + Properties: { + DistributionId: { + Ref: CloudFrontToS3CloudFrontDistribution241D9866, + }, + EnableMediaPackage: { + Fn::If: [ + EnableMediaPackageCondition, + true, + false, + ], + }, + GroupId: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -packaging-group, + ], + ], + }, + PackagingConfigurations: HLS,DASH,MSS,CMAF, + Resource: MediaPackageVod, + ServiceToken: { + Fn::GetAtt: [ + CustomResource8CDCD7A7, + Arn, + ], + }, + StackName: { + Ref: AWS::StackName, + }, + }, + Type: AWS::CloudFormation::CustomResource, + UpdateReplacePolicy: Delete, + }, + MediaPackageVodPolicy1DC9ECF2: { + Metadata: { + cdk_nag: { + rules_to_suppress: [ + { + id: AwsSolutions-IAM5, + reason: /* required to get/put objects to S3, + }, + ], + }, + }, + Properties: { + PolicyDocument: { + Statement: [ + { + Action: [ + s3:GetObject, + s3:GetBucketLocation, + s3:GetBucketRequestPayment, + ], + Effect: Allow, + Resource: [ + { + Fn::GetAtt: [ + Destination920A3C57, + Arn, + ], + }, + { + Fn::Join: [ + , + [ + { + Fn::GetAtt: [ + Destination920A3C57, + Arn, + ], + }, + /*, + ], + ], + }, + ], + }, + ], + Version: 2012-10-17, + }, + PolicyName: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -mediapackagevod-policy, + ], + ], + }, + Roles: [ + { + Ref: MediaPackageVodRole931E8163, + }, + ], + }, + Type: AWS::IAM::Policy, + }, + MediaPackageVodRole931E8163: { + Metadata: { + cfn_nag: { + rules_to_suppress: [ + { + id: W11, + reason: * is required to get objects from S3, + }, + ], + }, + }, + Properties: { + AssumeRolePolicyDocument: { + Statement: [ + { + Action: sts:AssumeRole, + Effect: Allow, + Principal: { + Service: mediapackage.amazonaws.com, + }, + }, + ], + Version: 2012-10-17, + }, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + }, + Type: AWS::IAM::Role, + }, + OutputValidateLambda2645C4BB: { + DependsOn: [ + OutputValidatePolicy8D988909, + OutputValidateRole553C8CD2, + ], + Metadata: { + cfn_nag: { + rules_to_suppress: [ + { + id: W89, + reason: Lambda functions do not need a VPC, + }, + { + id: W92, + reason: Lambda do not need ReservedConcurrentExecutions in this case, + }, + { + id: W58, + reason: Invalid warning: function has access to cloudwatch, + }, + ], + }, + }, + Properties: { + Code: { + S3Bucket: { + Fn::Sub: cdk-hnb659fds-assets-\${AWS::AccountId}-\${AWS::Region}, + }, + S3Key: [HASH REMOVED].zip, + }, + Description: Parses MediaConvert job output, + Environment: { + Variables: { + AWS_NODEJS_CONNECTION_REUSE_ENABLED: 1, + DynamoDBTable: { + Ref: DynamoDBTable59784FC0, + }, + EndPoint: { + Fn::GetAtt: [ + MediaConvertEndPoint, + EndpointUrl, + ], + }, + ErrorHandler: { + Fn::GetAtt: [ + ErrorHandlerLambdaFC10367C, + Arn, + ], + }, + SOLUTION_IDENTIFIER: AwsSolution/SO0021/%%VERSION%%, + }, + }, + FunctionName: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -output-validate, + ], + ], + }, + Handler: index.handler, + Role: { + Fn::GetAtt: [ + OutputValidateRole553C8CD2, + Arn, + ], + }, + Runtime: nodejs16.x, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + Timeout: 120, + }, + Type: AWS::Lambda::Function, + }, + OutputValidatePolicy8D988909: { + Metadata: { + cdk_nag: { + rules_to_suppress: [ + { + id: AwsSolutions-IAM5, + reason: * is used so that the Lambda function can create log groups, + }, + ], + }, + }, + Properties: { + PolicyDocument: { + Statement: [ + { + Action: dynamodb:GetItem, + Effect: Allow, + Resource: { + Fn::GetAtt: [ + DynamoDBTable59784FC0, + Arn, + ], + }, + }, + { + Action: s3:ListBucket, + Effect: Allow, + Resource: { + Fn::GetAtt: [ + Destination920A3C57, + Arn, + ], + }, + }, + { + Action: lambda:InvokeFunction, + Effect: Allow, + Resource: { + Fn::GetAtt: [ + ErrorHandlerLambdaFC10367C, + Arn, + ], + }, + }, + { + Action: [ + logs:CreateLogGroup, + logs:CreateLogStream, + logs:PutLogEvents, + ], + Effect: Allow, + Resource: { + Fn::Join: [ + , + [ + arn:, + { + Ref: AWS::Partition, + }, + :logs:, + { + Ref: AWS::Region, + }, + :, + { + Ref: AWS::AccountId, + }, + :log-group:/aws/lambda/*, + ], + ], + }, + }, + ], + Version: 2012-10-17, + }, + PolicyName: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -output-validate-role, + ], + ], + }, + Roles: [ + { + Ref: OutputValidateRole553C8CD2, + }, + ], + }, + Type: AWS::IAM::Policy, + }, + OutputValidateRole553C8CD2: { + Metadata: { + cfn_nag: { + rules_to_suppress: [ + { + id: W11, + reason: * is used so that the Lambda function can create log groups, + }, + ], + }, + }, + Properties: { + AssumeRolePolicyDocument: { + Statement: [ + { + Action: sts:AssumeRole, + Effect: Allow, + Principal: { + Service: lambda.amazonaws.com, + }, + }, + ], + Version: 2012-10-17, + }, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + }, + Type: AWS::IAM::Role, + }, + ProcessWorkflow95FAF321: { + DependsOn: [ + StepFunctionsServiceRoleDefaultPolicy6AD67B91, + StepFunctionsServiceRole2A83B843, + ], + Metadata: { + cdk_nag: { + rules_to_suppress: [ + { + id: AwsSolutions-SF1, + reason: Logging handled by DynamoDB Update step and Error Handler lambda, + }, + { + id: AwsSolutions-SF2, + reason: Optional configuration for this solution, + }, + ], + }, + }, + Properties: { + DefinitionString: { + Fn::Join: [ + , + [ + {"StartAt":"Profiler","States":{"Profiler":{"Next":"Encoding Profile Check","Retry":[{"ErrorEquals":["Lambda.ServiceException","Lambda.AWSLambdaException","Lambda.SdkClientException"],"IntervalSeconds":2,"MaxAttempts":6,"BackoffRate":2}],"Type":"Task","Resource":", + { + Fn::GetAtt: [ + ProfilerLambdaFAFF7893, + Arn, + ], + }, + "},"Encoding Profile Check":{"Type":"Choice","Choices":[{"Variable":"$.isCustomTemplate","BooleanEquals":true,"Next":"Custom jobTemplate"},{"Variable":"$.encodingProfile","NumericEquals":2160,"Next":"jobTemplate 2160p"},{"Variable":"$.encodingProfile","NumericEquals":1080,"Next":"jobTemplate 1080p"},{"Variable":"$.encodingProfile","NumericEquals":720,"Next":"jobTemplate 720p"}]},"Custom jobTemplate":{"Type":"Pass","Next":"Accelerated Transcoding Check"},"Accelerated Transcoding Check":{"Type":"Choice","Choices":[{"Variable":"$.acceleratedTranscoding","StringEquals":"ENABLED","Next":"Enabled"},{"Variable":"$.acceleratedTranscoding","StringEquals":"PREFERRED","Next":"Preferred"},{"Variable":"$.acceleratedTranscoding","StringEquals":"DISABLED","Next":"Disabled"}]},"jobTemplate 2160p":{"Type":"Pass","Next":"Accelerated Transcoding Check"},"jobTemplate 1080p":{"Type":"Pass","Next":"Accelerated Transcoding Check"},"jobTemplate 720p":{"Type":"Pass","Next":"Accelerated Transcoding Check"},"Enabled":{"Type":"Pass","Next":"Frame Capture Check"},"Frame Capture Check":{"Type":"Choice","Choices":[{"Variable":"$.frameCapture","BooleanEquals":true,"Next":"Frame Capture"},{"Variable":"$.frameCapture","BooleanEquals":false,"Next":"No Frame Capture"}]},"Preferred":{"Type":"Pass","Next":"Frame Capture Check"},"Disabled":{"Type":"Pass","Next":"Frame Capture Check"},"Frame Capture":{"Type":"Pass","Next":"Encode Job Submit"},"Encode Job Submit":{"Next":"DynamoDB Update (Process)","Retry":[{"ErrorEquals":["Lambda.ServiceException","Lambda.AWSLambdaException","Lambda.SdkClientException"],"IntervalSeconds":2,"MaxAttempts":6,"BackoffRate":2}],"Type":"Task","Resource":", + { + Fn::GetAtt: [ + EncodeLambdaDADCB2BB, + Arn, + ], + }, + "},"No Frame Capture":{"Type":"Pass","Next":"Encode Job Submit"},"DynamoDB Update (Process)":{"End":true,"Retry":[{"ErrorEquals":["Lambda.ServiceException","Lambda.AWSLambdaException","Lambda.SdkClientException"],"IntervalSeconds":2,"MaxAttempts":6,"BackoffRate":2}],"Type":"Task","Resource":", + { + Fn::GetAtt: [ + DynamoUpdateLambda0DF14C26, + Arn, + ], + }, + "}}}, + ], + ], + }, + RoleArn: { + Fn::GetAtt: [ + StepFunctionsServiceRole2A83B843, + Arn, + ], + }, + StateMachineName: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -process, + ], + ], + }, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + }, + Type: AWS::StepFunctions::StateMachine, + }, + ProfilerLambdaFAFF7893: { + DependsOn: [ + ProfilerPolicy71C838D4, + ProfilerRole5E433579, + ], + Metadata: { + cfn_nag: { + rules_to_suppress: [ + { + id: W89, + reason: Lambda functions do not need a VPC, + }, + { + id: W92, + reason: Lambda do not need ReservedConcurrentExecutions in this case, + }, + { + id: W58, + reason: Invalid warning: function has access to cloudwatch, + }, + ], + }, + }, + Properties: { + Code: { + S3Bucket: { + Fn::Sub: cdk-hnb659fds-assets-\${AWS::AccountId}-\${AWS::Region}, + }, + S3Key: [HASH REMOVED].zip, + }, + Description: Sets an EncodeProfile based on mediainfo output, + Environment: { + Variables: { + AWS_NODEJS_CONNECTION_REUSE_ENABLED: 1, + DynamoDBTable: { + Ref: DynamoDBTable59784FC0, + }, + ErrorHandler: { + Fn::GetAtt: [ + ErrorHandlerLambdaFC10367C, + Arn, + ], + }, + SOLUTION_IDENTIFIER: AwsSolution/SO0021/%%VERSION%%, + }, + }, + FunctionName: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -profiler, + ], + ], + }, + Handler: index.handler, + Role: { + Fn::GetAtt: [ + ProfilerRole5E433579, + Arn, + ], + }, + Runtime: nodejs16.x, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + Timeout: 120, + }, + Type: AWS::Lambda::Function, + }, + ProfilerPolicy71C838D4: { + Metadata: { + cdk_nag: { + rules_to_suppress: [ + { + id: AwsSolutions-IAM5, + reason: * is used so that the Lambda function can create log groups, + }, + ], + }, + }, + Properties: { + PolicyDocument: { + Statement: [ + { + Action: dynamodb:GetItem, + Effect: Allow, + Resource: { + Fn::GetAtt: [ + DynamoDBTable59784FC0, + Arn, + ], + }, + }, + { + Action: lambda:InvokeFunction, + Effect: Allow, + Resource: { + Fn::GetAtt: [ + ErrorHandlerLambdaFC10367C, + Arn, + ], + }, + }, + { + Action: [ + logs:CreateLogGroup, + logs:CreateLogStream, + logs:PutLogEvents, + ], + Effect: Allow, + Resource: { + Fn::Join: [ + , + [ + arn:, + { + Ref: AWS::Partition, + }, + :logs:, + { + Ref: AWS::Region, + }, + :, + { + Ref: AWS::AccountId, + }, + :log-group:/aws/lambda/*, + ], + ], + }, + }, + ], + Version: 2012-10-17, + }, + PolicyName: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -profiler-role, + ], + ], + }, + Roles: [ + { + Ref: ProfilerRole5E433579, + }, + ], + }, + Type: AWS::IAM::Policy, + }, + ProfilerRole5E433579: { + Metadata: { + cfn_nag: { + rules_to_suppress: [ + { + id: W11, + reason: * is used so that the Lambda function can create log groups, + }, + ], + }, + }, + Properties: { + AssumeRolePolicyDocument: { + Statement: [ + { + Action: sts:AssumeRole, + Effect: Allow, + Principal: { + Service: lambda.amazonaws.com, + }, + }, + ], + Version: 2012-10-17, + }, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + }, + Type: AWS::IAM::Role, + }, + PublishWorkflowEF670320: { + DependsOn: [ + StepFunctionsServiceRoleDefaultPolicy6AD67B91, + StepFunctionsServiceRole2A83B843, + ], + Metadata: { + cdk_nag: { + rules_to_suppress: [ + { + id: AwsSolutions-SF1, + reason: Logging handled by DynamoDB Update step and Error Handler lambda, + }, + { + id: AwsSolutions-SF2, + reason: Optional configuration for this solution, + }, + ], + }, + }, + Properties: { + DefinitionString: { + Fn::Join: [ + , + [ + {"StartAt":"Validate Encoding Outputs","States":{"Validate Encoding Outputs":{"Next":"Archive Source Choice","Retry":[{"ErrorEquals":["Lambda.ServiceException","Lambda.AWSLambdaException","Lambda.SdkClientException"],"IntervalSeconds":2,"MaxAttempts":6,"BackoffRate":2}],"Type":"Task","Resource":", + { + Fn::GetAtt: [ + OutputValidateLambda2645C4BB, + Arn, + ], + }, + "},"Archive Source Choice":{"Type":"Choice","Choices":[{"Variable":"$.archiveSource","StringEquals":"GLACIER","Next":"Archive"},{"Variable":"$.archiveSource","StringEquals":"DEEP_ARCHIVE","Next":"Deep Archive"}],"Default":"MediaPackage Choice"},"MediaPackage Choice":{"Type":"Choice","Choices":[{"Variable":"$.enableMediaPackage","BooleanEquals":true,"Next":"MediaPackage Assets"}],"Default":"DynamoDB Update (Publish)"},"Archive":{"Next":"MediaPackage Choice","Retry":[{"ErrorEquals":["Lambda.ServiceException","Lambda.AWSLambdaException","Lambda.SdkClientException"],"IntervalSeconds":2,"MaxAttempts":6,"BackoffRate":2}],"Type":"Task","Resource":", + { + Fn::GetAtt: [ + ArchiveSourceLambda320F09D9, + Arn, + ], + }, + "},"Deep Archive":{"Next":"MediaPackage Choice","Retry":[{"ErrorEquals":["Lambda.ServiceException","Lambda.AWSLambdaException","Lambda.SdkClientException"],"IntervalSeconds":2,"MaxAttempts":6,"BackoffRate":2}],"Type":"Task","Resource":", + { + Fn::GetAtt: [ + ArchiveSourceLambda320F09D9, + Arn, + ], + }, + "},"DynamoDB Update (Publish)":{"Next":"SQS Choice","Retry":[{"ErrorEquals":["Lambda.ServiceException","Lambda.AWSLambdaException","Lambda.SdkClientException"],"IntervalSeconds":2,"MaxAttempts":6,"BackoffRate":2}],"Type":"Task","Resource":", + { + Fn::GetAtt: [ + DynamoUpdateLambda0DF14C26, + Arn, + ], + }, + "},"MediaPackage Assets":{"Next":"DynamoDB Update (Publish)","Retry":[{"ErrorEquals":["Lambda.ServiceException","Lambda.AWSLambdaException","Lambda.SdkClientException"],"IntervalSeconds":2,"MaxAttempts":6,"BackoffRate":2}],"Type":"Task","Resource":", + { + Fn::GetAtt: [ + MediaPackageAssetsLambda63EB0986, + Arn, + ], + }, + "},"SQS Choice":{"Type":"Choice","Choices":[{"Variable":"$.enableSqs","BooleanEquals":true,"Next":"SQS Send Message"}],"Default":"SNS Choice (Publish)"},"SNS Choice (Publish)":{"Type":"Choice","Choices":[{"Variable":"$.enableSns","BooleanEquals":true,"Next":"SNS Notification (Publish)"}],"Default":"Complete"},"SQS Send Message":{"Next":"SNS Choice (Publish)","Retry":[{"ErrorEquals":["Lambda.ServiceException","Lambda.AWSLambdaException","Lambda.SdkClientException"],"IntervalSeconds":2,"MaxAttempts":6,"BackoffRate":2}],"Type":"Task","Resource":", + { + Fn::GetAtt: [ + SqsSendMessageLambda156FD22A, + Arn, + ], + }, + "},"Complete":{"Type":"Pass","End":true},"SNS Notification (Publish)":{"Next":"Complete","Retry":[{"ErrorEquals":["Lambda.ServiceException","Lambda.AWSLambdaException","Lambda.SdkClientException"],"IntervalSeconds":2,"MaxAttempts":6,"BackoffRate":2}],"Type":"Task","Resource":", + { + Fn::GetAtt: [ + SnsNotificationLambda1EA4A474, + Arn, + ], + }, + "}}}, + ], + ], + }, + RoleArn: { + Fn::GetAtt: [ + StepFunctionsServiceRole2A83B843, + Arn, + ], + }, + StateMachineName: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -publish, + ], + ], + }, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + }, + Type: AWS::StepFunctions::StateMachine, + }, + S3Config: { + DeletionPolicy: Delete, + DependsOn: [ + StepFunctionsLambdaCloudWatchLambdaInvokeCompletes8CE78F7D, + StepFunctionsLambda8B4F69C7, + StepFunctionsLambdaS3LambdaInvokeVideo456192AA, + ], + Properties: { + IngestArn: { + Fn::GetAtt: [ + StepFunctionsLambda8B4F69C7, + Arn, + ], + }, + Resource: S3Notification, + ServiceToken: { + Fn::GetAtt: [ + CustomResource8CDCD7A7, + Arn, + ], + }, + Source: { + Ref: Source71E471F1, + }, + WorkflowTrigger: { + Ref: WorkflowTrigger, + }, + }, + Type: AWS::CloudFormation::CustomResource, + UpdateReplacePolicy: Delete, + }, + SnsNotificationLambda1EA4A474: { + DependsOn: [ + SnsNotificationPolicy26B12503, + SnsNotificationRole48AE9F11, + ], + Metadata: { + cfn_nag: { + rules_to_suppress: [ + { + id: W89, + reason: Lambda functions do not need a VPC, + }, + { + id: W92, + reason: Lambda do not need ReservedConcurrentExecutions in this case, + }, + { + id: W58, + reason: Invalid warning: function has access to cloudwatch, + }, + ], + }, + }, + Properties: { + Code: { + S3Bucket: { + Fn::Sub: cdk-hnb659fds-assets-\${AWS::AccountId}-\${AWS::Region}, + }, + S3Key: [HASH REMOVED].zip, + }, + Description: Sends a notification when the encode job is completed, + Environment: { + Variables: { + AWS_NODEJS_CONNECTION_REUSE_ENABLED: 1, + ErrorHandler: { + Fn::GetAtt: [ + ErrorHandlerLambdaFC10367C, + Arn, + ], + }, + SOLUTION_IDENTIFIER: AwsSolution/SO0021/%%VERSION%%, + SnsTopic: { + Ref: SnsTopic2C1570A4, + }, + }, + }, + FunctionName: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -sns-notification, + ], + ], + }, + Handler: index.handler, + Role: { + Fn::GetAtt: [ + SnsNotificationRole48AE9F11, + Arn, + ], + }, + Runtime: nodejs16.x, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + Timeout: 120, + }, + Type: AWS::Lambda::Function, + }, + SnsNotificationPolicy26B12503: { + Metadata: { + cdk_nag: { + rules_to_suppress: [ + { + id: AwsSolutions-IAM5, + reason: * is used so that the Lambda function can create log groups, + }, + ], + }, + }, + Properties: { + PolicyDocument: { + Statement: [ + { + Action: sns:Publish, + Condition: { + Bool: { + aws:SecureTransport: true, + }, + }, + Effect: Allow, + Resource: { + Ref: SnsTopic2C1570A4, + }, + }, + { + Action: lambda:InvokeFunction, + Effect: Allow, + Resource: { + Fn::GetAtt: [ + ErrorHandlerLambdaFC10367C, + Arn, + ], + }, + }, + { + Action: [ + logs:CreateLogGroup, + logs:CreateLogStream, + logs:PutLogEvents, + ], + Effect: Allow, + Resource: { + Fn::Join: [ + , + [ + arn:, + { + Ref: AWS::Partition, + }, + :logs:, + { + Ref: AWS::Region, + }, + :, + { + Ref: AWS::AccountId, + }, + :log-group:/aws/lambda/*, + ], + ], + }, + }, + ], + Version: 2012-10-17, + }, + PolicyName: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -sns-notification-role, + ], + ], + }, + Roles: [ + { + Ref: SnsNotificationRole48AE9F11, + }, + ], + }, + Type: AWS::IAM::Policy, + }, + SnsNotificationRole48AE9F11: { + Metadata: { + cfn_nag: { + rules_to_suppress: [ + { + id: W11, + reason: * is used so that the Lambda function can create log groups, + }, + ], + }, + }, + Properties: { + AssumeRolePolicyDocument: { + Statement: [ + { + Action: sts:AssumeRole, + Effect: Allow, + Principal: { + Service: lambda.amazonaws.com, + }, + }, + ], + Version: 2012-10-17, + }, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + }, + Type: AWS::IAM::Role, + }, + SnsTopic2C1570A4: { + Properties: { + DisplayName: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -Notifications, + ], + ], + }, + KmsMasterKeyId: alias/aws/sns, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + }, + Type: AWS::SNS::Topic, + }, + SnsTopicTokenSubscription1D5A46B4F: { + Properties: { + Endpoint: { + Ref: AdminEmail, + }, + Protocol: email, + TopicArn: { + Ref: SnsTopic2C1570A4, + }, + }, + Type: AWS::SNS::Subscription, + }, + Source71E471F1: { + DeletionPolicy: Retain, + Metadata: { + cdk_nag: { + rules_to_suppress: [ + { + id: AwsSolutions-S10, + reason: Bucket is private and is not using HTTP, + }, + ], + }, + cfn_nag: { + rules_to_suppress: [ + { + id: W51, + reason: Bucket does not need a bucket policy, + }, + ], + }, + }, + Properties: { + BucketEncryption: { + ServerSideEncryptionConfiguration: [ + { + ServerSideEncryptionByDefault: { + SSEAlgorithm: AES256, + }, + }, + ], + }, + LifecycleConfiguration: { + Rules: [ + { + Id: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -soure-archive, + ], + ], + }, + Status: Enabled, + TagFilters: [ + { + Key: { + Ref: AWS::StackName, + }, + Value: GLACIER, + }, + ], + Transitions: [ + { + StorageClass: GLACIER, + TransitionInDays: 1, + }, + ], + }, + { + Id: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -source-deep-archive, + ], + ], + }, + Status: Enabled, + TagFilters: [ + { + Key: { + Ref: AWS::StackName, + }, + Value: DEEP_ARCHIVE, + }, + ], + Transitions: [ + { + StorageClass: DEEP_ARCHIVE, + TransitionInDays: 1, + }, + ], + }, + ], + }, + LoggingConfiguration: { + DestinationBucketName: { + Ref: Logs6819BB44, + }, + LogFilePrefix: source-bucket-logs/, + }, + PublicAccessBlockConfiguration: { + BlockPublicAcls: true, + BlockPublicPolicy: true, + IgnorePublicAcls: true, + RestrictPublicBuckets: true, + }, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + }, + Type: AWS::S3::Bucket, + UpdateReplacePolicy: Retain, + }, + SqsQueue13597403: { + DeletionPolicy: Delete, + Properties: { + KmsDataKeyReusePeriodSeconds: 300, + KmsMasterKeyId: alias/aws/sqs, + QueueName: { + Ref: AWS::StackName, + }, + RedrivePolicy: { + deadLetterTargetArn: { + Fn::GetAtt: [ + SqsQueueDlqFD591F76, + Arn, + ], + }, + maxReceiveCount: 1, + }, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + VisibilityTimeout: 120, + }, + Type: AWS::SQS::Queue, + UpdateReplacePolicy: Delete, + }, + SqsQueueDlqFD591F76: { + DeletionPolicy: Delete, + Metadata: { + cdk_nag: { + rules_to_suppress: [ + { + id: AwsSolutions-SQS3, + reason: This resource is a DLQ, + }, + ], + }, + }, + Properties: { + KmsDataKeyReusePeriodSeconds: 300, + KmsMasterKeyId: alias/aws/sqs, + QueueName: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -dlq, + ], + ], + }, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + VisibilityTimeout: 120, + }, + Type: AWS::SQS::Queue, + UpdateReplacePolicy: Delete, + }, + SqsQueueDlqPolicy601D5F1E: { + Properties: { + PolicyDocument: { + Statement: [ + { + Action: sqs:*, + Condition: { + Bool: { + aws:SecureTransport: false, + }, + }, + Effect: Deny, + Principal: { + AWS: *, + }, + Resource: { + Fn::GetAtt: [ + SqsQueueDlqFD591F76, + Arn, + ], + }, + }, + ], + Version: 2012-10-17, + }, + Queues: [ + { + Ref: SqsQueueDlqFD591F76, + }, + ], + }, + Type: AWS::SQS::QueuePolicy, + }, + SqsQueuePolicy24A842E9: { + Properties: { + PolicyDocument: { + Statement: [ + { + Action: sqs:*, + Condition: { + Bool: { + aws:SecureTransport: false, + }, + }, + Effect: Deny, + Principal: { + AWS: *, + }, + Resource: { + Fn::GetAtt: [ + SqsQueue13597403, + Arn, + ], + }, + }, + ], + Version: 2012-10-17, + }, + Queues: [ + { + Ref: SqsQueue13597403, + }, + ], + }, + Type: AWS::SQS::QueuePolicy, + }, + SqsSendMessageLambda156FD22A: { + DependsOn: [ + SqsSendMessagePolicy702314CC, + SqsSendMessageRole23292716, + ], + Metadata: { + cfn_nag: { + rules_to_suppress: [ + { + id: W89, + reason: Lambda functions do not need a VPC, + }, + { + id: W92, + reason: Lambda do not need ReservedConcurrentExecutions in this case, + }, + { + id: W58, + reason: Invalid warning: function has access to cloudwatch, + }, + ], + }, + }, + Properties: { + Code: { + S3Bucket: { + Fn::Sub: cdk-hnb659fds-assets-\${AWS::AccountId}-\${AWS::Region}, + }, + S3Key: [HASH REMOVED].zip, + }, + Description: Publish the workflow results to an SQS queue, + Environment: { + Variables: { + AWS_NODEJS_CONNECTION_REUSE_ENABLED: 1, + ErrorHandler: { + Fn::GetAtt: [ + ErrorHandlerLambdaFC10367C, + Arn, + ], + }, + SOLUTION_IDENTIFIER: AwsSolution/SO0021/%%VERSION%%, + SqsQueue: { + Ref: SqsQueue13597403, + }, + }, + }, + FunctionName: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -sqs-publish, + ], + ], + }, + Handler: index.handler, + Role: { + Fn::GetAtt: [ + SqsSendMessageRole23292716, + Arn, + ], + }, + Runtime: nodejs16.x, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + Timeout: 120, + }, + Type: AWS::Lambda::Function, + }, + SqsSendMessagePolicy702314CC: { + Metadata: { + cdk_nag: { + rules_to_suppress: [ + { + id: AwsSolutions-IAM5, + reason: * is used so that the Lambda function can create log groups, + }, + ], + }, + }, + Properties: { + PolicyDocument: { + Statement: [ + { + Action: sqs:SendMessage, + Condition: { + Bool: { + aws:SecureTransport: true, + }, + }, + Effect: Allow, + Resource: { + Fn::GetAtt: [ + SqsQueue13597403, + Arn, + ], + }, + }, + { + Action: lambda:InvokeFunction, + Effect: Allow, + Resource: { + Fn::GetAtt: [ + ErrorHandlerLambdaFC10367C, + Arn, + ], + }, + }, + { + Action: [ + logs:CreateLogGroup, + logs:CreateLogStream, + logs:PutLogEvents, + ], + Effect: Allow, + Resource: { + Fn::Join: [ + , + [ + arn:, + { + Ref: AWS::Partition, + }, + :logs:, + { + Ref: AWS::Region, + }, + :, + { + Ref: AWS::AccountId, + }, + :log-group:/aws/lambda/*, + ], + ], + }, + }, + ], + Version: 2012-10-17, + }, + PolicyName: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -sqs-publish-role, + ], + ], + }, + Roles: [ + { + Ref: SqsSendMessageRole23292716, + }, + ], + }, + Type: AWS::IAM::Policy, + }, + SqsSendMessageRole23292716: { + Metadata: { + cfn_nag: { + rules_to_suppress: [ + { + id: W11, + reason: * is used so that the Lambda function can create log groups, + }, + ], + }, + }, + Properties: { + AssumeRolePolicyDocument: { + Statement: [ + { + Action: sts:AssumeRole, + Effect: Allow, + Principal: { + Service: lambda.amazonaws.com, + }, + }, + ], + Version: 2012-10-17, + }, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + }, + Type: AWS::IAM::Role, + }, + StepFunctionsLambda8B4F69C7: { + DependsOn: [ + StepFunctionsPolicy4DB3D133, + StepFunctionsRole575CBBE2, + ], + Metadata: { + cfn_nag: { + rules_to_suppress: [ + { + id: W58, + reason: Invalid warning: function has access to cloudwatch, + }, + { + id: W89, + reason: This resource does not need to be deployed inside a VPC, + }, + { + id: W92, + reason: This resource does not need to define ReservedConcurrentExecutions to reserve simultaneous executions, + }, + ], + }, + }, + Properties: { + Code: { + S3Bucket: { + Fn::Sub: cdk-hnb659fds-assets-\${AWS::AccountId}-\${AWS::Region}, + }, + S3Key: [HASH REMOVED].zip, + }, + Description: Creates a unique identifer (GUID) and executes the Ingest StateMachine, + Environment: { + Variables: { + AWS_NODEJS_CONNECTION_REUSE_ENABLED: 1, + ErrorHandler: { + Fn::GetAtt: [ + ErrorHandlerLambdaFC10367C, + Arn, + ], + }, + IngestWorkflow: { + Fn::Join: [ + , + [ + arn:, + { + Ref: AWS::Partition, + }, + :states:, + { + Ref: AWS::Region, + }, + :, + { + Ref: AWS::AccountId, + }, + :stateMachine:, + { + Ref: AWS::StackName, + }, + -ingest, + ], + ], + }, + ProcessWorkflow: { + Fn::Join: [ + , + [ + arn:, + { + Ref: AWS::Partition, + }, + :states:, + { + Ref: AWS::Region, + }, + :, + { + Ref: AWS::AccountId, + }, + :stateMachine:, + { + Ref: AWS::StackName, + }, + -process, + ], + ], + }, + PublishWorkflow: { + Fn::Join: [ + , + [ + arn:, + { + Ref: AWS::Partition, + }, + :states:, + { + Ref: AWS::Region, + }, + :, + { + Ref: AWS::AccountId, + }, + :stateMachine:, + { + Ref: AWS::StackName, + }, + -publish, + ], + ], + }, + SOLUTION_IDENTIFIER: AwsSolution/SO0021/%%VERSION%%, + }, + }, + FunctionName: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -step-functions, + ], + ], + }, + Handler: index.handler, + Role: { + Fn::GetAtt: [ + StepFunctionsRole575CBBE2, + Arn, + ], + }, + Runtime: nodejs16.x, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + Timeout: 120, + }, + Type: AWS::Lambda::Function, + }, + StepFunctionsLambdaCloudWatchLambdaInvokeCompletes8CE78F7D: { + DependsOn: [ + StepFunctionsPolicy4DB3D133, + StepFunctionsRole575CBBE2, + ], + Properties: { + Action: lambda:InvokeFunction, + FunctionName: { + Fn::GetAtt: [ + StepFunctionsLambda8B4F69C7, + Arn, + ], + }, + Principal: events.amazonaws.com, + SourceArn: { + Fn::GetAtt: [ + EncodeCompleteRuleE2F74999, + Arn, + ], + }, + }, + Type: AWS::Lambda::Permission, + }, + StepFunctionsLambdaS3LambdaInvokeVideo456192AA: { + DependsOn: [ + StepFunctionsPolicy4DB3D133, + StepFunctionsRole575CBBE2, + ], + Properties: { + Action: lambda:InvokeFunction, + FunctionName: { + Fn::GetAtt: [ + StepFunctionsLambda8B4F69C7, + Arn, + ], + }, + Principal: s3.amazonaws.com, + SourceAccount: { + Ref: AWS::AccountId, + }, + }, + Type: AWS::Lambda::Permission, + }, + StepFunctionsPolicy4DB3D133: { + Metadata: { + cdk_nag: { + rules_to_suppress: [ + { + id: AwsSolutions-IAM5, + reason: * is used so that the Lambda function can create log groups, + }, + ], + }, + }, + Properties: { + PolicyDocument: { + Statement: [ + { + Action: states:StartExecution, + Effect: Allow, + Resource: [ + { + Fn::Join: [ + , + [ + arn:, + { + Ref: AWS::Partition, + }, + :states:, + { + Ref: AWS::Region, + }, + :, + { + Ref: AWS::AccountId, + }, + :stateMachine:, + { + Ref: AWS::StackName, + }, + -ingest, + ], + ], + }, + { + Fn::Join: [ + , + [ + arn:, + { + Ref: AWS::Partition, + }, + :states:, + { + Ref: AWS::Region, + }, + :, + { + Ref: AWS::AccountId, + }, + :stateMachine:, + { + Ref: AWS::StackName, + }, + -process, + ], + ], + }, + { + Fn::Join: [ + , + [ + arn:, + { + Ref: AWS::Partition, + }, + :states:, + { + Ref: AWS::Region, + }, + :, + { + Ref: AWS::AccountId, + }, + :stateMachine:, + { + Ref: AWS::StackName, + }, + -publish, + ], + ], + }, + ], + }, + { + Action: lambda:InvokeFunction, + Effect: Allow, + Resource: { + Fn::GetAtt: [ + ErrorHandlerLambdaFC10367C, + Arn, + ], + }, + }, + { + Action: [ + logs:CreateLogGroup, + logs:CreateLogStream, + logs:PutLogEvents, + ], + Effect: Allow, + Resource: { + Fn::Join: [ + , + [ + arn:, + { + Ref: AWS::Partition, + }, + :logs:, + { + Ref: AWS::Region, + }, + :, + { + Ref: AWS::AccountId, + }, + :log-group:/aws/lambda/*, + ], + ], + }, + }, + ], + Version: 2012-10-17, + }, + PolicyName: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -step-functions-role, + ], + ], + }, + Roles: [ + { + Ref: StepFunctionsRole575CBBE2, + }, + ], + }, + Type: AWS::IAM::Policy, + }, + StepFunctionsRole575CBBE2: { + Metadata: { + cfn_nag: { + rules_to_suppress: [ + { + id: W11, + reason: * is used so that the Lambda function can create log groups, + }, + ], + }, + }, + Properties: { + AssumeRolePolicyDocument: { + Statement: [ + { + Action: sts:AssumeRole, + Effect: Allow, + Principal: { + Service: lambda.amazonaws.com, + }, + }, + ], + Version: 2012-10-17, + }, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + }, + Type: AWS::IAM::Role, + }, + StepFunctionsServicePolicy7123B955: { + Metadata: { + cdk_nag: { + rules_to_suppress: [ + { + id: AwsSolutions-IAM5, + reason: * is used so that the Lambda function can create log groups, + }, + ], + }, + }, + Properties: { + PolicyDocument: { + Statement: [ + { + Action: lambda:InvokeFunction, + Effect: Allow, + Resource: { + Fn::Join: [ + , + [ + arn:, + { + Ref: AWS::Partition, + }, + :lambda:, + { + Ref: AWS::Region, + }, + :, + { + Ref: AWS::AccountId, + }, + :function:*, + ], + ], + }, + }, + ], + Version: 2012-10-17, + }, + PolicyName: { + Fn::Join: [ + , + [ + { + Ref: AWS::StackName, + }, + -stepfunctions-service-role, + ], + ], + }, + Roles: [ + { + Ref: StepFunctionsServiceRole2A83B843, + }, + ], + }, + Type: AWS::IAM::Policy, + }, + StepFunctionsServiceRole2A83B843: { + Metadata: { + cfn_nag: { + rules_to_suppress: [ + { + id: W11, + reason: The * resource is required since the functions need to be created before the state machine, + }, + ], + }, + }, + Properties: { + AssumeRolePolicyDocument: { + Statement: [ + { + Action: sts:AssumeRole, + Effect: Allow, + Principal: { + Service: { + Fn::Join: [ + , + [ + states., + { + Ref: AWS::Region, + }, + .amazonaws.com, + ], + ], + }, + }, + }, + ], + Version: 2012-10-17, + }, + Tags: [ + { + Key: SolutionId, + Value: SO0021, + }, + ], + }, + Type: AWS::IAM::Role, + }, + StepFunctionsServiceRoleDefaultPolicy6AD67B91: { + Metadata: { + cdk_nag: { + rules_to_suppress: [ + { + id: AwsSolutions-IAM5, + reason: Statements added to default policy by aws-stepfunctions.StateMachine class, + }, + ], + }, + cfn_nag: { + rules_to_suppress: [ + { + id: W76, + reason: testestesteslsdkfjsdlkfjlskdfklsdljf, + }, + ], + }, + }, + Properties: { + PolicyDocument: { + Statement: [ + { + Action: lambda:InvokeFunction, + Effect: Allow, + Resource: [ + { + Fn::GetAtt: [ + InputValidateLambdaA739FF97, + Arn, + ], + }, + { + Fn::Join: [ + , + [ + { + Fn::GetAtt: [ + InputValidateLambdaA739FF97, + Arn, + ], + }, + :*, + ], + ], + }, + ], + }, + { + Action: lambda:InvokeFunction, + Effect: Allow, + Resource: [ + { + Fn::GetAtt: [ + MediaInfoLambda172F634B, + Arn, + ], + }, + { + Fn::Join: [ + , + [ + { + Fn::GetAtt: [ + MediaInfoLambda172F634B, + Arn, + ], + }, + :*, + ], + ], + }, + ], + }, + { + Action: lambda:InvokeFunction, + Effect: Allow, + Resource: [ + { + Fn::GetAtt: [ + DynamoUpdateLambda0DF14C26, + Arn, + ], + }, + { + Fn::Join: [ + , + [ + { + Fn::GetAtt: [ + DynamoUpdateLambda0DF14C26, + Arn, + ], + }, + :*, + ], + ], + }, + ], + }, + { + Action: lambda:InvokeFunction, + Effect: Allow, + Resource: [ + { + Fn::GetAtt: [ + StepFunctionsLambda8B4F69C7, + Arn, + ], + }, + { + Fn::Join: [ + , + [ + { + Fn::GetAtt: [ + StepFunctionsLambda8B4F69C7, + Arn, + ], + }, + :*, + ], + ], + }, + ], + }, + { + Action: lambda:InvokeFunction, + Effect: Allow, + Resource: [ + { + Fn::GetAtt: [ + SnsNotificationLambda1EA4A474, + Arn, + ], + }, + { + Fn::Join: [ + , + [ + { + Fn::GetAtt: [ + SnsNotificationLambda1EA4A474, + Arn, + ], + }, + :*, + ], + ], + }, + ], + }, + { + Action: lambda:InvokeFunction, + Effect: Allow, + Resource: [ + { + Fn::GetAtt: [ + ProfilerLambdaFAFF7893, + Arn, + ], + }, + { + Fn::Join: [ + , + [ + { + Fn::GetAtt: [ + ProfilerLambdaFAFF7893, + Arn, + ], + }, + :*, + ], + ], + }, + ], + }, + { + Action: lambda:InvokeFunction, + Effect: Allow, + Resource: [ + { + Fn::GetAtt: [ + EncodeLambdaDADCB2BB, + Arn, + ], + }, + { + Fn::Join: [ + , + [ + { + Fn::GetAtt: [ + EncodeLambdaDADCB2BB, + Arn, + ], + }, + :*, + ], + ], + }, + ], + }, + { + Action: lambda:InvokeFunction, + Effect: Allow, + Resource: [ + { + Fn::GetAtt: [ + OutputValidateLambda2645C4BB, + Arn, + ], + }, + { + Fn::Join: [ + , + [ + { + Fn::GetAtt: [ + OutputValidateLambda2645C4BB, + Arn, + ], + }, + :*, + ], + ], + }, + ], + }, + { + Action: lambda:InvokeFunction, + Effect: Allow, + Resource: [ + { + Fn::GetAtt: [ + ArchiveSourceLambda320F09D9, + Arn, + ], + }, + { + Fn::Join: [ + , + [ + { + Fn::GetAtt: [ + ArchiveSourceLambda320F09D9, + Arn, + ], + }, + :*, + ], + ], + }, + ], + }, + { + Action: lambda:InvokeFunction, + Effect: Allow, + Resource: [ + { + Fn::GetAtt: [ + MediaPackageAssetsLambda63EB0986, + Arn, + ], + }, + { + Fn::Join: [ + , + [ + { + Fn::GetAtt: [ + MediaPackageAssetsLambda63EB0986, + Arn, + ], + }, + :*, + ], + ], + }, + ], + }, + { + Action: lambda:InvokeFunction, + Effect: Allow, + Resource: [ + { + Fn::GetAtt: [ + SqsSendMessageLambda156FD22A, + Arn, + ], + }, + { + Fn::Join: [ + , + [ + { + Fn::GetAtt: [ + SqsSendMessageLambda156FD22A, + Arn, + ], + }, + :*, + ], + ], + }, + ], + }, + ], + Version: 2012-10-17, + }, + PolicyName: StepFunctionsServiceRoleDefaultPolicy6AD67B91, + Roles: [ + { + Ref: StepFunctionsServiceRole2A83B843, + }, + ], + }, + Type: AWS::IAM::Policy, + }, + UUID: { + DeletionPolicy: Delete, + Properties: { + Resource: UUID, + ServiceToken: { + Fn::GetAtt: [ + CustomResource8CDCD7A7, + Arn, + ], + }, + }, + Type: AWS::CloudFormation::CustomResource, + UpdateReplacePolicy: Delete, + }, + }, +} +`; diff --git a/source/cdk/test/vod.test.ts b/source/cdk/test/vod.test.ts new file mode 100644 index 00000000..0e1acbbf --- /dev/null +++ b/source/cdk/test/vod.test.ts @@ -0,0 +1,40 @@ +/********************************************************************************************************************* + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * + * * + * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance * + * with the License. A copy of the License is located at * + * * + * http://www.apache.org/licenses/LICENSE-2.0 * + * * + * or in the 'license' file accompanying this file. This file is distributed on an 'AS IS' BASIS, WITHOUT WARRANTIES * + * OR CONDITIONS OF ANY KIND, express or implied. See the License for the specific language governing permissions * + * and limitations under the License. * + *********************************************************************************************************************/ + +import '@aws-cdk/assert/jest'; +import { Stack } from 'aws-cdk-lib'; +import { SynthUtils } from '@aws-cdk/assert'; +import * as VideoOnDemand from '../lib/vod-stack'; + +expect.addSnapshotSerializer({ + test: (val) => typeof val === 'string', + print: (val) => { + const valueReplacements = [ + { + regex: /([A-Fa-f0-9]{64}).zip/, + replacementValue: '[HASH REMOVED].zip' + } + ]; + + return `${valueReplacements.reduce( + (output, replacement) => output.replace(replacement.regex, replacement.replacementValue), + val as string + )}`; + } +}); + +test('VideoOnDemand Stack Test', () => { + const stack = new Stack(); + const vodTest = new VideoOnDemand.VideoOnDemand(stack, 'VideoOnDemand'); + expect(SynthUtils.toCloudFormation(vodTest)).toMatchSnapshot(); +}); \ No newline at end of file diff --git a/source/cdk/tsconfig.json b/source/cdk/tsconfig.json new file mode 100644 index 00000000..9f8e8bea --- /dev/null +++ b/source/cdk/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "target": "ES2018", + "module": "commonjs", + "lib": [ + "es2018" + ], + "declaration": true, + "strict": true, + "noImplicitAny": true, + "strictNullChecks": true, + "noImplicitThis": true, + "alwaysStrict": true, + "noUnusedLocals": false, + "noUnusedParameters": false, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": false, + "inlineSourceMap": true, + "inlineSources": true, + "experimentalDecorators": true, + "strictPropertyInitialization": false, + "typeRoots": [ + "./node_modules/@types" + ] + }, + "exclude": [ + "node_modules", + "cdk.out" + ] +} diff --git a/source/custom-resource/index.js b/source/custom-resource/index.js index 9e91baa6..469498c6 100644 --- a/source/custom-resource/index.js +++ b/source/custom-resource/index.js @@ -11,7 +11,7 @@ * and limitations under the License. * *********************************************************************************************************************/ -const uuidv4 = require('uuid/v4'); +const { v4: uuidv4 } = require('uuid'); const cfn = require('./lib/cfn'); const Metrics = require('./lib/metrics'); const S3 = require('./lib/s3'); diff --git a/source/custom-resource/lib/mediaconvert/index.js b/source/custom-resource/lib/mediaconvert/index.js index 4fba34a4..7dbae251 100644 --- a/source/custom-resource/lib/mediaconvert/index.js +++ b/source/custom-resource/lib/mediaconvert/index.js @@ -173,6 +173,7 @@ const _createTemplates = async (instance, templates, stackName) => { // Load template and set unique template name let params = JSON.parse(fs.readFileSync(tmpl.file, 'utf8')); params.Name = stackName + params.Name; + params.Tags = {'SolutionId': 'SO0021'}; await instance.createJobTemplate(params).promise(); console.log(`template created:: ${params.Name}`); @@ -245,8 +246,6 @@ const Delete = async (config) => { }); try { - let templates = []; - await _deleteTemplates(mediaconvert, mediaPackageTemplatesNoPreset, config.StackName); await _deleteTemplates(mediaconvert, qvbrTemplatesNoPreset, config.StackName); diff --git a/source/custom-resource/lib/mediapackage/index.js b/source/custom-resource/lib/mediapackage/index.js index df13c4f0..77429e7c 100644 --- a/source/custom-resource/lib/mediapackage/index.js +++ b/source/custom-resource/lib/mediapackage/index.js @@ -78,7 +78,12 @@ const create = async (properties) => { const mediaPackageVod = new AWS.MediaPackageVod({customUserAgent: process.env.SOLUTION_IDENTIFIER}); const randomId = crypto.randomBytes(8).toString('hex'); - const packagingGroup = await mediaPackageVod.createPackagingGroup({ Id: properties.GroupId }).promise(); + let groupParams = { + Id: properties.GroupId, + Tags: {'SolutionId': 'SO0021'} + }; + + const packagingGroup = await mediaPackageVod.createPackagingGroup(groupParams).promise(); let created = false; const configurations = Array.from(new Set(properties.PackagingConfigurations.split(','))); @@ -109,6 +114,7 @@ const create = async (properties) => { } if (params) { + params.Tags = {'SolutionId': 'SO0021'}; console.log(`Creating configuration:: ${JSON.stringify(params, null, 2)}`); await mediaPackageVod.createPackagingConfiguration(params).promise(); created = true; diff --git a/source/custom-resource/package.json b/source/custom-resource/package.json index e248a705..365f2908 100644 --- a/source/custom-resource/package.json +++ b/source/custom-resource/package.json @@ -8,12 +8,12 @@ "main": "index.js", "scripts": { "pretest": "npm i --quiet", - "test": "mocha lib/**/*.spec.js" + "test": "jest --coverage" }, "dependencies": { "axios": "^0.21.1", "moment": "^2.24.0", - "uuid": "^3.4.0" + "uuid": "^9.0.0" }, "devDependencies": { "aws-sdk": "^2.609.0", @@ -21,7 +21,8 @@ "mocha": "^7.0.0", "chai": "^4.2.0", "axios-mock-adapter": "^1.17.0", - "lodash": "^4.17.15" + "lodash": "^4.17.15", + "jest": "^29.2.2" }, "private": true, "author": { diff --git a/source/dynamo/jest.config.js b/source/dynamo/jest.config.js new file mode 100644 index 00000000..967fc50a --- /dev/null +++ b/source/dynamo/jest.config.js @@ -0,0 +1,6 @@ +module.exports = { + roots: ['/lib'], + testMatch: ['**/*.spec.js'], + coveragePathIgnorePatterns: ['/lib/utils.test.js'], + coverageReporters: [['lcov', { projectRoot: '../' }], 'text'] + }; \ No newline at end of file diff --git a/source/dynamo/package.json b/source/dynamo/package.json index 305b443c..f0412c72 100644 --- a/source/dynamo/package.json +++ b/source/dynamo/package.json @@ -8,7 +8,7 @@ "main": "index.js", "scripts": { "pretest": "npm i --quiet", - "test": "mocha lib/*.spec.js" + "test": "jest --coverage" }, "dependencies": {}, "devDependencies": { @@ -17,7 +17,8 @@ "chai": "^4.2.0", "mocha": "^7.0.0", "sinon": "^8.1.1", - "sinon-chai": "^3.4.0" + "sinon-chai": "^3.4.0", + "jest": "^29.2.2" }, "private": true, "author": { diff --git a/source/encode/index.js b/source/encode/index.js index 2cf17d58..61967d34 100644 --- a/source/encode/index.js +++ b/source/encode/index.js @@ -181,35 +181,37 @@ exports.handler = async (event) => { tmpl.JobTemplate.Settings.OutputGroups.forEach(group => { let found = false, defaultGroup = {}; - if (group.OutputGroupSettings.Type === 'FILE_GROUP_SETTINGS') { - found = true; - defaultGroup = mp4; - } - - if (group.OutputGroupSettings.Type === 'HLS_GROUP_SETTINGS') { - found = true; - defaultGroup = hls; - } - - if (group.OutputGroupSettings.Type === 'DASH_ISO_GROUP_SETTINGS') { - found = true; - defaultGroup = dash; - } - - if (group.OutputGroupSettings.Type === 'MS_SMOOTH_GROUP_SETTINGS') { - found = true; - defaultGroup = mss; - } - - if (group.OutputGroupSettings.Type === 'CMAF_GROUP_SETTINGS') { - found = true; - defaultGroup = cmaf; + switch (group.OutputGroupSettings.Type) { + case 'FILE_GROUP_SETTINGS': + found = true; + defaultGroup = mp4; + break; + + case 'HLS_GROUP_SETTINGS': + found = true; + defaultGroup = hls; + break; + + case 'DASH_ISO_GROUP_SETTINGS': + found = true; + defaultGroup = dash; + break; + + case 'MS_SMOOTH_GROUP_SETTINGS': + found = true; + defaultGroup = mss; + break; + + case 'CMAF_GROUP_SETTINGS': + found = true; + defaultGroup = cmaf; + break; } if (found) { - console.log(`${group.Name} found in Job Template`); + console.log(`${defaultGroup.Name} found in Job Template`); // PR: https://github.com/awslabs/video-on-demand-on-aws/pull/107 - const outputGroup = mergeSettingsWithDefault(event.isCustomTemplate, defaultGroup, group); + const outputGroup = mergeSettingsWithDefault(defaultGroup, group); job.Settings.OutputGroups.push(outputGroup); } }); @@ -225,7 +227,8 @@ exports.handler = async (event) => { job.Settings.TimecodeConfig = {"Source" : "ZEROBASED"} job.Settings.Inputs[0].TimecodeSource = "ZEROBASED" } - + job.Tags = {'SolutionId': 'SO0021'}; + let data = await mediaconvert.createJob(job).promise(); event.encodingJob = job; event.encodeJobId = data.Job.Id; diff --git a/source/encode/jest.config.js b/source/encode/jest.config.js new file mode 100644 index 00000000..967fc50a --- /dev/null +++ b/source/encode/jest.config.js @@ -0,0 +1,6 @@ +module.exports = { + roots: ['/lib'], + testMatch: ['**/*.spec.js'], + coveragePathIgnorePatterns: ['/lib/utils.test.js'], + coverageReporters: [['lcov', { projectRoot: '../' }], 'text'] + }; \ No newline at end of file diff --git a/source/encode/lib/index.spec.js b/source/encode/lib/index.spec.js index 5ba53172..81b1c6c3 100644 --- a/source/encode/lib/index.spec.js +++ b/source/encode/lib/index.spec.js @@ -71,7 +71,7 @@ describe('#ENCODE::', () => { const response = await lambda.handler(_event); expect(response.encodeJobId).to.equal('12345'); - expect(response.encodingJob.Settings.OutputGroups[0].Name).to.equal('HLS Group'); + expect(response.encodingJob.Settings.OutputGroups[0].OutputGroupSettings.Type).to.equal('HLS_GROUP_SETTINGS'); }); it('should succeed when FrameCapture is enabled', async () => { @@ -124,8 +124,8 @@ describe('#ENCODE::', () => { const settings = output.OutputGroupSettings.HlsGroupSettings; expect(settings).not.to.be.null; - expect(settings.SegmentLength).to.equal(5); - expect(settings.MinSegmentLength).to.equal(0); + expect(settings.SegmentLength).to.equal(10); + expect(settings.MinSegmentLength).to.equal(2); }); it('should fail when getJobTemplate throws an exception', async () => { diff --git a/source/encode/package.json b/source/encode/package.json index 33431952..97f4818a 100644 --- a/source/encode/package.json +++ b/source/encode/package.json @@ -8,7 +8,7 @@ "main": "index.js", "scripts": { "pretest": "npm i --quiet", - "test": "mocha lib/*.spec.js" + "test": "jest --coverage" }, "dependencies": { "lodash": "^4.17.15" @@ -19,7 +19,8 @@ "chai": "^4.2.0", "mocha": "^7.0.0", "sinon": "^8.1.1", - "sinon-chai": "^3.4.0" + "sinon-chai": "^3.4.0", + "jest": "^29.2.2" }, "private": true, "author": { diff --git a/source/error-handler/jest.config.js b/source/error-handler/jest.config.js new file mode 100644 index 00000000..967fc50a --- /dev/null +++ b/source/error-handler/jest.config.js @@ -0,0 +1,6 @@ +module.exports = { + roots: ['/lib'], + testMatch: ['**/*.spec.js'], + coveragePathIgnorePatterns: ['/lib/utils.test.js'], + coverageReporters: [['lcov', { projectRoot: '../' }], 'text'] + }; \ No newline at end of file diff --git a/source/error-handler/package.json b/source/error-handler/package.json index c2b96ba7..215371e8 100644 --- a/source/error-handler/package.json +++ b/source/error-handler/package.json @@ -8,7 +8,7 @@ "main": "index.js", "scripts": { "pretest": "npm i --quiet", - "test": "mocha lib/*.spec.js" + "test": "jest --coverage" }, "dependencies": {}, "devDependencies": { @@ -17,7 +17,8 @@ "chai": "^4.2.0", "mocha": "^7.0.0", "sinon": "^8.1.1", - "sinon-chai": "^3.4.0" + "sinon-chai": "^3.4.0", + "jest": "^29.2.2" }, "private": true, "author": { diff --git a/source/input-validate/index.js b/source/input-validate/index.js index 34fadca2..fc75110e 100644 --- a/source/input-validate/index.js +++ b/source/input-validate/index.js @@ -62,7 +62,7 @@ exports.handler = async (event) => { // https://github.com/awslabs/video-on-demand-on-aws/pull/23 // Normalize key in order to support different casing Object.keys(metadataFile).forEach((key) => { - const normalizedKey = key.charAt(0).toLowerCase() + key.substr(1); + const normalizedKey = key.charAt(0).toLowerCase() + key.substring(1); data[normalizedKey] = metadataFile[key]; }); diff --git a/source/input-validate/jest.config.js b/source/input-validate/jest.config.js new file mode 100644 index 00000000..967fc50a --- /dev/null +++ b/source/input-validate/jest.config.js @@ -0,0 +1,6 @@ +module.exports = { + roots: ['/lib'], + testMatch: ['**/*.spec.js'], + coveragePathIgnorePatterns: ['/lib/utils.test.js'], + coverageReporters: [['lcov', { projectRoot: '../' }], 'text'] + }; \ No newline at end of file diff --git a/source/input-validate/package.json b/source/input-validate/package.json index ae6a9ce5..34bc39a7 100644 --- a/source/input-validate/package.json +++ b/source/input-validate/package.json @@ -8,7 +8,7 @@ "main": "index.js", "scripts": { "pretest": "npm i --quiet", - "test": "mocha lib/*.spec.js" + "test": "jest --coverage" }, "dependencies": { "moment": "^2.24.0" @@ -19,7 +19,8 @@ "chai": "^4.2.0", "mocha": "^7.0.0", "sinon": "^8.1.1", - "sinon-chai": "^3.4.0" + "sinon-chai": "^3.4.0", + "jest": "^29.2.2" }, "private": true, "author": { diff --git a/source/media-package-assets/index.js b/source/media-package-assets/index.js index d03e29e5..375e1722 100644 --- a/source/media-package-assets/index.js +++ b/source/media-package-assets/index.js @@ -60,6 +60,7 @@ const handler = async (event) => { SourceRoleArn: process.env.MediaPackageVodRole, ResourceId: randomId }; + params.Tags = {'SolutionId': 'SO0021'}; console.log(`Ingesting asset:: ${JSON.stringify(params, null, 2)}`); const response = await mediaPackageVod.createAsset(params).promise(); diff --git a/source/media-package-assets/jest.config.js b/source/media-package-assets/jest.config.js new file mode 100644 index 00000000..967fc50a --- /dev/null +++ b/source/media-package-assets/jest.config.js @@ -0,0 +1,6 @@ +module.exports = { + roots: ['/lib'], + testMatch: ['**/*.spec.js'], + coveragePathIgnorePatterns: ['/lib/utils.test.js'], + coverageReporters: [['lcov', { projectRoot: '../' }], 'text'] + }; \ No newline at end of file diff --git a/source/media-package-assets/package.json b/source/media-package-assets/package.json index 31504fb6..19b76a51 100644 --- a/source/media-package-assets/package.json +++ b/source/media-package-assets/package.json @@ -8,7 +8,7 @@ "main": "index.js", "scripts": { "pretest": "npm i --quiet", - "test": "mocha lib/*.spec.js" + "test": "jest --coverage" }, "dependencies": {}, "devDependencies": { @@ -17,7 +17,8 @@ "chai": "^4.2.0", "mocha": "^7.0.0", "sinon": "^8.1.1", - "sinon-chai": "^3.4.0" + "sinon-chai": "^3.4.0", + "jest": "^29.2.2" }, "private": true, "author": { diff --git a/source/mediainfo/.coverage b/source/mediainfo/.coverage new file mode 100644 index 00000000..6b7d0d68 Binary files /dev/null and b/source/mediainfo/.coverage differ diff --git a/source/mediainfo/.coveragerc b/source/mediainfo/.coveragerc new file mode 100644 index 00000000..e0b2cbeb --- /dev/null +++ b/source/mediainfo/.coveragerc @@ -0,0 +1,6 @@ +[run] +relative_files = True +source = . +omit = + setup.py + test_*.py \ No newline at end of file diff --git a/source/output-validate/jest.config.js b/source/output-validate/jest.config.js new file mode 100644 index 00000000..967fc50a --- /dev/null +++ b/source/output-validate/jest.config.js @@ -0,0 +1,6 @@ +module.exports = { + roots: ['/lib'], + testMatch: ['**/*.spec.js'], + coveragePathIgnorePatterns: ['/lib/utils.test.js'], + coverageReporters: [['lcov', { projectRoot: '../' }], 'text'] + }; \ No newline at end of file diff --git a/source/output-validate/package.json b/source/output-validate/package.json index 20670643..c487372a 100644 --- a/source/output-validate/package.json +++ b/source/output-validate/package.json @@ -8,11 +8,11 @@ "main": "index.js", "scripts": { "pretest": "npm i --quiet", - "test": "mocha lib/*.spec.js" + "test": "jest --coverage" }, "dependencies": { "moment": "^2.24.0", - "uuid": "^3.4.0" + "uuid": "^9.0.0" }, "devDependencies": { "aws-sdk": "^2.609.0", @@ -20,7 +20,8 @@ "chai": "^4.2.0", "mocha": "^7.0.0", "sinon": "^8.1.1", - "sinon-chai": "^3.4.0" + "sinon-chai": "^3.4.0", + "jest": "^29.2.2" }, "private": true, "author": { diff --git a/source/profiler/jest.config.js b/source/profiler/jest.config.js new file mode 100644 index 00000000..967fc50a --- /dev/null +++ b/source/profiler/jest.config.js @@ -0,0 +1,6 @@ +module.exports = { + roots: ['/lib'], + testMatch: ['**/*.spec.js'], + coveragePathIgnorePatterns: ['/lib/utils.test.js'], + coverageReporters: [['lcov', { projectRoot: '../' }], 'text'] + }; \ No newline at end of file diff --git a/source/profiler/package.json b/source/profiler/package.json index 0b7dc5ce..aef399e3 100644 --- a/source/profiler/package.json +++ b/source/profiler/package.json @@ -8,7 +8,7 @@ "main": "index.js", "scripts": { "pretest": "npm i --quiet", - "test": "mocha lib/*.spec.js" + "test": "jest --coverage" }, "dependencies": {}, "devDependencies": { @@ -17,7 +17,8 @@ "chai": "^4.2.0", "mocha": "^7.0.0", "sinon": "^8.1.1", - "sinon-chai": "^3.4.0" + "sinon-chai": "^3.4.0", + "jest": "^29.2.2" }, "private": true, "author": { diff --git a/source/sns-notification/jest.config.js b/source/sns-notification/jest.config.js new file mode 100644 index 00000000..967fc50a --- /dev/null +++ b/source/sns-notification/jest.config.js @@ -0,0 +1,6 @@ +module.exports = { + roots: ['/lib'], + testMatch: ['**/*.spec.js'], + coveragePathIgnorePatterns: ['/lib/utils.test.js'], + coverageReporters: [['lcov', { projectRoot: '../' }], 'text'] + }; \ No newline at end of file diff --git a/source/sns-notification/package.json b/source/sns-notification/package.json index 7a7aef33..855e453a 100644 --- a/source/sns-notification/package.json +++ b/source/sns-notification/package.json @@ -8,7 +8,7 @@ "main": "index.js", "scripts": { "pretest": "npm i --quiet", - "test": "mocha lib/*.spec.js" + "test": "jest --coverage" }, "dependencies": {}, "devDependencies": { @@ -17,7 +17,8 @@ "chai": "^4.2.0", "mocha": "^7.0.0", "sinon": "^8.1.1", - "sinon-chai": "^3.4.0" + "sinon-chai": "^3.4.0", + "jest": "^29.2.2" }, "private": true, "author": { diff --git a/source/sqs-publish/jest.config.js b/source/sqs-publish/jest.config.js new file mode 100644 index 00000000..967fc50a --- /dev/null +++ b/source/sqs-publish/jest.config.js @@ -0,0 +1,6 @@ +module.exports = { + roots: ['/lib'], + testMatch: ['**/*.spec.js'], + coveragePathIgnorePatterns: ['/lib/utils.test.js'], + coverageReporters: [['lcov', { projectRoot: '../' }], 'text'] + }; \ No newline at end of file diff --git a/source/sqs-publish/package.json b/source/sqs-publish/package.json index 5afc6a7f..a7dacc9b 100644 --- a/source/sqs-publish/package.json +++ b/source/sqs-publish/package.json @@ -8,16 +8,17 @@ "main": "index.js", "scripts": { "pretest": "npm i --quiet", - "test": "mocha lib/*.spec.js" + "test": "jest --coverage" }, "dependencies": {}, "devDependencies": { "aws-sdk": "^2.609.0", - "aws-sdk-mock": "^4.5.0", + "aws-sdk-mock": "^5.8.0", "chai": "^4.2.0", "mocha": "^6.2.2", "sinon": "^7.5.0", - "sinon-chai": "^3.3.0" + "sinon-chai": "^3.3.0", + "jest": "^29.2.2" }, "private": true, "author": { diff --git a/source/step-functions/index.js b/source/step-functions/index.js index 447ab136..265cce5d 100644 --- a/source/step-functions/index.js +++ b/source/step-functions/index.js @@ -12,7 +12,7 @@ *********************************************************************************************************************/ const AWS = require('aws-sdk'); -const uuidv4 = require('uuid/v4'); +const { v4: uuidv4 } = require('uuid'); const error = require('./lib/error.js'); exports.handler = async (event) => { diff --git a/source/step-functions/jest.config.js b/source/step-functions/jest.config.js new file mode 100644 index 00000000..967fc50a --- /dev/null +++ b/source/step-functions/jest.config.js @@ -0,0 +1,6 @@ +module.exports = { + roots: ['/lib'], + testMatch: ['**/*.spec.js'], + coveragePathIgnorePatterns: ['/lib/utils.test.js'], + coverageReporters: [['lcov', { projectRoot: '../' }], 'text'] + }; \ No newline at end of file diff --git a/source/step-functions/package.json b/source/step-functions/package.json index a280c331..d813593b 100644 --- a/source/step-functions/package.json +++ b/source/step-functions/package.json @@ -8,11 +8,11 @@ "main": "index.js", "scripts": { "pretest": "npm i --quiet", - "test": "mocha lib/*.spec.js" + "test": "jest --coverage" }, "dependencies": { "moment": "^2.24.0", - "uuid": "^3.4.0" + "uuid": "^9.0.0" }, "devDependencies": { "aws-sdk": "^2.609.0", @@ -20,7 +20,8 @@ "chai": "^4.2.0", "mocha": "^7.0.0", "sinon": "^8.1.1", - "sinon-chai": "^3.4.0" + "sinon-chai": "^3.4.0", + "jest": "^29.2.2" }, "private": true, "author": { diff --git a/source/test/coverage-reports/jest/archive-source/lcov.info b/source/test/coverage-reports/jest/archive-source/lcov.info new file mode 100644 index 00000000..8258576e --- /dev/null +++ b/source/test/coverage-reports/jest/archive-source/lcov.info @@ -0,0 +1,44 @@ +TN: +SF:archive-source/index.js +FN:17,(anonymous_0) +FNF:1 +FNH:1 +FNDA:2,(anonymous_0) +DA:14,1 +DA:15,1 +DA:17,1 +DA:18,2 +DA:20,2 +DA:22,2 +DA:23,2 +DA:40,2 +DA:42,1 +DA:43,1 +DA:46,1 +LF:11 +LH:11 +BRF:0 +BRH:0 +end_of_record +TN: +SF:archive-source/lib/error.js +FN:16,(anonymous_0) +FNF:1 +FNH:1 +FNDA:1,(anonymous_0) +DA:14,1 +DA:16,1 +DA:17,1 +DA:21,1 +DA:22,1 +DA:29,1 +DA:34,1 +DA:36,0 +DA:37,0 +DA:40,1 +DA:43,1 +LF:11 +LH:9 +BRF:0 +BRH:0 +end_of_record diff --git a/source/test/coverage-reports/jest/cdk/clover.xml b/source/test/coverage-reports/jest/cdk/clover.xml new file mode 100644 index 00000000..ed3578c2 --- /dev/null +++ b/source/test/coverage-reports/jest/cdk/clover.xml @@ -0,0 +1,325 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/test/coverage-reports/jest/cdk/coverage-final.json b/source/test/coverage-reports/jest/cdk/coverage-final.json new file mode 100644 index 00000000..7e08a332 --- /dev/null +++ b/source/test/coverage-reports/jest/cdk/coverage-final.json @@ -0,0 +1,2 @@ +{"/codebuild/output/src986986522/src/source/cdk/lib/vod-stack.ts": {"path":"/codebuild/output/src986986522/src/source/cdk/lib/vod-stack.ts","statementMap":{"0":{"start":{"line":14,"column":0},"end":{"line":14,"column":35}},"1":{"start":{"line":16,"column":0},"end":{"line":16,"column":41}},"2":{"start":{"line":17,"column":0},"end":{"line":17,"column":43}},"3":{"start":{"line":18,"column":0},"end":{"line":18,"column":57}},"4":{"start":{"line":19,"column":0},"end":{"line":19,"column":49}},"5":{"start":{"line":20,"column":0},"end":{"line":20,"column":43}},"6":{"start":{"line":21,"column":0},"end":{"line":21,"column":43}},"7":{"start":{"line":22,"column":0},"end":{"line":22,"column":67}},"8":{"start":{"line":23,"column":0},"end":{"line":23,"column":53}},"9":{"start":{"line":24,"column":0},"end":{"line":24,"column":53}},"10":{"start":{"line":25,"column":0},"end":{"line":25,"column":61}},"11":{"start":{"line":26,"column":0},"end":{"line":26,"column":49}},"12":{"start":{"line":27,"column":0},"end":{"line":27,"column":58}},"13":{"start":{"line":28,"column":0},"end":{"line":28,"column":71}},"14":{"start":{"line":30,"column":0},"end":{"line":30,"column":77}},"15":{"start":{"line":31,"column":0},"end":{"line":31,"column":42}},"16":{"start":{"line":35,"column":4},"end":{"line":35,"column":28}},"17":{"start":{"line":40,"column":23},"end":{"line":40,"column":31}},"18":{"start":{"line":41,"column":25},"end":{"line":41,"column":49}},"19":{"start":{"line":42,"column":4},"end":{"line":42,"column":186}},"20":{"start":{"line":46,"column":23},"end":{"line":50,"column":6}},"21":{"start":{"line":51,"column":28},"end":{"line":56,"column":6}},"22":{"start":{"line":57,"column":20},"end":{"line":63,"column":6}},"23":{"start":{"line":64,"column":25},"end":{"line":69,"column":6}},"24":{"start":{"line":70,"column":31},"end":{"line":75,"column":6}},"25":{"start":{"line":76,"column":22},"end":{"line":81,"column":6}},"26":{"start":{"line":82,"column":22},"end":{"line":87,"column":6}},"27":{"start":{"line":88,"column":35},"end":{"line":93,"column":6}},"28":{"start":{"line":97,"column":4},"end":{"line":149,"column":6}},"29":{"start":{"line":153,"column":4},"end":{"line":159,"column":7}},"30":{"start":{"line":163,"column":40},"end":{"line":165,"column":6}},"31":{"start":{"line":166,"column":34},"end":{"line":168,"column":6}},"32":{"start":{"line":169,"column":31},"end":{"line":171,"column":6}},"33":{"start":{"line":172,"column":31},"end":{"line":174,"column":6}},"34":{"start":{"line":184,"column":23},"end":{"line":194,"column":6}},"35":{"start":{"line":195,"column":26},"end":{"line":195,"column":79}},"36":{"start":{"line":196,"column":4},"end":{"line":196,"column":75}},"37":{"start":{"line":197,"column":4},"end":{"line":197,"column":80}},"38":{"start":{"line":200,"column":4},"end":{"line":212,"column":6}},"39":{"start":{"line":214,"column":4},"end":{"line":225,"column":6}},"40":{"start":{"line":230,"column":19},"end":{"line":266,"column":6}},"41":{"start":{"line":267,"column":22},"end":{"line":267,"column":71}},"42":{"start":{"line":268,"column":4},"end":{"line":268,"column":71}},"43":{"start":{"line":269,"column":4},"end":{"line":269,"column":76}},"44":{"start":{"line":272,"column":4},"end":{"line":281,"column":6}},"45":{"start":{"line":283,"column":4},"end":{"line":291,"column":6}},"46":{"start":{"line":296,"column":24},"end":{"line":315,"column":6}},"47":{"start":{"line":316,"column":27},"end":{"line":316,"column":81}},"48":{"start":{"line":317,"column":4},"end":{"line":317,"column":76}},"49":{"start":{"line":318,"column":4},"end":{"line":318,"column":81}},"50":{"start":{"line":321,"column":4},"end":{"line":329,"column":6}},"51":{"start":{"line":337,"column":24},"end":{"line":345,"column":6}},"52":{"start":{"line":346,"column":25},"end":{"line":360,"column":6}},"53":{"start":{"line":363,"column":4},"end":{"line":371,"column":6}},"54":{"start":{"line":372,"column":4},"end":{"line":386,"column":6}},"55":{"start":{"line":392,"column":31},"end":{"line":394,"column":6}},"56":{"start":{"line":395,"column":33},"end":{"line":463,"column":6}},"57":{"start":{"line":464,"column":4},"end":{"line":464,"column":58}},"58":{"start":{"line":467,"column":34},"end":{"line":467,"column":94}},"59":{"start":{"line":468,"column":4},"end":{"line":480,"column":6}},"60":{"start":{"line":481,"column":36},"end":{"line":481,"column":100}},"61":{"start":{"line":482,"column":4},"end":{"line":494,"column":6}},"62":{"start":{"line":496,"column":4},"end":{"line":504,"column":6}},"63":{"start":{"line":506,"column":33},"end":{"line":517,"column":6}},"64":{"start":{"line":518,"column":4},"end":{"line":518,"column":64}},"65":{"start":{"line":519,"column":4},"end":{"line":519,"column":66}},"66":{"start":{"line":522,"column":36},"end":{"line":522,"column":105}},"67":{"start":{"line":523,"column":4},"end":{"line":538,"column":6}},"68":{"start":{"line":543,"column":33},"end":{"line":548,"column":6}},"69":{"start":{"line":553,"column":34},"end":{"line":562,"column":6}},"70":{"start":{"line":567,"column":28},"end":{"line":577,"column":6}},"71":{"start":{"line":582,"column":29},"end":{"line":584,"column":6}},"72":{"start":{"line":586,"column":31},"end":{"line":604,"column":6}},"73":{"start":{"line":605,"column":4},"end":{"line":605,"column":54}},"74":{"start":{"line":608,"column":32},"end":{"line":608,"column":90}},"75":{"start":{"line":609,"column":4},"end":{"line":618,"column":6}},"76":{"start":{"line":620,"column":4},"end":{"line":628,"column":6}},"77":{"start":{"line":633,"column":32},"end":{"line":635,"column":6}},"78":{"start":{"line":637,"column":34},"end":{"line":652,"column":6}},"79":{"start":{"line":653,"column":4},"end":{"line":653,"column":60}},"80":{"start":{"line":656,"column":35},"end":{"line":656,"column":96}},"81":{"start":{"line":657,"column":4},"end":{"line":666,"column":6}},"82":{"start":{"line":668,"column":4},"end":{"line":676,"column":6}},"83":{"start":{"line":682,"column":21},"end":{"line":684,"column":6}},"84":{"start":{"line":685,"column":4},"end":{"line":685,"column":92}},"85":{"start":{"line":686,"column":24},"end":{"line":686,"column":75}},"86":{"start":{"line":687,"column":4},"end":{"line":687,"column":49}},"87":{"start":{"line":692,"column":19},"end":{"line":696,"column":6}},"88":{"start":{"line":697,"column":22},"end":{"line":697,"column":71}},"89":{"start":{"line":698,"column":4},"end":{"line":698,"column":47}},"90":{"start":{"line":699,"column":4},"end":{"line":699,"column":49}},"91":{"start":{"line":701,"column":21},"end":{"line":709,"column":6}},"92":{"start":{"line":710,"column":24},"end":{"line":710,"column":75}},"93":{"start":{"line":711,"column":4},"end":{"line":711,"column":49}},"94":{"start":{"line":712,"column":4},"end":{"line":712,"column":51}},"95":{"start":{"line":715,"column":4},"end":{"line":723,"column":6}},"96":{"start":{"line":729,"column":26},"end":{"line":737,"column":6}},"97":{"start":{"line":738,"column":4},"end":{"line":748,"column":7}},"98":{"start":{"line":750,"column":24},"end":{"line":750,"column":85}},"99":{"start":{"line":751,"column":4},"end":{"line":751,"column":73}},"100":{"start":{"line":752,"column":4},"end":{"line":752,"column":78}},"101":{"start":{"line":755,"column":4},"end":{"line":767,"column":6}},"102":{"start":{"line":772,"column":29},"end":{"line":774,"column":6}},"103":{"start":{"line":776,"column":31},"end":{"line":801,"column":6}},"104":{"start":{"line":802,"column":4},"end":{"line":802,"column":54}},"105":{"start":{"line":805,"column":32},"end":{"line":805,"column":90}},"106":{"start":{"line":806,"column":4},"end":{"line":815,"column":6}},"107":{"start":{"line":817,"column":4},"end":{"line":825,"column":6}},"108":{"start":{"line":827,"column":31},"end":{"line":841,"column":6}},"109":{"start":{"line":842,"column":4},"end":{"line":842,"column":60}},"110":{"start":{"line":843,"column":4},"end":{"line":843,"column":62}},"111":{"start":{"line":846,"column":34},"end":{"line":846,"column":101}},"112":{"start":{"line":847,"column":4},"end":{"line":862,"column":6}},"113":{"start":{"line":864,"column":28},"end":{"line":877,"column":6}},"114":{"start":{"line":878,"column":4},"end":{"line":882,"column":7}},"115":{"start":{"line":888,"column":30},"end":{"line":890,"column":6}},"116":{"start":{"line":891,"column":32},"end":{"line":911,"column":6}},"117":{"start":{"line":912,"column":4},"end":{"line":912,"column":56}},"118":{"start":{"line":915,"column":33},"end":{"line":915,"column":92}},"119":{"start":{"line":916,"column":4},"end":{"line":925,"column":6}},"120":{"start":{"line":927,"column":4},"end":{"line":935,"column":6}},"121":{"start":{"line":937,"column":32},"end":{"line":976,"column":6}},"122":{"start":{"line":977,"column":4},"end":{"line":977,"column":62}},"123":{"start":{"line":978,"column":4},"end":{"line":978,"column":64}},"124":{"start":{"line":981,"column":35},"end":{"line":981,"column":103}},"125":{"start":{"line":982,"column":4},"end":{"line":997,"column":6}},"126":{"start":{"line":1002,"column":26},"end":{"line":1004,"column":6}},"127":{"start":{"line":1005,"column":28},"end":{"line":1025,"column":6}},"128":{"start":{"line":1026,"column":4},"end":{"line":1026,"column":48}},"129":{"start":{"line":1029,"column":29},"end":{"line":1029,"column":84}},"130":{"start":{"line":1030,"column":4},"end":{"line":1039,"column":6}},"131":{"start":{"line":1041,"column":4},"end":{"line":1049,"column":6}},"132":{"start":{"line":1051,"column":28},"end":{"line":1063,"column":6}},"133":{"start":{"line":1064,"column":4},"end":{"line":1064,"column":54}},"134":{"start":{"line":1065,"column":4},"end":{"line":1065,"column":56}},"135":{"start":{"line":1068,"column":31},"end":{"line":1068,"column":95}},"136":{"start":{"line":1069,"column":4},"end":{"line":1084,"column":6}},"137":{"start":{"line":1089,"column":29},"end":{"line":1091,"column":6}},"138":{"start":{"line":1092,"column":31},"end":{"line":1112,"column":6}},"139":{"start":{"line":1113,"column":4},"end":{"line":1113,"column":54}},"140":{"start":{"line":1116,"column":32},"end":{"line":1116,"column":90}},"141":{"start":{"line":1117,"column":4},"end":{"line":1126,"column":6}},"142":{"start":{"line":1128,"column":4},"end":{"line":1136,"column":6}},"143":{"start":{"line":1138,"column":31},"end":{"line":1152,"column":6}},"144":{"start":{"line":1153,"column":4},"end":{"line":1153,"column":60}},"145":{"start":{"line":1154,"column":4},"end":{"line":1154,"column":62}},"146":{"start":{"line":1157,"column":34},"end":{"line":1157,"column":101}},"147":{"start":{"line":1158,"column":4},"end":{"line":1173,"column":6}},"148":{"start":{"line":1178,"column":25},"end":{"line":1180,"column":6}},"149":{"start":{"line":1181,"column":27},"end":{"line":1201,"column":6}},"150":{"start":{"line":1202,"column":4},"end":{"line":1202,"column":46}},"151":{"start":{"line":1205,"column":28},"end":{"line":1205,"column":82}},"152":{"start":{"line":1206,"column":4},"end":{"line":1215,"column":6}},"153":{"start":{"line":1217,"column":4},"end":{"line":1225,"column":6}},"154":{"start":{"line":1227,"column":27},"end":{"line":1241,"column":6}},"155":{"start":{"line":1242,"column":4},"end":{"line":1242,"column":52}},"156":{"start":{"line":1243,"column":4},"end":{"line":1243,"column":54}},"157":{"start":{"line":1246,"column":30},"end":{"line":1246,"column":93}},"158":{"start":{"line":1247,"column":4},"end":{"line":1262,"column":6}},"159":{"start":{"line":1267,"column":23},"end":{"line":1269,"column":6}},"160":{"start":{"line":1270,"column":25},"end":{"line":1299,"column":6}},"161":{"start":{"line":1300,"column":4},"end":{"line":1300,"column":42}},"162":{"start":{"line":1303,"column":26},"end":{"line":1303,"column":78}},"163":{"start":{"line":1304,"column":4},"end":{"line":1313,"column":6}},"164":{"start":{"line":1315,"column":4},"end":{"line":1323,"column":6}},"165":{"start":{"line":1325,"column":25},"end":{"line":1340,"column":6}},"166":{"start":{"line":1341,"column":4},"end":{"line":1341,"column":48}},"167":{"start":{"line":1342,"column":4},"end":{"line":1342,"column":50}},"168":{"start":{"line":1345,"column":28},"end":{"line":1345,"column":89}},"169":{"start":{"line":1346,"column":4},"end":{"line":1361,"column":6}},"170":{"start":{"line":1366,"column":31},"end":{"line":1368,"column":6}},"171":{"start":{"line":1369,"column":33},"end":{"line":1393,"column":6}},"172":{"start":{"line":1394,"column":4},"end":{"line":1394,"column":58}},"173":{"start":{"line":1397,"column":34},"end":{"line":1397,"column":94}},"174":{"start":{"line":1398,"column":4},"end":{"line":1407,"column":6}},"175":{"start":{"line":1409,"column":4},"end":{"line":1417,"column":6}},"176":{"start":{"line":1419,"column":33},"end":{"line":1434,"column":6}},"177":{"start":{"line":1435,"column":4},"end":{"line":1435,"column":64}},"178":{"start":{"line":1436,"column":4},"end":{"line":1436,"column":66}},"179":{"start":{"line":1439,"column":36},"end":{"line":1439,"column":105}},"180":{"start":{"line":1440,"column":4},"end":{"line":1455,"column":6}},"181":{"start":{"line":1460,"column":30},"end":{"line":1462,"column":6}},"182":{"start":{"line":1463,"column":32},"end":{"line":1483,"column":6}},"183":{"start":{"line":1484,"column":4},"end":{"line":1484,"column":56}},"184":{"start":{"line":1487,"column":33},"end":{"line":1487,"column":92}},"185":{"start":{"line":1488,"column":4},"end":{"line":1497,"column":6}},"186":{"start":{"line":1499,"column":4},"end":{"line":1507,"column":6}},"187":{"start":{"line":1509,"column":32},"end":{"line":1522,"column":6}},"188":{"start":{"line":1523,"column":4},"end":{"line":1523,"column":62}},"189":{"start":{"line":1524,"column":4},"end":{"line":1524,"column":64}},"190":{"start":{"line":1527,"column":35},"end":{"line":1527,"column":103}},"191":{"start":{"line":1528,"column":4},"end":{"line":1543,"column":6}},"192":{"start":{"line":1548,"column":31},"end":{"line":1550,"column":6}},"193":{"start":{"line":1551,"column":33},"end":{"line":1576,"column":6}},"194":{"start":{"line":1577,"column":4},"end":{"line":1577,"column":58}},"195":{"start":{"line":1580,"column":34},"end":{"line":1580,"column":94}},"196":{"start":{"line":1581,"column":4},"end":{"line":1590,"column":6}},"197":{"start":{"line":1592,"column":4},"end":{"line":1600,"column":6}},"198":{"start":{"line":1602,"column":33},"end":{"line":1616,"column":6}},"199":{"start":{"line":1617,"column":4},"end":{"line":1617,"column":64}},"200":{"start":{"line":1618,"column":4},"end":{"line":1618,"column":66}},"201":{"start":{"line":1621,"column":36},"end":{"line":1621,"column":105}},"202":{"start":{"line":1622,"column":4},"end":{"line":1637,"column":6}},"203":{"start":{"line":1642,"column":32},"end":{"line":1644,"column":6}},"204":{"start":{"line":1645,"column":34},"end":{"line":1670,"column":6}},"205":{"start":{"line":1671,"column":4},"end":{"line":1671,"column":60}},"206":{"start":{"line":1674,"column":35},"end":{"line":1674,"column":96}},"207":{"start":{"line":1675,"column":4},"end":{"line":1684,"column":6}},"208":{"start":{"line":1686,"column":4},"end":{"line":1694,"column":6}},"209":{"start":{"line":1696,"column":34},"end":{"line":1710,"column":6}},"210":{"start":{"line":1711,"column":4},"end":{"line":1711,"column":66}},"211":{"start":{"line":1712,"column":4},"end":{"line":1712,"column":68}},"212":{"start":{"line":1715,"column":37},"end":{"line":1715,"column":107}},"213":{"start":{"line":1716,"column":4},"end":{"line":1731,"column":6}},"214":{"start":{"line":1736,"column":35},"end":{"line":1738,"column":6}},"215":{"start":{"line":1739,"column":37},"end":{"line":1767,"column":6}},"216":{"start":{"line":1768,"column":4},"end":{"line":1768,"column":66}},"217":{"start":{"line":1771,"column":38},"end":{"line":1771,"column":102}},"218":{"start":{"line":1772,"column":4},"end":{"line":1781,"column":6}},"219":{"start":{"line":1782,"column":40},"end":{"line":1782,"column":108}},"220":{"start":{"line":1783,"column":4},"end":{"line":1792,"column":6}},"221":{"start":{"line":1794,"column":4},"end":{"line":1802,"column":6}},"222":{"start":{"line":1804,"column":37},"end":{"line":1820,"column":6}},"223":{"start":{"line":1821,"column":4},"end":{"line":1821,"column":72}},"224":{"start":{"line":1822,"column":4},"end":{"line":1822,"column":74}},"225":{"start":{"line":1825,"column":40},"end":{"line":1825,"column":113}},"226":{"start":{"line":1826,"column":4},"end":{"line":1841,"column":6}},"227":{"start":{"line":1846,"column":30},"end":{"line":1848,"column":6}},"228":{"start":{"line":1850,"column":32},"end":{"line":1874,"column":6}},"229":{"start":{"line":1875,"column":4},"end":{"line":1875,"column":56}},"230":{"start":{"line":1878,"column":33},"end":{"line":1878,"column":92}},"231":{"start":{"line":1879,"column":4},"end":{"line":1888,"column":6}},"232":{"start":{"line":1890,"column":4},"end":{"line":1898,"column":6}},"233":{"start":{"line":1900,"column":32},"end":{"line":1916,"column":6}},"234":{"start":{"line":1917,"column":4},"end":{"line":1917,"column":62}},"235":{"start":{"line":1918,"column":4},"end":{"line":1918,"column":64}},"236":{"start":{"line":1921,"column":35},"end":{"line":1921,"column":103}},"237":{"start":{"line":1922,"column":4},"end":{"line":1937,"column":6}},"238":{"start":{"line":1939,"column":31},"end":{"line":1952,"column":6}},"239":{"start":{"line":1953,"column":4},"end":{"line":1957,"column":7}},"240":{"start":{"line":1958,"column":4},"end":{"line":1962,"column":7}},"241":{"start":{"line":1967,"column":21},"end":{"line":1975,"column":6}},"242":{"start":{"line":1976,"column":4},"end":{"line":1976,"column":53}},"243":{"start":{"line":1981,"column":37},"end":{"line":1983,"column":6}},"244":{"start":{"line":1984,"column":39},"end":{"line":1992,"column":6}},"245":{"start":{"line":1993,"column":4},"end":{"line":1993,"column":70}},"246":{"start":{"line":1996,"column":40},"end":{"line":1996,"column":106}},"247":{"start":{"line":1997,"column":4},"end":{"line":2006,"column":6}},"248":{"start":{"line":2008,"column":4},"end":{"line":2016,"column":6}},"249":{"start":{"line":2022,"column":30},"end":{"line":2025,"column":6}},"250":{"start":{"line":2026,"column":26},"end":{"line":2029,"column":6}},"251":{"start":{"line":2030,"column":37},"end":{"line":2033,"column":6}},"252":{"start":{"line":2034,"column":38},"end":{"line":2037,"column":6}},"253":{"start":{"line":2038,"column":38},"end":{"line":2041,"column":6}},"254":{"start":{"line":2042,"column":38},"end":{"line":2045,"column":6}},"255":{"start":{"line":2046,"column":39},"end":{"line":2049,"column":6}},"256":{"start":{"line":2050,"column":31},"end":{"line":2053,"column":6}},"257":{"start":{"line":2054,"column":25},"end":{"line":2057,"column":6}},"258":{"start":{"line":2058,"column":23},"end":{"line":2061,"column":6}},"259":{"start":{"line":2062,"column":31},"end":{"line":2065,"column":6}},"260":{"start":{"line":2066,"column":24},"end":{"line":2069,"column":6}},"261":{"start":{"line":2070,"column":28},"end":{"line":2073,"column":6}},"262":{"start":{"line":2074,"column":35},"end":{"line":2077,"column":6}},"263":{"start":{"line":2078,"column":31},"end":{"line":2081,"column":6}},"264":{"start":{"line":2082,"column":26},"end":{"line":2082,"column":56}},"265":{"start":{"line":2093,"column":4},"end":{"line":2093,"column":55}},"266":{"start":{"line":2094,"column":37},"end":{"line":2099,"column":39}},"267":{"start":{"line":2101,"column":27},"end":{"line":2105,"column":6}},"268":{"start":{"line":2108,"column":4},"end":{"line":2119,"column":6}},"269":{"start":{"line":2139,"column":38},"end":{"line":2156,"column":38}},"270":{"start":{"line":2158,"column":28},"end":{"line":2162,"column":6}},"271":{"start":{"line":2165,"column":4},"end":{"line":2176,"column":6}},"272":{"start":{"line":2193,"column":4},"end":{"line":2193,"column":51}},"273":{"start":{"line":2194,"column":29},"end":{"line":2194,"column":73}},"274":{"start":{"line":2195,"column":4},"end":{"line":2197,"column":32}},"275":{"start":{"line":2199,"column":4},"end":{"line":2199,"column":46}},"276":{"start":{"line":2200,"column":22},"end":{"line":2200,"column":56}},"277":{"start":{"line":2201,"column":4},"end":{"line":2203,"column":35}},"278":{"start":{"line":2205,"column":24},"end":{"line":2207,"column":22}},"279":{"start":{"line":2209,"column":4},"end":{"line":2209,"column":45}},"280":{"start":{"line":2210,"column":31},"end":{"line":2210,"column":74}},"281":{"start":{"line":2211,"column":4},"end":{"line":2213,"column":30}},"282":{"start":{"line":2215,"column":4},"end":{"line":2215,"column":41}},"283":{"start":{"line":2216,"column":4},"end":{"line":2216,"column":45}},"284":{"start":{"line":2217,"column":32},"end":{"line":2220,"column":36}},"285":{"start":{"line":2222,"column":38},"end":{"line":2223,"column":32}},"286":{"start":{"line":2225,"column":28},"end":{"line":2229,"column":6}},"287":{"start":{"line":2232,"column":38},"end":{"line":2232,"column":108}},"288":{"start":{"line":2233,"column":29},"end":{"line":2233,"column":91}},"289":{"start":{"line":2234,"column":4},"end":{"line":2243,"column":6}},"290":{"start":{"line":2245,"column":4},"end":{"line":2256,"column":6}},"291":{"start":{"line":2257,"column":4},"end":{"line":2265,"column":6}},"292":{"start":{"line":2271,"column":17},"end":{"line":2276,"column":6}},"293":{"start":{"line":2281,"column":4},"end":{"line":2294,"column":7}},"294":{"start":{"line":2299,"column":28},"end":{"line":2299,"column":74}},"295":{"start":{"line":2300,"column":27},"end":{"line":2309,"column":6}},"296":{"start":{"line":2310,"column":24},"end":{"line":2313,"column":6}},"297":{"start":{"line":2314,"column":4},"end":{"line":2314,"column":37}},"298":{"start":{"line":2315,"column":4},"end":{"line":2315,"column":59}},"299":{"start":{"line":2316,"column":4},"end":{"line":2316,"column":63}},"300":{"start":{"line":2317,"column":4},"end":{"line":2317,"column":71}},"301":{"start":{"line":2318,"column":4},"end":{"line":2318,"column":67}},"302":{"start":{"line":2319,"column":4},"end":{"line":2319,"column":69}},"303":{"start":{"line":2321,"column":4},"end":{"line":2321,"column":51}},"304":{"start":{"line":2322,"column":4},"end":{"line":2322,"column":56}},"305":{"start":{"line":2328,"column":4},"end":{"line":2332,"column":7}},"306":{"start":{"line":2333,"column":4},"end":{"line":2337,"column":7}},"307":{"start":{"line":2338,"column":4},"end":{"line":2342,"column":7}},"308":{"start":{"line":2343,"column":4},"end":{"line":2347,"column":7}},"309":{"start":{"line":2348,"column":4},"end":{"line":2352,"column":7}},"310":{"start":{"line":2353,"column":4},"end":{"line":2357,"column":7}},"311":{"start":{"line":2358,"column":4},"end":{"line":2362,"column":7}},"312":{"start":{"line":2363,"column":4},"end":{"line":2367,"column":7}},"313":{"start":{"line":2368,"column":4},"end":{"line":2372,"column":7}},"314":{"start":{"line":2377,"column":4},"end":{"line":2377,"column":52}},"315":{"start":{"line":33,"column":0},"end":{"line":33,"column":13}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":34,"column":2},"end":{"line":34,"column":14}},"loc":{"start":{"line":34,"column":66},"end":{"line":2379,"column":3}}}},"branchMap":{},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":1,"11":1,"12":1,"13":1,"14":1,"15":1,"16":1,"17":1,"18":1,"19":1,"20":1,"21":1,"22":1,"23":1,"24":1,"25":1,"26":1,"27":1,"28":1,"29":1,"30":1,"31":1,"32":1,"33":1,"34":1,"35":1,"36":1,"37":1,"38":1,"39":1,"40":1,"41":1,"42":1,"43":1,"44":1,"45":1,"46":1,"47":1,"48":1,"49":1,"50":1,"51":1,"52":1,"53":1,"54":1,"55":1,"56":1,"57":1,"58":1,"59":1,"60":1,"61":1,"62":1,"63":1,"64":1,"65":1,"66":1,"67":1,"68":1,"69":1,"70":1,"71":1,"72":1,"73":1,"74":1,"75":1,"76":1,"77":1,"78":1,"79":1,"80":1,"81":1,"82":1,"83":1,"84":1,"85":1,"86":1,"87":1,"88":1,"89":1,"90":1,"91":1,"92":1,"93":1,"94":1,"95":1,"96":1,"97":1,"98":1,"99":1,"100":1,"101":1,"102":1,"103":1,"104":1,"105":1,"106":1,"107":1,"108":1,"109":1,"110":1,"111":1,"112":1,"113":1,"114":1,"115":1,"116":1,"117":1,"118":1,"119":1,"120":1,"121":1,"122":1,"123":1,"124":1,"125":1,"126":1,"127":1,"128":1,"129":1,"130":1,"131":1,"132":1,"133":1,"134":1,"135":1,"136":1,"137":1,"138":1,"139":1,"140":1,"141":1,"142":1,"143":1,"144":1,"145":1,"146":1,"147":1,"148":1,"149":1,"150":1,"151":1,"152":1,"153":1,"154":1,"155":1,"156":1,"157":1,"158":1,"159":1,"160":1,"161":1,"162":1,"163":1,"164":1,"165":1,"166":1,"167":1,"168":1,"169":1,"170":1,"171":1,"172":1,"173":1,"174":1,"175":1,"176":1,"177":1,"178":1,"179":1,"180":1,"181":1,"182":1,"183":1,"184":1,"185":1,"186":1,"187":1,"188":1,"189":1,"190":1,"191":1,"192":1,"193":1,"194":1,"195":1,"196":1,"197":1,"198":1,"199":1,"200":1,"201":1,"202":1,"203":1,"204":1,"205":1,"206":1,"207":1,"208":1,"209":1,"210":1,"211":1,"212":1,"213":1,"214":1,"215":1,"216":1,"217":1,"218":1,"219":1,"220":1,"221":1,"222":1,"223":1,"224":1,"225":1,"226":1,"227":1,"228":1,"229":1,"230":1,"231":1,"232":1,"233":1,"234":1,"235":1,"236":1,"237":1,"238":1,"239":1,"240":1,"241":1,"242":1,"243":1,"244":1,"245":1,"246":1,"247":1,"248":1,"249":1,"250":1,"251":1,"252":1,"253":1,"254":1,"255":1,"256":1,"257":1,"258":1,"259":1,"260":1,"261":1,"262":1,"263":1,"264":1,"265":1,"266":1,"267":1,"268":1,"269":1,"270":1,"271":1,"272":1,"273":1,"274":1,"275":1,"276":1,"277":1,"278":1,"279":1,"280":1,"281":1,"282":1,"283":1,"284":1,"285":1,"286":1,"287":1,"288":1,"289":1,"290":1,"291":1,"292":1,"293":1,"294":1,"295":1,"296":1,"297":1,"298":1,"299":1,"300":1,"301":1,"302":1,"303":1,"304":1,"305":1,"306":1,"307":1,"308":1,"309":1,"310":1,"311":1,"312":1,"313":1,"314":1,"315":1},"f":{"0":1},"b":{}} +} diff --git a/source/test/coverage-reports/jest/cdk/lcov.info b/source/test/coverage-reports/jest/cdk/lcov.info new file mode 100644 index 00000000..074f377e --- /dev/null +++ b/source/test/coverage-reports/jest/cdk/lcov.info @@ -0,0 +1,327 @@ +TN: +SF:lib/vod-stack.ts +FN:34,(anonymous_0) +FNF:1 +FNH:1 +FNDA:1,(anonymous_0) +DA:14,1 +DA:16,1 +DA:17,1 +DA:18,1 +DA:19,1 +DA:20,1 +DA:21,1 +DA:22,1 +DA:23,1 +DA:24,1 +DA:25,1 +DA:26,1 +DA:27,1 +DA:28,1 +DA:30,1 +DA:31,1 +DA:33,1 +DA:35,1 +DA:40,1 +DA:41,1 +DA:42,1 +DA:46,1 +DA:51,1 +DA:57,1 +DA:64,1 +DA:70,1 +DA:76,1 +DA:82,1 +DA:88,1 +DA:97,1 +DA:153,1 +DA:163,1 +DA:166,1 +DA:169,1 +DA:172,1 +DA:184,1 +DA:195,1 +DA:196,1 +DA:197,1 +DA:200,1 +DA:214,1 +DA:230,1 +DA:267,1 +DA:268,1 +DA:269,1 +DA:272,1 +DA:283,1 +DA:296,1 +DA:316,1 +DA:317,1 +DA:318,1 +DA:321,1 +DA:337,1 +DA:346,1 +DA:363,1 +DA:372,1 +DA:392,1 +DA:395,1 +DA:464,1 +DA:467,1 +DA:468,1 +DA:481,1 +DA:482,1 +DA:496,1 +DA:506,1 +DA:518,1 +DA:519,1 +DA:522,1 +DA:523,1 +DA:543,1 +DA:553,1 +DA:567,1 +DA:582,1 +DA:586,1 +DA:605,1 +DA:608,1 +DA:609,1 +DA:620,1 +DA:633,1 +DA:637,1 +DA:653,1 +DA:656,1 +DA:657,1 +DA:668,1 +DA:682,1 +DA:685,1 +DA:686,1 +DA:687,1 +DA:692,1 +DA:697,1 +DA:698,1 +DA:699,1 +DA:701,1 +DA:710,1 +DA:711,1 +DA:712,1 +DA:715,1 +DA:729,1 +DA:738,1 +DA:750,1 +DA:751,1 +DA:752,1 +DA:755,1 +DA:772,1 +DA:776,1 +DA:802,1 +DA:805,1 +DA:806,1 +DA:817,1 +DA:827,1 +DA:842,1 +DA:843,1 +DA:846,1 +DA:847,1 +DA:864,1 +DA:878,1 +DA:888,1 +DA:891,1 +DA:912,1 +DA:915,1 +DA:916,1 +DA:927,1 +DA:937,1 +DA:977,1 +DA:978,1 +DA:981,1 +DA:982,1 +DA:1002,1 +DA:1005,1 +DA:1026,1 +DA:1029,1 +DA:1030,1 +DA:1041,1 +DA:1051,1 +DA:1064,1 +DA:1065,1 +DA:1068,1 +DA:1069,1 +DA:1089,1 +DA:1092,1 +DA:1113,1 +DA:1116,1 +DA:1117,1 +DA:1128,1 +DA:1138,1 +DA:1153,1 +DA:1154,1 +DA:1157,1 +DA:1158,1 +DA:1178,1 +DA:1181,1 +DA:1202,1 +DA:1205,1 +DA:1206,1 +DA:1217,1 +DA:1227,1 +DA:1242,1 +DA:1243,1 +DA:1246,1 +DA:1247,1 +DA:1267,1 +DA:1270,1 +DA:1300,1 +DA:1303,1 +DA:1304,1 +DA:1315,1 +DA:1325,1 +DA:1341,1 +DA:1342,1 +DA:1345,1 +DA:1346,1 +DA:1366,1 +DA:1369,1 +DA:1394,1 +DA:1397,1 +DA:1398,1 +DA:1409,1 +DA:1419,1 +DA:1435,1 +DA:1436,1 +DA:1439,1 +DA:1440,1 +DA:1460,1 +DA:1463,1 +DA:1484,1 +DA:1487,1 +DA:1488,1 +DA:1499,1 +DA:1509,1 +DA:1523,1 +DA:1524,1 +DA:1527,1 +DA:1528,1 +DA:1548,1 +DA:1551,1 +DA:1577,1 +DA:1580,1 +DA:1581,1 +DA:1592,1 +DA:1602,1 +DA:1617,1 +DA:1618,1 +DA:1621,1 +DA:1622,1 +DA:1642,1 +DA:1645,1 +DA:1671,1 +DA:1674,1 +DA:1675,1 +DA:1686,1 +DA:1696,1 +DA:1711,1 +DA:1712,1 +DA:1715,1 +DA:1716,1 +DA:1736,1 +DA:1739,1 +DA:1768,1 +DA:1771,1 +DA:1772,1 +DA:1782,1 +DA:1783,1 +DA:1794,1 +DA:1804,1 +DA:1821,1 +DA:1822,1 +DA:1825,1 +DA:1826,1 +DA:1846,1 +DA:1850,1 +DA:1875,1 +DA:1878,1 +DA:1879,1 +DA:1890,1 +DA:1900,1 +DA:1917,1 +DA:1918,1 +DA:1921,1 +DA:1922,1 +DA:1939,1 +DA:1953,1 +DA:1958,1 +DA:1967,1 +DA:1976,1 +DA:1981,1 +DA:1984,1 +DA:1993,1 +DA:1996,1 +DA:1997,1 +DA:2008,1 +DA:2022,1 +DA:2026,1 +DA:2030,1 +DA:2034,1 +DA:2038,1 +DA:2042,1 +DA:2046,1 +DA:2050,1 +DA:2054,1 +DA:2058,1 +DA:2062,1 +DA:2066,1 +DA:2070,1 +DA:2074,1 +DA:2078,1 +DA:2082,1 +DA:2093,1 +DA:2094,1 +DA:2101,1 +DA:2108,1 +DA:2139,1 +DA:2158,1 +DA:2165,1 +DA:2193,1 +DA:2194,1 +DA:2195,1 +DA:2199,1 +DA:2200,1 +DA:2201,1 +DA:2205,1 +DA:2209,1 +DA:2210,1 +DA:2211,1 +DA:2215,1 +DA:2216,1 +DA:2217,1 +DA:2222,1 +DA:2225,1 +DA:2232,1 +DA:2233,1 +DA:2234,1 +DA:2245,1 +DA:2257,1 +DA:2271,1 +DA:2281,1 +DA:2299,1 +DA:2300,1 +DA:2310,1 +DA:2314,1 +DA:2315,1 +DA:2316,1 +DA:2317,1 +DA:2318,1 +DA:2319,1 +DA:2321,1 +DA:2322,1 +DA:2328,1 +DA:2333,1 +DA:2338,1 +DA:2343,1 +DA:2348,1 +DA:2353,1 +DA:2358,1 +DA:2363,1 +DA:2368,1 +DA:2377,1 +LF:316 +LH:316 +BRF:0 +BRH:0 +end_of_record diff --git a/source/test/coverage-reports/jest/custom-resource/clover.xml b/source/test/coverage-reports/jest/custom-resource/clover.xml new file mode 100644 index 00000000..346ec406 --- /dev/null +++ b/source/test/coverage-reports/jest/custom-resource/clover.xml @@ -0,0 +1,236 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/test/coverage-reports/jest/custom-resource/coverage-final.json b/source/test/coverage-reports/jest/custom-resource/coverage-final.json new file mode 100644 index 00000000..d8ad18a6 --- /dev/null +++ b/source/test/coverage-reports/jest/custom-resource/coverage-final.json @@ -0,0 +1,8 @@ +{"/codebuild/output/src986986522/src/source/custom-resource/lib/cfn/index.js": {"path":"/codebuild/output/src986986522/src/source/custom-resource/lib/cfn/index.js","statementMap":{"0":{"start":{"line":14,"column":14},"end":{"line":14,"column":30}},"1":{"start":{"line":16,"column":19},"end":{"line":43,"column":1}},"2":{"start":{"line":19,"column":25},"end":{"line":27,"column":6}},"3":{"start":{"line":29,"column":19},"end":{"line":38,"column":5}},"4":{"start":{"line":40,"column":4},"end":{"line":40,"column":31}},"5":{"start":{"line":42,"column":4},"end":{"line":42,"column":23}},"6":{"start":{"line":45,"column":0},"end":{"line":47,"column":2}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":16,"column":19},"end":{"line":16,"column":20}},"loc":{"start":{"line":16,"column":75},"end":{"line":43,"column":1}},"line":16}},"branchMap":{},"s":{"0":1,"1":1,"2":2,"3":2,"4":2,"5":1,"6":1},"f":{"0":2},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"ebfb7562f7a0b6f79362f50f621df9b4f79e3d0c"} +,"/codebuild/output/src986986522/src/source/custom-resource/lib/mediaconvert/index.js": {"path":"/codebuild/output/src986986522/src/source/custom-resource/lib/mediaconvert/index.js","statementMap":{"0":{"start":{"line":14,"column":11},"end":{"line":14,"column":24}},"1":{"start":{"line":15,"column":12},"end":{"line":15,"column":30}},"2":{"start":{"line":17,"column":17},"end":{"line":17,"column":22}},"3":{"start":{"line":18,"column":20},"end":{"line":18,"column":44}},"4":{"start":{"line":20,"column":20},"end":{"line":97,"column":1}},"5":{"start":{"line":100,"column":22},"end":{"line":113,"column":1}},"6":{"start":{"line":115,"column":30},"end":{"line":128,"column":1}},"7":{"start":{"line":131,"column":30},"end":{"line":144,"column":1}},"8":{"start":{"line":146,"column":38},"end":{"line":159,"column":1}},"9":{"start":{"line":162,"column":21},"end":{"line":169,"column":1}},"10":{"start":{"line":163,"column":25},"end":{"line":163,"column":97}},"11":{"start":{"line":164,"column":17},"end":{"line":164,"column":65}},"12":{"start":{"line":166,"column":4},"end":{"line":168,"column":6}},"13":{"start":{"line":171,"column":25},"end":{"line":181,"column":1}},"14":{"start":{"line":172,"column":4},"end":{"line":180,"column":5}},"15":{"start":{"line":174,"column":21},"end":{"line":174,"column":67}},"16":{"start":{"line":175,"column":8},"end":{"line":175,"column":46}},"17":{"start":{"line":176,"column":8},"end":{"line":176,"column":47}},"18":{"start":{"line":178,"column":8},"end":{"line":178,"column":59}},"19":{"start":{"line":179,"column":8},"end":{"line":179,"column":57}},"20":{"start":{"line":183,"column":15},"end":{"line":194,"column":1}},"21":{"start":{"line":184,"column":25},"end":{"line":188,"column":6}},"22":{"start":{"line":190,"column":4},"end":{"line":190,"column":90}},"23":{"start":{"line":191,"column":4},"end":{"line":191,"column":82}},"24":{"start":{"line":193,"column":4},"end":{"line":193,"column":21}},"25":{"start":{"line":196,"column":15},"end":{"line":221,"column":1}},"26":{"start":{"line":197,"column":25},"end":{"line":201,"column":6}},"27":{"start":{"line":203,"column":28},"end":{"line":203,"column":35}},"28":{"start":{"line":204,"column":15},"end":{"line":204,"column":84}},"29":{"start":{"line":207,"column":4},"end":{"line":211,"column":7}},"30":{"start":{"line":208,"column":8},"end":{"line":210,"column":9}},"31":{"start":{"line":209,"column":12},"end":{"line":209,"column":39}},"32":{"start":{"line":214,"column":4},"end":{"line":218,"column":5}},"33":{"start":{"line":215,"column":8},"end":{"line":215,"column":57}},"34":{"start":{"line":216,"column":8},"end":{"line":216,"column":86}},"35":{"start":{"line":217,"column":8},"end":{"line":217,"column":94}},"36":{"start":{"line":220,"column":4},"end":{"line":220,"column":21}},"37":{"start":{"line":223,"column":23},"end":{"line":230,"column":1}},"38":{"start":{"line":224,"column":4},"end":{"line":229,"column":5}},"39":{"start":{"line":225,"column":19},"end":{"line":225,"column":42}},"40":{"start":{"line":227,"column":8},"end":{"line":227,"column":62}},"41":{"start":{"line":228,"column":8},"end":{"line":228,"column":48}},"42":{"start":{"line":232,"column":25},"end":{"line":239,"column":1}},"43":{"start":{"line":233,"column":4},"end":{"line":238,"column":5}},"44":{"start":{"line":234,"column":19},"end":{"line":234,"column":40}},"45":{"start":{"line":236,"column":8},"end":{"line":236,"column":67}},"46":{"start":{"line":237,"column":8},"end":{"line":237,"column":50}},"47":{"start":{"line":241,"column":15},"end":{"line":264,"column":1}},"48":{"start":{"line":242,"column":25},"end":{"line":246,"column":6}},"49":{"start":{"line":248,"column":4},"end":{"line":261,"column":5}},"50":{"start":{"line":249,"column":8},"end":{"line":249,"column":94}},"51":{"start":{"line":250,"column":8},"end":{"line":250,"column":86}},"52":{"start":{"line":254,"column":8},"end":{"line":254,"column":78}},"53":{"start":{"line":255,"column":8},"end":{"line":255,"column":85}},"54":{"start":{"line":256,"column":8},"end":{"line":256,"column":74}},"55":{"start":{"line":259,"column":8},"end":{"line":259,"column":25}},"56":{"start":{"line":260,"column":8},"end":{"line":260,"column":18}},"57":{"start":{"line":263,"column":4},"end":{"line":263,"column":21}},"58":{"start":{"line":266,"column":0},"end":{"line":271,"column":2}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":162,"column":21},"end":{"line":162,"column":22}},"loc":{"start":{"line":162,"column":33},"end":{"line":169,"column":1}},"line":162},"1":{"name":"(anonymous_1)","decl":{"start":{"line":171,"column":25},"end":{"line":171,"column":26}},"loc":{"start":{"line":171,"column":67},"end":{"line":181,"column":1}},"line":171},"2":{"name":"(anonymous_2)","decl":{"start":{"line":183,"column":15},"end":{"line":183,"column":16}},"loc":{"start":{"line":183,"column":33},"end":{"line":194,"column":1}},"line":183},"3":{"name":"(anonymous_3)","decl":{"start":{"line":196,"column":15},"end":{"line":196,"column":16}},"loc":{"start":{"line":196,"column":33},"end":{"line":221,"column":1}},"line":196},"4":{"name":"(anonymous_4)","decl":{"start":{"line":207,"column":30},"end":{"line":207,"column":31}},"loc":{"start":{"line":207,"column":42},"end":{"line":211,"column":5}},"line":207},"5":{"name":"(anonymous_5)","decl":{"start":{"line":223,"column":23},"end":{"line":223,"column":24}},"loc":{"start":{"line":223,"column":63},"end":{"line":230,"column":1}},"line":223},"6":{"name":"(anonymous_6)","decl":{"start":{"line":232,"column":25},"end":{"line":232,"column":26}},"loc":{"start":{"line":232,"column":67},"end":{"line":239,"column":1}},"line":232},"7":{"name":"(anonymous_7)","decl":{"start":{"line":241,"column":15},"end":{"line":241,"column":16}},"loc":{"start":{"line":241,"column":33},"end":{"line":264,"column":1}},"line":241}},"branchMap":{"0":{"loc":{"start":{"line":208,"column":8},"end":{"line":210,"column":9}},"type":"if","locations":[{"start":{"line":208,"column":8},"end":{"line":210,"column":9}},{"start":{},"end":{}}],"line":208},"1":{"loc":{"start":{"line":208,"column":12},"end":{"line":208,"column":92}},"type":"binary-expr","locations":[{"start":{"line":208,"column":12},"end":{"line":208,"column":52}},{"start":{"line":208,"column":56},"end":{"line":208,"column":92}}],"line":208},"2":{"loc":{"start":{"line":214,"column":4},"end":{"line":218,"column":5}},"type":"if","locations":[{"start":{"line":214,"column":4},"end":{"line":218,"column":5}},{"start":{},"end":{}}],"line":214}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":2,"11":2,"12":1,"13":1,"14":7,"15":19,"16":19,"17":19,"18":19,"19":18,"20":1,"21":2,"22":2,"23":1,"24":1,"25":1,"26":4,"27":4,"28":4,"29":3,"30":3,"31":1,"32":3,"33":2,"34":2,"35":2,"36":3,"37":1,"38":1,"39":19,"40":19,"41":19,"42":1,"43":4,"44":12,"45":12,"46":12,"47":1,"48":1,"49":1,"50":1,"51":1,"52":1,"53":1,"54":1,"55":0,"56":0,"57":1,"58":1},"f":{"0":2,"1":7,"2":2,"3":4,"4":3,"5":1,"6":4,"7":1},"b":{"0":[1,2],"1":[3,3],"2":[2,1]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"a16340927efa784f06c4193539290a042f9730bd"} +,"/codebuild/output/src986986522/src/source/custom-resource/lib/mediapackage/cloudfront.js": {"path":"/codebuild/output/src986986522/src/source/custom-resource/lib/mediapackage/cloudfront.js","statementMap":{"0":{"start":{"line":14,"column":12},"end":{"line":14,"column":30}},"1":{"start":{"line":15,"column":17},"end":{"line":15,"column":30}},"2":{"start":{"line":17,"column":0},"end":{"line":112,"column":2}},"3":{"start":{"line":18,"column":4},"end":{"line":20,"column":5}},"4":{"start":{"line":19,"column":8},"end":{"line":19,"column":59}},"5":{"start":{"line":22,"column":4},"end":{"line":24,"column":5}},"6":{"start":{"line":23,"column":8},"end":{"line":23,"column":55}},"7":{"start":{"line":26,"column":23},"end":{"line":26,"column":93}},"8":{"start":{"line":27,"column":21},"end":{"line":27,"column":93}},"9":{"start":{"line":28,"column":19},"end":{"line":28,"column":46}},"10":{"start":{"line":30,"column":25},"end":{"line":30,"column":80}},"11":{"start":{"line":30,"column":59},"end":{"line":30,"column":79}},"12":{"start":{"line":31,"column":4},"end":{"line":34,"column":5}},"13":{"start":{"line":32,"column":8},"end":{"line":32,"column":99}},"14":{"start":{"line":33,"column":8},"end":{"line":33,"column":15}},"15":{"start":{"line":36,"column":4},"end":{"line":36,"column":83}},"16":{"start":{"line":37,"column":16},"end":{"line":37,"column":35}},"17":{"start":{"line":39,"column":25},"end":{"line":52,"column":5}},"18":{"start":{"line":54,"column":4},"end":{"line":54,"column":70}},"19":{"start":{"line":56,"column":27},"end":{"line":87,"column":5}},"20":{"start":{"line":89,"column":4},"end":{"line":93,"column":5}},"21":{"start":{"line":90,"column":8},"end":{"line":90,"column":57}},"22":{"start":{"line":92,"column":8},"end":{"line":92,"column":55}},"23":{"start":{"line":95,"column":4},"end":{"line":95,"column":72}},"24":{"start":{"line":97,"column":4},"end":{"line":111,"column":5}},"25":{"start":{"line":98,"column":23},"end":{"line":102,"column":9}},"26":{"start":{"line":104,"column":8},"end":{"line":104,"column":62}},"27":{"start":{"line":105,"column":8},"end":{"line":105,"column":82}},"28":{"start":{"line":106,"column":8},"end":{"line":106,"column":97}},"29":{"start":{"line":108,"column":8},"end":{"line":110,"column":9}},"30":{"start":{"line":109,"column":12},"end":{"line":109,"column":24}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":17,"column":33},"end":{"line":17,"column":34}},"loc":{"start":{"line":17,"column":71},"end":{"line":112,"column":1}},"line":17},"1":{"name":"(anonymous_1)","decl":{"start":{"line":30,"column":51},"end":{"line":30,"column":52}},"loc":{"start":{"line":30,"column":59},"end":{"line":30,"column":79}},"line":30}},"branchMap":{"0":{"loc":{"start":{"line":18,"column":4},"end":{"line":20,"column":5}},"type":"if","locations":[{"start":{"line":18,"column":4},"end":{"line":20,"column":5}},{"start":{},"end":{}}],"line":18},"1":{"loc":{"start":{"line":22,"column":4},"end":{"line":24,"column":5}},"type":"if","locations":[{"start":{"line":22,"column":4},"end":{"line":24,"column":5}},{"start":{},"end":{}}],"line":22},"2":{"loc":{"start":{"line":31,"column":4},"end":{"line":34,"column":5}},"type":"if","locations":[{"start":{"line":31,"column":4},"end":{"line":34,"column":5}},{"start":{},"end":{}}],"line":31},"3":{"loc":{"start":{"line":46,"column":23},"end":{"line":46,"column":38}},"type":"binary-expr","locations":[{"start":{"line":46,"column":23},"end":{"line":46,"column":31}},{"start":{"line":46,"column":35},"end":{"line":46,"column":38}}],"line":46},"4":{"loc":{"start":{"line":89,"column":4},"end":{"line":93,"column":5}},"type":"if","locations":[{"start":{"line":89,"column":4},"end":{"line":93,"column":5}},{"start":{"line":91,"column":11},"end":{"line":93,"column":5}}],"line":89},"5":{"loc":{"start":{"line":108,"column":8},"end":{"line":110,"column":9}},"type":"if","locations":[{"start":{"line":108,"column":8},"end":{"line":110,"column":9}},{"start":{},"end":{}}],"line":108}},"s":{"0":2,"1":2,"2":2,"3":11,"4":1,"5":10,"6":1,"7":9,"8":9,"9":8,"10":8,"11":9,"12":8,"13":4,"14":4,"15":4,"16":4,"17":4,"18":4,"19":4,"20":4,"21":0,"22":4,"23":4,"24":4,"25":4,"26":4,"27":2,"28":2,"29":2,"30":1},"f":{"0":11,"1":9},"b":{"0":[1,10],"1":[1,9],"2":[4,4],"3":[4,4],"4":[0,4],"5":[1,1]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"3295f7bafe68d84e8abe90a37a12a6b84b4a7d4b"} +,"/codebuild/output/src986986522/src/source/custom-resource/lib/mediapackage/index.js": {"path":"/codebuild/output/src986986522/src/source/custom-resource/lib/mediapackage/index.js","statementMap":{"0":{"start":{"line":14,"column":12},"end":{"line":14,"column":30}},"1":{"start":{"line":15,"column":15},"end":{"line":15,"column":32}},"2":{"start":{"line":16,"column":25},"end":{"line":16,"column":48}},"3":{"start":{"line":18,"column":31},"end":{"line":18,"column":32}},"4":{"start":{"line":19,"column":42},"end":{"line":19,"column":44}},"5":{"start":{"line":20,"column":30},"end":{"line":20,"column":37}},"6":{"start":{"line":22,"column":25},"end":{"line":36,"column":2}},"7":{"start":{"line":22,"column":49},"end":{"line":36,"column":1}},"8":{"start":{"line":38,"column":26},"end":{"line":49,"column":2}},"9":{"start":{"line":38,"column":50},"end":{"line":49,"column":1}},"10":{"start":{"line":51,"column":25},"end":{"line":60,"column":2}},"11":{"start":{"line":51,"column":49},"end":{"line":60,"column":1}},"12":{"start":{"line":62,"column":26},"end":{"line":75,"column":2}},"13":{"start":{"line":62,"column":50},"end":{"line":75,"column":1}},"14":{"start":{"line":77,"column":15},"end":{"line":134,"column":1}},"15":{"start":{"line":78,"column":28},"end":{"line":78,"column":103}},"16":{"start":{"line":79,"column":21},"end":{"line":79,"column":58}},"17":{"start":{"line":81,"column":22},"end":{"line":84,"column":5}},"18":{"start":{"line":86,"column":27},"end":{"line":86,"column":92}},"19":{"start":{"line":87,"column":18},"end":{"line":87,"column":23}},"20":{"start":{"line":89,"column":27},"end":{"line":89,"column":93}},"21":{"start":{"line":90,"column":4},"end":{"line":122,"column":5}},"22":{"start":{"line":91,"column":21},"end":{"line":91,"column":23}},"23":{"start":{"line":93,"column":8},"end":{"line":114,"column":9}},"24":{"start":{"line":95,"column":16},"end":{"line":95,"column":97}},"25":{"start":{"line":96,"column":16},"end":{"line":96,"column":22}},"26":{"start":{"line":99,"column":16},"end":{"line":99,"column":99}},"27":{"start":{"line":100,"column":16},"end":{"line":100,"column":22}},"28":{"start":{"line":103,"column":16},"end":{"line":103,"column":97}},"29":{"start":{"line":104,"column":16},"end":{"line":104,"column":22}},"30":{"start":{"line":107,"column":16},"end":{"line":107,"column":99}},"31":{"start":{"line":108,"column":16},"end":{"line":108,"column":22}},"32":{"start":{"line":111,"column":16},"end":{"line":111,"column":74}},"33":{"start":{"line":112,"column":16},"end":{"line":112,"column":30}},"34":{"start":{"line":113,"column":16},"end":{"line":113,"column":22}},"35":{"start":{"line":116,"column":8},"end":{"line":121,"column":9}},"36":{"start":{"line":117,"column":12},"end":{"line":117,"column":51}},"37":{"start":{"line":118,"column":12},"end":{"line":118,"column":87}},"38":{"start":{"line":119,"column":12},"end":{"line":119,"column":81}},"39":{"start":{"line":120,"column":12},"end":{"line":120,"column":27}},"40":{"start":{"line":124,"column":4},"end":{"line":126,"column":5}},"41":{"start":{"line":125,"column":8},"end":{"line":125,"column":88}},"42":{"start":{"line":128,"column":4},"end":{"line":128,"column":97}},"43":{"start":{"line":130,"column":4},"end":{"line":133,"column":6}},"44":{"start":{"line":136,"column":15},"end":{"line":148,"column":1}},"45":{"start":{"line":137,"column":28},"end":{"line":137,"column":103}},"46":{"start":{"line":138,"column":27},"end":{"line":138,"column":109}},"47":{"start":{"line":140,"column":4},"end":{"line":142,"column":5}},"48":{"start":{"line":141,"column":8},"end":{"line":141,"column":101}},"49":{"start":{"line":144,"column":4},"end":{"line":147,"column":6}},"50":{"start":{"line":150,"column":14},"end":{"line":189,"column":1}},"51":{"start":{"line":151,"column":28},"end":{"line":151,"column":103}},"52":{"start":{"line":152,"column":20},"end":{"line":152,"column":38}},"53":{"start":{"line":155,"column":4},"end":{"line":164,"column":20}},"54":{"start":{"line":156,"column":25},"end":{"line":156,"column":116}},"55":{"start":{"line":158,"column":8},"end":{"line":161,"column":9}},"56":{"start":{"line":159,"column":12},"end":{"line":159,"column":74}},"57":{"start":{"line":160,"column":12},"end":{"line":160,"column":55}},"58":{"start":{"line":163,"column":8},"end":{"line":163,"column":35}},"59":{"start":{"line":166,"column":4},"end":{"line":175,"column":20}},"60":{"start":{"line":167,"column":25},"end":{"line":167,"column":133}},"61":{"start":{"line":169,"column":8},"end":{"line":172,"column":9}},"62":{"start":{"line":170,"column":12},"end":{"line":170,"column":92}},"63":{"start":{"line":171,"column":12},"end":{"line":171,"column":64}},"64":{"start":{"line":174,"column":8},"end":{"line":174,"column":35}},"65":{"start":{"line":177,"column":4},"end":{"line":184,"column":5}},"66":{"start":{"line":178,"column":8},"end":{"line":178,"column":78}},"67":{"start":{"line":179,"column":8},"end":{"line":179,"column":50}},"68":{"start":{"line":181,"column":8},"end":{"line":183,"column":9}},"69":{"start":{"line":182,"column":12},"end":{"line":182,"column":24}},"70":{"start":{"line":186,"column":4},"end":{"line":188,"column":6}},"71":{"start":{"line":191,"column":0},"end":{"line":195,"column":2}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":22,"column":25},"end":{"line":22,"column":26}},"loc":{"start":{"line":22,"column":49},"end":{"line":36,"column":1}},"line":22},"1":{"name":"(anonymous_1)","decl":{"start":{"line":38,"column":26},"end":{"line":38,"column":27}},"loc":{"start":{"line":38,"column":50},"end":{"line":49,"column":1}},"line":38},"2":{"name":"(anonymous_2)","decl":{"start":{"line":51,"column":25},"end":{"line":51,"column":26}},"loc":{"start":{"line":51,"column":49},"end":{"line":60,"column":1}},"line":51},"3":{"name":"(anonymous_3)","decl":{"start":{"line":62,"column":26},"end":{"line":62,"column":27}},"loc":{"start":{"line":62,"column":50},"end":{"line":75,"column":1}},"line":62},"4":{"name":"(anonymous_4)","decl":{"start":{"line":77,"column":15},"end":{"line":77,"column":16}},"loc":{"start":{"line":77,"column":37},"end":{"line":134,"column":1}},"line":77},"5":{"name":"(anonymous_5)","decl":{"start":{"line":136,"column":15},"end":{"line":136,"column":16}},"loc":{"start":{"line":136,"column":37},"end":{"line":148,"column":1}},"line":136},"6":{"name":"(anonymous_6)","decl":{"start":{"line":150,"column":14},"end":{"line":150,"column":15}},"loc":{"start":{"line":150,"column":36},"end":{"line":189,"column":1}},"line":150}},"branchMap":{"0":{"loc":{"start":{"line":93,"column":8},"end":{"line":114,"column":9}},"type":"switch","locations":[{"start":{"line":94,"column":12},"end":{"line":96,"column":22}},{"start":{"line":98,"column":12},"end":{"line":100,"column":22}},{"start":{"line":102,"column":12},"end":{"line":104,"column":22}},{"start":{"line":106,"column":12},"end":{"line":108,"column":22}},{"start":{"line":110,"column":12},"end":{"line":113,"column":22}}],"line":93},"1":{"loc":{"start":{"line":116,"column":8},"end":{"line":121,"column":9}},"type":"if","locations":[{"start":{"line":116,"column":8},"end":{"line":121,"column":9}},{"start":{},"end":{}}],"line":116},"2":{"loc":{"start":{"line":124,"column":4},"end":{"line":126,"column":5}},"type":"if","locations":[{"start":{"line":124,"column":4},"end":{"line":126,"column":5}},{"start":{},"end":{}}],"line":124},"3":{"loc":{"start":{"line":140,"column":4},"end":{"line":142,"column":5}},"type":"if","locations":[{"start":{"line":140,"column":4},"end":{"line":142,"column":5}},{"start":{},"end":{}}],"line":140},"4":{"loc":{"start":{"line":181,"column":8},"end":{"line":183,"column":9}},"type":"if","locations":[{"start":{"line":181,"column":8},"end":{"line":183,"column":9}},{"start":{},"end":{}}],"line":181}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":5,"8":1,"9":3,"10":1,"11":0,"12":1,"13":0,"14":1,"15":7,"16":7,"17":7,"18":7,"19":6,"20":6,"21":6,"22":10,"23":10,"24":5,"25":5,"26":3,"27":3,"28":0,"29":0,"30":0,"31":0,"32":2,"33":2,"34":2,"35":10,"36":8,"37":8,"38":8,"39":7,"40":5,"41":1,"42":4,"43":3,"44":1,"45":2,"46":2,"47":2,"48":1,"49":2,"50":1,"51":3,"52":3,"53":3,"54":3,"55":3,"56":2,"57":2,"58":3,"59":3,"60":3,"61":3,"62":2,"63":2,"64":3,"65":3,"66":3,"67":1,"68":2,"69":1,"70":2,"71":1},"f":{"0":5,"1":3,"2":0,"3":0,"4":7,"5":2,"6":3},"b":{"0":[5,3,0,0,2],"1":[8,2],"2":[1,4],"3":[1,1],"4":[1,1]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"9be6a75dfd0ab5fa3dc2fe3fd191c1cc459803a5"} +,"/codebuild/output/src986986522/src/source/custom-resource/lib/mediapackage/test-assets.js": {"path":"/codebuild/output/src986986522/src/source/custom-resource/lib/mediapackage/test-assets.js","statementMap":{"0":{"start":{"line":1,"column":28},"end":{"line":42,"column":1}},"1":{"start":{"line":44,"column":28},"end":{"line":51,"column":1}},"2":{"start":{"line":53,"column":0},"end":{"line":58,"column":2}}},"fnMap":{},"branchMap":{},"s":{"0":2,"1":2,"2":2},"f":{},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"00ef0a0a97e5255cc8fdde7b18e1688dd6a80ec7"} +,"/codebuild/output/src986986522/src/source/custom-resource/lib/metrics/index.js": {"path":"/codebuild/output/src986986522/src/source/custom-resource/lib/metrics/index.js","statementMap":{"0":{"start":{"line":14,"column":14},"end":{"line":14,"column":30}},"1":{"start":{"line":15,"column":15},"end":{"line":15,"column":32}},"2":{"start":{"line":17,"column":21},"end":{"line":23,"column":1}},"3":{"start":{"line":19,"column":4},"end":{"line":19,"column":34}},"4":{"start":{"line":20,"column":4},"end":{"line":20,"column":30}},"5":{"start":{"line":22,"column":4},"end":{"line":22,"column":18}},"6":{"start":{"line":25,"column":13},"end":{"line":48,"column":1}},"7":{"start":{"line":28,"column":20},"end":{"line":33,"column":5}},"8":{"start":{"line":35,"column":19},"end":{"line":43,"column":5}},"9":{"start":{"line":45,"column":4},"end":{"line":45,"column":31}},"10":{"start":{"line":47,"column":4},"end":{"line":47,"column":23}},"11":{"start":{"line":50,"column":0},"end":{"line":53,"column":2}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":17,"column":21},"end":{"line":17,"column":22}},"loc":{"start":{"line":17,"column":33},"end":{"line":23,"column":1}},"line":17},"1":{"name":"(anonymous_1)","decl":{"start":{"line":25,"column":13},"end":{"line":25,"column":14}},"loc":{"start":{"line":25,"column":31},"end":{"line":48,"column":1}},"line":25}},"branchMap":{},"s":{"0":1,"1":1,"2":1,"3":3,"4":3,"5":3,"6":1,"7":2,"8":2,"9":2,"10":1,"11":1},"f":{"0":3,"1":2},"b":{},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"8f26680736dd6673bacb72e3d26c79bf62c6bbe5"} +,"/codebuild/output/src986986522/src/source/custom-resource/lib/s3/index.js": {"path":"/codebuild/output/src986986522/src/source/custom-resource/lib/s3/index.js","statementMap":{"0":{"start":{"line":14,"column":12},"end":{"line":14,"column":30}},"1":{"start":{"line":16,"column":22},"end":{"line":351,"column":1}},"2":{"start":{"line":17,"column":15},"end":{"line":17,"column":77}},"3":{"start":{"line":21,"column":4},"end":{"line":348,"column":5}},"4":{"start":{"line":23,"column":12},"end":{"line":317,"column":14}},"5":{"start":{"line":319,"column":12},"end":{"line":319,"column":78}},"6":{"start":{"line":320,"column":12},"end":{"line":320,"column":74}},"7":{"start":{"line":321,"column":12},"end":{"line":321,"column":18}},"8":{"start":{"line":324,"column":12},"end":{"line":340,"column":14}},"9":{"start":{"line":342,"column":12},"end":{"line":342,"column":78}},"10":{"start":{"line":343,"column":12},"end":{"line":343,"column":74}},"11":{"start":{"line":344,"column":12},"end":{"line":344,"column":18}},"12":{"start":{"line":347,"column":12},"end":{"line":347,"column":82}},"13":{"start":{"line":350,"column":4},"end":{"line":350,"column":21}},"14":{"start":{"line":353,"column":0},"end":{"line":355,"column":2}}},"fnMap":{"0":{"name":"(anonymous_0)","decl":{"start":{"line":16,"column":22},"end":{"line":16,"column":23}},"loc":{"start":{"line":16,"column":40},"end":{"line":351,"column":1}},"line":16}},"branchMap":{"0":{"loc":{"start":{"line":21,"column":4},"end":{"line":348,"column":5}},"type":"switch","locations":[{"start":{"line":22,"column":8},"end":{"line":321,"column":18}},{"start":{"line":323,"column":8},"end":{"line":344,"column":18}},{"start":{"line":346,"column":8},"end":{"line":347,"column":82}}],"line":21}},"s":{"0":1,"1":1,"2":4,"3":4,"4":2,"5":2,"6":2,"7":1,"8":1,"9":1,"10":1,"11":1,"12":1,"13":2,"14":1},"f":{"0":4},"b":{"0":[2,1,1]},"_coverageSchema":"1a1c01bbd47fc00a2c39e90264f33305004495a9","hash":"9391fdd1932091d5e45a241a9192fb28cba7960b"} +} diff --git a/source/test/coverage-reports/jest/custom-resource/lcov.info b/source/test/coverage-reports/jest/custom-resource/lcov.info new file mode 100644 index 00000000..c64d1135 --- /dev/null +++ b/source/test/coverage-reports/jest/custom-resource/lcov.info @@ -0,0 +1,333 @@ +TN: +SF:lib/cfn/index.js +FN:16,(anonymous_0) +FNF:1 +FNH:1 +FNDA:2,(anonymous_0) +DA:14,1 +DA:16,1 +DA:19,2 +DA:29,2 +DA:40,2 +DA:42,1 +DA:45,1 +LF:7 +LH:7 +BRF:0 +BRH:0 +end_of_record +TN: +SF:lib/mediaconvert/index.js +FN:162,(anonymous_0) +FN:171,(anonymous_1) +FN:183,(anonymous_2) +FN:196,(anonymous_3) +FN:207,(anonymous_4) +FN:223,(anonymous_5) +FN:232,(anonymous_6) +FN:241,(anonymous_7) +FNF:8 +FNH:8 +FNDA:2,(anonymous_0) +FNDA:7,(anonymous_1) +FNDA:2,(anonymous_2) +FNDA:4,(anonymous_3) +FNDA:3,(anonymous_4) +FNDA:1,(anonymous_5) +FNDA:4,(anonymous_6) +FNDA:1,(anonymous_7) +DA:14,1 +DA:15,1 +DA:17,1 +DA:18,1 +DA:20,1 +DA:100,1 +DA:115,1 +DA:131,1 +DA:146,1 +DA:162,1 +DA:163,2 +DA:164,2 +DA:166,1 +DA:171,1 +DA:172,7 +DA:174,19 +DA:175,19 +DA:176,19 +DA:178,19 +DA:179,18 +DA:183,1 +DA:184,2 +DA:190,2 +DA:191,1 +DA:193,1 +DA:196,1 +DA:197,4 +DA:203,4 +DA:204,4 +DA:207,3 +DA:208,3 +DA:209,1 +DA:214,3 +DA:215,2 +DA:216,2 +DA:217,2 +DA:220,3 +DA:223,1 +DA:224,1 +DA:225,19 +DA:227,19 +DA:228,19 +DA:232,1 +DA:233,4 +DA:234,12 +DA:236,12 +DA:237,12 +DA:241,1 +DA:242,1 +DA:248,1 +DA:249,1 +DA:250,1 +DA:254,1 +DA:255,1 +DA:256,1 +DA:259,0 +DA:260,0 +DA:263,1 +DA:266,1 +LF:59 +LH:57 +BRDA:208,0,0,1 +BRDA:208,0,1,2 +BRDA:208,1,0,3 +BRDA:208,1,1,3 +BRDA:214,2,0,2 +BRDA:214,2,1,1 +BRF:6 +BRH:6 +end_of_record +TN: +SF:lib/mediapackage/cloudfront.js +FN:17,(anonymous_0) +FN:30,(anonymous_1) +FNF:2 +FNH:2 +FNDA:11,(anonymous_0) +FNDA:9,(anonymous_1) +DA:14,2 +DA:15,2 +DA:17,2 +DA:18,11 +DA:19,1 +DA:22,10 +DA:23,1 +DA:26,9 +DA:27,9 +DA:28,8 +DA:30,9 +DA:31,8 +DA:32,4 +DA:33,4 +DA:36,4 +DA:37,4 +DA:39,4 +DA:54,4 +DA:56,4 +DA:89,4 +DA:90,0 +DA:92,4 +DA:95,4 +DA:97,4 +DA:98,4 +DA:104,4 +DA:105,2 +DA:106,2 +DA:108,2 +DA:109,1 +LF:30 +LH:29 +BRDA:18,0,0,1 +BRDA:18,0,1,10 +BRDA:22,1,0,1 +BRDA:22,1,1,9 +BRDA:31,2,0,4 +BRDA:31,2,1,4 +BRDA:46,3,0,4 +BRDA:46,3,1,4 +BRDA:89,4,0,0 +BRDA:89,4,1,4 +BRDA:108,5,0,1 +BRDA:108,5,1,1 +BRF:12 +BRH:11 +end_of_record +TN: +SF:lib/mediapackage/index.js +FN:22,(anonymous_0) +FN:38,(anonymous_1) +FN:51,(anonymous_2) +FN:62,(anonymous_3) +FN:77,(anonymous_4) +FN:136,(anonymous_5) +FN:150,(anonymous_6) +FNF:7 +FNH:5 +FNDA:5,(anonymous_0) +FNDA:3,(anonymous_1) +FNDA:0,(anonymous_2) +FNDA:0,(anonymous_3) +FNDA:7,(anonymous_4) +FNDA:2,(anonymous_5) +FNDA:3,(anonymous_6) +DA:14,1 +DA:15,1 +DA:16,1 +DA:18,1 +DA:19,1 +DA:20,1 +DA:22,5 +DA:38,3 +DA:51,1 +DA:62,1 +DA:77,1 +DA:78,7 +DA:79,7 +DA:81,7 +DA:86,7 +DA:87,6 +DA:89,6 +DA:90,6 +DA:91,10 +DA:93,10 +DA:95,5 +DA:96,5 +DA:99,3 +DA:100,3 +DA:103,0 +DA:104,0 +DA:107,0 +DA:108,0 +DA:111,2 +DA:112,2 +DA:113,2 +DA:116,10 +DA:117,8 +DA:118,8 +DA:119,8 +DA:120,7 +DA:124,5 +DA:125,1 +DA:128,4 +DA:130,3 +DA:136,1 +DA:137,2 +DA:138,2 +DA:140,2 +DA:141,1 +DA:144,2 +DA:150,1 +DA:151,3 +DA:152,3 +DA:155,3 +DA:156,3 +DA:158,3 +DA:159,2 +DA:160,2 +DA:163,3 +DA:166,3 +DA:167,3 +DA:169,3 +DA:170,2 +DA:171,2 +DA:174,3 +DA:177,3 +DA:178,3 +DA:179,1 +DA:181,2 +DA:182,1 +DA:186,2 +DA:191,1 +LF:68 +LH:64 +BRDA:93,0,0,5 +BRDA:93,0,1,3 +BRDA:93,0,2,0 +BRDA:93,0,3,0 +BRDA:93,0,4,2 +BRDA:116,1,0,8 +BRDA:116,1,1,2 +BRDA:124,2,0,1 +BRDA:124,2,1,4 +BRDA:140,3,0,1 +BRDA:140,3,1,1 +BRDA:181,4,0,1 +BRDA:181,4,1,1 +BRF:13 +BRH:11 +end_of_record +TN: +SF:lib/mediapackage/test-assets.js +FNF:0 +FNH:0 +DA:1,2 +DA:44,2 +DA:53,2 +LF:3 +LH:3 +BRF:0 +BRH:0 +end_of_record +TN: +SF:lib/metrics/index.js +FN:17,(anonymous_0) +FN:25,(anonymous_1) +FNF:2 +FNH:2 +FNDA:3,(anonymous_0) +FNDA:2,(anonymous_1) +DA:14,1 +DA:15,1 +DA:17,1 +DA:19,3 +DA:20,3 +DA:22,3 +DA:25,1 +DA:28,2 +DA:35,2 +DA:45,2 +DA:47,1 +DA:50,1 +LF:12 +LH:12 +BRF:0 +BRH:0 +end_of_record +TN: +SF:lib/s3/index.js +FN:16,(anonymous_0) +FNF:1 +FNH:1 +FNDA:4,(anonymous_0) +DA:14,1 +DA:16,1 +DA:17,4 +DA:21,4 +DA:23,2 +DA:319,2 +DA:320,2 +DA:321,1 +DA:324,1 +DA:342,1 +DA:343,1 +DA:344,1 +DA:347,1 +DA:350,2 +DA:353,1 +LF:15 +LH:15 +BRDA:21,0,0,2 +BRDA:21,0,1,1 +BRDA:21,0,2,1 +BRF:3 +BRH:3 +end_of_record diff --git a/source/test/coverage-reports/jest/dynamo/lcov.info b/source/test/coverage-reports/jest/dynamo/lcov.info new file mode 100644 index 00000000..4afcb9d1 --- /dev/null +++ b/source/test/coverage-reports/jest/dynamo/lcov.info @@ -0,0 +1,57 @@ +TN: +SF:dynamo/index.js +FN:17,(anonymous_0) +FN:34,(anonymous_1) +FNF:2 +FNH:2 +FNDA:2,(anonymous_0) +FNDA:2,(anonymous_1) +DA:14,1 +DA:15,1 +DA:17,1 +DA:18,2 +DA:20,2 +DA:25,2 +DA:28,2 +DA:29,2 +DA:30,2 +DA:31,2 +DA:32,2 +DA:34,2 +DA:35,2 +DA:36,2 +DA:37,2 +DA:40,2 +DA:50,2 +DA:51,2 +DA:54,1 +DA:56,1 +DA:57,1 +DA:60,1 +LF:22 +LH:22 +BRF:0 +BRH:0 +end_of_record +TN: +SF:dynamo/lib/error.js +FN:16,(anonymous_0) +FNF:1 +FNH:1 +FNDA:1,(anonymous_0) +DA:14,1 +DA:16,1 +DA:17,1 +DA:21,1 +DA:22,1 +DA:29,1 +DA:34,1 +DA:36,0 +DA:37,0 +DA:40,1 +DA:43,1 +LF:11 +LH:9 +BRF:0 +BRH:0 +end_of_record diff --git a/source/test/coverage-reports/jest/encode/lcov.info b/source/test/coverage-reports/jest/encode/lcov.info new file mode 100644 index 00000000..cae5307c --- /dev/null +++ b/source/test/coverage-reports/jest/encode/lcov.info @@ -0,0 +1,124 @@ +TN: +SF:encode/index.js +FN:18,(anonymous_0) +FN:29,(anonymous_1) +FN:42,(anonymous_2) +FN:55,(anonymous_3) +FN:68,(anonymous_4) +FN:81,(anonymous_5) +FN:119,(anonymous_6) +FN:123,(anonymous_7) +FN:181,(anonymous_8) +FNF:9 +FNH:9 +FNDA:5,(anonymous_0) +FNDA:5,(anonymous_1) +FNDA:5,(anonymous_2) +FNDA:5,(anonymous_3) +FNDA:5,(anonymous_4) +FNDA:5,(anonymous_5) +FNDA:4,(anonymous_6) +FNDA:5,(anonymous_7) +FNDA:4,(anonymous_8) +DA:14,1 +DA:15,1 +DA:16,1 +DA:18,5 +DA:29,5 +DA:42,5 +DA:55,5 +DA:68,5 +DA:81,5 +DA:119,1 +DA:120,4 +DA:123,1 +DA:124,5 +DA:126,5 +DA:131,5 +DA:132,5 +DA:133,5 +DA:136,5 +DA:168,5 +DA:169,5 +DA:170,5 +DA:171,5 +DA:172,5 +DA:173,5 +DA:175,5 +DA:176,4 +DA:181,4 +DA:182,4 +DA:184,4 +DA:186,0 +DA:187,0 +DA:188,0 +DA:191,4 +DA:192,4 +DA:193,4 +DA:196,0 +DA:197,0 +DA:198,0 +DA:201,0 +DA:202,0 +DA:203,0 +DA:206,0 +DA:207,0 +DA:208,0 +DA:211,4 +DA:212,4 +DA:214,4 +DA:215,4 +DA:219,4 +DA:220,1 +DA:225,4 +DA:226,3 +DA:227,3 +DA:228,3 +DA:230,4 +DA:232,4 +DA:233,3 +DA:234,3 +DA:236,3 +DA:238,2 +DA:239,2 +DA:242,3 +LF:62 +LH:50 +BRDA:184,0,0,0 +BRDA:184,0,1,4 +BRDA:184,0,2,0 +BRDA:184,0,3,0 +BRDA:184,0,4,0 +BRDA:211,1,0,4 +BRDA:211,1,1,0 +BRDA:219,2,0,1 +BRDA:219,2,1,3 +BRDA:225,3,0,3 +BRDA:225,3,1,1 +BRDA:225,4,0,4 +BRDA:225,4,1,2 +BRF:13 +BRH:8 +end_of_record +TN: +SF:encode/lib/error.js +FN:16,(anonymous_0) +FNF:1 +FNH:1 +FNDA:2,(anonymous_0) +DA:14,1 +DA:16,1 +DA:17,2 +DA:21,2 +DA:22,2 +DA:29,2 +DA:34,2 +DA:36,0 +DA:37,0 +DA:40,2 +DA:43,1 +LF:11 +LH:9 +BRF:0 +BRH:0 +end_of_record diff --git a/source/test/coverage-reports/jest/error-handler/lcov.info b/source/test/coverage-reports/jest/error-handler/lcov.info new file mode 100644 index 00000000..68956971 --- /dev/null +++ b/source/test/coverage-reports/jest/error-handler/lcov.info @@ -0,0 +1,36 @@ +TN: +SF:error-handler/index.js +FN:16,(anonymous_0) +FNF:1 +FNH:1 +FNDA:4,(anonymous_0) +DA:14,1 +DA:16,1 +DA:17,4 +DA:19,4 +DA:24,4 +DA:34,4 +DA:35,2 +DA:36,2 +DA:37,2 +DA:45,2 +DA:54,4 +DA:55,2 +DA:56,2 +DA:57,2 +DA:65,2 +DA:74,4 +DA:77,4 +DA:86,4 +DA:90,3 +DA:96,3 +DA:98,2 +LF:21 +LH:21 +BRDA:34,0,0,2 +BRDA:34,0,1,2 +BRDA:54,1,0,2 +BRDA:54,1,1,2 +BRF:4 +BRH:4 +end_of_record diff --git a/source/test/coverage-reports/jest/input-validate/lcov.info b/source/test/coverage-reports/jest/input-validate/lcov.info new file mode 100644 index 00000000..cd2d3913 --- /dev/null +++ b/source/test/coverage-reports/jest/input-validate/lcov.info @@ -0,0 +1,68 @@ +TN: +SF:input-validate/index.js +FN:18,(anonymous_0) +FN:64,(anonymous_1) +FNF:2 +FNH:2 +FNDA:5,(anonymous_0) +FNDA:12,(anonymous_1) +DA:14,1 +DA:15,1 +DA:16,1 +DA:18,1 +DA:19,5 +DA:21,5 +DA:24,5 +DA:27,5 +DA:47,5 +DA:49,4 +DA:51,4 +DA:52,4 +DA:55,4 +DA:57,3 +DA:58,3 +DA:59,0 +DA:64,3 +DA:65,12 +DA:66,12 +DA:70,3 +DA:72,3 +DA:75,1 +DA:76,1 +DA:79,0 +DA:83,4 +DA:85,1 +DA:86,1 +DA:89,4 +LF:28 +LH:26 +BRDA:47,0,0,4 +BRDA:47,0,1,1 +BRDA:47,0,2,0 +BRDA:58,1,0,0 +BRDA:58,1,1,3 +BRF:5 +BRH:3 +end_of_record +TN: +SF:input-validate/lib/error.js +FN:16,(anonymous_0) +FNF:1 +FNH:1 +FNDA:1,(anonymous_0) +DA:14,1 +DA:16,1 +DA:17,1 +DA:21,1 +DA:22,1 +DA:29,1 +DA:34,1 +DA:36,0 +DA:37,0 +DA:40,1 +DA:43,1 +LF:11 +LH:9 +BRF:0 +BRH:0 +end_of_record diff --git a/source/test/coverage-reports/jest/media-package-assets/lcov.info b/source/test/coverage-reports/jest/media-package-assets/lcov.info new file mode 100644 index 00000000..a6af1fc9 --- /dev/null +++ b/source/test/coverage-reports/jest/media-package-assets/lcov.info @@ -0,0 +1,74 @@ +TN: +SF:media-package-assets/index.js +FN:18,(anonymous_0) +FN:35,(anonymous_1) +FN:39,(anonymous_2) +FN:49,(anonymous_3) +FNF:4 +FNH:4 +FNDA:5,(anonymous_0) +FNDA:1,(anonymous_1) +FNDA:2,(anonymous_2) +FNDA:2,(anonymous_3) +DA:14,1 +DA:15,1 +DA:16,1 +DA:18,1 +DA:19,5 +DA:21,5 +DA:22,1 +DA:25,4 +DA:26,4 +DA:35,1 +DA:36,1 +DA:37,1 +DA:39,1 +DA:40,2 +DA:41,2 +DA:43,2 +DA:46,1 +DA:49,1 +DA:50,2 +DA:52,2 +DA:53,2 +DA:55,2 +DA:56,2 +DA:63,2 +DA:65,2 +DA:66,2 +DA:67,1 +DA:68,1 +DA:69,1 +DA:71,1 +DA:72,1 +DA:75,1 +DA:78,1 +LF:33 +LH:33 +BRDA:21,0,0,1 +BRDA:21,0,1,4 +BRF:2 +BRH:2 +end_of_record +TN: +SF:media-package-assets/lib/error.js +FN:16,(anonymous_0) +FNF:1 +FNH:1 +FNDA:1,(anonymous_0) +DA:14,1 +DA:16,1 +DA:17,1 +DA:21,1 +DA:22,1 +DA:29,1 +DA:34,1 +DA:36,0 +DA:37,0 +DA:40,1 +DA:43,1 +LF:11 +LH:9 +BRF:0 +BRH:0 +end_of_record diff --git a/source/test/coverage-reports/jest/output-validate/lcov.info b/source/test/coverage-reports/jest/output-validate/lcov.info new file mode 100644 index 00000000..0f09acfd --- /dev/null +++ b/source/test/coverage-reports/jest/output-validate/lcov.info @@ -0,0 +1,125 @@ +TN: +SF:output-validate/index.js +FN:18,(anonymous_0) +FN:20,(anonymous_1) +FN:49,(anonymous_2) +FN:68,(anonymous_3) +FNF:4 +FNH:4 +FNDA:6,(anonymous_0) +FNDA:5,(anonymous_1) +FNDA:5,(anonymous_2) +FNDA:1,(anonymous_3) +DA:14,1 +DA:15,1 +DA:16,1 +DA:18,6 +DA:20,1 +DA:21,5 +DA:23,5 +DA:28,5 +DA:30,5 +DA:32,5 +DA:34,5 +DA:41,5 +DA:42,4 +DA:44,4 +DA:45,4 +DA:46,4 +DA:49,4 +DA:50,5 +DA:52,5 +DA:54,1 +DA:55,1 +DA:57,1 +DA:60,1 +DA:61,1 +DA:63,1 +DA:66,1 +DA:67,1 +DA:68,1 +DA:70,1 +DA:71,1 +DA:72,1 +DA:76,1 +DA:77,1 +DA:78,1 +DA:81,1 +DA:84,1 +DA:85,1 +DA:87,1 +DA:90,1 +DA:91,1 +DA:93,1 +DA:94,1 +DA:96,1 +DA:99,0 +DA:107,4 +DA:109,0 +DA:110,0 +DA:112,0 +DA:117,0 +DA:119,0 +DA:120,0 +DA:121,0 +DA:122,0 +DA:124,0 +DA:130,1 +DA:131,1 +DA:133,4 +LF:57 +LH:47 +BRDA:52,0,0,1 +BRDA:52,0,1,1 +BRDA:52,0,2,1 +BRDA:52,0,3,1 +BRDA:52,0,4,1 +BRDA:52,0,5,0 +BRDA:70,1,0,1 +BRDA:70,1,1,0 +BRDA:76,2,0,1 +BRDA:76,2,1,0 +BRDA:76,3,0,1 +BRDA:76,3,1,1 +BRDA:107,4,0,0 +BRDA:107,4,1,4 +BRDA:119,5,0,0 +BRDA:119,5,1,0 +BRF:16 +BRH:10 +end_of_record +TN: +SF:output-validate/lib/error.js +FN:16,(anonymous_0) +FNF:1 +FNH:1 +FNDA:1,(anonymous_0) +DA:14,1 +DA:16,1 +DA:17,1 +DA:21,1 +DA:22,1 +DA:29,1 +DA:34,1 +DA:36,0 +DA:37,0 +DA:40,1 +DA:43,1 +LF:11 +LH:9 +BRF:0 +BRH:0 +end_of_record +TN: +SF:output-validate/lib/test-events.js +FNF:0 +FNH:0 +DA:1,1 +DA:32,1 +DA:58,1 +DA:97,1 +LF:4 +LH:4 +BRF:0 +BRH:0 +end_of_record diff --git a/source/test/coverage-reports/jest/profiler/lcov.info b/source/test/coverage-reports/jest/profiler/lcov.info new file mode 100644 index 00000000..65edc08d --- /dev/null +++ b/source/test/coverage-reports/jest/profiler/lcov.info @@ -0,0 +1,78 @@ +TN: +SF:profiler/index.js +FN:17,(anonymous_0) +FN:36,(anonymous_1) +FN:49,(anonymous_2) +FNF:3 +FNH:3 +FNDA:3,(anonymous_0) +FNDA:12,(anonymous_1) +FNDA:6,(anonymous_2) +DA:14,1 +DA:15,1 +DA:17,1 +DA:18,3 +DA:20,3 +DA:25,3 +DA:27,3 +DA:34,3 +DA:36,2 +DA:37,12 +DA:40,2 +DA:41,2 +DA:42,2 +DA:45,2 +DA:49,2 +DA:50,6 +DA:51,6 +DA:52,0 +DA:55,6 +DA:56,6 +DA:59,2 +DA:61,2 +DA:63,2 +DA:69,2 +DA:70,2 +DA:75,2 +DA:77,1 +DA:83,1 +DA:84,1 +DA:86,1 +DA:88,1 +DA:91,1 +DA:92,1 +DA:95,2 +DA:96,2 +LF:35 +LH:34 +BRDA:51,0,0,0 +BRDA:51,0,1,6 +BRDA:61,1,0,2 +BRDA:61,1,1,0 +BRDA:75,2,0,1 +BRDA:75,2,1,1 +BRF:6 +BRH:4 +end_of_record +TN: +SF:profiler/lib/error.js +FN:16,(anonymous_0) +FNF:1 +FNH:1 +FNDA:1,(anonymous_0) +DA:14,1 +DA:16,1 +DA:17,1 +DA:21,1 +DA:22,1 +DA:29,1 +DA:34,1 +DA:36,0 +DA:37,0 +DA:40,1 +DA:43,1 +LF:11 +LH:9 +BRF:0 +BRH:0 +end_of_record diff --git a/source/test/coverage-reports/jest/sns-notification/lcov.info b/source/test/coverage-reports/jest/sns-notification/lcov.info new file mode 100644 index 00000000..89d6e852 --- /dev/null +++ b/source/test/coverage-reports/jest/sns-notification/lcov.info @@ -0,0 +1,69 @@ +TN: +SF:sns-notification/index.js +FN:32,(anonymous_0) +FN:59,(anonymous_1) +FNF:2 +FNH:2 +FNDA:5,(anonymous_0) +FNDA:12,(anonymous_1) +DA:14,1 +DA:15,1 +DA:17,1 +DA:32,1 +DA:33,5 +DA:35,5 +DA:40,5 +DA:42,5 +DA:43,5 +DA:45,5 +DA:46,2 +DA:47,2 +DA:48,2 +DA:49,2 +DA:50,2 +DA:51,2 +DA:52,2 +DA:54,2 +DA:59,12 +DA:61,3 +DA:62,2 +DA:68,1 +DA:71,4 +DA:73,4 +DA:79,4 +DA:81,2 +DA:82,2 +DA:85,3 +LF:28 +LH:28 +BRDA:45,0,0,2 +BRDA:45,0,1,3 +BRDA:54,1,0,1 +BRDA:54,1,1,1 +BRDA:61,2,0,2 +BRDA:61,2,1,1 +BRF:6 +BRH:6 +end_of_record +TN: +SF:sns-notification/lib/error.js +FN:16,(anonymous_0) +FNF:1 +FNH:1 +FNDA:2,(anonymous_0) +DA:14,1 +DA:16,1 +DA:17,2 +DA:21,2 +DA:22,2 +DA:29,2 +DA:34,2 +DA:36,0 +DA:37,0 +DA:40,2 +DA:43,1 +LF:11 +LH:9 +BRF:0 +BRH:0 +end_of_record diff --git a/source/test/coverage-reports/jest/sqs-publish/lcov.info b/source/test/coverage-reports/jest/sqs-publish/lcov.info new file mode 100644 index 00000000..d97c96d7 --- /dev/null +++ b/source/test/coverage-reports/jest/sqs-publish/lcov.info @@ -0,0 +1,45 @@ +TN: +SF:sqs-publish/index.js +FN:18,(anonymous_0) +FNF:1 +FNH:1 +FNDA:2,(anonymous_0) +DA:14,1 +DA:15,1 +DA:18,1 +DA:19,2 +DA:21,2 +DA:26,2 +DA:28,2 +DA:30,2 +DA:35,2 +DA:38,1 +DA:39,1 +DA:42,1 +LF:12 +LH:12 +BRF:0 +BRH:0 +end_of_record +TN: +SF:sqs-publish/lib/error.js +FN:16,(anonymous_0) +FNF:1 +FNH:1 +FNDA:1,(anonymous_0) +DA:14,1 +DA:16,1 +DA:17,1 +DA:21,1 +DA:22,1 +DA:29,1 +DA:34,1 +DA:36,0 +DA:37,0 +DA:40,1 +DA:43,1 +LF:11 +LH:9 +BRF:0 +BRH:0 +end_of_record diff --git a/source/test/coverage-reports/jest/step-functions/lcov.info b/source/test/coverage-reports/jest/step-functions/lcov.info new file mode 100644 index 00000000..2f73e9d4 --- /dev/null +++ b/source/test/coverage-reports/jest/step-functions/lcov.info @@ -0,0 +1,67 @@ +TN: +SF:step-functions/index.js +FN:18,(anonymous_0) +FNF:1 +FNH:1 +FNDA:5,(anonymous_0) +DA:14,1 +DA:15,1 +DA:16,1 +DA:18,1 +DA:19,5 +DA:21,5 +DA:29,5 +DA:30,5 +DA:33,2 +DA:36,2 +DA:37,2 +DA:38,0 +DA:40,2 +DA:42,2 +DA:47,2 +DA:48,2 +DA:52,1 +DA:59,1 +DA:60,1 +DA:64,1 +DA:69,1 +DA:70,1 +DA:73,1 +DA:76,4 +DA:77,3 +DA:79,2 +DA:80,2 +DA:83,3 +LF:28 +LH:27 +BRDA:30,0,0,2 +BRDA:30,0,1,1 +BRDA:30,0,2,1 +BRDA:30,0,3,1 +BRDA:37,1,0,0 +BRDA:37,1,1,2 +BRF:6 +BRH:5 +end_of_record +TN: +SF:step-functions/lib/error.js +FN:16,(anonymous_0) +FNF:1 +FNH:1 +FNDA:2,(anonymous_0) +DA:14,1 +DA:16,1 +DA:17,2 +DA:21,2 +DA:22,2 +DA:29,2 +DA:34,2 +DA:36,0 +DA:37,0 +DA:40,2 +DA:43,1 +LF:11 +LH:9 +BRF:0 +BRH:0 +end_of_record diff --git a/source/test/coverage-reports/pytest/mediainfo/coverage.xml b/source/test/coverage-reports/pytest/mediainfo/coverage.xml new file mode 100644 index 00000000..d1e507e5 --- /dev/null +++ b/source/test/coverage-reports/pytest/mediainfo/coverage.xml @@ -0,0 +1,115 @@ + + + + + + . + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +