From 32f64751cb828fb47a65c397921c9c1d139c7145 Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Mon, 4 Mar 2024 01:43:18 +0300 Subject: [PATCH 01/40] Aws Custom Cluster Template --- aws-custom-cluster/template.yaml | 540 +++++++++++++++++++++++++++++++ 1 file changed, 540 insertions(+) create mode 100644 aws-custom-cluster/template.yaml diff --git a/aws-custom-cluster/template.yaml b/aws-custom-cluster/template.yaml new file mode 100644 index 00000000..2b8a4eac --- /dev/null +++ b/aws-custom-cluster/template.yaml @@ -0,0 +1,540 @@ +AWSTemplateFormatVersion: '2010-09-09' + +Description: Ant Media Server - AWS API Gateway, ASG, Lambda +Parameters: + KeyName: + Description: Name of an existing EC2 KeyPair to enable SSH access to the AWS Elastic + Beanstalk instance + Type: AWS::EC2::KeyPair::KeyName + ConstraintDescription: must be the name of an existing EC2 KeyPair. + SslCertificate: + Type: String + Description: "AWS ARN to ACM generated SSL certificate." + +Resources: + + AntMediaVPC: + Type: AWS::EC2::VPC + Properties: + CidrBlock: 10.0.0.0/16 + EnableDnsHostnames: true + EnableDnsSupport: true + Tags: + - Key: Name + Value: !Sub ${AWS::StackName}-AntMedia-VPC + + OriginZone: + Type: AWS::EC2::Subnet + DependsOn: AntMediaVPC + Properties: + VpcId: !Ref AntMediaVPC + CidrBlock: 10.0.1.0/24 + MapPublicIpOnLaunch: true + AvailabilityZone: + Fn::Select: + - 0 + - Fn::GetAZs: "" + Tags: + - Key: Name + Value: !Sub ${AWS::StackName}-AntMedia-Origin-Subnet + + EdgeZone: + Type: AWS::EC2::Subnet + DependsOn: AntMediaVPC + Properties: + VpcId: !Ref AntMediaVPC + CidrBlock: 10.0.2.0/24 + MapPublicIpOnLaunch: true + AvailabilityZone: + Fn::Select: + - 1 + - Fn::GetAZs: "" + Tags: + - Key: Name + Value: !Sub ${AWS::StackName}-AntMedia-Edge-Subnet + + DefaultGateway: + Type: AWS::EC2::InternetGateway + + InternetGatewayAttachment: + Type: AWS::EC2::VPCGatewayAttachment + Properties: + InternetGatewayId: !Ref DefaultGateway + VpcId: !Ref AntMediaVPC + + RouteTable: + Type: AWS::EC2::RouteTable + Properties: + VpcId: !Ref AntMediaVPC + Tags: + - Key: Name + Value: !Sub ${AWS::StackName}-AntMedia-Route-Table + + DefaultRoute: + Type: AWS::EC2::Route + DependsOn: InternetGatewayAttachment + Properties: + RouteTableId: !Ref RouteTable + GatewayId: !Ref DefaultGateway + DestinationCidrBlock: 0.0.0.0/0 + + SubnetRouteTableAssociationOrigin: + Type: AWS::EC2::SubnetRouteTableAssociation + Properties: + RouteTableId: !Ref RouteTable + SubnetId: !Ref OriginZone + + SubnetRouteTableAssociationEdge: + Type: AWS::EC2::SubnetRouteTableAssociation + Properties: + RouteTableId: !Ref RouteTable + SubnetId: !Ref EdgeZone + + OriginGroup: + Type: 'AWS::AutoScaling::AutoScalingGroup' + DependsOn: + - LaunchTemplateOrigin + Properties: + VPCZoneIdentifier: + - !Ref OriginZone + LaunchTemplate: + LaunchTemplateName: !Sub ${AWS::StackName}-AntMedia-LaunchTemplateOrigin + Version: !GetAtt 'LaunchTemplateOrigin.LatestVersionNumber' + MinSize: 0 + MaxSize: 1 + DesiredCapacity: 0 + TargetGroupARNs: + - !Ref ALBTargetGroupOrigin + Tags: + - Key: Name + Value: Ant-Media-Server + PropagateAtLaunch: 'true' + + ELBSecurityGroup: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: Allows access + VpcId: !Ref AntMediaVPC + 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 + - CidrIp: 0.0.0.0/0 + IpProtocol: tcp + FromPort: 4444 + ToPort: 4444 + Description: Allow 4444. Port for accessing Dashboard + + ApplicationLoadBalancer: + Type: 'AWS::ElasticLoadBalancingV2::LoadBalancer' + Properties: + Subnets: + - !Ref OriginZone + - !Ref EdgeZone + SecurityGroups: + - !GetAtt [ ELBSecurityGroup, GroupId ] + + ALBTargetGroupOrigin: + Type: 'AWS::ElasticLoadBalancingV2::TargetGroup' + Properties: + HealthCheckIntervalSeconds: 30 + HealthCheckTimeoutSeconds: 5 + HealthyThresholdCount: 3 + Port: 5080 + Protocol: HTTP + UnhealthyThresholdCount: 5 + VpcId: !Ref AntMediaVPC + TargetGroupAttributes: + - Key: stickiness.enabled + Value: 'true' + - Key: stickiness.type + Value: lb_cookie + - Key: stickiness.lb_cookie.duration_seconds + Value: '30' + + ALBTargetGroupLambda: + Type: 'AWS::ElasticLoadBalancingV2::TargetGroup' + Properties: + Name: ALBTargetGroupLambda + TargetType: lambda + Targets: + - Id: !GetAtt LambdaFunctionTrigger.Arn + + ALBListener443: + Type: 'AWS::ElasticLoadBalancingV2::Listener' + Properties: + Certificates: + - CertificateArn: !Ref SslCertificate + DefaultActions: + - Type: forward + TargetGroupArn: !Ref ALBTargetGroupOrigin + LoadBalancerArn: !Ref ApplicationLoadBalancer + Port: '443' + Protocol: HTTPS + + ALBListenerRuleWithPath: + Type: AWS::ElasticLoadBalancingV2::ListenerRule + Properties: + Actions: + - Type: forward + TargetGroupArn: !Ref ALBTargetGroupLambda + Conditions: + - Field: path-pattern + Values: [ "*" ] + ListenerArn: !Ref ALBListener443 + Priority: 1 + + LaunchTemplateOrigin: + Type: 'AWS::EC2::LaunchTemplate' + Properties: + LaunchTemplateName: !Sub ${AWS::StackName}-AntMedia-LaunchTemplateOrigin + LaunchTemplateData: + InstanceType: t2.medium + KeyName: !Ref KeyName + ImageId: ami-05603669082b6ebf0 + SecurityGroupIds: + - !GetAtt "InstanceSecurityGroup.GroupId" + BlockDeviceMappings: + - DeviceName: /dev/sda1 + Ebs: + VolumeSize: 10 + VolumeType: gp2 + DeleteOnTermination: true + UserData: + Fn::Base64: !Sub | + #!/bin/bash + apt-get update + apt-get install -y python3-pip + apt-get install -y python3-setuptools + mkdir -p /opt/aws/bin + wget https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz + python3 -m easy_install --script-dir /opt/aws/bin aws-cfn-bootstrap-py3-latest.tar.gz + /opt/aws/bin/cfn-signal -e $? --stack ${AWS::StackName} --resource OriginGroup --region ${AWS::Region} + TagSpecifications: + - ResourceType: instance + Tags: + - Key: Name + Value: OriginInstance + TagSpecifications: + - ResourceType: launch-template + Tags: + - Key: Name + Value: !Sub ${AWS::StackName}-AntMedia-LaunchTemplateOrigin + + 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: 0.0.0.0/0 + - IpProtocol: tcp + FromPort: '443' + ToPort: '443' + CidrIp: 0.0.0.0/0 + - IpProtocol: tcp + FromPort: '5080' + ToPort: '5080' + CidrIp: 10.0.0.0/16 + - IpProtocol: tcp + FromPort: '1935' + ToPort: '1935' + CidrIp: 0.0.0.0/0 + - IpProtocol: udp + FromPort: '50000' + ToPort: '60000' + CidrIp: 0.0.0.0/0 + - IpProtocol: tcp + FromPort: '5443' + ToPort: '5443' + CidrIp: 0.0.0.0/0 + VpcId: !Ref AntMediaVPC + + LambdaSecurityGroup: + Type: "AWS::EC2::SecurityGroup" + Properties: + GroupName: "Lambda Security Group" + GroupDescription: "Lambda Traffic" + VpcId: !Ref AntMediaVPC + SecurityGroupEgress: + - IpProtocol: "-1" + CidrIp: "0.0.0.0/0" + + # Lambda function to get Elastic Beanstalk Load Balancer ARN + GetEBLoadBalancerArn: + Type: AWS::Lambda::Function + Properties: + Code: + ZipFile: | + import boto3 + import cfnresponse + import traceback + + def lambda_handler(event, context): + try: + elasticbeanstalk = boto3.client('elasticbeanstalk') + environment_name = event['ResourceProperties']['EnvironmentName'] + + response = elasticbeanstalk.describe_environment_resources( + EnvironmentName=environment_name + ) + + # Load balancer bilgilerini içeren listeyi al + load_balancers = response['EnvironmentResources']['LoadBalancers'] + + if load_balancers: + # İlk yük dengeleyicisinin adını al + load_balancer_name = load_balancers[0].get('Name') + if load_balancer_name: + cfnresponse.send(event, context, cfnresponse.SUCCESS, {"LoadBalancerName": load_balancer_name}, load_balancer_name) + else: + cfnresponse.send(event, context, cfnresponse.FAILED, {"Error": "Load balancer name not found."}) + else: + cfnresponse.send(event, context, cfnresponse.FAILED, {"Error": "No load balancers found for the environment."}) + + except Exception as e: + traceback.print_exc() + cfnresponse.send(event, context, cfnresponse.FAILED, {"Error": str(e)}) + Runtime: python3.12 + Handler: "index.lambda_handler" + Role: !GetAtt LambdaIamRole.Arn + Timeout: 60 + + # Custom resource to get Elastic Beanstalk Load Balancer information + # GetEBLoadBalancer: + # Type: Custom::GetEBLoadBalancer + # Properties: + # ServiceToken: !Sub ${GetEBLoadBalancerArn.Arn} + # EnvironmentName: !Ref AMSEnvironment + + # Lambda Function trigger by Cloudwatch + LambdaFunctionTrigger: + Type: AWS::Lambda::Function + Properties: + Code: + ZipFile: | + + import json, boto3, os + + def lambda_handler(event, context): + listener_arn = os.environ['LISTENER_ARN'] + new_desired_capacity = 1 + autoscaling_client = boto3.client('autoscaling') + ec2_client = boto3.client('ec2') + elb_client = boto3.client('elbv2') + # Find Auto Scaling Group names with specific prefixes + asg_names = autoscaling_client.describe_auto_scaling_groups() + asg_origin_name = [group for group in asg_names['AutoScalingGroups'] if + 'OriginGroup' in group['AutoScalingGroupName']] + asg_origin_group_names = [group['AutoScalingGroupName'] for group in asg_origin_name][0] + + autoscaling_client = boto3.client('autoscaling') + response = autoscaling_client.update_auto_scaling_group( + AutoScalingGroupName=asg_origin_group_names, + DesiredCapacity=new_desired_capacity + ) + + listener_response = elb_client.describe_rules( + ListenerArn=listener_arn + ) + + for rule in listener_response['Rules']: + if 'Priority' in rule and rule['Priority'] != 'default': + lambda_rule=rule['RuleArn'] + + delete_response = elb_client.delete_rule( + RuleArn=lambda_rule + ) + + html_content = """ + + + + + + My Lambda HTML Page + + +

Hello!

+

Your Ant Media Server Cluster will be ready soon. Please wait..

