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