AWSTemplateFormatVersion: "2010-09-09"

Description: "The AWS CloudFormation template for deployment of the AWS Data Lake resources"

Parameters:
    UserPoolId:
        Type: String
        Description: Id of data lake Amazon Cognito User Pool
    UserPoolClientId:
        Type: String
        Description: Id of data lake Amazon Cognito App Client
    SourceS3Bucket:
        Type: String
    FederatedLogin:
        Type: String

Resources:
    DataLakeKmsKey:
        Type: "AWS::KMS::Key"
        Properties:
            Description: "Data Lake KMS Key"
            KeyPolicy:
                Version: "2012-10-17"
                Id: "data-lake-key-default-1"
                Statement:
                    -
                        Sid: "Allow administration of the key"
                        Effect: "Allow"
                        Principal:
                            AWS: !Join ["", ["arn:aws:iam::", Ref: "AWS::AccountId", ":root"]]
                        Action:
                            - "kms:*"
                        # Resource being created now. No target key id defined yet.
                        Resource: "*"

    DataLakeKmsKeyAlias:
        Type: AWS::KMS::Alias
        Properties:
            AliasName: !Join ["", ["alias/datalake-", Ref: "AWS::Region" ]]
            TargetKeyId:
                Ref: DataLakeKmsKey

    DataLakeLoggingRole:
        Type: "AWS::IAM::Role"
        Properties:
            RoleName: !Join ["-", ["data-lake-logging-role", Ref: "AWS::Region" ]]
            AssumeRolePolicyDocument:
                Version: "2012-10-17"
                Statement:
                    -
                        Effect: "Allow"
                        Principal:
                            Service:
                                - "lambda.amazonaws.com"
                        Action:
                                - "sts:AssumeRole"
            Path: "/"

    DataLakeLoggingPolicy:
        Type: "AWS::IAM::ManagedPolicy"
        Properties:
            Description: "Data Lake policy for the logging microservice Lambda function."
            PolicyDocument:
                Version: "2012-10-17"
                Statement:
                    -
                        Effect: "Allow"
                        Action:
                            - "logs:CreateLogGroup"
                            - "logs:CreateLogStream"
                            - "logs:PutLogEvents"
                        Resource:
                            - !Join ["", ["arn:aws:logs:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":log-group:/datalake/audit-log:*"]]
                            - !Join ["", ["arn:aws:logs:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":log-group:/aws/lambda/data-lake-logging-service:*"]]
                    -
                        Effect: "Allow"
                        Action:
                            - "dynamodb:GetItem"
                            - "dynamodb:PutItem"
                            - "dynamodb:Query"
                            - "dynamodb:Scan"
                            - "dynamodb:UpdateItem"
                        Resource:
                            - !Join ["", ["arn:aws:dynamodb:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":table/data-lake-settings"]]
            Roles:
                -
                    Ref: "DataLakeLoggingRole"

    DataLakeLoggingService:
        Type: "AWS::Lambda::Function"
        Properties:
            Code:
                S3Bucket: !Ref SourceS3Bucket
                S3Key: "data-lake-logging-service.zip"
            Description: "A data lake microservice function for logging functionality"
            FunctionName: "data-lake-logging-service"
            Handler: "index.handler"
            MemorySize: "256"
            Role:
                Fn::GetAtt:
                    - "DataLakeLoggingRole"
                    - "Arn"
            Runtime: "nodejs8.10"
            Timeout: "60"

    DataLakeAdminRole:
        Type: "AWS::IAM::Role"
        Properties:
            RoleName: !Join ["-", ["data-lake-admin-role", Ref: "AWS::Region" ]]
            AssumeRolePolicyDocument:
                Version: "2012-10-17"
                Statement:
                    -
                        Effect: "Allow"
                        Principal:
                            Service:
                                - "lambda.amazonaws.com"
                        Action:
                                - "sts:AssumeRole"
            Path: "/"

    DataLakeAdminPolicy:
        Type: "AWS::IAM::ManagedPolicy"
        Properties:
            Description: "Data Lake policy for the admin microservice Lambda function."
            PolicyDocument:
                Version: "2012-10-17"
                Statement:
                    -
                        Effect: "Allow"
                        Action:
                            - "logs:CreateLogGroup"
                            - "logs:CreateLogStream"
                            - "logs:PutLogEvents"
                        Resource:
                            !Join ["", ["arn:aws:logs:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":log-group:/aws/lambda/data-lake-admin-service:*"]]
                    -
                        Effect: "Allow"
                        Action:
                            - "*"
                        Resource:
                            !Join ["", ["arn:aws:cognito-idp:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":userpool/", Ref: UserPoolId]]
                    -
                        Effect: "Allow"
                        Action:
                            - "dynamodb:BatchGetItem"
                            - "dynamodb:BatchWriteItem"
                            - "dynamodb:DeleteItem"
                            - "dynamodb:GetItem"
                            - "dynamodb:PutItem"
                            - "dynamodb:Query"
                            - "dynamodb:Scan"
                            - "dynamodb:UpdateItem"
                        Resource:
                            - !Join ["", ["arn:aws:dynamodb:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":table/data-lake-settings"]]
                            - !Join ["", ["arn:aws:dynamodb:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":table/data-lake-keys"]]
                    -
                        Effect: "Allow"
                        Action:
                            - "lambda:InvokeFunction"
                        Resource:
                            -
                                Fn::GetAtt:
                                    - "DataLakeLoggingService"
                                    - "Arn"
                    -
                        Effect: "Allow"
                        Action:
                            - "kms:Encrypt"
                            - "kms:Decrypt"
                            - "kms:ReEncrypt*"
                            - "kms:GenerateDataKey*"
                            - "kms:DescribeKey"
                        Resource:
                            Fn::GetAtt:
                                - "DataLakeKmsKey"
                                - "Arn"
            Roles:
                -
                    Ref: "DataLakeAdminRole"

    DataLakeAdminService:
        Type: "AWS::Lambda::Function"
        Properties:
            Code:
                S3Bucket: !Ref SourceS3Bucket
                S3Key: "data-lake-admin-service.zip"
            Description: "A data lake microservice function for admin functionality"
            FunctionName: "data-lake-admin-service"
            Handler: "index.handler"
            MemorySize: "256"
            Role:
                Fn::GetAtt:
                    - "DataLakeAdminRole"
                    - "Arn"
            Runtime: "nodejs8.10"
            Timeout: "60"
            Environment:
                Variables:
                    USER_POOL_ID: !Ref "UserPoolId"
                    USER_POOL_CLIENT_ID: !Ref "UserPoolClientId"
                    FEDERATED_LOGIN: !Ref "FederatedLogin"

    DataLakeSearchRole:
        Type: "AWS::IAM::Role"
        Properties:
            RoleName: !Join ["-", ["data-lake-search-role", Ref: "AWS::Region" ]]
            AssumeRolePolicyDocument:
                Version: "2012-10-17"
                Statement:
                    -
                        Effect: "Allow"
                        Principal:
                            Service:
                                - "lambda.amazonaws.com"
                        Action:
                                - "sts:AssumeRole"
            Path: "/"

    DataLakeSearchPolicy:
        Type: "AWS::IAM::ManagedPolicy"
        Properties:
            Description: "Data Lake policy for the search microservice Lambda function."
            PolicyDocument:
                Version: "2012-10-17"
                Statement:
                    -
                        Effect: "Allow"
                        Action:
                            - "logs:CreateLogGroup"
                            - "logs:CreateLogStream"
                            - "logs:PutLogEvents"
                        Resource:
                            !Join ["", ["arn:aws:logs:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":log-group:/aws/lambda/data-lake-search-service:*"]]
                    -
                        Effect: "Allow"
                        Action:
                            - "es:ESHttpPost"
                            - "es:ESHttpDelete"
                            - "es:ESHttpPut"
                            - "es:ESHttpGet"
                        Resource:
                            !Join ["", ["arn:aws:es:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":domain/data-lake/*"]]
                    -
                        Effect: "Allow"
                        Action:
                            - "dynamodb:GetItem"
                            - "dynamodb:Query"
                            - "dynamodb:Scan"
                        Resource:
                            - !Join ["", ["arn:aws:dynamodb:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":table/data-lake-settings"]]
                    -
                        Effect: "Allow"
                        Action:
                            - "dynamodb:GetItem"
                        Resource:
                            - !Join ["", ["arn:aws:dynamodb:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":table/data-lake-packages"]]
                    -
                        Effect: "Allow"
                        Action:
                            - "lambda:InvokeFunction"
                        Resource:
                            -
                                Fn::GetAtt:
                                    - "DataLakeAdminService"
                                    - "Arn"
                            -
                                Fn::GetAtt:
                                    - "DataLakeLoggingService"
                                    - "Arn"
                    -
                        Effect: "Allow"
                        Action:
                            - "cognito-idp:AdminListGroupsForUser"
                            - "cognito-idp:AdminGetUser"
                        Resource:
                            !Join ["", ["arn:aws:cognito-idp:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":userpool/", Ref: UserPoolId]]
                    -
                        Effect: "Allow"
                        Action:
                            - "glue:GetTables"
                        # At the time of writing this service does not have ARNs, so * will be used
                        Resource: "*"
            Roles:
                -
                    Ref: "DataLakeSearchRole"

    DataLakeSearchService:
        Type: "AWS::Lambda::Function"
        Properties:
            Code:
                S3Bucket: !Ref SourceS3Bucket
                S3Key: "data-lake-search-service.zip"
            Description: "A data lake microservice function for interacting with the elasticsearch cluster"
            FunctionName: "data-lake-search-service"
            Handler: "index.handler"
            MemorySize: "256"
            Role:
                Fn::GetAtt:
                    - "DataLakeSearchRole"
                    - "Arn"
            Runtime: "nodejs8.10"
            Timeout: "60"
            Environment:
                Variables:
                    USER_POOL_ID: !Ref "UserPoolId"
                    FEDERATED_LOGIN: !Ref "FederatedLogin"

    DataLakeGlueCrawlerEventRule:
        Type: "AWS::Events::Rule"
        Properties:
            Description: "DataLakeSearchService is invoked when an AWS Glue crawler finishes successfully."
            EventPattern:
                source:
                    - "aws.glue"
                detail-type:
                    - "Glue Crawler State Change"
                detail:
                    state:
                        - "Succeeded"
            State: "ENABLED"
            Targets:
                -
                    Arn:
                        Fn::GetAtt:
                            - "DataLakeSearchService"
                            - "Arn"
                    Id: !Join ["-", ["data-lake-glue-crawler-event-rule", Ref: "AWS::Region" ]]

    PermissionForEventsToInvokeLambda:
        Type: "AWS::Lambda::Permission"
        Properties:
            FunctionName:
                Ref: "DataLakeSearchService"
            Action: "lambda:InvokeFunction"
            Principal: "events.amazonaws.com"
            SourceArn:
                Fn::GetAtt:
                    - "DataLakeGlueCrawlerEventRule"
                    - "Arn"

    DataLakeManifestRole:
        Type: "AWS::IAM::Role"
        Properties:
            RoleName: !Join ["-", ["data-lake-manifest-role", Ref: "AWS::Region" ]]
            AssumeRolePolicyDocument:
                Version: "2012-10-17"
                Statement:
                    -
                        Effect: "Allow"
                        Principal:
                            Service:
                                - "lambda.amazonaws.com"
                        Action:
                                - "sts:AssumeRole"
            Path: "/"

    DataLakeManifestPolicy:
        Type: "AWS::IAM::ManagedPolicy"
        Properties:
            Description: "Data Lake policy for the manifest microservice Lambda function."
            PolicyDocument:
                Version: "2012-10-17"
                Statement:
                    -
                        Effect: "Allow"
                        Action:
                            - "logs:CreateLogGroup"
                            - "logs:CreateLogStream"
                            - "logs:PutLogEvents"
                        Resource:
                            !Join ["", ["arn:aws:logs:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":log-group:/aws/lambda/data-lake-manifest-service:*"]]
                    -
                        Effect: "Allow"
                        Action:
                            - "dynamodb:BatchGetItem"
                            - "dynamodb:BatchWriteItem"
                            - "dynamodb:DeleteItem"
                            - "dynamodb:GetItem"
                            - "dynamodb:PutItem"
                            - "dynamodb:Query"
                            - "dynamodb:Scan"
                            - "dynamodb:UpdateItem"
                        Resource:
                            - !Join ["", ["arn:aws:dynamodb:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":table/data-lake-settings"]]
                            - !Join ["", ["arn:aws:dynamodb:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":table/data-lake-cart"]]
                            - !Join ["", ["arn:aws:dynamodb:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":table/data-lake-datasets"]]
                    -
                        Effect: "Allow"
                        Action:
                            - "lambda:InvokeFunction"
                        Resource:
                            -
                                Fn::GetAtt:
                                    - "DataLakeAdminService"
                                    - "Arn"
                    -
                        Effect: "Allow"
                        Action:
                            - "s3:GetObject"
                        Resource:
                            - "arn:aws:s3:::*"
                    -
                        Effect: "Allow"
                        Action:
                            - "s3:ListBucket"
                            - "s3:PutObject"
                        Resource:
                            - !Join ["", ["arn:aws:s3:::data-lake-", Ref: "AWS::Region", "-", Ref: "AWS::AccountId", "/*" ]]
                            - !Join ["", ["arn:aws:s3:::data-lake-", Ref: "AWS::Region", "-", Ref: "AWS::AccountId"]]
                    -
                        Effect: "Allow"
                        Action:
                            - "kms:Encrypt"
                            - "kms:Decrypt"
                            - "kms:ReEncrypt*"
                            - "kms:GenerateDataKey*"
                            - "kms:DescribeKey"
                        Resource:
                            Fn::GetAtt:
                                - "DataLakeKmsKey"
                                - "Arn"
            Roles:
                -
                    Ref: "DataLakeManifestRole"

    DataLakeManifestService:
        Type: "AWS::Lambda::Function"
        Properties:
            Code:
                S3Bucket: !Ref SourceS3Bucket
                S3Key: "data-lake-manifest-service.zip"
            Description: "A data lake microservice function for manifest functionality"
            FunctionName: "data-lake-manifest-service"
            Handler: "index.handler"
            MemorySize: "256"
            Role:
                Fn::GetAtt:
                    - "DataLakeManifestRole"
                    - "Arn"
            Runtime: "nodejs8.10"
            Timeout: "300"

    DataLakeCartRole:
        Type: "AWS::IAM::Role"
        Properties:
            RoleName: !Join ["-", ["data-lake-cart-role", Ref: "AWS::Region" ]]
            AssumeRolePolicyDocument:
                Version: "2012-10-17"
                Statement:
                    -
                        Effect: "Allow"
                        Principal:
                            Service:
                                - "lambda.amazonaws.com"
                        Action:
                                - "sts:AssumeRole"
            Path: "/"

    DataLakeCartPolicy:
        Type: "AWS::IAM::ManagedPolicy"
        Properties:
            Description: "Data Lake policy for the cart microservice Lambda function."
            PolicyDocument:
                Version: "2012-10-17"
                Statement:
                    -
                        Effect: "Allow"
                        Action:
                            - "logs:CreateLogGroup"
                            - "logs:CreateLogStream"
                            - "logs:PutLogEvents"
                        Resource:
                            !Join ["", ["arn:aws:logs:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":log-group:/aws/lambda/data-lake-cart-service:*"]]
                    -
                        Effect: "Allow"
                        Action:
                            - "dynamodb:BatchGetItem"
                            - "dynamodb:BatchWriteItem"
                            - "dynamodb:DeleteItem"
                            - "dynamodb:GetItem"
                            - "dynamodb:PutItem"
                            - "dynamodb:Query"
                            - "dynamodb:Scan"
                            - "dynamodb:UpdateItem"
                        Resource:
                            - !Join ["", ["arn:aws:dynamodb:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":table/data-lake-settings"]]
                            - !Join ["", ["arn:aws:dynamodb:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":table/data-lake-cart"]]
                            - !Join ["", ["arn:aws:dynamodb:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":table/data-lake-packages"]]
                    -
                        Effect: "Allow"
                        Action:
                            - "lambda:InvokeFunction"
                        Resource:
                            -
                                Fn::GetAtt:
                                    - "DataLakeAdminService"
                                    - "Arn"
                            -
                                Fn::GetAtt:
                                    - "DataLakeLoggingService"
                                    - "Arn"
                            -
                                Fn::GetAtt:
                                    - "DataLakeManifestService"
                                    - "Arn"
                    -
                        Effect: "Allow"
                        Action:
                            - "cognito-idp:AdminListGroupsForUser"
                            - "cognito-idp:AdminGetUser"
                        Resource:
                            !Join ["", ["arn:aws:cognito-idp:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":userpool/", Ref: UserPoolId]]
            Roles:
                -
                    Ref: "DataLakeCartRole"

    DataLakeCartService:
        Type: "AWS::Lambda::Function"
        Properties:
            Code:
                S3Bucket: !Ref SourceS3Bucket
                S3Key: "data-lake-cart-service.zip"
            Description: "A data lake microservice function for cart functionality"
            FunctionName: "data-lake-cart-service"
            Handler: "index.handler"
            MemorySize: "256"
            Role:
                Fn::GetAtt:
                    - "DataLakeCartRole"
                    - "Arn"
            Runtime: "nodejs8.10"
            Timeout: "60"
            Environment:
                Variables:
                    USER_POOL_ID: !Ref "UserPoolId"
                    FEDERATED_LOGIN: !Ref "FederatedLogin"

    DataLakePackageCrawlerRole:
        Type: "AWS::IAM::Role"
        Properties:
            RoleName: !Join ["-", ["data-lake-package-crawler-role", Ref: "AWS::Region" ]]
            AssumeRolePolicyDocument:
                Version: "2012-10-17"
                Statement:
                    -
                        Effect: "Allow"
                        Principal:
                            Service:
                                - "glue.amazonaws.com"
                        Action:
                                - "sts:AssumeRole"
            Path: "/"

    DataLakePackageCrawlerPolicy:
        Type: "AWS::IAM::ManagedPolicy"
        Properties:
            Description: "Data Lake policy for the package crawler."
            PolicyDocument:
                Version: "2012-10-17"
                Statement:
                    -
                        Effect: "Allow"
                        Action:
                            - "glue:*"
                            - "s3:GetBucketLocation"
                            - "s3:ListBucket"
                            - "s3:ListAllMyBuckets"
                            - "s3:GetBucketAcl"
                            - "ec2:DescribeVpcEndpoints"
                            - "ec2:DescribeRouteTables"
                            - "ec2:CreateNetworkInterface"
                            - "ec2:DeleteNetworkInterface"
                            - "ec2:DescribeNetworkInterfaces"
                            - "ec2:DescribeSecurityGroups"
                            - "ec2:DescribeSubnets"
                            - "ec2:DescribeVpcAttribute"
                            - "iam:ListRolePolicies"
                            - "iam:GetRole"
                            - "iam:GetRolePolicy"
                        # Policy based on http://amzn.to/2G6NlG3 and http://amzn.to/2oYQCza
                        Resource: "*"
                    -
                        Effect: "Allow"
                        Action:
                            - "s3:CreateBucket"
                        Resource:
                            - "arn:aws:s3:::aws-glue-*"
                    -
                        Effect: "Allow"
                        Action:
                            - "s3:GetObject"
                            - "s3:PutObject"
                            - "s3:DeleteObject"
                        Resource:
                            - "arn:aws:s3:::aws-glue-*/*"
                            - "arn:aws:s3:::*/*aws-glue-*/*"
                    -
                        Effect: "Allow"
                        Action:
                            - "s3:GetObject"
                        Resource:
                            - "arn:aws:s3:::crawler-public*"
                            - "arn:aws:s3:::aws-glue-*"
                    -
                        Effect: "Allow"
                        Action:
                            - "logs:CreateLogGroup"
                            - "logs:CreateLogStream"
                            - "logs:PutLogEvents"
                        Resource:
                            - "arn:aws:logs:*:*:/aws-glue/*"
                    -
                        Effect: "Allow"
                        Action:
                            - "ec2:CreateTags"
                            - "ec2:DeleteTags"
                        Resource:
                            - "arn:aws:ec2:*:*:network-interface/*"
                            - "arn:aws:ec2:*:*:security-group/*"
                            - "arn:aws:ec2:*:*:instance/*"
                        Condition:
                            ForAllValues:StringEquals:
                                aws:TagKeys: "aws-glue-service-resource"
                    -
                        Effect: "Allow"
                        Action:
                            - "kms:Decrypt"
                        Resource:
                            Fn::GetAtt:
                                - "DataLakeKmsKey"
                                - "Arn"
                    -
                        Effect: "Allow"
                        Action:
                            - "s3:ListBucket"
                            - "s3:GetObject"
                        Resource:
                            - !Join ["", ["arn:aws:s3:::data-lake-", Ref: "AWS::Region", "-", Ref: "AWS::AccountId", "/*" ]]
            Roles:
                -
                    Ref: "DataLakePackageCrawlerRole"


    DataLakePackagesRole:
        Type: "AWS::IAM::Role"
        Properties:
            RoleName: !Join ["-", ["data-lake-packages-role", Ref: "AWS::Region" ]]
            AssumeRolePolicyDocument:
                Version: "2012-10-17"
                Statement:
                    -
                        Effect: "Allow"
                        Principal:
                            Service:
                                - "lambda.amazonaws.com"
                        Action:
                                - "sts:AssumeRole"
            Path: "/"

    DataLakePackagesPolicy:
        Type: "AWS::IAM::ManagedPolicy"
        Properties:
            Description: "Data Lake policy for the package microservice Lambda function."
            PolicyDocument:
                Version: "2012-10-17"
                Statement:
                    -
                        Effect: "Allow"
                        Action:
                            - "logs:CreateLogGroup"
                            - "logs:CreateLogStream"
                            - "logs:PutLogEvents"
                        Resource:
                            !Join ["", ["arn:aws:logs:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":log-group:/aws/lambda/data-lake-package-service:*"]]
                    -
                        Effect: "Allow"
                        Action:
                            - "dynamodb:BatchGetItem"
                            - "dynamodb:BatchWriteItem"
                            - "dynamodb:DeleteItem"
                            - "dynamodb:GetItem"
                            - "dynamodb:PutItem"
                            - "dynamodb:Query"
                            - "dynamodb:Scan"
                            - "dynamodb:UpdateItem"
                        Resource:
                            - !Join ["", ["arn:aws:dynamodb:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":table/data-lake-packages"]]
                            - !Join ["", ["arn:aws:dynamodb:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":table/data-lake-datasets"]]
                            - !Join ["", ["arn:aws:dynamodb:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":table/data-lake-metadata"]]
                            - !Join ["", ["arn:aws:dynamodb:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":table/data-lake-settings"]]
                    -
                        Effect: "Allow"
                        Action:
                            - "lambda:InvokeFunction"
                        Resource:
                            -
                                Fn::GetAtt:
                                    - "DataLakeAdminService"
                                    - "Arn"
                            -
                                Fn::GetAtt:
                                    - "DataLakeSearchService"
                                    - "Arn"
                            -
                                Fn::GetAtt:
                                    - "DataLakeManifestService"
                                    - "Arn"
                            -
                                Fn::GetAtt:
                                    - "DataLakeLoggingService"
                                    - "Arn"
                    -
                        Effect: "Allow"
                        Action:
                            - "s3:ListBucket"
                            - "s3:GetObject"
                            - "s3:DeleteObject"
                            - "s3:PutObject"
                        Resource:
                            - !Join ["", ["arn:aws:s3:::data-lake-", Ref: "AWS::Region", "-", Ref: "AWS::AccountId", "/*" ]]
                            - !Join ["", ["arn:aws:s3:::data-lake-", Ref: "AWS::Region", "-", Ref: "AWS::AccountId"]]
                    -
                        Effect: "Allow"
                        Action:
                            - "kms:Encrypt"
                            - "kms:Decrypt"
                            - "kms:ReEncrypt*"
                            - "kms:GenerateDataKey*"
                            - "kms:DescribeKey"
                        Resource:
                            Fn::GetAtt:
                                - "DataLakeKmsKey"
                                - "Arn"
                    -
                        Effect: "Allow"
                        Action:
                            - "glue:CreateDatabase"
                            - "glue:DeleteDatabase"
                            - "glue:GetDatabase"
                            - "glue:GetDatabases"
                            - "glue:UpdateDatabase"
                            - "glue:CreateTable"
                            - "glue:DeleteTable"
                            - "glue:BatchDeleteTable"
                            - "glue:UpdateTable"
                            - "glue:GetTable"
                            - "glue:GetTables"
                            - "glue:BatchCreatePartition"
                            - "glue:CreatePartition"
                            - "glue:DeletePartition"
                            - "glue:BatchDeletePartition"
                            - "glue:UpdatePartition"
                            - "glue:GetPartition"
                            - "glue:GetPartitions"
                            - "glue:BatchGetPartition"
                            - "glue:DeleteCrawler"
                            - "glue:StartCrawler"
                            - "glue:StartCrawlerSchedule"
                            - "glue:StopCrawler"
                            - "glue:StopCrawlerSchedule"
                            - "glue:CreateCrawler"
                            - "glue:GetCrawler"
                            - "glue:UpdateCrawler"
                        # At the time of writing this service does not have ARNs, so * will be used
                        Resource: "*"
                    -
                        Effect: "Allow"
                        Action:
                            - "athena:CancelQueryExecution"
                            - "athena:GetCatalogs"
                            - "athena:GetExecutionEngine"
                            - "athena:GetExecutionEngines"
                            - "athena:GetNamespace"
                            - "athena:GetNamespaces"
                            - "athena:GetQueryExecution"
                            - "athena:GetQueryExecutions"
                            - "athena:GetQueryResults"
                            - "athena:GetTable"
                            - "athena:GetTables"
                            - "athena:RunQuery"
                            - "athena:StartQueryExecution"
                            - "athena:StopQueryExecution"
                        # At the time of writing this service does not have ARNs, so * will be used
                        Resource: "*"
                    -
                        Effect: "Allow"
                        Action:
                            - "s3:GetBucketLocation"
                            - "s3:GetObject"
                            - "s3:ListBucket"
                            - "s3:ListBucketMultipartUploads"
                            - "s3:ListMultipartUploadParts"
                            - "s3:AbortMultipartUpload"
                            - "s3:CreateBucket"
                            - "s3:PutObject"
                        Resource: "arn:aws:s3:::aws-athena-query-results-*"
                    -
                        Effect: "Allow"
                        Action:
                            - "iam:PassRole"
                        Resource:
                            Fn::GetAtt:
                                - "DataLakePackageCrawlerRole"
                                - "Arn"
                    -
                        Effect: "Allow"
                        Action:
                            - "cognito-idp:AdminListGroupsForUser"
                            - "cognito-idp:AdminGetUser"
                        Resource:
                            !Join ["", ["arn:aws:cognito-idp:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":userpool/", Ref: UserPoolId]]
            Roles:
                -
                    Ref: "DataLakePackagesRole"

    DataLakePackagesService:
        Type: "AWS::Lambda::Function"
        Properties:
            Code:
                S3Bucket: !Ref SourceS3Bucket
                S3Key: "data-lake-package-service.zip"
            Description: "A data lake microservice function for package functionality"
            FunctionName: "data-lake-package-service"
            Handler: "index.handler"
            MemorySize: "256"
            Role:
                Fn::GetAtt:
                    - "DataLakePackagesRole"
                    - "Arn"
            Runtime: "nodejs8.10"
            Timeout: "300"
            Environment:
                Variables:
                    CRAWLER_ROLE_ARN: !GetAtt DataLakePackageCrawlerRole.Arn
                    ACCOUNT_ID: !Ref "AWS::AccountId"
                    USER_POOL_ID: !Ref "UserPoolId"
                    FEDERATED_LOGIN: !Ref "FederatedLogin"

    DataLakeProfileRole:
        Type: "AWS::IAM::Role"
        Properties:
            RoleName: !Join ["-", ["data-lake-profile-role", Ref: "AWS::Region" ]]
            AssumeRolePolicyDocument:
                Version: "2012-10-17"
                Statement:
                    -
                        Effect: "Allow"
                        Principal:
                            Service:
                                - "lambda.amazonaws.com"
                        Action:
                                - "sts:AssumeRole"
            Path: "/"

    DataLakeProfilePolicy:
        Type: "AWS::IAM::ManagedPolicy"
        Properties:
            Description: "Data Lake policy for the profile microservice Lambda function."
            PolicyDocument:
                Version: "2012-10-17"
                Statement:
                    -
                        Effect: "Allow"
                        Action:
                            - "logs:CreateLogGroup"
                            - "logs:CreateLogStream"
                            - "logs:PutLogEvents"
                        Resource:
                            !Join ["", ["arn:aws:logs:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":log-group:/aws/lambda/data-lake-profile-service:*"]]
                    -
                        Effect: "Allow"
                        Action:
                            - "dynamodb:GetItem"
                            - "dynamodb:Query"
                            - "dynamodb:Scan"
                        Resource:
                            - !Join ["", ["arn:aws:dynamodb:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":table/data-lake-settings"]]
                    -
                        Effect: "Allow"
                        Action:
                            - "lambda:InvokeFunction"
                        Resource:
                            -
                                Fn::GetAtt:
                                    - "DataLakeAdminService"
                                    - "Arn"
                            -
                                Fn::GetAtt:
                                    - "DataLakeLoggingService"
                                    - "Arn"
                    -
                        Effect: "Allow"
                        Action:
                            - "*"
                        Resource:
                            !Join ["", ["arn:aws:cognito-idp:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":userpool/", Ref: UserPoolId]]
                    -
                        Effect: "Allow"
                        Action:
                            - "kms:Encrypt"
                            - "kms:Decrypt"
                            - "kms:ReEncrypt*"
                            - "kms:GenerateDataKey*"
                            - "kms:DescribeKey"
                        Resource:
                            Fn::GetAtt:
                                - "DataLakeKmsKey"
                                - "Arn"
            Roles:
                -
                    Ref: "DataLakeProfileRole"

    DataLakeProfileService:
        Type: "AWS::Lambda::Function"
        Properties:
            Code:
                S3Bucket: !Ref SourceS3Bucket
                S3Key: "data-lake-profile-service.zip"
            Description: "A data lake microservice function for profile functionality"
            FunctionName: "data-lake-profile-service"
            Handler: "index.handler"
            MemorySize: "256"
            Role:
                Fn::GetAtt:
                    - "DataLakeProfileRole"
                    - "Arn"
            Runtime: "nodejs8.10"
            Timeout: "300"
            Environment:
                Variables:
                    FEDERATED_LOGIN: !Ref "FederatedLogin"

    DataLakeAuthorizerRole:
        Type: "AWS::IAM::Role"
        Properties:
            RoleName: !Join ["-", ["data-lake-authorizer-role", Ref: "AWS::Region" ]]
            AssumeRolePolicyDocument:
                Version: "2012-10-17"
                Statement:
                    -
                        Effect: "Allow"
                        Principal:
                            Service:
                                - "lambda.amazonaws.com"
                        Action:
                                - "sts:AssumeRole"
            Path: "/"

    DataLakeAuthorizerPolicy:
        Type: "AWS::IAM::ManagedPolicy"
        Properties:
            Description: "Data Lake policy for the authorizer microservice Lambda function."
            PolicyDocument:
                Version: "2012-10-17"
                Statement:
                    -
                        Effect: "Allow"
                        Action:
                            - "logs:CreateLogGroup"
                            - "logs:CreateLogStream"
                            - "logs:PutLogEvents"
                        Resource:
                            !Join ["", ["arn:aws:logs:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":log-group:/aws/lambda/data-lake-authorizer:*"]]
                    -
                        Effect: "Allow"
                        Action:
                            - "*"
                        Resource:
                            !Join ["", ["arn:aws:cognito-idp:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":userpool/", Ref: UserPoolId]]
                    -
                        Effect: "Allow"
                        Action:
                            - "dynamodb:GetItem"
                            - "dynamodb:Query"
                        Resource:
                            - !Join ["", ["arn:aws:dynamodb:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":table/data-lake-settings"]]
                            - !Join ["", ["arn:aws:dynamodb:", Ref: "AWS::Region", ":", Ref: "AWS::AccountId", ":table/data-lake-keys"]]
                    -
                        Effect: "Allow"
                        Action:
                            - "kms:Encrypt"
                            - "kms:Decrypt"
                            - "kms:ReEncrypt*"
                            - "kms:GenerateDataKey*"
                            - "kms:DescribeKey"
                        Resource:
                            Fn::GetAtt:
                                - "DataLakeKmsKey"
                                - "Arn"
            Roles:
                -
                    Ref: "DataLakeAuthorizerRole"

    DataLakeAuthorizerService:
        Type: "AWS::Lambda::Function"
        Properties:
            Code:
                S3Bucket: !Ref SourceS3Bucket
                S3Key: "data-lake-authorizer.zip"
            Description: "A data lake microservice function for custom authorizer functionality"
            FunctionName: "data-lake-authorizer"
            Handler: "index.handler"
            MemorySize: "256"
            Role:
                Fn::GetAtt:
                    - "DataLakeAuthorizerRole"
                    - "Arn"
            Runtime: "nodejs8.10"
            Timeout: "60"

Outputs:
    AuthorizerArn:
        Description: "ARN of data lake API Gateway custom authorizer function"
        Value: !GetAtt DataLakeAuthorizerService.Arn
    AdminArn:
        Description: "ARN of data lake admin services function"
        Value: !GetAtt DataLakeAdminService.Arn
    CartArn:
        Description: "ARN of data lake cart services function"
        Value: !GetAtt DataLakeCartService.Arn
    ProfileArn:
        Description: "ARN of data lake profile services function"
        Value: !GetAtt DataLakeProfileService.Arn
    SearchArn:
        Description: "ARN of data lake search services function"
        Value: !GetAtt DataLakeSearchService.Arn
    PackageArn:
        Description: "ARN of data lake package services function"
        Value: !GetAtt DataLakePackagesService.Arn
