AWSTemplateFormatVersion: 2010-09-09 Description: >- Ant Media Server AutoScaling CloudFormation Templates. If you have any questions, please just drop a line to contact (at) antmedia.io Parameters: VpcId: Type: 'AWS::EC2::VPC::Id' Description: VpcId of your existing Virtual Private Cloud (VPC) ConstraintDescription: must be the VPC Id of an existing Virtual Private Cloud. Subnets: Type: 'List' Description: The list of SubnetIds in your Virtual Private Cloud (VPC) ConstraintDescription: >- must be a list of at least two existing subnets associated with at least two different availability zones. They should be residing in the selected Virtual Private Cloud. EdgeInstanceType: Description: Ant Media Server Edge EC2 instance type Type: String Default: c5.xlarge AllowedValues: - t2.large - t2.xlarge - t2.2xlarge - m3.large - m3.xlarge - m3.2xlarge - m4.large - m4.xlarge - m4.2xlarge - m4.4xlarge - m4.10xlarge - m4.16xlarge - m5.large - m5.xlarge - m5.2xlarge - m5.4xlarge - m5.12xlarge - m5.24xlarge - c3.large - c3.xlarge - c3.2xlarge - c3.4xlarge - c3.8xlarge - c4.large - c4.xlarge - c4.2xlarge - c4.4xlarge - c4.8xlarge - c5.large - c5.xlarge - c5.2xlarge - c5.4xlarge - c5.9xlarge - c5.12xlarge - c5.18xlarge - c5.24xlarge - c5d.large - c5d.xlarge - c5d.2xlarge - c5d.4xlarge - c5d.9xlarge - c5d.18xlarge - c5n.large - c5n.xlarge - c5n.2xlarge - c5n.4xlarge - c5n.9xlarge - c5n.18xlarge - r3.large - r3.xlarge - r3.2xlarge - r3.4xlarge - r3.8xlarge ConstraintDescription: must be a valid EC2 instance type. OriginInstanceType: Description: Ant Media Server Origin EC2 instance type Type: String Default: c5.xlarge AllowedValues: - t2.large - t2.xlarge - t2.2xlarge - m3.large - m3.xlarge - m3.2xlarge - m4.large - m4.xlarge - m4.2xlarge - m4.4xlarge - m4.10xlarge - m4.16xlarge - m5.large - m5.xlarge - m5.2xlarge - m5.4xlarge - m5.12xlarge - m5.24xlarge - c3.large - c3.xlarge - c3.2xlarge - c3.4xlarge - c3.8xlarge - c4.large - c4.xlarge - c4.2xlarge - c4.4xlarge - c4.8xlarge - c5.large - c5.xlarge - c5.2xlarge - c5.4xlarge - c5.9xlarge - c5.12xlarge - c5.18xlarge - c5.24xlarge - c5d.large - c5d.xlarge - c5d.2xlarge - c5d.4xlarge - c5d.9xlarge - c5d.18xlarge - c5n.large - c5n.xlarge - c5n.2xlarge - c5n.4xlarge - c5n.9xlarge - c5n.18xlarge - r3.large - r3.xlarge - r3.2xlarge - r3.4xlarge - r3.8xlarge ConstraintDescription: must be a valid EC2 instance type. MongoDBInstanceType: Description: Ant Media MongoDB EC2 instance type Type: String Default: c5.xlarge AllowedValues: - c5.large - c5.xlarge - c5.2xlarge - c5.4xlarge - c5.9xlarge - c5.18xlarge - m5.large - m5.xlarge - m5.2xlarge - m5.4xlarge - m5.12xlarge - m5.24xlarge - r5.large - r5.xlarge - r5.2xlarge - r5.4xlarge - r5.12xlarge - r5.24xlarge - m5a.large - m5a.xlarge - m5a.2xlarge - m5a.4xlarge - m5a.12xlarge - m5a.24xlarge - r5a.large - r5a.xlarge - r5a.2xlarge - r5a.4xlarge - r5a.12xlarge - r5a.24xlarge KeyName: Description: Name of an existing EC2 KeyPair to enable SSH access to the instances Type: 'AWS::EC2::KeyPair::KeyName' MinLength: '1' MaxLength: '255' AllowedPattern: '[\x20-\x7E]*' ConstraintDescription: can contain only ASCII characters. SSHLocation: Description: The IP address range that can be used to SSH to the EC2 instances Type: String MinLength: '9' MaxLength: '18' Default: 0.0.0.0/0 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. Email: Description: EMail address to notify if there are any scaling operations 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 address. VpcCidrBlock: Description: 'If VPC values are empty, you need to create a new one VPC' Type: String MinLength: '9' MaxLength: '18' Default: 172.31.0.0/16 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. AntMediaOriginCapacity: Default: '1' Description: The initial number of Ant Media Origin instances Type: Number MinValue: '1' MaxValue: '20' AntMediaOriginCapacityMax: Default: '10' Description: The initial number of Ant Media Origin instances Type: Number ConstraintDescription: must be between 2 and 20 EC2 instances. AntMediaEdgeCapacity: Default: '2' Description: The initial number of Ant Media Edge instances Type: Number MinValue: '1' MaxValue: '20' AntMediaEdgeCapacityMax: Default: '10' Description: The initial number of Ant Media Edge instances Type: Number ConstraintDescription: must be between 2 and 20 EC2 instances. CPUPolicyTargetValue: Type: Number Default: 60.0 Description: This parameter for creates new Instances when CPU load exceed to %60. LoadBalancerCertificateArn: Description: 'Optional Amazon Resource Name (ARN) of the certificate to associate with the load balancer.' Type: String Default: '' RTMP: Description: If you want to install RTMP Load balancer, please select True Default: false Type: String AllowedValues: - true - false Conditions: CreateRTMPResources: !Equals - !Ref RTMP - true Resources: DescribeImagesRole: Type: AWS::IAM::Role Properties: AssumeRolePolicyDocument: Version: '2012-10-17' Statement: - Action: sts:AssumeRole Effect: Allow Principal: Service: lambda.amazonaws.com ManagedPolicyArns: - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole Policies: - PolicyName: DescribeImages PolicyDocument: Version: '2012-10-17' Statement: - Action: ec2:DescribeImages Effect: Allow Resource: "*" GetLatestAMI: Type: AWS::Lambda::Function Properties: Runtime: python3.6 Handler: index.handler Role: !Sub ${DescribeImagesRole.Arn} Timeout: 60 Code: ZipFile: | import boto3 import cfnresponse import json import traceback def handler(event, context): try: response = boto3.client('ec2').describe_images( Owners=[event['ResourceProperties']['Owner']], Filters=[ {'Name': 'name', 'Values': [event['ResourceProperties']['Name']]}, {'Name': 'architecture', 'Values': [event['ResourceProperties']['Architecture']]}, {'Name': 'root-device-type', 'Values': ['ebs']}, ], ) amis = sorted(response['Images'], key=lambda x: x['CreationDate'], reverse=True) id = amis[0]['ImageId'] cfnresponse.send(event, context, cfnresponse.SUCCESS, {}, id) except: traceback.print_last() cfnresponse.send(event, context, cfnresponse.FAIL, {}, "ok") AntMediaAmi: Type: Custom::FindAMI Properties: ServiceToken: !Sub ${GetLatestAMI.Arn} Owner: "679593333241" Name: "AntMedia-AWS-Marketplace-EE-*" Architecture: "x86_64" UbuntuAmi: Type: Custom::FindAMI Properties: ServiceToken: !Sub ${GetLatestAMI.Arn} Owner: "099720109477" Name: "ubuntu/images/hvm-ssd/ubuntu-bionic-18.04-amd64-server-*" Architecture: "x86_64" NotificationTopic: Type: 'AWS::SNS::Topic' Properties: Subscription: - Endpoint: !Ref Email Protocol: email RTMPLoadBalancer: Condition: CreateRTMPResources Type: AWS::ElasticLoadBalancing::LoadBalancer Properties: ConnectionDrainingPolicy: Enabled: true Timeout: 60 ConnectionSettings: IdleTimeout: 60 CrossZone: true Listeners: - InstancePort: 1935 InstanceProtocol: TCP LoadBalancerPort: 1935 Protocol: TCP Subnets: !Ref Subnets SecurityGroups: - !GetAtt [ RTMPSecurityGroup, GroupId ] RTMPSecurityGroup: Condition: CreateRTMPResources Type: AWS::EC2::SecurityGroup Properties: GroupName: Ant Media Server RTMP Load Balancer Security Group GroupDescription: Allows access VpcId: !Ref VpcId SecurityGroupIngress: - CidrIp: 0.0.0.0/0 IpProtocol: tcp FromPort: 1935 ToPort: 1935 Description: Allow 1935. Port for Origin Instances OriginGroup: Type: 'AWS::AutoScaling::AutoScalingGroup' Properties: VPCZoneIdentifier: !Ref Subnets LaunchConfigurationName: !Ref LaunchConfigOrigin MinSize: !Ref AntMediaOriginCapacity MaxSize: !Ref AntMediaOriginCapacityMax DesiredCapacity: !Ref AntMediaOriginCapacity TargetGroupARNs: - !Ref ALBTargetGroupOrigin LoadBalancerNames: - !If [CreateRTMPResources, !Ref RTMPLoadBalancer, !Ref "AWS::NoValue"] Tags: - Key: Name Value: Antmedia-Origin PropagateAtLaunch: 'true' CreationPolicy: ResourceSignal: Timeout: PT15M Count: !Ref AntMediaOriginCapacity UpdatePolicy: AutoScalingRollingUpdate: MinInstancesInService: '1' MaxBatchSize: '1' PauseTime: PT15M WaitOnResourceSignals: 'true' NotificationConfiguration: TopicARN: !Ref NotificationTopic NotificationTypes: - 'autoscaling:EC2_INSTANCE_LAUNCH' - 'autoscaling:EC2_INSTANCE_LAUNCH_ERROR' - 'autoscaling:EC2_INSTANCE_TERMINATE' - 'autoscaling:EC2_INSTANCE_TERMINATE_ERROR' LaunchConfigOrigin: Type: 'AWS::AutoScaling::LaunchConfiguration' Metadata: Comment: Install a simple application 'AWS::CloudFormation::Init': configSets: setup: - "configure_cfn" configure_cfn: files: /etc/cfn/cfn-hup.conf: content: !Sub | [main] stack=${AWS::StackId} region=${AWS::Region} verbose=true interval=5 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.LaunchConfigOrigin.Metadata.AWS::CloudFormation::Init action=/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource LaunchConfigOrigin --configsets setup --region ${AWS::Region} mode: "000400" owner: root group: root /lib/systemd/system/cfn-hup.service: content: !Sub | [Unit] Description=cfn-hup daemon [Service] Type=simple ExecStart=/opt/aws/bin/cfn-hup Restart=always [Install] WantedBy=multi-user.target mode: "000400" owner: root group: root Properties: KeyName: !Ref KeyName ImageId: !Ref AntMediaAmi SecurityGroups: - !Ref InstanceSecurityGroup InstanceType: !Ref OriginInstanceType UserData: Fn::Base64: !Sub | #!/bin/bash bash /usr/local/antmedia/change_server_mode.sh cluster ${DBInstance.PrivateIp} apt-get update -y apt-get install -y python-pip apt-get install -y python-setuptools mkdir -p /opt/aws/bin python /usr/lib/python2.7/dist-packages/easy_install.py --script-dir /opt/aws/bin https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-latest.tar.gz /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource LaunchConfigOrigin --configsets setup --region ${AWS::Region} /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource OriginGroup --region ${AWS::Region} EdgeGroup: Type: 'AWS::AutoScaling::AutoScalingGroup' Properties: VPCZoneIdentifier: !Ref Subnets LaunchConfigurationName: !Ref LaunchConfigEdge MinSize: !Ref AntMediaEdgeCapacity MaxSize: !Ref AntMediaEdgeCapacityMax DesiredCapacity: !Ref AntMediaEdgeCapacity TargetGroupARNs: - !Ref ALBTargetGroupEdge Tags: - Key: Name Value: Antmedia-Edge PropagateAtLaunch: 'true' CreationPolicy: ResourceSignal: Timeout: PT15M Count: !Ref AntMediaEdgeCapacity UpdatePolicy: AutoScalingRollingUpdate: MinInstancesInService: '1' MaxBatchSize: '1' PauseTime: PT15M WaitOnResourceSignals: 'true' NotificationConfiguration: TopicARN: !Ref NotificationTopic NotificationTypes: - 'autoscaling:EC2_INSTANCE_LAUNCH' - 'autoscaling:EC2_INSTANCE_LAUNCH_ERROR' - 'autoscaling:EC2_INSTANCE_TERMINATE' - 'autoscaling:EC2_INSTANCE_TERMINATE_ERROR' LaunchConfigEdge: Type: 'AWS::AutoScaling::LaunchConfiguration' Metadata: Comment: Install a simple application 'AWS::CloudFormation::Init': configSets: setup: - "configure_cfn" configure_cfn: files: /etc/cfn/cfn-hup.conf: content: !Sub | [main] stack=${AWS::StackId} region=${AWS::Region} verbose=true interval=5 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.LaunchConfigEdge.Metadata.AWS::CloudFormation::Init action=/opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource LaunchConfigEdge --configsets setup --region ${AWS::Region} mode: "000400" owner: root group: root /lib/systemd/system/cfn-hup.service: content: !Sub | [Unit] Description=cfn-hup daemon [Service] Type=simple ExecStart=/opt/aws/bin/cfn-hup Restart=always [Install] WantedBy=multi-user.target mode: "000400" owner: root group: root Properties: KeyName: !Ref KeyName ImageId: !Ref AntMediaAmi SecurityGroups: - !Ref InstanceSecurityGroup InstanceType: !Ref EdgeInstanceType UserData: Fn::Base64: !Sub | #!/bin/bash bash /usr/local/antmedia/change_server_mode.sh cluster ${DBInstance.PrivateIp} apt-get update apt-get install -y python-pip apt-get install -y python-setuptools mkdir -p /opt/aws/bin python /usr/lib/python2.7/dist-packages/easy_install.py --script-dir /opt/aws/bin https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-latest.tar.gz /opt/aws/bin/cfn-init -v --stack ${AWS::StackName} --resource LaunchConfigEdge --configsets setup --region ${AWS::Region} /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource EdgeGroup --region ${AWS::Region} ELBSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: GroupName: Ant Media Server LB Security Group GroupDescription: Allows access VpcId: !Ref VpcId SecurityGroupIngress: - CidrIp: 0.0.0.0/0 IpProtocol: tcp FromPort: 80 ToPort: 80 Description: Allow 80. Port for Origin Instances - CidrIp: 0.0.0.0/0 IpProtocol: tcp FromPort: 443 ToPort: 443 Description: Allow 443. Port for Origin Instances - CidrIp: 0.0.0.0/0 IpProtocol: tcp FromPort: 5080 ToPort: 5080 Description: Allow 5080. Port for Edge Instances - CidrIp: 0.0.0.0/0 IpProtocol: tcp FromPort: 5443 ToPort: 5443 Description: Allow 5443. Port for Edge Instances ApplicationLoadBalancer: Type: 'AWS::ElasticLoadBalancingV2::LoadBalancer' Properties: Subnets: !Ref Subnets SecurityGroups: - !GetAtt [ ELBSecurityGroup, GroupId ] ALBListener443: Type: 'AWS::ElasticLoadBalancingV2::Listener' Properties: Certificates: - CertificateArn: !Ref LoadBalancerCertificateArn DefaultActions: - Type: forward TargetGroupArn: !Ref ALBTargetGroupOrigin LoadBalancerArn: !Ref ApplicationLoadBalancer Port: '443' Protocol: HTTPS ALBListener5443: Type: 'AWS::ElasticLoadBalancingV2::Listener' Properties: Certificates: - CertificateArn: !Ref LoadBalancerCertificateArn DefaultActions: - Type: forward TargetGroupArn: !Ref ALBTargetGroupEdge LoadBalancerArn: !Ref ApplicationLoadBalancer Port: '5443' Protocol: HTTPS ALBListener5080: Type: 'AWS::ElasticLoadBalancingV2::Listener' Properties: DefaultActions: - Type: forward TargetGroupArn: !Ref ALBTargetGroupEdge LoadBalancerArn: !Ref ApplicationLoadBalancer Port: '5080' Protocol: HTTP ALBListener80: Type: 'AWS::ElasticLoadBalancingV2::Listener' Properties: DefaultActions: - Type: forward TargetGroupArn: !Ref ALBTargetGroupOrigin LoadBalancerArn: !Ref ApplicationLoadBalancer Port: '80' Protocol: HTTP ALBTargetGroupOrigin: Type: 'AWS::ElasticLoadBalancingV2::TargetGroup' Properties: HealthCheckIntervalSeconds: 30 HealthCheckTimeoutSeconds: 5 HealthyThresholdCount: 3 Port: 5080 Protocol: HTTP UnhealthyThresholdCount: 5 VpcId: !Ref VpcId TargetGroupAttributes: - Key: stickiness.enabled Value: 'true' - Key: stickiness.type Value: lb_cookie - Key: stickiness.lb_cookie.duration_seconds Value: '30' ALBTargetGroupEdge: Type: 'AWS::ElasticLoadBalancingV2::TargetGroup' Properties: HealthCheckIntervalSeconds: 30 HealthCheckTimeoutSeconds: 5 HealthyThresholdCount: 3 Port: 5080 Protocol: HTTP UnhealthyThresholdCount: 5 VpcId: !Ref VpcId TargetGroupAttributes: - Key: stickiness.enabled Value: 'true' - Key: stickiness.type Value: lb_cookie - Key: stickiness.lb_cookie.duration_seconds Value: '30' InstanceSecurityGroup: Type: 'AWS::EC2::SecurityGroup' Properties: GroupDescription: Enable SSH access and HTTP access on the configured port SecurityGroupIngress: - IpProtocol: tcp FromPort: '22' ToPort: '22' CidrIp: !Ref SSHLocation - IpProtocol: tcp FromPort: '5080' ToPort: '5080' CidrIp: 0.0.0.0/0 - IpProtocol: tcp FromPort: '5443' ToPort: '5443' CidrIp: 0.0.0.0/0 - IpProtocol: tcp FromPort: '1935' ToPort: '1935' CidrIp: 0.0.0.0/0 - IpProtocol: tcp FromPort: '0' ToPort: '65535' CidrIp: !Ref VpcCidrBlock - IpProtocol: udp FromPort: '5000' ToPort: '65000' CidrIp: 0.0.0.0/0 VpcId: !Ref VpcId OriginCPUPolicy: Type: AWS::AutoScaling::ScalingPolicy Properties: AutoScalingGroupName: !Ref OriginGroup PolicyType: TargetTrackingScaling TargetTrackingConfiguration: PredefinedMetricSpecification: PredefinedMetricType: ASGAverageCPUUtilization TargetValue: !Ref CPUPolicyTargetValue EdgeCPUPolicy: Type: AWS::AutoScaling::ScalingPolicy Properties: AutoScalingGroupName: !Ref EdgeGroup PolicyType: TargetTrackingScaling TargetTrackingConfiguration: PredefinedMetricSpecification: PredefinedMetricType: ASGAverageCPUUtilization TargetValue: !Ref CPUPolicyTargetValue DBEC2SecurityGroup: Type: 'AWS::EC2::SecurityGroup' Properties: VpcId: !Ref VpcId GroupDescription: MongoDB SecurityGroup SecurityGroupIngress: - IpProtocol: tcp FromPort: '27017' ToPort: '27017' CidrIp: !Ref VpcCidrBlock - IpProtocol: tcp FromPort: '22' ToPort: '22' CidrIp: 0.0.0.0/0 DBInstance: Type: 'AWS::EC2::Instance' Properties: KeyName: !Ref KeyName ImageId: !Ref UbuntuAmi InstanceType: !Ref MongoDBInstanceType SubnetId: !Select [ 0, !Ref Subnets ] SecurityGroupIds: - !GetAtt "DBEC2SecurityGroup.GroupId" Tags: - Key: Name Value: Antmedia-MongoDB UserData: Fn::Base64: !Sub | #!/bin/bash -xe wget -qO - https://www.mongodb.org/static/pgp/server-4.2.asc | sudo apt-key add - echo "deb [ arch=amd64 ] https://repo.mongodb.org/apt/ubuntu `lsb_release -cs`/mongodb-org/4.2 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-4.2.list sudo apt-get update sudo apt-get install -y mongodb-org python-pip python-setuptools sed -i 's/127.0.0.1/0.0.0.0/g' /etc/mongod.conf systemctl enable mongod systemctl restart mongod Outputs: HTTP: Description: URL of the Ant Media Server Value: !Join - '' - - 'http://' - !GetAtt - ApplicationLoadBalancer - DNSName HTTPS: Description: URL of the Ant Media Server Value: !Join - '' - - 'https://' - !GetAtt - ApplicationLoadBalancer - DNSName RTMP: Condition: CreateRTMPResources Description: RTMP URL of the Ant Media Server Value: !Join - '' - - 'rtmp://' - !GetAtt - RTMPLoadBalancer - DNSName