AWSTemplateFormatVersion: "2010-09-09"
Description: >-
  Scheduled Job Monitor for AWS - connects this account. Creates a read-only role our service can assume
  (with your External ID) and forwards ECS task, AWS Batch job and Step Functions execution state changes
  to the monitor. Deploy once per region that runs scheduled jobs (CreateRole=true in the first region only).

Parameters:
  ExternalId:
    Type: String
    Description: External ID shown on your Connect page. Only requests carrying it can assume the role.
    MinLength: 8
  TrustedAccountId:
    Type: String
    Description: AWS account of the monitoring service (pre-filled).
    AllowedPattern: "[0-9]{12}"
  IngestUrl:
    Type: String
    Description: Event endpoint of the monitoring service (pre-filled).
    AllowedPattern: "https://.+"
  IngestKey:
    Type: String
    NoEcho: true
    Description: Your workspace's event key (pre-filled).
    MinLength: 20
  CreateRole:
    Type: String
    AllowedValues: ["true", "false"]
    Default: "true"
    Description: Create the cross-account role (IAM is global, so only in the first region you deploy to).
  AllowStopTask:
    Type: String
    AllowedValues: ["true", "false"]
    Default: "false"
    Description: Let the monitor stop ECS tasks that run longer than the limit you set (only for jobs where you turn auto-stop on).

Conditions:
  MakeRole: !Equals [!Ref CreateRole, "true"]
  MakeStop: !And [!Condition MakeRole, !Equals [!Ref AllowStopTask, "true"]]

Resources:
  MonitorRole:
    Type: AWS::IAM::Role
    Condition: MakeRole
    Properties:
      Description: Read-only access for Scheduled Job Monitor for AWS
      MaxSessionDuration: 3600
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal: {AWS: !Sub "arn:${AWS::Partition}:iam::${TrustedAccountId}:root"}
            Action: sts:AssumeRole
            Condition: {StringEquals: {"sts:ExternalId": !Ref ExternalId}}
      Policies:
        - PolicyName: read-schedules-and-runs
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action:
                  - events:ListRules
                  - events:DescribeRule
                  - events:ListTargetsByRule
                  - scheduler:ListSchedules
                  - scheduler:GetSchedule
                  - scheduler:ListScheduleGroups
                  - cloudwatch:GetMetricData
                  - ecs:DescribeTasks
                  - ecs:ListTasks
                  - batch:DescribeJobs
                  - states:DescribeExecution
                  - lambda:GetFunctionConfiguration
                Resource: "*"

  StopTaskPolicy:
    Type: AWS::IAM::RolePolicy
    Condition: MakeStop
    Properties:
      RoleName: !Ref MonitorRole
      PolicyName: stop-overrunning-tasks
      PolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Action: ecs:StopTask
            Resource: "*"

  IngestConnection:
    Type: AWS::Events::Connection
    Properties:
      Description: Scheduled Job Monitor event key
      AuthorizationType: API_KEY
      AuthParameters:
        ApiKeyAuthParameters:
          ApiKeyName: x-jobmon-key
          ApiKeyValue: !Ref IngestKey

  IngestDestination:
    Type: AWS::Events::ApiDestination
    Properties:
      ConnectionArn: !GetAtt IngestConnection.Arn
      InvocationEndpoint: !Ref IngestUrl
      HttpMethod: POST
      InvocationRateLimitPerSecond: 20

  ForwardRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal: {Service: events.amazonaws.com}
            Action: sts:AssumeRole
      Policies:
        - PolicyName: invoke-monitor
          PolicyDocument:
            Version: "2012-10-17"
            Statement:
              - Effect: Allow
                Action: events:InvokeApiDestination
                Resource: !GetAtt IngestDestination.Arn

  # Scheduled/one-off ECS tasks only: tasks started by ECS services (startedBy ecs-svc/...) are not forwarded.
  EcsTaskRule:
    Type: AWS::Events::Rule
    Properties:
      Description: Forward ECS task state changes (not service tasks) to Scheduled Job Monitor
      EventPattern:
        source: ["aws.ecs"]
        detail-type: ["ECS Task State Change"]
        detail:
          lastStatus: ["PENDING", "RUNNING", "STOPPED"]
          startedBy: [{"anything-but": {"prefix": "ecs-svc/"}}]
      Targets:
        - Id: monitor
          Arn: !GetAtt IngestDestination.Arn
          RoleArn: !GetAtt ForwardRole.Arn
          RetryPolicy: {MaximumRetryAttempts: 20, MaximumEventAgeInSeconds: 3600}

  JobRule:
    Type: AWS::Events::Rule
    Properties:
      Description: Forward AWS Batch and Step Functions state changes to Scheduled Job Monitor
      EventPattern:
        source: ["aws.batch", "aws.states"]
        detail-type: ["Batch Job State Change", "Step Functions Execution Status Change"]
      Targets:
        - Id: monitor
          Arn: !GetAtt IngestDestination.Arn
          RoleArn: !GetAtt ForwardRole.Arn
          RetryPolicy: {MaximumRetryAttempts: 20, MaximumEventAgeInSeconds: 3600}

Outputs:
  RoleArn:
    Condition: MakeRole
    Description: Paste this into the Connect page.
    Value: !GetAtt MonitorRole.Arn
