AWSTemplateFormatVersion: 2010-09-09
Description: CloudFormation Template for the Releem Agent.

Parameters:
  APIKey:
    Type: String
    NoEcho: true
    Default: ""
    Description: "The Releem API Key. Stores in AWS Secrets Manager."
  APIKeyArn:
    Description: The AWS Secrets Manager ARN that contains the API key for the agent.
    Type: String
    AllowedPattern: "arn:.*:secretsmanager:.*"
    Default: "arn:aws:secretsmanager:DEFAULT"
  DBID:
    Description: The RDS database ID.
    Type: String
  DBUser:
    Description: Username that Releem agent will use for connecting to your DB instance.
    Type: String
  DBPassword:
    Type: String
    NoEcho: true
    Default: ""
    Description: "The Password for the database user."
  DBPasswordArn:
    Description: The AWS Secrets Manager ARN that contains the password.
    Type: String
    AllowedPattern: "arn:.*:secretsmanager:.*"
    Default: "arn:aws:secretsmanager:DEFAULT"
  DBSSLMode:
    Description: Enable SSL connection to MySQL. Default value is "false".
    Type: String
    AllowedValues:
      - "true"
      - "false"
    Default: "false"
  DBParameterGroup:
    Description: The Parameter group name.
    Type: String    
  ClusterName:
    Description: ECS cluster name
    Type: String
    Default: AUTOGENERATED_DEFAULT
  Image:
    Description: The Releem agent image
    Default: releem/releem-agent:[version]
    Type: String
  SecurityGroupIDs:
    Description: Security groups associated with the agent. Should be allowed outbound https to 0.0.0.0/0 and connection to the database.
    Type: List<AWS::EC2::SecurityGroup::Id>
  SubnetIDs:
    Description: Subnets associated with the agent. Should support outbound internet traffic and connection to the database.
    Type: List<AWS::EC2::Subnet::Id>
  VerboseLogging:
    Description: Enable debug information in logs. Default value is "false".
    Type: String
    AllowedValues:
      - "true"
      - "false"
    Default: "false"
  QueryOptimization:
    Description: Enable collecting EXPLAIN for queryes for optimization. Default value is "false".
    Type: String
    AllowedValues:
      - "true"
      - "false"
    Default: "false"
  DBQueryOptimization:
    Type: String
    Default: ""
    Description: "A list of database names for SQL query optimization. Leave empty to optimize queries across all databases."
  IntervalCollectAllMetrics:
    Description: Interval in seconds for collecting all metrics. In most cases, leave the default value unchanged.
    Type: Number
    MinValue: 1
    Default: 43200
    
Conditions:
  CreateCluster: !Equals
    - !Ref ClusterName
    - AUTOGENERATED_DEFAULT
  CreateDBPasswordSecret: !Equals
    - !Ref DBPasswordArn
    - "arn:aws:secretsmanager:DEFAULT"
  CreateAPIKeySecret: !Equals
    - !Ref APIKeyArn
    - "arn:aws:secretsmanager:DEFAULT"

Rules:
  DBPasswordSet:
    Assertions:
      - Assert:
          Fn::Not:
            - Fn::And:
                - Fn::Equals:
                  - !Ref DBPassword
                  - ""
                - Fn::Equals:
                  - !Ref DBPasswordArn
                  - arn:aws:secretsmanager:DEFAULT
        AssertDescription: DBPassword or DBPasswordArn must be set
  APIKeySet:
    Assertions:
      - Assert:
          Fn::Not:
            - Fn::And:
                - Fn::Equals:
                  - !Ref APIKey
                  - ""
                - Fn::Equals:
                  - !Ref APIKeyArn
                  - arn:aws:secretsmanager:DEFAULT
        AssertDescription: APIKey or APIKeyArn must be set


