cloudformation.yaml (286 lines of code) (raw):

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