AWSTemplateFormatVersion: 2010-09-09 Description: >- WebServer, NGINX in ASG behind ALB (qs-1rus69tib) Metadata: cfn-lint: config: ignore_checks: - W9002 - W9003 - W9006 Parameters: DeployBucket: Description: S3 bucket containing deployment packages. Type: String MinLength: 1 DeployBucketRegion: Description: Region in which the deployment packages are stored. Type: String MinLength: 1 Default: us-east-1 WebDeployPackage: Description: S3 key package to deploy Web UI into WebServer EC2 instances Type: String InstanceType: Description: EC2 instance type Type: String Default: t3.large AllowedValues: - t3.large - t3.xlarge - t3.2xlarge - t3a.large - t3a.xlarge - t3a.2xlarge - m6i.large - m6i.xlarge - m6i.2xlarge - m6i.4xlarge - m6i.8xlarge - m6i.12xlarge - m6i.16xlarge - m6i.24xlarge - m6i.32xlarge - m6id.large - m6id.xlarge - m6id.2xlarge - m6id.4xlarge - m6id.8xlarge - m6id.12xlarge - m6id.16xlarge - m6id.24xlarge - m6id.32xlarge - m6in.large - m6in.xlarge - m6in.2xlarge - m6in.4xlarge - m6in.8xlarge - m6in.12xlarge - m6in.16xlarge - m6in.24xlarge - m6in.32xlarge - m6idn.large - m6idn.xlarge - m6idn.2xlarge - m6idn.4xlarge - m6idn.8xlarge - m6idn.12xlarge - m6idn.16xlarge - m6idn.24xlarge - m6idn.32xlarge - m6a.large - m6a.xlarge - m6a.2xlarge - m6a.4xlarge - m6a.8xlarge - m6a.12xlarge - m6a.16xlarge - m6a.24xlarge - m6a.32xlarge - m6a.48xlarge - c6i.xlarge - c6i.2xlarge - c6i.4xlarge - c6i.8xlarge - c6i.12xlarge - c6i.16xlarge - c6i.24xlarge - c6i.32xlarge - c6id.xlarge - c6id.2xlarge - c6id.4xlarge - c6id.8xlarge - c6id.12xlarge - c6id.16xlarge - c6id.24xlarge - c6id.32xlarge - c6in.xlarge - c6in.2xlarge - c6in.4xlarge - c6in.8xlarge - c6in.12xlarge - c6in.16xlarge - c6in.24xlarge - c6in.32xlarge - c6a.xlarge - c6a.2xlarge - c6a.4xlarge - c6a.8xlarge - c6a.12xlarge - c6a.16xlarge - c6a.24xlarge - c6a.32xlarge - c6a.48xlarge - r6i.large - r6i.xlarge - r6i.2xlarge - r6i.4xlarge - r6i.8xlarge - r6i.12xlarge - r6i.16xlarge - r6i.24xlarge - r6i.32xlarge - r6id.large - r6id.xlarge - r6id.2xlarge - r6id.4xlarge - r6id.8xlarge - r6id.12xlarge - r6id.16xlarge - r6id.24xlarge - r6id.32xlarge - r6in.large - r6in.xlarge - r6in.2xlarge - r6in.4xlarge - r6in.8xlarge - r6in.12xlarge - r6in.16xlarge - r6in.24xlarge - r6in.32xlarge - r6idn.large - r6idn.xlarge - r6idn.2xlarge - r6idn.4xlarge - r6idn.8xlarge - r6idn.12xlarge - r6idn.16xlarge - r6idn.24xlarge - r6idn.32xlarge - r6a.large - r6a.xlarge - r6a.2xlarge - r6a.4xlarge - r6a.8xlarge - r6a.12xlarge - r6a.16xlarge - r6a.24xlarge - r6a.32xlarge - r6a.48xlarge ConstraintDescription: must be a valid EC2 instance type. EmailAddress: Description: Email Address for notification Type: String AllowedPattern: >- ([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?) ConstraintDescription: Must be a valid email id. KeyPairName: Type: 'AWS::EC2::KeyPair::KeyName' Description: Name of an existing EC2 KeyPair. WebAccessCIDR: AllowedPattern: >- ^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))$ ConstraintDescription: CIDR block parameter must be in the form x.x.x.x/x Description: Allowed CIDR block for webserver access Type: String PrivateSubnet1ID: Description: Private Subnet Id 1 Type: 'AWS::EC2::Subnet::Id' PrivateSubnet2ID: Description: Private Subnet Id 2 Type: 'AWS::EC2::Subnet::Id' PublicSubnet1ID: Description: Public Subnet Id 1 Type: 'AWS::EC2::Subnet::Id' PublicSubnet2ID: Description: Public Subnet Id 2 Type: 'AWS::EC2::Subnet::Id' VPCID: Description: 'ID of the VPC (e.g., vpc-0343606e)' Type: 'AWS::EC2::VPC::Id' PASOEURL: Description: URL to ELB to access PASOE Type: String MinScalingInstances: Description: Minimum number of EC2 instances in ASG Type: String MaxScalingInstances: Description: Maximum number of EC2 instances in ASG Type: String Mappings: AWSAMIRegionMap: AMI: AmazonLinux2AMI: Amazon Linux 2 AMI af-south-1: AmazonLinux2AMI: ami-0c9919139cb112e21 ap-east-1: AmazonLinux2AMI: ami-0f80f09f43db27f08 ap-northeast-1: AmazonLinux2AMI: ami-07c2a88388bb80eb0 ap-northeast-2: AmazonLinux2AMI: ami-073858dcf4e30e586 ap-south-1: AmazonLinux2AMI: ami-078efad6f7ec18b8a ap-southeast-1: AmazonLinux2AMI: ami-06ebb7936bfa62864 ap-southeast-2: AmazonLinux2AMI: ami-0da25504d467cb134 ca-central-1: AmazonLinux2AMI: ami-0cbfa6bba4589dcbb eu-central-1: AmazonLinux2AMI: ami-08e415170f52d1657 eu-north-1: AmazonLinux2AMI: ami-042bae94a952c9eba eu-south-1: AmazonLinux2AMI: ami-08226db9ffa402896 eu-west-1: AmazonLinux2AMI: ami-0e23c576dacf2e3df eu-west-2: AmazonLinux2AMI: ami-0a6006bac3b9bb8d3 eu-west-3: AmazonLinux2AMI: ami-0c39e8f767a5e89c8 sa-east-1: AmazonLinux2AMI: ami-0c4bcf0e0554b7eb9 us-east-1: AmazonLinux2AMI: ami-0bef6cc322bfff646 us-east-2: AmazonLinux2AMI: ami-05842f1afbf311a43 us-west-1: AmazonLinux2AMI: ami-04669a22aad391419 us-west-2: AmazonLinux2AMI: ami-03c7c1f17ee073747 Resources: NotificationTopic: Type: 'AWS::SNS::Topic' Properties: Subscription: - Endpoint: !Ref EmailAddress Protocol: email S3AccessRole: Type: "AWS::IAM::Role" Properties: Path: "/" AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Effect: Allow Principal: Service: - 'ec2.amazonaws.com' Action: sts:AssumeRole S3AccessRolePolicy: Type: "AWS::IAM::Policy" Properties: PolicyName: S3AccessRolePolicy PolicyDocument: Version: "2012-10-17" Statement: - Effect: Allow Action: - s3:Get* - s3:List* - s3:AbortMultipartUpload Resource: - !Sub 'arn:${AWS::Partition}:s3:::${DeployBucket}/${WebDeployPackage}*' - !Sub 'arn:${AWS::Partition}:s3:::${DeployBucket}' - !Sub 'arn:${AWS::Partition}:s3:::${DeployBucket}/*' Roles: - Ref: S3AccessRole S3AccessProfile: Type: "AWS::IAM::InstanceProfile" Properties: Path: / Roles: - !Ref S3AccessRole WebServerGroup: Type: 'AWS::AutoScaling::AutoScalingGroup' Properties: VPCZoneIdentifier: - !Ref PrivateSubnet1ID - !Ref PrivateSubnet2ID LaunchConfigurationName: !Ref WebServerLaunchConfig MinSize: !Ref MinScalingInstances MaxSize: !Ref MaxScalingInstances TargetGroupARNs: - !Ref ALBTargetGroup NotificationConfigurations: - TopicARN: !Ref NotificationTopic NotificationTypes: - autoscaling:EC2_INSTANCE_LAUNCH - autoscaling:EC2_INSTANCE_LAUNCH_ERROR - autoscaling:EC2_INSTANCE_TERMINATE - autoscaling:EC2_INSTANCE_TERMINATE_ERROR Tags: - Key: Name Value: webserver PropagateAtLaunch: true CreationPolicy: ResourceSignal: Timeout: PT15M Count: '1' UpdatePolicy: AutoScalingRollingUpdate: MinInstancesInService: 1 MaxBatchSize: 1 PauseTime: PT15M WaitOnResourceSignals: true WebServerLaunchConfig: Type: 'AWS::AutoScaling::LaunchConfiguration' Metadata: # "AWS::CloudFormation::Init": # config: # awscli: # commands: # 00_set_default_s3_signature_version: # command: aws configure set default.s3.signature_version s3v4 # 01_set_default_region: # command: !Sub "aws configure set default.region '${DeployBucketRegion}'" # files: # "/install/app.tar.gz": # source: !Sub 'https://${DeployBucket}.s3.${DeployBucketRegion}.${AWS::URLSuffix}/${WebDeployPackage}' # mode: '000755' # owner: ec2-user # group: ec2-user # authentication: S3AccessCreds 'AWS::CloudFormation::Authentication': S3AccessCreds: type: S3 roleName: !Ref S3AccessRole Properties: IamInstanceProfile: !Ref S3AccessProfile KeyName: !Ref KeyPairName ImageId: !FindInMap - AWSAMIRegionMap - !Ref 'AWS::Region' - AmazonLinux2AMI InstanceType: !Ref InstanceType SecurityGroups: - !Ref WebServerSecurityGroup UserData: !Base64 'Fn::Sub': - > #!/bin/bash -x #cfn-init -v --stack ${AWS::StackName} --resource #WebServerLaunchConfig --configsets webserver_install #--region ${AWS::Region} || cfn_fail # Install WebUI mkdir -p /install aws configure set default.s3.signature_version s3v4 aws configure set default.region ${DeployRegion} aws s3 cp ${DeployPackage} /install/app.tar.gz amazon-linux-extras install nginx1 -y systemctl enable nginx systemctl start nginx mkdir -p /var/www/ ln -s /usr/share/nginx/html/ /var/www/html export OE_ENV=webserver export PASOEURL=${PASOEURL} /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource WebServerLaunchConfig --region ${AWS::Region} tar xCf /install /install/app.tar.gz /install/app/deploy.sh /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource WebServerGroup --region ${AWS::Region} #[ $(qs_status) == 0 ] && cfn_success || cfn_fail - DeployRegion: !Ref DeployBucketRegion DeployPackage: !Sub 's3://${DeployBucket}/${WebDeployPackage}' WebServerScaleUpPolicy: Type: 'AWS::AutoScaling::ScalingPolicy' Properties: AdjustmentType: ChangeInCapacity AutoScalingGroupName: !Ref WebServerGroup Cooldown: '60' ScalingAdjustment: 1 WebServerScaleDownPolicy: Type: 'AWS::AutoScaling::ScalingPolicy' Properties: AdjustmentType: ChangeInCapacity AutoScalingGroupName: !Ref WebServerGroup Cooldown: '60' ScalingAdjustment: -1 WebServerCPUAlarmHigh: Type: 'AWS::CloudWatch::Alarm' Properties: AlarmDescription: Scale-up if CPU > 90% for 10 minutes MetricName: CPUUtilization Namespace: AWS/EC2 Statistic: Average Period: 300 EvaluationPeriods: 2 Threshold: 90 AlarmActions: - !Ref WebServerScaleUpPolicy Dimensions: - Name: AutoScalingGroupName Value: !Ref WebServerGroup ComparisonOperator: GreaterThanThreshold WebServerCPUAlarmLow: Type: 'AWS::CloudWatch::Alarm' Properties: AlarmDescription: Scale-down if CPU < 70% for 10 minutes MetricName: CPUUtilization Namespace: AWS/EC2 Statistic: Average Period: 300 EvaluationPeriods: 2 Threshold: 70 AlarmActions: - !Ref WebServerScaleDownPolicy Dimensions: - Name: AutoScalingGroupName Value: !Ref WebServerGroup ComparisonOperator: LessThanThreshold ApplicationLoadBalancer: Type: 'AWS::ElasticLoadBalancingV2::LoadBalancer' Properties: Subnets: - !Ref PublicSubnet1ID - !Ref PublicSubnet2ID SecurityGroups: - !Ref ELBWebServerSecurityGroup Tags: - Key: name Value: WebServer ASG Instance ALBListener: Type: 'AWS::ElasticLoadBalancingV2::Listener' Properties: DefaultActions: - Type: forward TargetGroupArn: !Ref ALBTargetGroup LoadBalancerArn: !Ref ApplicationLoadBalancer Port: 80 Protocol: HTTP ALBTargetGroup: Type: 'AWS::ElasticLoadBalancingV2::TargetGroup' Properties: HealthCheckIntervalSeconds: 30 HealthCheckTimeoutSeconds: 5 HealthyThresholdCount: 3 Port: 80 Protocol: HTTP UnhealthyThresholdCount: 5 VpcId: !Ref VPCID WebServerSecurityGroup: Type: 'AWS::EC2::SecurityGroup' Metadata: cfn_nag: rules_to_suppress: - id: F1000 reason: "Standard Amazon practice" Properties: GroupDescription: Enable access to the WebServer EC2 Instance SecurityGroupIngress: - IpProtocol: tcp FromPort: 22 ToPort: 22 CidrIp: '0.0.0.0/0' - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: '0.0.0.0/0' VpcId: !Ref VPCID ELBWebServerSecurityGroup: Type: 'AWS::EC2::SecurityGroup' Metadata: cfn_nag: rules_to_suppress: - id: F1000 reason: "Standard Amazon practice" Properties: GroupDescription: Enable HTTP to the load balancer SecurityGroupIngress: - IpProtocol: tcp FromPort: 80 ToPort: 80 CidrIp: !Ref WebAccessCIDR VpcId: !Ref VPCID Outputs: URL: Description: The URL of WebServer Value: !Join - '' - - 'http://' - !GetAtt - ApplicationLoadBalancer - DNSName