+ + + """ + + + + # HTTP yanıtı hazırla (HTML içeriği) + response = { + "statusCode": 200, + "headers": { + "Content-Type": "text/html", + }, + "body": html_content + } + + return response + Handler: "index.lambda_handler" + Role: !GetAtt LambdaIamRole.Arn + Runtime: python3.12 + Timeout: 60 + MemorySize: 128 + Environment: + Variables: + LISTENER_ARN: !Ref ALBListener443 + #TARGETGROUP_ARN: !Ref ALBTargetGroupLambda + + LambdaCloudwatchFunction: + Type: AWS::Lambda::Function + Properties: + Code: + ZipFile: | + import boto3 + + def lambda_handler(event, context): + listener_arn = os.environ['LISTENER_ARN'] + autoscaling_client = boto3.client('autoscaling') + elb_client = boto3.client('elbv2') + asg_names = autoscaling_client.describe_auto_scaling_groups() + asg_origin_name = [group for group in asg_names['AutoScalingGroups'] if + 'OriginGroup' in group['AutoScalingGroupName']] + asg_origin_group_names = [group['AutoScalingGroupName'] for group in asg_origin_name][0] + response = autoscaling_client.update_auto_scaling_group( + AutoScalingGroupName=asg_origin_group_names, + MinSize=0, + DesiredCapacity=0 + ) + + target_group_name = elb_client.describe_target_groups(Names=['ALBTargetGroupLambda']) + target_group_arn= target_group_name['TargetGroups'][0]['TargetGroupArn'] + + create_rule = elb_client.create_rule( + Actions=[ + { + 'Type': 'forward', + 'TargetGroupArn': target_group_arn + } + ], + Conditions=[ + { + 'Field': 'path-pattern', + 'Values': ['*'] + } + ], + ListenerArn=listener_arn, + Priority=1 + ) + + print(response) + + return { + 'statusCode': 200, + 'body': 'Auto Scaling Group updated successfully!' + } + #FunctionName: InstanceDeleteFunction + Handler: "index.lambda_handler" + Role: !GetAtt LambdaIamRole.Arn + Runtime: python3.12 + Timeout: 60 + MemorySize: 128 + Environment: + Variables: + AUTO_SCALING_GROUP_NAME: !Sub "${AWS::StackName}-OriginGroup" + LISTENER_ARN: !Ref ALBListener443 + + # General IAM Role for Lambda Functions + LambdaIamRole: + Type: "AWS::IAM::Role" + Properties: + RoleName: "MyLambdaExecutionRole" + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: "Allow" + Principal: + Service: "lambda.amazonaws.com" + Action: "sts:AssumeRole" + Policies: + - PolicyName: "EC2FullAccessPolicy1" + PolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: "Allow" + Action: + - "ec2:RunInstances" + - "ec2:CreateTags" + - "ec2:DescribeInstances" + - "autoscaling:UpdateAutoScalingGroup" + - "autoscaling:DescribeAutoScalingGroups" + - "elasticbeanstalk:DescribeEnvironmentResources" + - "ec2:CreateNetworkInterface" + - "ec2:DescribeNetworkInterfaces" + - "ec2:DeleteNetworkInterface" + - "elasticloadbalancing:DescribeRules" + - "elasticloadbalancing:DeleteRule" + Resource: "*" + - PolicyName: "CloudWatchLogsPolicy" + PolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: "Allow" + Action: + - "logs:CreateLogGroup" + - "logs:CreateLogStream" + - "logs:PutLogEvents" + Resource: "*" + Path: '/' + + # Lambda Resource Based Policy for Cloudwatch + LambdaInvokePermission: + Type: AWS::Lambda::Permission + Properties: + Action: lambda:InvokeFunction + FunctionName: !Ref LambdaCloudwatchFunction + Principal: lambda.alarms.cloudwatch.amazonaws.com + SourceArn: !GetAtt AutoScalingGroupScaleDownAlarm.Arn + + ELBLambdaInvokePermission: + Type: AWS::Lambda::Permission + Properties: + FunctionName: !GetAtt LambdaFunctionTrigger.Arn + Action: lambda:InvokeFunction + Principal: elasticloadbalancing.amazonaws.com + + # Cloudwatch rule to set 0 in Autoscale + AutoScalingGroupScaleDownAlarm: + Type: 'AWS::CloudWatch::Alarm' + Properties: + AlarmName: AutoScalingGroupScaleDownAlarm + ComparisonOperator: LessThanOrEqualToThreshold + EvaluationPeriods: 3 + MetricName: CPUUtilization + Namespace: AWS/EC2 + Period: 300 + Statistic: Average + Threshold: 10 + ActionsEnabled: true + AlarmActions: + - !GetAtt LambdaCloudwatchFunction.Arn + Dimensions: + - Name: AutoScalingGroupName + Value: !Ref OriginGroup + From 93f963d62dc55cd79fd8142f17130006f97fcfbd Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Mon, 4 Mar 2024 02:11:42 +0300 Subject: [PATCH 02/40] Add os module in lambda function --- aws-custom-cluster/template.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws-custom-cluster/template.yaml b/aws-custom-cluster/template.yaml index 2b8a4eac..6998f721 100644 --- a/aws-custom-cluster/template.yaml +++ b/aws-custom-cluster/template.yaml @@ -407,7 +407,7 @@ Resources: Properties: Code: ZipFile: | - import boto3 + import boto3, os def lambda_handler(event, context): listener_arn = os.environ['LISTENER_ARN'] From cb88b0203d8391cd0c34ef9b2023933b150215b6 Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Mon, 4 Mar 2024 02:12:19 +0300 Subject: [PATCH 03/40] Add Role for Lambda Function --- aws-custom-cluster/template.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/aws-custom-cluster/template.yaml b/aws-custom-cluster/template.yaml index 6998f721..37976c9b 100644 --- a/aws-custom-cluster/template.yaml +++ b/aws-custom-cluster/template.yaml @@ -490,6 +490,7 @@ Resources: - "ec2:DeleteNetworkInterface" - "elasticloadbalancing:DescribeRules" - "elasticloadbalancing:DeleteRule" + - "elasticloadbalancing:DescribeTargetGroups" Resource: "*" - PolicyName: "CloudWatchLogsPolicy" PolicyDocument: From cb41cf49c1ac45581d40ea8571e46c5a0a295706 Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Mon, 4 Mar 2024 02:13:45 +0300 Subject: [PATCH 04/40] Add CreateRule --- aws-custom-cluster/template.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/aws-custom-cluster/template.yaml b/aws-custom-cluster/template.yaml index 37976c9b..86913ae0 100644 --- a/aws-custom-cluster/template.yaml +++ b/aws-custom-cluster/template.yaml @@ -491,6 +491,7 @@ Resources: - "elasticloadbalancing:DescribeRules" - "elasticloadbalancing:DeleteRule" - "elasticloadbalancing:DescribeTargetGroups" + - "elasticloadbalancing:CreateRule" Resource: "*" - PolicyName: "CloudWatchLogsPolicy" PolicyDocument: From 2adab639b50e8cc72ee0077a71bc697791673b39 Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Wed, 6 Mar 2024 11:29:14 +0300 Subject: [PATCH 05/40] Add path based return --- aws-custom-cluster/template.yaml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/aws-custom-cluster/template.yaml b/aws-custom-cluster/template.yaml index 86913ae0..a3050cb0 100644 --- a/aws-custom-cluster/template.yaml +++ b/aws-custom-cluster/template.yaml @@ -336,6 +336,10 @@ Resources: import json, boto3, os def lambda_handler(event, context): + headers = event.get('headers', {}) + host = headers.get('host', '') + path = event.get('path', '') + full_url = f"https://{host}{path}" listener_arn = os.environ['LISTENER_ARN'] new_desired_capacity = 1 autoscaling_client = boto3.client('autoscaling') @@ -370,7 +374,7 @@ Resources: - + My Lambda HTML Page @@ -378,11 +382,9 @@ Resources:

Your Ant Media Server Cluster will be ready soon. Please wait..

- """ - + """.format(full_url=full_url) - # HTTP yanıtı hazırla (HTML içeriği) response = { "statusCode": 200, "headers": { From 86f67c63075874275cb7e46ba4dec83fcc774022 Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Thu, 7 Mar 2024 19:30:04 +0300 Subject: [PATCH 06/40] Remove Beanstalk permission --- aws-custom-cluster/template.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/aws-custom-cluster/template.yaml b/aws-custom-cluster/template.yaml index a3050cb0..52220908 100644 --- a/aws-custom-cluster/template.yaml +++ b/aws-custom-cluster/template.yaml @@ -486,7 +486,6 @@ Resources: - "ec2:DescribeInstances" - "autoscaling:UpdateAutoScalingGroup" - "autoscaling:DescribeAutoScalingGroups" - - "elasticbeanstalk:DescribeEnvironmentResources" - "ec2:CreateNetworkInterface" - "ec2:DescribeNetworkInterfaces" - "ec2:DeleteNetworkInterface" From 26af2107b58442db0aaa082c41f260e4f4f72473 Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Fri, 8 Mar 2024 18:21:41 +0300 Subject: [PATCH 07/40] Update html code --- aws-custom-cluster/template.yaml | 39 ++++++++++++++++++++++++++------ 1 file changed, 32 insertions(+), 7 deletions(-) diff --git a/aws-custom-cluster/template.yaml b/aws-custom-cluster/template.yaml index 52220908..d6b63ef7 100644 --- a/aws-custom-cluster/template.yaml +++ b/aws-custom-cluster/template.yaml @@ -369,20 +369,45 @@ Resources: RuleArn=lambda_rule ) - html_content = """ + html_content = f""" - - My Lambda HTML Page + Ant Media Server is starting - -

Hello!

-

Your Ant Media Server Cluster will be ready soon. Please wait..

+ +

Get Ready!

+

Ant Media Server is starting

+

This page will be automatically redirected soon...

+ - """.format(full_url=full_url) + """ response = { From 557195bac6f5bf065b226bb5391dd64a8b068612 Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Fri, 8 Mar 2024 19:10:48 +0300 Subject: [PATCH 08/40] Add deregistration_delay parameter --- aws-custom-cluster/template.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/aws-custom-cluster/template.yaml b/aws-custom-cluster/template.yaml index d6b63ef7..763c9029 100644 --- a/aws-custom-cluster/template.yaml +++ b/aws-custom-cluster/template.yaml @@ -168,6 +168,8 @@ Resources: Value: lb_cookie - Key: stickiness.lb_cookie.duration_seconds Value: '30' + - Key: deregistration_delay.timeout_seconds + Value: 0 ALBTargetGroupLambda: Type: 'AWS::ElasticLoadBalancingV2::TargetGroup' From bc05c7e87bcbf4b7d939cdc785dac53996c51d8a Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Fri, 8 Mar 2024 19:12:09 +0300 Subject: [PATCH 09/40] Update datapoint period --- aws-custom-cluster/template.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws-custom-cluster/template.yaml b/aws-custom-cluster/template.yaml index 763c9029..e948ce47 100644 --- a/aws-custom-cluster/template.yaml +++ b/aws-custom-cluster/template.yaml @@ -558,7 +558,7 @@ Resources: EvaluationPeriods: 3 MetricName: CPUUtilization Namespace: AWS/EC2 - Period: 300 + Period: 120 Statistic: Average Threshold: 10 ActionsEnabled: true From eef2cd9ddd1791891cca16924dc02478f2482c27 Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Wed, 13 Mar 2024 17:39:32 +0300 Subject: [PATCH 10/40] Add other instance types and marketplace image --- aws-custom-cluster/template.yaml | 125 ++++++++++++++++++++++++++++++- 1 file changed, 123 insertions(+), 2 deletions(-) diff --git a/aws-custom-cluster/template.yaml b/aws-custom-cluster/template.yaml index e948ce47..37d74d88 100644 --- a/aws-custom-cluster/template.yaml +++ b/aws-custom-cluster/template.yaml @@ -10,9 +10,130 @@ Parameters: SslCertificate: Type: String Description: "AWS ARN to ACM generated SSL certificate." + InstanceType: + 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. 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: "*" + AMSGetLatestAMI: + Type: AWS::Lambda::Function + Properties: + Runtime: python3.11 + 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( + Filters=[ + {'Name': 'product-code', 'Values': [event['ResourceProperties']['ProductId']]}, + {'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 ${AMSGetLatestAMI.Arn} + ProductId: "4wh7rhpic3wfwamyp5905tsbt" + Name: "AntMedia-AWS-Marketplace-EE-*" + Architecture: "x86_64" + AntMediaVPC: Type: AWS::EC2::VPC Properties: @@ -208,9 +329,9 @@ Resources: Properties: LaunchTemplateName: !Sub ${AWS::StackName}-AntMedia-LaunchTemplateOrigin LaunchTemplateData: - InstanceType: t2.medium + InstanceType: !Ref InstanceType KeyName: !Ref KeyName - ImageId: ami-05603669082b6ebf0 + ImageId: !Ref AntMediaAmi SecurityGroupIds: - !GetAtt "InstanceSecurityGroup.GroupId" BlockDeviceMappings: From b39ae6e7985f28c8a63e06f66b842a088030e2b3 Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Wed, 13 Mar 2024 17:43:47 +0300 Subject: [PATCH 11/40] Update Parameter of ARN --- aws-custom-cluster/template.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/aws-custom-cluster/template.yaml b/aws-custom-cluster/template.yaml index 37d74d88..79d7e037 100644 --- a/aws-custom-cluster/template.yaml +++ b/aws-custom-cluster/template.yaml @@ -3,13 +3,13 @@ AWSTemplateFormatVersion: '2010-09-09' Description: Ant Media Server - AWS API Gateway, ASG, Lambda Parameters: KeyName: - Description: Name of an existing EC2 KeyPair to enable SSH access to the AWS Elastic - Beanstalk instance + Description: Name of an existing EC2 KeyPair Type: AWS::EC2::KeyPair::KeyName ConstraintDescription: must be the name of an existing EC2 KeyPair. - SslCertificate: + LoadBalancerCertificateArn: + Description: 'Amazon Resource Name (ARN) of the certificate to associate with the load balancer. If you do not have the SSL certificate, please check this guide: https://antmedia.io/ant-media-server-cloudformation-installation/ ' Type: String - Description: "AWS ARN to ACM generated SSL certificate." + Default: '' InstanceType: Description: Ant Media Server Edge EC2 instance type Type: String @@ -304,7 +304,7 @@ Resources: Type: 'AWS::ElasticLoadBalancingV2::Listener' Properties: Certificates: - - CertificateArn: !Ref SslCertificate + - CertificateArn: !Ref LoadBalancerCertificateArn DefaultActions: - Type: forward TargetGroupArn: !Ref ALBTargetGroupOrigin From fa8dcd462217c2e235498f699dfb6649e1b061a6 Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Wed, 13 Mar 2024 17:52:18 +0300 Subject: [PATCH 12/40] Update description --- aws-custom-cluster/template.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws-custom-cluster/template.yaml b/aws-custom-cluster/template.yaml index 79d7e037..486c4d4b 100644 --- a/aws-custom-cluster/template.yaml +++ b/aws-custom-cluster/template.yaml @@ -1,6 +1,6 @@ AWSTemplateFormatVersion: '2010-09-09' -Description: Ant Media Server - AWS API Gateway, ASG, Lambda +Description: Ant Media Server - Self-Hosted Solution Parameters: KeyName: Description: Name of an existing EC2 KeyPair From ddcd08386db4c963723516cf4f7292a46295f7f4 Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Wed, 13 Mar 2024 17:54:02 +0300 Subject: [PATCH 13/40] Add output --- aws-custom-cluster/template.yaml | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/aws-custom-cluster/template.yaml b/aws-custom-cluster/template.yaml index 486c4d4b..1df1cba4 100644 --- a/aws-custom-cluster/template.yaml +++ b/aws-custom-cluster/template.yaml @@ -688,4 +688,12 @@ Resources: Dimensions: - Name: AutoScalingGroupName Value: !Ref OriginGroup - +Outputs: + EndPoint: + Description: HTTPS URL of the Ant Media Server + Value: !Join + - '' + - - 'https://' + - !GetAtt + - ApplicationLoadBalancer + - DNSName From c28a882238275469931486c6c67a01a10c5e7f22 Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Wed, 13 Mar 2024 18:44:42 +0300 Subject: [PATCH 14/40] Add CNAME output --- aws-custom-cluster/template.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/aws-custom-cluster/template.yaml b/aws-custom-cluster/template.yaml index 1df1cba4..4d0d3038 100644 --- a/aws-custom-cluster/template.yaml +++ b/aws-custom-cluster/template.yaml @@ -689,6 +689,13 @@ Resources: - Name: AutoScalingGroupName Value: !Ref OriginGroup Outputs: + DNSCnameRecord: + Description: CNAME record for your DNS Server + Value: !Join + - '' + - !GetAtt + - ApplicationLoadBalancer + - DNSName EndPoint: Description: HTTPS URL of the Ant Media Server Value: !Join @@ -697,3 +704,5 @@ Outputs: - !GetAtt - ApplicationLoadBalancer - DNSName + + From b4ecf6c31579d7de55195551a592bb503786cb3b Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Wed, 13 Mar 2024 18:50:18 +0300 Subject: [PATCH 15/40] Update Output section --- aws-custom-cluster/template.yaml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/aws-custom-cluster/template.yaml b/aws-custom-cluster/template.yaml index 4d0d3038..2e1e18f9 100644 --- a/aws-custom-cluster/template.yaml +++ b/aws-custom-cluster/template.yaml @@ -691,11 +691,7 @@ Resources: Outputs: DNSCnameRecord: Description: CNAME record for your DNS Server - Value: !Join - - '' - - !GetAtt - - ApplicationLoadBalancer - - DNSName + Value: !GetAtt ApplicationLoadBalancer.DNSName EndPoint: Description: HTTPS URL of the Ant Media Server Value: !Join From ce9553fef757d0f7c6c27ed325c3fca9d1e46599 Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Mon, 18 Mar 2024 01:12:42 +0300 Subject: [PATCH 16/40] Add Api Gateway --- aws-custom-cluster/template.yaml | 323 ++++++++++++++++++++++++------- 1 file changed, 254 insertions(+), 69 deletions(-) diff --git a/aws-custom-cluster/template.yaml b/aws-custom-cluster/template.yaml index 2e1e18f9..bf975b0e 100644 --- a/aws-custom-cluster/template.yaml +++ b/aws-custom-cluster/template.yaml @@ -331,7 +331,7 @@ Resources: LaunchTemplateData: InstanceType: !Ref InstanceType KeyName: !Ref KeyName - ImageId: !Ref AntMediaAmi + ImageId: ami-05603669082b6ebf0 SecurityGroupIds: - !GetAtt "InstanceSecurityGroup.GroupId" BlockDeviceMappings: @@ -449,99 +449,235 @@ Resources: # ServiceToken: !Sub ${GetEBLoadBalancerArn.Arn} # EnvironmentName: !Ref AMSEnvironment - # Lambda Function trigger by Cloudwatch - LambdaFunctionTrigger: + ApiGatewayApiKey: + Type: 'AWS::ApiGateway::ApiKey' + DependsOn: ApiGatewayStage + Properties: + Name: AMSAPIKey + Enabled: true + StageKeys: + - RestApiId: !Ref ApiGatewayRestApi + StageName: 'v1' + + ApiGatewayUsagePlan: + Type: 'AWS::ApiGateway::UsagePlan' + DependsOn: ApiGatewayApiKey + Properties: + ApiStages: + - ApiId: !Ref ApiGatewayRestApi + Stage: !Ref ApiGatewayStage + Description: Usage plan for API Key + Quota: + Limit: 10000 + Period: MONTH + Throttle: + BurstLimit: 500 + RateLimit: 1000 + UsagePlanName: MyUsagePlan + + ApiUsagePlanKey: + Type: 'AWS::ApiGateway::UsagePlanKey' + DependsOn: ApiGatewayUsagePlan + Properties: + KeyId: !Ref ApiGatewayApiKey + KeyType: API_KEY + UsagePlanId: !Ref ApiGatewayUsagePlan + + ApiGatewayRestApi: + Type: AWS::ApiGateway::RestApi + Properties: + ApiKeySourceType: HEADER + Description: An API Gateway with a Lambda Integration + EndpointConfiguration: + Types: + - EDGE + Name: Ant Media Server Api Gateway + + ApiGatewayCreateResource: + Type: AWS::ApiGateway::Resource + Properties: + ParentId: !GetAtt ApiGatewayRestApi.RootResourceId + PathPart: 'create' + RestApiId: !Ref ApiGatewayRestApi + + ApiGatewayCreateMethod: + Type: AWS::ApiGateway::Method + Properties: + ApiKeyRequired: true + AuthorizationType: NONE + HttpMethod: GET + Integration: + ConnectionType: INTERNET + Credentials: !GetAtt ApiGatewayIamRole.Arn + IntegrationHttpMethod: POST + TimeoutInMillis: 29000 + Type: AWS + Uri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${LambdaCreateFunction.Arn}/invocations' + IntegrationResponses: + - StatusCode: 200 + RequestTemplates: + application/json: | + ## See http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html + ## This template will pass through all parameters including path, querystring, header, stage variables, and context through to the integration endpoint via the body/payload + #set($allParams = $input.params()) + { + "body" : $input.json('$'), + "params" : { + #foreach($type in $allParams.keySet()) + #set($params = $allParams.get($type)) + "$type" : { + #foreach($paramName in $params.keySet()) + "$paramName" : "$util.escapeJavaScript($params.get($paramName))" + #if($foreach.hasNext),#end + #end + } + #if($foreach.hasNext),#end + #end + }, + "stageVariables" : { + #foreach($key in $stageVariables.keySet()) + "$key" : "$util.escapeJavaScript($stageVariables.get($key))" + #if($foreach.hasNext),#end + #end + }, + "context" : { + "accountId" : "$context.identity.accountId", + "apiId" : "$context.apiId", + "apiKey" : "$context.identity.apiKey", + "authorizerPrincipalId" : "$context.authorizer.principalId", + "caller" : "$context.identity.caller", + "cognitoAuthenticationProvider" : "$context.identity.cognitoAuthenticationProvider", + "cognitoAuthenticationType" : "$context.identity.cognitoAuthenticationType", + "cognitoIdentityId" : "$context.identity.cognitoIdentityId", + "cognitoIdentityPoolId" : "$context.identity.cognitoIdentityPoolId", + "httpMethod" : "$context.httpMethod", + "stage" : "$context.stage", + "sourceIp" : "$context.identity.sourceIp", + "user" : "$context.identity.user", + "userAgent" : "$context.identity.userAgent", + "userArn" : "$context.identity.userArn", + "requestId" : "$context.requestId", + "resourceId" : "$context.resourceId", + "resourcePath" : "$context.resourcePath" + } + } + MethodResponses: + - StatusCode: 200 + ResponseModels: + application/json: 'Empty' + RequestParameters: + method.request.querystring.name: false + OperationName: 'lambda' + ResourceId: !Ref ApiGatewayCreateResource + RestApiId: !Ref ApiGatewayRestApi + + ApiGatewayModel: + Type: AWS::ApiGateway::Model + Properties: + ContentType: 'application/json' + RestApiId: !Ref ApiGatewayRestApi + Schema: {} + + ApiGatewayStage: + Type: AWS::ApiGateway::Stage + DependsOn: ApiGatewayDeployment + Properties: + DeploymentId: !Ref ApiGatewayDeployment + Description: Lambda API Stage v1 + RestApiId: !Ref ApiGatewayRestApi + StageName: 'v1' + + ApiGatewayDeployment: + Type: AWS::ApiGateway::Deployment + DependsOn: ApiGatewayCreateMethod + Properties: + Description: Lambda API Deployment + RestApiId: !Ref ApiGatewayRestApi + + ApiGatewayIamRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Sid: '' + Effect: 'Allow' + Principal: + Service: + - 'apigateway.amazonaws.com' + Action: + - 'sts:AssumeRole' + Path: '/' + Policies: + - PolicyName: LambdaAccess + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: 'Allow' + Action: 'lambda:*' + Resource: + - !GetAtt LambdaCreateFunction.Arn + LambdaCreateFunction: Type: AWS::Lambda::Function Properties: Code: ZipFile: | - - import json, boto3, os + import boto3, os def lambda_handler(event, context): - headers = event.get('headers', {}) - host = headers.get('host', '') - path = event.get('path', '') - full_url = f"https://{host}{path}" listener_arn = os.environ['LISTENER_ARN'] new_desired_capacity = 1 autoscaling_client = boto3.client('autoscaling') ec2_client = boto3.client('ec2') elb_client = boto3.client('elbv2') + # Find Auto Scaling Group names with specific prefixes asg_names = autoscaling_client.describe_auto_scaling_groups() asg_origin_name = [group for group in asg_names['AutoScalingGroups'] if 'OriginGroup' in group['AutoScalingGroupName']] asg_origin_group_names = [group['AutoScalingGroupName'] for group in asg_origin_name][0] - + autoscaling_client = boto3.client('autoscaling') response = autoscaling_client.update_auto_scaling_group( AutoScalingGroupName=asg_origin_group_names, DesiredCapacity=new_desired_capacity ) - + listener_response = elb_client.describe_rules( - ListenerArn=listener_arn + ListenerArn=listener_arn ) for rule in listener_response['Rules']: if 'Priority' in rule and rule['Priority'] != 'default': - lambda_rule=rule['RuleArn'] - + lambda_rule = rule['RuleArn'] + delete_response = elb_client.delete_rule( - RuleArn=lambda_rule + RuleArn=lambda_rule ) - - html_content = f""" - - - - - Ant Media Server is starting - - -

