AWSTemplateFormatVersion: '2010-09-09' Description: >- This template creates an S3 bucket in the same region where the stack is launched and copy the Lambda functions code from original bucket to the new bucket. (qs-1nae6brn2) Metadata: 'AWS::CloudFormation::Interface': ParameterGroups: - Label: {default: "AWS Quick Start configuration"} Parameters: - QSS3BucketName - QSS3KeyPrefix ParameterLabels: QSS3BucketName: default: Quick Start S3 bucket name QSS3KeyPrefix: default: Quick Start S3 key prefix Parameters: QSS3BucketName: AllowedPattern: ^[0-9a-zA-Z]+([0-9a-zA-Z-]*[0-9a-zA-Z])*$ ConstraintDescription: >- S3 bucket name can include numbers, lowercase letters, uppercase letters, and hyphens (-). It cannot start or end with a hyphen (-). Default: aws-quickstart Description: >- Alphanumeric string which identifies the S3 bucket name for the Quick Start assets. It's the bucket to store the copy of the Quick Start assets if you decided to customize or extend them for your own use. Type: String QSS3KeyPrefix: AllowedPattern: ^[0-9a-zA-Z-/._]*$ ConstraintDescription: >- S3 key prefix can include numbers, lowercase letters, uppercase letters, hyphens (-), and forward slash (/). Default: quickstart-codepipeline-bluegreen-deployment/ Description: >- Alphanumeric string which identifies the S3 key prefix used to simulate a folder for your copy of the Quick Start assets if you decided to customize or extend them for your own use. Type: String Resources: CopyObjects: Properties: ServiceToken: !GetAtt CopyObjectsFunction.Arn DestBucket: Ref: LambdaZipsBucket Objects: - functions/packages/ACMCert/lambda.zip SourceBucket: !Ref QSS3BucketName Prefix: !Ref QSS3KeyPrefix Type: AWS::CloudFormation::CustomResource CopyObjectsFunction: Properties: Code: ZipFile: | import json import logging import threading import boto3 import cfnresponse def copy_objects(source_bucket, dest_bucket, prefix, objects): s3 = boto3.client('s3') for o in objects: key = prefix + o copy_source = { 'Bucket': source_bucket, 'Key': key } s3.copy_object(CopySource=copy_source, Bucket=dest_bucket, Key=key) def delete_objects(bucket): client = boto3.client('s3') print("Collecting data from" + bucket) paginator = client.get_paginator('list_object_versions') result = paginator.paginate(Bucket=bucket) objects = [] for page in result: try: for k in page['Versions']: objects.append({'Key':k['Key'],'VersionId': k['VersionId']}) try: for k in page['DeleteMarkers']: version = k['VersionId'] key = k['Key'] objects.append({'Key': key,'VersionId': version}) except: pass print("deleting objects") client.delete_objects(Bucket=bucket, Delete={'Objects': objects}) # objects = [] except: pass print("bucket already empty") 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'] dest_bucket = event['ResourceProperties']['DestBucket'] prefix = event['ResourceProperties']['Prefix'] objects = event['ResourceProperties']['Objects'] if event['RequestType'] == 'Delete': delete_objects(dest_bucket) else: copy_objects(source_bucket, dest_bucket, prefix, objects) except Exception as e: logging.error('Exception: %s' % e, exc_info=True) status = cfnresponse.FAILED finally: timer.cancel() cfnresponse.send(event, context, status, {}, None) Description: Copies objects from a source S3 bucket to a destination S3 bucket Handler: index.handler Role: !GetAtt CopyObjectsRole.Arn Runtime: python3.8 Timeout: 240 Type: AWS::Lambda::Function CopyObjectsRole: Properties: AssumeRolePolicyDocument: Statement: - Action: sts:AssumeRole Effect: Allow Principal: Service: lambda.amazonaws.com Version: '2012-10-17' ManagedPolicyArns: - !Sub arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole Path: / Policies: - PolicyDocument: Statement: - Action: - s3:GetObject Effect: Allow Resource: - !Sub arn:${AWS::Partition}:s3:::${QSS3BucketName}/${QSS3KeyPrefix}* - Action: - s3:PutObject - s3:DeleteObject - s3:GetObject - s3:ListBucket - s3:ListBucketVersions - s3:DeleteObjectVersion - s3:GetObjectVersion - s3:GetBucketVersioning Effect: Allow Resource: - !Sub arn:${AWS::Partition}:s3:::${LambdaZipsBucket}/${QSS3KeyPrefix}* - !Sub arn:${AWS::Partition}:s3:::${LambdaZipsBucket} Version: '2012-10-17' PolicyName: object-copier Type: AWS::IAM::Role LambdaZipsBucket: Properties: Tags: [] VersioningConfiguration: Status: Enabled Type: AWS::S3::Bucket CleanUpS3Bucket: Properties: DestBucket: Ref: LambdaZipsBucket ServiceToken: !GetAtt CleanUpS3BucketFunction.Arn Type: AWS::CloudFormation::CustomResource CleanUpS3BucketFunction: Properties: Code: ZipFile: | import json import logging import threading import boto3 import cfnresponse client = boto3.client('s3') def delete_NonVersionedobjects(bucket): print("Collecting data from" + bucket) paginator = client.get_paginator('list_objects_v2') result = paginator.paginate(Bucket=bucket) objects = [] for page in result: try: for k in page['Contents']: objects.append({'Key': k['Key']}) print("deleting objects") client.delete_objects(Bucket=bucket, Delete={'Objects': objects}) objects = [] except: pass print("bucket is already empty") def delete_versionedobjects(bucket): print("Collecting data from" + bucket) paginator = client.get_paginator('list_object_versions') result = paginator.paginate(Bucket=bucket) objects = [] for page in result: try: for k in page['Versions']: objects.append({'Key':k['Key'],'VersionId': k['VersionId']}) try: for k in page['DeleteMarkers']: version = k['VersionId'] key = k['Key'] objects.append({'Key': key,'VersionId': version}) except: pass print("deleting objects") client.delete_objects(Bucket=bucket, Delete={'Objects': objects}) # objects = [] except: pass print("bucket already empty") 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: dest_bucket = event['ResourceProperties']['DestBucket'] if event['RequestType'] == 'Delete': CheckifVersioned = client.get_bucket_versioning(Bucket=dest_bucket) print (CheckifVersioned) if 'Status' in CheckifVersioned: print (CheckifVersioned['Status']) print ("This is a versioned Bucket") delete_versionedobjects(dest_bucket) else: print ("This is not a versioned bucket") delete_NonVersionedobjects(dest_bucket) else: print("Nothing to do") except Exception as e: logging.error('Exception: %s' % e, exc_info=True) status = cfnresponse.FAILED finally: timer.cancel() cfnresponse.send(event, context, status, {}, None) Description: Empty the S3 Bucket Handler: index.handler Role: !GetAtt S3CleanUpRole.Arn Runtime: python3.8 Timeout: 240 Type: AWS::Lambda::Function S3CleanUpRole: Properties: AssumeRolePolicyDocument: Statement: - Action: sts:AssumeRole Effect: Allow Principal: Service: lambda.amazonaws.com Version: '2012-10-17' ManagedPolicyArns: - !Sub arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole Path: / Policies: - PolicyDocument: Statement: - Action: - s3:PutObject - s3:DeleteObject - s3:GetObject - s3:ListBucket - s3:ListBucketVersions - s3:DeleteObjectVersion - s3:GetObjectVersion - s3:GetBucketVersioning Effect: Allow Resource: - !GetAtt LambdaZipsBucket.Arn - !Sub arn:${AWS::Partition}:s3:::* Version: '2012-10-17' PolicyName: Empty-bucket Type: AWS::IAM::Role Outputs: LambdaZipsBucket: Description: S3 Bucket for the Lambda Function Code Value: Ref: LambdaZipsBucket