Description: 'Building a User-Generated Content Workflow'



# Collect the needed parameters from the user
# Add human-readable descriptions to help users understand the parameter choices
Parameters:

  Code:
    Description: Select the Code Base to use in the lambda function.
    Type: String
    Default: python3.7
    AllowedValues:
      - python3.7
      - python3.6
      
  S3Accelerate:
    Description: Do you want to use Transfer Acceleration on your bucket?
    Type: String
    Default: Yes
    AllowedValues:
      - Yes
      - No
    
#  FileExtensions:
#    Description: Include a comma-separated list of valid file extensions that you will allow users to upload to your S3 bucket. e.g. mp4,mov,m4v     
#    Type: String
    # File Extensions cannot be blank
#    MinLength: 1
#    Default: mp4,mov
    
    
# Separate the parameters into categories and make the labels human-readable
Metadata:
    AWS::CloudFormation::Interface:
        ParameterGroups:
            -
              Label:
                default: Deployment Options
              Parameters:
                - Code
                - S3Accelerate
                #- FileExtensions
           
        ParameterLabels:          
          Code:
            default: Source Code
          S3Accelerate:
            default: Transfer Acceleration   
          #FileExtensions:
            #default: Valid File Extensions   



# Mappings allow us to store variables that we will reference later
Mappings:
  SourceCode:
    General:
      S3Bucket: brianbedard.com
      KeyPrefix: user-generated-content


Conditions:
    S3AccelerateCondition: !Equals [ !Ref S3Accelerate , Yes ]

    
    
# Instantiate the AWS resources
Resources:
# Create the S3 Bucket for storing the content
  UserGeneratedContentBucket:
    Type: AWS::S3::Bucket
    DeletionPolicy: Delete
    Properties:
      BucketEncryption:
        ServerSideEncryptionConfiguration:
        - ServerSideEncryptionByDefault:
            SSEAlgorithm: AES256
      AccelerateConfiguration:
        AccelerationStatus: !If [S3AccelerateCondition, 'Enabled', 'Suspended']


# Create the role for the Lambda function to use
  LambdaRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: 2012-10-17
        Statement:
          -
            Effect: Allow
            Principal:
              Service:
                - "lambda.amazonaws.com"
            Action:
              - sts:AssumeRole
 