Get Ready!

-

Ant Media Server is starting

-

This page will be automatically redirected soon...

- - - - """ - - - response = { - "statusCode": 200, - "headers": { - "Content-Type": "text/html", - }, - "body": html_content + + return { + 'statusCode': 200, + 'body': 'Lambda function executed successfully!' } - return response + + Description: AWS Lambda function + Handler: "index.lambda_handler" + MemorySize: 256 + Role: !GetAtt LambdaIamRole.Arn + Runtime: python3.12 + Timeout: 60 + Environment: + Variables: + LISTENER_ARN: !Ref ALBListener443 + + # Lambda Function trigger by Cloudwatch + LambdaFunctionTrigger: + Type: AWS::Lambda::Function + Properties: + Code: + S3Bucket: ams-beanstalk + S3Key: lambda.zip Handler: "index.lambda_handler" Role: !GetAtt LambdaIamRole.Arn Runtime: python3.12 @@ -549,7 +685,7 @@ Resources: MemorySize: 128 Environment: Variables: - LISTENER_ARN: !Ref ALBListener443 + API_URL: !Sub "https://${ApiGatewayRestApi}.execute-api.${AWS::Region}.amazonaws.com/" #TARGETGROUP_ARN: !Ref ALBTargetGroupLambda LambdaCloudwatchFunction: @@ -609,12 +745,60 @@ Resources: Variables: AUTO_SCALING_GROUP_NAME: !Sub "${AWS::StackName}-OriginGroup" LISTENER_ARN: !Ref ALBListener443 + LambdaGetApiKey: + Type: AWS::Lambda::Function + DependsOn: ApiGatewayApiKey + Properties: + Code: + ZipFile: | + import boto3 + import cfnresponse + import traceback + + def lambda_handler(event, context): + try: + api_key_name = "AMSAPIKey" + + client = boto3.client('apigateway') + + response = client.get_api_keys( + nameQuery=api_key_name, + includeValues=True + ) + + if response['ResponseMetadata']['HTTPStatusCode'] == 200: + api_key_value = response['items'][0]['value'] + print("API Key Value:", api_key_value) + cfnresponse.send(event, context, cfnresponse.SUCCESS, {"APIKeyValue": api_key_value}) + return {"APIKeyValue": api_key_value} + else: + cfnresponse.send(event, context, cfnresponse.FAILED, {"Error": "Failed to retrieve API key"}) + return {"Error": "Failed to retrieve API key"} + except Exception as e: + traceback.print_exc() + cfnresponse.send(event, context, cfnresponse.FAILED, {"Error": str(e)}) + return {"Error": str(e)} + #Environment: + # Variables: + # API_KEY_NAME: !Sub "${AWS::StackName}-OriginGroup" + Description: AWS Lambda function + Handler: "index.lambda_handler" + MemorySize: 256 + Role: !GetAtt LambdaIamRole.Arn + Runtime: python3.12 + Timeout: 60 + + LambdaApiValue: + Type: Custom::LambdaApiKey + DependsOn: LambdaGetApiKey + Properties: + ServiceToken: !Sub "${LambdaGetApiKey.Arn}" # General IAM Role for Lambda Functions LambdaIamRole: Type: "AWS::IAM::Role" Properties: - RoleName: "MyLambdaExecutionRole" + RoleName: !Sub "MyLambdaExecutionRole-${AWS::StackName}" AssumeRolePolicyDocument: Version: "2012-10-17" Statement: @@ -641,6 +825,7 @@ Resources: - "elasticloadbalancing:DeleteRule" - "elasticloadbalancing:DescribeTargetGroups" - "elasticloadbalancing:CreateRule" + - "apigateway:GET" Resource: "*" - PolicyName: "CloudWatchLogsPolicy" PolicyDocument: @@ -697,8 +882,8 @@ Outputs: Value: !Join - '' - - 'https://' - - !GetAtt - - ApplicationLoadBalancer - - DNSName + - !GetAtt ApplicationLoadBalancer.DNSName + - "?api_key=" + - !GetAtt LambdaApiValue.APIKeyValue From 57656721d930e7493dab2e4aed6325b4f44a7822 Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Tue, 19 Mar 2024 09:32:33 +0300 Subject: [PATCH 17/40] Add Redis Serverless Cache --- aws-custom-cluster/template.yaml | 77 +++++++++++++++++++++++--------- 1 file changed, 56 insertions(+), 21 deletions(-) diff --git a/aws-custom-cluster/template.yaml b/aws-custom-cluster/template.yaml index bf975b0e..031b4ae9 100644 --- a/aws-custom-cluster/template.yaml +++ b/aws-custom-cluster/template.yaml @@ -211,26 +211,6 @@ Resources: RouteTableId: !Ref RouteTable SubnetId: !Ref EdgeZone - OriginGroup: - Type: 'AWS::AutoScaling::AutoScalingGroup' - DependsOn: - - LaunchTemplateOrigin - Properties: - VPCZoneIdentifier: - - !Ref OriginZone - LaunchTemplate: - LaunchTemplateName: !Sub ${AWS::StackName}-AntMedia-LaunchTemplateOrigin - Version: !GetAtt 'LaunchTemplateOrigin.LatestVersionNumber' - MinSize: 0 - MaxSize: 1 - DesiredCapacity: 0 - TargetGroupARNs: - - !Ref ALBTargetGroupOrigin - Tags: - - Key: Name - Value: Ant-Media-Server - PropagateAtLaunch: 'true' - ELBSecurityGroup: Type: AWS::EC2::SecurityGroup Properties: @@ -324,6 +304,44 @@ Resources: ListenerArn: !Ref ALBListener443 Priority: 1 + RedisServerlessCache: + Type: AWS::ElastiCache::ServerlessCache + DependsOn: + - OriginGroup + Properties: + DailySnapshotTime: "03:00" + Description: "Ant Media Server - Redis Serverless Cache" + Engine: "Redis" + SecurityGroupIds: + - !GetAtt "InstanceSecurityGroup.GroupId" + ServerlessCacheName: !Sub ${AWS::StackName}-ServerlessRedis + SubnetIds: + - !Ref OriginZone + - !Ref EdgeZone + Tags: + - Key: Name + Value: RedisCache + + OriginGroup: + Type: 'AWS::AutoScaling::AutoScalingGroup' + DependsOn: + - LaunchTemplateOrigin + Properties: + VPCZoneIdentifier: + - !Ref OriginZone + LaunchTemplate: + LaunchTemplateName: !Sub ${AWS::StackName}-AntMedia-LaunchTemplateOrigin + Version: !GetAtt 'LaunchTemplateOrigin.LatestVersionNumber' + MinSize: 0 + MaxSize: 1 + DesiredCapacity: 0 + TargetGroupARNs: + - !Ref ALBTargetGroupOrigin + Tags: + - Key: Name + Value: Ant-Media-Server + PropagateAtLaunch: 'true' + LaunchTemplateOrigin: Type: 'AWS::EC2::LaunchTemplate' Properties: @@ -343,7 +361,24 @@ Resources: UserData: Fn::Base64: !Sub | #!/bin/bash - apt-get update + sudo apt-get update -y + sudo apt-get install stunnel -y + sudo tee /etc/stunnel/stunnel.conf > /dev/null < Date: Tue, 19 Mar 2024 09:34:28 +0300 Subject: [PATCH 18/40] Update Security Group for the port 5080 --- aws-custom-cluster/template.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws-custom-cluster/template.yaml b/aws-custom-cluster/template.yaml index 031b4ae9..b901423f 100644 --- a/aws-custom-cluster/template.yaml +++ b/aws-custom-cluster/template.yaml @@ -412,7 +412,7 @@ Resources: - IpProtocol: tcp FromPort: '5080' ToPort: '5080' - CidrIp: 10.0.0.0/16 + CidrIp: 0.0.0.0/0 - IpProtocol: tcp FromPort: '1935' ToPort: '1935' From f35ac8a916759676b349e1734e383f5bd48bfd78 Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Tue, 19 Mar 2024 10:27:58 +0300 Subject: [PATCH 19/40] Fix Circular dependency issue --- aws-custom-cluster/template.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/aws-custom-cluster/template.yaml b/aws-custom-cluster/template.yaml index b901423f..291c26fb 100644 --- a/aws-custom-cluster/template.yaml +++ b/aws-custom-cluster/template.yaml @@ -306,8 +306,6 @@ Resources: RedisServerlessCache: Type: AWS::ElastiCache::ServerlessCache - DependsOn: - - OriginGroup Properties: DailySnapshotTime: "03:00" Description: "Ant Media Server - Redis Serverless Cache" From dbc71f22b9e0e2c41a39c6a6606b6414a8496c71 Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Tue, 19 Mar 2024 10:28:54 +0300 Subject: [PATCH 20/40] Update security group for Redis --- aws-custom-cluster/template.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/aws-custom-cluster/template.yaml b/aws-custom-cluster/template.yaml index 291c26fb..57ceca79 100644 --- a/aws-custom-cluster/template.yaml +++ b/aws-custom-cluster/template.yaml @@ -423,6 +423,10 @@ Resources: FromPort: '5443' ToPort: '5443' CidrIp: 0.0.0.0/0 + - IpProtocol: tcp + FromPort: '6379' + ToPort: '6379' + CidrIp: 0.0.0.0/0 VpcId: !Ref AntMediaVPC LambdaSecurityGroup: From ec3a63cc27c30c1e5a9871ccce254d3f491ba721 Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Tue, 19 Mar 2024 13:38:56 +0300 Subject: [PATCH 21/40] Add ACM policy --- aws-custom-cluster/template.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/aws-custom-cluster/template.yaml b/aws-custom-cluster/template.yaml index 57ceca79..6e9ea68a 100644 --- a/aws-custom-cluster/template.yaml +++ b/aws-custom-cluster/template.yaml @@ -458,11 +458,9 @@ Resources: EnvironmentName=environment_name ) - # Load balancer bilgilerini içeren listeyi al load_balancers = response['EnvironmentResources']['LoadBalancers'] if load_balancers: - # İlk yük dengeleyicisinin adını al load_balancer_name = load_balancers[0].get('Name') if load_balancer_name: cfnresponse.send(event, context, cfnresponse.SUCCESS, {"LoadBalancerName": load_balancer_name}, load_balancer_name) @@ -863,6 +861,7 @@ Resources: - "elasticloadbalancing:DescribeTargetGroups" - "elasticloadbalancing:CreateRule" - "apigateway:GET" + - "acm:DescribeCertificate" Resource: "*" - PolicyName: "CloudWatchLogsPolicy" PolicyDocument: From fccf1c6a5b23009b464d8ccb9bf093b5d4b4682d Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Tue, 19 Mar 2024 13:40:04 +0300 Subject: [PATCH 22/40] Remove unused function (ALB) --- aws-custom-cluster/template.yaml | 45 -------------------------------- 1 file changed, 45 deletions(-) diff --git a/aws-custom-cluster/template.yaml b/aws-custom-cluster/template.yaml index 6e9ea68a..3d1a13ad 100644 --- a/aws-custom-cluster/template.yaml +++ b/aws-custom-cluster/template.yaml @@ -439,51 +439,6 @@ Resources: - IpProtocol: "-1" CidrIp: "0.0.0.0/0" - # Lambda function to get Elastic Beanstalk Load Balancer ARN - GetEBLoadBalancerArn: - Type: AWS::Lambda::Function - Properties: - Code: - ZipFile: | - import boto3 - import cfnresponse - import traceback - - def lambda_handler(event, context): - try: - elasticbeanstalk = boto3.client('elasticbeanstalk') - environment_name = event['ResourceProperties']['EnvironmentName'] - - response = elasticbeanstalk.describe_environment_resources( - EnvironmentName=environment_name - ) - - load_balancers = response['EnvironmentResources']['LoadBalancers'] - - if load_balancers: - load_balancer_name = load_balancers[0].get('Name') - if load_balancer_name: - cfnresponse.send(event, context, cfnresponse.SUCCESS, {"LoadBalancerName": load_balancer_name}, load_balancer_name) - else: - cfnresponse.send(event, context, cfnresponse.FAILED, {"Error": "Load balancer name not found."}) - else: - cfnresponse.send(event, context, cfnresponse.FAILED, {"Error": "No load balancers found for the environment."}) - - except Exception as e: - traceback.print_exc() - cfnresponse.send(event, context, cfnresponse.FAILED, {"Error": str(e)}) - Runtime: python3.12 - Handler: "index.lambda_handler" - Role: !GetAtt LambdaIamRole.Arn - Timeout: 60 - - # Custom resource to get Elastic Beanstalk Load Balancer information - # GetEBLoadBalancer: - # Type: Custom::GetEBLoadBalancer - # Properties: - # ServiceToken: !Sub ${GetEBLoadBalancerArn.Arn} - # EnvironmentName: !Ref AMSEnvironment - ApiGatewayApiKey: Type: 'AWS::ApiGateway::ApiKey' DependsOn: ApiGatewayStage From 40b55cfae1d7efcbfb64392aab25bec8ac99fe24 Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Fri, 29 Mar 2024 14:17:26 +0300 Subject: [PATCH 23/40] Remove Api Key --- aws-custom-cluster/template.yaml | 149 ++++++++++++++++++++++++++++--- 1 file changed, 138 insertions(+), 11 deletions(-) diff --git a/aws-custom-cluster/template.yaml b/aws-custom-cluster/template.yaml index 3d1a13ad..e0cda567 100644 --- a/aws-custom-cluster/template.yaml +++ b/aws-custom-cluster/template.yaml @@ -444,7 +444,7 @@ Resources: DependsOn: ApiGatewayStage Properties: Name: AMSAPIKey - Enabled: true + Enabled: false StageKeys: - RestApiId: !Ref ApiGatewayRestApi StageName: 'v1' @@ -493,7 +493,7 @@ Resources: ApiGatewayCreateMethod: Type: AWS::ApiGateway::Method Properties: - ApiKeyRequired: true + ApiKeyRequired: false AuthorizationType: NONE HttpMethod: GET Integration: @@ -666,8 +666,135 @@ Resources: Type: AWS::Lambda::Function Properties: Code: - S3Bucket: ams-beanstalk - S3Key: lambda.zip + ZipFile: | + import json + import boto3 + import os + + def lambda_handler(event, context): + headers = event.get('headers', {}) + host = headers.get('host', '') + path = event.get('path', '') + full_url = f"https://{host}{path}" + # request = event['queryStringParameters'] + # api_key = request.get('api_key', None) + api_url = os.environ['API_URL'] + "v1/create" + # api_key = os.environ['API_KEY'] + print (api_url) + + # response = requests.get(api_url, headers={'x-api-key': api_key}) + + javascript_code = """ + // success callback + var onSuccess = function(response) { + var errorDivs = document.getElementsByClassName("hcaptcha-error"); + if (errorDivs.length) { + errorDivs[0].className = ""; + } + var errorMsgs = document.getElementsByClassName("hcaptcha-error-message"); + if (errorMsgs.length) { + errorMsgs[0].parentNode.removeChild(errorMsgs[0]); + } + + var logEl = document.querySelector(".hcaptcha-success"); + if (!logEl) { + logEl = document.createElement("div"); + logEl.className = "hcaptcha-success"; + document.body.appendChild(logEl); + } + logEl.innerHTML = "This page will be automatically redirected soon..."; + + fetch(api, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + mode: 'no-cors' + }) + .then(response => { + console.log('Request successfully sent.'); + console.log(api); + console.log(api_key) + }) + .catch(error => { + console.error('Error:', error); + }); + + }; + + var onExpire = function(response) { + var logEl = document.querySelector(".hcaptcha-success"); + if (!logEl) { + logEl = document.createElement("div"); + logEl.className = "hcaptcha-success"; + document.body.appendChild(logEl); + } + logEl.innerHTML = "Token expired."; + }; + """ + + # if response.status_code == 200: + html_content = f""" + + + + + Ant Media Server will be starting + + + +

