AWSTemplateFormatVersion: "2010-09-09"
Description: >-
  PagerBeauty: PagerDuty on-call widget for your monitoring dashboard

# ------- Mappings ----------------------------------------------------------- #

Mappings:
  AWSInstanceType2Arch:
    m4.large:
      Arch: HVM64
    m4.xlarge:
      Arch: HVM64
    m4.2xlarge:
      Arch: HVM64
    m4.4xlarge:
      Arch: HVM64
    m4.10xlarge:
      Arch: HVM64
    m4.16xlarge:
      Arch: HVM64
    m5.large:
      Arch: HVM64
    m5.xlarge:
      Arch: HVM64
    m5.2xlarge:
      Arch: HVM64
    m5.4xlarge:
      Arch: HVM64
    m5.8xlarge:
      Arch: HVM64
    m5.12xlarge:
      Arch: HVM64
    m5.16xlarge:
      Arch: HVM64
    m5.24xlarge:
      Arch: HVM64
    m5.metal:
      Arch: HVM64
    m5d.large:
      Arch: HVM64
    m5d.xlarge:
      Arch: HVM64
    m5d.2xlarge:
      Arch: HVM64
    m5d.4xlarge:
      Arch: HVM64
    m5d.8xlarge:
      Arch: HVM64
    m5d.12xlarge:
      Arch: HVM64
    m5d.16xlarge:
      Arch: HVM64
    m5d.24xlarge:
      Arch: HVM64
    m5d.metal:
      Arch: HVM64
    t2.nano:
      Arch: HVM64
    t2.micro:
      Arch: HVM64
    t2.small:
      Arch: HVM64
    t2.medium:
      Arch: HVM64
    t2.large:
      Arch: HVM64
    t2.xlarge:
      Arch: HVM64
    t2.2xlarge:
      Arch: HVM64
  AWSRegionArch2AMI:
    # https://aws.amazon.com/amazon-linux-ami/2018.03-release-notes/
    ap-east-1:
      HVM64: ami-f3e19982
    ap-northeast-1:
      HVM64: ami-03170618b41df9458
    ap-northeast-2:
      HVM64: ami-0761fc884501dd08f
    ap-northeast-3:
      HVM64: ami-06f86eac01b79c18d
    ap-south-1:
      HVM64: ami-0d2e8ef01c8b6708d
    ap-southeast-1:
      HVM64: ami-0131c0c47922b4c1e
    ap-southeast-2:
      HVM64: ami-081790451483a9fec
    ca-central-1:
      HVM64: ami-00a6bab89074a4d9e
    cn-north-1:
      HVM64: ami-00076fc62e7f14ef8
    cn-northwest-1:
      HVM64: ami-09405a182abd40504
    eu-central-1:
      HVM64: ami-0a0f438792b407c3d
    eu-north-1:
      HVM64: ami-bf4bc3c1
    eu-west-1:
      HVM64: ami-0dc9a8d2479a3c7d7
    eu-west-2:
      HVM64: ami-0e9a26a04cf77de3b
    eu-west-3:
      HVM64: ami-0f606357fbf7be639
    sa-east-1:
      HVM64: ami-0d532fdce993b9c55
    us-east-1:
      HVM64: ami-0e05097610fae5559
    us-east-2:
      HVM64: ami-0b00e7f461c40ed19
    us-gov-east-1:
      HVM64: ami-7db0510c
    us-gov-west-1:
      HVM64: ami-fb4d319a
    us-west-1:
      HVM64: ami-0b4a9c56e9f69e9f8
    us-west-2:
      HVM64: ami-0d1d9e1864d1494b6

# ------- Parameters --------------------------------------------------------- #

