AWSTemplateFormatVersion: "2010-09-09"
Description: Safya unified log stack
Parameters:
  AutoScalingEnabled:
    Type: String
    AllowedValues:
      - true
      - false
    Default: true
    Description: Enable auto scaling on this stack
  AutoScalingMaxCapacity:
    Type: Number
    Default: 200
    Description: If auto-scaling is enabled, the maximum capacity to scale dynamodb up to
  AutoScalingMinCapacity:
    Type: Number
    Default: 5
    Description: If auto-scaling is enabled, the minimum capacity to scale dynamodb down to
  InitialProvisionedCapacity:
    Type: Number
    Default: 5
    Description: The initial capacity to provision the dynamodb tables with
  PreferredPartitionCount:
    Type: Number
    Default: 10
    Description: Preferred number of partitions
Conditions:
  ProvisionAutoScaling:
    Fn::Equals:
      - Ref: AutoScalingEnabled
      - true
Resources:
  EventsBucket:
    Type: AWS::S3::Bucket
  PartitionsTable:
    Type: AWS::DynamoDB::Table
    Properties:
      AttributeDefinitions:
        - AttributeName: partitionId
          AttributeType: S
      KeySchema:
        - AttributeName: partitionId
          KeyType: HASH
      ProvisionedThroughput:
        ReadCapacityUnits:
          Ref: InitialProvisionedCapacity
        WriteCapacityUnits:
          Ref: InitialProvisionedCapacity
  ConsumersTable:
    Type: AWS::DynamoDB::Table
    Properties:
      AttributeDefinitions:
        - AttributeName: consumerId
          AttributeType: S
        - AttributeName: partitionId
          AttributeType: S
      KeySchema:
        - AttributeName: partitionId
          KeyType: HASH
        - AttributeName: consumerId
          KeyType: RANGE
      ProvisionedThroughput:
        ReadCapacityUnits:
          Ref: InitialProvisionedCapacity
        WriteCapacityUnits:
          Ref: InitialProvisionedCapacity
  EventsTopic:
    Type: AWS::SNS::Topic
  PartitionsReadAutoScalingTarget:
    Type: AWS::ApplicationAutoScaling::ScalableTarget
    Condition: ProvisionAutoScaling
    Properties:
      MaxCapacity:
        Ref: AutoScalingMaxCapacity
      MinCapacity:
        Ref: AutoScalingMinCapacity
      ScalableDimension: dynamodb:table:ReadCapacityUnits
      RoleARN:
        Fn::GetAtt: [AutoScalingRole, Arn]
      ResourceId:
        Fn::Join:
          - /
          - - table
            - Ref: PartitionsTable
      ServiceNamespace: dynamodb
  PartitionsReadAutoScalingPolicy:
    Type: "AWS::ApplicationAutoScaling::ScalingPolicy"
    Condition: ProvisionAutoScaling
    Properties:
      PolicyName: PartitionsReadAutoScalingPolicy
      PolicyType: TargetTrackingScaling
      ScalingTargetId:
        Ref: PartitionsReadAutoScalingTarget
      TargetTrackingScalingPolicyConfiguration:
        TargetValue: 70.0
        ScaleInCooldown: 60
        ScaleOutCooldown: 60
        PredefinedMetricSpecification:
          PredefinedMetricType: DynamoDBReadCapacityUtilization
  PartitionsWriteAutoScalingTarget:
    Type: AWS::ApplicationAutoScaling::ScalableTarget
    Condition: ProvisionAutoScaling
    Properties:
      MaxCapacity:
        Ref: AutoScalingMaxCapacity
      MinCapacity:
        Ref: AutoScalingMinCapacity
      ScalableDimension: dynamodb:table:WriteCapacityUnits
      RoleARN:
        Fn::GetAtt: [AutoScalingRole, Arn]
      ResourceId:
        Fn::Join:
          - /
          - - table
            - Ref: PartitionsTable
      ServiceNamespace: dynamodb
  PartitionsWriteAutoScalingPolicy:
    Type: "AWS::ApplicationAutoScaling::ScalingPolicy"
    Condition: ProvisionAutoScaling
    Properties:
      PolicyName: PartitionsWriteAutoScalingPolicy
      PolicyType: TargetTrackingScaling
      ScalingTargetId:
        Ref: PartitionsWriteAutoScalingTarget
      TargetTrackingScalingPolicyConfiguration:
        TargetValue: 70.0
        ScaleInCooldown: 60
        ScaleOutCooldown: 60
        PredefinedMetricSpecification:
          PredefinedMetricType: DynamoDBWriteCapacityUtilization
  ConsumersReadAutoScalingTarget:
    Type: AWS::ApplicationAutoScaling::ScalableTarget
    Condition: ProvisionAutoScaling
    Properties:
      MaxCapacity:
        Ref: AutoScalingMaxCapacity
      MinCapacity:
        Ref: AutoScalingMinCapacity
      ScalableDimension: dynamodb:table:ReadCapacityUnits
      RoleARN:
        Fn::GetAtt: [AutoScalingRole, Arn]
      ResourceId:
        Fn::Join:
          - /
          - - table
            - Ref: ConsumersTable
      ServiceNamespace: dynamodb
  ConsumersReadAutoScalingPolicy:
    Type: "AWS::ApplicationAutoScaling::ScalingPolicy"
    Condition: ProvisionAutoScaling
    Properties:
      PolicyName: ConsumersReadAutoScalingPolicy
      PolicyType: TargetTrackingScaling
      ScalingTargetId:
        Ref: ConsumersReadAutoScalingTarget
      TargetTrackingScalingPolicyConfiguration:
        TargetValue: 70.0
        ScaleInCooldown: 60
        ScaleOutCooldown: 60
        PredefinedMetricSpecification:
          PredefinedMetricType: DynamoDBReadCapacityUtilization
  ConsumersWriteAutoScalingTarget:
    Type: AWS::ApplicationAutoScaling::ScalableTarget
    Condition: ProvisionAutoScaling
    Properties:
      MaxCapacity:
        Ref: AutoScalingMaxCapacity
      MinCapacity:
        Ref: AutoScalingMinCapacity
      ScalableDimension: dynamodb:table:WriteCapacityUnits
      RoleARN:
        Fn::GetAtt: [AutoScalingRole, Arn]
      ResourceId:
        Fn::Join:
          - /
          - - table
            - Ref: ConsumersTable
      ServiceNamespace: dynamodb
  ConsumersWriteAutoScalingPolicy:
    Type: "AWS::ApplicationAutoScaling::ScalingPolicy"
    Condition: ProvisionAutoScaling
    Properties:
      PolicyName: ConsumersWriteAutoScalingPolicy
      PolicyType: TargetTrackingScaling
      ScalingTargetId:
        Ref: ConsumersWriteAutoScalingTarget
      TargetTrackingScalingPolicyConfiguration:
        TargetValue: 70.0
        ScaleInCooldown: 60
        ScaleOutCooldown: 60
        PredefinedMetricSpecification:
          PredefinedMetricType: DynamoDBWriteCapacityUtilization
  AutoScalingRole:
    Type: AWS::IAM::Role
    Condition: ProvisionAutoScaling
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service:
                - application-autoscaling.amazonaws.com
            Action: "sts:AssumeRole"
      Policies:
        - PolicyName: AutoScalingRolePolicy
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - "dynamodb:DescribeTable"
                  - "dynamodb:UpdateTable"
                  - "cloudwatch:PutMetricAlarm"
                  - "cloudwatch:DescribeAlarms"
                  - "cloudwatch:GetMetricStatistics"
                  - "cloudwatch:SetAlarmState"
                  - "cloudwatch:DeleteAlarms"
                Resource: "*"
  ProducerPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action:
              - "s3:PutObject"
            Resource:
              Fn::Sub: "arn:aws:s3:::${EventsBucket}/*"
          - Effect: Allow
            Action:
              - "dynamodb:GetItem"
              - "dynamodb:DeleteItem"
              - "dynamodb:PutItem"
              - "dynamodb:Scan"
              - "dynamodb:Query"
              - "dynamodb:UpdateItem"
              - "dynamodb:BatchWriteItem"
              - "dynamodb:BatchGetItem"
            Resource:
              - Fn::Sub: "arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${PartitionsTable}"
          - Effect: Allow
            Action: "sns:Publish"
            Resource:
              - Ref: EventsTopic
  ConsumerPolicy:
    Type: AWS::IAM::ManagedPolicy
    Properties:
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action: "s3:GetObject"
            Resource:
              Fn::Sub: "arn:aws:s3:::${EventsBucket}/*"
          - Effect: Allow
            Action: "s3:ListBucket"
            Resource:
              Fn::Sub: "arn:aws:s3:::${EventsBucket}"
          - Effect: Allow
            Action:
              - "dynamodb:GetItem"
              - "dynamodb:DeleteItem"
              - "dynamodb:PutItem"
              - "dynamodb:Scan"
              - "dynamodb:Query"
              - "dynamodb:UpdateItem"
              - "dynamodb:BatchWriteItem"
              - "dynamodb:BatchGetItem"
            Resource:
              Fn::Sub: "arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${ConsumersTable}"
          - Effect: Allow
            Action:
              - "dynamodb:GetItem"
              - "dynamodb:Scan"
              - "dynamodb:Query"
              - "dynamodb:BatchGetItem"
            Resource:
              Fn::Sub: "arn:aws:dynamodb:${AWS::Region}:${AWS::AccountId}:table/${PartitionsTable}"
