---

AWSTemplateFormatVersion: '2010-09-09'
Description: 'Deploy Lambda to execute Redshift stored procedure, views and queries'

Parameters:
  VPC:
    Type: AWS::EC2::VPC::Id
    Description: 'Select VPC to create Lambda function in'

  LambdaSubnets:
    Type: List<AWS::EC2::Subnet::Id>
    Description: 'Select two subnets which can communicate with Redshift cluster'

  ApiGatewayVpcEndpointExists:
    Type: String
    Description: 'VPC Endpoint for API Gateway with DNS enabled present'
    Default: 'No'
    AllowedValues:
      - 'Yes'
      - 'No'

  redshiftApiStageName:
    Type: String
    Description: 'Rest Api stage name. No changes needed'
    Default: v1
    AllowedValues:
      - v1

  RedshiftSessionDurationSeconds:
    Type: Number
    Description: 'Redshift credentials session duration in seconds. Minimum 900 seconds, maximum 3600 seconds'
    Default: 900
    MinValue: 900
    MaxValue: 3600

Metadata:
  'AWS::CloudFormation::Interface':
    ParameterGroups:
      - Label:
          default: 'Lambda VPC Configuration'
        Parameters:
          - VPC
          - LambdaSubnets
          - ApiGatewayVpcEndpointExists
      - Label:
          default: 'Redshift Session Duration'
        Parameters:
          - RedshiftSessionDurationSeconds
      - Label:
          default: 'Redshift Service API Gateway Stage Version'
        Parameters:
          - redshiftApiStageName

    ParameterLabels:
      VPC:
        default: VPC where Lambda will run and endpoint will be created
      LambdaSubnets:
        default: Subnets from VPC selected above for Lambda
      ApiGatewayVpcEndpointExists:
        default: Do you have VPC Endpoint present for API Gateway with DNS enabled? choose "Yes" if you have one
      redshiftApiStageName:
        default: Stage name for Rest Api. Use default value provided.
      RedshiftSessionDurationSeconds:
        default: Redshift Credentials Validity Duration in seconds

Conditions:
  ConditionCreateApiGatewayEndpoint: !Equals [ !Ref ApiGatewayVpcEndpointExists, "No" ]
  ConditionExistingApiGatewayEndpoint: !Equals [ !Ref ApiGatewayVpcEndpointExists, "Yes" ]