Parameters:
  KeyName:
    Description: The EC2 Key Pair to allow SSH access to the instance
    Type: 'AWS::EC2::KeyPair::KeyName'
  InstanceType:
    Description: WebServer EC2 instance type
    Type: String
    Default: t2.micro
    AllowedValues:
       # https://aws.amazon.com/amazon-linux-ami/instance-type-matrix/
       # https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-types.html
      - m4.large
      - m4.xlarge
      - m4.2xlarge
      - m4.4xlarge
      - m4.10xlarge
      - m4.16xlarge
      - m5.large
      - m5.xlarge
      - m5.2xlarge
      - m5.4xlarge
      - m5.8xlarge
      - m5.12xlarge
      - m5.16xlarge
      - m5.24xlarge
      - m5.metal
      - m5d.large
      - m5d.xlarge
      - m5d.2xlarge
      - m5d.4xlarge
      - m5d.8xlarge
      - m5d.12xlarge
      - m5d.16xlarge
      - m5d.24xlarge
      - m5d.metal
      - t2.nano
      - t2.micro
      - t2.small
      - t2.medium
      - t2.large
      - t2.xlarge
      - t2.2xlarge
    ConstraintDescription: must be a valid EC2 instance type.
  SSHLocation:
    Description: |
      The IP address range that can be used to SSH to the EC2 instances.
      To allow connections from all IPs, enter 0.0.0.0/0
    Type: String
    MinLength: '9'
    MaxLength: '18'
    AllowedPattern: '(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})'
    ConstraintDescription:  Must be a valid IP CIDR range of the form x.x.x.x/x.
  PagerbeautyPdApiKey:
    Description: |
      Prefer Read-Only key.
      Docs: https://support.pagerduty.com/docs/using-the-api
    Type: String
    NoEcho: True
  PagerbeautyPdSchedules:
    Description: |
      Comma-separated list of PagerDuty schedule ids.
      You can find schedule id in the URL of the schedule on PagerDuty website after symbol #.
      For example, schedule https://example.pagerduty.com/schedules#PJ1P5JQ has id PJ1P5JQ
    Type: CommaDelimitedList
  PagerbeautyHTTPUser:
    Description: (optional)
    Type: String
  PagerbeautyHTTPPassword:
    Description: (optional)
    Type: String
    NoEcho: True
  PagerbeautyHTTPAccessToken:
    Description: |
      (optional) Random characters and digits of your choice.
      Makes possible embedding PagerBeauty in browsers that don't support
      iframes with basic HTTP authentication enabled
    Type: String
    NoEcho: True

# ------- Metadata ----------------------------------------------------------- #
Metadata:
  'AWS::CloudFormation::Interface':
    ParameterGroups:
      - Label:
          default: "EC2 Configuration"
        Parameters:
          - InstanceType
          - KeyName
          - SSHLocation
      - Label:
          default: "Pager Beauty: Settings"
        Parameters:
          - PagerbeautyPdApiKey
          - PagerbeautyPdSchedules

      - Label:
          default: "Pager Beauty: Enable HTTP Authentication (Optional)"
        Parameters:
          - PagerbeautyHTTPUser
          - PagerbeautyHTTPPassword
          - PagerbeautyHTTPAccessToken

    ParameterLabels:
      InstanceType:
        default: Instance Type
      KeyName:
        default: Key Name
      SSHLocation:
        default: SSH Location
      # Pagerbeauty: PagerDuty settings
      PagerbeautyPdApiKey:
        default: PagerDuty REST API v2 Access Key
      PagerbeautyPdSchedules:
        default: PagerDuty Schedule ids
      # Pagerbeauty HTTP Authentication
      PagerbeautyHTTPUser:
        default: "HTTP authentication: user name"
      PagerbeautyHTTPPassword:
        default: "HTTP authentication: password"
      PagerbeautyHTTPAccessToken:
        default: HTTP authentication access token

# ------- Resources ---------------------------------------------------------- #
Resources:

  # ------ Security Group ------
  PagerBeautySecurityGroup:
    Type: 'AWS::EC2::SecurityGroup'
    Properties:
      GroupDescription: Enable HTTP 80 and SSH 22
      SecurityGroupIngress:
        - IpProtocol: tcp
          FromPort: 80
          ToPort: 80
          CidrIp: 0.0.0.0/0
        - IpProtocol: tcp
          FromPort: 22
          ToPort: 22
          CidrIp: !Ref SSHLocation

  # ------ Elastic IP ------
  PagerBeautyEIP:
    Type: AWS::EC2::EIP
    Properties:
      InstanceId: !Ref PagerBeautyEC2


  # ------ PagerBeauty App Server ------
  PagerBeautyEC2:
    Type: AWS::EC2::Instance
    Properties:
      InstanceType: !Ref InstanceType
      SecurityGroups:
        - !Ref PagerBeautySecurityGroup
      KeyName: !Ref KeyName
      ImageId: !FindInMap
        - AWSRegionArch2AMI
        - !Ref 'AWS::Region'
        - !FindInMap
          - AWSInstanceType2Arch
          - !Ref InstanceType
          - Arch
      UserData:
        Fn::Base64:
          !Sub |
            #!/bin/bash -ex
            yum update -y aws-cfn-bootstrap

            # Install the files and packages from the metadata
            /opt/aws/bin/cfn-init -v \
              --stack ${AWS::StackName} \
              --resource PagerBeautyEC2 \
              --region ${AWS::Region} \
              --configsets install_pagerbeauty

            /opt/aws/bin/cfn-signal -e $? \
              --stack ${AWS::StackName} \
              --resource PagerBeautyEC2 \
              --region ${AWS::Region}
    Metadata:
      'AWS::CloudFormation::Init':
        configSets:
          install_pagerbeauty:
            - install_cfn
            - add_nodejs_official_repos
            - install_nodejs
            - download_pagerbeauty
            - config_pagerbeauty
            - install_pagerbeauty

        # ------ CFN -----------------------------------------------------------
        install_cfn:
          files:
            /etc/cfn/cfn-hup.conf:
              content: !Sub |
                [main]
                stack=${AWS::StackId}
                region=${AWS::Region}
                interval=1
              mode: '000400'
              owner: root
              group: root
            /etc/cfn/hooks.d/cfn-auto-reloader.conf:
              content: !Sub |
                [cfn-auto-reloader-hook]
                triggers=post.update
                path=Resources.LaunchConfig.Metadata.AWS::CloudFormation::Init
                action=/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource LaunchConfig --configsets wordpress_install --region ${AWS::Region}
                runas=root
              mode: "000400"
              owner: "root"
              group: "root"
          services:
            sysvinit:
              cfn-hup:
                enabled: true
                ensureRunning: true
                files:
                  - /etc/cfn/cfn-hup.conf
                  - /etc/cfn/hooks.d/cfn-auto-reloader.conf

        # ------ Nodejs --------------------------------------------------------
        add_nodejs_official_repos:
          # https://nodejs.org/en/download/package-manager ->
          # https://github.com/nodesource/distributions/blob/master/README.md
          commands:
            00_nodejs_repo:
              command: curl -sL https://rpm.nodesource.com/setup_12.x | bash -
            01_yarn_repo:
              command: curl -sL https://dl.yarnpkg.com/rpm/yarn.repo | tee /etc/yum.repos.d/yarn.repo

        install_nodejs:
          # Must be executed after add_nodejs_official_repos
          packages:
            yum:
              nodejs: []
              yarn: []

        # ------ Pagerbeauty ---------------------------------------------------
        download_pagerbeauty:
          # Run the app as a user with minimal privileges
          users:
            pagerbeauty:
              homeDir: /opt/pagerbeauty
          # Download pagerbeauty build from github
          sources:
            /opt/pagerbeauty: https://github.com/sergiitk/pagerbeauty/archive/v1.1.1.tar.gz
          commands:
            # Link pagerbeauty versioned folder to app path ans
            # setup pagerbeauty user permissions to its home
            00_setup_home_folder:
              command: |
                ln -nvsf /opt/pagerbeauty/pagerbeauty-1.1.1 /opt/pagerbeauty/app
                chown -R pagerbeauty:pagerbeauty /opt/pagerbeauty

        config_pagerbeauty:
          files:
            '/opt/pagerbeauty/app/.env':
              mode: "000640"
              owner: "pagerbeauty"
              group: "pagerbeauty"
              context:
                PagerbeautyPdApiKey: !Ref 'PagerbeautyPdApiKey'
                PagerbeautyPdSchedules: !Join
                  - ','
                  - !Ref 'PagerbeautyPdSchedules'
                PagerbeautyHTTPUser: !Ref 'PagerbeautyHTTPUser'
                PagerbeautyHTTPPassword: !Ref 'PagerbeautyHTTPPassword'
                PagerbeautyHTTPAccessToken: !Ref 'PagerbeautyHTTPAccessToken'
              content: |
                # For Docker compatibility, do not placed quotation marks around the values.
                # https://docs.docker.com/compose/env-file/

                # PagerDuty REST API v2 Access Key (Read-only)
                # Docs: https://support.pagerduty.com/docs/using-the-api
                PAGERBEAUTY_PD_API_KEY={{PagerbeautyPdApiKey}}

                # Comma-separated list of PagerDuty schedule ids
                # You can find schedule id in the URL of the schedule on PagerDuty website after symbol #
                # For example, schedule https://example.pagerduty.com/schedules#PJ1P5JQ has id PJ1P5JQ
                PAGERBEAUTY_PD_SCHEDULES={{PagerbeautyPdSchedules}}

                # (Optional) How often to refresh the schedules, in minutes.
                # Default: 10.
                # PAGERBEAUTY_REFRESH_RATE_MINUTES=10

                # (Optional) Disable polling for active incidents.
                # Default: false
                # PAGERBEAUTY_INCIDENTS_DISABLE=true

                # (Optional) How often to refresh active incidents, in minutes.
                # Default: 1
                # PAGERBEAUTY_INCIDENTS_REFRESH_RATE_MINUTES=5

                # (Optional) Highest logging level to include into application logs.
                # One of: error, warn, info, verbose, debug, silly
                # Default: info
                # PAGERBEAUTY_LOG_LEVEL=verbose
                PAGERBEAUTY_LOG_LEVEL=verbose

                # (Optional) Log format. One of:
                # machine - Machine-readable JSON format
                # human   - Human-readable colorized format
                # Default: resolved to `human` for development and `machine` for production.
                # PAGERBEAUTY_LOG_FORMAT=machine
                PAGERBEAUTY_LOG_FORMAT=human

                # (Optional) The port for HTTP server to listen on.
                # Default: 8080
                # PAGERBEAUTY_HTTP_PORT=80

                # (Optional) Enable basic HTTP authentication
                # Default: disabled
                # PAGERBEAUTY_HTTP_USER=basic_username
                # PAGERBEAUTY_HTTP_PASSWORD=basic_password
                PAGERBEAUTY_HTTP_USER={{PagerbeautyHTTPUser}}
                PAGERBEAUTY_HTTP_PASSWORD={{PagerbeautyHTTPPassword}}

                # (Optional) Enable authentication access token (RFC6750)
                # Note: embedding iframes that link to a page with basic HTTP name/password
                # authentication is not supported by most modern browsers. To bypass it, you
                # can set random access_token and append it to schedule URL. For example, if you can't embed schedule
                # https://pb.example.com/v1/schedules/P538IZH.html, you can append your access token like so:
                # https://pb.example.com/v1/schedules/P538IZH.html?acccess_token=your_token
                # This link is embeddable now. Please use HTTPS.
                # Default: disabled
                # PAGERBEAUTY_HTTP_ACCESS_TOKEN=your_token
                PAGERBEAUTY_HTTP_ACCESS_TOKEN={{PagerbeautyHTTPAccessToken}}
          commands:
            # Redirect port 80 to port 8080 to avoid granting unprivileged users low ports binding
            00_redirect_port_80:
              command: |
                iptables -A PREROUTING -t nat -i eth0 -p tcp --dport 80 -j REDIRECT --to-port 8080
                iptables -t nat -A OUTPUT -o lo -p tcp --dport 80 -j REDIRECT --to-port 8080
                service iptables save

        install_pagerbeauty:
          commands:
            00_install_modules:
              command: |
                cd /opt/pagerbeauty/app
                sudo -u pagerbeauty yarn install --prod --frozen-lockfile
                sudo -u pagerbeauty yarn cache clean
            01_start_and_setup_sysvinit_script:
              command: |
                npm install -g pm2
                cd /opt/pagerbeauty/app
                sudo -u pagerbeauty pm2 start "yarn app:prod" --name "pagerbeauty"
                sudo -u pagerbeauty pm2 save
                env PATH=$PATH:/usr/bin /usr/lib/node_modules/pm2/bin/pm2 startup systemv -u pagerbeauty --hp /opt/pagerbeauty
          services:
            sysvinit:
              pm2-pagerbeauty:
                enabled: true
                ensureRun: true