Outputs:
  ProducerPolicyArn:
    Description: Arn of managed policy representing the permissions required to produce events on this Safya stack
    Value:
      Ref: ProducerPolicy
  ConsumerPolicyArn:
    Description: Arn of managed policy representing the permissions required to consumer events from this Safya stack
    Value:
      Ref: ConsumerPolicy
  ProducerPolicyName:
    Description: Name of managed policy representing the permissions required to produce events on this Safya stack
    Value:
      Fn::Select:
        - 1
        - Fn::Split: [ '/', { Ref: ProducerPolicy } ]
  ConsumerPolicyName:
    Description: Name of managed policy representing the permissions required to consumer events from this Safya stack
    Value:
      Fn::Select:
        - 1
        - Fn::Split: [ '/', { Ref: ConsumerPolicy } ]
  EventsTopicArn:
    Description: Topic for listening to events. Run a Safya consumer inside a lambda listening on this topic for real-time consumption
    Value:
      Ref: EventsTopic
  ConfigString:
    Description: "Safya configuration string. Use this for constructing a new Safya object: new Safya({ config: <SafyaConfig> })"
    Value:
      Fn::Sub: >
        {
          "partitionsTable": "${PartitionsTable}",
          "consumersTable": "${ConsumersTable}",
          "eventsBucket": "${EventsBucket}",
          "preferredPartitionCount": ${PreferredPartitionCount},
          "eventsTopicArn": "${EventsTopic}",
          "awsRegion": "${AWS::Region}"
        }
