AWSTemplateFormatVersion: '2010-09-09'
Description: Podcasts RSS feed generator
Parameters:
  Stack:
    Description: Stack name
    Type: String
    Default: content-api
  Stage:
    Description: Stage name
    Type: String
    Default: PROD
  VPC:
    Description: Virtual Private Cloud to run EC2 instances within
    Type: AWS::EC2::VPC::Id
  Subnets:
    Description: Subnets to run load balancer within
    Type: List<AWS::EC2::Subnet::Id>
  AMI:
    Description: AMI ID
    Type: String
  AlarmTopic:
    Description: SNS topic ARN for Cloudwatch alerts
    Type: String
Resources:
  RootRole:
    Type: AWS::IAM::Role
    Properties:
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM
      AssumeRolePolicyDocument:
        Statement:
        - Effect: Allow
          Principal:
            Service:
            - ec2.amazonaws.com
          Action:
          - sts:AssumeRole
      Path: "/"
  DescribeStackResourcePolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: describe-stack-resource
      PolicyDocument:
        Statement:
        - Effect: Allow
          Action:
          - cloudformation:DescribeStackResource
          Resource: "*"
      Roles:
      - Ref: RootRole
  DownloadConfigFromS3Policy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: download-config-from-s3
      PolicyDocument:
        Statement:
        - Effect: Allow
          Action:
          - s3:GetObject
          Resource: 
          - arn:aws:s3:::content-api-config/*
          - arn:aws:s3:::content-api-dist/*
      Roles:
      - Ref: RootRole
  CloudwatchPolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: cloudwatch-policy
      PolicyDocument:
        Statement:
        - Effect: Allow
          Action:
          - cloudwatch:*
          Resource: '*'
      Roles:
      - !Ref RootRole
  DescribeInstancesPolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyDocument:
        Statement:
          - Action: ec2:DescribeInstances
            Effect: Allow
            Resource: '*'
      PolicyName: ec2-describe-instances
      Roles:
        - !Ref RootRole
  Ec2DescribeAutoScalingGroupsPolicy:
    Type: AWS::IAM::Policy
    Properties:
      PolicyName: ec2-describe-autoscaling-groups
      PolicyDocument:
        Statement:
          - Effect: Allow
            Action:
              - autoscaling:DescribeAutoScalingGroups
              - autoscaling:DescribeAutoScalingInstances
            Resource: "*"
      Roles:
        - !Ref RootRole
  InstanceProfile:
    Type: AWS::IAM::InstanceProfile
    Properties:
      Path: "/"
      Roles:
      - Ref: RootRole
  LoadBalancer:
    Type: AWS::ElasticLoadBalancing::LoadBalancer
    Properties:
      Scheme: internet-facing
      SecurityGroups:
      - Ref: LoadBalancerSecurityGroup
      Subnets:
        Ref: Subnets
      CrossZone: true
      Listeners:
      - Protocol: HTTP
        LoadBalancerPort: '80'
        InstancePort: '9000'
      HealthCheck:
        Target: HTTP:9000/healthcheck
        Timeout: '5'
        Interval: '10'
        UnhealthyThreshold: '2'
        HealthyThreshold: '2'
  AutoscalingGroup:
    Type: AWS::AutoScaling::AutoScalingGroup
    Properties:
      VPCZoneIdentifier:
        Ref: Subnets
      AvailabilityZones:
        Fn::GetAZs: ''
      LaunchConfigurationName:
        Ref: LaunchConfig
      MinSize: '2'
      MaxSize: '20'
      HealthCheckType: ELB
      HealthCheckGracePeriod: 300
      LoadBalancerNames:
      - Ref: LoadBalancer
      Tags:
      - Key: Stage
        Value:
          Ref: Stage
        PropagateAtLaunch: 'true'
      - Key: Stack
        Value:
          Ref: Stack
        PropagateAtLaunch: 'true'
      - Key: App
        Value: podcasts-rss
        PropagateAtLaunch: 'true'
  ScaleUpPolicy:
    Type: AWS::AutoScaling::ScalingPolicy
    Properties:
      AutoScalingGroupName:
        Ref: AutoscalingGroup
      AdjustmentType: PercentChangeInCapacity
      ScalingAdjustment: '100'
      Cooldown: '60'
  ScaleDownPolicy:
    Type: AWS::AutoScaling::ScalingPolicy
    Properties:
      AutoScalingGroupName:
        Ref: AutoscalingGroup
      AdjustmentType: ChangeInCapacity
      ScalingAdjustment: "-1"
      Cooldown: '600'
  HighCPUAlarm:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmDescription: CPU utilization alarm for autoscaling
      EvaluationPeriods: '1'
      Statistic: Average
      Threshold: '10'
      AlarmActions:
      - Ref: ScaleUpPolicy
      OKActions:
      - Ref: ScaleDownPolicy
      ComparisonOperator: GreaterThanThreshold
      Dimensions:
      - Name: AutoScalingGroupName
        Value:
          Ref: AutoscalingGroup
      MetricName: CPUUtilization
      Namespace: AWS/EC2
      Period: '60'
  High5xxAlarmAlert:
    Type: AWS::CloudWatch::Alarm
    Properties:
      AlarmName: !Sub ${Stack}-podcasts-rss-${Stage}-alarm-5xx-alert
      AlarmDescription: Number of 5XXs is >= 20 per minute for a 2 minute period
      Namespace: AWS/ELB
      Dimensions:
      - Name: LoadBalancerName
        Value: !Ref LoadBalancer
      MetricName: HTTPCode_Backend_5XX
      Statistic: Sum
      ComparisonOperator: GreaterThanOrEqualToThreshold
      Threshold: '20'
      Period: '60'
      EvaluationPeriods: '2'
      AlarmActions:
      - !Ref AlarmTopic
  LaunchConfig:
    Type: AWS::AutoScaling::LaunchConfiguration
    Properties:
      ImageId:
        Ref: AMI
      AssociatePublicIpAddress: true
      SecurityGroups:
      - Ref: ApplicationSecurityGroup
      - Ref: WazuhSecurityGroup
      InstanceType: t4g.small
      IamInstanceProfile:
        Ref: InstanceProfile
      UserData:
        Fn::Base64:
          !Sub |
            #!/bin/bash -ev
            adduser --disabled-password content-api
            aws configure set region ${AWS::Region}

            cd /home/content-api
            mkdir logs
            mkdir -p /etc/gu
            aws s3 cp s3://content-api-config/podcasts-rss/${Stage}/podcasts-rss.conf /etc/gu/podcasts-rss.conf
            aws s3 cp s3://content-api-dist/${Stack}/${Stage}/podcasts-rss/podcasts-rss.service /etc/systemd/system/podcasts-rss.service
            aws s3 cp s3://content-api-dist/${Stack}/${Stage}/podcasts-rss/podcasts-rss-0.1.0-SNAPSHOT.tgz .

            tar -xvf podcasts-rss-0.1.0-SNAPSHOT.tgz
            rm podcasts-rss-0.1.0-SNAPSHOT.tgz
            mv podcasts-rss-0.1.0-SNAPSHOT podcasts-rss

            chown -R content-api /home/content-api /etc/gu
            chgrp -R content-api /home/content-api /etc/gu
            systemctl start podcasts-rss

  LoadBalancerSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Public access to the load balancer on port 80
      VpcId:
        Ref: VPC
      SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: '80'
        ToPort: '80'
        CidrIp: 0.0.0.0/0
      SecurityGroupEgress:
      - IpProtocol: tcp
        FromPort: '9000'
        ToPort: '9000'
        CidrIp: 0.0.0.0/0
  ApplicationSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: SSH and HTTP
      VpcId:
        Ref: VPC
      SecurityGroupIngress:
      - IpProtocol: tcp
        FromPort: '9000'
        ToPort: '9000'
        CidrIp: 77.91.248.0/21
      - IpProtocol: tcp
        FromPort: '9000'
        ToPort: '9000'
        SourceSecurityGroupId:
          Ref: LoadBalancerSecurityGroup
      - IpProtocol: tcp
        FromPort: '22'
        ToPort: '22'
        CidrIp: 77.91.248.0/21
  WazuhSecurityGroup:
    Type: AWS::EC2::SecurityGroup
    Properties:
      GroupDescription: Allow outbound traffic from wazuh agent to manager
      VpcId:
        !Ref VPC
      SecurityGroupEgress:
        - IpProtocol: tcp
          FromPort: 1514
          ToPort: 1515
          CidrIp: 0.0.0.0/0
Outputs:
  LoadBalancer:
    Value:
      Fn::GetAtt:
      - LoadBalancer
      - DNSName
