Skip to content

Instantly share code, notes, and snippets.

@mcfantom-3xm
Created June 8, 2020 13:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mcfantom-3xm/d72b6f63059218cb930ccaaa87736d47 to your computer and use it in GitHub Desktop.
Save mcfantom-3xm/d72b6f63059218cb930ccaaa87736d47 to your computer and use it in GitHub Desktop.
CloudFormation Custom Resource
AWSTemplateFormatVersion: 2010-09-09
Description: Rekognition Video Blog
Parameters:
ApplicationName:
Description: Name of the application deploying for the Social Media Analytics
Type: String
Default: RekognitionVideoBlog
EmailAddress:
Description: Email address used for SNS notification whenever people are found in video
Type: String
S3BucketName:
Description: Name for bucket
Type: String
AllowedPattern: (?=^.{3,63}$)(?!^(\d+\.)+\d+$)(^(([a-z0-9]|[a-z0-9][a-z0-9\-]*[a-z0-9])\.)*([a-z0-9]|[a-z0-9][a-z0-9\-]*[a-z0-9])$)
ConstraintDescription: S3 bucket name must be between 3 and 63 characters, be only lower-case, may contain numbers, periods, and dashes. Must start with a lowercase letter or number. Cannot contain underscore, end with a dash, contain consecutive periods, or dashes adyacent to periods. Cannot be formatted as an IP address.
Resources:
EventMetadataStream:
Type: 'AWS::Kinesis::Stream'
Properties:
Name: !Sub '${ApplicationName}-Stream'
ShardCount: 1
SNSTopic:
Type: 'AWS::SNS::Topic'
Properties:
DisplayName: !Sub '${ApplicationName} SNS Topic'
Subscription:
- Endpoint: !Sub '${EmailAddress}'
Protocol: email
SNSPublishRole:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: 'sts:AssumeRole'
Path: /
Policies:
- PolicyName: lambda-comprehend-policy
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- 'sns:Publish'
Resource: !Ref SNSTopic
- Effect: Allow
Action:
- 'kinesis:GetRecords'
- 'kinesis:GetShardIterator'
- 'kinesis:DescribeStream'
- 'kinesis:ListStreams'
Resource: !GetAtt EventMetadataStream.Arn
- Effect: Allow
Action:
- 'kinesis:ListStreams'
Resource: '*'
RekognitionVideoIAM:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service: rekognition.amazonaws.com
Action: 'sts:AssumeRole'
- Effect: Allow
Principal:
Service: lambda.amazonaws.com
Action: 'sts:AssumeRole'
Path: /
Policies:
- PolicyName: lambda-comprehend-policy
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- 'kinesis:*'
Resource: !GetAtt EventMetadataStream.Arn
- Effect: Allow
Action:
- 'kinesisvideo:*'
Resource: '*'
- Effect: Allow
Action:
- 'rekognition:*'
Resource: '*'
- Effect: Allow
Action:
- 'logs:*'
Resource: '*'
- Effect: Allow
Action:
- 'lambda:TagResource'
- 'lambda:ListTags'
Resource: '*'
- Effect: Allow
Action:
- 's3:GetAccessPoint'
- 's3:PutAccountPublicAccessBlock'
- 's3:ListAccessPoints'
- 's3:ListJobs'
- 's3:GetAccountPublicAccessBlock'
- 's3:ListAllMyBuckets'
- 's3:CreateJob'
- 's3:HeadBucket'
Resource: '*'
- Effect: Allow
Action:
- 's3:*'
Resource: '*'
RekognitionVideoLambda:
Type: 'AWS::Lambda::Function'
Properties:
Environment:
Variables:
sns: !Ref SNSTopic
Handler: index.handler
Role: !GetAtt SNSPublishRole.Arn
Code:
S3Bucket: us-west-2.serverless-analytics
S3Key: KinesisVideoRekognitionVideo-blog/Lambda.zip
Runtime: nodejs10.x
Timeout: '120'
RekognitionLambdaKinesisMapping:
Type: 'AWS::Lambda::EventSourceMapping'
Properties:
BatchSize: 10
Enabled: true
EventSourceArn: !GetAtt EventMetadataStream.Arn
FunctionName: !GetAtt RekognitionVideoLambda.Arn
StartingPosition: TRIM_HORIZON
LambdaExtraResourcesCfg:
Type: 'AWS::Lambda::Function'
Properties:
Code:
ZipFile: !Sub |
import os
import json
import logging
import signal
import boto3
from urllib2 import build_opener, HTTPHandler, Request
LOGGER = logging.getLogger()
LOGGER.setLevel(logging.INFO)
def handler(event, context):
signal.alarm((context.get_remaining_time_in_millis() / 1000) - 1)
kinesisVideoStream = boto3.client('kinesisvideo')
rekognitionCollection = boto3.client('rekognition')
currentLambda = boto3.client('lambda')
try:
varStackName = str(os.environ['StackName'])
LOGGER.info('LAMBDA ARN: ' + str(context.invoked_function_arn))
LOGGER.info('STACK NAME: ' + varStackName)
if event['RequestType'] == 'Create':
LOGGER.info('CREATED!')
response = kinesisVideoStream.create_stream(DeviceName=varStackName+'KinesisVideoStream',
StreamName=varStackName+'KinesisVideoStream', MediaType='video/h264', DataRetentionInHours=1,
Tags={'OwnedByStack': varStackName})
stream_ARN = response['StreamARN']
LOGGER.info('Kinesis Video Stream ARN: ' + str(stream_ARN))
response = rekognitionCollection.create_collection(CollectionId=varStackName+'RekognitionCollection')
collection_ARN = response['CollectionArn']
LOGGER.info('Rekognition Collection ARN: ' + str(collection_ARN))
currentLambda.tag_resource(Resource=context.invoked_function_arn, Tags={'KinesisVideoStreamARN':stream_ARN, 'RekognitionCollectionARN':collection_ARN, 'RekognitionCollectionID':varStackName+'RekognitionCollection'})
LOGGER.info('Lambda tagged succesfully.')
send_response(event, context, "SUCCESS", {"Message": "Resource creation successful!"})
elif event['RequestType'] == 'Update':
LOGGER.info('UPDATE!')
send_response(event, context, "SUCCESS", {"Message": "Resource update successful!"})
elif event['RequestType'] == 'Delete':
LOGGER.info('DELETE!')
lambdaTagsListResponse = currentLambda.list_tags(Resource=context.invoked_function_arn)
response = rekognitionCollection.delete_collection(CollectionId=lambdaTagsListResponse['Tags'].get('RekognitionCollectionID'))
LOGGER.info('Delete Rekognition Collection ID: ' + lambdaTagsListResponse['Tags'].get('RekognitionCollectionID') + ' Message: ' + str(response['StatusCode']))
response = kinesisVideoStream.delete_stream(StreamARN=lambdaTagsListResponse['Tags'].get('KinesisVideoStreamARN'))
LOGGER.info('Delete Kinesis Video Stream ARN: ' + lambdaTagsListResponse['Tags'].get('KinesisVideoStreamARN') + ' Response: ' + str(response))
send_response(event, context, "SUCCESS", {"Message": "Resource deletion successful!"})
else:
LOGGER.info('FAILED!')
send_response(event, context, "FAILED", {"Message": "Unexpected event received from CF"})
except Exception as error:
LOGGER.exception(error)
LOGGER.info('FAILED!')
send_response(event, context, "FAILED", {"Message": "Exception during lambda execution"})
finally:
send_response(event, context, "SUCCESS", {"Message": "See previous logs to see the result of lambda execution."})
def send_response(event, context, response_status, response_data):
response_body = json.dumps({"Status": response_status,"Reason": "See the details in CloudWatch Log Stream: " + context.log_stream_name,
"PhysicalResourceId": context.log_stream_name,"StackId": event['StackId'],"RequestId": event['RequestId'],"LogicalResourceId": event['LogicalResourceId'],
"Data": response_data})
LOGGER.info('ResponseURL: ' + str(event['ResponseURL']) + ' - ResponseBody: ' + str(response_body))
opener = build_opener(HTTPHandler)
request = Request(event['ResponseURL'], data=response_body)
request.add_header('Content-Type', '')
request.add_header('Content-Length', len(response_body))
request.get_method = lambda: 'PUT'
response = opener.open(request)
LOGGER.info('Status code: ' + str(response.getcode()) + ' Status message: ' + str(response.msg))
def timeout_handler(_signal, _frame):
raise Exception('Time exceeded')
signal.signal(signal.SIGALRM, timeout_handler)
Environment:
Variables:
StackName: !Ref 'AWS::StackName'
Description: Lambda function to create some extra resources (Kinesis Video Stream and Rekognition Collection)
FunctionName: lambda_function
Handler: index.handler
Role: !GetAtt RekognitionVideoIAM.Arn
Runtime: python2.7
Timeout: '15'
Tags:
- Key: OwnedByStack
Value: !Ref 'AWS::StackName'
LambdaExtraResourcesCfgCaller:
Type: Custom::RekognitionVideoBlogLambdaInlineCaller
Properties:
ServiceToken: !GetAtt LambdaExtraResourcesCfg.Arn
S3BucketRekognitionCollection:
Type: AWS::S3::Bucket
Properties:
BucketName: !Ref S3BucketName
Outputs:
KinesisDataStreamArn:
Description: Kinesis Data Stream Arn (used in Stream Processer Input)
Value: !GetAtt EventMetadataStream.Arn
RekognitionVideoIAM:
Description: Rekognition Video Processing IAM Arn (used in Stream Processer Input)
Value: !GetAtt RekognitionVideoIAM.Arn
S3BucketRekognitionCollection:
Description: "Name of S3 bucket"
Value: !Join
- ''
- - 'https://'
- !GetAtt
- S3BucketRekognitionCollection
- DomainName
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment