AWSTemplateFormatVersion: 2010-09-09 Description: Copy S3 object to local S3 bucket Parameters: S3BucketSources: Type: String Description: S3 bucket with source files MaxLength: 63 MinLength: 3 Default: aws-bigdata-blog AllowedValues: ["aws-bigdata-blog"] S3SourcesPrefix: Type: String Description: S3 prefix with sources WITH ending slash MaxLength: 63 MinLength: 3 Default: artifacts/aws-blog-emr-ranger AllowedValues: ["artifacts/aws-blog-emr-ranger"] ProjectVersion: Default: 3.0 Description: Project version Type: String AllowedValues: - 3.0 - beta S3Objects: Type: CommaDelimitedList Description: S3 Object to be copied Default: launch-cluster.zip, scripts/download-scripts.sh, scripts/remove-yum-package-name-validator.sh, scripts/configure_ranger_glue_support_with_bootstrap.sh, scripts/enable-glue-catalog-support.sh, scripts/create-hdfs-home-ba.sh, scripts/setup-trino-redshift-connector.sh Resources: S3BucketRegionSources: Type: AWS::S3::Bucket Properties: BucketEncryption: ServerSideEncryptionConfiguration: - ServerSideEncryptionByDefault: SSEAlgorithm: AES256 DeletionPolicy: Delete CopyZips: Type: AWS::CloudFormation::CustomResource DependsOn: - S3BucketRegionSources Properties: ServiceToken: !GetAtt 'CopyZipsFunction.Arn' DestBucket: !Ref 'S3BucketRegionSources' SourceBucket: !Ref 'S3BucketSources' SourcePrefix: !Ref 'S3SourcesPrefix' ProjectVersion: !Ref 'ProjectVersion' Counter: "1" Objects: !Ref S3Objects CopyZipsRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: lambda.amazonaws.com Action: sts:AssumeRole ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole Path: / Policies: - PolicyName: lambda-copier PolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Action: - s3:GetObject - s3:GetObjectTagging Resource: - !Sub 'arn:aws:s3:::${S3BucketSources}/*' - Effect: Allow Action: - s3:ListBucket Resource: - !Sub 'arn:aws:s3:::${S3BucketSources}' - Effect: Allow Action: - s3:ListBucket Resource: - !Sub 'arn:aws:s3:::${S3BucketRegionSources}' - Effect: Allow Action: - s3:PutObject - s3:DeleteObject - s3:PutObjectTagging Resource: - !Sub 'arn:aws:s3:::${S3BucketRegionSources}/*' CopyZipsFunction: Type: AWS::Lambda::Function Properties: FunctionName: "CopyRangerArtifacts" Description: Copies objects from a source S3 bucket to a destination Handler: index.handler Runtime: python3.9 Role: !GetAtt 'CopyZipsRole.Arn' Timeout: 240 Code: ZipFile: | import json import logging import threading import boto3 import cfnresponse def copy_objects(source_bucket, dest_bucket, objects, prefix, project_version): s3 = boto3.client('s3') for o in objects: key = prefix + '/' + project_version + '/' + o copy_source = { 'Bucket': source_bucket, 'Key': key } print('copy source_bucket:' + source_bucket + ' destination_bucket: '+ dest_bucket + ' key: ' + key) s3.copy_object(CopySource=copy_source, Bucket=dest_bucket, Key=key) def delete_objects(bucket, objects, prefix, project_version): s3 = boto3.client('s3') objects = {'Objects': [{'Key': prefix + '/' + project_version + '/' + o} for o in objects]} s3.delete_objects(Bucket=bucket, Delete=objects) s3_list_response = s3.list_objects_v2(Bucket=bucket, Prefix=prefix + '/' + project_version + '/emr-tls/') if 'Contents' in s3_list_response: for object in s3_list_response['Contents']: print('Deleting', object['Key']) s3.delete_object(Bucket=bucket, Key=object['Key']) def timeout(event, context): logging.error('Execution is about to time out, sending failure response to CloudFormation') cfnresponse.send(event, context, cfnresponse.FAILED, {}, None) def handler(event, context): # make sure we send a failure to CloudFormation if the function is going to timeout timer = threading.Timer((context.get_remaining_time_in_millis() / 1000.00) - 0.5, timeout, args=[event, context]) timer.start() print('Received event: %s' % json.dumps(event)) status = cfnresponse.SUCCESS try: source_bucket = event['ResourceProperties']['SourceBucket'] source_prefix = event['ResourceProperties']['SourcePrefix'] project_version = event['ResourceProperties']['ProjectVersion'] dest_bucket = event['ResourceProperties']['DestBucket'] if source_bucket == dest_bucket: return objects = event['ResourceProperties']['Objects'] if event['RequestType'] == 'Delete': delete_objects(dest_bucket, objects, source_prefix, project_version) else: copy_objects(source_bucket, dest_bucket, objects, source_prefix, project_version) except Exception as e: logging.error('Exception: %s' % e, exc_info=True) status = cfnresponse.FAILED finally: timer.cancel() cfnresponse.send(event, context, status, {}, None) Outputs: RegionalS3Bucket: Description: Regional S3 bucket with artifacts required by the EMR cluster. This bucket can be reused as the 'S3Bucket' value for future EMR cluster stacks Value: !Ref S3BucketRegionSources