AWSTemplateFormatVersion: "2010-09-09"
Description: Demo - Orchestrate ETL process for Amazon Redshift using Amazon S3 events and AWS Step Functions
Resources:
  blogdemoRedshiftSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
        GroupDescription: "Redshift Security Group"
        SecurityGroupEgress:
            - IpProtocol: tcp
              FromPort: 22
              ToPort: 22
              CidrIp: 127.0.0.1/32
        VpcId: 
           Ref: DeploymentVPC
  
  blogdemoRedshift:
    Type: AWS::Redshift::Cluster
    Properties: 
      ClusterType: multi-node
      DBName: dev
      MasterUsername: 
        Ref: MasterUserName
      MasterUserPassword:
        Ref: MasterUserPassword
      NodeType: ra3.xlplus
      NumberOfNodes: 2
      PubliclyAccessible: false
      Encrypted: true
      VpcSecurityGroupIds: 
        - !GetAtt blogdemoRedshiftSecurityGroup.GroupId
      IamRoles:
        - !GetAtt blogdemoRoleRedshift.Arn

  blogdemoBucket:
    Type: AWS::S3::Bucket
    Properties:
      BucketEncryption:
        ServerSideEncryptionConfiguration:
          - ServerSideEncryptionByDefault:
                SSEAlgorithm: 'AES256'
      PublicAccessBlockConfiguration:
        BlockPublicAcls: true
        BlockPublicPolicy: true
        IgnorePublicAcls: true
        RestrictPublicBuckets: true
      LoggingConfiguration:
        LogFilePrefix: logs
  
  blogdemoBucketPolicy:
    Type: AWS::S3::BucketPolicy
    Properties:
        Bucket: !Ref blogdemoBucket
        PolicyDocument:
            Version: 2012-10-17
            Statement:
                - Action: 
                    - 's3:*'
                  Effect: Deny
                  Resource:
                    - !Sub arn:aws:s3:::${blogdemoBucket}
                    - !Sub arn:aws:s3:::${blogdemoBucket}/*
                  Principal: '*'
                  Condition:
                    Bool:
                        'aws:SecureTransport': 'false'
  
  blogdemoStepFunctions:
    Type: AWS::StepFunctions::StateMachine
    Properties:
        DefinitionString: |-
            {
                "StartAt": "Pass",
                "States": {
                    "Pass": {
                        "Type": "Pass",
                        "End": true
                    }
                }
            }
        RoleArn: !GetAtt blogdemoRoleStepFunctions.Arn
        
  blogdemoEventBridge:
    Type: AWS::Events::Rule
    Properties:
        ScheduleExpression: rate(5 minutes)
        State: DISABLED
   
  blogdemoSQS:
    Type: AWS::SQS::Queue

  blogdemoSNS:
    Type: AWS::SNS::Topic  

  blogdemoLambdaVerify:
    Type: AWS::Lambda::Function
    Properties:
        Code:
            ZipFile: |
                import boto3
                
                def lambda_handler(event, context):
                    #verify_filename
        Role: !GetAtt blogdemoRoleLambda.Arn
        Runtime: python3.9
        Handler: index.lambda_handler
        ReservedConcurrentExecutions: 1
        
  blogdemoLambdaGenerate:
    Type: AWS::Lambda::Function
    Properties:
        Code:
            ZipFile: |
                import boto3
                
                def lambda_handler(event, context):
                    #generate_manifest_file
        Role: !GetAtt blogdemoRoleLambda.Arn
        Runtime: python3.9
        Handler: index.lambda_handler
        ReservedConcurrentExecutions: 1
  
  blogdemoPolicyS3:
    Type: AWS::IAM::Policy
    Properties:
        PolicyDocument:
            Version: "2012-10-17"
            Statement:
                - Effect: Allow
                  Action:
                    - s3:PutObject
                    - s3:GetObject
                    - s3:ListBucket
                    - s3:DeleteObject
                    - s3:ListMultipartUploadParts
                  Resource: 
                    - !GetAtt blogdemoBucket.Arn
                    - !Sub arn:aws:s3:::${blogdemoBucket}/*
        PolicyName: !Sub AccessTo-${blogdemoBucket}
        Roles:
            - Ref: blogdemoRoleRedshift
            - Ref: blogdemoRoleLambda
  
  blogdemoPolicyRedshift:
    Type: AWS::IAM::Policy
    Properties:
        PolicyDocument:
            Version: "2012-10-17"
            Statement:
                - Effect: Allow
                  Action:
                    - redshift:GetClusterCredentials
                  Resource: 
                    - !Sub arn:aws:redshift:${AWS::Region}:${AWS::AccountId}:dbuser:${blogdemoRedshift}/*
                    - !Sub arn:aws:redshift:${AWS::Region}:${AWS::AccountId}:dbname:${blogdemoRedshift}/*
        PolicyName: !Sub AccessTo-${blogdemoRedshift}
        Roles:
            - Ref: blogdemoRoleStepFunctions
  
  blogdemoPolicyRedshiftDataExecute:
    Type: AWS::IAM::Policy
    Properties:
        PolicyDocument:
            Version: "2012-10-17"
            Statement:
                - Effect: Allow
                  Action:
                    - redshift-data:ExecuteStatement
                  Resource: 
                    - !Sub arn:aws:redshift:${AWS::Region}:${AWS::AccountId}:cluster:${blogdemoRedshift}
        PolicyName: !Sub AccessTo-${blogdemoRedshift}-Execute
        Roles:
            - Ref: blogdemoRoleStepFunctions

  blogdemoPolicyRedshiftDataDescribe:
    Type: AWS::IAM::Policy
    Properties:
        PolicyDocument:
            Version: "2012-10-17"
            Statement:
                - Effect: Allow
                  Action:
                    - redshift-data:DescribeStatement
                  Resource: '*'
        PolicyName: !Sub AccessTo-${blogdemoRedshift}-Describe
        Roles:
            - Ref: blogdemoRoleStepFunctions
  
  blogdemoPolicyLambdaGenerate:
    Type: AWS::IAM::Policy
    Properties:
        PolicyDocument:
            Version: "2012-10-17"
            Statement:
                - Effect: Allow
                  Action:
                    - lambda:InvokeFunction
                  Resource: !Sub ${blogdemoLambdaGenerate.Arn}:$LATEST
        PolicyName: !Sub AccessTo-${blogdemoLambdaGenerate}
        Roles:
            - Ref: blogdemoRoleStepFunctions

  blogdemoPolicyStepFunctions:
    Type: AWS::IAM::Policy
    Properties:
        PolicyDocument:
            Version: "2012-10-17"
            Statement:
                - Effect: Allow
                  Action: 
                    - states:StartExecution
                    - states:SendTaskSuccess
                    - states:SendTaskFailure
                  Resource: !GetAtt blogdemoStepFunctions.Arn
        PolicyName: !Sub AccessTo-${blogdemoStepFunctions.Name}
        Roles:
            - Ref: blogdemoRoleEventBridge
            - Ref: blogdemoRoleLambda
  
  blogdemoPolicySQS:
    Type: AWS::IAM::Policy
    Properties:
        PolicyDocument:
            Version: "2012-10-17"
            Statement:
                - Effect: Allow
                  Action:
                    - sqs:DeleteMessage
                    - sqs:GetQueueUrl
                    - sqs:ReceiveMessage
                  Resource: !GetAtt blogdemoSQS.Arn
        PolicyName: !Sub AccessTo-${blogdemoSQS.QueueName}
        Roles:
            - Ref: blogdemoRoleLambda
  
  blogdemoRoleRedshift:
    Type: AWS::IAM::Role
    Properties:
        AssumeRolePolicyDocument:
            Version: "2012-10-17"
            Statement:
                - Effect: Allow
                  Principal:
                    Service:
                        - redshift.amazonaws.com
                  Action:
                    - 'sts:AssumeRole'
            
  blogdemoRoleLambda:
    Type: AWS::IAM::Role
    Properties:
        AssumeRolePolicyDocument:
            Version: "2012-10-17"
            Statement:
                - Effect: Allow
                  Principal:
                    Service:
                        - lambda.amazonaws.com
                  Action:
                    - 'sts:AssumeRole'      
  
  blogdemoRoleStepFunctions:
    Type: AWS::IAM::Role
    Properties:
        AssumeRolePolicyDocument:
            Version: "2012-10-17"
            Statement:
                - Effect: Allow
                  Principal:
                    Service:
                        - states.amazonaws.com
                  Action:
                    - 'sts:AssumeRole'  

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

  blogdemoSNSTopicPolicy:
    Type: AWS::SNS::TopicPolicy
    Properties:
        Topics:
          - !Ref blogdemoSNS
        PolicyDocument:
          Version: "2012-10-17"
          Statement: 
            - Effect: Allow
              Action: 
                - sns:Publish 
              Resource: 
                - !Ref blogdemoSNS
              Principal:  
                Service: 
                    - s3.amazonaws.com    
              Condition:
                ArnEquals: 
                    'aws:SourceArn': !GetAtt blogdemoBucket.Arn

  blogdemoSQSQueuePolicy:
    Type: AWS::SQS::QueuePolicy
    Properties:
        Queues:
          - !Ref blogdemoSQS
        PolicyDocument:
          Version: "2012-10-17"
          Statement: 
            - Effect: Allow
              Action: 
                - sqs:SendMessage 
              Resource: 
                - !GetAtt blogdemoSQS.Arn
              Principal:  
                Service: 
                    - s3.amazonaws.com    
              Condition:
                ArnEquals: 
                    'aws:SourceArn': !GetAtt blogdemoBucket.Arn
            - Effect: Allow
              Action: 
                - sqs:ReceiveMessage 
              Resource: 
                - !GetAtt blogdemoSQS.Arn
              Principal:  
                Service: 
                    - lambda.amazonaws.com    
              Condition:
                ArnEquals: 
                    'aws:SourceArn': !GetAtt blogdemoLambdaGenerate.Arn
Parameters:
  MasterUserName:
    Type: String
    
  MasterUserPassword:
    Type: String
    NoEcho: true
  
  DeploymentVPC:
    Type : AWS::EC2::VPC::Id