@description('Stack name') param stackName string @description(''' [selfsigned] Not recommended for production use. If you don't have a FQDN, (DomainName parameter) you can use this option to generate a self-signed certificate. [owncert] Valid for productions environments. If you have a FQDN, (DomainName parameter) and an Elastic IP, you can use this option to use your own certificate. [letsencrypt] Valid for production environments. If you have a FQDN, (DomainName parameter) and an Elastic IP, you can use this option to generate a Let's Encrypt certificate. ''') @allowed([ 'selfsigned' 'owncert' 'letsencrypt' ]) param certificateType string = 'selfsigned' @description('Domain name for the OpenVidu Deployment. Blank will generate default domain') param domainName string @description('If certificate type is \'owncert\', this parameter will be used to specify the public certificate') param ownPublicCertificate string = '' @description('If certificate type is \'owncert\', this parameter will be used to specify the private certificate') param ownPrivateCertificate string = '' @description('If certificate type is \'letsencrypt\', this email will be used for Let\'s Encrypt notifications') param letsEncryptEmail string = '' @description('Name of the PublicIPAddress resource in Azure when using certificateType \'owncert\' or \'letsencrypt\'') param publicIpAddressResourceName string = '' @description('(Optional) Domain name for the TURN server with TLS. Only needed if your users are behind restrictive firewalls') param turnDomainName string = '' @description('(Optional) This setting is applicable if the certificate type is set to \'owncert\' and the TurnDomainName is specified.') param turnOwnPublicCertificate string = '' @description('(Optional) This setting is applicable if the certificate type is set to \'owncert\' and the TurnDomainName is specified.') param turnOwnPrivateCertificate string = '' @description('Name of the PublicIPAddress resource in Azure when using TURN server with TLS') param turnPublicIpAddressResourceName string = '' @description('Visit https://openvidu.io/account') @secure() param openviduLicense string @description('RTCEngine media engine to use') @allowed([ 'pion' 'mediasoup' ]) param rtcEngine string = 'pion' @description('Specifies the EC2 instance type for your OpenVidu Master Node') @allowed([ 'Standard_B1s' 'Standard_B1ms' 'Standard_B2s' 'Standard_B2ms' 'Standard_B4ms' 'Standard_B8ms' 'Standard_D2_v3' 'Standard_D4_v3' 'Standard_D8_v3' 'Standard_D16_v3' 'Standard_D32_v3' 'Standard_D48_v3' 'Standard_D64_v3' 'Standard_D2_v4' 'Standard_D4_v4' 'Standard_D8_v4' 'Standard_D16_v4' 'Standard_D32_v4' 'Standard_D48_v4' 'Standard_D64_v4' 'Standard_D96_v4' 'Standard_D2_v5' 'Standard_D4_v5' 'Standard_D8_v5' 'Standard_D16_v5' 'Standard_D32_v5' 'Standard_D48_v5' 'Standard_D64_v5' 'Standard_D96_v5' 'Standard_F2' 'Standard_F4' 'Standard_F8' 'Standard_F16' 'Standard_F32' 'Standard_F64' 'Standard_F72' 'Standard_F2s_v2' 'Standard_F4s_v2' 'Standard_F8s_v2' 'Standard_F16s_v2' 'Standard_F32s_v2' 'Standard_F64s_v2' 'Standard_F72s_v2' 'Standard_E2_v3' 'Standard_E4_v3' 'Standard_E8_v3' 'Standard_E16_v3' 'Standard_E32_v3' 'Standard_E48_v3' 'Standard_E64_v3' 'Standard_E96_v3' 'Standard_E2_v4' 'Standard_E4_v4' 'Standard_E8_v4' 'Standard_E16_v4' 'Standard_E32_v4' 'Standard_E48_v4' 'Standard_E64_v4' 'Standard_E2_v5' 'Standard_E4_v5' 'Standard_E8_v5' 'Standard_E16_v5' 'Standard_E32_v5' 'Standard_E48_v5' 'Standard_E64_v5' 'Standard_E96_v5' 'Standard_M64' 'Standard_M128' 'Standard_M208ms_v2' 'Standard_M416ms_v2' 'Standard_L4s_v2' 'Standard_L8s_v2' 'Standard_L16s_v2' 'Standard_L32s_v2' 'Standard_L64s_v2' 'Standard_L80s_v2' 'Standard_NC6' 'Standard_NC12' 'Standard_NC24' 'Standard_NC24r' 'Standard_ND6s' 'Standard_ND12s' 'Standard_ND24s' 'Standard_ND24rs' 'Standard_NV6' 'Standard_NV12' 'Standard_NV24' 'Standard_H8' 'Standard_H16' 'Standard_H16r' 'Standard_H16mr' 'Standard_HB120rs_v2' 'Standard_HC44rs' 'Standard_DC2s' 'Standard_DC4s' 'Standard_DC2s_v2' 'Standard_DC4s_v2' 'Standard_DC8s_v2' 'Standard_DC16s_v2' 'Standard_DC32s_v2' 'Standard_A1_v2' 'Standard_A2_v2' 'Standard_A4_v2' 'Standard_A8_v2' 'Standard_A2m_v2' 'Standard_A4m_v2' 'Standard_A8m_v2' ]) param masterNodeInstanceType string = 'Standard_B2s' @description('Size of the disk in GB') param masterNodesDiskSize int = 100 @description('Specifies the EC2 instance type for your OpenVidu Media Nodes') @allowed([ 'Standard_B1s' 'Standard_B1ms' 'Standard_B2s' 'Standard_B2ms' 'Standard_B4ms' 'Standard_B8ms' 'Standard_D2_v3' 'Standard_D4_v3' 'Standard_D8_v3' 'Standard_D16_v3' 'Standard_D32_v3' 'Standard_D48_v3' 'Standard_D64_v3' 'Standard_D2_v4' 'Standard_D4_v4' 'Standard_D8_v4' 'Standard_D16_v4' 'Standard_D32_v4' 'Standard_D48_v4' 'Standard_D64_v4' 'Standard_D96_v4' 'Standard_D2_v5' 'Standard_D4_v5' 'Standard_D8_v5' 'Standard_D16_v5' 'Standard_D32_v5' 'Standard_D48_v5' 'Standard_D64_v5' 'Standard_D96_v5' 'Standard_F2' 'Standard_F4' 'Standard_F8' 'Standard_F16' 'Standard_F32' 'Standard_F64' 'Standard_F72' 'Standard_F2s_v2' 'Standard_F4s_v2' 'Standard_F8s_v2' 'Standard_F16s_v2' 'Standard_F32s_v2' 'Standard_F64s_v2' 'Standard_F72s_v2' 'Standard_E2_v3' 'Standard_E4_v3' 'Standard_E8_v3' 'Standard_E16_v3' 'Standard_E32_v3' 'Standard_E48_v3' 'Standard_E64_v3' 'Standard_E96_v3' 'Standard_E2_v4' 'Standard_E4_v4' 'Standard_E8_v4' 'Standard_E16_v4' 'Standard_E32_v4' 'Standard_E48_v4' 'Standard_E64_v4' 'Standard_E2_v5' 'Standard_E4_v5' 'Standard_E8_v5' 'Standard_E16_v5' 'Standard_E32_v5' 'Standard_E48_v5' 'Standard_E64_v5' 'Standard_E96_v5' 'Standard_M64' 'Standard_M128' 'Standard_M208ms_v2' 'Standard_M416ms_v2' 'Standard_L4s_v2' 'Standard_L8s_v2' 'Standard_L16s_v2' 'Standard_L32s_v2' 'Standard_L64s_v2' 'Standard_L80s_v2' 'Standard_NC6' 'Standard_NC12' 'Standard_NC24' 'Standard_NC24r' 'Standard_ND6s' 'Standard_ND12s' 'Standard_ND24s' 'Standard_ND24rs' 'Standard_NV6' 'Standard_NV12' 'Standard_NV24' 'Standard_H8' 'Standard_H16' 'Standard_H16r' 'Standard_H16mr' 'Standard_HB120rs_v2' 'Standard_HC44rs' 'Standard_DC2s' 'Standard_DC4s' 'Standard_DC2s_v2' 'Standard_DC4s_v2' 'Standard_DC8s_v2' 'Standard_DC16s_v2' 'Standard_DC32s_v2' 'Standard_A1_v2' 'Standard_A2_v2' 'Standard_A4_v2' 'Standard_A8_v2' 'Standard_A2m_v2' 'Standard_A4m_v2' 'Standard_A8m_v2' ]) param mediaNodeInstanceType string = 'Standard_B2s' @description('Username for the Virtual Machine.') param adminUsername string @description('SSH Key for the Virtual Machine.') @secure() param adminSshKey string @description('Number of initial media nodes to deploy') param initialNumberOfMediaNodes int = 1 @description('Minimum number of media nodes to deploy') param minNumberOfMediaNodes int = 1 @description('Maximum number of media nodes to deploy') param maxNumberOfMediaNodes int = 5 @description('Target CPU percentage to scale up or down') param scaleTargetCPU int = 50 /*------------------------------------------- VARIABLES AND VALIDATIONS -------------------------------------------*/ var masterNodeVMSettings = { osDiskType: 'StandardSSD_LRS' osDiskSize: masterNodesDiskSize ubuntuOSVersion: { publisher: 'Canonical' offer: '0001-com-ubuntu-server-jammy' sku: '22_04-lts-gen2' version: 'latest' } linuxConfiguration: { disablePasswordAuthentication: true ssh: { publicKeys: [ { path: '/home/${adminUsername}/.ssh/authorized_keys' keyData: adminSshKey } ] } } } var mediaNodeVMSettings = { vmName: '${stackName}-VN-MediaNode' osDiskType: 'StandardSSD_LRS' ubuntuOSVersion: { publisher: 'Canonical' offer: '0001-com-ubuntu-server-jammy' sku: '22_04-lts-gen2' version: 'latest' } linuxConfiguration: { disablePasswordAuthentication: true ssh: { publicKeys: [ { path: '/home/${adminUsername}/.ssh/authorized_keys' keyData: adminSshKey } ] } } } var turnTLSIsEnabled = turnDomainName != '' var fqdn = domainName var keyVaultName = '${stackName}-keyvault' var location = resourceGroup().location var tenantId = subscription().tenantId var deploymentUser = az.deployer().objectId /*------------------------------------------- KEY VAULT -------------------------------------------*/ resource openviduSharedInfo 'Microsoft.KeyVault/vaults@2023-07-01' = { name: keyVaultName location: location properties: { enabledForDeployment: true enabledForDiskEncryption: false enabledForTemplateDeployment: true tenantId: tenantId enableSoftDelete: false accessPolicies: [ { objectId: openviduMasterNode1.identity.principalId tenantId: tenantId permissions: { secrets: ['get', 'set', 'list'] } } { objectId: openviduMasterNode2.identity.principalId tenantId: tenantId permissions: { secrets: ['get', 'set', 'list'] } } { objectId: openviduMasterNode3.identity.principalId tenantId: tenantId permissions: { secrets: ['get', 'set', 'list'] } } { objectId: openviduMasterNode4.identity.principalId tenantId: tenantId permissions: { secrets: ['get', 'set', 'list'] } } { objectId: openviduScaleSetMediaNode.identity.principalId tenantId: tenantId permissions: { secrets: ['get'] } } { objectId: deploymentUser tenantId: tenantId permissions: { secrets: ['get', 'list', 'set', 'delete', 'recover', 'backup', 'restore'] } } ] sku: { name: 'standard' family: 'A' } networkAcls: { defaultAction: 'Allow' bypass: 'AzureServices' } } } /*------------------------------------------- MASTER NODE -------------------------------------------*/ var stringInterpolationParamsMaster1 = { domainName: domainName turnDomainName: turnDomainName certificateType: certificateType letsEncryptEmail: letsEncryptEmail ownPublicCertificate: ownPublicCertificate ownPrivateCertificate: ownPrivateCertificate turnOwnPublicCertificate: turnOwnPublicCertificate turnOwnPrivateCertificate: turnOwnPrivateCertificate fqdn: fqdn openviduLicense: openviduLicense rtcEngine: rtcEngine keyVaultName: keyVaultName masterNodeNum: '1' } var stringInterpolationParamsMaster2 = { domainName: domainName turnDomainName: turnDomainName certificateType: certificateType letsEncryptEmail: letsEncryptEmail ownPublicCertificate: ownPublicCertificate ownPrivateCertificate: ownPrivateCertificate turnOwnPublicCertificate: turnOwnPublicCertificate turnOwnPrivateCertificate: turnOwnPrivateCertificate fqdn: fqdn openviduLicense: openviduLicense rtcEngine: rtcEngine keyVaultName: keyVaultName masterNodeNum: '2' } var stringInterpolationParamsMaster3 = { domainName: domainName turnDomainName: turnDomainName certificateType: certificateType letsEncryptEmail: letsEncryptEmail ownPublicCertificate: ownPublicCertificate ownPrivateCertificate: ownPrivateCertificate turnOwnPublicCertificate: turnOwnPublicCertificate turnOwnPrivateCertificate: turnOwnPrivateCertificate fqdn: fqdn openviduLicense: openviduLicense rtcEngine: rtcEngine keyVaultName: keyVaultName masterNodeNum: '3' } var stringInterpolationParamsMaster4 = { domainName: domainName turnDomainName: turnDomainName certificateType: certificateType letsEncryptEmail: letsEncryptEmail ownPublicCertificate: ownPublicCertificate ownPrivateCertificate: ownPrivateCertificate turnOwnPublicCertificate: turnOwnPublicCertificate turnOwnPrivateCertificate: turnOwnPrivateCertificate fqdn: fqdn openviduLicense: openviduLicense rtcEngine: rtcEngine keyVaultName: keyVaultName masterNodeNum: '4' } var installScriptTemplateMaster = ''' #!/bin/bash -x set -e OPENVIDU_VERSION=main DOMAIN= # Assume azure cli is installed apt-get update && apt-get install -y \ curl \ unzip \ jq \ wget # Configure Domain if [[ "${domainName}" == '' ]]; then DOMAIN=${fqdn} else DOMAIN=${domainName} fi # Wait for the keyvault availability MAX_WAIT=100 WAIT_INTERVAL=1 ELAPSED_TIME=0 set +e while true; do # Check keyvault availability az keyvault secret list --vault-name ${keyVaultName} # If it is available, exit the loop if [ $? -eq 0 ]; then break fi # If not, wait and check again incrementing the time ELAPSED_TIME=$((ELAPSED_TIME + WAIT_INTERVAL)) # If exceeded the maximum time, exit with error if [ $ELAPSED_TIME -ge $MAX_WAIT ]; then exit 1 fi # Esperar antes de la próxima comprobación sleep $WAIT_INTERVAL done set -e MASTER_NODE_NUM=${masterNodeNum} # Get own private IP PRIVATE_IP=$(curl -H Metadata:true --noproxy "*" "http://169.254.169.254/metadata/instance/network/interface/0/ipv4/ipAddress/0/privateIpAddress?api-version=2017-08-01&format=text") # Store current private IP PRIVATE_IP="$(/usr/local/bin/store_secret.sh save MASTER-NODE-${masterNodeNum}-PRIVATE-IP $PRIVATE_IP)" if [[ $MASTER_NODE_NUM -eq 1 ]] && [[ "$ALL_SECRETS_GENERATED" == "" || "$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 if [[ "${certificateType}" == "letsencrypt" ]]; then LETSENCRYPT_EMAIL=$(/usr/local/bin/store_secret.sh save LETSENCRYPT-EMAIL "${letsEncryptEmail}") fi # Store usernames and generate random passwords 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}")" 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)" DEFAULT_APP_USERNAME="$(/usr/local/bin/store_secret.sh save DEFAULT-APP-USERNAME "calluser")" DEFAULT_APP_PASSWORD="$(/usr/local/bin/store_secret.sh generate DEFAULT-APP-PASSWORD)" DEFAULT_APP_ADMIN_USERNAME="$(/usr/local/bin/store_secret.sh save DEFAULT-APP-ADMIN-USERNAME "calladmin")" DEFAULT_APP_ADMIN_PASSWORD="$(/usr/local/bin/store_secret.sh generate DEFAULT-APP-ADMIN-PASSWORD)" 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)" OPENVIDU_VERSION="$(/usr/local/bin/store_secret.sh save OPENVIDU-VERSION "${OPENVIDU_VERSION}")" ENABLED_MODULES="$(/usr/local/bin/store_secret.sh save ENABLED-MODULES "observability,app")" ALL_SECRETS_GENERATED="$(/usr/local/bin/store_secret.sh save ALL-SECRETS-GENERATED "true")" fi while true; do MASTER_NODE_1_PRIVATE_IP=$(az keyvault secret show --vault-name ${keyVaultName} --name MASTER-NODE-1-PRIVATE-IP --query value -o tsv) || true MASTER_NODE_2_PRIVATE_IP=$(az keyvault secret show --vault-name ${keyVaultName} --name MASTER-NODE-2-PRIVATE-IP --query value -o tsv) || true MASTER_NODE_3_PRIVATE_IP=$(az keyvault secret show --vault-name ${keyVaultName} --name MASTER-NODE-3-PRIVATE-IP --query value -o tsv) || true MASTER_NODE_4_PRIVATE_IP=$(az keyvault secret show --vault-name ${keyVaultName} --name MASTER-NODE-4-PRIVATE-IP --query value -o tsv) || true # Check if all master nodes have stored their private IPs if [[ "$MASTER_NODE_1_PRIVATE_IP" != "" ]] && [[ "$MASTER_NODE_2_PRIVATE_IP" != "" ]] && [[ "$MASTER_NODE_3_PRIVATE_IP" != "" ]] && [[ "$MASTER_NODE_4_PRIVATE_IP" != "" ]]; then break fi sleep 5 done # Fetch the values in the keyvault MASTER_NODE_1_PRIVATE_IP=$(az keyvault secret show --vault-name ${keyVaultName} --name MASTER-NODE-1-PRIVATE-IP --query value -o tsv) MASTER_NODE_2_PRIVATE_IP=$(az keyvault secret show --vault-name ${keyVaultName} --name MASTER-NODE-2-PRIVATE-IP --query value -o tsv) MASTER_NODE_3_PRIVATE_IP=$(az keyvault secret show --vault-name ${keyVaultName} --name MASTER-NODE-3-PRIVATE-IP --query value -o tsv) MASTER_NODE_4_PRIVATE_IP=$(az keyvault secret show --vault-name ${keyVaultName} --name MASTER-NODE-4-PRIVATE-IP --query value -o tsv) 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=$(az keyvault secret show --vault-name ${keyVaultName} --name DOMAIN-NAME --query value -o tsv) if [[ -n "${turnDomainName}" ]]; then LIVEKIT_TURN_DOMAIN_NAME=$(az keyvault secret show --vault-name ${keyVaultName} --name LIVEKIT-TURN-DOMAIN-NAME --query value -o tsv) fi if [[ "${certificateType}" == "letsencrypt" ]]; then LETSENCRYPT_EMAIL=$(az keyvault secret show --vault-name ${keyVaultName} --name LETSENCRYPT-EMAIL --query value -o tsv) fi OPENVIDU_RTC_ENGINE=$(az keyvault secret show --vault-name ${keyVaultName} --name OPENVIDU-RTC-ENGINE --query value -o tsv) OPENVIDU_PRO_LICENSE=$(az keyvault secret show --vault-name ${keyVaultName} --name OPENVIDU-PRO-LICENSE --query value -o tsv) REDIS_PASSWORD=$(az keyvault secret show --vault-name ${keyVaultName} --name REDIS-PASSWORD --query value -o tsv) MONGO_ADMIN_USERNAME=$(az keyvault secret show --vault-name ${keyVaultName} --name MONGO-ADMIN-USERNAME --query value -o tsv) MONGO_ADMIN_PASSWORD=$(az keyvault secret show --vault-name ${keyVaultName} --name MONGO-ADMIN-PASSWORD --query value -o tsv) MONGO_REPLICA_SET_KEY=$(az keyvault secret show --vault-name ${keyVaultName} --name MONGO-REPLICA-SET-KEY --query value -o tsv) DASHBOARD_ADMIN_USERNAME=$(az keyvault secret show --vault-name ${keyVaultName} --name DASHBOARD-ADMIN-USERNAME --query value -o tsv) DASHBOARD_ADMIN_PASSWORD=$(az keyvault secret show --vault-name ${keyVaultName} --name DASHBOARD-ADMIN-PASSWORD --query value -o tsv) MINIO_ACCESS_KEY=$(az keyvault secret show --vault-name ${keyVaultName} --name MINIO-ACCESS-KEY --query value -o tsv) MINIO_SECRET_KEY=$(az keyvault secret show --vault-name ${keyVaultName} --name MINIO-SECRET-KEY --query value -o tsv) GRAFANA_ADMIN_USERNAME=$(az keyvault secret show --vault-name ${keyVaultName} --name GRAFANA-ADMIN-USERNAME --query value -o tsv) GRAFANA_ADMIN_PASSWORD=$(az keyvault secret show --vault-name ${keyVaultName} --name GRAFANA-ADMIN-PASSWORD --query value -o tsv) LIVEKIT_API_KEY=$(az keyvault secret show --vault-name ${keyVaultName} --name LIVEKIT-API-KEY --query value -o tsv) LIVEKIT_API_SECRET=$(az keyvault secret show --vault-name ${keyVaultName} --name LIVEKIT-API-SECRET --query value -o tsv) DEFAULT_APP_USERNAME=$(az keyvault secret show --vault-name ${keyVaultName} --name DEFAULT-APP-USERNAME --query value -o tsv) DEFAULT_APP_PASSWORD=$(az keyvault secret show --vault-name ${keyVaultName} --name DEFAULT-APP-PASSWORD --query value -o tsv) DEFAULT_APP_ADMIN_USERNAME=$(az keyvault secret show --vault-name ${keyVaultName} --name DEFAULT-APP-ADMIN-USERNAME --query value -o tsv) DEFAULT_APP_ADMIN_PASSWORD=$(az keyvault secret show --vault-name ${keyVaultName} --name DEFAULT-APP-ADMIN-PASSWORD --query value -o tsv) ENABLED_MODULES=$(az keyvault secret show --vault-name ${keyVaultName} --name ENABLED-MODULES --query value -o tsv) # 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=azure" "--deployment-type='ha'" "--node-role='master-node'" "--external-load-balancer" "--internal-tls-termination" "--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" "--default-app-user=$DEFAULT_APP_USERNAME" "--default-app-password=$DEFAULT_APP_PASSWORD" "--default-app-admin-user=$DEFAULT_APP_ADMIN_USERNAME" "--default-app-admin-password=$DEFAULT_APP_ADMIN_PASSWORD" "--livekit-api-key=$LIVEKIT_API_KEY" "--livekit-api-secret=$LIVEKIT_API_SECRET" ) if [[ $LIVEKIT_TURN_DOMAIN_NAME != "" ]]; then COMMON_ARGS+=("--turn-domain-name=$LIVEKIT_TURN_DOMAIN_NAME}") fi # Certificate arguments if [[ "${certificateType}" == "selfsigned" ]]; then CERT_ARGS=( "--certificate-type=selfsigned" ) elif [[ "${certificateType}" == "letsencrypt" ]]; then CERT_ARGS=( "--certificate-type=letsencrypt" "--letsencrypt-email=$LETSENCRYPT_EMAIL" ) else # Download owncert files mkdir -p /tmp/owncert wget -O /tmp/owncert/fullchain.pem ${ownPublicCertificate} wget -O /tmp/owncert/privkey.pem ${ownPrivateCertificate} # Convert to base64 OWN_CERT_CRT=$(base64 -w 0 /tmp/owncert/fullchain.pem) OWN_CERT_KEY=$(base64 -w 0 /tmp/owncert/privkey.pem) CERT_ARGS=( "--certificate-type=owncert" "--owncert-public-key=$OWN_CERT_CRT" "--owncert-private-key=$OWN_CERT_KEY" ) # Turn with TLS and own certificate if [[ "${turnDomainName}" != '' ]]; then # Download owncert files mkdir -p /tmp/owncert-turn wget -O /tmp/owncert-turn/fullchain.pem ${turnOwnPublicCertificate} wget -O /tmp/owncert-turn/privkey.pem ${turnOwnPrivateCertificate} # Convert to base64 OWN_CERT_CRT_TURN=$(base64 -w 0 /tmp/owncert-turn/fullchain.pem) OWN_CERT_KEY_TURN=$(base64 -w 0 /tmp/owncert-turn/privkey.pem) CERT_ARGS+=( "--turn-owncert-private-key=$OWN_CERT_KEY_TURN" "--turn-owncert-public-key=$OWN_CERT_CRT_TURN" ) fi fi # Construct the final command FINAL_COMMAND="$INSTALL_COMMAND $(printf "%s " "${COMMON_ARGS[@]}") $(printf "%s " "${CERT_ARGS[@]}")" # Install OpenVidu exec bash -c "$FINAL_COMMAND" ''' var after_installScriptTemplateMaster = ''' #!/bin/bash set -e az login --identity --allow-no-subscriptions > /dev/null # Generate URLs DOMAIN=$(az keyvault secret show --vault-name ${keyVaultName} --name DOMAIN-NAME --query value -o tsv) DASHBOARD_URL="https://${DOMAIN}/dashboard/" GRAFANA_URL="https://${DOMAIN}/grafana/" MINIO_URL="https://${DOMAIN}/minio-console/" # Update shared secret az keyvault secret set --vault-name ${keyVaultName} --name DOMAIN-NAME --value $DOMAIN az keyvault secret set --vault-name ${keyVaultName} --name DASHBOARD-URL --value $DASHBOARD_URL az keyvault secret set --vault-name ${keyVaultName} --name GRAFANA-URL --value $GRAFANA_URL az keyvault secret set --vault-name ${keyVaultName} --name MINIO-URL --value $MINIO_URL az keyvault secret show --vault-name ${keyVaultName} --name MINIO-URL if [[ $? -ne 0 ]]; then echo "Error updating keyvault" fi ''' var update_config_from_secretScriptTemplateMaster = ''' #!/bin/bash set -e az login --identity --allow-no-subscriptions > /dev/null # 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=$(az keyvault secret show --vault-name ${keyVaultName} --name DOMAIN-NAME --query value -o tsv) 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=$(az keyvault secret show --vault-name ${keyVaultName} --name LIVEKIT-TURN-DOMAIN-NAME --query value -o tsv) 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 if [[ ${certificateType} == "letsencrypt" ]]; then export LETSENCRYPT_EMAIL=$(az keyvault secret show --vault-name ${keyVaultName} --name LETSENCRYPT-EMAIL --query value -o tsv) sed -i "s/LETSENCRYPT_EMAIL=.*/LETSENCRYPT_EMAIL=$LETSENCRYPT_EMAIL/" "${CLUSTER_CONFIG_DIR}/openvidu.env" fi # Get the rest of the values export REDIS_PASSWORD=$(az keyvault secret show --vault-name ${keyVaultName} --name REDIS-PASSWORD --query value -o tsv) export OPENVIDU_RTC_ENGINE=$(az keyvault secret show --vault-name ${keyVaultName} --name OPENVIDU-RTC-ENGINE --query value -o tsv) export OPENVIDU_PRO_LICENSE=$(az keyvault secret show --vault-name ${keyVaultName} --name OPENVIDU-PRO-LICENSE --query value -o tsv) export MONGO_ADMIN_USERNAME=$(az keyvault secret show --vault-name ${keyVaultName} --name MONGO-ADMIN-USERNAME --query value -o tsv) export MONGO_ADMIN_PASSWORD=$(az keyvault secret show --vault-name ${keyVaultName} --name MONGO-ADMIN-PASSWORD --query value -o tsv) export MONGO_REPLICA_SET_KEY=$(az keyvault secret show --vault-name ${keyVaultName} --name MONGO-REPLICA-SET-KEY --query value -o tsv) export DASHBOARD_ADMIN_USERNAME=$(az keyvault secret show --vault-name ${keyVaultName} --name DASHBOARD-ADMIN-USERNAME --query value -o tsv) export DASHBOARD_ADMIN_PASSWORD=$(az keyvault secret show --vault-name ${keyVaultName} --name DASHBOARD-ADMIN-PASSWORD --query value -o tsv) export MINIO_ACCESS_KEY=$(az keyvault secret show --vault-name ${keyVaultName} --name MINIO-ACCESS-KEY --query value -o tsv) export MINIO_SECRET_KEY=$(az keyvault secret show --vault-name ${keyVaultName} --name MINIO-SECRET-KEY --query value -o tsv) export GRAFANA_ADMIN_USERNAME=$(az keyvault secret show --vault-name ${keyVaultName} --name GRAFANA-ADMIN-USERNAME --query value -o tsv) export GRAFANA_ADMIN_PASSWORD=$(az keyvault secret show --vault-name ${keyVaultName} --name GRAFANA-ADMIN-PASSWORD --query value -o tsv) export LIVEKIT_API_KEY=$(az keyvault secret show --vault-name ${keyVaultName} --name LIVEKIT-API-KEY --query value -o tsv) export LIVEKIT_API_SECRET=$(az keyvault secret show --vault-name ${keyVaultName} --name LIVEKIT-API-SECRET --query value -o tsv) export DEFAULT_APP_USERNAME=$(az keyvault secret show --vault-name ${keyVaultName} --name DEFAULT-APP-USERNAME --query value -o tsv) export DEFAULT_APP_PASSWORD=$(az keyvault secret show --vault-name ${keyVaultName} --name DEFAULT-APP-PASSWORD --query value -o tsv) export DEFAULT_APP_ADMIN_USERNAME=$(az keyvault secret show --vault-name ${keyVaultName} --name DEFAULT-APP-ADMIN-USERNAME --query value -o tsv) export DEFAULT_APP_ADMIN_PASSWORD=$(az keyvault secret show --vault-name ${keyVaultName} --name DEFAULT-APP-ADMIN-PASSWORD --query value -o tsv) export ENABLED_MODULES=$(az keyvault secret show --vault-name ${keyVaultName} --name ENABLED-MODULES --query value -o tsv) # Replace rest of the values sed -i "s/REDIS_PASSWORD=.*/REDIS_PASSWORD=$REDIS_PASSWORD/" "${MASTER_NODE_CONFIG_DIR}/master_node.env" sed -i "s/OPENVIDU_RTC_ENGINE=.*/OPENVIDU_RTC_ENGINE=$OPENVIDU_RTC_ENGINE/" "${CLUSTER_CONFIG_DIR}/openvidu.env" sed -i "s/OPENVIDU_PRO_LICENSE=.*/OPENVIDU_PRO_LICENSE=$OPENVIDU_PRO_LICENSE/" "${CLUSTER_CONFIG_DIR}/openvidu.env" sed -i "s/MONGO_ADMIN_USERNAME=.*/MONGO_ADMIN_USERNAME=$MONGO_ADMIN_USERNAME/" "${CLUSTER_CONFIG_DIR}/openvidu.env" sed -i "s/MONGO_ADMIN_PASSWORD=.*/MONGO_ADMIN_PASSWORD=$MONGO_ADMIN_PASSWORD/" "${CLUSTER_CONFIG_DIR}/openvidu.env" sed -i "s/MONGO_REPLICA_SET_KEY=.*/MONGO_REPLICA_SET_KEY=$MONGO_REPLICA_SET_KEY/" "${CLUSTER_CONFIG_DIR}/openvidu.env" sed -i "s/DASHBOARD_ADMIN_USERNAME=.*/DASHBOARD_ADMIN_USERNAME=$DASHBOARD_ADMIN_USERNAME/" "${CLUSTER_CONFIG_DIR}/openvidu.env" sed -i "s/DASHBOARD_ADMIN_PASSWORD=.*/DASHBOARD_ADMIN_PASSWORD=$DASHBOARD_ADMIN_PASSWORD/" "${CLUSTER_CONFIG_DIR}/openvidu.env" sed -i "s/MINIO_ACCESS_KEY=.*/MINIO_ACCESS_KEY=$MINIO_ACCESS_KEY/" "${CLUSTER_CONFIG_DIR}/openvidu.env" sed -i "s/MINIO_SECRET_KEY=.*/MINIO_SECRET_KEY=$MINIO_SECRET_KEY/" "${CLUSTER_CONFIG_DIR}/openvidu.env" sed -i "s/GRAFANA_ADMIN_USERNAME=.*/GRAFANA_ADMIN_USERNAME=$GRAFANA_ADMIN_USERNAME/" "${CLUSTER_CONFIG_DIR}/openvidu.env" sed -i "s/GRAFANA_ADMIN_PASSWORD=.*/GRAFANA_ADMIN_PASSWORD=$GRAFANA_ADMIN_PASSWORD/" "${CLUSTER_CONFIG_DIR}/openvidu.env" sed -i "s/LIVEKIT_API_KEY=.*/LIVEKIT_API_KEY=$LIVEKIT_API_KEY/" "${CLUSTER_CONFIG_DIR}/openvidu.env" sed -i "s/LIVEKIT_API_SECRET=.*/LIVEKIT_API_SECRET=$LIVEKIT_API_SECRET/" "${CLUSTER_CONFIG_DIR}/openvidu.env" sed -i "s/CALL_USER=.*/CALL_USER=$DEFAULT_APP_USERNAME/" "${CLUSTER_CONFIG_DIR}/master_node/app.env" sed -i "s/CALL_SECRET=.*/CALL_SECRET=$DEFAULT_APP_PASSWORD/" "${CLUSTER_CONFIG_DIR}/master_node/app.env" sed -i "s/CALL_ADMIN_USER=.*/CALL_ADMIN_USER=$DEFAULT_APP_ADMIN_USERNAME/" "${CLUSTER_CONFIG_DIR}/master_node/app.env" sed -i "s/CALL_ADMIN_SECRET=.*/CALL_ADMIN_SECRET=$DEFAULT_APP_ADMIN_PASSWORD/" "${CLUSTER_CONFIG_DIR}/master_node/app.env" sed -i "s/ENABLED_MODULES=.*/ENABLED_MODULES=$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/" # Update shared secret az keyvault secret set --vault-name ${keyVaultName} --name DOMAIN-NAME --value $DOMAIN az keyvault secret set --vault-name ${keyVaultName} --name DASHBOARD-URL --value $DASHBOARD_URL az keyvault secret set --vault-name ${keyVaultName} --name GRAFANA-URL --value $GRAFANA_URL az keyvault secret set --vault-name ${keyVaultName} --name MINIO-URL --value $MINIO_URL ''' var update_secret_from_configScriptTemplateMaster = ''' #!/bin/bash set -e az login --identity --allow-no-subscriptions > /dev/null # Installation directory INSTALL_DIR="/opt/openvidu" CLUSTER_CONFIG_DIR="${INSTALL_DIR}/config/cluster" MASTER_NODE_CONFIG_DIR="${INSTALL_DIR}/config/node" if [[ ${certificateType} == "letsencrypt" ]]; then LETSENCRYPT_EMAIL="$(/usr/local/bin/get_value_from_config.sh LETSENCRYPT_EMAIL "${CLUSTER_CONFIG_DIR}/openvidu.env")" az keyvault secret set --vault-name ${keyVaultName} --name "LETSENCRYPT-EMAIL" --value $LETSENCRYPT_EMAIL fi # Get current values of the config REDIS_PASSWORD="$(/usr/local/bin/get_value_from_config.sh REDIS_PASSWORD "${MASTER_NODE_CONFIG_DIR}/master_node.env")" DOMAIN_NAME="$(/usr/local/bin/get_value_from_config.sh DOMAIN_NAME "${CLUSTER_CONFIG_DIR}/openvidu.env")" LIVEKIT_TURN_DOMAIN_NAME="$(/usr/local/bin/get_value_from_config.sh LIVEKIT_TURN_DOMAIN_NAME "${CLUSTER_CONFIG_DIR}/openvidu.env")" OPENVIDU_RTC_ENGINE="$(/usr/local/bin/get_value_from_config.sh OPENVIDU_RTC_ENGINE "${CLUSTER_CONFIG_DIR}/openvidu.env")" OPENVIDU_PRO_LICENSE="$(/usr/local/bin/get_value_from_config.sh OPENVIDU_PRO_LICENSE "${CLUSTER_CONFIG_DIR}/openvidu.env")" MONGO_ADMIN_USERNAME="$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_USERNAME "${CLUSTER_CONFIG_DIR}/openvidu.env")" MONGO_ADMIN_PASSWORD="$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_PASSWORD "${CLUSTER_CONFIG_DIR}/openvidu.env")" MONGO_REPLICA_SET_KEY="$(/usr/local/bin/get_value_from_config.sh MONGO_REPLICA_SET_KEY "${CLUSTER_CONFIG_DIR}/openvidu.env")" MINIO_ACCESS_KEY="$(/usr/local/bin/get_value_from_config.sh MINIO_ACCESS_KEY "${CLUSTER_CONFIG_DIR}/openvidu.env")" MINIO_SECRET_KEY="$(/usr/local/bin/get_value_from_config.sh MINIO_SECRET_KEY "${CLUSTER_CONFIG_DIR}/openvidu.env")" DASHBOARD_ADMIN_USERNAME="$(/usr/local/bin/get_value_from_config.sh DASHBOARD_ADMIN_USERNAME "${CLUSTER_CONFIG_DIR}/openvidu.env")" DASHBOARD_ADMIN_PASSWORD="$(/usr/local/bin/get_value_from_config.sh DASHBOARD_ADMIN_PASSWORD "${CLUSTER_CONFIG_DIR}/openvidu.env")" GRAFANA_ADMIN_USERNAME="$(/usr/local/bin/get_value_from_config.sh GRAFANA_ADMIN_USERNAME "${CLUSTER_CONFIG_DIR}/openvidu.env")" GRAFANA_ADMIN_PASSWORD="$(/usr/local/bin/get_value_from_config.sh GRAFANA_ADMIN_PASSWORD "${CLUSTER_CONFIG_DIR}/openvidu.env")" LIVEKIT_API_KEY="$(/usr/local/bin/get_value_from_config.sh LIVEKIT_API_KEY "${CLUSTER_CONFIG_DIR}/openvidu.env")" LIVEKIT_API_SECRET="$(/usr/local/bin/get_value_from_config.sh LIVEKIT_API_SECRET "${CLUSTER_CONFIG_DIR}/openvidu.env")" DEFAULT_APP_USERNAME="$(/usr/local/bin/get_value_from_config.sh CALL_USER "${CLUSTER_CONFIG_DIR}/master_node/app.env")" DEFAULT_APP_PASSWORD="$(/usr/local/bin/get_value_from_config.sh CALL_SECRET "${CLUSTER_CONFIG_DIR}/master_node/app.env")" DEFAULT_APP_ADMIN_USERNAME="$(/usr/local/bin/get_value_from_config.sh CALL_ADMIN_USER "${CLUSTER_CONFIG_DIR}/master_node/app.env")" DEFAULT_APP_ADMIN_PASSWORD="$(/usr/local/bin/get_value_from_config.sh CALL_ADMIN_SECRET "${CLUSTER_CONFIG_DIR}/master_node/app.env")" ENABLED_MODULES="$(/usr/local/bin/get_value_from_config.sh ENABLED_MODULES "${CLUSTER_CONFIG_DIR}/openvidu.env")" # Update shared secret az keyvault secret set --vault-name ${keyVaultName} --name REDIS-PASSWORD --value $REDIS_PASSWORD az keyvault secret set --vault-name ${keyVaultName} --name DOMAIN-NAME --value $DOMAIN_NAME az keyvault secret set --vault-name ${keyVaultName} --name LIVEKIT-TURN-DOMAIN-NAME --value $LIVEKIT_TURN_DOMAIN_NAME az keyvault secret set --vault-name ${keyVaultName} --name OPENVIDU-RTC-ENGINE --value $OPENVIDU_RTC_ENGINE az keyvault secret set --vault-name ${keyVaultName} --name OPENVIDU-PRO-LICENSE --value $OPENVIDU_PRO_LICENSE az keyvault secret set --vault-name ${keyVaultName} --name MONGO-ADMIN-USERNAME --value $MONGO_ADMIN_USERNAME az keyvault secret set --vault-name ${keyVaultName} --name MONGO-ADMIN-PASSWORD --value $MONGO_ADMIN_PASSWORD az keyvault secret set --vault-name ${keyVaultName} --name MONGO-REPLICA-SET-KEY --value $MONGO_REPLICA_SET_KEY az keyvault secret set --vault-name ${keyVaultName} --name MINIO-ACCESS-KEY --value $MINIO_ACCESS_KEY az keyvault secret set --vault-name ${keyVaultName} --name MINIO-SECRET-KEY --value $MINIO_SECRET_KEY az keyvault secret set --vault-name ${keyVaultName} --name DASHBOARD-ADMIN-USERNAME --value $DASHBOARD_ADMIN_USERNAME az keyvault secret set --vault-name ${keyVaultName} --name DASHBOARD-ADMIN-PASSWORD --value $DASHBOARD_ADMIN_PASSWORD az keyvault secret set --vault-name ${keyVaultName} --name GRAFANA-ADMIN-USERNAME --value $GRAFANA_ADMIN_USERNAME az keyvault secret set --vault-name ${keyVaultName} --name GRAFANA-ADMIN-PASSWORD --value $GRAFANA_ADMIN_PASSWORD az keyvault secret set --vault-name ${keyVaultName} --name LIVEKIT-API-KEY --value $LIVEKIT_API_KEY az keyvault secret set --vault-name ${keyVaultName} --name LIVEKIT-API-SECRET --value $LIVEKIT_API_SECRET az keyvault secret set --vault-name ${keyVaultName} --name DEFAULT-APP-USERNAME --value $DEFAULT_APP_USERNAME az keyvault secret set --vault-name ${keyVaultName} --name DEFAULT-APP-PASSWORD --value $DEFAULT_APP_PASSWORD az keyvault secret set --vault-name ${keyVaultName} --name DEFAULT-APP-ADMIN-USERNAME --value $DEFAULT_APP_ADMIN_USERNAME az keyvault secret set --vault-name ${keyVaultName} --name DEFAULT-APP-ADMIN-PASSWORD --value $DEFAULT_APP_ADMIN_PASSWORD az keyvault secret set --vault-name ${keyVaultName} --name ENABLED-MODULES --value $ENABLED_MODULES ''' var get_value_from_configScriptMaster = ''' #!/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 " 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" ''' var store_secretScriptTemplateMaster = ''' #!/bin/bash set -e az login --identity --allow-no-subscriptions > /dev/null # 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" 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}" az keyvault secret set --vault-name ${keyVaultName} --name $SECRET_KEY_NAME --value $RANDOM_PASSWORD > /dev/null if [[ $? -ne 0 ]]; then echo "Error generating secret" fi echo "$RANDOM_PASSWORD" elif [[ "$MODE" == "save" ]]; then SECRET_KEY_NAME="$2" SECRET_VALUE="$3" az keyvault secret set --vault-name ${keyVaultName} --name $SECRET_KEY_NAME --value $SECRET_VALUE > /dev/null if [[ $? -ne 0 ]]; then echo "Error generating secret" fi echo "$SECRET_VALUE" else exit 1 fi ''' var check_app_readyScriptMaster = ''' #!/bin/bash set -e while true; do HTTP_STATUS=$(curl -Ik http://localhost:7880/twirp/health | head -n1 | awk '{print $2}') if [ $HTTP_STATUS == 200 ]; then break fi sleep 5 done ''' var restartScriptMaster = ''' #!/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 ''' var installScriptMaster1 = reduce( items(stringInterpolationParamsMaster1), { value: installScriptTemplateMaster }, (curr, next) => { value: replace(curr.value, '\${${next.key}}', next.value) } ).value var installScriptMaster2 = reduce( items(stringInterpolationParamsMaster2), { value: installScriptTemplateMaster }, (curr, next) => { value: replace(curr.value, '\${${next.key}}', next.value) } ).value var installScriptMaster3 = reduce( items(stringInterpolationParamsMaster3), { value: installScriptTemplateMaster }, (curr, next) => { value: replace(curr.value, '\${${next.key}}', next.value) } ).value var installScriptMaster4 = reduce( items(stringInterpolationParamsMaster4), { value: installScriptTemplateMaster }, (curr, next) => { value: replace(curr.value, '\${${next.key}}', next.value) } ).value var after_installScriptMaster = reduce( items(stringInterpolationParamsMaster1), { value: after_installScriptTemplateMaster }, (curr, next) => { value: replace(curr.value, '\${${next.key}}', next.value) } ).value var update_config_from_secretScriptMaster = reduce( items(stringInterpolationParamsMaster1), { value: update_config_from_secretScriptTemplateMaster }, (curr, next) => { value: replace(curr.value, '\${${next.key}}', next.value) } ).value var update_secret_from_configScriptMaster = reduce( items(stringInterpolationParamsMaster1), { value: update_secret_from_configScriptTemplateMaster }, (curr, next) => { value: replace(curr.value, '\${${next.key}}', next.value) } ).value var store_secretScriptMaster = reduce( items(stringInterpolationParamsMaster1), { value: store_secretScriptTemplateMaster }, (curr, next) => { value: replace(curr.value, '\${${next.key}}', next.value) } ).value var base64installMaster1 = base64(installScriptMaster1) var base64installMaster2 = base64(installScriptMaster2) var base64installMaster3 = base64(installScriptMaster3) var base64installMaster4 = base64(installScriptMaster4) var base64after_installMaster = base64(after_installScriptMaster) var base64update_config_from_secretMaster = base64(update_config_from_secretScriptMaster) var base64update_secret_from_configMaster = base64(update_secret_from_configScriptMaster) var base64get_value_from_configMaster = base64(get_value_from_configScriptMaster) var base64store_secretMaster = base64(store_secretScriptMaster) var base64check_app_readyMaster = base64(check_app_readyScriptMaster) var base64restartMaster = base64(restartScriptMaster) var userDataParamsMasterNode1 = { base64install: base64installMaster1 base64after_install: base64after_installMaster base64update_config_from_secret: base64update_config_from_secretMaster base64update_secret_from_config: base64update_secret_from_configMaster base64get_value_from_config: base64get_value_from_configMaster base64store_secret: base64store_secretMaster base64check_app_ready: base64check_app_readyMaster base64restart: base64restartMaster keyVaultName: keyVaultName masterNodeNum: '1' } var userDataParamsMasterNode2 = { base64install: base64installMaster2 base64after_install: base64after_installMaster base64update_config_from_secret: base64update_config_from_secretMaster base64update_secret_from_config: base64update_secret_from_configMaster base64get_value_from_config: base64get_value_from_configMaster base64store_secret: base64store_secretMaster base64check_app_ready: base64check_app_readyMaster base64restart: base64restartMaster keyVaultName: keyVaultName masterNodeNum: '2' } var userDataParamsMasterNode3 = { base64install: base64installMaster3 base64after_install: base64after_installMaster base64update_config_from_secret: base64update_config_from_secretMaster base64update_secret_from_config: base64update_secret_from_configMaster base64get_value_from_config: base64get_value_from_configMaster base64store_secret: base64store_secretMaster base64check_app_ready: base64check_app_readyMaster base64restart: base64restartMaster keyVaultName: keyVaultName masterNodeNum: '3' } var userDataParamsMasterNode4 = { base64install: base64installMaster4 base64after_install: base64after_installMaster base64update_config_from_secret: base64update_config_from_secretMaster base64update_secret_from_config: base64update_secret_from_configMaster base64get_value_from_config: base64get_value_from_configMaster base64store_secret: base64store_secretMaster base64check_app_ready: base64check_app_readyMaster base64restart: base64restartMaster keyVaultName: keyVaultName masterNodeNum: '4' storageAccountName: storageAccount.name } var userDataTemplateMasterNode = ''' #!/bin/bash -x set -eu -o pipefail # Introduce the scripts in the instance # install.sh echo ${base64install} | base64 -d > /usr/local/bin/install.sh chmod +x /usr/local/bin/install.sh # after_install.sh echo ${base64after_install} | base64 -d > /usr/local/bin/after_install.sh chmod +x /usr/local/bin/after_install.sh # update_config_from_secret.sh echo ${base64update_config_from_secret} | base64 -d > /usr/local/bin/update_config_from_secret.sh chmod +x /usr/local/bin/update_config_from_secret.sh # update_secret_from_config.sh echo ${base64update_secret_from_config} | base64 -d > /usr/local/bin/update_secret_from_config.sh chmod +x /usr/local/bin/update_secret_from_config.sh # get_value_from_config.sh echo ${base64get_value_from_config} | base64 -d > /usr/local/bin/get_value_from_config.sh chmod +x /usr/local/bin/get_value_from_config.sh # store_secret.sh echo ${base64store_secret} | base64 -d > /usr/local/bin/store_secret.sh chmod +x /usr/local/bin/store_secret.sh # check_app_ready.sh echo ${base64check_app_ready} | base64 -d > /usr/local/bin/check_app_ready.sh chmod +x /usr/local/bin/check_app_ready.sh # restart.sh echo ${base64restart} | base64 -d > /usr/local/bin/restart.sh chmod +x /usr/local/bin/restart.sh # Install azure cli curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash az login --identity --allow-no-subscriptions apt-get update && apt-get install -y export HOME="/root" # Install OpenVidu /usr/local/bin/install.sh || { echo "[OpenVidu] error installing OpenVidu"; 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" 2>&1 | crontab MASTER_NODE_NUM=${masterNodeNum} if [[ $MASTER_NODE_NUM -eq 4 ]]; then set +e az storage blob upload --account-name ${storageAccountName} --container-name automation-locks --name lock.txt --file /dev/null --auth-mode key set -e az keyvault secret set --vault-name ${keyVaultName} --name FINISH-MASTER-NODE --value "true" fi # Wait for the app sleep 150 /usr/local/bin/check_app_ready.sh ''' var userDataMasterNode1 = reduce( items(userDataParamsMasterNode1), { value: userDataTemplateMasterNode }, (curr, next) => { value: replace(curr.value, '\${${next.key}}', next.value) } ).value var userDataMasterNode2 = reduce( items(userDataParamsMasterNode2), { value: userDataTemplateMasterNode }, (curr, next) => { value: replace(curr.value, '\${${next.key}}', next.value) } ).value var userDataMasterNode3 = reduce( items(userDataParamsMasterNode3), { value: userDataTemplateMasterNode }, (curr, next) => { value: replace(curr.value, '\${${next.key}}', next.value) } ).value var userDataMasterNode4 = reduce( items(userDataParamsMasterNode4), { value: userDataTemplateMasterNode }, (curr, next) => { value: replace(curr.value, '\${${next.key}}', next.value) } ).value resource openviduMasterNode1 'Microsoft.Compute/virtualMachines@2023-09-01' = { name: '${stackName}-VM-MasterNode1' location: location identity: { type: 'SystemAssigned' } properties: { hardwareProfile: { vmSize: masterNodeInstanceType } storageProfile: { osDisk: { createOption: 'FromImage' managedDisk: { storageAccountType: masterNodeVMSettings.osDiskType } diskSizeGB: masterNodeVMSettings.osDiskSize } imageReference: masterNodeVMSettings.ubuntuOSVersion } networkProfile: { networkInterfaces: [ { id: netInterfaceMasterNode1.id } ] } osProfile: { computerName: '${stackName}-VM-MasterNode1' adminUsername: adminUsername adminPassword: adminSshKey linuxConfiguration: masterNodeVMSettings.linuxConfiguration } userData: base64(userDataMasterNode1) } } resource openviduMasterNode2 'Microsoft.Compute/virtualMachines@2023-09-01' = { name: '${stackName}-VM-MasterNode2' location: location identity: { type: 'SystemAssigned' } properties: { hardwareProfile: { vmSize: masterNodeInstanceType } storageProfile: { osDisk: { createOption: 'FromImage' managedDisk: { storageAccountType: masterNodeVMSettings.osDiskType } diskSizeGB: masterNodeVMSettings.osDiskSize } imageReference: masterNodeVMSettings.ubuntuOSVersion } networkProfile: { networkInterfaces: [ { id: netInterfaceMasterNode2.id } ] } osProfile: { computerName: '${stackName}-VM-MasterNode2' adminUsername: adminUsername adminPassword: adminSshKey linuxConfiguration: masterNodeVMSettings.linuxConfiguration } userData: base64(userDataMasterNode2) } dependsOn: [openviduMasterNode1] } resource openviduMasterNode3 'Microsoft.Compute/virtualMachines@2023-09-01' = { name: '${stackName}-VM-MasterNode3' location: location identity: { type: 'SystemAssigned' } properties: { hardwareProfile: { vmSize: masterNodeInstanceType } storageProfile: { osDisk: { createOption: 'FromImage' managedDisk: { storageAccountType: masterNodeVMSettings.osDiskType } diskSizeGB: masterNodeVMSettings.osDiskSize } imageReference: masterNodeVMSettings.ubuntuOSVersion } networkProfile: { networkInterfaces: [ { id: netInterfaceMasterNode3.id } ] } osProfile: { computerName: '${stackName}-VM-MasterNode3' adminUsername: adminUsername adminPassword: adminSshKey linuxConfiguration: masterNodeVMSettings.linuxConfiguration } userData: base64(userDataMasterNode3) } dependsOn: [openviduMasterNode2] } resource openviduMasterNode4 'Microsoft.Compute/virtualMachines@2023-09-01' = { name: '${stackName}-VM-MasterNode4' location: location identity: { type: 'SystemAssigned' } properties: { hardwareProfile: { vmSize: masterNodeInstanceType } storageProfile: { osDisk: { createOption: 'FromImage' managedDisk: { storageAccountType: masterNodeVMSettings.osDiskType } diskSizeGB: masterNodeVMSettings.osDiskSize } imageReference: masterNodeVMSettings.ubuntuOSVersion } networkProfile: { networkInterfaces: [ { id: netInterfaceMasterNode4.id } ] } osProfile: { computerName: '${stackName}-VM-MasterNode4' adminUsername: adminUsername adminPassword: adminSshKey linuxConfiguration: masterNodeVMSettings.linuxConfiguration } userData: base64(userDataMasterNode4) } dependsOn: [openviduMasterNode3] } /*------------------------------------------- MEDIA NODES -------------------------------------------*/ var privateIPMasterNode1 = netInterfaceMasterNode1.properties.ipConfigurations[0].properties.privateIPAddress var privateIPMasterNode2 = netInterfaceMasterNode2.properties.ipConfigurations[0].properties.privateIPAddress var privateIPMasterNode3 = netInterfaceMasterNode3.properties.ipConfigurations[0].properties.privateIPAddress var privateIPMasterNode4 = netInterfaceMasterNode4.properties.ipConfigurations[0].properties.privateIPAddress var stringInterpolationParamsMedia = { privateIPMasterNode1: privateIPMasterNode1 privateIPMasterNode2: privateIPMasterNode2 privateIPMasterNode3: privateIPMasterNode3 privateIPMasterNode4: privateIPMasterNode4 keyVaultName: keyVaultName } var installScriptTemplateMedia = ''' #!/bin/bash -x DOMAIN= # Install dependencies apt-get update && apt-get install -y \ curl \ unzip \ jq \ wget # Get own private IP PRIVATE_IP=$(curl -H Metadata:true --noproxy "*" "http://169.254.169.254/metadata/instance/network/interface/0/ipv4/ipAddress/0/privateIpAddress?api-version=2017-08-01&format=text") WAIT_INTERVAL=1 MAX_WAIT=10000 ELAPSED_TIME=0 set +e while true; do # get secret value FINISH_MASTER_NODE=$(az keyvault secret show --vault-name ${keyVaultName} --name FINISH-MASTER-NODE --query value -o tsv) # Check if all master nodes finished if [ "$FINISH_MASTER_NODE" == "true" ]; then break fi ELAPSED_TIME=$((ELAPSED_TIME + WAIT_INTERVAL)) # Check if the maximum waiting time has been reached if [ $ELAPSED_TIME -ge $MAX_WAIT ]; then exit 1 fi sleep $WAIT_INTERVAL done set -e MASTER_NODE_1_PRIVATE_IP=$(az keyvault secret show --vault-name ${keyVaultName} --name MASTER-NODE-1-PRIVATE-IP --query value -o tsv) MASTER_NODE_2_PRIVATE_IP=$(az keyvault secret show --vault-name ${keyVaultName} --name MASTER-NODE-2-PRIVATE-IP --query value -o tsv) MASTER_NODE_3_PRIVATE_IP=$(az keyvault secret show --vault-name ${keyVaultName} --name MASTER-NODE-3-PRIVATE-IP --query value -o tsv) MASTER_NODE_4_PRIVATE_IP=$(az keyvault secret show --vault-name ${keyVaultName} --name MASTER-NODE-4-PRIVATE-IP --query value -o tsv) 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=$(az keyvault secret show --vault-name ${keyVaultName} --name REDIS-PASSWORD --query value -o tsv) ENABLED_MODULES=$(az keyvault secret show --vault-name ${keyVaultName} --name ENABLED-MODULES --query value -o tsv) OPENVIDU_VERSION=$(az keyvault secret show --vault-name ${keyVaultName} --name OPENVIDU-VERSION --query value -o tsv) # 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=azure" "--deployment-type='ha'" "--node-role='media-node'" "--master-node-private-ip-list=$MASTER_NODE_PRIVATE_IP_LIST" "--private-ip=$PRIVATE_IP" "--enabled-modules='$ENABLED_MODULES'" "--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" ''' var stopMediaNodeParams = { subscriptionId: subscription().subscriptionId resourceGroupName: resourceGroup().name vmScaleSetName: '${stackName}-mediaNodeScaleSet' storageAccountName: storageAccount.name } var stop_media_nodesScriptMediaTemplate = ''' #!/bin/bash set -e if ! (set -o noclobber ; echo > /tmp/global.lock) ; then exit 1 # the global.lock already exists fi # Execute if docker is installed if [ -x "$(command -v docker)" ]; then echo "Stopping media node services and waiting for termination..." docker container kill --signal=SIGINT openvidu || true docker container kill --signal=SIGINT ingress || true docker container kill --signal=SIGINT egress || true # Wait for running containers to not be openvidu, ingress or egress while [ $(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 done fi az login --identity RESOURCE_GROUP_NAME=${resourceGroupName} VM_SCALE_SET_NAME=${vmScaleSetName} SUBSCRIPTION_ID=${subscriptionId} BEFORE_INSTANCE_ID=$(curl -H Metadata:true --noproxy "*" "http://169.254.169.254/metadata/instance?api-version=2021-02-01" | jq -r '.compute.resourceId') INSTANCE_ID=$(echo $BEFORE_INSTANCE_ID | awk -F'/' '{print $NF}') RESOURCE_ID=/subscriptions/$SUBSCRIPTION_ID/resourcegroups/$RESOURCE_GROUP_NAME/providers/Microsoft.Compute/virtualMachineScaleSets/$VM_SCALE_SET_NAME TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ") az tag update --resource-id $RESOURCE_ID --operation replace --tags "STATUS"="HEALTHY" "InstanceDeleteTime"="$TIMESTAMP" "storageAccount"="${storageAccountName}" az vmss delete-instances --resource-group $RESOURCE_GROUP_NAME --name $VM_SCALE_SET_NAME --instance-ids $INSTANCE_ID ''' var userDataMediaNodeTemplate = ''' #!/bin/bash -x set -eu -o pipefail # Introduce the scripts in the instance # install.sh echo ${base64install} | base64 -d > /usr/local/bin/install.sh chmod +x /usr/local/bin/install.sh # stop_media_nodes.sh echo ${base64stop} | base64 -d > /usr/local/bin/stop_media_node.sh chmod +x /usr/local/bin/stop_media_node.sh apt-get update && apt-get install -y apt-get install -y jq # Install azure cli curl -sL https://aka.ms/InstallAzureCLIDeb | sudo bash az login --identity # Protect from scale in actions RESOURCE_GROUP_NAME=${resourceGroupName} VM_SCALE_SET_NAME=${vmScaleSetName} BEFORE_INSTANCE_ID=$(curl -H Metadata:true --noproxy "*" "http://169.254.169.254/metadata/instance?api-version=2021-02-01" | jq -r '.compute.resourceId') INSTANCE_ID=$(echo $BEFORE_INSTANCE_ID | awk -F'/' '{print $NF}') az vmss update --resource-group $RESOURCE_GROUP_NAME --name $VM_SCALE_SET_NAME --instance-id $INSTANCE_ID --protect-from-scale-in true export HOME="/root" # Install OpenVidu /usr/local/bin/install.sh || { echo "[OpenVidu] error installing OpenVidu"; exit 1; } # Start OpenVidu systemctl start openvidu || { echo "[OpenVidu] error starting OpenVidu"; exit 1; } ''' var installScriptMedia = reduce( items(stringInterpolationParamsMedia), { value: installScriptTemplateMedia }, (curr, next) => { value: replace(curr.value, '\${${next.key}}', next.value) } ).value var stop_media_nodesScriptMedia = reduce( items(stopMediaNodeParams), { value: stop_media_nodesScriptMediaTemplate }, (curr, next) => { value: replace(curr.value, '\${${next.key}}', next.value) } ).value var base64installMedia = base64(installScriptMedia) var base64stopMediaNode = base64(stop_media_nodesScriptMedia) var userDataParamsMedia = { base64install: base64installMedia base64stop: base64stopMediaNode } var userDataMediaNode = reduce( items(userDataParamsMedia), { value: userDataMediaNodeTemplate }, (curr, next) => { value: replace(curr.value, '\${${next.key}}', next.value) } ).value var base64userDataMediaNode = base64(userDataMediaNode) param datetime string = utcNow('u') resource openviduScaleSetMediaNode 'Microsoft.Compute/virtualMachineScaleSets@2024-07-01' = { name: '${stackName}-mediaNodeScaleSet' location: location tags: { InstanceDeleteTime: datetime storageAccount: storageAccount.name } identity: { type: 'SystemAssigned' } sku: { name: mediaNodeInstanceType tier: 'Standard' capacity: initialNumberOfMediaNodes } properties: { overprovision: true upgradePolicy: { mode: 'Automatic' } singlePlacementGroup: true platformFaultDomainCount: 1 virtualMachineProfile: { storageProfile: { osDisk: { createOption: 'FromImage' managedDisk: { storageAccountType: mediaNodeVMSettings.osDiskType } diskSizeGB: 50 } imageReference: mediaNodeVMSettings.ubuntuOSVersion } osProfile: { computerNamePrefix: mediaNodeVMSettings.vmName adminUsername: adminUsername adminPassword: adminSshKey linuxConfiguration: mediaNodeVMSettings.linuxConfiguration } networkProfile: { networkInterfaceConfigurations: [ { name: '${stackName}-mediaNodeNetInterface' properties: { primary: true ipConfigurations: [ { name: 'ipconfigMediaNode' properties: { subnet: { id: vnet_OV.properties.subnets[0].id } applicationSecurityGroups: [ { id: openviduMediaNodeASG.id } ] publicIPAddressConfiguration: { name: 'publicIPAddressMediaNode' properties: { publicIPAddressVersion: 'IPv4' } } } } ] networkSecurityGroup: { id: openviduMediaNodeNSG.id } } } ] } userData: base64userDataMediaNode } } } resource openviduAutoScaleSettingsMediaNode 'Microsoft.Insights/autoscaleSettings@2022-10-01' = { name: '${stackName}-autoscaleSettings' location: resourceGroup().location properties: { profiles: [ { name: 'openvidu-medianode-autoscale' capacity: { minimum: string(minNumberOfMediaNodes) maximum: string(maxNumberOfMediaNodes) default: string(initialNumberOfMediaNodes) } rules: [ { metricTrigger: { metricName: 'Percentage CPU' metricNamespace: 'Microsoft.Compute/virtualMachineScaleSets' metricResourceUri: openviduScaleSetMediaNode.id statistic: 'Average' operator: 'GreaterThan' threshold: scaleTargetCPU timeAggregation: 'Average' timeWindow: 'PT5M' timeGrain: 'PT1M' } scaleAction: { direction: 'Increase' type: 'ChangeCount' value: '1' cooldown: 'PT5M' } } { metricTrigger: { metricName: 'Percentage CPU' metricNamespace: 'Microsoft.Compute/virtualMachineScaleSets' metricResourceUri: openviduScaleSetMediaNode.id statistic: 'Average' operator: 'LessThan' threshold: scaleTargetCPU timeAggregation: 'Average' timeWindow: 'PT5M' timeGrain: 'PT1M' } scaleAction: { direction: 'Decrease' type: 'ChangeCount' value: '1' cooldown: 'PT5M' } } ] } ] enabled: true targetResourceUri: openviduScaleSetMediaNode.id } } /*------------------------------------------- SCALE IN ------------------------------------------*/ resource roleAssignmentMasterNode 'Microsoft.Authorization/roleAssignments@2022-04-01' = { name: guid('roleAssignmentForMasterNode${openviduMasterNode4.name}') scope: resourceGroup() properties: { roleDefinitionId: subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c' ) principalId: openviduMasterNode4.identity.principalId } } resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { name: guid('roleAssignmentForScaleSet${openviduScaleSetMediaNode.name}') scope: resourceGroup() properties: { roleDefinitionId: subscriptionResourceId( 'Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c' ) principalId: openviduScaleSetMediaNode.identity.principalId } } @description('Automation Account Name to create a runbook inside it for scale in') param automationAccountName string module webhookModule '../../shared/webhookdeployment.json' = { params: { automationAccountName: automationAccountName runbookName: 'scaleInRunbook' webhookName: 'webhookForScaleIn' WebhookExpiryTime: '2035-03-30T00:00:00Z' _artifactsLocation: 'https://raw.githubusercontent.com/OpenVidu/openvidu/refs/heads/master/openvidu-deployment/pro/shared/scaleInRunbook.ps1' } name: 'WebhookDeployment' } resource actionGroupScaleIn 'Microsoft.Insights/actionGroups@2023-01-01' = { name: 'actiongrouptest' location: 'global' properties: { groupShortName: 'scaleinag' enabled: true automationRunbookReceivers: [ { name: 'scalein' useCommonAlertSchema: false automationAccountId: webhookModule.outputs.automationAccountId runbookName: 'scaleInRunbook' webhookResourceId: webhookModule.outputs.webhookId isGlobalRunbook: false serviceUri: webhookModule.outputs.webhookUri } ] } } resource scaleInActivityLogRule 'Microsoft.Insights/activityLogAlerts@2020-10-01' = { name: 'ScaleInAlertRule' location: 'global' properties: { scopes: [ openviduScaleSetMediaNode.id ] condition: { allOf: [ { field: 'category' equals: 'Administrative' } { field: 'operationName' equals: 'Microsoft.Compute/virtualMachineScaleSets/write' } { field: 'level' containsAny: [ 'error' ] } { field: 'status' containsAny: [ 'failed' ] } { field: 'caller' equals: '42628537-ebd8-40bf-941a-dddd338e1fe9' } ] } actions: { actionGroups: [ { actionGroupId: actionGroupScaleIn.id } ] } enabled: true } } /*------------------------------------------- NETWORK -------------------------------------------*/ var isEmptyIp = publicIpAddressResourceName == '' var turnIsEmptyIp = turnPublicIpAddressResourceName == '' var lbName = '${stackName}-loadBalancer' var lbFrontEndName = 'LoadBalancerFrontEnd' var lbBackendPoolNameMasterNode = 'LoadBalancerBackEndMasterNode' resource publicIPAddressLoadBalancer 'Microsoft.Network/publicIPAddresses@2024-05-01' = if (isEmptyIp == true) { name: '${stackName}-publicIPAddressLoadBalancer' location: location sku: { name: 'Standard' } properties: { publicIPAddressVersion: 'IPv4' publicIPAllocationMethod: 'Static' } } resource publicIP_LoadBalancer_ifNotEmpty 'Microsoft.Network/publicIPAddresses@2023-11-01' existing = if (!isEmptyIp == true) { name: publicIpAddressResourceName } resource publicIPAddressTurnTLSLoadBalancer 'Microsoft.Network/publicIPAddresses@2024-05-01' = if (turnTLSIsEnabled == true) { name: '${stackName}-publicIPAddressTurnTLSLoadBalancer' location: location sku: { name: 'Standard' } properties: { publicIPAddressVersion: 'IPv4' publicIPAllocationMethod: 'Static' } } resource publicIP_TurnTLSLoadBalancer_ifNotEmpty 'Microsoft.Network/publicIPAddresses@2023-11-01' existing = if (!turnIsEmptyIp && turnTLSIsEnabled == true) { name: publicIpAddressResourceName } resource LoadBalancer 'Microsoft.Network/loadBalancers@2024-05-01' = { name: lbName location: location sku: { name: 'Standard' } properties: { frontendIPConfigurations: [ { name: lbFrontEndName properties: { publicIPAddress: { id: isEmptyIp ? publicIPAddressLoadBalancer.id : publicIP_LoadBalancer_ifNotEmpty.id } } } ] backendAddressPools: [ { name: lbBackendPoolNameMasterNode } ] loadBalancingRules: [ { name: 'HTTPSRuleforMasterNode' properties: { frontendIPConfiguration: { id: resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', lbName, lbFrontEndName) } backendAddressPool: { id: resourceId('Microsoft.Network/loadBalancers/backendAddressPools', lbName, lbBackendPoolNameMasterNode) } frontendPort: 443 backendPort: 443 enableFloatingIP: false protocol: 'Tcp' enableTcpReset: true loadDistribution: 'Default' disableOutboundSnat: true probe: { id: resourceId('Microsoft.Network/loadBalancers/probes', lbName, 'probeForHTTPSRuleMasterNode') } } } { name: 'RTMPRuleforMasterNode' properties: { frontendIPConfiguration: { id: resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', lbName, lbFrontEndName) } backendAddressPool: { id: resourceId('Microsoft.Network/loadBalancers/backendAddressPools', lbName, lbBackendPoolNameMasterNode) } frontendPort: 1935 backendPort: 1945 enableFloatingIP: false protocol: 'Tcp' enableTcpReset: true loadDistribution: 'Default' disableOutboundSnat: true probe: { id: resourceId('Microsoft.Network/loadBalancers/probes', lbName, 'probeForRTMPRuleMasterNode') } } } { name: 'HTTPRuleforMasterNode' properties: { frontendIPConfiguration: { id: resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', lbName, lbFrontEndName) } backendAddressPool: { id: resourceId('Microsoft.Network/loadBalancers/backendAddressPools', lbName, lbBackendPoolNameMasterNode) } frontendPort: 80 backendPort: 80 enableFloatingIP: false protocol: 'Tcp' enableTcpReset: true loadDistribution: 'Default' disableOutboundSnat: true probe: { id: resourceId('Microsoft.Network/loadBalancers/probes', lbName, 'probeForHTTPSRuleMasterNode') } } } ] probes: [ { name: 'probeForHTTPSRuleMasterNode' properties: { protocol: 'Http' requestPath: '/health/caddy' port: 7880 probeThreshold: 3 intervalInSeconds: 10 numberOfProbes: 5 } } { name: 'probeForRTMPRuleMasterNode' properties: { protocol: 'Tcp' port: 1945 intervalInSeconds: 5 numberOfProbes: 2 } } ] outboundRules: [] } } var tlbName = '${stackName}-loadBalancer' var tlbFrontEndName = 'LoadBalancerFrontEnd' resource TurnTLSLoadbalancer 'Microsoft.Network/loadBalancers@2024-05-01' = if (turnTLSIsEnabled == true) { name: tlbName location: location sku: { name: 'Standard' } properties: { frontendIPConfigurations: [ { name: tlbFrontEndName properties: { privateIPAllocationMethod: 'Dynamic' privateIPAddressVersion: 'IPv4' publicIPAddress: { id: turnIsEmptyIp ? publicIPAddressLoadBalancer.id : publicIP_TurnTLSLoadBalancer_ifNotEmpty.id } } } ] backendAddressPools: [ { name: lbBackendPoolNameMasterNode } ] loadBalancingRules: [ { name: 'TURNTLSRuleforMasterNode' properties: { frontendIPConfiguration: { id: resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', tlbName, tlbFrontEndName) } backendAddressPool: { id: resourceId('Microsoft.Network/loadBalancers/backendAddressPools', tlbName, lbBackendPoolNameMasterNode) } frontendPort: 443 backendPort: 443 enableFloatingIP: false protocol: 'Tcp' enableTcpReset: true loadDistribution: 'Default' disableOutboundSnat: true probe: { id: resourceId('Microsoft.Network/loadBalancers/probes', tlbName, 'probeForHTTPSRuleMasterNode') } } } ] probes: [ { name: 'probeForTURNTLSRuleMasterNode' properties: { protocol: 'Http' requestPath: '/' port: 443 probeThreshold: 3 intervalInSeconds: 10 numberOfProbes: 5 } } ] } } resource natGateway 'Microsoft.Network/natGateways@2021-05-01' = { name: '${stackName}-natGateway' location: location sku: { name: 'Standard' } properties: { idleTimeoutInMinutes: 4 publicIpAddresses: [ { id: natGatewayPublicIPAddress.id } ] } } resource natGatewayPublicIPAddress 'Microsoft.Network/publicIPAddresses@2021-05-01' = { name: '${stackName}-publicIPnatGateway' location: location sku: { name: 'Standard' } properties: { publicIPAddressVersion: 'IPv4' publicIPAllocationMethod: 'Static' idleTimeoutInMinutes: 4 } } var networkSettings = { vNetAddressPrefix: '10.0.0.0/16' subnetAddressPrefixMaster1: '10.0.1.0/24' subnetAddressPrefixMaster2: '10.0.2.0/24' subnetAddressPrefixMedia: '10.0.0.0/24' vNetName: '${stackName}-virtualNetwork' } resource vnet_OV 'Microsoft.Network/virtualNetworks@2023-11-01' = { name: networkSettings.vNetName location: location properties: { addressSpace: { addressPrefixes: [ networkSettings.vNetAddressPrefix ] } subnets: [ { name: 'subnetForMediaNodes' properties: { addressPrefix: networkSettings.subnetAddressPrefixMedia privateEndpointNetworkPolicies: 'Disabled' privateLinkServiceNetworkPolicies: 'Enabled' networkSecurityGroup: { id: openviduMediaNodeNSG.id } } } ] } } resource subnetMasterNode1 'Microsoft.Network/virtualNetworks/subnets@2023-11-01' = { parent: vnet_OV name: 'firstSubnetForMasterNodes' properties: { addressPrefix: networkSettings.subnetAddressPrefixMaster1 privateEndpointNetworkPolicies: 'Disabled' privateLinkServiceNetworkPolicies: 'Enabled' natGateway: { id: natGateway.id } } } resource subnetMasterNode2 'Microsoft.Network/virtualNetworks/subnets@2023-11-01' = { parent: vnet_OV name: 'secondSubnetForMasterNodes' properties: { addressPrefix: networkSettings.subnetAddressPrefixMaster2 privateEndpointNetworkPolicies: 'Disabled' privateLinkServiceNetworkPolicies: 'Enabled' natGateway: { id: natGateway.id } } } resource netInterfaceMasterNode1 'Microsoft.Network/networkInterfaces@2023-11-01' = { name: '${stackName}-masterNodeNetInterface1' location: location properties: { ipConfigurations: [ { name: 'primaryIPConfig' properties: { privateIPAllocationMethod: 'Dynamic' subnet: { id: subnetMasterNode1.id } applicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] loadBalancerBackendAddressPools: [ { id: LoadBalancer.properties.backendAddressPools[0].id } ] } } ] networkSecurityGroup: { id: openviduMasterNodeNSG.id } } } resource netInterfaceMasterNode2 'Microsoft.Network/networkInterfaces@2023-11-01' = { name: '${stackName}-masterNodeNetInterface2' location: location properties: { ipConfigurations: [ { name: 'primaryIPConfig' properties: { privateIPAllocationMethod: 'Dynamic' subnet: { id: subnetMasterNode2.id } applicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] loadBalancerBackendAddressPools: [ { id: LoadBalancer.properties.backendAddressPools[0].id } ] } } ] networkSecurityGroup: { id: openviduMasterNodeNSG.id } } } resource netInterfaceMasterNode3 'Microsoft.Network/networkInterfaces@2023-11-01' = { name: '${stackName}-masterNodeNetInterface3' location: location properties: { ipConfigurations: [ { name: 'primaryIPConfig' properties: { privateIPAllocationMethod: 'Dynamic' subnet: { id: subnetMasterNode1.id } applicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] loadBalancerBackendAddressPools: [ { id: LoadBalancer.properties.backendAddressPools[0].id } ] } } ] networkSecurityGroup: { id: openviduMasterNodeNSG.id } } } resource netInterfaceMasterNode4 'Microsoft.Network/networkInterfaces@2023-11-01' = { name: '${stackName}-masterNodeNetInterface4' location: location properties: { ipConfigurations: [ { name: 'primaryIPConfig' properties: { privateIPAllocationMethod: 'Dynamic' subnet: { id: subnetMasterNode2.id } applicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] loadBalancerBackendAddressPools: [ { id: LoadBalancer.properties.backendAddressPools[0].id } ] } } ] networkSecurityGroup: { id: openviduMasterNodeNSG.id } } } resource openviduMasterNodeNSG 'Microsoft.Network/networkSecurityGroups@2023-11-01' = { name: '${stackName}-masterNodeNSG' location: location properties: { securityRules: [ { name: 'SSH' properties: { protocol: 'Tcp' sourceAddressPrefix: '*' sourcePortRange: '*' destinationAddressPrefix: '*' destinationPortRange: '22' access: 'Allow' priority: 100 direction: 'Inbound' } } { name: 'ProbeAPI' properties: { protocol: 'Tcp' sourceAddressPrefix: 'AzureLoadBalancer' sourcePortRange: '*' destinationAddressPrefix: '*' destinationPortRange: '7880' access: 'Allow' priority: 500 direction: 'Inbound' } } { name: 'HTTP' properties: { protocol: 'Tcp' sourceAddressPrefix: '*' sourcePortRange: '*' destinationAddressPrefix: '*' destinationPortRange: '80' access: 'Allow' priority: 510 direction: 'Inbound' } } ] } } resource openviduMasterNodeASG 'Microsoft.Network/applicationSecurityGroups@2024-03-01' = { name: '${stackName}-masterNodeASG' location: location } resource loadBalancerToMasterIngress 'Microsoft.Network/networkSecurityGroups/securityRules@2023-11-01' = { parent: openviduMasterNodeNSG name: 'loadBalancer_to_masterNode_INGRESS' properties: { protocol: 'Tcp' sourceAddressPrefix: '*' sourcePortRange: '*' destinationApplicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] destinationPortRange: '443' access: 'Allow' priority: 110 direction: 'Inbound' } } resource masterToMasterRedisIngress 'Microsoft.Network/networkSecurityGroups/securityRules@2023-11-01' = { parent: openviduMasterNodeNSG name: 'masterNode_to_masterNode_REDIS_INGRESS' properties: { protocol: 'Tcp' sourceApplicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] sourcePortRange: '7000' destinationApplicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] destinationPortRange: '7001' access: 'Allow' priority: 120 direction: 'Inbound' } } resource mediaToMasterRedisIngress 'Microsoft.Network/networkSecurityGroups/securityRules@2023-11-01' = { parent: openviduMasterNodeNSG name: 'mediaNode_to_masterNode_REDIS_INGRESS' properties: { protocol: 'Tcp' sourceApplicationSecurityGroups: [ { id: openviduMediaNodeASG.id } ] sourcePortRange: '7000' destinationApplicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] destinationPortRange: '7001' access: 'Allow' priority: 130 direction: 'Inbound' } } resource masterToMasterMinioIngress 'Microsoft.Network/networkSecurityGroups/securityRules@2023-11-01' = { parent: openviduMasterNodeNSG name: 'masterNode_to_masterNode_MINIO_INGRESS' properties: { protocol: 'Tcp' sourceApplicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] sourcePortRange: '*' destinationApplicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] destinationPortRange: '9100' access: 'Allow' priority: 140 direction: 'Inbound' } } resource masterToMasterMinioConsoleIngress 'Microsoft.Network/networkSecurityGroups/securityRules@2023-11-01' = { parent: openviduMasterNodeNSG name: 'masterNode_to_masterNode_MINIO_CONSOLE_INGRESS' properties: { protocol: 'Tcp' sourceApplicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] sourcePortRange: '*' destinationApplicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] destinationPortRange: '9101' access: 'Allow' priority: 150 direction: 'Inbound' } } resource mediaToMasterMinioIngress 'Microsoft.Network/networkSecurityGroups/securityRules@2023-11-01' = { parent: openviduMasterNodeNSG name: 'mediaNode_to_masterNode_MINIO_INGRESS' properties: { protocol: 'Tcp' sourceApplicationSecurityGroups: [ { id: openviduMediaNodeASG.id } ] sourcePortRange: '*' destinationApplicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] destinationPortRange: '9100' access: 'Allow' priority: 160 direction: 'Inbound' } } resource masterToMasterMongoIngress 'Microsoft.Network/networkSecurityGroups/securityRules@2023-11-01' = { parent: openviduMasterNodeNSG name: 'masterNode_to_masterNode_MONGO_INGRESS' properties: { protocol: 'Tcp' sourceApplicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] sourcePortRange: '*' destinationApplicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] destinationPortRange: '20000' access: 'Allow' priority: 170 direction: 'Inbound' } } resource mediaToMasterMongoIngress 'Microsoft.Network/networkSecurityGroups/securityRules@2023-11-01' = { parent: openviduMasterNodeNSG name: 'mediaNode_to_masterNode_MONGO_INGRESS' properties: { protocol: 'Tcp' sourceApplicationSecurityGroups: [ { id: openviduMediaNodeASG.id } ] sourcePortRange: '*' destinationApplicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] destinationPortRange: '20000' access: 'Allow' priority: 180 direction: 'Inbound' } } resource masterToMasterMimirGrpcIngress 'Microsoft.Network/networkSecurityGroups/securityRules@2023-11-01' = { parent: openviduMasterNodeNSG name: 'masterNode_to_masterNode_MIMIRGRPC_INGRESS' properties: { protocol: 'Tcp' sourceApplicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] sourcePortRange: '*' destinationApplicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] destinationPortRange: '9095' access: 'Allow' priority: 190 direction: 'Inbound' } } resource masterToMasterMimirGossipIngress 'Microsoft.Network/networkSecurityGroups/securityRules@2023-11-01' = { parent: openviduMasterNodeNSG name: 'masterNode_to_masterNode_MIMIRGOSSIP_INGRESS' properties: { protocol: 'Tcp' sourceApplicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] sourcePortRange: '*' destinationApplicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] destinationPortRange: '7946' access: 'Allow' priority: 200 direction: 'Inbound' } } resource mediaToMasterMimirIngress 'Microsoft.Network/networkSecurityGroups/securityRules@2023-11-01' = { parent: openviduMasterNodeNSG name: 'mediaNode_to_masterNode_MIMIR_INGRESS' properties: { protocol: 'Tcp' sourceApplicationSecurityGroups: [ { id: openviduMediaNodeASG.id } ] sourcePortRange: '*' destinationApplicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] destinationPortRange: '9009' access: 'Allow' priority: 210 direction: 'Inbound' } } resource masterToMasterLokiGrpcIngress 'Microsoft.Network/networkSecurityGroups/securityRules@2023-11-01' = { parent: openviduMasterNodeNSG name: 'masterNode_to_masterNode_LOKIGRPC_INGRESS' properties: { protocol: 'Tcp' sourceApplicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] sourcePortRange: '*' destinationApplicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] destinationPortRange: '9096' access: 'Allow' priority: 220 direction: 'Inbound' } } resource masterToMasterLokiGossipIngress 'Microsoft.Network/networkSecurityGroups/securityRules@2023-11-01' = { parent: openviduMasterNodeNSG name: 'masterNode_to_masterNode_LOKIGOSSIP_INGRESS' properties: { protocol: 'Tcp' sourceApplicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] sourcePortRange: '*' destinationApplicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] destinationPortRange: '7947' access: 'Allow' priority: 230 direction: 'Inbound' } } resource masterToMasterDashboardIngress 'Microsoft.Network/networkSecurityGroups/securityRules@2023-11-01' = { parent: openviduMasterNodeNSG name: 'masterNode_to_masterNode_DASHBOARD_INGRESS' properties: { protocol: 'Tcp' sourceApplicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] sourcePortRange: '*' destinationApplicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] destinationPortRange: '5000' access: 'Allow' priority: 240 direction: 'Inbound' } } resource masterToMasterGrafanaIngress 'Microsoft.Network/networkSecurityGroups/securityRules@2023-11-01' = { parent: openviduMasterNodeNSG name: 'masterNode_to_masterNode_GRAFANA_INGRESS' properties: { protocol: 'Tcp' sourceApplicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] sourcePortRange: '*' destinationApplicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] destinationPortRange: '3000' access: 'Allow' priority: 250 direction: 'Inbound' } } resource mediaToMasterLokiIngress 'Microsoft.Network/networkSecurityGroups/securityRules@2023-11-01' = { parent: openviduMasterNodeNSG name: 'mediaNode_to_masterNode_LOKI_INGRESS' properties: { protocol: 'Tcp' sourceApplicationSecurityGroups: [ { id: openviduMediaNodeASG.id } ] sourcePortRange: '*' destinationApplicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] destinationPortRange: '3100' access: 'Allow' priority: 260 direction: 'Inbound' } } resource masterToMasterV2CompatibilityIngress 'Microsoft.Network/networkSecurityGroups/securityRules@2023-11-01' = { parent: openviduMasterNodeNSG name: 'masterNode_to_masterNode_V2COMPATIBILITY_INGRESS' properties: { protocol: 'Tcp' sourceApplicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] sourcePortRange: '*' destinationApplicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] destinationPortRange: '4443' access: 'Allow' priority: 270 direction: 'Inbound' } } resource mediaToMasterV2CompatibilityWebhookIngress 'Microsoft.Network/networkSecurityGroups/securityRules@2023-11-01' = { parent: openviduMasterNodeNSG name: 'mediaNode_to_masterNode_V2COMPATIBILITY_WEBHOOK_INGRESS' properties: { protocol: 'Tcp' sourceApplicationSecurityGroups: [ { id: openviduMediaNodeASG.id } ] sourcePortRange: '*' destinationApplicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] destinationPortRange: '4443' access: 'Allow' priority: 280 direction: 'Inbound' } } resource masterToMasterDefaultApp 'Microsoft.Network/networkSecurityGroups/securityRules@2023-11-01' = { parent: openviduMasterNodeNSG name: 'masterNode_to_masterNode_DEFAULTAPP_INGRESS' properties: { protocol: 'Tcp' sourceApplicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] sourcePortRange: '*' destinationApplicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] destinationPortRange: '6080' access: 'Allow' priority: 290 direction: 'Inbound' } } resource mediaToMasterDefaultAppWebhookIngress 'Microsoft.Network/networkSecurityGroups/securityRules@2023-11-01' = { parent: openviduMasterNodeNSG name: 'mediaNode_to_masterNode_DEFAULTAPP_WEBHOOK_INGRESS' properties: { protocol: 'Tcp' sourceApplicationSecurityGroups: [ { id: openviduMediaNodeASG.id } ] sourcePortRange: '*' destinationApplicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] destinationPortRange: '6080' access: 'Allow' priority: 300 direction: 'Inbound' } } resource openviduMediaNodeNSG 'Microsoft.Network/networkSecurityGroups@2023-11-01' = { name: '${stackName}-mediaNodeNSG' location: location properties: { securityRules: [ { name: 'SSH' properties: { protocol: 'Tcp' sourceAddressPrefix: '*' sourcePortRange: '*' destinationAddressPrefix: '*' destinationPortRange: '22' access: 'Allow' priority: 100 direction: 'Inbound' } } { name: 'TURN' properties: { protocol: 'Udp' sourceAddressPrefix: '*' sourcePortRange: '*' destinationAddressPrefix: '*' destinationPortRange: '443' access: 'Allow' priority: 110 direction: 'Inbound' } } { name: 'WebRTC_over_TCP' properties: { protocol: 'Tcp' sourceAddressPrefix: '*' sourcePortRange: '*' destinationAddressPrefix: '*' destinationPortRange: '7881' access: 'Allow' priority: 120 direction: 'Inbound' } } { name: 'WebRTC_using_WHIP' properties: { protocol: 'Udp' sourceAddressPrefix: '*' sourcePortRange: '*' destinationAddressPrefix: '*' destinationPortRange: '7885' access: 'Allow' priority: 130 direction: 'Inbound' } } { name: 'WebRTC_traffic_UDP' properties: { protocol: 'Udp' sourceAddressPrefix: '*' sourcePortRange: '*' destinationAddressPrefix: '*' destinationPortRanges: [ '50000' '60000' ] access: 'Allow' priority: 140 direction: 'Inbound' } } ] } } resource openviduMediaNodeASG 'Microsoft.Network/applicationSecurityGroups@2024-03-01' = { name: '${stackName}-mediaNodeASG' location: location } resource loadBalancerToMediaRtmpIngress 'Microsoft.Network/networkSecurityGroups/securityRules@2023-11-01' = { parent: openviduMediaNodeNSG name: 'loadBalancer_to_mediaNode_RTMP_INGRESS' properties: { protocol: 'Tcp' sourceAddressPrefix: 'AzureLoadBalancer' sourcePortRange: '*' destinationApplicationSecurityGroups: [ { id: openviduMediaNodeASG.id } ] destinationPortRange: '1945' access: 'Allow' priority: 150 direction: 'Inbound' } } resource loadBalancerToMediaHealthcheckIngress 'Microsoft.Network/networkSecurityGroups/securityRules@2023-11-01' = { parent: openviduMediaNodeNSG name: 'loadBalancer_to_mediaNode_HEALTHCHECK_INGRESS' properties: { protocol: 'Tcp' sourceAddressPrefix: 'AzureLoadBalancer' sourcePortRange: '*' destinationApplicationSecurityGroups: [ { id: openviduMediaNodeASG.id } ] destinationPortRange: '9092' access: 'Allow' priority: 160 direction: 'Inbound' } } resource loadBalancerToMediaTurnTlsIngress 'Microsoft.Network/networkSecurityGroups/securityRules@2023-11-01' = if (turnTLSIsEnabled == true) { parent: openviduMediaNodeNSG name: 'loadbalancer_to_mediaNode_TURN_TLS_INGRESS' properties: { protocol: 'Tcp' sourceAddressPrefix: 'AzureLoadBalancer' sourcePortRange: '*' destinationApplicationSecurityGroups: [ { id: openviduMediaNodeASG.id } ] destinationPortRange: '5349' access: 'Allow' priority: 170 direction: 'Inbound' } } resource loadBalancerToMediaTurnTlsHealthCheckIngress 'Microsoft.Network/networkSecurityGroups/securityRules@2023-11-01' = if (turnTLSIsEnabled == true) { parent: openviduMediaNodeNSG name: 'masterNode_to_mediaNode_TURN_TLSHEALTHCHECK_INGRESS' properties: { protocol: 'Tcp' sourceAddressPrefix: 'AzureLoadBalancer' sourcePortRange: '*' destinationApplicationSecurityGroups: [ { id: openviduMediaNodeASG.id } ] destinationPortRange: '7880' access: 'Allow' priority: 180 direction: 'Inbound' } } resource masterToMediaServerIngress 'Microsoft.Network/networkSecurityGroups/securityRules@2023-11-01' = { parent: openviduMediaNodeNSG name: 'masterNode_to_mediaNode_SERVER_INGRESS' properties: { protocol: 'Tcp' sourceApplicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] sourcePortRange: '*' destinationApplicationSecurityGroups: [ { id: openviduMediaNodeASG.id } ] destinationPortRange: '7880' access: 'Allow' priority: 190 direction: 'Inbound' } } resource masterToMediaClientIngress 'Microsoft.Network/networkSecurityGroups/securityRules@2023-11-01' = { parent: openviduMediaNodeNSG name: 'masterNode_to_mediaNode_CLIENT_INGRESS' properties: { protocol: 'Tcp' sourceApplicationSecurityGroups: [ { id: openviduMasterNodeASG.id } ] sourcePortRange: '*' destinationApplicationSecurityGroups: [ { id: openviduMediaNodeASG.id } ] destinationPortRange: '8080' access: 'Allow' priority: 200 direction: 'Inbound' } } /*------------------------------------------- STORAGE ACCOUNT ----------------------------------------*/ resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = { name: uniqueString(resourceGroup().id) location: resourceGroup().location sku: { name: 'Standard_LRS' } kind: 'StorageV2' properties: { accessTier: 'Cool' supportsHttpsTrafficOnly: true } } resource blobContainerScaleIn 'Microsoft.Storage/storageAccounts/blobServices/containers@2023-01-01' = { name: '${storageAccount.name}/default/automation-locks' properties: { publicAccess: 'None' } } @description('Name of the bucket where OpenVidu will store the recordings. If not specified, a default bucket will be created.') param containerName string = '' var isEmptyContainerName = containerName == '' resource blobContainer 'Microsoft.Storage/storageAccounts/blobServices/containers@2023-01-01' = { name: isEmptyContainerName ? '${storageAccount.name}/default/openvidu-appdata' : '${storageAccount.name}/default/${containerName}' properties: { publicAccess: 'None' } } /*------------------------------------------- OUTPUTS -------------------------------------------*/