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-1s3afc2u3)
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 QuickStart 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, uppercaseletters, hyphens (-), and forward slash (/).
    Default: quickstart-cisco-meraki-sd-wan-vmx/
    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:
        Fn::GetAtt:
        - CopyObjectsFunction
        - Arn
      DestBucket:
        Ref: LambdaZipsBucket
      Objects:
      - functions/packages/lambda.zip
      SourceBucket:
        Ref: QSS3BucketName
      Prefix:
        Ref: QSS3KeyPrefix
    Type: AWS::CloudFormation::CustomResource
  CopyObjectsFunction:
    Properties:
      Code:
        ZipFile:
          Fn::Join:
          - "\n"
          - - 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:
        Fn::GetAtt:
        - CopyObjectsRole
        - Arn
      Runtime: python3.7
      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::Partition}:policy/service-role/AWSLambdaBasicExecutionRole
      Path: "/"
      Policies:
      - PolicyDocument:
          Statement:
          - Action:
            - s3:GetObject
            Effect: Allow
            Resource:
            - Fn::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:
            - Fn::Sub: arn:${AWS::Partition}:s3:::${LambdaZipsBucket}/${QSS3KeyPrefix}*
            - Fn::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:
        Fn::GetAtt:
        - CleanUpS3BucketFunction
        - Arn
    Type: AWS::CloudFormation::CustomResource
  CleanUpS3BucketFunction:
    Properties:
      Code:
        ZipFile:
          Fn::Join:
          - "\n"
          - - 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:
        Fn::GetAtt:
        - S3CleanUpRole
        - Arn
      Runtime: python3.7
      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::Partition}: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:
            - Fn::GetAtt:
              - LambdaZipsBucket
              - Arn
            - Fn::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