Get Ready!

+

Ant Media Server will be starting

+
+
+ +
+
+
+ +
+ + + + """ + + # else: + # html_content = "Waiting for the server to be ready" + + response = { + "statusCode": 200, + "headers": { + "Content-Type": "text/html", + }, + "body": html_content + } + + return response + Handler: "index.lambda_handler" Role: !GetAtt LambdaIamRole.Arn Runtime: python3.12 @@ -865,16 +992,16 @@ Resources: - Name: AutoScalingGroupName Value: !Ref OriginGroup Outputs: - DNSCnameRecord: - Description: CNAME record for your DNS Server - Value: !GetAtt ApplicationLoadBalancer.DNSName EndPoint: - Description: HTTPS URL of the Ant Media Server + Description: Load Balancer Address of Ant Media Server Value: !Join - '' - - 'https://' - - !GetAtt ApplicationLoadBalancer.DNSName - - "?api_key=" - - !GetAtt LambdaApiValue.APIKeyValue + - !GetAtt + - ApplicationLoadBalancer + - DNSName +# ApiKey: +# Description: API Key +# Value: !GetAtt LambdaApiValue.APIKeyValue From 77c29596502836b3d6930ba681a1c9d920be95fa Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Fri, 10 May 2024 13:39:35 +0300 Subject: [PATCH 24/40] Fix lambda functions to point correct ASG --- aws-custom-cluster/template.yaml | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/aws-custom-cluster/template.yaml b/aws-custom-cluster/template.yaml index e0cda567..cb3659da 100644 --- a/aws-custom-cluster/template.yaml +++ b/aws-custom-cluster/template.yaml @@ -616,6 +616,7 @@ Resources: def lambda_handler(event, context): listener_arn = os.environ['LISTENER_ARN'] + origin_asg = os.environ['ORIGIN_ASG'] new_desired_capacity = 1 autoscaling_client = boto3.client('autoscaling') ec2_client = boto3.client('ec2') @@ -624,7 +625,7 @@ Resources: # Find Auto Scaling Group names with specific prefixes asg_names = autoscaling_client.describe_auto_scaling_groups() asg_origin_name = [group for group in asg_names['AutoScalingGroups'] if - 'OriginGroup' in group['AutoScalingGroupName']] + origin_asg in group['AutoScalingGroupName']] asg_origin_group_names = [group['AutoScalingGroupName'] for group in asg_origin_name][0] autoscaling_client = boto3.client('autoscaling') @@ -659,7 +660,8 @@ Resources: Timeout: 60 Environment: Variables: - LISTENER_ARN: !Ref ALBListener443 + LISTENER_ARN: !Ref ALBListener443 + ORIGIN_ASG: !Sub "${AWS::StackName}-OriginGroup" # Lambda Function trigger by Cloudwatch LambdaFunctionTrigger: @@ -679,7 +681,7 @@ Resources: # request = event['queryStringParameters'] # api_key = request.get('api_key', None) api_url = os.environ['API_URL'] + "v1/create" - # api_key = os.environ['API_KEY'] + # api_key = os.environ['API_KEY'] print (api_url) # response = requests.get(api_url, headers={'x-api-key': api_key}) @@ -805,6 +807,8 @@ Resources: API_URL: !Sub "https://${ApiGatewayRestApi}.execute-api.${AWS::Region}.amazonaws.com/" #TARGETGROUP_ARN: !Ref ALBTargetGroupLambda + + LambdaCloudwatchFunction: Type: AWS::Lambda::Function Properties: @@ -814,11 +818,12 @@ Resources: def lambda_handler(event, context): listener_arn = os.environ['LISTENER_ARN'] + origin_asg = os.environ['ORIGIN_ASG'] autoscaling_client = boto3.client('autoscaling') elb_client = boto3.client('elbv2') asg_names = autoscaling_client.describe_auto_scaling_groups() asg_origin_name = [group for group in asg_names['AutoScalingGroups'] if - 'OriginGroup' in group['AutoScalingGroupName']] + origin_asg in group['AutoScalingGroupName']] asg_origin_group_names = [group['AutoScalingGroupName'] for group in asg_origin_name][0] response = autoscaling_client.update_auto_scaling_group( AutoScalingGroupName=asg_origin_group_names, @@ -826,6 +831,7 @@ Resources: DesiredCapacity=0 ) + target_group_name = elb_client.describe_target_groups(Names=['ALBTargetGroupLambda']) target_group_arn= target_group_name['TargetGroups'][0]['TargetGroupArn'] @@ -860,7 +866,7 @@ Resources: MemorySize: 128 Environment: Variables: - AUTO_SCALING_GROUP_NAME: !Sub "${AWS::StackName}-OriginGroup" + ORIGIN_ASG: !Sub "${AWS::StackName}-OriginGroup" LISTENER_ARN: !Ref ALBListener443 LambdaGetApiKey: Type: AWS::Lambda::Function @@ -1003,5 +1009,3 @@ Outputs: # ApiKey: # Description: API Key # Value: !GetAtt LambdaApiValue.APIKeyValue - - From 489df0a447332a3a275370b35ad095883db40e10 Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Fri, 10 May 2024 14:47:41 +0300 Subject: [PATCH 25/40] Remove static TargetName --- aws-custom-cluster/template.yaml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/aws-custom-cluster/template.yaml b/aws-custom-cluster/template.yaml index cb3659da..7a757c89 100644 --- a/aws-custom-cluster/template.yaml +++ b/aws-custom-cluster/template.yaml @@ -275,7 +275,7 @@ Resources: ALBTargetGroupLambda: Type: 'AWS::ElasticLoadBalancingV2::TargetGroup' Properties: - Name: ALBTargetGroupLambda + # Name: ALBTargetGroupLambda TargetType: lambda Targets: - Id: !GetAtt LambdaFunctionTrigger.Arn @@ -819,6 +819,7 @@ Resources: def lambda_handler(event, context): listener_arn = os.environ['LISTENER_ARN'] origin_asg = os.environ['ORIGIN_ASG'] + target_group_arn = os.environ['TARGETGROUP_ARN'] autoscaling_client = boto3.client('autoscaling') elb_client = boto3.client('elbv2') asg_names = autoscaling_client.describe_auto_scaling_groups() @@ -832,9 +833,6 @@ Resources: ) - target_group_name = elb_client.describe_target_groups(Names=['ALBTargetGroupLambda']) - target_group_arn= target_group_name['TargetGroups'][0]['TargetGroupArn'] - create_rule = elb_client.create_rule( Actions=[ { @@ -868,6 +866,7 @@ Resources: Variables: ORIGIN_ASG: !Sub "${AWS::StackName}-OriginGroup" LISTENER_ARN: !Ref ALBListener443 + TARGETGROUP_ARN: !Ref ALBTargetGroupLambda LambdaGetApiKey: Type: AWS::Lambda::Function DependsOn: ApiGatewayApiKey From e9f2a12e89a80e0d7ccccf399da47619ec457f69 Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Sun, 12 May 2024 23:38:37 +0300 Subject: [PATCH 26/40] Add SSL Support --- aws-custom-cluster/template-ssl.yaml | 1068 ++++++++++++++++++++++++++ 1 file changed, 1068 insertions(+) create mode 100644 aws-custom-cluster/template-ssl.yaml diff --git a/aws-custom-cluster/template-ssl.yaml b/aws-custom-cluster/template-ssl.yaml new file mode 100644 index 00000000..a9d79aef --- /dev/null +++ b/aws-custom-cluster/template-ssl.yaml @@ -0,0 +1,1068 @@ +AWSTemplateFormatVersion: '2010-09-09' + +Description: Ant Media Server - Self-Hosted Solution +Parameters: + KeyName: + Description: Name of an existing EC2 KeyPair + Type: AWS::EC2::KeyPair::KeyName + ConstraintDescription: must be the name of an existing EC2 KeyPair. + LoadBalancerCertificateArn: + Description: 'Amazon Resource Name (ARN) of the certificate to associate with the load balancer. If you do not have the SSL certificate, please check this guide: https://antmedia.io/ant-media-server-cloudformation-installation/ ' + Type: String + Default: '' + InstanceType: + 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. + +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: "*" + AMSGetLatestAMI: + Type: AWS::Lambda::Function + Properties: + Runtime: python3.11 + 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( + Filters=[ + {'Name': 'product-code', 'Values': [event['ResourceProperties']['ProductId']]}, + {'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") + ACMCertificateImportFunction: + Type: AWS::Lambda::Function + Properties: + Code: + ZipFile: | + import boto3 + import cfnresponse + + def lambda_handler(event, context): + try: + acm_client = boto3.client('acm') + response = acm_client.import_certificate( + Certificate=event['ResourceProperties']['CertificateBody'], + PrivateKey=event['ResourceProperties']['PrivateKey'], + CertificateChain=event['ResourceProperties']['CertificateChain'] + ) + certificate_arn = response['CertificateArn'] + cfnresponse.send(event, context, cfnresponse.SUCCESS, {'CertificateArn': certificate_arn}) + except Exception as e: + print("Error:", e) + cfnresponse.send(event, context, cfnresponse.FAILED, {}) + + Handler: index.lambda_handler + Role: !Sub '${AcmCertificateImportLambdaExecutionRole.Arn}' + Runtime: python3.12 + AcmCertificateImportLambdaExecutionRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Principal: + Service: lambda.amazonaws.com + Action: ['sts:AssumeRole'] + ManagedPolicyArns: [!Sub 'arn:${AWS::Partition}:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole'] + Policies: + - PolicyName: main + PolicyDocument: + Version: '2012-10-17' + Statement: + - Sid: Acm + Effect: Allow + Action: + - 'acm:AddTagsToCertificate' + - 'acm:ImportCertificate' + Resource: '*' + + CertificateImportCustomResource: + Type: Custom::ACMCertificateImport + Properties: + ServiceToken: !GetAtt ACMCertificateImportFunction.Arn + CertificateBody: | +---CertificateBody--- + PrivateKey: | +---PrivateKey--- + CertificateChain: | +---CertificaChain--- + AntMediaAmi: + Type: Custom::FindAMI + Properties: + ServiceToken: !Sub ${AMSGetLatestAMI.Arn} + ProductId: "4wh7rhpic3wfwamyp5905tsbt" + Name: "AntMedia-AWS-Marketplace-EE-*" + Architecture: "x86_64" + + AntMediaVPC: + Type: AWS::EC2::VPC + Properties: + CidrBlock: 10.0.0.0/16 + EnableDnsHostnames: true + EnableDnsSupport: true + Tags: + - Key: Name + Value: !Sub ${AWS::StackName}-AntMedia-VPC + + OriginZone: + Type: AWS::EC2::Subnet + DependsOn: AntMediaVPC + Properties: + VpcId: !Ref AntMediaVPC + CidrBlock: 10.0.1.0/24 + MapPublicIpOnLaunch: true + AvailabilityZone: + Fn::Select: + - 0 + - Fn::GetAZs: "" + Tags: + - Key: Name + Value: !Sub ${AWS::StackName}-AntMedia-Origin-Subnet + + EdgeZone: + Type: AWS::EC2::Subnet + DependsOn: AntMediaVPC + Properties: + VpcId: !Ref AntMediaVPC + CidrBlock: 10.0.2.0/24 + MapPublicIpOnLaunch: true + AvailabilityZone: + Fn::Select: + - 1 + - Fn::GetAZs: "" + Tags: + - Key: Name + Value: !Sub ${AWS::StackName}-AntMedia-Edge-Subnet + + DefaultGateway: + Type: AWS::EC2::InternetGateway + + InternetGatewayAttachment: + Type: AWS::EC2::VPCGatewayAttachment + Properties: + InternetGatewayId: !Ref DefaultGateway + VpcId: !Ref AntMediaVPC + + RouteTable: + Type: AWS::EC2::RouteTable + Properties: + VpcId: !Ref AntMediaVPC + Tags: + - Key: Name + Value: !Sub ${AWS::StackName}-AntMedia-Route-Table + + DefaultRoute: + Type: AWS::EC2::Route + DependsOn: InternetGatewayAttachment + Properties: + RouteTableId: !Ref RouteTable + GatewayId: !Ref DefaultGateway + DestinationCidrBlock: 0.0.0.0/0 + + SubnetRouteTableAssociationOrigin: + Type: AWS::EC2::SubnetRouteTableAssociation + Properties: + RouteTableId: !Ref RouteTable + SubnetId: !Ref OriginZone + + SubnetRouteTableAssociationEdge: + Type: AWS::EC2::SubnetRouteTableAssociation + Properties: + RouteTableId: !Ref RouteTable + SubnetId: !Ref EdgeZone + + ELBSecurityGroup: + Type: AWS::EC2::SecurityGroup + Properties: + GroupDescription: Allows access + VpcId: !Ref AntMediaVPC + 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 + - CidrIp: 0.0.0.0/0 + IpProtocol: tcp + FromPort: 4444 + ToPort: 4444 + Description: Allow 4444. Port for accessing Dashboard + + ApplicationLoadBalancer: + Type: 'AWS::ElasticLoadBalancingV2::LoadBalancer' + Properties: + Subnets: + - !Ref OriginZone + - !Ref EdgeZone + SecurityGroups: + - !GetAtt [ ELBSecurityGroup, GroupId ] + + ALBTargetGroupOrigin: + Type: 'AWS::ElasticLoadBalancingV2::TargetGroup' + Properties: + HealthCheckIntervalSeconds: 30 + HealthCheckTimeoutSeconds: 5 + HealthyThresholdCount: 3 + Port: 5080 + Protocol: HTTP + UnhealthyThresholdCount: 5 + VpcId: !Ref AntMediaVPC + TargetGroupAttributes: + - Key: stickiness.enabled + Value: 'true' + - Key: stickiness.type + Value: lb_cookie + - Key: stickiness.lb_cookie.duration_seconds + Value: '30' + - Key: deregistration_delay.timeout_seconds + Value: 0 + + ALBTargetGroupLambda: + Type: 'AWS::ElasticLoadBalancingV2::TargetGroup' + Properties: + # Name: ALBTargetGroupLambda + TargetType: lambda + Targets: + - Id: !GetAtt LambdaFunctionTrigger.Arn + + ALBListener443: + Type: 'AWS::ElasticLoadBalancingV2::Listener' + Properties: + Certificates: + - CertificateArn: !GetAtt CertificateImportCustomResource.CertificateArn + DefaultActions: + - Type: forward + TargetGroupArn: !Ref ALBTargetGroupOrigin + LoadBalancerArn: !Ref ApplicationLoadBalancer + Port: '443' + Protocol: HTTPS + + ALBListenerRuleWithPath: + Type: AWS::ElasticLoadBalancingV2::ListenerRule + Properties: + Actions: + - Type: forward + TargetGroupArn: !Ref ALBTargetGroupLambda + Conditions: + - Field: path-pattern + Values: [ "*" ] + ListenerArn: !Ref ALBListener443 + Priority: 1 + + RedisServerlessCache: + Type: AWS::ElastiCache::ServerlessCache + Properties: + DailySnapshotTime: "03:00" + Description: "Ant Media Server - Redis Serverless Cache" + Engine: "Redis" + SecurityGroupIds: + - !GetAtt "InstanceSecurityGroup.GroupId" + ServerlessCacheName: !Sub ${AWS::StackName}-ServerlessRedis + SubnetIds: + - !Ref OriginZone + - !Ref EdgeZone + Tags: + - Key: Name + Value: RedisCache + + OriginGroup: + Type: 'AWS::AutoScaling::AutoScalingGroup' + DependsOn: + - LaunchTemplateOrigin + Properties: + VPCZoneIdentifier: + - !Ref OriginZone + LaunchTemplate: + LaunchTemplateName: !Sub ${AWS::StackName}-AntMedia-LaunchTemplateOrigin + Version: !GetAtt 'LaunchTemplateOrigin.LatestVersionNumber' + MinSize: 0 + MaxSize: 1 + DesiredCapacity: 0 + TargetGroupARNs: + - !Ref ALBTargetGroupOrigin + Tags: + - Key: Name + Value: Ant-Media-Server + PropagateAtLaunch: 'true' + + LaunchTemplateOrigin: + Type: 'AWS::EC2::LaunchTemplate' + Properties: + LaunchTemplateName: !Sub ${AWS::StackName}-AntMedia-LaunchTemplateOrigin + LaunchTemplateData: + InstanceType: !Ref InstanceType + KeyName: !Ref KeyName + ImageId: ami-05603669082b6ebf0 + SecurityGroupIds: + - !GetAtt "InstanceSecurityGroup.GroupId" + BlockDeviceMappings: + - DeviceName: /dev/sda1 + Ebs: + VolumeSize: 10 + VolumeType: gp2 + DeleteOnTermination: true + UserData: + Fn::Base64: !Sub | + #!/bin/bash + sudo apt-get update -y + sudo apt-get install stunnel -y + sudo tee /etc/stunnel/stunnel.conf > /dev/null < { + console.log('Request successfully sent.'); + console.log(api); + console.log(api_key) + }) + .catch(error => { + console.error('Error:', error); + }); + + }; + + var onExpire = function(response) { + var logEl = document.querySelector(".hcaptcha-success"); + if (!logEl) { + logEl = document.createElement("div"); + logEl.className = "hcaptcha-success"; + document.body.appendChild(logEl); + } + logEl.innerHTML = "Token expired."; + }; + """ + + # if response.status_code == 200: + html_content = f""" + + + + + Ant Media Server will be starting + + + +