Resources:
    Cluster:
      Type: AWS::ECS::Cluster
      Condition: CreateCluster
    Agent:
        Type: AWS::ECS::Service
        Properties: 
          Cluster: 
            !If [CreateCluster, !Ref Cluster, !Ref ClusterName]
          DesiredCount: 1
          LaunchType: FARGATE
          NetworkConfiguration: 
            AwsvpcConfiguration:
              AssignPublicIp: ENABLED
              SecurityGroups: !Ref SecurityGroupIDs
              Subnets: !Ref SubnetIDs
          PlatformVersion: "1.4.0"
          PropagateTags: TASK_DEFINITION
          ServiceName: ReleemAgentService
          TaskDefinition: !Ref AgentTaskDefintion
    
    LogGroup:
      Type: AWS::Logs::LogGroup
      Properties:
        LogGroupName: !Sub
          - ecs/releem-agent/${DBID}
          - { DBID: !Ref DBID}

    AgentTaskDefintion:
        Type: AWS::ECS::TaskDefinition
        DependsOn: LogGroup
        Properties: 
          ContainerDefinitions: 
            - 
              Name: ReleemAgent
              Environment:
                - Name: AWS_RDS_DB
                  Value: !Ref DBID
                - Name: DB_USER
                  Value: !Ref DBUser
                - Name: AWS_REGION
                  Value: !Ref AWS::Region
                - Name: RELEEM_ENV
                  Value: prod
                - Name: INSTANCE_TYPE
                  Value: aws/rds
                - Name: RELEEM_DEBUG
                  Value: !Ref VerboseLogging
                - Name: DB_SSL
                  Value: !Ref DBSSLMode                  
                - Name: RELEEM_QUERY_OPTIMIZATION
                  Value: !Ref QueryOptimization       
                - Name: AWS_RDS_PARAMETER_GROUP
                  Value: !Ref DBParameterGroup
                - Name: RELEEM_DATABASES_QUERY_OPTIMIZATION
                  Value: !Ref DBQueryOptimization
                - Name: RELEEM_INTERVAL_COLLECT_ALL_METRICS
                  Value: !Ref IntervalCollectAllMetrics
              Essential: true
              LogConfiguration:
                LogDriver: awslogs
                Options:
                  awslogs-group: !Select [6, !Split [':', !GetAtt LogGroup.Arn]]
                  awslogs-region: !Ref AWS::Region
                  awslogs-stream-prefix: ecs
              Image: !Ref Image
              Secrets:
                - Name: DB_PASSWORD
                  ValueFrom: 
                    Fn::If:
                      - CreateDBPasswordSecret
                      - !Ref DBPasswordSecret
                      - !Ref DBPasswordArn
                - Name: RELEEM_API_KEY
                  ValueFrom: 
                    Fn::If:
                      - CreateAPIKeySecret
                      - !Ref APIKeySecret
                      - !Ref APIKeyArn
          Cpu: 512
          Memory: 4096
          NetworkMode: awsvpc
          Tags: 
            - Key: Description
              Value: Container for the Releem agent
          ExecutionRoleArn: !GetAtt
            - ExecutionRole
            - Arn
          TaskRoleArn: !GetAtt 
            - TaskRole
            - Arn
    
    DBPasswordSecret:
      Type: AWS::SecretsManager::Secret
      Condition: CreateDBPasswordSecret
      Properties:
        Description: "Database password"
        SecretString: !Ref DBPassword
    
    APIKeySecret:
      Type: AWS::SecretsManager::Secret
      Condition: CreateAPIKeySecret
      Properties:
        Description: "Releem API Key"
        SecretString: !Ref APIKey

    ExecutionRole:
      Type: AWS::IAM::Role
      Properties:
        AssumeRolePolicyDocument: 
          Version: "2008-10-17"
          Statement:
            - Sid: ''
              Effect: Allow
              Principal:
                Service: ecs-tasks.amazonaws.com
              Action: 'sts:AssumeRole'         
        Policies:
          - 
            PolicyName: !Sub
              - ${StackName}-agent-execution
              - { StackName: !Ref AWS::StackName }
            PolicyDocument:
              Version: "2012-10-17"
              Statement:
                - 
                  Effect: "Allow"
                  Action:
                    - secretsmanager:GetSecretValue
                  Resource:
                    - 
                      Fn::If:
                        - CreateAPIKeySecret
                        - !Ref APIKeySecret
                        - !Sub ${APIKeyArn}*
                    -
                      Fn::If:
                        - CreateDBPasswordSecret
                        - !Ref DBPasswordSecret
                        - !Sub ${DBPasswordArn}*
                -
                  Effect: "Allow"
                  Action:
                    - ecr:GetAuthorizationToken
                    - ecr:BatchCheckLayerAvailability
                    - ecr:GetDownloadUrlForLayer
                    - ecr:BatchGetImage
                  Resource: "*"
                - 
                  Effect: "Allow"
                  Action:
                    - logs:CreateLogStream
                    - logs:PutLogEvents
                  Resource:
                    - !GetAtt
                      - LogGroup
                      - Arn

    TaskRole:
      Type: AWS::IAM::Role
      Properties:
        AssumeRolePolicyDocument: 
          Version: "2008-10-17"
          Statement:
            - Sid: ''
              Effect: Allow
              Principal:
                Service: ecs-tasks.amazonaws.com
              Action: 'sts:AssumeRole'          
        Policies:
          - 
            PolicyName: !Sub
              - ${StackName}-agent-task
              - { StackName: !Ref AWS::StackName }
            PolicyDocument:
              Version: "2012-10-17"
              Statement:
                - 
                  Effect: "Allow"
                  Action:
                    - logs:Get*
                    - rds:Describe*
                    - cloudwatch:Get*
                    - rds:ModifyDBParameterGroup
                  Resource: "*"
                  
Metadata:
  AWS::CloudFormation::Interface:
    ParameterGroups:
    - Label:
        default: Required
      Parameters:
        - APIKey
        - APIKeyArn
        - DBID
        - DBUser
        - DBPassword
        - DBPasswordArn
        - ClusterName
        - SecurityGroupIDs
        - SubnetIDs
        - Image
        - VerboseLogging
        - QueryOptimization
        - DBQueryOptimization
        - DBParameterGroup
        - DBSSLMode
        - IntervalCollectAllMetrics