Resources:
  LambdaArtifactsBucket:
    Type: 'AWS::S3::Bucket'

  GetLambdaArtifacts:
    Type: 'Custom::GetLambdaArtifacts'
    Properties:
      ServiceToken: !GetAtt GetLambdaArtifactsFunction.Arn
      inputBucketName: !Ref LambdaArtifactsBucket

  GetLambdaArtifactsFunction:
    DependsOn:
      - LambdaArtifactsBucket
      - GetLambdaArtifactsFunctionLambdaRolePolicy
    Type: 'AWS::Lambda::Function'
    Properties:
      Description: 'Get Lambda artifacts S3 data bucket'
      Handler: index.lambda_handler
      Runtime: python3.7
      MemorySize: 128
      Timeout: 60
      Role: !GetAtt GetLambdaArtifactsFunctionLambdaRole.Arn
      Code:
        ZipFile: !Sub |
          import json
          import boto3
          import urllib3

          def copyArtifacts(bucket_name):
              print("Attempting to copy the artifacts to bucket {0}".format(bucket_name))
              s3_client = boto3.client('s3')
              s3 = boto3.resource('s3')

              try:
                  bucket = s3.Bucket(bucket_name).load()
              except ClientError:
                  print("Bucket {0} does not exist".format(bucket_name))
                  return
              # Copy the two source artifacts
              copyRedshiftLambdaZip = {
                  'Bucket': 'redshift-immersionday-labs',
                  'Key': 'data/auto-dba/lambda-function.zip'
              }
              copyRedshiftServiceAPILambdaZip = {
                  'Bucket': 'redshift-immersionday-labs',
                  'Key': 'data/auto-dba/redshift-service-api-lambda-function.zip'
              }
              try:
                  s3.meta.client.copy(copyRedshiftLambdaZip, bucket_name, 'lambda-function.zip')
                  s3.meta.client.copy(copyRedshiftServiceAPILambdaZip, bucket_name, 'redshift-service-api-lambda-function.zip')
                  print("Successfully copied the artifacts to the bucket {0}".format(bucket_name))
              except Exception as e:
                  print(e)

          def lambda_handler(event, context):
              try:
                  bucket = event['ResourceProperties']['inputBucketName']
                  if event['RequestType'] == 'Create':
                      copyArtifacts(bucket)
                  sendResponse(event, context, "SUCCESS")
              except Exception as e:
                  print(e)
                  sendResponse(event, context, "FAILED")

          def sendResponse(event, context, status):
              http = urllib3.PoolManager()
              response_body = {'Status': status,
                               'Reason': 'Log stream name: ' + context.log_stream_name,
                               'PhysicalResourceId': context.log_stream_name,
                               'StackId': event['StackId'],
                               'RequestId': event['RequestId'],
                               'LogicalResourceId': event['LogicalResourceId'],
                               'Data': json.loads("{}")}
              http.request('PUT', event['ResponseURL'], body=json.dumps(response_body))

  GetLambdaArtifactsFunctionLambdaRole:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: 'Allow'
            Principal:
              Service:
                - 'lambda.amazonaws.com'
            Action: 'sts:AssumeRole'
      Path: '/'

  GetLambdaArtifactsFunctionLambdaRolePolicy:
    Type: 'AWS::IAM::ManagedPolicy'
    Properties:
      Roles:
        - !Ref GetLambdaArtifactsFunctionLambdaRole
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: 'Allow'
            Action:
              - 's3:Get*'
              - 's3:List*'
            Resource:
              - 'arn:aws:s3:::redshift-immersionday-labs/data/auto-dba/*'
          - Effect: 'Allow'
            Action:
              - 's3:*'
            Resource:
              - !GetAtt LambdaArtifactsBucket.Arn
              - !Join [ '', [ !GetAtt LambdaArtifactsBucket.Arn, '/' ]]
              - !Join [ '', [ !GetAtt LambdaArtifactsBucket.Arn, '/*' ]]
          - Effect: 'Deny'
            Action:
              - 's3:DeleteBucket'
            Resource: '*'
          - Effect: 'Allow'
            Action:
              - ec2:CreateNetworkInterface
              - ec2:DescribeNetworkInterfaces
              - ec2:DeleteNetworkInterface
              - logs:CreateLogGroup
              - logs:CreateLogStream
              - logs:PutLogEvents
            Resource: '*'

  CleanupS3Bucket:
    Type: 'Custom::CleanUpBucket'
    Properties:
      ServiceToken: !GetAtt CleanupS3Function.Arn
      inputBucketName: !Ref LambdaArtifactsBucket

  CleanupS3Function:
    DependsOn:
      - LambdaArtifactsBucket
      - CleanupS3FunctionLambdaRolePolicy
    Type: 'AWS::Lambda::Function'
    Properties:
      Description: 'Clean-up Lambda artifacts S3 data bucket'
      Handler: index.lambda_handler
      Runtime: python3.7
      MemorySize: 128
      Timeout: 60
      Role: !GetAtt CleanupS3FunctionLambdaRole.Arn
      Code:
        ZipFile: !Sub |
          import json
          import boto3
          import urllib3

          def empty_bucket(bucket_name):
              print("Attempting to empty the bucket {0}".format(bucket_name))
              s3_client = boto3.client('s3')
              s3 = boto3.resource('s3')

              try:
                  bucket = s3.Bucket(bucket_name).load()
              except ClientError:
                  print("Bucket {0} does not exist".format(bucket_name))
                  return
              # Confirm if versioning is enabled
              version_status = s3_client.get_bucket_versioning(Bucket=bucket_name)
              status = version_status.get('Status','')
              if status == 'Enabled':
                  version_status = s3_client.put_bucket_versioning(Bucket=bucket_name,
                                                             VersioningConfiguration={'Status': 'Suspended'})
              version_paginator = s3_client.get_paginator('list_object_versions')
              version_iterator = version_paginator.paginate(
                  Bucket=bucket_name
              )

              for page in version_iterator:
                  print(page)
                  if 'DeleteMarkers' in page:
                      delete_markers = page['DeleteMarkers']
                      if delete_markers is not None:
                          for delete_marker in delete_markers:
                              key = delete_marker['Key']
                              versionId = delete_marker['VersionId']
                              s3_client.delete_object(Bucket=bucket_name, Key=key, VersionId=versionId)
                  if 'Versions' in page and page['Versions'] is not None:
                      versions = page['Versions']
                      for version in versions:
                          print(version)
                          key = version['Key']
                          versionId = version['VersionId']
                          s3_client.delete_object(Bucket=bucket_name, Key=key, VersionId=versionId)
              object_paginator = s3_client.get_paginator('list_objects_v2')
              object_iterator = object_paginator.paginate(
                  Bucket=bucket_name
              )
              for page in object_iterator:
                  if 'Contents' in page:
                      for content in page['Contents']:
                          key = content['Key']
                          s3_client.delete_object(Bucket=bucket_name, Key=content['Key'])
              print("Successfully emptied the bucket {0}".format(bucket_name))

          def lambda_handler(event, context):
              try:
                  bucket = event['ResourceProperties']['inputBucketName']
                  if event['RequestType'] == 'Delete':
                      empty_bucket(bucket)
                  sendResponse(event, context, "SUCCESS")
              except Exception as e:
                  print(e)
                  sendResponse(event, context, "FAILED")

          def sendResponse(event, context, status):
              http = urllib3.PoolManager()
              response_body = {'Status': status,
                               'Reason': 'Log stream name: ' + context.log_stream_name,
                               'PhysicalResourceId': context.log_stream_name,
                               'StackId': event['StackId'],
                               'RequestId': event['RequestId'],
                               'LogicalResourceId': event['LogicalResourceId'],
                               'Data': json.loads("{}")}
              http.request('PUT', event['ResponseURL'], body=json.dumps(response_body))

  CleanupS3FunctionLambdaRole:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: 'Allow'
            Principal:
              Service:
                - 'lambda.amazonaws.com'
            Action: 'sts:AssumeRole'
      Path: '/'

  CleanupS3FunctionLambdaRolePolicy:
    Type: 'AWS::IAM::ManagedPolicy'
    Properties:
      Roles:
        - !Ref CleanupS3FunctionLambdaRole
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: 'Allow'
            Action:
              - 's3:*'
            Resource:
              - !GetAtt LambdaArtifactsBucket.Arn
              - !Join [ '', [ !GetAtt LambdaArtifactsBucket.Arn, '/' ]]
              - !Join [ '', [ !GetAtt LambdaArtifactsBucket.Arn, '/*' ]]
          - Effect: 'Deny'
            Action:
              - 's3:DeleteBucket'
            Resource: '*'
          - Effect: 'Allow'
            Action:
              - ec2:CreateNetworkInterface
              - ec2:DescribeNetworkInterfaces
              - ec2:DeleteNetworkInterface
              - logs:CreateLogGroup
              - logs:CreateLogStream
              - logs:PutLogEvents
            Resource: '*'

  ApiGatewayVpcEndpointSecurityGroup:
    Type: 'AWS::EC2::SecurityGroup'
    Properties:
      GroupDescription: 'Security group for API Gateway VPC endpoint'
      VpcId: !Ref VPC

  ApiGatewayVpcEndpointIngressLambda:
    Type: 'AWS::EC2::SecurityGroupIngress'
    Properties:
      GroupId: !Ref ApiGatewayVpcEndpointSecurityGroup
      IpProtocol: tcp
      FromPort: 443
      ToPort: 443
      SourceSecurityGroupId: !Ref LambdaSecurityGroup

  ApiGatewayVpcEndpoint:
    Type: 'AWS::EC2::VPCEndpoint'
    Condition: ConditionCreateApiGatewayEndpoint
    Properties:
      ServiceName: !Sub com.amazonaws.${AWS::Region}.execute-api
      VpcEndpointType: Interface
      VpcId: !Ref VPC
      SubnetIds:
        - !Select [0, !Ref LambdaSubnets]
        - !Select [1, !Ref LambdaSubnets]
      SecurityGroupIds:
        - !Ref ApiGatewayVpcEndpointSecurityGroup
      PrivateDnsEnabled: true
      PolicyDocument: !Sub |
        {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Principal": "*",
              "Action": "*",
              "Resource": "*"
            }
          ]
        }

  FindApiGatewayEndpointId:
    DependsOn: ApiGatewayVpcEndpointSecurityGroup
    Type: 'Custom::ApiGatewayEndpointId'
    Condition: ConditionExistingApiGatewayEndpoint
    Properties:
      ServiceToken: !GetAtt ApiGatewayEndpointIdFunction.Arn
      VpcID: !Ref VPC
      ApiGatewaySecurityGroupId: !Ref ApiGatewayVpcEndpointSecurityGroup

  ApiGatewayEndpointIdFunction:
    DependsOn: 
      - ApiGatewayVpcEndpointSecurityGroup
      - ApiGatewayEndpointIdFunctionLambdaRolePolicy
    Type: 'AWS::Lambda::Function'
    Condition: ConditionExistingApiGatewayEndpoint
    Properties:
      Description: 'Find VPC Endpoint for API Gateway'
      Handler: index.lambda_handler
      Runtime: python3.7
      MemorySize: 128
      Timeout: 60
      Role: !GetAtt ApiGatewayEndpointIdFunctionLambdaRole.Arn
      Environment:
        Variables:
          REGION: !Ref 'AWS::Region'
      Code:
        ZipFile: !Sub |
          import boto3
          from os import environ
          import json
          import urllib3          

          print("[INFO]", "Initialize function")
          session = boto3.session.Session(region_name=environ["REGION"])
          ec2 = session.client('ec2')
          http = urllib3.PoolManager()          

          def findVpcEndpoint(vpcId, regionName, serviceName):
            print("[INFO]", "Trying to find VPC Endpoint for VPC ID: " +vpcId+ " for service: " +serviceName)
            try:
              vpcEndpointResponse = ec2.describe_vpc_endpoints(Filters=[
                {'Name': 'vpc-id', 'Values': [vpcId]},
                {'Name': 'service-name', 'Values': ['com.amazonaws.' +regionName+ '.' +serviceName]}
                ])
              
              if len(vpcEndpointResponse['VpcEndpoints']) == 1:
                print("[INFO]", "Found matching VPC Endpoint for VPC ID: " +vpcId+ " for service: " +serviceName)
                vpcEndpointId = vpcEndpointResponse['VpcEndpoints'][0]['VpcEndpointId']
              else:
                vpcEndpointId = None          

              return vpcEndpointId          

            except Exception as e:
              print("[ERROR]", e)          

          def addVpcEndpointSecurityGroup(vpcEndpointId, vpcEndpointSecurityGroupId):
            print("[INFO]", "Adding Security Group for VPC Endpoint: " +vpcEndpointId)
            try:
              ec2.modify_vpc_endpoint(VpcEndpointId=vpcEndpointId, AddSecurityGroupIds=[vpcEndpointSecurityGroupId])
            except Exception as e:
              print("[ERROR]", e)          

          def removeVpcEndpointSecurityGroup(vpcEndpointId, vpcEndpointSecurityGroupId):
            print("[INFO]", "Removing Security Group for VPC Endpoint: " +vpcEndpointId)
            try:
              ec2.modify_vpc_endpoint(VpcEndpointId=vpcEndpointId, RemoveSecurityGroupIds=[vpcEndpointSecurityGroupId])
            except Exception as e:
              print("[ERROR]", e)          

          def sendResponse(event, context, status, data, reason):
              response_body = {'Status': status,
                               'Reason': reason,
                               'PhysicalResourceId': context.log_stream_name,
                               'StackId': event['StackId'],
                               'RequestId': event['RequestId'],
                               'LogicalResourceId': event['LogicalResourceId'],
                               'Data': data}
              http.request('PUT', event['ResponseURL'], body=json.dumps(response_body))          

          def lambda_handler(event, context):
            print("[INFO]", "Locating VPC Endpoint")          

            try:
                ApiSecurityGroupId = event["ResourceProperties"]["ApiGatewaySecurityGroupId"]
                VpcID = event["ResourceProperties"]["VpcID"]
                Region = environ["REGION"]
                ServiceName = 'execute-api'          

                response_data = {}          

                # Find VPC Endpoint
                if event["RequestType"] == 'Create':
                    apiVpcEndpoint = findVpcEndpoint(VpcID, Region, ServiceName)
                    if apiVpcEndpoint != None:
                        addVpcEndpointSecurityGroup(apiVpcEndpoint, ApiSecurityGroupId)
                        response_status = "SUCCESS"
                        response_reason = "Successfully added the SG"
                    elif apiVpcEndpoint == None:
                        response_status = "FAILED"
                        response_reason = "No VPC Endpoint found"

                    response_data['vpcEndpointId'] = apiVpcEndpoint
                    sendResponse(event, context, response_status, response_data, response_reason)          

                elif event["RequestType"] == 'Delete':
                    apiVpcEndpoint = findVpcEndpoint(VpcID, Region, ServiceName)
                    if apiVpcEndpoint != None:
                        removeVpcEndpointSecurityGroup(apiVpcEndpoint, ApiSecurityGroupId)
                        response_status = "SUCCESS"
                        response_reason = "Successfully removed the SG"
                    elif apiVpcEndpoint == None:
                        response_status = "SUCCESS"
                        response_reason = "No VPC Endpoint found"
                    sendResponse(event, context, response_status, response_data, response_reason)
            except Exception as e:
              print("[ERROR]", e)
              response_data = {}
              sendResponse(event, context, "FAILED", response_data, e)          

            print("[INFO]", "Execution complete")

  ApiGatewayEndpointIdFunctionLambdaRole:
    Type: 'AWS::IAM::Role'
    Condition: ConditionExistingApiGatewayEndpoint
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: 'Allow'
            Principal:
              Service:
                - 'lambda.amazonaws.com'
            Action: 'sts:AssumeRole'
      Path: '/'

  ApiGatewayEndpointIdFunctionLambdaRolePolicy:
    Type: 'AWS::IAM::ManagedPolicy'
    Condition: ConditionExistingApiGatewayEndpoint
    Properties:
      Roles:
        - !Ref ApiGatewayEndpointIdFunctionLambdaRole
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: 'Allow'
            Action:
              - ec2:CreateNetworkInterface
              - ec2:DescribeNetworkInterfaces
              - ec2:DeleteNetworkInterface
              - ec2:ModifyVpcEndpoint
              - ec2:DescribeVpcEndpoints
              - logs:CreateLogGroup
              - logs:CreateLogStream
              - logs:PutLogEvents
            Resource: '*'

  RedshiftRestApi:
    Type: 'AWS::ApiGateway::RestApi'
    Properties:
      Name: !Sub '${AWS::StackName}_RedshiftApi_Call'
      Description: 'The API gateway will route the request for Redshift API to Lambda'
      EndpointConfiguration:
        Types:
          - PRIVATE
        VpcEndpointIds:
          - !If [ ConditionCreateApiGatewayEndpoint, !Ref ApiGatewayVpcEndpoint, !GetAtt FindApiGatewayEndpointId.vpcEndpointId ]
      Policy: !Sub |
        {
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Principal": "*",
              "Action": "execute-api:Invoke",
              "Resource": "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:*/*/*/*"
            },
            {
              "Effect": "Deny",
              "Principal": "*",
              "Action": "execute-api:Invoke",
              "Resource": "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:*/*/*/*",
              "Condition": {
                "StringNotEquals": {
                  "aws:SourceVpc": "${VPC}"
                }
              }
            }
          ]
        }

  RedshiftRestApiResource:
    Type: 'AWS::ApiGateway::Resource'
    Properties:
      RestApiId: !Ref RedshiftRestApi
      ParentId: !GetAtt RedshiftRestApi.RootResourceId
      PathPart: api

  RedshiftRestApiPost:
    Type: 'AWS::ApiGateway::Method'
    Properties:
      RestApiId: !Ref RedshiftRestApi
      ResourceId: !Ref RedshiftRestApiResource
      HttpMethod: POST
      AuthorizationType: NONE
      Integration:
        Type: AWS_PROXY
        IntegrationHttpMethod: POST
        Uri: !Sub
          - 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${redshiftLambdaArn}/invocations'
          - redshiftLambdaArn: !GetAtt RedshiftApiLambda.Arn

  RedshiftRestApiDeployment:
    Type: 'AWS::ApiGateway::Deployment'
    DependsOn:
      - RedshiftRestApiPost
    Properties:
      RestApiId: !Ref RedshiftRestApi
      StageName: !Ref redshiftApiStageName

  RedshiftApiLambdaRole:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: sts:AssumeRole
      Path: /

  RedshiftApiLambdaRolePolicy:
    Type: 'AWS::IAM::ManagedPolicy'
    Properties:
      Roles:
        - !Ref RedshiftApiLambdaRole
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Action:
              - redshift:GetClusterCredentials
              - redshift:Describe*
              - logs:CreateLogGroup
              - logs:CreateLogStream
              - logs:PutLogEvents
            Resource: "*"

  RedshiftApiLambda:
    Type: 'AWS::Lambda::Function'
    DependsOn:
      - GetLambdaArtifacts
      - RedshiftApiLambdaRolePolicy
    Properties:
      FunctionName: !Sub ${AWS::StackName}_RedshiftApi_Function
      Description: 'Run API calls for Amazon Redshift'
      Handler: index.lambda_handler
      Runtime: python3.7
      MemorySize: 128
      Timeout: 60
      Role: !GetAtt RedshiftApiLambdaRole.Arn
      Code:
        S3Bucket: !Ref LambdaArtifactsBucket
        S3Key: redshift-service-api-lambda-function.zip

  RedshiftApiLambdaPermission:
    Type: 'AWS::Lambda::Permission'
    Properties:
      Action: lambda:InvokeFunction
      FunctionName: !GetAtt RedshiftApiLambda.Arn
      Principal: apigateway.amazonaws.com
      SourceArn: !Sub 'arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${RedshiftRestApi}/*/POST/api'

  LambdaSecurityGroup:
    Type: 'AWS::EC2::SecurityGroup'
    Properties:
      GroupDescription: 'Security group for Lambda'
      VpcId: !Ref VPC

  LambdaServiceRole:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: lambda.amazonaws.com
            Action: sts:AssumeRole
      Path: /

  LambdaServiceRolePolicy:
    Type: 'AWS::IAM::ManagedPolicy'
    Properties:
      Roles:
        - !Ref LambdaServiceRole
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Action:
              - redshift:GetClusterCredentials
              - redshift:Describe*
              - ec2:CreateNetworkInterface
              - ec2:DescribeNetworkInterfaces
              - ec2:DeleteNetworkInterface
              - logs:CreateLogGroup
              - logs:CreateLogStream
              - logs:PutLogEvents
            Resource: "*"

  RedshiftExecuteSQLFunction:
    Type: 'AWS::Lambda::Function'
    DependsOn:
      - GetLambdaArtifacts
      - LambdaServiceRolePolicy
    Properties:
      FunctionName: !Sub ${AWS::StackName}_Lambda_Function
      Description: 'Executes stored procedure, views and sql in Amazon Redshift remotely'
      Handler: index.lambda_handler
      Runtime: python3.7
      MemorySize: 128
      Timeout: 60
      Role: !GetAtt LambdaServiceRole.Arn
      Environment:
        Variables:
          REGION: !Ref 'AWS::Region'
          API_URL: !Sub 'https://${RedshiftRestApi}.execute-api.${AWS::Region}.amazonaws.com/${redshiftApiStageName}/api'
          REDSHIFT_SESSION_DURATION_SECONDS: !Ref RedshiftSessionDurationSeconds
      Code:
        S3Bucket: !Ref LambdaArtifactsBucket
        S3Key: lambda-function.zip
      VpcConfig:
        SecurityGroupIds:
          - !Ref LambdaSecurityGroup
        SubnetIds:
          - !Select [0, !Ref LambdaSubnets]
          - !Select [1, !Ref LambdaSubnets]

  SchedulerRedshiftExecutionLambdaPermission:
    Type: 'AWS::Lambda::Permission'
    Properties:
      Action: lambda:InvokeFunction
      FunctionName: !GetAtt RedshiftExecuteSQLFunction.Arn
      Principal: events.amazonaws.com

  SchedulerRedshiftExecutionLambdaRole:
    Type: 'AWS::IAM::Role'
    Properties:
      AssumeRolePolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Principal:
              Service: events.amazonaws.com
            Action: sts:AssumeRole
      Path: /

  SchedulerRedshiftExecutionLambdaRolePolicy:
    Type: 'AWS::IAM::ManagedPolicy'
    Properties:
      Roles:
        - !Ref SchedulerRedshiftExecutionLambdaRole
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
          - Effect: Allow
            Action:
              - lambda:InvokeFunction
              - lambda:InvokeAsync
            Resource:
              - !Sub '${RedshiftExecuteSQLFunction.Arn}'

Outputs:
  RedshiftExecuteSQLFunction:
    Description: 'AWS Lambda function executing the SQL objects in Amazon Redshift'
    Value: !Ref RedshiftExecuteSQLFunction

  RedshiftExecuteSQLFunctionARN:
    Description: 'ARN value for AWS Lambda function executing the SQL objects in Amazon Redshift'
    Value: !GetAtt RedshiftExecuteSQLFunction.Arn

  RedshiftExecuteSQLFunctionSecurityGroup:
    Description: 'Security Group ID for Lambda function executing the SQL objects in Amazon Redshift'
    Value: !Ref LambdaSecurityGroup

  SchedulerExecutionRole:
    Description: 'AWS IAM Role with permission to invoke Lambda function'
    Value: !GetAtt 'SchedulerRedshiftExecutionLambdaRole.Arn'