Get Ready!

+

Ant Media Server will be starting

+
+ + +
+
+
+ +
+ + + + """ + + # else: + # html_content = "Waiting for the server to be ready" + + response = { + "statusCode": 200, + "headers": { + "Content-Type": "text/html", + }, + "body": html_content + } + + return response + + Handler: "index.lambda_handler" + Role: !GetAtt LambdaIamRole.Arn + Runtime: python3.12 + Timeout: 60 + MemorySize: 128 + Environment: + Variables: + API_URL: !Sub "https://${ApiGatewayRestApi}.execute-api.${AWS::Region}.amazonaws.com/" + #TARGETGROUP_ARN: !Ref ALBTargetGroupLambda + + + + LambdaCloudwatchFunction: + Type: AWS::Lambda::Function + Properties: + Code: + ZipFile: | + import boto3, os + + def lambda_handler(event, context): + listener_arn = os.environ['LISTENER_ARN'] + origin_asg = os.environ['ORIGIN_ASG'] + target_group_arn = os.environ['TARGETGROUP_ARN'] + autoscaling_client = boto3.client('autoscaling') + elb_client = boto3.client('elbv2') + asg_names = autoscaling_client.describe_auto_scaling_groups() + asg_origin_name = [group for group in asg_names['AutoScalingGroups'] if + origin_asg in group['AutoScalingGroupName']] + asg_origin_group_names = [group['AutoScalingGroupName'] for group in asg_origin_name][0] + response = autoscaling_client.update_auto_scaling_group( + AutoScalingGroupName=asg_origin_group_names, + MinSize=0, + DesiredCapacity=0 + ) + + + create_rule = elb_client.create_rule( + Actions=[ + { + 'Type': 'forward', + 'TargetGroupArn': target_group_arn + } + ], + Conditions=[ + { + 'Field': 'path-pattern', + 'Values': ['*'] + } + ], + ListenerArn=listener_arn, + Priority=1 + ) + + print(response) + + return { + 'statusCode': 200, + 'body': 'Auto Scaling Group updated successfully!' + } + #FunctionName: InstanceDeleteFunction + Handler: "index.lambda_handler" + Role: !GetAtt LambdaIamRole.Arn + Runtime: python3.12 + Timeout: 60 + MemorySize: 128 + Environment: + Variables: + ORIGIN_ASG: !Sub "${AWS::StackName}-OriginGroup" + LISTENER_ARN: !Ref ALBListener443 + TARGETGROUP_ARN: !Ref ALBTargetGroupLambda + LambdaGetApiKey: + Type: AWS::Lambda::Function + DependsOn: ApiGatewayApiKey + Properties: + Code: + ZipFile: | + import boto3 + import cfnresponse + import traceback + + def lambda_handler(event, context): + try: + api_key_name = "AMSAPIKey" + + client = boto3.client('apigateway') + + response = client.get_api_keys( + nameQuery=api_key_name, + includeValues=True + ) + + if response['ResponseMetadata']['HTTPStatusCode'] == 200: + api_key_value = response['items'][0]['value'] + print("API Key Value:", api_key_value) + cfnresponse.send(event, context, cfnresponse.SUCCESS, {"APIKeyValue": api_key_value}) + return {"APIKeyValue": api_key_value} + else: + cfnresponse.send(event, context, cfnresponse.FAILED, {"Error": "Failed to retrieve API key"}) + return {"Error": "Failed to retrieve API key"} + except Exception as e: + traceback.print_exc() + cfnresponse.send(event, context, cfnresponse.FAILED, {"Error": str(e)}) + return {"Error": str(e)} + #Environment: + # Variables: + # API_KEY_NAME: !Sub "${AWS::StackName}-OriginGroup" + Description: AWS Lambda function + Handler: "index.lambda_handler" + MemorySize: 256 + Role: !GetAtt LambdaIamRole.Arn + Runtime: python3.12 + Timeout: 60 + + LambdaApiValue: + Type: Custom::LambdaApiKey + DependsOn: LambdaGetApiKey + Properties: + ServiceToken: !Sub "${LambdaGetApiKey.Arn}" + + # General IAM Role for Lambda Functions + LambdaIamRole: + Type: "AWS::IAM::Role" + Properties: + RoleName: !Sub "MyLambdaExecutionRole-${AWS::StackName}" + AssumeRolePolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: "Allow" + Principal: + Service: "lambda.amazonaws.com" + Action: "sts:AssumeRole" + Policies: + - PolicyName: "EC2FullAccessPolicy1" + PolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: "Allow" + Action: + - "ec2:RunInstances" + - "ec2:CreateTags" + - "ec2:DescribeInstances" + - "autoscaling:UpdateAutoScalingGroup" + - "autoscaling:DescribeAutoScalingGroups" + - "ec2:CreateNetworkInterface" + - "ec2:DescribeNetworkInterfaces" + - "ec2:DeleteNetworkInterface" + - "elasticloadbalancing:DescribeRules" + - "elasticloadbalancing:DeleteRule" + - "elasticloadbalancing:DescribeTargetGroups" + - "elasticloadbalancing:CreateRule" + - "apigateway:GET" + - "acm:DescribeCertificate" + Resource: "*" + - PolicyName: "CloudWatchLogsPolicy" + PolicyDocument: + Version: "2012-10-17" + Statement: + - Effect: "Allow" + Action: + - "logs:CreateLogGroup" + - "logs:CreateLogStream" + - "logs:PutLogEvents" + Resource: "*" + Path: '/' + + # Lambda Resource Based Policy for Cloudwatch + LambdaInvokePermission: + Type: AWS::Lambda::Permission + Properties: + Action: lambda:InvokeFunction + FunctionName: !Ref LambdaCloudwatchFunction + Principal: lambda.alarms.cloudwatch.amazonaws.com + SourceArn: !GetAtt AutoScalingGroupScaleDownAlarm.Arn + + ELBLambdaInvokePermission: + Type: AWS::Lambda::Permission + Properties: + FunctionName: !GetAtt LambdaFunctionTrigger.Arn + Action: lambda:InvokeFunction + Principal: elasticloadbalancing.amazonaws.com + + # Cloudwatch rule to set 0 in Autoscale + AutoScalingGroupScaleDownAlarm: + Type: 'AWS::CloudWatch::Alarm' + Properties: + AlarmName: AutoScalingGroupScaleDownAlarm + ComparisonOperator: LessThanOrEqualToThreshold + EvaluationPeriods: 3 + MetricName: CPUUtilization + Namespace: AWS/EC2 + Period: 120 + Statistic: Average + Threshold: 10 + ActionsEnabled: true + AlarmActions: + - !GetAtt LambdaCloudwatchFunction.Arn + Dimensions: + - Name: AutoScalingGroupName + Value: !Ref OriginGroup +Outputs: + EndPoint: + Description: Load Balancer Address of Ant Media Server + Value: !Join + - '' + - - 'https://' + - !GetAtt + - ApplicationLoadBalancer + - DNSName +# ApiKey: +# Description: API Key +# Value: !GetAtt LambdaApiValue.APIKeyValue From 0576401709ae111f1a9fa20a780f40dae67e6eab Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Mon, 13 May 2024 00:01:08 +0300 Subject: [PATCH 27/40] Update cfn for Ubuntu 22.04 --- aws-custom-cluster/template-ssl.yaml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/aws-custom-cluster/template-ssl.yaml b/aws-custom-cluster/template-ssl.yaml index a9d79aef..edaf272d 100644 --- a/aws-custom-cluster/template-ssl.yaml +++ b/aws-custom-cluster/template-ssl.yaml @@ -418,7 +418,7 @@ Resources: Fn::Base64: !Sub | #!/bin/bash sudo apt-get update -y - sudo apt-get install stunnel -y + sudo apt-get install stunnel python3-pip -y sudo tee /etc/stunnel/stunnel.conf > /dev/null < Date: Mon, 13 May 2024 00:01:14 +0300 Subject: [PATCH 28/40] Update cfn for Ubuntu 22.04 --- aws-custom-cluster/template.yaml | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/aws-custom-cluster/template.yaml b/aws-custom-cluster/template.yaml index 7a757c89..1881d859 100644 --- a/aws-custom-cluster/template.yaml +++ b/aws-custom-cluster/template.yaml @@ -360,7 +360,7 @@ Resources: Fn::Base64: !Sub | #!/bin/bash sudo apt-get update -y - sudo apt-get install stunnel -y + sudo apt-get install stunnel python3-pip -y sudo tee /etc/stunnel/stunnel.conf > /dev/null < Date: Wed, 22 May 2024 14:51:07 +0300 Subject: [PATCH 29/40] Refactor Lambda Script --- aws-custom-cluster/template.yaml | 195 ++++++++++++++++++++++++++----- 1 file changed, 167 insertions(+), 28 deletions(-) diff --git a/aws-custom-cluster/template.yaml b/aws-custom-cluster/template.yaml index 1881d859..3c841262 100644 --- a/aws-custom-cluster/template.yaml +++ b/aws-custom-cluster/template.yaml @@ -350,6 +350,8 @@ Resources: ImageId: ami-05603669082b6ebf0 SecurityGroupIds: - !GetAtt "InstanceSecurityGroup.GroupId" + IamInstanceProfile: + Arn: !GetAtt InstanceProfile.Arn BlockDeviceMappings: - DeviceName: /dev/sda1 Ebs: @@ -360,7 +362,7 @@ Resources: Fn::Base64: !Sub | #!/bin/bash sudo apt-get update -y - sudo apt-get install stunnel python3-pip -y + sudo apt-get install stunnel python3-pip jq -y sudo tee /etc/stunnel/stunnel.conf > /dev/null < 0)] | length > 0' /tmp/curl_output.txt + """ + asg_names = autoscaling_client.describe_auto_scaling_groups() asg_origin_name = [group for group in asg_names['AutoScalingGroups'] if - origin_asg in group['AutoScalingGroupName']] + origin_asg in group['AutoScalingGroupName']] asg_origin_group_names = [group['AutoScalingGroupName'] for group in asg_origin_name][0] - response = autoscaling_client.update_auto_scaling_group( - AutoScalingGroupName=asg_origin_group_names, - MinSize=0, - DesiredCapacity=0 - ) + print(f"Auto Scaling Group name: {asg_origin_group_names}") + origin_calculate_total_instance = autoscaling_client.describe_auto_scaling_groups(AutoScalingGroupNames=[asg_origin_group_names]) + origin_current_capacity = len(origin_calculate_total_instance['AutoScalingGroups'][0]['Instances']) + instance = origin_calculate_total_instance['AutoScalingGroups'][0]['Instances'] + try: + instance_id = instance[0]['InstanceId'] + print("Current capacity and Instance ID", {"origin_current_capacity": origin_current_capacity, "instance_id": instance_id}) + except IndexError: + cloudwatch_set_ok(alarm_name) + print("No instances found in Auto Scaling Group", asg_origin_group_names) + return { + 'statusCode': 200, + 'body': json.dumps({"message": "No instances found in Auto Scaling Group"}) + } - create_rule = elb_client.create_rule( - Actions=[ - { - 'Type': 'forward', - 'TargetGroupArn': target_group_arn - } - ], - Conditions=[ - { - 'Field': 'path-pattern', - 'Values': ['*'] - } - ], - ListenerArn=listener_arn, - Priority=1 - ) + smm_response = ssm_client.send_command( + DocumentName ='AWS-RunShellScript', + Parameters = {'commands': [script]}, + InstanceIds = [instance_id] + ) - print(response) + command_id = smm_response['Command']['CommandId'] + while True: + time.sleep(2) + invocation_response = ssm_client.get_command_invocation( + CommandId=command_id, + InstanceId=instance_id, + ) + if invocation_response['Status'] not in ['Pending', 'InProgress']: + break + + smm_output = invocation_response['StandardOutputContent'].strip() + print(f"SSM command output: {smm_output}") + #debug + #print(invocation_response['StandardOutputContent']) + #error = invocation_response['StandardErrorContent'].strip() + + if origin_current_capacity == 1: + if smm_output == 'false': + print("No live streams found. Updating Auto Scaling Group.") + origin_response = autoscaling_client.update_auto_scaling_group( + AutoScalingGroupName=asg_origin_group_names, + MinSize=0, + DesiredCapacity=0 + ) + + create_rule = elb_client.create_rule( + Actions=[ + { + 'Type': 'forward', + 'TargetGroupArn': target_group_arn + } + ], + Conditions=[ + { + 'Field': 'path-pattern', + 'Values': ['*'] + } + ], + ListenerArn=listener_arn, + Priority=1 + ) + cloudwatch_set_ok(alarm_name) + print(origin_response) + return { + 'statusCode': 200, + 'body': 'Auto Scaling Group updated successfully!' + } + else: + print("Live streams found.") + else: + print(f"Current capacity is not 1, it is {origin_current_capacity}. No updates needed.") + + cloudwatch_set_ok(alarm_name) return { 'statusCode': 200, - 'body': 'Auto Scaling Group updated successfully!' + 'body': 'Auto Scaling Group does not require update.' } + + + def cloudwatch_set_ok(alarm_name): + try: + cloudwatch_client = boto3.client('cloudwatch') + cloudwatch_response = cloudwatch_client.set_alarm_state( + AlarmName=alarm_name, + StateValue='OK', + StateReason='Updating alarm state to OK' + ) + print("Alarm state updated to OK", cloudwatch_response) + return cloudwatch_response + except Exception as e: + error_message = { + "error": str(e), + "function": "cloudwatch_set_ok", + "alarm_name": alarm_name + } + print(json.dumps(error_message)) + raise e + #FunctionName: InstanceDeleteFunction Handler: "index.lambda_handler" Role: !GetAtt LambdaIamRole.Arn @@ -928,7 +1031,7 @@ Resources: Service: "lambda.amazonaws.com" Action: "sts:AssumeRole" Policies: - - PolicyName: "EC2FullAccessPolicy1" + - PolicyName: "AccessPolicy" PolicyDocument: Version: "2012-10-17" Statement: @@ -948,6 +1051,9 @@ Resources: - "elasticloadbalancing:CreateRule" - "apigateway:GET" - "acm:DescribeCertificate" + - "ssm:GetCommandInvocation" + - "ssm:SendCommand" + - "cloudwatch:SetAlarmState" Resource: "*" - PolicyName: "CloudWatchLogsPolicy" PolicyDocument: @@ -977,6 +1083,39 @@ Resources: Action: lambda:InvokeFunction Principal: elasticloadbalancing.amazonaws.com + InstanceProfile: + Type: 'AWS::IAM::InstanceProfile' + Properties: + Roles: + - !Ref EC2Role + + EC2Role: + Type: 'AWS::IAM::Role' + Properties: + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Principal: + Service: [ec2.amazonaws.com] + Action: ['sts:AssumeRole'] + Policies: + - PolicyName: AllowSSM + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: Allow + Action: + - "ssm:*" + - "cloudwatch:PutMetricData" + - "ds:CreateComputer" + - "ds:DescribeDirectories" + - "ec2:DescribeInstanceStatus" + - "logs:*" + - "ssm:*" + - "ec2messages:*" + Resource: '*' + # Cloudwatch rule to set 0 in Autoscale AutoScalingGroupScaleDownAlarm: Type: 'AWS::CloudWatch::Alarm' From d44941cf4b874238c23217e7e2aec46b9a15bb8c Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Wed, 22 May 2024 21:45:08 +0300 Subject: [PATCH 30/40] Remove API configurations --- aws-custom-cluster/template.yaml | 168 ------------------------------- 1 file changed, 168 deletions(-) diff --git a/aws-custom-cluster/template.yaml b/aws-custom-cluster/template.yaml index 3c841262..7f0d13db 100644 --- a/aws-custom-cluster/template.yaml +++ b/aws-custom-cluster/template.yaml @@ -454,174 +454,6 @@ Resources: - IpProtocol: "-1" CidrIp: "0.0.0.0/0" - ApiGatewayApiKey: - Type: 'AWS::ApiGateway::ApiKey' - DependsOn: ApiGatewayStage - Properties: - Name: AMSAPIKey - Enabled: false - StageKeys: - - RestApiId: !Ref ApiGatewayRestApi - StageName: 'v1' - - ApiGatewayUsagePlan: - Type: 'AWS::ApiGateway::UsagePlan' - DependsOn: ApiGatewayApiKey - Properties: - ApiStages: - - ApiId: !Ref ApiGatewayRestApi - Stage: !Ref ApiGatewayStage - Description: Usage plan for API Key - Quota: - Limit: 10000 - Period: MONTH - Throttle: - BurstLimit: 500 - RateLimit: 1000 - UsagePlanName: MyUsagePlan - - ApiUsagePlanKey: - Type: 'AWS::ApiGateway::UsagePlanKey' - DependsOn: ApiGatewayUsagePlan - Properties: - KeyId: !Ref ApiGatewayApiKey - KeyType: API_KEY - UsagePlanId: !Ref ApiGatewayUsagePlan - - ApiGatewayRestApi: - Type: AWS::ApiGateway::RestApi - Properties: - ApiKeySourceType: HEADER - Description: An API Gateway with a Lambda Integration - EndpointConfiguration: - Types: - - EDGE - Name: Ant Media Server Api Gateway - - ApiGatewayCreateResource: - Type: AWS::ApiGateway::Resource - Properties: - ParentId: !GetAtt ApiGatewayRestApi.RootResourceId - PathPart: 'create' - RestApiId: !Ref ApiGatewayRestApi - - ApiGatewayCreateMethod: - Type: AWS::ApiGateway::Method - Properties: - ApiKeyRequired: false - AuthorizationType: NONE - HttpMethod: GET - Integration: - ConnectionType: INTERNET - Credentials: !GetAtt ApiGatewayIamRole.Arn - IntegrationHttpMethod: POST - TimeoutInMillis: 29000 - Type: AWS - Uri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${LambdaCreateFunction.Arn}/invocations' - IntegrationResponses: - - StatusCode: 200 - RequestTemplates: - application/json: | - ## See http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html - ## This template will pass through all parameters including path, querystring, header, stage variables, and context through to the integration endpoint via the body/payload - #set($allParams = $input.params()) - { - "body" : $input.json('$'), - "params" : { - #foreach($type in $allParams.keySet()) - #set($params = $allParams.get($type)) - "$type" : { - #foreach($paramName in $params.keySet()) - "$paramName" : "$util.escapeJavaScript($params.get($paramName))" - #if($foreach.hasNext),#end - #end - } - #if($foreach.hasNext),#end - #end - }, - "stageVariables" : { - #foreach($key in $stageVariables.keySet()) - "$key" : "$util.escapeJavaScript($stageVariables.get($key))" - #if($foreach.hasNext),#end - #end - }, - "context" : { - "accountId" : "$context.identity.accountId", - "apiId" : "$context.apiId", - "apiKey" : "$context.identity.apiKey", - "authorizerPrincipalId" : "$context.authorizer.principalId", - "caller" : "$context.identity.caller", - "cognitoAuthenticationProvider" : "$context.identity.cognitoAuthenticationProvider", - "cognitoAuthenticationType" : "$context.identity.cognitoAuthenticationType", - "cognitoIdentityId" : "$context.identity.cognitoIdentityId", - "cognitoIdentityPoolId" : "$context.identity.cognitoIdentityPoolId", - "httpMethod" : "$context.httpMethod", - "stage" : "$context.stage", - "sourceIp" : "$context.identity.sourceIp", - "user" : "$context.identity.user", - "userAgent" : "$context.identity.userAgent", - "userArn" : "$context.identity.userArn", - "requestId" : "$context.requestId", - "resourceId" : "$context.resourceId", - "resourcePath" : "$context.resourcePath" - } - } - MethodResponses: - - StatusCode: 200 - ResponseModels: - application/json: 'Empty' - RequestParameters: - method.request.querystring.name: false - OperationName: 'lambda' - ResourceId: !Ref ApiGatewayCreateResource - RestApiId: !Ref ApiGatewayRestApi - - ApiGatewayModel: - Type: AWS::ApiGateway::Model - Properties: - ContentType: 'application/json' - RestApiId: !Ref ApiGatewayRestApi - Schema: {} - - ApiGatewayStage: - Type: AWS::ApiGateway::Stage - DependsOn: ApiGatewayDeployment - Properties: - DeploymentId: !Ref ApiGatewayDeployment - Description: Lambda API Stage v1 - RestApiId: !Ref ApiGatewayRestApi - StageName: 'v1' - - ApiGatewayDeployment: - Type: AWS::ApiGateway::Deployment - DependsOn: ApiGatewayCreateMethod - Properties: - Description: Lambda API Deployment - RestApiId: !Ref ApiGatewayRestApi - - ApiGatewayIamRole: - Type: AWS::IAM::Role - Properties: - AssumeRolePolicyDocument: - Version: '2012-10-17' - Statement: - - Sid: '' - Effect: 'Allow' - Principal: - Service: - - 'apigateway.amazonaws.com' - Action: - - 'sts:AssumeRole' - Path: '/' - Policies: - - PolicyName: LambdaAccess - PolicyDocument: - Version: '2012-10-17' - Statement: - - Effect: 'Allow' - Action: 'lambda:*' - Resource: - - !GetAtt LambdaCreateFunction.Arn LambdaCreateFunction: Type: AWS::Lambda::Function Properties: From 50215173ddf42da04584e217c9e12bbd3328b598 Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Wed, 22 May 2024 21:46:00 +0300 Subject: [PATCH 31/40] Add API configurations --- aws-custom-cluster/template.yaml | 168 +++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) diff --git a/aws-custom-cluster/template.yaml b/aws-custom-cluster/template.yaml index 7f0d13db..3c841262 100644 --- a/aws-custom-cluster/template.yaml +++ b/aws-custom-cluster/template.yaml @@ -454,6 +454,174 @@ Resources: - IpProtocol: "-1" CidrIp: "0.0.0.0/0" + ApiGatewayApiKey: + Type: 'AWS::ApiGateway::ApiKey' + DependsOn: ApiGatewayStage + Properties: + Name: AMSAPIKey + Enabled: false + StageKeys: + - RestApiId: !Ref ApiGatewayRestApi + StageName: 'v1' + + ApiGatewayUsagePlan: + Type: 'AWS::ApiGateway::UsagePlan' + DependsOn: ApiGatewayApiKey + Properties: + ApiStages: + - ApiId: !Ref ApiGatewayRestApi + Stage: !Ref ApiGatewayStage + Description: Usage plan for API Key + Quota: + Limit: 10000 + Period: MONTH + Throttle: + BurstLimit: 500 + RateLimit: 1000 + UsagePlanName: MyUsagePlan + + ApiUsagePlanKey: + Type: 'AWS::ApiGateway::UsagePlanKey' + DependsOn: ApiGatewayUsagePlan + Properties: + KeyId: !Ref ApiGatewayApiKey + KeyType: API_KEY + UsagePlanId: !Ref ApiGatewayUsagePlan + + ApiGatewayRestApi: + Type: AWS::ApiGateway::RestApi + Properties: + ApiKeySourceType: HEADER + Description: An API Gateway with a Lambda Integration + EndpointConfiguration: + Types: + - EDGE + Name: Ant Media Server Api Gateway + + ApiGatewayCreateResource: + Type: AWS::ApiGateway::Resource + Properties: + ParentId: !GetAtt ApiGatewayRestApi.RootResourceId + PathPart: 'create' + RestApiId: !Ref ApiGatewayRestApi + + ApiGatewayCreateMethod: + Type: AWS::ApiGateway::Method + Properties: + ApiKeyRequired: false + AuthorizationType: NONE + HttpMethod: GET + Integration: + ConnectionType: INTERNET + Credentials: !GetAtt ApiGatewayIamRole.Arn + IntegrationHttpMethod: POST + TimeoutInMillis: 29000 + Type: AWS + Uri: !Sub 'arn:aws:apigateway:${AWS::Region}:lambda:path/2015-03-31/functions/${LambdaCreateFunction.Arn}/invocations' + IntegrationResponses: + - StatusCode: 200 + RequestTemplates: + application/json: | + ## See http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-mapping-template-reference.html + ## This template will pass through all parameters including path, querystring, header, stage variables, and context through to the integration endpoint via the body/payload + #set($allParams = $input.params()) + { + "body" : $input.json('$'), + "params" : { + #foreach($type in $allParams.keySet()) + #set($params = $allParams.get($type)) + "$type" : { + #foreach($paramName in $params.keySet()) + "$paramName" : "$util.escapeJavaScript($params.get($paramName))" + #if($foreach.hasNext),#end + #end + } + #if($foreach.hasNext),#end + #end + }, + "stageVariables" : { + #foreach($key in $stageVariables.keySet()) + "$key" : "$util.escapeJavaScript($stageVariables.get($key))" + #if($foreach.hasNext),#end + #end + }, + "context" : { + "accountId" : "$context.identity.accountId", + "apiId" : "$context.apiId", + "apiKey" : "$context.identity.apiKey", + "authorizerPrincipalId" : "$context.authorizer.principalId", + "caller" : "$context.identity.caller", + "cognitoAuthenticationProvider" : "$context.identity.cognitoAuthenticationProvider", + "cognitoAuthenticationType" : "$context.identity.cognitoAuthenticationType", + "cognitoIdentityId" : "$context.identity.cognitoIdentityId", + "cognitoIdentityPoolId" : "$context.identity.cognitoIdentityPoolId", + "httpMethod" : "$context.httpMethod", + "stage" : "$context.stage", + "sourceIp" : "$context.identity.sourceIp", + "user" : "$context.identity.user", + "userAgent" : "$context.identity.userAgent", + "userArn" : "$context.identity.userArn", + "requestId" : "$context.requestId", + "resourceId" : "$context.resourceId", + "resourcePath" : "$context.resourcePath" + } + } + MethodResponses: + - StatusCode: 200 + ResponseModels: + application/json: 'Empty' + RequestParameters: + method.request.querystring.name: false + OperationName: 'lambda' + ResourceId: !Ref ApiGatewayCreateResource + RestApiId: !Ref ApiGatewayRestApi + + ApiGatewayModel: + Type: AWS::ApiGateway::Model + Properties: + ContentType: 'application/json' + RestApiId: !Ref ApiGatewayRestApi + Schema: {} + + ApiGatewayStage: + Type: AWS::ApiGateway::Stage + DependsOn: ApiGatewayDeployment + Properties: + DeploymentId: !Ref ApiGatewayDeployment + Description: Lambda API Stage v1 + RestApiId: !Ref ApiGatewayRestApi + StageName: 'v1' + + ApiGatewayDeployment: + Type: AWS::ApiGateway::Deployment + DependsOn: ApiGatewayCreateMethod + Properties: + Description: Lambda API Deployment + RestApiId: !Ref ApiGatewayRestApi + + ApiGatewayIamRole: + Type: AWS::IAM::Role + Properties: + AssumeRolePolicyDocument: + Version: '2012-10-17' + Statement: + - Sid: '' + Effect: 'Allow' + Principal: + Service: + - 'apigateway.amazonaws.com' + Action: + - 'sts:AssumeRole' + Path: '/' + Policies: + - PolicyName: LambdaAccess + PolicyDocument: + Version: '2012-10-17' + Statement: + - Effect: 'Allow' + Action: 'lambda:*' + Resource: + - !GetAtt LambdaCreateFunction.Arn LambdaCreateFunction: Type: AWS::Lambda::Function Properties: From 18c41c71399218f0ce059ba49b9837638610d765 Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Thu, 23 May 2024 10:00:33 +0300 Subject: [PATCH 32/40] Add cluster configuration --- aws-custom-cluster/template-ssl.yaml | 72 +++++++++++++++++----------- 1 file changed, 45 insertions(+), 27 deletions(-) diff --git a/aws-custom-cluster/template-ssl.yaml b/aws-custom-cluster/template-ssl.yaml index edaf272d..0153a79e 100644 --- a/aws-custom-cluster/template-ssl.yaml +++ b/aws-custom-cluster/template-ssl.yaml @@ -435,7 +435,7 @@ Resources: sudo systemctl enable stunnel4 sudo stunnel /etc/stunnel/stunnel.conf touch /usr/local/antmedia/conf/initialized - bash /usr/local/antmedia/change_server_mode.sh standalone redis://localhost:6379 + bash /usr/local/antmedia/change_server_mode.sh cluster redis://localhost:6379 mkdir -p /opt/aws/bin sudo pip3 install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz sudo ln -s /usr/local/init/ubuntu/cfn-hup /etc/init.d/cfn-hup @@ -450,6 +450,15 @@ Resources: Tags: - Key: Name Value: !Sub ${AWS::StackName}-AntMedia-LaunchTemplateOrigin + OriginCPUPolicy: + Type: AWS::AutoScaling::ScalingPolicy + Properties: + AutoScalingGroupName: !Ref OriginGroup + PolicyType: TargetTrackingScaling + TargetTrackingConfiguration: + PredefinedMetricSpecification: + PredefinedMetricType: ASGAverageCPUUtilization + TargetValue: 50 InstanceSecurityGroup: Type: 'AWS::EC2::SecurityGroup' @@ -883,36 +892,45 @@ Resources: asg_origin_name = [group for group in asg_names['AutoScalingGroups'] if origin_asg in group['AutoScalingGroupName']] asg_origin_group_names = [group['AutoScalingGroupName'] for group in asg_origin_name][0] - response = autoscaling_client.update_auto_scaling_group( - AutoScalingGroupName=asg_origin_group_names, - MinSize=0, - DesiredCapacity=0 - ) + origin_calculate_total_instance = autoscaling_client.describe_auto_scaling_groups(AutoScalingGroupNames=[asg_origin_group_names]) + origin_current_capacity = len(origin_calculate_total_instance['AutoScalingGroups'][0]['Instances']) - create_rule = elb_client.create_rule( - Actions=[ - { - 'Type': 'forward', - 'TargetGroupArn': target_group_arn - } - ], - Conditions=[ - { - 'Field': 'path-pattern', - 'Values': ['*'] - } - ], - ListenerArn=listener_arn, - Priority=1 - ) + if origin_current_capacity >= 1: + origin_response = autoscaling_client.update_auto_scaling_group( + AutoScalingGroupName=asg_origin_group_names, + MinSize=0, + DesiredCapacity=0 + ) + + create_rule = elb_client.create_rule( + Actions=[ + { + 'Type': 'forward', + 'TargetGroupArn': target_group_arn + } + ], + Conditions=[ + { + 'Field': 'path-pattern', + 'Values': ['*'] + } + ], + ListenerArn=listener_arn, + Priority=1 + ) - print(response) + print(origin_response) - return { - 'statusCode': 200, - 'body': 'Auto Scaling Group updated successfully!' - } + return { + 'statusCode': 200, + 'body': 'Auto Scaling Group updated successfully!' + } + else: + return { + 'statusCode': 200, + 'body': 'Auto Scaling Group does not require update.' + } #FunctionName: InstanceDeleteFunction Handler: "index.lambda_handler" Role: !GetAtt LambdaIamRole.Arn From a0447878da25026c0a1dfa74b560ce2c560089fd Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Fri, 31 May 2024 13:24:32 +0300 Subject: [PATCH 33/40] Add Marketplace Image --- aws-custom-cluster/template.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws-custom-cluster/template.yaml b/aws-custom-cluster/template.yaml index 3c841262..e9c9c47e 100644 --- a/aws-custom-cluster/template.yaml +++ b/aws-custom-cluster/template.yaml @@ -347,7 +347,7 @@ Resources: LaunchTemplateData: InstanceType: !Ref InstanceType KeyName: !Ref KeyName - ImageId: ami-05603669082b6ebf0 + ImageId: !Ref AntMediaAmi SecurityGroupIds: - !GetAtt "InstanceSecurityGroup.GroupId" IamInstanceProfile: From 1264d870be433591cf0aa9c2ac53391868dad619 Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Tue, 6 Aug 2024 20:23:41 +0300 Subject: [PATCH 34/40] Add ImageId --- aws-custom-cluster/template-ssl.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws-custom-cluster/template-ssl.yaml b/aws-custom-cluster/template-ssl.yaml index 0153a79e..b7686ffb 100644 --- a/aws-custom-cluster/template-ssl.yaml +++ b/aws-custom-cluster/template-ssl.yaml @@ -405,7 +405,7 @@ Resources: LaunchTemplateData: InstanceType: !Ref InstanceType KeyName: !Ref KeyName - ImageId: ami-05603669082b6ebf0 + ImageId: !Ref AntMediaAmi SecurityGroupIds: - !GetAtt "InstanceSecurityGroup.GroupId" BlockDeviceMappings: From c0216607592486db0a03cdb3de0c11068baf7edd Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Fri, 9 Aug 2024 09:04:37 +0300 Subject: [PATCH 35/40] Remove Load Balancer input area --- aws-custom-cluster/template-ssl.yaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/aws-custom-cluster/template-ssl.yaml b/aws-custom-cluster/template-ssl.yaml index b7686ffb..2d86a4f8 100644 --- a/aws-custom-cluster/template-ssl.yaml +++ b/aws-custom-cluster/template-ssl.yaml @@ -6,10 +6,6 @@ Parameters: Description: Name of an existing EC2 KeyPair Type: AWS::EC2::KeyPair::KeyName ConstraintDescription: must be the name of an existing EC2 KeyPair. - LoadBalancerCertificateArn: - Description: 'Amazon Resource Name (ARN) of the certificate to associate with the load balancer. If you do not have the SSL certificate, please check this guide: https://antmedia.io/ant-media-server-cloudformation-installation/ ' - Type: String - Default: '' InstanceType: Description: Ant Media Server Edge EC2 instance type Type: String From 392a18379d13164ff1aa572e91694b5637b36f0d Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Fri, 30 Aug 2024 07:59:26 +0300 Subject: [PATCH 36/40] Fix Lambda runtime version --- aws-custom-cluster/template-ssl.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/aws-custom-cluster/template-ssl.yaml b/aws-custom-cluster/template-ssl.yaml index 2d86a4f8..4d1a0865 100644 --- a/aws-custom-cluster/template-ssl.yaml +++ b/aws-custom-cluster/template-ssl.yaml @@ -146,7 +146,7 @@ Resources: Handler: index.lambda_handler Role: !Sub '${AcmCertificateImportLambdaExecutionRole.Arn}' - Runtime: python3.12 + Runtime: python3.11 AcmCertificateImportLambdaExecutionRole: Type: AWS::IAM::Role Properties: From a9cd0a466eece79c3934f28c3ce818c5d01d0f8f Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Mon, 18 Nov 2024 10:11:18 +0300 Subject: [PATCH 37/40] Add Cooldown setting for scaling-in --- aws-custom-cluster/template-ssl.yaml | 2 ++ aws-custom-cluster/template.yaml | 2 ++ 2 files changed, 4 insertions(+) diff --git a/aws-custom-cluster/template-ssl.yaml b/aws-custom-cluster/template-ssl.yaml index 4d1a0865..bbddc03a 100644 --- a/aws-custom-cluster/template-ssl.yaml +++ b/aws-custom-cluster/template-ssl.yaml @@ -455,6 +455,8 @@ Resources: PredefinedMetricSpecification: PredefinedMetricType: ASGAverageCPUUtilization TargetValue: 50 + ScaleInCooldown: 300 + ScaleOutCooldown: 300 InstanceSecurityGroup: Type: 'AWS::EC2::SecurityGroup' diff --git a/aws-custom-cluster/template.yaml b/aws-custom-cluster/template.yaml index e9c9c47e..4dccdf3e 100644 --- a/aws-custom-cluster/template.yaml +++ b/aws-custom-cluster/template.yaml @@ -408,6 +408,8 @@ Resources: PredefinedMetricSpecification: PredefinedMetricType: ASGAverageCPUUtilization TargetValue: 50 + ScaleInCooldown: 300 + ScaleOutCooldown: 300 InstanceSecurityGroup: Type: 'AWS::EC2::SecurityGroup' From 848e69fff4d1029c8c88e34aac3af5ca280f88e1 Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Mon, 18 Nov 2024 10:15:59 +0300 Subject: [PATCH 38/40] Update AutoScalingGroup MaxSize value --- aws-custom-cluster/template-ssl.yaml | 2 +- aws-custom-cluster/template.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/aws-custom-cluster/template-ssl.yaml b/aws-custom-cluster/template-ssl.yaml index bbddc03a..a606bc01 100644 --- a/aws-custom-cluster/template-ssl.yaml +++ b/aws-custom-cluster/template-ssl.yaml @@ -385,7 +385,7 @@ Resources: LaunchTemplateName: !Sub ${AWS::StackName}-AntMedia-LaunchTemplateOrigin Version: !GetAtt 'LaunchTemplateOrigin.LatestVersionNumber' MinSize: 0 - MaxSize: 1 + MaxSize: 100 DesiredCapacity: 0 TargetGroupARNs: - !Ref ALBTargetGroupOrigin diff --git a/aws-custom-cluster/template.yaml b/aws-custom-cluster/template.yaml index 4dccdf3e..f4fccb84 100644 --- a/aws-custom-cluster/template.yaml +++ b/aws-custom-cluster/template.yaml @@ -331,7 +331,7 @@ Resources: LaunchTemplateName: !Sub ${AWS::StackName}-AntMedia-LaunchTemplateOrigin Version: !GetAtt 'LaunchTemplateOrigin.LatestVersionNumber' MinSize: 0 - MaxSize: 1 + MaxSize: 100 DesiredCapacity: 0 TargetGroupARNs: - !Ref ALBTargetGroupOrigin From 022fa0385cb9a91106541244326ff0b82b29202e Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Mon, 2 Dec 2024 00:36:40 +0300 Subject: [PATCH 39/40] Update template.yaml --- aws-custom-cluster/template.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/aws-custom-cluster/template.yaml b/aws-custom-cluster/template.yaml index f4fccb84..b0176c7c 100644 --- a/aws-custom-cluster/template.yaml +++ b/aws-custom-cluster/template.yaml @@ -403,13 +403,12 @@ Resources: Type: AWS::AutoScaling::ScalingPolicy Properties: AutoScalingGroupName: !Ref OriginGroup + Cooldown: 120 PolicyType: TargetTrackingScaling TargetTrackingConfiguration: PredefinedMetricSpecification: PredefinedMetricType: ASGAverageCPUUtilization TargetValue: 50 - ScaleInCooldown: 300 - ScaleOutCooldown: 300 InstanceSecurityGroup: Type: 'AWS::EC2::SecurityGroup' From dea288a9612cfde37fa0a3fd508fbf43fc97be32 Mon Sep 17 00:00:00 2001 From: Murat Ugur Eminoglu Date: Mon, 2 Dec 2024 00:37:04 +0300 Subject: [PATCH 40/40] Update template-ssl.yaml --- aws-custom-cluster/template-ssl.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/aws-custom-cluster/template-ssl.yaml b/aws-custom-cluster/template-ssl.yaml index a606bc01..846f1094 100644 --- a/aws-custom-cluster/template-ssl.yaml +++ b/aws-custom-cluster/template-ssl.yaml @@ -450,13 +450,12 @@ Resources: Type: AWS::AutoScaling::ScalingPolicy Properties: AutoScalingGroupName: !Ref OriginGroup + Cooldown: 120 PolicyType: TargetTrackingScaling TargetTrackingConfiguration: PredefinedMetricSpecification: PredefinedMetricType: ASGAverageCPUUtilization TargetValue: 50 - ScaleInCooldown: 300 - ScaleOutCooldown: 300 InstanceSecurityGroup: Type: 'AWS::EC2::SecurityGroup'