AWSTemplateFormatVersion: '2010-09-09'
Description: Schedule automatic deletion of CloudFormation stacks
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
      - Label:
          default: Input configuration
        Parameters:
          - StackName
          - TTL
    ParameterLabels:
      StackName:
        default: Stack name
      TTL:
        default: Time-to-live
Parameters:
  EnvironmentName:
    Type: String
    Default: development
    Description: 'Your deployment environment: DEV, QA , PROD'
  BUILDID:
    Type: String
    Default: ''
  StackName:
    Type: String
    Description: Stack name that will be deleted.
  DeleteStackName:
    Type: String
    Description: Stack name that will be deleted.
  TTL:
    Type: Number
    Description: Time-to-live in minutes for the stack.
Resources:
  DeleteCFNLambda:
    Type: "AWS::Lambda::Function"
    Properties:
      FunctionName: !Join [ "", [ 'DeleteCFNLambda', !Ref BUILDID ] ]
      Code:
        ZipFile: |
          import boto3
          import os
          import json
          
          stack_name = os.environ['stackName']
          delete_stack_name = os.environ['deleteStackName']
          
          def delete_cfn(stack_name):
              try:
                  cfn = boto3.resource('cloudformation')
                  stack = cfn.Stack(stack_name)
                  stack.delete()
                  return "SUCCESS"
              except:
                  return "ERROR" 
          
          def handler(event, context):
              print("Received event:")
              print(json.dumps(event))
              result = delete_cfn(stack_name)
              delete_cfn(delete_stack_name)
              return result
      Environment:
        Variables:
          stackName: !Ref 'StackName'
          deleteStackName: !Ref 'DeleteStackName'
      Handler: "index.handler"
      Runtime: "python3.6"
      Timeout: "5"
      Role: 
       'Fn::ImportValue': !Sub '${EnvironmentName}:DeleteCFNLambdaExecutionRole'
  DeleteStackEventRule:
     DependsOn:
       - DeleteCFNLambda
       - GenerateCronExpression
     Type: "AWS::Events::Rule"
     Properties:
       Name: !Join [ "", [ 'DeleteStackEventRule', !Ref BUILDID ] ]
       Description: Delete stack event
       ScheduleExpression: !GetAtt GenerateCronExpression.cron_exp
       State: "ENABLED"
       Targets: 
          - 
            Arn: !GetAtt DeleteCFNLambda.Arn
            Id: 'DeleteCFNLambda' 
  PermissionForDeleteCFNLambda: 
    Type: "AWS::Lambda::Permission"
    DependsOn:
      - DeleteStackEventRule
    Properties: 
      FunctionName: !Join [ "", [ 'DeleteCFNLambda', !Ref BUILDID ] ]
      Action: "lambda:InvokeFunction"
      Principal: "events.amazonaws.com"
      SourceArn: !GetAtt DeleteStackEventRule.Arn
  GenerateCronExpLambda:
    Type: "AWS::Lambda::Function"
    Properties:
      FunctionName: !Join [ "", [ 'GenerateCronExpressionLambda', !Ref BUILDID ] ]
      Code:
        ZipFile: |
          from datetime import datetime, timedelta
          import os
          import logging
          import json
          import cfnresponse
          
          def deletion_time(ttl):
              delete_at_time = datetime.now() + timedelta(minutes=int(ttl))
              hh = delete_at_time.hour
              mm = delete_at_time.minute
              yyyy = delete_at_time.year
              month = delete_at_time.month
              dd = delete_at_time.day
              # minutes hours day month day-of-week year
              cron_exp = "cron({} {} {} {} ? {})".format(mm, hh, dd, month, yyyy)
              return cron_exp
          
          def handler(event, context):
            print('Received event: %s' % json.dumps(event))
            status = cfnresponse.SUCCESS
            try:
                if event['RequestType'] == 'Delete':
                    cfnresponse.send(event, context, status, {})
                else:
                    ttl = event['ResourceProperties']['ttl']
                    responseData = {}
                    responseData['cron_exp'] = deletion_time(ttl)
                    cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData)
            except Exception as e:
                logging.error('Exception: %s' % e, exc_info=True)
                status = cfnresponse.FAILED
                cfnresponse.send(event, context, status, {}, None)
      Handler: "index.handler"
      Runtime: "python3.6"
      Timeout: "5"
      Role: 
       'Fn::ImportValue': !Sub '${EnvironmentName}:DeleteCFNLambdaExecutionRole'
  GenerateCronExpression:
    Type: "Custom::GenerateCronExpression"
    Version: "1.0"
    Properties:
      Name: !Join [ "", [ 'GenerateCronExpression', !Ref BUILDID ] ]
      ServiceToken: !GetAtt GenerateCronExpLambda.Arn
      ttl: !Ref 'TTL'
