openvidu/openvidu-deployment/pro/ha/aws/cf-openvidu-ha.yaml

2475 lines
94 KiB
YAML

AWSTemplateFormatVersion: 2010-09-09
Description: OpenVidu Pro - High Availability
Parameters:
DomainName:
Type: String
Description: Domain name for the OpenVidu High Availability cluster
AllowedPattern: ^$|^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$
ConstraintDescription: The domain name does not have a valid domain name format
OpenViduCertificateARN:
Description: 'Amazon certificate arn resource to load into the LoadBalancer'
Type: String
AllowedPattern: '.+'
ConstraintDescription: The Load Balancer domain name must be defined
TurnDomainName:
Description: '(Optional) Domain name for the TURN server with TLS.'
Type: String
Default: ''
TurnCertificateARN:
Description: '(Optional) Amazon certificate arn resource to load into the TURN LoadBalancer'
Type: String
Default: ''
OpenViduLicense:
Description: "Visit https://openvidu.io/account"
Type: String
AllowedPattern: ^(?!\s*$).+$
NoEcho: true
ConstraintDescription: OpenVidu Pro License is mandatory
RTCEngine:
Description: "RTCEngine media engine to use"
Type: String
AllowedValues:
- pion
- mediasoup
Default: pion
MasterNodeInstanceType:
Description: "Specifies the EC2 instance type for your OpenVidu Master Node"
Type: String
Default: c6a.xlarge
AllowedValues:
- t2.large
- t2.xlarge
- t2.2xlarge
- t3.medium
- t3.large
- t3.xlarge
- t3.2xlarge
- m4.large
- m4.xlarge
- m4.2xlarge
- m4.4xlarge
- m4.10xlarge
- m4.16xlarge
- m5.large
- m5.xlarge
- m5.2xlarge
- m5.4xlarge
- m5.8xlarge
- m5.12xlarge
- m5.16xlarge
- m5.24xlarge
- m6i.large
- m6i.xlarge
- m6i.2xlarge
- m6i.4xlarge
- m6i.8xlarge
- m6i.12xlarge
- m6i.16xlarge
- m6i.24xlarge
- m6i.32xlarge
- m6i.metal
- 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
- c6a.large
- c6a.xlarge
- c6a.2xlarge
- c6a.4xlarge
- c6a.8xlarge
- c6a.12xlarge
- c6a.16xlarge
- c6a.24xlarge
- c6a.32xlarge
- c6a.48xlarge
- c6a.metal
- c6i.large
- c6i.xlarge
- c6i.2xlarge
- c6i.4xlarge
- c6i.8xlarge
- c6i.12xlarge
- c6i.16xlarge
- c6i.24xlarge
- c6i.32xlarge
- c6i.metal
- c7a.medium
- c7a.large
- c7a.xlarge
- c7a.2xlarge
- c7a.4xlarge
- c7a.8xlarge
- c7a.12xlarge
- c7a.16xlarge
- c7a.24xlarge
- c7a.32xlarge
- c7a.48xlarge
- c7a.metal-48xl
- c7i.large
- c7i.xlarge
- c7i.2xlarge
- c7i.4xlarge
- c7i.8xlarge
- c7i.12xlarge
- c7i.16xlarge
- c7i.24xlarge
- c7i.48xlarge
- c7i.metal-24xl
- c7i.metal-48xl
- c5n.large
- c5n.xlarge
- c5n.2xlarge
- c5n.4xlarge
- c5n.9xlarge
- c5n.18xlarge
- m5n.large
- m5n.xlarge
- m5n.2xlarge
- m5n.4xlarge
- m5n.8xlarge
- m5n.12xlarge
- m5n.16xlarge
- m5n.24xlarge
- m6in.large
- m6in.xlarge
- m6in.2xlarge
- m6in.4xlarge
- m6in.8xlarge
- m6in.12xlarge
- m6in.16xlarge
- m6in.24xlarge
- m6in.32xlarge
- r5n.large
- r5n.xlarge
- r5n.2xlarge
- r5n.4xlarge
- r5n.8xlarge
- r5n.12xlarge
- r5n.16xlarge
- r5n.24xlarge
ConstraintDescription: "Must be a valid EC2 instance type"
MediaNodeInstanceType:
Description: "Specifies the EC2 instance type for your OpenVidu Media Nodes"
Type: String
Default: c6a.xlarge
AllowedValues:
- t2.large
- t2.xlarge
- t2.2xlarge
- t3.medium
- t3.large
- t3.xlarge
- t3.2xlarge
- m4.large
- m4.xlarge
- m4.2xlarge
- m4.4xlarge
- m4.10xlarge
- m4.16xlarge
- m5.large
- m5.xlarge
- m5.2xlarge
- m5.4xlarge
- m5.8xlarge
- m5.12xlarge
- m5.16xlarge
- m5.24xlarge
- m6i.large
- m6i.xlarge
- m6i.2xlarge
- m6i.4xlarge
- m6i.8xlarge
- m6i.12xlarge
- m6i.16xlarge
- m6i.24xlarge
- m6i.32xlarge
- m6i.metal
- 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
- c6a.large
- c6a.xlarge
- c6a.2xlarge
- c6a.4xlarge
- c6a.8xlarge
- c6a.12xlarge
- c6a.16xlarge
- c6a.24xlarge
- c6a.32xlarge
- c6a.48xlarge
- c6a.metal
- c6i.large
- c6i.xlarge
- c6i.2xlarge
- c6i.4xlarge
- c6i.8xlarge
- c6i.12xlarge
- c6i.16xlarge
- c6i.24xlarge
- c6i.32xlarge
- c6i.metal
- c7a.medium
- c7a.large
- c7a.xlarge
- c7a.2xlarge
- c7a.4xlarge
- c7a.8xlarge
- c7a.12xlarge
- c7a.16xlarge
- c7a.24xlarge
- c7a.32xlarge
- c7a.48xlarge
- c7a.metal-48xl
- c7i.large
- c7i.xlarge
- c7i.2xlarge
- c7i.4xlarge
- c7i.8xlarge
- c7i.12xlarge
- c7i.16xlarge
- c7i.24xlarge
- c7i.48xlarge
- c7i.metal-24xl
- c7i.metal-48xl
- c5n.large
- c5n.xlarge
- c5n.2xlarge
- c5n.4xlarge
- c5n.9xlarge
- c5n.18xlarge
- m5n.large
- m5n.xlarge
- m5n.2xlarge
- m5n.4xlarge
- m5n.8xlarge
- m5n.12xlarge
- m5n.16xlarge
- m5n.24xlarge
- m6in.large
- m6in.xlarge
- m6in.2xlarge
- m6in.4xlarge
- m6in.8xlarge
- m6in.12xlarge
- m6in.16xlarge
- m6in.24xlarge
- m6in.32xlarge
- r5n.large
- r5n.xlarge
- r5n.2xlarge
- r5n.4xlarge
- r5n.8xlarge
- r5n.12xlarge
- r5n.16xlarge
- r5n.24xlarge
ConstraintDescription: "Must be a valid EC2 instance type"
KeyName:
Type: AWS::EC2::KeyPair::KeyName
Description: Name of an existing EC2 KeyPair to enable SSH access to the instances
AllowedPattern: ^.+$
ConstraintDescription: must be the name of an existing EC2 KeyPair.
AmiId:
Type: AWS::SSM::Parameter::Value<AWS::EC2::Image::Id>
Default: /aws/service/canonical/ubuntu/server/jammy/stable/current/amd64/hvm/ebs-gp2/ami-id
Description: AMI ID for the EC2 instances
InitialNumberOfMediaNodes:
Type: Number
Default: 1
Description: Number of initial media nodes to deploy
MinNumberOfMediaNodes:
Type: Number
Default: 1
Description: Minimum number of media nodes to deploy
MaxNumberOfMediaNodes:
Type: Number
Default: 5
Description: Maximum number of media nodes to deploy
ScaleTargetCPU:
Type: Number
Default: 50
Description: Target CPU percentage to scale up or down
S3AppDataBucketName:
Type: String
Description: Name of the S3 bucket to store data and recordings. If empty, a bucket will be created
S3ClusterDataBucketName:
Type: String
Description: Name of the S3 bucket to store cluster data. If empty, a bucket will be created
AdditionalInstallFlags:
Description: Additional optional flags to pass to the OpenVidu installer (comma-separated, e.g., "--flag1=value, --flag2").
Type: String
Default: ""
AllowedPattern: '^[A-Za-z0-9, =_.\-]*$' # Allows letters, numbers, comma, space, underscore, dot, equals, and hyphen
ConstraintDescription: Must be a comma-separated list of flags (for example, --flag=value, --bool-flag).
OpenViduVPC:
Description: "Dedicated VPC for OpenVidu cluster"
Type: AWS::EC2::VPC::Id
AllowedPattern: ^.+$
ConstraintDescription: You must specify a VPC ID
OpenViduMasterNodeSubnets:
Description: "Subnets for OpenVidu Master Node"
Type: List<AWS::EC2::Subnet::Id>
AllowedPattern: ^.+$
ConstraintDescription: You must specify a list of subnet IDs
OpenViduMediaNodeSubnets:
Description: "Subnets for OpenVidu Media Nodes"
Type: List<AWS::EC2::Subnet::Id>
AllowedPattern: ^.+$
ConstraintDescription: You must specify a list of subnet IDs
MasterNodesDiskSize:
Description: Size of the disk in GB
Type: Number
Default: 100
MinValue: 50
ConstraintDescription: The disk size must be at least 50 GB
Metadata:
'AWS::CloudFormation::Interface':
ParameterGroups:
- Label:
default: Domain and Load Blancer configuration
Parameters:
- DomainName
- OpenViduCertificateARN
- Label:
default: OpenVidu High Availability configuration
Parameters:
- OpenViduLicense
- RTCEngine
- Label:
default: EC2 Instance configuration
Parameters:
- MasterNodeInstanceType
- MediaNodeInstanceType
- KeyName
- AmiId
- Label:
default: Media Nodes Autoscaling Group configuration
Parameters:
- InitialNumberOfMediaNodes
- MinNumberOfMediaNodes
- MaxNumberOfMediaNodes
- ScaleTargetCPU
- Label:
default: S3 bucket for application data, cluster data and recordings
Parameters:
- S3AppDataBucketName
- S3ClusterDataBucketName
- Label:
default: VPC configuration
Parameters:
- OpenViduVPC
- OpenViduMasterNodeSubnets
- OpenViduMediaNodeSubnets
- Label:
default: Volumes configuration
Parameters:
- MasterNodesDiskSize
- Label:
default: "(Optional) Additional Installer Flags"
Parameters:
- AdditionalInstallFlags
- Label:
default: (Optional) TURN server configuration with TLS
Parameters:
- TurnDomainName
- TurnCertificateARN
Conditions:
TurnTLSIsEnabled: !Or [!Not [!Equals [!Ref TurnDomainName, ""]], !Not [!Equals [!Ref TurnCertificateARN, ""]]]
CreateRecordingsBucket: !Equals [!Ref S3AppDataBucketName, ""]
CreateClusterDataBucket: !Equals [!Ref S3ClusterDataBucketName, ""]
# ---
# Experimental TURN TLS with main domain
ExperimentalTurnTLSWithMainDomain:
Fn::Not:
- Fn::Equals:
- !Ref AdditionalInstallFlags
- !Select [0, !Split ["--experimental-turn-tls-with-main-domain", !Ref AdditionalInstallFlags]]
NotExperimentalTurnTLSWithMainDomain:
Fn::Or:
- Fn::Equals:
- !Ref AdditionalInstallFlags
- !Select [0, !Split ["--experimental-turn-tls-with-main-domain", !Ref AdditionalInstallFlags]]
- Fn::Equals:
- !Ref AdditionalInstallFlags
- ""
# ---
Resources:
OpenViduSharedInfo:
Type: AWS::SecretsManager::Secret
UpdateReplacePolicy: Retain
DeletionPolicy: Delete
Properties:
Name: !Sub openvidu-ha-${AWS::Region}-${AWS::StackName}
Description: Secret for OpenVidu High Availability to store deployment info and seed secret
# All the values are initialized by one master node and shared with the rest of the nodes
SecretString: |
{
"ALL_SECRETS_GENERATED": "false",
"DOMAIN_NAME": "none",
"LIVEKIT_TURN_DOMAIN_NAME": "none",
"OPENVIDU_PRO_LICENSE": "none",
"OPENVIDU_RTC_ENGINE": "none",
"REDIS_PASSWORD": "none",
"MONGO_ADMIN_USERNAME": "none",
"MONGO_ADMIN_PASSWORD": "none",
"MONGO_REPLICA_SET_KEY": "none",
"MINIO_URL": "none",
"MINIO_ACCESS_KEY": "none",
"MINIO_SECRET_KEY": "none",
"DASHBOARD_URL": "none",
"DASHBOARD_ADMIN_USERNAME": "none",
"DASHBOARD_ADMIN_PASSWORD": "none",
"GRAFANA_URL": "none",
"GRAFANA_ADMIN_USERNAME": "none",
"GRAFANA_ADMIN_PASSWORD": "none",
"MEET_ADMIN_USER": "none",
"MEET_ADMIN_SECRET": "none",
"MEET_API_KEY": "none",
"LIVEKIT_API_KEY": "none",
"LIVEKIT_API_SECRET": "none",
"ENABLED_MODULES": "none",
"MASTER_NODE_1_PRIVATE_IP": "none",
"MASTER_NODE_2_PRIVATE_IP": "none",
"MASTER_NODE_3_PRIVATE_IP": "none",
"MASTER_NODE_4_PRIVATE_IP": "none",
"OPENVIDU_VERSION": "none"
}
S3AppDataBucketResource:
Type: 'AWS::S3::Bucket'
Properties:
### Unique bucket name using Stack ID
BucketName: !Join ["-" , [ 'openvidu-appdata', !Select [0, !Split ["-", !Select [2, !Split [/, !Ref AWS::StackId ]]]]]]
AccessControl: Private
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls : true
RestrictPublicBuckets: true
DeletionPolicy: Retain
UpdateReplacePolicy: Retain
Condition: CreateRecordingsBucket
S3ClusterDataBucketResource:
Type: 'AWS::S3::Bucket'
Properties:
### Unique bucket name using Stack ID
BucketName: !Join ["-" , [ 'openvidu-clusterdata', !Select [0, !Split ["-", !Select [2, !Split [/, !Ref AWS::StackId ]]]]]]
AccessControl: Private
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls : true
RestrictPublicBuckets: true
DeletionPolicy: Retain
UpdateReplacePolicy: Retain
Condition: CreateClusterDataBucket
# -------------------------
# Preprocess subnets to allocate Volumes and ENIs across Availability Zones
# For OpenVidu Master Nodes
# -------------------------
SubnetProcessorFunction:
Type: AWS::Lambda::Function
Properties:
FunctionName: !Sub 'SubnetProcessor-${AWS::Region}-${AWS::StackName}'
Handler: index.lambda_handler
Role: !GetAtt LambdaExecutionRole.Arn
Code:
ZipFile: |
import cfnresponse
import boto3
def lambda_handler(event, context):
try:
# Process event data
subnets = event['ResourceProperties']['Subnets']
ec2 = boto3.client('ec2')
# Ensure we have at least four subnets by cycling through the available subnets
subnets = (subnets * 4)[:4] # Repeat the list to have at least 4 elements and then take the first 4
# Prepare the response
responseData = {
'Subnet1': subnets[0],
'Subnet2': subnets[1],
'Subnet3': subnets[2],
'Subnet4': subnets[3],
}
cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData)
except Exception as e:
cfnresponse.send(event, context, cfnresponse.FAILED, {'Message': str(e)})
Runtime: python3.12
Timeout: 120
SubnetProcessor:
Type: Custom::SubnetProcessor
Properties:
ServiceToken: !GetAtt SubnetProcessorFunction.Arn
Subnets: !Ref OpenViduMasterNodeSubnets
LambdaLogGroup:
UpdateReplacePolicy: Retain
DeletionPolicy: Delete
Type: AWS::Logs::LogGroup
Properties:
LogGroupName: !Sub '/aws/lambda/SubnetProcessor-${AWS::Region}-${AWS::StackName}'
RetentionInDays: 7
LambdaExecutionRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action: 'sts:AssumeRole'
Policies:
- PolicyName: LambdaLogsPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- logs:CreateLogStream
- logs:PutLogEvents
Resource: !Sub 'arn:${AWS::Partition}:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/lambda/SubnetProcessor-${AWS::Region}-${AWS::StackName}:*'
- Effect: Allow
Action:
- ec2:DescribeSubnets
Resource: '*'
OpenViduMasterNodeRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action:
- 'sts:AssumeRole'
Path: "/"
Policies:
- PolicyName: !Sub openvidu-master-policy-${AWS::Region}-${AWS::StackName}
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- autoscaling:SetInstanceHealth
Resource: '*'
Condition:
StringEquals:
'aws:ResourceTag/aws:cloudformation:stack-id': !Ref 'AWS::StackId'
- Effect: Allow
Action:
- secretsmanager:GetSecretValue
- secretsmanager:UpdateSecret
Resource: !Ref OpenViduSharedInfo
- Fn::If:
- CreateRecordingsBucket
- Effect: Allow
Action:
- s3:DeleteObject
- s3:GetObject
- s3:PutObject
Resource: !Sub ${S3AppDataBucketResource.Arn}/*
- Effect: Allow
Action:
- s3:DeleteObject
- s3:GetObject
- s3:PutObject
Resource: !Sub arn:${AWS::Partition}:s3:::${S3AppDataBucketName}/*
- Fn::If:
- CreateRecordingsBucket
- Effect: Allow
Action:
- s3:ListBucket
- s3:GetBucketLocation
Resource: !GetAtt S3AppDataBucketResource.Arn
- Effect: Allow
Action:
- s3:ListBucket
- s3:GetBucketLocation
Resource: !Sub arn:${AWS::Partition}:s3:::${S3AppDataBucketName}
- Fn::If:
- CreateClusterDataBucket
- Effect: Allow
Action:
- s3:DeleteObject
- s3:GetObject
- s3:PutObject
Resource: !Sub ${S3ClusterDataBucketResource.Arn}/*
- Effect: Allow
Action:
- s3:DeleteObject
- s3:GetObject
- s3:PutObject
Resource: !Sub arn:${AWS::Partition}:s3:::${S3ClusterDataBucketName}/*
- Fn::If:
- CreateClusterDataBucket
- Effect: Allow
Action:
- s3:ListBucket
- s3:GetBucketLocation
Resource: !GetAtt S3ClusterDataBucketResource.Arn
- Effect: Allow
Action:
- s3:ListBucket
- s3:GetBucketLocation
Resource: !Sub arn:${AWS::Partition}:s3:::${S3ClusterDataBucketName}
RoleName:
Fn::Join:
# Generate a not too long and unique role name
# Getting a unique identifier from the stack id
- ''
- - openvidu-master-role-
- !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]
OpenViduMediaNodeRole:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Principal:
Service:
- ec2.amazonaws.com
Action:
- 'sts:AssumeRole'
Path: /
ManagedPolicyArns:
- !Sub arn:${AWS::Partition}:iam::aws:policy/AmazonSSMManagedInstanceCore
Policies:
- PolicyName: !Sub openvidu-media-policy-${AWS::Region}-${AWS::StackName}
PolicyDocument:
Version: 2012-10-17
Statement:
- Effect: Allow
Action:
- secretsmanager:GetSecretValue
Resource: !Ref OpenViduSharedInfo
- Effect: Allow
Action:
- autoscaling:SetInstanceHealth
- autoscaling:CompleteLifecycleAction
- autoscaling:RecordLifecycleActionHeartbeat
Resource: '*'
Condition:
StringEquals:
'aws:ResourceTag/aws:cloudformation:stack-name': !Ref 'AWS::StackName'
- Fn::If:
- CreateRecordingsBucket
- Effect: Allow
Action:
- s3:DeleteObject
- s3:GetObject
- s3:PutObject
Resource: !Sub ${S3AppDataBucketResource.Arn}/*
- Effect: Allow
Action:
- s3:DeleteObject
- s3:GetObject
- s3:PutObject
Resource: !Sub arn:${AWS::Partition}:s3:::${S3AppDataBucketName}/*
- Fn::If:
- CreateRecordingsBucket
- Effect: Allow
Action:
- s3:ListBucket
- s3:GetBucketLocation
Resource: !GetAtt S3AppDataBucketResource.Arn
- Effect: Allow
Action:
- s3:ListBucket
- s3:GetBucketLocation
Resource: !Sub arn:${AWS::Partition}:s3:::${S3AppDataBucketName}
RoleName:
Fn::Join:
# Generate a not too long and unique role name
# Getting a unique identifier from the stack id
- ''
- - openvidu-media-role-
- !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]
OpenViduMasterInstanceProfile:
Type: AWS::IAM::InstanceProfile
Properties:
InstanceProfileName: !Sub OpenViduMasterInstanceProfile-${AWS::Region}-${AWS::StackName}
Roles:
- !Ref OpenViduMasterNodeRole
OpenViduMediaInstanceProfile:
Type: AWS::IAM::InstanceProfile
DependsOn:
- MasterNodesWaitCondition4
Properties:
InstanceProfileName: !Sub OpenViduMediaInstanceProfile-${AWS::Region}-${AWS::StackName}
Roles:
- !Ref OpenViduMediaNodeRole
OpenViduMasterLaunchTemplate:
Type: AWS::EC2::LaunchTemplate
Metadata:
Comment: Launch template for OpenVidu Master Node
AWS::CloudFormation::Init:
config:
files:
'/usr/local/bin/install.sh':
content: !Sub |
#!/bin/bash -x
set -e
OPENVIDU_VERSION=main
DOMAIN=
YQ_VERSION=v4.44.5
# Install dependencies
apt-get update && apt-get install -y \
curl \
unzip \
jq \
wget
wget https://github.com/mikefarah/yq/releases/download/${!YQ_VERSION}/yq_linux_amd64.tar.gz -O - |\
tar xz && mv yq_linux_amd64 /usr/bin/yq
# Install aws-cli
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip -qq awscliv2.zip
./aws/install
rm -rf awscliv2.zip aws
# Singal to notify instance is waiting
SIGNAL_NAME="$1"
# Token for IMDSv2
TOKEN="$(curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")"
# Subnets IDs
SUBNETS=(
"${SubnetProcessor.Subnet1}"
"${SubnetProcessor.Subnet2}"
"${SubnetProcessor.Subnet3}"
"${SubnetProcessor.Subnet4}"
)
MAC_ADDRESS="$(curl -H "X-aws-ec2-metadata-token: $TOKEN" -s http://169.254.169.254//latest/meta-data/mac)"
SUBNET_ID="$(curl -H "X-aws-ec2-metadata-token: $TOKEN" -s "http://169.254.169.254/latest/meta-data/network/interfaces/macs/$MAC_ADDRESS/subnet-id")"
# Check master node number
MASTER_NODE_NUM=1
for subnet in "${!SUBNETS[@]}"; do
if [[ "$subnet" == "$SUBNET_ID" ]]; then
SHARED_SECRET=$(aws secretsmanager get-secret-value \
--region ${AWS::Region} \
--secret-id openvidu-ha-${AWS::Region}-${AWS::StackName} \
--query SecretString --output text || echo 'none')
# Check if current master node is reacheable with ping
ACUTAL_MASTER_NODE_IP=$(echo "$SHARED_SECRET" | jq -r ".MASTER_NODE_${!MASTER_NODE_NUM}_PRIVATE_IP")
if [[ "$ACUTAL_MASTER_NODE_IP" == "none" ]]; then
break
fi
fi
MASTER_NODE_NUM=$((MASTER_NODE_NUM + 1))
done
# Get own private IP
PRIVATE_IP="$(curl -H "X-aws-ec2-metadata-token: $TOKEN" -s http://169.254.169.254/latest/meta-data/local-ipv4)"
if [[ "$PRIVATE_IP" == "" ]]; then
echo "Error: Private IP not found"
exit 1
fi
# Store current private IP
/usr/local/bin/store_secret.sh save MASTER_NODE_${!MASTER_NODE_NUM}_PRIVATE_IP "${!PRIVATE_IP}"
SHARED_SECRET=$(aws secretsmanager get-secret-value \
--region ${AWS::Region} \
--secret-id openvidu-ha-${AWS::Region}-${AWS::StackName} \
--query SecretString --output text)
ALL_SECRETS_GENERATED=$(echo "$SHARED_SECRET" | jq -r '.ALL_SECRETS_GENERATED')
# If the private IP is the same as the first master node, generate the secrets
if [[ $MASTER_NODE_NUM -eq 1 ]] && [[ "$ALL_SECRETS_GENERATED" == "false" ]]; then
DOMAIN="$(/usr/local/bin/store_secret.sh save DOMAIN_NAME "${DomainName}")"
if [[ -n "${TurnDomainName}" ]]; then
LIVEKIT_TURN_DOMAIN_NAME="$(/usr/local/bin/store_secret.sh save LIVEKIT_TURN_DOMAIN_NAME "${TurnDomainName}")"
fi
OPENVIDU_PRO_LICENSE="$(/usr/local/bin/store_secret.sh save OPENVIDU_PRO_LICENSE "${OpenViduLicense}")"
OPENVIDU_RTC_ENGINE="$(/usr/local/bin/store_secret.sh save OPENVIDU_RTC_ENGINE "${RTCEngine}")"
# Store version so media nodes can use it to install the same version
/usr/local/bin/store_secret.sh save OPENVIDU_VERSION "${!OPENVIDU_VERSION}"
# Store usernames and generate random passwords
REDIS_PASSWORD="$(/usr/local/bin/store_secret.sh generate REDIS_PASSWORD)"
MONGO_ADMIN_USERNAME="$(/usr/local/bin/store_secret.sh save MONGO_ADMIN_USERNAME "mongoadmin")"
MONGO_ADMIN_PASSWORD="$(/usr/local/bin/store_secret.sh generate MONGO_ADMIN_PASSWORD)"
MONGO_REPLICA_SET_KEY="$(/usr/local/bin/store_secret.sh generate MONGO_REPLICA_SET_KEY)"
MINIO_ACCESS_KEY="$(/usr/local/bin/store_secret.sh save MINIO_ACCESS_KEY "minioadmin")"
MINIO_SECRET_KEY="$(/usr/local/bin/store_secret.sh generate MINIO_SECRET_KEY)"
DASHBOARD_ADMIN_USERNAME="$(/usr/local/bin/store_secret.sh save DASHBOARD_ADMIN_USERNAME "dashboardadmin")"
DASHBOARD_ADMIN_PASSWORD="$(/usr/local/bin/store_secret.sh generate DASHBOARD_ADMIN_PASSWORD)"
GRAFANA_ADMIN_USERNAME="$(/usr/local/bin/store_secret.sh save GRAFANA_ADMIN_USERNAME "grafanaadmin")"
GRAFANA_ADMIN_PASSWORD="$(/usr/local/bin/store_secret.sh generate GRAFANA_ADMIN_PASSWORD)"
MEET_ADMIN_USER="$(/usr/local/bin/store_secret.sh save MEET_ADMIN_USER "meetadmin")"
MEET_ADMIN_SECRET="$(/usr/local/bin/store_secret.sh generate MEET_ADMIN_SECRET)"
MEET_API_KEY="$(/usr/local/bin/store_secret.sh generate MEET_API_KEY)"
LIVEKIT_API_KEY="$(/usr/local/bin/store_secret.sh generate LIVEKIT_API_KEY "API" 12)"
LIVEKIT_API_SECRET="$(/usr/local/bin/store_secret.sh generate LIVEKIT_API_SECRET)"
ENABLED_MODULES="$(/usr/local/bin/store_secret.sh save ENABLED_MODULES "observability,v2compatibility,openviduMeet")"
ALL_SECRETS_GENERATED="$(/usr/local/bin/store_secret.sh save ALL_SECRETS_GENERATED "true")"
fi
# Fetch the shared secret again
SHARED_SECRET=$(aws secretsmanager get-secret-value \
--region ${AWS::Region} \
--secret-id openvidu-ha-${AWS::Region}-${AWS::StackName} \
--query SecretString --output text)
ALL_SECRETS_GENERATED=$(echo "$SHARED_SECRET" | jq -r '.ALL_SECRETS_GENERATED')
if [[ "${!ALL_SECRETS_GENERATED}" == "false" ]]; then
echo "Error: Secrets not generated"
exit 1
fi
# sending the signal call
cfn-signal -e $? --stack ${AWS::StackId} --resource "$SIGNAL_NAME" --region ${AWS::Region}
# Wait for all master nodes to store their private IPs
while true; do
SHARED_SECRET=$(aws secretsmanager get-secret-value \
--region ${AWS::Region} \
--secret-id openvidu-ha-${AWS::Region}-${AWS::StackName} \
--query SecretString --output text || echo 'none')
MASTER_NODE_1_PRIVATE_IP=$(echo "$SHARED_SECRET" | jq -r '.MASTER_NODE_1_PRIVATE_IP')
MASTER_NODE_2_PRIVATE_IP=$(echo "$SHARED_SECRET" | jq -r '.MASTER_NODE_2_PRIVATE_IP')
MASTER_NODE_3_PRIVATE_IP=$(echo "$SHARED_SECRET" | jq -r '.MASTER_NODE_3_PRIVATE_IP')
MASTER_NODE_4_PRIVATE_IP=$(echo "$SHARED_SECRET" | jq -r '.MASTER_NODE_4_PRIVATE_IP')
# Check if all master nodes have stored their private IPs
if [[ "$MASTER_NODE_1_PRIVATE_IP" != "none" ]] &&
[[ "$MASTER_NODE_2_PRIVATE_IP" != "none" ]] &&
[[ "$MASTER_NODE_3_PRIVATE_IP" != "none" ]] &&
[[ "$MASTER_NODE_4_PRIVATE_IP" != "none" ]]; then
break
fi
sleep 5
done
SHARED_SECRET=$(aws secretsmanager get-secret-value \
--region ${AWS::Region} \
--secret-id openvidu-ha-${AWS::Region}-${AWS::StackName} \
--query SecretString --output text)
MASTER_NODE_1_PRIVATE_IP=$(echo "$SHARED_SECRET" | jq -r '.MASTER_NODE_1_PRIVATE_IP')
MASTER_NODE_2_PRIVATE_IP=$(echo "$SHARED_SECRET" | jq -r '.MASTER_NODE_2_PRIVATE_IP')
MASTER_NODE_3_PRIVATE_IP=$(echo "$SHARED_SECRET" | jq -r '.MASTER_NODE_3_PRIVATE_IP')
MASTER_NODE_4_PRIVATE_IP=$(echo "$SHARED_SECRET" | jq -r '.MASTER_NODE_4_PRIVATE_IP')
MASTER_NODE_PRIVATE_IP_LIST="$MASTER_NODE_1_PRIVATE_IP,$MASTER_NODE_2_PRIVATE_IP,$MASTER_NODE_3_PRIVATE_IP,$MASTER_NODE_4_PRIVATE_IP"
DOMAIN=$(echo "$SHARED_SECRET" | jq -r '.DOMAIN_NAME')
LIVEKIT_TURN_DOMAIN_NAME=$(echo "$SHARED_SECRET" | jq -r '.LIVEKIT_TURN_DOMAIN_NAME')
OPENVIDU_PRO_LICENSE=$(echo "$SHARED_SECRET" | jq -r '.OPENVIDU_PRO_LICENSE')
OPENVIDU_RTC_ENGINE=$(echo "$SHARED_SECRET" | jq -r '.OPENVIDU_RTC_ENGINE')
REDIS_PASSWORD=$(echo "$SHARED_SECRET" | jq -r '.REDIS_PASSWORD')
MONGO_ADMIN_USERNAME=$(echo "$SHARED_SECRET" | jq -r '.MONGO_ADMIN_USERNAME')
MONGO_ADMIN_PASSWORD=$(echo "$SHARED_SECRET" | jq -r '.MONGO_ADMIN_PASSWORD')
MONGO_REPLICA_SET_KEY=$(echo "$SHARED_SECRET" | jq -r '.MONGO_REPLICA_SET_KEY')
MINIO_ACCESS_KEY=$(echo "$SHARED_SECRET" | jq -r '.MINIO_ACCESS_KEY')
MINIO_SECRET_KEY=$(echo "$SHARED_SECRET" | jq -r '.MINIO_SECRET_KEY')
DASHBOARD_ADMIN_USERNAME=$(echo "$SHARED_SECRET" | jq -r '.DASHBOARD_ADMIN_USERNAME')
DASHBOARD_ADMIN_PASSWORD=$(echo "$SHARED_SECRET" | jq -r '.DASHBOARD_ADMIN_PASSWORD')
GRAFANA_ADMIN_USERNAME=$(echo "$SHARED_SECRET" | jq -r '.GRAFANA_ADMIN_USERNAME')
GRAFANA_ADMIN_PASSWORD=$(echo "$SHARED_SECRET" | jq -r '.GRAFANA_ADMIN_PASSWORD')
MEET_ADMIN_USER=$(echo "$SHARED_SECRET" | jq -r '.MEET_ADMIN_USER')
MEET_ADMIN_SECRET=$(echo "$SHARED_SECRET" | jq -r '.MEET_ADMIN_SECRET')
MEET_API_KEY=$(echo "$SHARED_SECRET" | jq -r '.MEET_API_KEY')
LIVEKIT_API_KEY=$(echo "$SHARED_SECRET" | jq -r '.LIVEKIT_API_KEY')
LIVEKIT_API_SECRET=$(echo "$SHARED_SECRET" | jq -r '.LIVEKIT_API_SECRET')
ENABLED_MODULES=$(echo "$SHARED_SECRET" | jq -r '.ENABLED_MODULES')
# Base command
INSTALL_COMMAND="sh <(curl -fsSL http://get.openvidu.io/pro/ha/$OPENVIDU_VERSION/install_ov_master_node.sh)"
# Common arguments
COMMON_ARGS=(
"--no-tty"
"--install"
"--environment=aws"
"--deployment-type='ha'"
"--node-role='master-node'"
"--external-load-balancer"
"--master-node-private-ip-list='$MASTER_NODE_PRIVATE_IP_LIST'"
"--openvidu-pro-license='$OPENVIDU_PRO_LICENSE'"
"--domain-name='$DOMAIN'"
"--enabled-modules='$ENABLED_MODULES'"
"--rtc-engine=$OPENVIDU_RTC_ENGINE"
"--redis-password=$REDIS_PASSWORD"
"--mongo-admin-user=$MONGO_ADMIN_USERNAME"
"--mongo-admin-password=$MONGO_ADMIN_PASSWORD"
"--mongo-replica-set-key=$MONGO_REPLICA_SET_KEY"
"--minio-access-key=$MINIO_ACCESS_KEY"
"--minio-secret-key=$MINIO_SECRET_KEY"
"--dashboard-admin-user=$DASHBOARD_ADMIN_USERNAME"
"--dashboard-admin-password=$DASHBOARD_ADMIN_PASSWORD"
"--grafana-admin-user=$GRAFANA_ADMIN_USERNAME"
"--grafana-admin-password=$GRAFANA_ADMIN_PASSWORD"
"--meet-admin-user=$MEET_ADMIN_USER"
"--meet-admin-password=$MEET_ADMIN_SECRET"
"--meet-api-key=$MEET_API_KEY"
"--livekit-api-key=$LIVEKIT_API_KEY"
"--livekit-api-secret=$LIVEKIT_API_SECRET"
)
# Include additional installer flags provided by the user
if [[ "${AdditionalInstallFlags}" != "" ]]; then
IFS=',' read -ra EXTRA_FLAGS <<< "${AdditionalInstallFlags}"
for extra_flag in "${!EXTRA_FLAGS[@]}"; do
# Trim whitespace around each flag
extra_flag="$(echo -e "${!extra_flag}" | sed -e 's/^[ \t]*//' -e 's/[ \t]*$//')"
if [[ "$extra_flag" != "" ]]; then
COMMON_ARGS+=("$extra_flag")
fi
done
fi
if [[ "${!LIVEKIT_TURN_DOMAIN_NAME}" != "none" ]]; then
COMMON_ARGS+=("--turn-domain-name='${!LIVEKIT_TURN_DOMAIN_NAME}'")
fi
# Construct the final command
FINAL_COMMAND="$INSTALL_COMMAND $(printf "%s " "${!COMMON_ARGS[@]}")"
# Install OpenVidu
exec bash -c "$FINAL_COMMAND"
mode: '000755'
owner: root
group: root
'/usr/local/bin/config_s3.sh':
content: !Sub
- |
#!/bin/bash
set -e
# Install dir and config dir
INSTALL_DIR="/opt/openvidu"
CLUSTER_CONFIG_DIR="${!INSTALL_DIR}/config/cluster"
# Config S3 bucket
EXTERNAL_S3_ENDPOINT="https://s3.${AWS::Region}.amazonaws.com"
EXTERNAL_S3_REGION="${AWS::Region}"
EXTERNAL_S3_PATH_STYLE_ACCESS="false"
EXTERNAL_S3_BUCKET_APP_DATA=${S3AppDataBucketResourceName}
EXTERNAL_S3_BUCKET_CLUSTER_DATA=${S3ClusterDataBucketResourceName}
sed -i "s|EXTERNAL_S3_ENDPOINT=.*|EXTERNAL_S3_ENDPOINT=$EXTERNAL_S3_ENDPOINT|" "${!CLUSTER_CONFIG_DIR}/openvidu.env"
sed -i "s|EXTERNAL_S3_REGION=.*|EXTERNAL_S3_REGION=$EXTERNAL_S3_REGION|" "${!CLUSTER_CONFIG_DIR}/openvidu.env"
sed -i "s|EXTERNAL_S3_PATH_STYLE_ACCESS=.*|EXTERNAL_S3_PATH_STYLE_ACCESS=$EXTERNAL_S3_PATH_STYLE_ACCESS|" "${!CLUSTER_CONFIG_DIR}/openvidu.env"
sed -i "s|EXTERNAL_S3_BUCKET_APP_DATA=.*|EXTERNAL_S3_BUCKET_APP_DATA=$EXTERNAL_S3_BUCKET_APP_DATA|" "${!CLUSTER_CONFIG_DIR}/openvidu.env"
sed -i "s|EXTERNAL_S3_BUCKET_CLUSTER_DATA=.*|EXTERNAL_S3_BUCKET_CLUSTER_DATA=$EXTERNAL_S3_BUCKET_CLUSTER_DATA|" "${!CLUSTER_CONFIG_DIR}/openvidu.env"
- S3AppDataBucketResourceName: !If
- CreateRecordingsBucket
- !Ref S3AppDataBucketResource
- !Ref S3AppDataBucketName
S3ClusterDataBucketResourceName: !If
- CreateClusterDataBucket
- !Ref S3ClusterDataBucketResource
- !Ref S3ClusterDataBucketName
mode: "000755"
owner: "root"
group: "root"
'/usr/local/bin/after_install.sh':
content: !Sub |
#!/bin/bash
set -e
# Get current shared secret
SHARED_SECRET=$(aws secretsmanager get-secret-value \
--region ${AWS::Region} \
--secret-id openvidu-ha-${AWS::Region}-${AWS::StackName} \
--query SecretString --output text)
# Save access URLs
DOMAIN=$(echo "$SHARED_SECRET" | jq -r '.DOMAIN_NAME')
DASHBOARD_URL="https://${!DOMAIN}/dashboard/"
GRAFANA_URL="https://${!DOMAIN}/grafana/"
MINIO_URL="https://${!DOMAIN}/minio-console/"
# Update shared secret
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"DOMAIN_NAME": "'"$DOMAIN"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"DASHBOARD_URL": "'"$DASHBOARD_URL"'" }')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"GRAFANA_URL": "'"$GRAFANA_URL"'" }')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"MINIO_URL": "'"$MINIO_URL"'" }')"
# Update shared secret
aws secretsmanager update-secret \
--region ${AWS::Region} \
--secret-id openvidu-ha-${AWS::Region}-${AWS::StackName} \
--secret-string "$SHARED_SECRET"
mode: "000755"
owner: "root"
group: "root"
'/usr/local/bin/update_config_from_secret.sh':
content: !Sub |
#!/bin/bash
set -e
# Token for IMDSv2
TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
# Get current shared secret
SHARED_SECRET=$(aws secretsmanager get-secret-value \
--region ${AWS::Region} \
--secret-id openvidu-ha-${AWS::Region}-${AWS::StackName} \
--query SecretString --output text)
# Installation directory
INSTALL_DIR="/opt/openvidu"
CLUSTER_CONFIG_DIR="${!INSTALL_DIR}/config/cluster"
MASTER_NODE_CONFIG_DIR="${!INSTALL_DIR}/config/node"
# Replace DOMAIN_NAME
export DOMAIN=$(echo $SHARED_SECRET | jq -r .DOMAIN_NAME)
if [[ -n "$DOMAIN" ]]; then
sed -i "s/DOMAIN_NAME=.*/DOMAIN_NAME=$DOMAIN/" "${!CLUSTER_CONFIG_DIR}/openvidu.env"
else
exit 1
fi
# Replace LIVEKIT_TURN_DOMAIN_NAME
export LIVEKIT_TURN_DOMAIN_NAME=$(echo $SHARED_SECRET | jq -r .LIVEKIT_TURN_DOMAIN_NAME)
if [[ -n "$LIVEKIT_TURN_DOMAIN_NAME" ]]; then
sed -i "s/LIVEKIT_TURN_DOMAIN_NAME=.*/LIVEKIT_TURN_DOMAIN_NAME=$LIVEKIT_TURN_DOMAIN_NAME/" "${!CLUSTER_CONFIG_DIR}/openvidu.env"
fi
# Replace rest of the values
sed -i "s/REDIS_PASSWORD=.*/REDIS_PASSWORD=$(echo $SHARED_SECRET | jq -r .REDIS_PASSWORD)/" "${!MASTER_NODE_CONFIG_DIR}/master_node.env"
sed -i "s/OPENVIDU_RTC_ENGINE=.*/OPENVIDU_RTC_ENGINE=$(echo $SHARED_SECRET | jq -r .OPENVIDU_RTC_ENGINE)/" "${!CLUSTER_CONFIG_DIR}/openvidu.env"
sed -i "s/OPENVIDU_PRO_LICENSE=.*/OPENVIDU_PRO_LICENSE=$(echo $SHARED_SECRET | jq -r .OPENVIDU_PRO_LICENSE)/" "${!CLUSTER_CONFIG_DIR}/openvidu.env"
sed -i "s/MONGO_ADMIN_USERNAME=.*/MONGO_ADMIN_USERNAME=$(echo $SHARED_SECRET | jq -r .MONGO_ADMIN_USERNAME)/" "${!CLUSTER_CONFIG_DIR}/openvidu.env"
sed -i "s/MONGO_ADMIN_PASSWORD=.*/MONGO_ADMIN_PASSWORD=$(echo $SHARED_SECRET | jq -r .MONGO_ADMIN_PASSWORD)/" "${!CLUSTER_CONFIG_DIR}/openvidu.env"
sed -i "s/DASHBOARD_ADMIN_USERNAME=.*/DASHBOARD_ADMIN_USERNAME=$(echo $SHARED_SECRET | jq -r .DASHBOARD_ADMIN_USERNAME)/" "${!CLUSTER_CONFIG_DIR}/openvidu.env"
sed -i "s/DASHBOARD_ADMIN_PASSWORD=.*/DASHBOARD_ADMIN_PASSWORD=$(echo $SHARED_SECRET | jq -r .DASHBOARD_ADMIN_PASSWORD)/" "${!CLUSTER_CONFIG_DIR}/openvidu.env"
sed -i "s/MINIO_ACCESS_KEY=.*/MINIO_ACCESS_KEY=$(echo $SHARED_SECRET | jq -r .MINIO_ACCESS_KEY)/" "${!CLUSTER_CONFIG_DIR}/openvidu.env"
sed -i "s/MINIO_SECRET_KEY=.*/MINIO_SECRET_KEY=$(echo $SHARED_SECRET | jq -r .MINIO_SECRET_KEY)/" "${!CLUSTER_CONFIG_DIR}/openvidu.env"
sed -i "s/GRAFANA_ADMIN_USERNAME=.*/GRAFANA_ADMIN_USERNAME=$(echo $SHARED_SECRET | jq -r .GRAFANA_ADMIN_USERNAME)/" "${!CLUSTER_CONFIG_DIR}/openvidu.env"
sed -i "s/GRAFANA_ADMIN_PASSWORD=.*/GRAFANA_ADMIN_PASSWORD=$(echo $SHARED_SECRET | jq -r .GRAFANA_ADMIN_PASSWORD)/" "${!CLUSTER_CONFIG_DIR}/openvidu.env"
sed -i "s/LIVEKIT_API_KEY=.*/LIVEKIT_API_KEY=$(echo $SHARED_SECRET | jq -r .LIVEKIT_API_KEY)/" "${!CLUSTER_CONFIG_DIR}/openvidu.env"
sed -i "s/LIVEKIT_API_SECRET=.*/LIVEKIT_API_SECRET=$(echo $SHARED_SECRET | jq -r .LIVEKIT_API_SECRET)/" "${!CLUSTER_CONFIG_DIR}/openvidu.env"
sed -i "s/MEET_ADMIN_USER=.*/MEET_ADMIN_USER=$(echo $SHARED_SECRET | jq -r .MEET_ADMIN_USER)/" "${!CLUSTER_CONFIG_DIR}/master_node/meet.env"
sed -i "s/MEET_ADMIN_SECRET=.*/MEET_ADMIN_SECRET=$(echo $SHARED_SECRET | jq -r .MEET_ADMIN_SECRET)/" "${!CLUSTER_CONFIG_DIR}/master_node/meet.env"
sed -i "s/MEET_API_KEY=.*/MEET_API_KEY=$(echo $SHARED_SECRET | jq -r .MEET_API_KEY)/" "${!CLUSTER_CONFIG_DIR}/master_node/meet.env"
sed -i "s/ENABLED_MODULES=.*/ENABLED_MODULES=$(echo $SHARED_SECRET | jq -r .ENABLED_MODULES)/" "${!CLUSTER_CONFIG_DIR}/openvidu.env"
# Update URLs in secret
DASHBOARD_URL="https://${!DOMAIN}/dashboard/"
GRAFANA_URL="https://${!DOMAIN}/grafana/"
MINIO_URL="https://${!DOMAIN}/minio-console/"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"DOMAIN_NAME": "'"$DOMAIN"'" }')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"DASHBOARD_URL": "'"$DASHBOARD_URL"'" }')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"GRAFANA_URL": "'"$GRAFANA_URL"'" }')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"MINIO_URL": "'"$MINIO_URL"'" }')"
aws secretsmanager update-secret \
--region ${AWS::Region} \
--secret-id openvidu-ha-${AWS::Region}-${AWS::StackName} \
--secret-string "$SHARED_SECRET"
mode: "000755"
owner: "root"
group: "root"
'/usr/local/bin/update_secret_from_config.sh':
content: !Sub |
#!/bin/bash
set -e
# Get current shared secret
SHARED_SECRET=$(aws secretsmanager get-secret-value \
--region ${AWS::Region} \
--secret-id openvidu-ha-${AWS::Region}-${AWS::StackName} \
--query SecretString --output text)
# Installation directory
INSTALL_DIR="/opt/openvidu"
CLUSTER_CONFIG_DIR="${!INSTALL_DIR}/config/cluster"
MASTER_NODE_CONFIG_DIR="${!INSTALL_DIR}/config/node"
# Update shared secret
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"REDIS_PASSWORD": "'"$(/usr/local/bin/get_value_from_config.sh REDIS_PASSWORD "${!MASTER_NODE_CONFIG_DIR}/master_node.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"DOMAIN_NAME": "'"$(/usr/local/bin/get_value_from_config.sh DOMAIN_NAME "${!CLUSTER_CONFIG_DIR}/openvidu.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"LIVEKIT_TURN_DOMAIN_NAME": "'"$(/usr/local/bin/get_value_from_config.sh LIVEKIT_TURN_DOMAIN_NAME "${!CLUSTER_CONFIG_DIR}/openvidu.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"OPENVIDU_RTC_ENGINE": "'"$(/usr/local/bin/get_value_from_config.sh OPENVIDU_RTC_ENGINE "${!CLUSTER_CONFIG_DIR}/openvidu.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"OPENVIDU_PRO_LICENSE": "'"$(/usr/local/bin/get_value_from_config.sh OPENVIDU_PRO_LICENSE "${!CLUSTER_CONFIG_DIR}/openvidu.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"MONGO_ADMIN_USERNAME": "'"$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_USERNAME "${!CLUSTER_CONFIG_DIR}/openvidu.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"MONGO_ADMIN_PASSWORD": "'"$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_PASSWORD "${!CLUSTER_CONFIG_DIR}/openvidu.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"MINIO_ACCESS_KEY": "'"$(/usr/local/bin/get_value_from_config.sh MINIO_ACCESS_KEY "${!CLUSTER_CONFIG_DIR}/openvidu.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"MINIO_SECRET_KEY": "'"$(/usr/local/bin/get_value_from_config.sh MINIO_SECRET_KEY "${!CLUSTER_CONFIG_DIR}/openvidu.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"DASHBOARD_ADMIN_USERNAME": "'"$(/usr/local/bin/get_value_from_config.sh DASHBOARD_ADMIN_USERNAME "${!CLUSTER_CONFIG_DIR}/openvidu.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"DASHBOARD_ADMIN_PASSWORD": "'"$(/usr/local/bin/get_value_from_config.sh DASHBOARD_ADMIN_PASSWORD "${!CLUSTER_CONFIG_DIR}/openvidu.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"GRAFANA_ADMIN_USERNAME": "'"$(/usr/local/bin/get_value_from_config.sh GRAFANA_ADMIN_USERNAME "${!CLUSTER_CONFIG_DIR}/openvidu.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"GRAFANA_ADMIN_PASSWORD": "'"$(/usr/local/bin/get_value_from_config.sh GRAFANA_ADMIN_PASSWORD "${!CLUSTER_CONFIG_DIR}/openvidu.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"LIVEKIT_API_KEY": "'"$(/usr/local/bin/get_value_from_config.sh LIVEKIT_API_KEY "${!CLUSTER_CONFIG_DIR}/openvidu.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"LIVEKIT_API_SECRET": "'"$(/usr/local/bin/get_value_from_config.sh LIVEKIT_API_SECRET "${!CLUSTER_CONFIG_DIR}/openvidu.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"MEET_ADMIN_USER": "'"$(/usr/local/bin/get_value_from_config.sh MEET_ADMIN_USER "${!CLUSTER_CONFIG_DIR}/master_node/meet.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"MEET_ADMIN_SECRET": "'"$(/usr/local/bin/get_value_from_config.sh MEET_ADMIN_SECRET "${!CLUSTER_CONFIG_DIR}/master_node/meet.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"MEET_API_KEY": "'"$(/usr/local/bin/get_value_from_config.sh MEET_API_KEY "${!CLUSTER_CONFIG_DIR}/master_node/meet.env")"'"}')"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"ENABLED_MODULES": "'"$(/usr/local/bin/get_value_from_config.sh ENABLED_MODULES "${!CLUSTER_CONFIG_DIR}/openvidu.env")"'"}')"
# Update shared secret
aws secretsmanager update-secret \
--region ${AWS::Region} \
--secret-id openvidu-ha-${AWS::Region}-${AWS::StackName} \
--secret-string "$SHARED_SECRET"
mode: "000755"
owner: "root"
group: "root"
'/usr/local/bin/get_value_from_config.sh':
content: |
#!/bin/bash
set -e
# Function to get the value of a given key from the environment file
get_value() {
local key="$1"
local file_path="$2"
# Use grep to find the line with the key, ignoring lines starting with #
# Use awk to split on '=' and print the second field, which is the value
local value=$(grep -E "^\s*$key\s*=" "$file_path" | awk -F= '{print $2}' | sed 's/#.*//; s/^\s*//; s/\s*$//')
# If the value is empty, return "none"
if [ -z "$value" ]; then
echo "none"
else
echo "$value"
fi
}
# Check if the correct number of arguments are supplied
if [ "$#" -ne 2 ]; then
echo "Usage: $0 <key> <file_path>"
exit 1
fi
# Get the key and file path from the arguments
key="$1"
file_path="$2"
# Get and print the value
get_value "$key" "$file_path"
mode: "000755"
owner: "root"
group: "root"
'/usr/local/bin/store_secret.sh':
content: !Sub |
#!/bin/bash
set -e
# Modes: save, generate
# save mode: save the secret in the secret manager
# generate mode: generate a random password and save it in the secret manager
MODE="$1"
SHARED_SECRET="$(aws secretsmanager get-secret-value \
--region ${AWS::Region} \
--secret-id ${OpenViduSharedInfo} \
--query SecretString --output text)"
if [[ "$MODE" == "generate" ]]; then
SECRET_KEY_NAME="$2"
PREFIX="${!3:-}"
LENGTH="${!4:-44}"
RANDOM_PASSWORD="$(openssl rand -base64 64 | tr -d '+/=\n' | cut -c -${!LENGTH})"
RANDOM_PASSWORD="${!PREFIX}${!RANDOM_PASSWORD}"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"'"$SECRET_KEY_NAME"'": "'"$RANDOM_PASSWORD"'"}')"
aws secretsmanager update-secret \
--region ${AWS::Region} \
--secret-id ${OpenViduSharedInfo} \
--secret-string "$SHARED_SECRET" > /dev/null 2>&1
echo "$RANDOM_PASSWORD"
elif [[ "$MODE" == "save" ]]; then
SECRET_KEY_NAME="$2"
SECRET_VALUE="$3"
SHARED_SECRET="$(echo "$SHARED_SECRET" | jq '. + {"'"$SECRET_KEY_NAME"'": "'"$SECRET_VALUE"'"}')"
aws secretsmanager update-secret \
--region ${AWS::Region} \
--secret-id ${OpenViduSharedInfo} \
--secret-string "$SHARED_SECRET" > /dev/null 2>&1
echo "$SECRET_VALUE"
else
exit 1
fi
mode: "000755"
owner: "root"
group: "root"
'/usr/local/bin/restart.sh':
content: |
#!/bin/bash
set -e
# Stop all services
systemctl stop openvidu
# Update config from secret
/usr/local/bin/update_config_from_secret.sh
# Start all services
systemctl start openvidu
mode: "000755"
owner: "root"
group: "root"
Properties:
LaunchTemplateName: !Sub 'openvidu-ha-master-${AWS::Region}-${AWS::StackName}'
LaunchTemplateData:
# Enable IMDSv2
MetadataOptions:
HttpEndpoint: enabled
HttpPutResponseHopLimit: 1
HttpTokens: required
IamInstanceProfile:
Name: !Ref OpenViduMasterInstanceProfile
ImageId: !Ref AmiId
InstanceType: !Ref MasterNodeInstanceType
KeyName: !Ref KeyName
SecurityGroupIds:
- !Ref OpenViduMasterNodeSG
BlockDeviceMappings:
- DeviceName: /dev/sda1
Ebs:
VolumeSize: !Ref MasterNodesDiskSize
VolumeType: gp3
DeleteOnTermination: true
OpenViduMasterNode1:
Type: AWS::EC2::Instance
Properties:
LaunchTemplate:
LaunchTemplateId: !Ref OpenViduMasterLaunchTemplate
Version: !GetAtt OpenViduMasterLaunchTemplate.LatestVersionNumber
Tags:
- Key: Name
Value: !Sub ${AWS::StackName} - OpenVidu HA - Master Node 1
SubnetId: !GetAtt SubnetProcessor.Subnet1
UserData:
Fn::Base64: !Sub |
#!/bin/bash
set -eu -o pipefail
apt-get update && apt-get install -y \
python3-pip \
ec2-instance-connect
pip3 install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz
cfn-init -v --region ${AWS::Region} --stack ${AWS::StackName} --resource OpenViduMasterLaunchTemplate
# Install OpenVidu
/usr/local/bin/install.sh "MasterNodesWaitCondition1" || { echo "[OpenVidu] error installing OpenVidu"; exit 1; }
# Config S3 bucket
/usr/local/bin/config_s3.sh || { echo "[OpenVidu] error configuring S3 bucket"; exit 1; }
# Start OpenVidu
systemctl start openvidu || { echo "[OpenVidu] error starting OpenVidu"; exit 1; }
# Update shared secret
/usr/local/bin/after_install.sh || { echo "[OpenVidu] error updating shared secret"; exit 1; }
# Launch on reboot
echo "@reboot /usr/local/bin/restart.sh &> /var/log/openvidu-restart.log" | crontab
MasterNodesWaitCondition1:
Type: 'AWS::CloudFormation::WaitCondition'
CreationPolicy:
ResourceSignal:
Timeout: PT10M
Count: '1'
OpenViduMasterNode2:
Type: AWS::EC2::Instance
DependsOn: MasterNodesWaitCondition1
Properties:
LaunchTemplate:
LaunchTemplateId: !Ref OpenViduMasterLaunchTemplate
Version: !GetAtt OpenViduMasterLaunchTemplate.LatestVersionNumber
Tags:
- Key: Name
Value: !Sub ${AWS::StackName} - OpenVidu HA - Master Node 2
SubnetId: !GetAtt SubnetProcessor.Subnet2
UserData:
Fn::Base64: !Sub |
#!/bin/bash
set -eu -o pipefail
apt-get update && apt-get install -y \
python3-pip \
ec2-instance-connect
pip3 install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz
cfn-init -v --region ${AWS::Region} --stack ${AWS::StackName} --resource OpenViduMasterLaunchTemplate
# Install OpenVidu
/usr/local/bin/install.sh "MasterNodesWaitCondition2" || { echo "[OpenVidu] error installing OpenVidu"; exit 1; }
# Config S3 bucket
/usr/local/bin/config_s3.sh || { echo "[OpenVidu] error configuring S3 bucket"; exit 1; }
# Start OpenVidu
systemctl start openvidu || { echo "[OpenVidu] error starting OpenVidu"; exit 1; }
# Update shared secret
/usr/local/bin/after_install.sh || { echo "[OpenVidu] error updating shared secret"; exit 1; }
# Launch on reboot
echo "@reboot /usr/local/bin/restart.sh &> /var/log/openvidu-restart.log" | crontab
MasterNodesWaitCondition2:
Type: 'AWS::CloudFormation::WaitCondition'
CreationPolicy:
ResourceSignal:
Timeout: PT10M
Count: '1'
OpenViduMasterNode3:
Type: AWS::EC2::Instance
DependsOn: MasterNodesWaitCondition2
Properties:
LaunchTemplate:
LaunchTemplateId: !Ref OpenViduMasterLaunchTemplate
Version: !GetAtt OpenViduMasterLaunchTemplate.LatestVersionNumber
Tags:
- Key: Name
Value: !Sub ${AWS::StackName} - OpenVidu HA - Master Node 3
SubnetId: !GetAtt SubnetProcessor.Subnet3
UserData:
Fn::Base64: !Sub |
#!/bin/bash
set -eu -o pipefail
apt-get update && apt-get install -y \
python3-pip \
ec2-instance-connect
pip3 install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz
cfn-init -v --region ${AWS::Region} --stack ${AWS::StackName} --resource OpenViduMasterLaunchTemplate
# Install OpenVidu
/usr/local/bin/install.sh "MasterNodesWaitCondition3" || { echo "[OpenVidu] error installing OpenVidu"; exit 1; }
# Config S3 bucket
/usr/local/bin/config_s3.sh || { echo "[OpenVidu] error configuring S3 bucket"; exit 1; }
# Start OpenVidu
systemctl start openvidu || { echo "[OpenVidu] error starting OpenVidu"; exit 1; }
# Update shared secret
/usr/local/bin/after_install.sh || { echo "[OpenVidu] error updating shared secret"; exit 1; }
# Launch on reboot
systemctl enable crond.service
systemctl start crond.service
echo "@reboot /usr/local/bin/restart.sh &> /var/log/openvidu-restart.log" | crontab
MasterNodesWaitCondition3:
Type: 'AWS::CloudFormation::WaitCondition'
CreationPolicy:
ResourceSignal:
Timeout: PT10M
Count: '1'
OpenViduMasterNode4:
Type: AWS::EC2::Instance
DependsOn: MasterNodesWaitCondition3
Properties:
LaunchTemplate:
LaunchTemplateId: !Ref OpenViduMasterLaunchTemplate
Version: !GetAtt OpenViduMasterLaunchTemplate.LatestVersionNumber
Tags:
- Key: Name
Value: !Sub ${AWS::StackName} - OpenVidu HA - Master Node 4
SubnetId: !GetAtt SubnetProcessor.Subnet4
UserData:
Fn::Base64: !Sub |
#!/bin/bash
set -eu -o pipefail
apt-get update && apt-get install -y \
python3-pip \
ec2-instance-connect
pip3 install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz
cfn-init -v --region ${AWS::Region} --stack ${AWS::StackName} --resource OpenViduMasterLaunchTemplate
# Install OpenVidu
/usr/local/bin/install.sh "MasterNodesWaitCondition4" || { echo "[OpenVidu] error installing OpenVidu"; exit 1; }
# Config S3 bucket
/usr/local/bin/config_s3.sh || { echo "[OpenVidu] error configuring S3 bucket"; exit 1; }
# Start OpenVidu
systemctl start openvidu || { echo "[OpenVidu] error starting OpenVidu"; exit 1; }
# Update shared secret
/usr/local/bin/after_install.sh || { echo "[OpenVidu] error updating shared secret"; exit 1; }
# Launch on reboot
systemctl enable crond.service
systemctl start crond.service
echo "@reboot /usr/local/bin/restart.sh &> /var/log/openvidu-restart.log" | crontab
MasterNodesWaitCondition4:
Type: 'AWS::CloudFormation::WaitCondition'
CreationPolicy:
ResourceSignal:
Timeout: PT10M
Count: '1'
OpenViduMediaNodeLaunchTemplate:
Type: AWS::EC2::LaunchTemplate
Metadata:
Comment: Launch template for OpenVidu Media Node
AWS::CloudFormation::Init:
config:
files:
'/usr/local/bin/install.sh':
content: !Sub |
#!/bin/bash
set -e
YQ_VERSION=v4.44.5
# Install dependencies
apt-get update && apt-get install -y \
curl \
unzip \
jq \
wget
wget https://github.com/mikefarah/yq/releases/download/${!YQ_VERSION}/yq_linux_amd64.tar.gz -O - |\
tar xz && mv yq_linux_amd64 /usr/bin/yq
# Install aws-cli
curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"
unzip -qq awscliv2.zip
./aws/install
rm -rf awscliv2.zip aws
# Token for IMDSv2
TOKEN="$(curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")"
# Get own private IP
PRIVATE_IP="$(curl -H "X-aws-ec2-metadata-token: $TOKEN" -s http://169.254.169.254/latest/meta-data/local-ipv4)"
SHARED_SECRET="$(aws secretsmanager get-secret-value \
--region ${AWS::Region} \
--secret-id openvidu-ha-${AWS::Region}-${AWS::StackName} \
--query SecretString --output text || echo 'none')"
if [[ "$SHARED_SECRET" == "none" ]]; then
echo "Error: Shared secret not found"
exit 1
fi
# Get OpenVidu Media Nodes version to deploy
OPENVIDU_VERSION=$(echo "$SHARED_SECRET" | jq -r '.OPENVIDU_VERSION')
if [[ "$OPENVIDU_VERSION" == "none" ]]; then
echo "OpenVidu version not found"
exit 1
fi
ALL_SECRETS_GENERATED=$(echo "$SHARED_SECRET" | jq -r '.ALL_SECRETS_GENERATED')
if [[ "$ALL_SECRETS_GENERATED" == "none" ]]; then
echo "Error: Secrets not generated"
exit 1
fi
MASTER_NODE_1_PRIVATE_IP=$(echo "$SHARED_SECRET" | jq -r '.MASTER_NODE_1_PRIVATE_IP')
MASTER_NODE_2_PRIVATE_IP=$(echo "$SHARED_SECRET" | jq -r '.MASTER_NODE_2_PRIVATE_IP')
MASTER_NODE_3_PRIVATE_IP=$(echo "$SHARED_SECRET" | jq -r '.MASTER_NODE_3_PRIVATE_IP')
MASTER_NODE_4_PRIVATE_IP=$(echo "$SHARED_SECRET" | jq -r '.MASTER_NODE_4_PRIVATE_IP')
MASTER_NODE_PRIVATE_IP_LIST="$MASTER_NODE_1_PRIVATE_IP,$MASTER_NODE_2_PRIVATE_IP,$MASTER_NODE_3_PRIVATE_IP,$MASTER_NODE_4_PRIVATE_IP"
REDIS_PASSWORD=$(echo "$SHARED_SECRET" | jq -r '.REDIS_PASSWORD')
# Base command
INSTALL_COMMAND="sh <(curl -fsSL http://get.openvidu.io/pro/ha/$OPENVIDU_VERSION/install_ov_media_node.sh)"
# Common arguments
COMMON_ARGS=(
"--no-tty"
"--install"
"--environment=aws"
"--deployment-type='ha'"
"--node-role='media-node'"
"--master-node-private-ip-list=$MASTER_NODE_PRIVATE_IP_LIST"
"--private-ip=$PRIVATE_IP"
"--redis-password=$REDIS_PASSWORD"
)
# Construct the final command with all arguments
FINAL_COMMAND="$INSTALL_COMMAND $(printf "%s " "${!COMMON_ARGS[@]}")"
# Install OpenVidu
exec bash -c "$FINAL_COMMAND"
mode: '000755'
owner: root
group: root
'/usr/local/bin/set_as_unhealthy.sh':
content: !Sub |
#!/bin/bash
set -e
# Token for IMDSv2
TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
# Get own instance ID
INSTANCE_ID=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/instance-id)
# Set instance as unhealthy
aws autoscaling set-instance-health \
--region ${AWS::Region} \
--instance-id "$INSTANCE_ID" \
--health-status Unhealthy
mode: "000755"
owner: "root"
group: "root"
'/usr/local/bin/stop_media_node.sh':
content: !Sub |
#!/bin/bash
set -e
# Token for IMDSv2
TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
# Get own instance ID
INSTANCE_ID=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/instance-id)
ASG_NAME=openvidu-ha-media-asg-${AWS::Region}-${AWS::StackName}
# Execute if docker is installed
if [ -x "$(command -v docker)" ]; then
echo "Stopping media node services and waiting for termination..."
docker container kill --signal=SIGQUIT openvidu || true
docker container kill --signal=SIGQUIT ingress || true
docker container kill --signal=SIGQUIT egress || true
for agent_container in $(docker ps --filter "label=openvidu-agent=true" --format '{{.Names}}'); do
docker container kill --signal=SIGQUIT "$agent_container"
done
TIME_PASSED=0
HEARTBEAT_MAX=1800
# Wait for running containers to not be openvidu, ingress, egress or an openvidu agent
while [ $(docker ps --filter "label=openvidu-agent=true" -q | wc -l) -gt 0 ] || \
[ $(docker inspect -f '{{.State.Running}}' openvidu 2>/dev/null) == "true" ] || \
[ $(docker inspect -f '{{.State.Running}}' ingress 2>/dev/null) == "true" ] || \
[ $(docker inspect -f '{{.State.Running}}' egress 2>/dev/null) == "true" ]; do
echo "Waiting for containers to stop..."
sleep 5
TIME_PASSED=$((TIME_PASSED+5))
if [ $TIME_PASSED -ge $HEARTBEAT_MAX ]; then
echo "Increase lifecycle hook timeout to continue waiting for termination"
# Increase lifecycle hook timeout
aws autoscaling record-lifecycle-action-heartbeat \
--region ${AWS::Region} \
--lifecycle-hook-name StopMediaNodeLifecycleHook-${AWS::Region}-${AWS::StackName} \
--auto-scaling-group-name "$ASG_NAME" \
--instance-id "$INSTANCE_ID"
TIME_PASSED=0
fi
done
fi
aws autoscaling complete-lifecycle-action \
--region ${AWS::Region} \
--lifecycle-hook-name StopMediaNodeLifecycleHook-${AWS::Region}-${AWS::StackName} \
--auto-scaling-group-name "$ASG_NAME" \
--lifecycle-action-result CONTINUE \
--instance-id "$INSTANCE_ID"
mode: "000755"
owner: "root"
group: "root"
Properties:
LaunchTemplateName: !Sub 'openvidu-ha-media-${AWS::Region}-${AWS::StackName}'
LaunchTemplateData:
# Enable IMDSv2 by default
MetadataOptions:
HttpEndpoint: enabled
HttpPutResponseHopLimit: 1
HttpTokens: required
IamInstanceProfile:
Arn: !GetAtt OpenViduMediaInstanceProfile.Arn
SecurityGroupIds:
- !GetAtt OpenViduMediaNodeSG.GroupId
ImageId: !Ref AmiId
KeyName: !Ref KeyName
InstanceType: !Ref MediaNodeInstanceType
UserData:
Fn::Base64: !Sub |
#!/bin/bash
set -eu -o pipefail
apt-get update && apt-get install -y \
python3-pip \
ec2-instance-connect
pip3 install https://s3.amazonaws.com/cloudformation-examples/aws-cfn-bootstrap-py3-latest.tar.gz
cfn-init --region ${AWS::Region} --stack ${AWS::StackId} --resource OpenViduMediaNodeLaunchTemplate
export HOME="/root"
# Install OpenVidu
/usr/local/bin/install.sh || { echo "[OpenVidu] error installing OpenVidu"; /usr/local/bin/set_as_unhealthy.sh; exit 1; }
# Start OpenVidu
systemctl start openvidu || { echo "[OpenVidu] error starting OpenVidu"; /usr/local/bin/set_as_unhealthy.sh; exit 1; }
# Wait for the app
# /usr/local/bin/check_app_ready.sh
BlockDeviceMappings:
- DeviceName: /dev/sda1
Ebs:
VolumeType: gp3
DeleteOnTermination: true
VolumeSize: 50
OpenViduMediaNodeASG:
DependsOn:
- OpenViduMediaInstanceProfile
- OpenViduMasterInstanceProfile
- StopMediaNodeCloudWatchEventRule
Type: AWS::AutoScaling::AutoScalingGroup
Properties:
AutoScalingGroupName: !Sub openvidu-ha-media-asg-${AWS::Region}-${AWS::StackName}
LaunchTemplate:
LaunchTemplateId: !Ref OpenViduMediaNodeLaunchTemplate
Version: !GetAtt OpenViduMediaNodeLaunchTemplate.DefaultVersionNumber
TargetGroupARNs:
Fn::If:
- TurnTLSIsEnabled
- - !Ref OpenViduMediaNodeRTMPTG
- !Ref OpenViduMediaNodeTurnTLSTG
- - !Ref OpenViduMediaNodeRTMPTG
MinSize: !Ref MinNumberOfMediaNodes
MaxSize: !Ref MaxNumberOfMediaNodes
DesiredCapacity: !Ref InitialNumberOfMediaNodes
VPCZoneIdentifier: !Ref OpenViduMediaNodeSubnets
Tags:
- Key: Name
Value: !Sub ${AWS::StackName} - OpenVidu HA - Media Node
PropagateAtLaunch: true
StopMediaNodeLifecycleHook:
Type: 'AWS::AutoScaling::LifecycleHook'
Properties:
LifecycleHookName: !Sub StopMediaNodeLifecycleHook-${AWS::Region}-${AWS::StackName}
AutoScalingGroupName: !Ref OpenViduMediaNodeASG
LifecycleTransition: 'autoscaling:EC2_INSTANCE_TERMINATING'
DefaultResult: 'CONTINUE'
HeartbeatTimeout: 3600
StopMediaNodeDocumentRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- ssm.amazonaws.com
Action:
- sts:AssumeRole
Policies:
- PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- ssm:DescribeInstanceInformation
- ssm:ListCommands
- ssm:ListCommandInvocations
Resource: "*"
- Effect: Allow
Action:
- ssm:SendCommand
Resource: !Sub arn:${AWS::Partition}:ssm:${AWS::Region}::document/AWS-RunShellScript
- Action:
- ssm:SendCommand
Resource: !Sub arn:${AWS::Partition}:ec2:*:*:instance/*
Condition:
StringEquals:
'aws:ResourceTag/aws:cloudformation:stack-name': !Ref 'AWS::StackName'
Effect: Allow
PolicyName: SSM-Automation-Policy
StopMediaNodeAutomationDocument:
Type: AWS::SSM::Document
Properties:
Name:
Fn::Join:
# Generate a not too long and unique document name
# Getting a unique identifier from the stack id
- ''
- - 'StopMediaNodeAutomationDocument-'
- !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]
DocumentType: Automation
Content:
schemaVersion: '0.3'
assumeRole: "{{automationAssumeRole}}"
description: This document stops the OpenVidu services in a Media Node and
terminates the instance. It stop the OpenVidu services without interrupting
the running sessions, ingress and egress running in the Media Node.
Also it sends a CONTINUE signal to the Auto Scaling Group to continue the
instance termination when the services are stopped.
parameters:
InstanceId:
type: String
automationAssumeRole:
type: String
default: !GetAtt StopMediaNodeDocumentRole.Arn
description: "(Required) The ARN of the role that allows Automation to
perform the actions."
mainSteps:
- name: RunCommand
action: aws:runCommand
inputs:
DocumentName: AWS-RunShellScript
InstanceIds:
- "{{ InstanceId }}"
Parameters:
# 24 hours as a timeout to wait for the instance to stop all services
executionTimeout: "60"
commands:
- nohup /usr/local/bin/stop_media_node.sh > /var/log/stop_media_node.log 2>&1 &
StopMediaNodeCloudWatchEventRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service:
- events.amazonaws.com
Action:
- sts:AssumeRole
Policies:
- PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- ssm:StartAutomationExecution
Resource: !Sub arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:automation-definition/${StopMediaNodeAutomationDocument}:$DEFAULT
PolicyName: !Sub StopMediaNodeCloudWatchEventPolicy-${AWS::Region}-${AWS::StackName}
- PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- iam:PassRole
Resource: !GetAtt StopMediaNodeDocumentRole.Arn
PolicyName: Pass-Role-SSM-Automation-Policy
StopMediaNodeCloudWatchEventRule:
Type: AWS::Events::Rule
Properties:
Description: Rule to trigger the StopMediaNodeAutomationDocument when an instance is terminated
EventPattern:
source:
- aws.autoscaling
detail-type:
- EC2 Instance-terminate Lifecycle Action
detail:
AutoScalingGroupName:
- !Sub openvidu-ha-media-asg-${AWS::Region}-${AWS::StackName}
Name:
Fn::Join:
# Generate a not too long and unique rule name
# Getting a unique identifier from the stack id
- ''
- - StopMediaNodeCloudWatchEventRule-
- !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]
Targets:
- Arn: !Sub arn:${AWS::Partition}:ssm:${AWS::Region}:${AWS::AccountId}:automation-definition/${StopMediaNodeAutomationDocument}:$DEFAULT
RoleArn: !GetAtt StopMediaNodeCloudWatchEventRole.Arn
Id:
Fn::Join:
# Generate a not too long and unique target id
# Getting a unique identifier from the stack id
- ''
- - StopMediaNodeCloudWatchEventRule-
- !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]
InputTransformer:
InputPathsMap:
instanceid: "$.detail.EC2InstanceId"
InputTemplate: |
{
"InstanceId": [ <instanceid> ]
}
OpenViduMediaNodeASGScalingPolicy:
Type: AWS::AutoScaling::ScalingPolicy
Properties:
AutoScalingGroupName: !Ref OpenViduMediaNodeASG
PolicyType: TargetTrackingScaling
EstimatedInstanceWarmup: 120
TargetTrackingConfiguration:
PredefinedMetricSpecification:
PredefinedMetricType: ASGAverageCPUUtilization
TargetValue: !Ref ScaleTargetCPU
OpenViduMasterNodeSG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for OpenVidu Master Node
GroupName: !Sub openvidu-ha-master-sg-${AWS::Region}-${AWS::StackName}
VpcId: !Ref OpenViduVPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIpv6: ::/0
OpenViduLoadBalancerToMasterIngressSG:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref OpenViduMasterNodeSG
IpProtocol: tcp
FromPort: 7880
ToPort: 7880
SourceSecurityGroupId: !Ref OpenViduLoadBalancerSG
OpenViduMasterToMasterRedisIngressSG:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref OpenViduMasterNodeSG
IpProtocol: tcp
FromPort: 7000
ToPort: 7001
SourceSecurityGroupId: !Ref OpenViduMasterNodeSG
OpenViduMediaNodeToMasterRedisIngressSG:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref OpenViduMasterNodeSG
IpProtocol: tcp
FromPort: 7000
ToPort: 7001
SourceSecurityGroupId: !Ref OpenViduMediaNodeSG
OpenViduMasterToMasterMinioIngressSG:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref OpenViduMasterNodeSG
IpProtocol: tcp
FromPort: 9100
ToPort: 9100
SourceSecurityGroupId: !Ref OpenViduMasterNodeSG
OpenViduMasterToMasterMinioConsoleSG:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref OpenViduMasterNodeSG
IpProtocol: tcp
FromPort: 9101
ToPort: 9101
SourceSecurityGroupId: !Ref OpenViduMasterNodeSG
OpenViduMediaNodeToMasterMinioIngressSG:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref OpenViduMasterNodeSG
IpProtocol: tcp
FromPort: 9100
ToPort: 9100
SourceSecurityGroupId: !Ref OpenViduMediaNodeSG
OpenViduMasterToMasterMongoIngressSG:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref OpenViduMasterNodeSG
IpProtocol: tcp
FromPort: 20000
ToPort: 20000
SourceSecurityGroupId: !Ref OpenViduMasterNodeSG
OpenViduMediaNodeToMasterMongoIngressSG:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref OpenViduMasterNodeSG
IpProtocol: tcp
FromPort: 20000
ToPort: 20000
SourceSecurityGroupId: !Ref OpenViduMediaNodeSG
OpenViduMasterToMasterMimirGrpcIngressSG:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref OpenViduMasterNodeSG
IpProtocol: tcp
FromPort: 9095
ToPort: 9095
SourceSecurityGroupId: !Ref OpenViduMasterNodeSG
OpenViduMasterToMasterMimirGossipIngressSG:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref OpenViduMasterNodeSG
IpProtocol: tcp
FromPort: 7946
ToPort: 7946
SourceSecurityGroupId: !Ref OpenViduMasterNodeSG
OpenViduMediaNodeToMasterHTTPMimirSG:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref OpenViduMasterNodeSG
IpProtocol: tcp
FromPort: 9009
ToPort: 9009
SourceSecurityGroupId: !Ref OpenViduMediaNodeSG
OpenViduMasterToMasterLokiGrpcIngressSG:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref OpenViduMasterNodeSG
IpProtocol: tcp
FromPort: 9096
ToPort: 9096
SourceSecurityGroupId: !Ref OpenViduMasterNodeSG
OpenViduMasterToMasterLokiGossipIngressSG:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref OpenViduMasterNodeSG
IpProtocol: tcp
FromPort: 7947
ToPort: 7947
SourceSecurityGroupId: !Ref OpenViduMasterNodeSG
OpenViduMasterToMasterDashboardsIngressSG:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref OpenViduMasterNodeSG
IpProtocol: tcp
FromPort: 5000
ToPort: 5000
SourceSecurityGroupId: !Ref OpenViduMasterNodeSG
OpenViduMasterToMasterGrafanaIngressSG:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref OpenViduMasterNodeSG
IpProtocol: tcp
FromPort: 3000
ToPort: 3000
SourceSecurityGroupId: !Ref OpenViduMasterNodeSG
OpenViduMediaNodeToMasterHTTPLokiSG:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref OpenViduMasterNodeSG
IpProtocol: tcp
FromPort: 3100
ToPort: 3100
SourceSecurityGroupId: !Ref OpenViduMediaNodeSG
OpenViduMasterToMasterV2CompatibilityIngress:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref OpenViduMasterNodeSG
IpProtocol: tcp
FromPort: 4443
ToPort: 4443
SourceSecurityGroupId: !Ref OpenViduMasterNodeSG
OpenViduMediaNodeToMasterV2CompatibilityWebhookIngress:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !GetAtt OpenViduMasterNodeSG.GroupId
IpProtocol: tcp
FromPort: 4443
ToPort: 4443
SourceSecurityGroupId: !GetAtt OpenViduMediaNodeSG.GroupId
OpenViduMasterToMasterDefaultAppIngress:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref OpenViduMasterNodeSG
IpProtocol: tcp
FromPort: 6080
ToPort: 6080
SourceSecurityGroupId: !Ref OpenViduMasterNodeSG
OpenViduMediaNodeToMasterDefaultAppWebhookIngress:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !GetAtt OpenViduMasterNodeSG.GroupId
IpProtocol: tcp
FromPort: 6080
ToPort: 6080
SourceSecurityGroupId: !GetAtt OpenViduMediaNodeSG.GroupId
OpenViduMediaNodeSG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for OpenVidu Media Node
GroupName: !Sub openvidu-ha-media-sg-${AWS::Region}-${AWS::StackName}
VpcId: !Ref OpenViduVPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 22
ToPort: 22
CidrIpv6: ::/0
- IpProtocol: udp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
- IpProtocol: udp
FromPort: 443
ToPort: 443
CidrIpv6: ::/0
- IpProtocol: tcp
FromPort: 7881
ToPort: 7881
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 7881
ToPort: 7881
CidrIpv6: ::/0
- IpProtocol: udp
FromPort: 7885
ToPort: 7885
CidrIp: 0.0.0.0/0
- IpProtocol: udp
FromPort: 7885
ToPort: 7885
CidrIpv6: ::/0
- IpProtocol: udp
FromPort: 50000
ToPort: 60000
CidrIp: 0.0.0.0/0
- IpProtocol: udp
FromPort: 50000
ToPort: 60000
CidrIpv6: ::/0
- IpProtocol: tcp
FromPort: 50000
ToPort: 60000
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 50000
ToPort: 60000
CidrIpv6: ::/0
OpenViduLoadBalancerToMediaNodeRTMPIngressSG:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref OpenViduMediaNodeSG
IpProtocol: tcp
FromPort: 1945
ToPort: 1945
SourceSecurityGroupId: !Ref OpenViduLoadBalancerSG
OpenViduLoadBalancerToMediaNodeIngressHealthCheckSG:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref OpenViduMediaNodeSG
IpProtocol: tcp
FromPort: 9092
ToPort: 9092
SourceSecurityGroupId: !Ref OpenViduLoadBalancerSG
OpenViduLoadBalancerTurnTLSToMediaNodeIngressSG:
Type: AWS::EC2::SecurityGroupIngress
Condition: TurnTLSIsEnabled
Properties:
GroupId: !Ref OpenViduMediaNodeSG
IpProtocol: tcp
FromPort: 5349
ToPort: 5349
SourceSecurityGroupId: !Ref OpenViduTurnTLSLoadBalancerSG
OpenViduLoadBalancerTurnTLSToMediaNodeHealthCheckSG:
Type: AWS::EC2::SecurityGroupIngress
Condition: TurnTLSIsEnabled
Properties:
GroupId: !Ref OpenViduMediaNodeSG
IpProtocol: tcp
FromPort: 7880
ToPort: 7880
SourceSecurityGroupId: !Ref OpenViduTurnTLSLoadBalancerSG
OpenViduMasterToMediaNodeServerIngressSG:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref OpenViduMediaNodeSG
IpProtocol: tcp
FromPort: 7880
ToPort: 7880
SourceSecurityGroupId: !Ref OpenViduMasterNodeSG
OpenViduMasterToMediaNodeClientIngressSG:
Type: AWS::EC2::SecurityGroupIngress
Properties:
GroupId: !Ref OpenViduMediaNodeSG
IpProtocol: tcp
FromPort: 8080
ToPort: 8080
SourceSecurityGroupId: !Ref OpenViduMasterNodeSG
# ---
# Experimental TURN TLS with main domain
OpenViduTurnTLSMasterNodeToMediaNodeIngressSG:
Type: AWS::EC2::SecurityGroupIngress
Condition: ExperimentalTurnTLSWithMainDomain
Properties:
GroupId: !Ref OpenViduMediaNodeSG
IpProtocol: tcp
FromPort: 5349
ToPort: 5349
SourceSecurityGroupId: !Ref OpenViduMasterNodeSG
OpenViduTurnTLSLoadBalancerToMediaNodeIngressSG:
Type: AWS::EC2::SecurityGroupIngress
Condition: ExperimentalTurnTLSWithMainDomain
Properties:
GroupId: !Ref OpenViduMasterNodeSG
IpProtocol: tcp
FromPort: 443
ToPort: 443
SourceSecurityGroupId: !Ref OpenViduLoadBalancerSG
# ---
OpenViduLoadBalancerSG:
Type: AWS::EC2::SecurityGroup
Properties:
GroupDescription: Security group for the Load Balancer
GroupName: !Sub openvidu-ha-lb-sg-${AWS::Region}-${AWS::StackName}
VpcId: !Ref OpenViduVPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 80
ToPort: 80
CidrIpv6: ::/0
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIpv6: ::/0
- IpProtocol: tcp
FromPort: 1935
ToPort: 1935
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 1935
ToPort: 1935
CidrIpv6: ::/0
OpenViduTurnTLSLoadBalancerSG:
Type: AWS::EC2::SecurityGroup
Condition: TurnTLSIsEnabled
Properties:
GroupDescription: Security group for the Load Balancer for TURN with TLS
GroupName: !Sub openvidu-ha-turn-tls-lb-sg-${AWS::Region}-${AWS::StackName}
VpcId: !Ref OpenViduVPC
SecurityGroupIngress:
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIp: 0.0.0.0/0
- IpProtocol: tcp
FromPort: 443
ToPort: 443
CidrIpv6: ::/0
LoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
DependsOn:
- MasterNodesWaitCondition4
Properties:
Name:
Fn::Join:
# Generate a not too long and unique load balancer name
# Getting a unique identifier from the stack id
- ''
- - OpenViduHA-
- !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]
Subnets: !Ref OpenViduMasterNodeSubnets
SecurityGroups:
- !Ref OpenViduLoadBalancerSG
Type: network
Tags:
- Key: Name
Value: !Sub ${AWS::StackName} - OpenVidu HA - Load Balancer
TurnTLSLoadBalancer:
Type: AWS::ElasticLoadBalancingV2::LoadBalancer
Condition: TurnTLSIsEnabled
Properties:
Name:
Fn::Join:
# Generate a not too long and unique load balancer name
# Getting a unique identifier from the stack id
- ''
- - OpenViduHA-TurnTLS-
- !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]
Subnets: !Ref OpenViduMediaNodeSubnets
SecurityGroups:
- !Ref OpenViduTurnTLSLoadBalancerSG
Type: network
Tags:
- Key: Name
Value: !Sub ${AWS::StackName} - OpenVidu HA - TURN with TLS Load Balancer
OpenViduMasterNodeListener:
Type: 'AWS::ElasticLoadBalancingV2::Listener'
Condition: NotExperimentalTurnTLSWithMainDomain
Properties:
DefaultActions:
- Type: forward
TargetGroupArn: !Ref OpenViduMasterNodeTG
LoadBalancerArn: !Ref LoadBalancer
Port: 443
Protocol: TLS
Certificates:
- CertificateArn: !Ref OpenViduCertificateARN
# ---
# Experimental TURN TLS with main domain
OpenViduMasterNodeWithTurnTLSListener:
Type: 'AWS::ElasticLoadBalancingV2::Listener'
Condition: ExperimentalTurnTLSWithMainDomain
Properties:
DefaultActions:
- Type: forward
TargetGroupArn: !Ref OpenViduMasterNodeWithTurnTLSTG
LoadBalancerArn: !Ref LoadBalancer
Port: 443
Protocol: TLS
Certificates:
- CertificateArn: !Ref OpenViduCertificateARN
# ---
OpenViduRTMPMediaNodeListener:
Type: 'AWS::ElasticLoadBalancingV2::Listener'
Properties:
DefaultActions:
- Type: forward
TargetGroupArn: !Ref OpenViduMediaNodeRTMPTG
LoadBalancerArn: !Ref LoadBalancer
Port: 1935
Protocol: TLS
Certificates:
- CertificateArn: !Ref OpenViduCertificateARN
OpenViduTurnTLSMediaNodeListener:
Type: 'AWS::ElasticLoadBalancingV2::Listener'
Condition: TurnTLSIsEnabled
Properties:
DefaultActions:
- Type: forward
TargetGroupArn: !Ref OpenViduMediaNodeTurnTLSTG
LoadBalancerArn: !Ref TurnTLSLoadBalancer
Port: 443
Protocol: TLS
Certificates:
- CertificateArn: !Ref TurnCertificateARN
OpenViduMasterNodeTG:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Condition: NotExperimentalTurnTLSWithMainDomain
Properties:
Name:
Fn::Join:
# Generate a not too long and unique target id
# Getting a unique identifier from the stack id
- ''
- - OpenVidu-
- !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]
TargetType: instance
Targets:
- Id: !Ref OpenViduMasterNode1
- Id: !Ref OpenViduMasterNode2
- Id: !Ref OpenViduMasterNode3
- Id: !Ref OpenViduMasterNode4
VpcId: !Ref OpenViduVPC
Port: 7880
Protocol: TCP
Matcher:
HttpCode: '200'
HealthCheckIntervalSeconds: 10
HealthCheckPath: /health/caddy
HealthCheckProtocol: HTTP
HealthCheckPort: '7880'
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 3
UnhealthyThresholdCount: 4
TargetGroupAttributes:
- Key: deregistration_delay.timeout_seconds
Value: 60
Tags:
- Key: Name
Value: !Sub ${AWS::StackName} - OpenVidu HA - Master Target Group
# ---
# Experimental TURN TLS with main domain
OpenViduMasterNodeWithTurnTLSTG:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Condition: ExperimentalTurnTLSWithMainDomain
Properties:
Name:
Fn::Join:
# Generate a not too long and unique target id
# Getting a unique identifier from the stack id
- ''
- - OVTurnTLSMaster-
- !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]
TargetType: instance
Targets:
- Id: !Ref OpenViduMasterNode1
- Id: !Ref OpenViduMasterNode2
- Id: !Ref OpenViduMasterNode3
- Id: !Ref OpenViduMasterNode4
VpcId: !Ref OpenViduVPC
Port: 443
Protocol: TCP
Matcher:
HttpCode: '200'
HealthCheckIntervalSeconds: 10
HealthCheckPath: /health/caddy
HealthCheckProtocol: HTTP
HealthCheckPort: '7880'
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 3
UnhealthyThresholdCount: 4
TargetGroupAttributes:
- Key: deregistration_delay.timeout_seconds
Value: 60
Tags:
- Key: Name
Value: !Sub ${AWS::StackName} - OpenVidu HA - TURN TLS Master Target Group
# ---
OpenViduMediaNodeRTMPTG:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Properties:
Name:
Fn::Join:
# Generate a not too long and unique target id
# Getting a unique identifier from the stack id
- ''
- - OVRTMP-
- !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]
VpcId: !Ref OpenViduVPC
Port: 1945
Protocol: TCP
Matcher:
HttpCode: '200'
HealthCheckIntervalSeconds: 10
HealthCheckPath: /
HealthCheckProtocol: HTTP
# Ingress health check port
HealthCheckPort: '9092'
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 3
UnhealthyThresholdCount: 4
TargetGroupAttributes:
- Key: deregistration_delay.timeout_seconds
Value: 60
Tags:
- Key: Name
Value: !Sub ${AWS::StackName} - OpenVidu HA - RTMP Target Group
OpenViduMediaNodeTurnTLSTG:
Type: AWS::ElasticLoadBalancingV2::TargetGroup
Condition: TurnTLSIsEnabled
Properties:
Name:
Fn::Join:
# Generate a not too long and unique target id
# Getting a unique identifier from the stack id
- ''
- - OVTurnTLS-
- !Select [4, !Split ['-', !Select [2, !Split ['/', !Ref AWS::StackId]]]]
VpcId: !Ref OpenViduVPC
Port: 5349
Protocol: TCP
Matcher:
HttpCode: '200'
HealthCheckIntervalSeconds: 10
HealthCheckPath: /
HealthCheckProtocol: HTTP
HealthCheckPort: '7880'
HealthCheckTimeoutSeconds: 5
HealthyThresholdCount: 3
UnhealthyThresholdCount: 4
Tags:
- Key: Name
Value: !Sub ${AWS::StackName} - OpenVidu HA - TURN TLS Target Group
Outputs:
ServicesAndCredentials:
Description: Services and credentials
Value: !Sub https://${AWS::Region}.console.aws.amazon.com/secretsmanager/home?region=${AWS::Region}#!/secret?name=openvidu-ha-${AWS::Region}-${AWS::StackName}