# Create the policy for the role to use
  LambdaRolePolicy:
    Type: AWS::IAM::Policy
    Properties:
      Roles:
        - !Ref LambdaRole
      PolicyName: !Sub "${AWS::StackName}-user-generated-content-policy"
      PolicyDocument:
        Statement:
          - # This adds write permissions to the s3 bucket.
            Effect: Allow
            Action:
              - s3:PutObject
            Resource:
              - !Join ["", ["arn:aws:s3:::", Ref: UserGeneratedContentBucket, "/*"]]
          -
            Effect: Allow
            Action:
               # Allows assumers of the role to create log groups
              - logs:CreateLogGroup
              
            Resource:
              - !Join ["", ["arn:aws:logs:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":*"]]
          -
            Effect: Allow
            Action:
               # Allows assumers of the role to write logs so we can troubleshoot any issues
              - logs:CreateLogStream
              - logs:PutLogEvents
            Resource:
              - !Join ["", ["arn:aws:logs:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":log-group:/aws/lambda/", Ref: "AWS::StackName","-GeneratePresignedUrl:*"]]
  

# Create the S3 Bucket Policy   
  UGCBucketPolicy:
    Type: 'AWS::S3::BucketPolicy'
    Properties:
      Bucket: !Ref UserGeneratedContentBucket
      PolicyDocument:
        Version: '2012-10-17'
        Statement:
        - Sid: 'Allow the role to PUT mp4 files'
          Principal:
            AWS: !Sub '${LambdaRole.Arn}'
          Action: 's3:PutObject'
          Effect: Allow
          Resource: !Sub '${UserGeneratedContentBucket.Arn}/*.mp4'        
        - Sid: 'Deny everyone from PUT on object that is not mp4'
          Principal: "*"
          Action: 's3:PutObject'
          Effect: Deny
          NotResource: !Sub '${UserGeneratedContentBucket.Arn}/*.mp4'
  
          
# Create the Lambda function to create presigned URLs  
  UserGeneratedContentLambda:
    Type: AWS::Lambda::Function
#    DependsOn: LambdaInvokePermission
    Properties: 
        FunctionName: !Sub ${AWS::StackName}-GeneratePresignedUrl
        Description: Generates a presigned URL and returns it to the user.
        Timeout: 60
        MemorySize: 256
        Runtime:  !Ref Code        
        Handler: lambda_function.lambda_handler
        Role: !GetAtt LambdaRole.Arn
        Code:
            S3Bucket: !FindInMap ["SourceCode", "General", "S3Bucket"]
            S3Key: !Join ["/", [!FindInMap ["SourceCode", "General", "KeyPrefix"],  "ugc-lambda.zip"]]
        Environment: 
            Variables:
              FILE_EXTENSION: "mp4"#!Ref FileExtensions
              REGION: !Ref "AWS::Region"
              BUCKET_NAME: !Ref UserGeneratedContentBucket
              S3_ACCELERATE: !If [S3AccelerateCondition, true, false]
 

# Create a Cognito User Pool
  CognitoUserPool:
    Type: AWS::Cognito::UserPool
    Properties: 
        UserPoolName: !Sub "${AWS::StackName}-UGC-Cognito-Pool"


# Create a Cognito User Client      
  CognitoPoolClient:
    Type: AWS::Cognito::UserPoolClient
    Properties:
        UserPoolId: !Ref CognitoUserPool
        ExplicitAuthFlows: ["ALLOW_ADMIN_USER_PASSWORD_AUTH", "ALLOW_CUSTOM_AUTH", "ALLOW_USER_PASSWORD_AUTH", "ALLOW_USER_SRP_AUTH", "ALLOW_REFRESH_TOKEN_AUTH"]
        GenerateSecret: false
        

# Create a Cognito User
  CognitoUser:
    Type: AWS::Cognito::UserPoolUser
    Properties:
        UserPoolId: !Ref CognitoUserPool
        Username: "testuser"


# Create an APIGateway REST API
  ApiGatewayRestApi:
    Type: AWS::ApiGateway::RestApi
    Properties:
        Description: "API Call to receive a pre-signed URL"
        EndpointConfiguration:
            Types: 
                - "EDGE"
        Name: !Sub "${AWS::StackName}-UGC-API"

      
# Create an APIGateway Authorizer
  Authorizer:
    Type: 'AWS::ApiGateway::Authorizer'
    Properties:
        Name: "Cognito_Group"
        Type: "COGNITO_USER_POOLS"
        RestApiId: !Ref ApiGatewayRestApi
        IdentitySource: "method.request.header.Authorization"
        ProviderARNs: 
            - !GetAtt CognitoUserPool.Arn
 
 
# Create the API GET Method 
  APIGetMethod:
    Type: 'AWS::ApiGateway::Method'
    DependsOn: LambdaInvokePermission
    Properties:
        RestApiId: !Ref ApiGatewayRestApi
        AuthorizationType: "COGNITO_USER_POOLS"
        AuthorizerId: !Ref Authorizer
        ResourceId: !GetAtt 
            - ApiGatewayRestApi
            - RootResourceId
        HttpMethod: "GET"        
        RequestParameters:
            'method.request.header.Authorization': 'true'
        Integration:
            Type: AWS
            IntegrationHttpMethod: "POST"
            Uri: !Sub >-
                arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${UserGeneratedContentLambda.Arn}/invocations
            IntegrationResponses:
              - StatusCode: 200
        MethodResponses:
            - StatusCode: 200

      
# Create an APIGateway Deployment on stage "prod"
  APIDeployment:
    Type: "AWS::ApiGateway::Deployment"
    DependsOn: 
        - APIGetMethod
    Properties:
      RestApiId: !Ref "ApiGatewayRestApi"
      Description: "First version of our API Gateway Deployment"
      StageName: "DummyStage"


# Create the stage that we will use for the API calls
  ApiGatewayStage:
    Type: AWS::ApiGateway::Stage
    Properties:
      DeploymentId: !Ref APIDeployment
      MethodSettings:
        - HttpMethod: "GET"
        - ResourcePath: "/*"
      Description: "Lambda API Stage v1"
      RestApiId: !Ref ApiGatewayRestApi
      StageName: "v1"
      
      
# Allow the Lambda function to be invoked by APIGateway
  LambdaInvokePermission:
    Type: AWS::Lambda::Permission
    DependsOn: UserGeneratedContentLambda
    Properties:
        FunctionName: !GetAtt 
            - UserGeneratedContentLambda
            - Arn
        Action: 'lambda:InvokeFunction'
        Principal: apigateway.amazonaws.com
        #SourceAccount: !Ref 'AWS::AccountId'
        SourceArn: !Sub "arn:aws:execute-api:${AWS::Region}:${AWS::AccountId}:${ApiGatewayRestApi}/*/GET/"





# Create the outputs
Outputs:
  UserGeneratedContentBucket:
    Description: This is the bucket where the content will be stored.
    Value: !Ref UserGeneratedContentBucket
  APIGatewayInvokeURL:
    Value: !Sub "https://${ApiGatewayRestApi}.execute-api.${AWS::Region}.amazonaws.com/v1/"
    Description: This is the URL to invoke your API.
  UserCurlCommand:
    Value: !Sub "aws cognito-idp admin-set-user-password --user-pool-id ${CognitoUserPool} --username testuser --password Test1234* --permanent"
    Description: Run this cURL command to the Account Status on your testuser to CONFIRMED.
  UserPoolClientId:
    Value: !Sub '{"AuthParameters" : {"USERNAME" : "testuser", "PASSWORD" : "Test1234*"},"AuthFlow" : "USER_PASSWORD_AUTH","ClientId" : "${CognitoPoolClient}"}' 
    Description: This is the JSON used to create your aws-auth-data.json file.
  CurlLoginCommand:
    Value: !Sub "curl -X POST --data @aws-auth-data.json -H 'X-Amz-Target: AWSCognitoIdentityProviderService.InitiateAuth' -H 'Content-Type: application/x-amz-json-1.1' https://cognito-idp.${AWS::Region}.amazonaws.com/"
    Description: This is the curl to run to log your testuser in & receive an IdToken.
  CurlUploadFile:
    Value: "curl --upload-file testfile.mp4 'ENTER_PRESIGNED_URL_HERE'"
    Description: This curl will upload the testfile.mp4 from your computer to the S3 bucket.
