2025-04-22 11:46:24 +02:00
{
"$schema" : "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#" ,
"contentVersion" : "1.0.0.0" ,
"metadata" : {
"_generator" : {
"name" : "bicep" ,
2026-09-07 16:58:47 +02:00
"version" : "0.46.1.21595" ,
2026-10-16 13:02:11 +02:00
"templateHash" : "3526845124636083473"
2025-04-22 11:46:24 +02:00
}
} ,
"parameters" : {
"stackName" : {
"type" : "string" ,
2026-06-09 11:37:02 +02:00
"minLength" : 3 ,
"maxLength" : 15 ,
2025-04-22 11:46:24 +02:00
"metadata" : {
2026-06-09 11:37:02 +02:00
"description" : "Stack name. Maximum 15 characters: used as the Azure Key Vault name prefix (\"<stackName>-keyvault\"), which is limited to 24 characters."
2025-04-22 11:46:24 +02:00
}
} ,
"certificateType" : {
"type" : "string" ,
2025-09-09 22:06:36 +02:00
"defaultValue" : "letsencrypt" ,
2025-04-22 11:46:24 +02:00
"allowedValues" : [
"selfsigned" ,
"owncert" ,
"letsencrypt"
] ,
"metadata" : {
2026-07-02 17:21:42 +02:00
"description" : "[selfsigned] Not recommended for production use. Just for testing purposes or development environments. You don't need a FQDN to use this option.\n[owncert] Valid for production environments. Use your own certificate. You need a FQDN to use this option.\n[letsencrypt] Valid for production environments. Can be used with or without a FQDN (if no FQDN is provided, the public IP will be used).\n"
2025-04-22 11:46:24 +02:00
}
} ,
"domainName" : {
"type" : "string" ,
2025-09-09 22:06:36 +02:00
"defaultValue" : "" ,
2025-04-22 11:46:24 +02:00
"metadata" : {
2025-05-13 11:01:22 +02:00
"description" : "Domain name for the OpenVidu Deployment."
2025-04-22 11:46:24 +02:00
}
} ,
"ownPublicCertificate" : {
"type" : "string" ,
"defaultValue" : "" ,
"metadata" : {
2025-12-23 22:11:16 +01:00
"description" : "If certificate type is 'owncert', this parameter will be used to specify the public certificate in base64 format"
2025-04-22 11:46:24 +02:00
}
} ,
"ownPrivateCertificate" : {
"type" : "string" ,
"defaultValue" : "" ,
"metadata" : {
2025-12-23 22:11:16 +01:00
"description" : "If certificate type is 'owncert', this parameter will be used to specify the private certificate in base64 format"
2025-04-22 11:46:24 +02:00
}
} ,
2025-05-23 13:53:24 +02:00
"publicIpAddressObject" : {
"type" : "object" ,
2025-04-22 11:46:24 +02:00
"metadata" : {
"description" : "Name of the PublicIPAddress resource in Azure when using certificateType 'owncert' or 'letsencrypt'"
}
} ,
"openviduLicense" : {
"type" : "securestring" ,
"metadata" : {
"description" : "Visit https://openvidu.io/account"
}
} ,
"rtcEngine" : {
"type" : "string" ,
2026-09-29 14:56:16 +02:00
"defaultValue" : "mediasoup" ,
2025-04-22 11:46:24 +02:00
"allowedValues" : [
"pion" ,
"mediasoup"
] ,
"metadata" : {
"description" : "RTCEngine media engine to use"
}
} ,
2025-09-16 17:52:10 +02:00
"initialMeetAdminPassword" : {
"type" : "securestring" ,
"defaultValue" : "" ,
"metadata" : {
"description" : "Initial password for the 'admin' user in OpenVidu Meet. If not provided, a random password will be generated."
}
} ,
"initialMeetApiKey" : {
"type" : "securestring" ,
"defaultValue" : "" ,
"metadata" : {
"description" : "Initial API key for OpenVidu Meet. If not provided, no API key will be set and the user can set it later from Meet Console."
}
} ,
2025-04-22 11:46:24 +02:00
"masterNodeInstanceType" : {
"type" : "string" ,
2025-12-02 19:21:16 +01:00
"defaultValue" : "Standard_B4s" ,
2025-04-22 11:46:24 +02:00
"metadata" : {
2025-12-02 19:21:16 +01:00
"description" : "Specifies the VM size for your OpenVidu Master Node. You can use any valid Azure VM size (e.g., Standard_B4s, Standard_D4s_v5, Standard_E4ps_v5). See https://learn.microsoft.com/en-us/azure/virtual-machines/sizes for available sizes."
2025-04-22 11:46:24 +02:00
}
} ,
"masterNodesDiskSize" : {
"type" : "int" ,
"defaultValue" : 100 ,
"metadata" : {
"description" : "Size of the disk in GB"
}
} ,
"mediaNodeInstanceType" : {
"type" : "string" ,
2025-12-02 19:21:16 +01:00
"defaultValue" : "Standard_B4s" ,
2025-04-22 11:46:24 +02:00
"metadata" : {
2025-12-02 19:21:16 +01:00
"description" : "Specifies the VM size for your OpenVidu Media Nodes. You can use any valid Azure VM size (e.g., Standard_B4s, Standard_D4s_v5, Standard_E4ps_v5). See https://learn.microsoft.com/en-us/azure/virtual-machines/sizes for available sizes."
2025-04-22 11:46:24 +02:00
}
} ,
"adminUsername" : {
"type" : "string" ,
"metadata" : {
"description" : "Username for the Virtual Machine."
}
} ,
"adminSshKey" : {
2025-05-23 13:53:24 +02:00
"type" : "secureObject" ,
2025-04-22 11:46:24 +02:00
"metadata" : {
"description" : "SSH Key for the Virtual Machine."
}
} ,
"initialNumberOfMediaNodes" : {
"type" : "int" ,
"defaultValue" : 1 ,
"metadata" : {
"description" : "Number of initial media nodes to deploy"
}
} ,
"minNumberOfMediaNodes" : {
"type" : "int" ,
"defaultValue" : 1 ,
"metadata" : {
"description" : "Minimum number of media nodes to deploy"
}
} ,
"maxNumberOfMediaNodes" : {
"type" : "int" ,
"defaultValue" : 5 ,
"metadata" : {
"description" : "Maximum number of media nodes to deploy"
}
} ,
"scaleTargetCPU" : {
"type" : "int" ,
"defaultValue" : 50 ,
"metadata" : {
"description" : "Target CPU percentage to scale up or down"
}
} ,
2025-06-18 16:26:50 +02:00
"additionalInstallFlags" : {
"type" : "string" ,
"defaultValue" : ""
} ,
2025-04-22 11:46:24 +02:00
"datetime" : {
"type" : "string" ,
"defaultValue" : "[utcNow('u')]"
} ,
"automationAccountName" : {
"type" : "string" ,
2025-05-27 11:18:05 +02:00
"defaultValue" : "" ,
2025-04-22 11:46:24 +02:00
"metadata" : {
"description" : "Automation Account Name to create a runbook inside it for scale in"
}
2025-04-23 13:26:33 +02:00
} ,
2025-05-13 11:01:22 +02:00
"storageAccountName" : {
"type" : "string" ,
"defaultValue" : "" ,
"metadata" : {
"description" : "Name of an existing storage account. It is essential that this parameter is filled just when you want to save recordings and still using the same container after an update. If not specified, a new storage account will be generated."
}
} ,
2025-06-10 16:30:41 +02:00
"appDataContainerName" : {
"type" : "string" ,
"defaultValue" : "" ,
"metadata" : {
"description" : "Name of the bucket where OpenVidu will store the recordings if a new Storage account is being creating. If not specified, a default bucket will be created. If you want to use an existing storage account, fill this parameter with the name of the container where the recordings are stored."
}
2025-04-22 11:46:24 +02:00
}
} ,
"variables" : {
2025-09-09 22:06:36 +02:00
"isEmptyIp" : "[equals(parameters('publicIpAddressObject').newOrExistingOrNone, 'none')]" ,
"isEmptyDomain" : "[equals(parameters('domainName'), '')]" ,
2025-12-02 19:21:16 +01:00
"masterNodeInstanceTypeLower" : "[toLower(parameters('masterNodeInstanceType'))]" ,
"mediaNodeInstanceTypeLower" : "[toLower(parameters('mediaNodeInstanceType'))]" ,
"isMasterArm64" : "[or(or(or(or(or(or(or(contains(variables('masterNodeInstanceTypeLower'), 'ps_v'), contains(variables('masterNodeInstanceTypeLower'), 'pls_v')), contains(variables('masterNodeInstanceTypeLower'), 'pds_v')), contains(variables('masterNodeInstanceTypeLower'), 'plds_v')), contains(variables('masterNodeInstanceTypeLower'), 'psv')), contains(variables('masterNodeInstanceTypeLower'), 'plsv')), contains(variables('masterNodeInstanceTypeLower'), 'pdsv')), contains(variables('masterNodeInstanceTypeLower'), 'pldsv'))]" ,
"isMediaArm64" : "[or(or(or(or(or(or(or(contains(variables('mediaNodeInstanceTypeLower'), 'ps_v'), contains(variables('mediaNodeInstanceTypeLower'), 'pls_v')), contains(variables('mediaNodeInstanceTypeLower'), 'pds_v')), contains(variables('mediaNodeInstanceTypeLower'), 'plds_v')), contains(variables('mediaNodeInstanceTypeLower'), 'psv')), contains(variables('mediaNodeInstanceTypeLower'), 'plsv')), contains(variables('mediaNodeInstanceTypeLower'), 'pdsv')), contains(variables('mediaNodeInstanceTypeLower'), 'pldsv'))]" ,
2025-12-02 20:04:38 +01:00
"masterUbuntuSku" : "[if(variables('isMasterArm64'), 'server-arm64', 'server')]" ,
"mediaUbuntuSku" : "[if(variables('isMediaArm64'), 'server-arm64', 'server')]" ,
2025-04-22 11:46:24 +02:00
"masterNodeVMSettings" : {
"osDiskType" : "StandardSSD_LRS" ,
"osDiskSize" : "[parameters('masterNodesDiskSize')]" ,
"ubuntuOSVersion" : {
"publisher" : "Canonical" ,
2025-12-02 20:04:38 +01:00
"offer" : "ubuntu-24_04-lts" ,
2025-12-02 18:29:44 +01:00
"sku" : "[variables('masterUbuntuSku')]" ,
2025-04-22 11:46:24 +02:00
"version" : "latest"
} ,
"linuxConfiguration" : {
"disablePasswordAuthentication" : true ,
"ssh" : {
"publicKeys" : [
{
"path" : "[format('/home/{0}/.ssh/authorized_keys', parameters('adminUsername'))]" ,
2025-05-23 13:53:24 +02:00
"keyData" : "[parameters('adminSshKey').sshPublicKey]"
2025-04-22 11:46:24 +02:00
}
]
}
}
} ,
"mediaNodeVMSettings" : {
"vmName" : "[format('{0}-VN-MediaNode', parameters('stackName'))]" ,
"osDiskType" : "StandardSSD_LRS" ,
"ubuntuOSVersion" : {
"publisher" : "Canonical" ,
2025-12-02 20:04:38 +01:00
"offer" : "ubuntu-24_04-lts" ,
2025-12-02 18:29:44 +01:00
"sku" : "[variables('mediaUbuntuSku')]" ,
2025-04-22 11:46:24 +02:00
"version" : "latest"
} ,
"linuxConfiguration" : {
"disablePasswordAuthentication" : true ,
"ssh" : {
"publicKeys" : [
{
"path" : "[format('/home/{0}/.ssh/authorized_keys', parameters('adminUsername'))]" ,
2025-05-23 13:53:24 +02:00
"keyData" : "[parameters('adminSshKey').sshPublicKey]"
2025-04-22 11:46:24 +02:00
}
]
}
}
} ,
"keyVaultName" : "[format('{0}-keyvault', parameters('stackName'))]" ,
"location" : "[resourceGroup().location]" ,
"tenantId" : "[subscription().tenantId]" ,
"deploymentUser" : "[deployer().objectId]" ,
2026-10-23 14:57:54 +02:00
"installScriptTemplateMaster" : "#!/bin/bash -x\nset -e\nOPENVIDU_VERSION=main\nDOMAIN=\n\n# Assume azure cli is installed\n\necho \"DPkg::Lock::Timeout \\\"-1\\\";\" > /etc/apt/apt.conf.d/99timeout\n\napt-get update && apt-get install -y \\\n curl \\\n unzip \\\n jq \\\n wget\n\n\n# Wait for the keyvault availability\nMAX_WAIT=300\nWAIT_INTERVAL=1\nELAPSED_TIME=0\nset +e\nwhile true; do\n # Check keyvault availability\n az keyvault secret list --vault-name ${keyVaultName} \n\n # If it is available, exit the loop\n if [ $? -eq 0 ]; then\n break\n fi\n\n # If not, wait and check again incrementing the time\n ELAPSED_TIME=$((ELAPSED_TIME + WAIT_INTERVAL))\n\n # If exceeded the maximum time, exit with error\n if [ $ELAPSED_TIME -ge $MAX_WAIT ]; then\n exit 1\n fi\n\n # Esperar antes de la próxima comprobación\n sleep $WAIT_INTERVAL\ndone\nset -e\n\nMASTER_NODE_NUM=${masterNodeNum}\n\n# Get own private IP\nPRIVATE_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\")\n\n# Store current private IP\nPRIVATE_IP=\"$(/usr/local/bin/store_secret.sh save MASTER-NODE-${masterNodeNum}-PRIVATE-IP $PRIVATE_IP)\"\n\n\nif [[ $MASTER_NODE_NUM -eq 1 ]] && [[ \"$ALL_SECRETS_GENERATED\" == \"\" || \"$ALL_SECRETS_GENERATED\" == \"false\" ]]; then\n\n # Configure Domain name\n if [[ \"${domainName}\" == '' ]]; then\n # Get public IP using the get_public_ip.sh script\n PUBLIC_IP=$(/usr/local/bin/get_public_ip.sh 2>/dev/null)\n if [[ $? -ne 0 || -z \"${PUBLIC_IP}\" ]]; then\n echo \"Could not determine public IP.\"\n exit 1\n fi\n\n DOMAIN=\"$PUBLIC_IP\"\n else\n DOMAIN=${domainName}\n fi\n DOMAIN=\"$(/usr/local/bin/store_secret.sh save DOMAIN-NAME \"${DOMAIN}\")\"\n\n # Publish service URLs early (idempotent; after_install re-writes byte-identical values)\n OPENVIDU_URL=\"$(/usr/local/bin/store_secret.sh save OPENVIDU-URL \"https://${DOMAIN}/\")\"\n LIVEKIT_URL=\"$(/usr/local/bin/store_secret.sh save LIVEKIT-URL \"wss://${DOMAIN}/\")\"\n DASHBOARD_URL=\"$(/usr/local/bin/store_secret.sh save DASHBOARD-URL \"https://${DOMAIN}/dashboard/\")\"\n GRAFANA_URL=\"$(/usr/local/bin/store_secret.sh save GRAFANA-URL \"https://${DOMAIN}/grafana/\")\"\n MINIO_URL=\"$(/usr/local/bin/store_secret.sh save MINIO-URL \"https://${DOMAIN}/minio-console/\")\"\n\n # Meet initial admin user and password\n MEET_INITIAL_ADMIN_USER=\"$(/usr/local/bin/store_secret.sh save MEET-INITIAL-ADMIN-USER \"admin\")\"\n if [[ \"${initialMeetAdminPassword}\" != '' ]]; then\n MEET_INITIAL_ADMIN_PASSWORD=\"$(/usr/local/bin/store_secret.sh save MEET-INITIAL-ADMIN-PASSWORD \"${initialMeetAdminPassword}\")\"\n else\n MEET_INITIAL_ADMIN_PASSWORD=\"$(/usr/local/bin/store_secret.sh generate MEET-INITIAL-ADMIN-PASSWORD)\"\n fi\n if [[ \"${initialMeetApiKey}\" != '' ]]; then\n MEET_INITIAL_API_KEY=\"$(/usr/local/bin/store_secret.sh save MEET-INITIAL-API-KEY \"${initialMeetApiKey}\")\"\n else\n MEET_INITIAL_API_KEY=\"$(/usr/local/bin/store_secret.sh save MEET-INITIAL-API-KEY \"\")\"\n fi\n\n # Store usernames and generate random passwords\n OPENVIDU_PRO_LICENSE=\"$(/usr/local/bin/store_secret.sh save OPENVIDU-PRO-LICENSE \"${openviduLicense}\")\"\n OPENVIDU_RTC_ENGINE=\"$(/usr/local/bin/store_secret.sh save OPENVIDU-RTC-ENGINE \"${rtcEngine}\")\"\n REDIS_PASSWORD=\"$(/usr/local/bin/store_secret.sh generate REDIS-PASSWORD)\"\n MONGO_ADMIN_USERNAME=\"$(/usr/local/bin/store_secret.sh save MONGO-ADMIN-USERNAME \"mongoadmin\")\"\n MONGO_ADMIN_PASSWORD=\"$(/usr/local/bin/store_secret.sh generate MONGO-ADMIN-PASSWORD)\"\n MONGO_REPLICA_SET_KEY=\"$(/usr/local/bin/store_secret.sh generate MONGO-REPLICA-SET-KEY)\"\n MINIO_ACCESS_KEY=\"$(/usr/local/bin/store_secret.sh save MINIO-ACCESS-KEY \"minioadmin\")\"\n MINIO_SECRET_KEY=\"$(/usr/local/bin/store_secret.sh generate MINIO-SECRET-KEY)\"\n DASHBOARD_ADMIN_USERNAME=\"$(/usr/local/bin/store_secret.sh save DASHBOARD-ADMIN-USERNAME \" d a s h b o
2026-07-29 11:56:08 +02:00
"after_installScriptTemplateMaster" : "#!/bin/bash\nset -e\n\naz login --identity --client-id ${masterIdentityClientId} --allow-no-subscriptions > /dev/null\n\n# Generate URLs\nDOMAIN=$(az keyvault secret show --vault-name ${keyVaultName} --name DOMAIN-NAME --query value -o tsv)\nOPENVIDU_URL=\"https://${DOMAIN}/\"\nLIVEKIT_URL=\"wss://${DOMAIN}/\"\nDASHBOARD_URL=\"https://${DOMAIN}/dashboard/\"\nGRAFANA_URL=\"https://${DOMAIN}/grafana/\"\nMINIO_URL=\"https://${DOMAIN}/minio-console/\"\n\n# Update shared secret\naz keyvault secret set --vault-name ${keyVaultName} --name DOMAIN-NAME --value $DOMAIN\naz keyvault secret set --vault-name ${keyVaultName} --name OPENVIDU-URL --value $OPENVIDU_URL\naz keyvault secret set --vault-name ${keyVaultName} --name LIVEKIT-URL --value $LIVEKIT_URL\naz keyvault secret set --vault-name ${keyVaultName} --name DASHBOARD-URL --value $DASHBOARD_URL\naz keyvault secret set --vault-name ${keyVaultName} --name GRAFANA-URL --value $GRAFANA_URL\naz keyvault secret set --vault-name ${keyVaultName} --name MINIO-URL --value $MINIO_URL\n\naz keyvault secret show --vault-name ${keyVaultName} --name MINIO-URL\n\nif [[ $? -ne 0 ]]; then\n echo \"Error updating keyvault\"\nfi\n" ,
"update_config_from_secretScriptTemplateMaster" : "#!/bin/bash\nset -e\n\naz login --identity --client-id ${masterIdentityClientId} --allow-no-subscriptions > /dev/null\n\n# Installation directory\nINSTALL_DIR=\"/opt/openvidu\"\nCLUSTER_CONFIG_DIR=\"${INSTALL_DIR}/config/cluster\"\nMASTER_NODE_CONFIG_DIR=\"${INSTALL_DIR}/config/node\"\n\n# Replace DOMAIN_NAME\nexport DOMAIN=$(az keyvault secret show --vault-name ${keyVaultName} --name DOMAIN-NAME --query value -o tsv)\nif [[ -n \"$DOMAIN\" ]]; then\n sed -i \"s/DOMAIN_NAME=.*/DOMAIN_NAME=$DOMAIN/\" \"${CLUSTER_CONFIG_DIR}/openvidu.env\"\nelse\n exit 1\nfi\n\n# Get the rest of the values\nexport REDIS_PASSWORD=$(az keyvault secret show --vault-name ${keyVaultName} --name REDIS-PASSWORD --query value -o tsv)\nexport OPENVIDU_RTC_ENGINE=$(az keyvault secret show --vault-name ${keyVaultName} --name OPENVIDU-RTC-ENGINE --query value -o tsv)\nexport OPENVIDU_PRO_LICENSE=$(az keyvault secret show --vault-name ${keyVaultName} --name OPENVIDU-PRO-LICENSE --query value -o tsv)\nexport MONGO_ADMIN_USERNAME=$(az keyvault secret show --vault-name ${keyVaultName} --name MONGO-ADMIN-USERNAME --query value -o tsv)\nexport MONGO_ADMIN_PASSWORD=$(az keyvault secret show --vault-name ${keyVaultName} --name MONGO-ADMIN-PASSWORD --query value -o tsv)\nexport MONGO_REPLICA_SET_KEY=$(az keyvault secret show --vault-name ${keyVaultName} --name MONGO-REPLICA-SET-KEY --query value -o tsv)\nexport DASHBOARD_ADMIN_USERNAME=$(az keyvault secret show --vault-name ${keyVaultName} --name DASHBOARD-ADMIN-USERNAME --query value -o tsv)\nexport DASHBOARD_ADMIN_PASSWORD=$(az keyvault secret show --vault-name ${keyVaultName} --name DASHBOARD-ADMIN-PASSWORD --query value -o tsv)\nexport MINIO_ACCESS_KEY=$(az keyvault secret show --vault-name ${keyVaultName} --name MINIO-ACCESS-KEY --query value -o tsv)\nexport MINIO_SECRET_KEY=$(az keyvault secret show --vault-name ${keyVaultName} --name MINIO-SECRET-KEY --query value -o tsv)\nexport GRAFANA_ADMIN_USERNAME=$(az keyvault secret show --vault-name ${keyVaultName} --name GRAFANA-ADMIN-USERNAME --query value -o tsv)\nexport GRAFANA_ADMIN_PASSWORD=$(az keyvault secret show --vault-name ${keyVaultName} --name GRAFANA-ADMIN-PASSWORD --query value -o tsv)\nexport LIVEKIT_API_KEY=$(az keyvault secret show --vault-name ${keyVaultName} --name LIVEKIT-API-KEY --query value -o tsv)\nexport LIVEKIT_API_SECRET=$(az keyvault secret show --vault-name ${keyVaultName} --name LIVEKIT-API-SECRET --query value -o tsv)\nexport MEET_INITIAL_ADMIN_USER=$(az keyvault secret show --vault-name ${keyVaultName} --name MEET-INITIAL-ADMIN-USER --query value -o tsv)\nexport MEET_INITIAL_ADMIN_PASSWORD=$(az keyvault secret show --vault-name ${keyVaultName} --name MEET-INITIAL-ADMIN-PASSWORD --query value -o tsv)\nif [[ \"${initialMeetApiKey}\" != '' ]]; then\n export MEET_INITIAL_API_KEY=$(az keyvault secret show --vault-name ${keyVaultName} --name MEET-INITIAL-API-KEY --query value -o tsv)\nfi\nexport ENABLED_MODULES=$(az keyvault secret show --vault-name ${keyVaultName} --name ENABLED-MODULES --query value -o tsv)\n\n# Replace rest of the values\nsed -i \"s/REDIS_PASSWORD=.*/REDIS_PASSWORD=$REDIS_PASSWORD/\" \"${MASTER_NODE_CONFIG_DIR}/master_node.env\"\nsed -i \"s/OPENVIDU_RTC_ENGINE=.*/OPENVIDU_RTC_ENGINE=$OPENVIDU_RTC_ENGINE/\" \"${CLUSTER_CONFIG_DIR}/openvidu.env\"\nsed -i \"s/OPENVIDU_PRO_LICENSE=.*/OPENVIDU_PRO_LICENSE=$OPENVIDU_PRO_LICENSE/\" \"${CLUSTER_CONFIG_DIR}/openvidu.env\"\nsed -i \"s/MONGO_ADMIN_USERNAME=.*/MONGO_ADMIN_USERNAME=$MONGO_ADMIN_USERNAME/\" \"${CLUSTER_CONFIG_DIR}/openvidu.env\"\nsed -i \"s/MONGO_ADMIN_PASSWORD=.*/MONGO_ADMIN_PASSWORD=$MONGO_ADMIN_PASSWORD/\" \"${CLUSTER_CONFIG_DIR}/openvidu.env\"\nsed -i \"s/MONGO_REPLICA_SET_KEY=.*/MONGO_REPLICA_SET_KEY=$MONGO_REPLICA_SET_KEY/\" \"${CLUSTER_CONFIG_DIR}/openvidu.env\"\nsed -i \"s/DASHBOARD_ADMIN_USERNAME=.*/DASHBOARD_ADMIN_USERNAME=$DASHBOARD_ADMIN_USERNAME/\" \"${CLUSTER_CONFIG_DIR}/openvidu.env\"\nsed -i \"s/DASHBOARD_ADMIN_PASSWORD=.*/DASHBOARD_ADMIN_PASSWORD=$DASHBOARD_ADMIN_PASSWORD/\" \" $ { C L U S T E
"update_secret_from_configScriptTemplateMaster" : "#!/bin/bash\nset -e\n\naz login --identity --client-id ${masterIdentityClientId} --allow-no-subscriptions > /dev/null\n\n# Installation directory\nINSTALL_DIR=\"/opt/openvidu\"\nCLUSTER_CONFIG_DIR=\"${INSTALL_DIR}/config/cluster\"\nMASTER_NODE_CONFIG_DIR=\"${INSTALL_DIR}/config/node\"\n\n# Get current values of the config\nREDIS_PASSWORD=\"$(/usr/local/bin/get_value_from_config.sh REDIS_PASSWORD \"${MASTER_NODE_CONFIG_DIR}/master_node.env\")\"\nDOMAIN_NAME=\"$(/usr/local/bin/get_value_from_config.sh DOMAIN_NAME \"${CLUSTER_CONFIG_DIR}/openvidu.env\")\"\nOPENVIDU_RTC_ENGINE=\"$(/usr/local/bin/get_value_from_config.sh OPENVIDU_RTC_ENGINE \"${CLUSTER_CONFIG_DIR}/openvidu.env\")\"\nOPENVIDU_PRO_LICENSE=\"$(/usr/local/bin/get_value_from_config.sh OPENVIDU_PRO_LICENSE \"${CLUSTER_CONFIG_DIR}/openvidu.env\")\"\nMONGO_ADMIN_USERNAME=\"$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_USERNAME \"${CLUSTER_CONFIG_DIR}/openvidu.env\")\"\nMONGO_ADMIN_PASSWORD=\"$(/usr/local/bin/get_value_from_config.sh MONGO_ADMIN_PASSWORD \"${CLUSTER_CONFIG_DIR}/openvidu.env\")\"\nMONGO_REPLICA_SET_KEY=\"$(/usr/local/bin/get_value_from_config.sh MONGO_REPLICA_SET_KEY \"${CLUSTER_CONFIG_DIR}/openvidu.env\")\"\nMINIO_ACCESS_KEY=\"$(/usr/local/bin/get_value_from_config.sh MINIO_ACCESS_KEY \"${CLUSTER_CONFIG_DIR}/openvidu.env\")\"\nMINIO_SECRET_KEY=\"$(/usr/local/bin/get_value_from_config.sh MINIO_SECRET_KEY \"${CLUSTER_CONFIG_DIR}/openvidu.env\")\"\nDASHBOARD_ADMIN_USERNAME=\"$(/usr/local/bin/get_value_from_config.sh DASHBOARD_ADMIN_USERNAME \"${CLUSTER_CONFIG_DIR}/openvidu.env\")\"\nDASHBOARD_ADMIN_PASSWORD=\"$(/usr/local/bin/get_value_from_config.sh DASHBOARD_ADMIN_PASSWORD \"${CLUSTER_CONFIG_DIR}/openvidu.env\")\"\nGRAFANA_ADMIN_USERNAME=\"$(/usr/local/bin/get_value_from_config.sh GRAFANA_ADMIN_USERNAME \"${CLUSTER_CONFIG_DIR}/openvidu.env\")\"\nGRAFANA_ADMIN_PASSWORD=\"$(/usr/local/bin/get_value_from_config.sh GRAFANA_ADMIN_PASSWORD \"${CLUSTER_CONFIG_DIR}/openvidu.env\")\"\nLIVEKIT_API_KEY=\"$(/usr/local/bin/get_value_from_config.sh LIVEKIT_API_KEY \"${CLUSTER_CONFIG_DIR}/openvidu.env\")\"\nLIVEKIT_API_SECRET=\"$(/usr/local/bin/get_value_from_config.sh LIVEKIT_API_SECRET \"${CLUSTER_CONFIG_DIR}/openvidu.env\")\"\nMEET_INITIAL_ADMIN_USER=\"$(/usr/local/bin/get_value_from_config.sh MEET_INITIAL_ADMIN_USER \"${CLUSTER_CONFIG_DIR}/master_node/meet.env\")\"\nMEET_INITIAL_ADMIN_PASSWORD=\"$(/usr/local/bin/get_value_from_config.sh MEET_INITIAL_ADMIN_PASSWORD \"${CLUSTER_CONFIG_DIR}/master_node/meet.env\")\"\nif [[ \"${initialMeetApiKey}\" != '' ]]; then\n MEET_INITIAL_API_KEY=\"$(/usr/local/bin/get_value_from_config.sh MEET_INITIAL_API_KEY \"${CLUSTER_CONFIG_DIR}/master_node/meet.env\")\"\nfi\nENABLED_MODULES=\"$(/usr/local/bin/get_value_from_config.sh ENABLED_MODULES \"${CLUSTER_CONFIG_DIR}/openvidu.env\")\" \ n \ n # U p d a t e s h a r e d s e c r e t \ n a z k e y v a u l t s e c r e t s e t - - v a u l t - n a m e $ { k e y V a u l t N a m e } - - n a m e R E D I S - P A S S W O R D - - v a l u e $ R E D I S _ P A S S W O R D \ n a z k e y v a u l t s e c r e t s e t - - v a u l t - n a m e $ { k e y V a u l t N a m e } - - n a m e D O M A I N - N A M E - - v a l u e $ D O M A I N _ N A M E \ n a z k e y v a u l t s e c r e t s e t - - v a u l t - n a m e $ { k e y V a u l t N a m e } - - n a m e O P E N V I D U - R T C - E N G I N E - - v a l u e $ O P E N V I D U _ R T C _ E N G I N E \ n a z k e y v a u l t s e c r e t s e t - - v a u l t - n a m e $ { k e y V a u l t N a m e } - - n a m e O P E N V I D U - P R O - L I C E N S E - - v a l u e $ O P E N V I D U _ P R O _ L I C E N S E \ n a z k e y v a u l t s e c r e t s e t - - v a u l t - n a m e $ { k e y V a u l t N a m e } - - n a m e M O N G O - A D M I N - U S E R N A M E - - v a l u e $ M O N G O _ A D M I N _ U S E R N A M E \ n a z k e y v a u l t s e c r e t s e t - - v a u l t - n a m e $ { k e y V a u l t N a m e } - - n a m e M O N G O - A D M I N - P A S S W O R D - - v a l u e $ M O N G O _ A D M I N _ P A S S W O R D \ n a z k e y v a u l t s e c r e t s e t - - v a u l t - n a m e $ { k e y V a u l t N a m e } - - n a m e M O N G O - R E P L I C A - S E T - K E Y - - v a l u e $ M O N G O _ R E P L I C A _ S E T _ K E Y \ n a z k e y v a u l t s e c r e t s e t - - v a u l t - n a m e $ { k e y V a u l t N a m e } - - n a m e M I N I O - A C C E S S - K E Y - - v a l u e $ M I N I O _ A C C E S S _ K E Y \ n a z k e y v a u l t s e c r e t s e t - - v a u l t - n a m e $ { k e y V a u l t N a m e } - - n a m e M I N I O - S E C R E T - K E Y - - v a l u e $ M I N I O _ S E C R E T _ K E Y \ n a z k e y v a u l t s e c r e t s e t - - v a u l t - n a m e $ { k e y V a u l t N a m e } - - n a m e D A S H B O A R D - A D M I N - U S E R N A M E - - v a l u e $ D A S H B O A R D _ A D M I N _ U S E R N A M E \ n a z k e y v a u l t s e c r e t s e t - - v a u l t - n a m e $ { k e y V a u l t N a m e } - - n a m e D A S H B O A R D - A D M I N - P A S S W O R D - - v a l u e $ D A S H B O A R D _ A D M I N _
2025-04-22 11:46:24 +02:00
"get_value_from_configScriptMaster" : "#!/bin/bash\nset -e\n\n# Function to get the value of a given key from the environment file\nget_value() {\n local key=\"$1\"\n local file_path=\"$2\"\n\n # Use grep to find the line with the key, ignoring lines starting with #\n # Use awk to split on '=' and print the second field, which is the value\n local value=$(grep -E \"^\\s*$key\\s*=\" \"$file_path\" | awk -F= '{print $2}' | sed 's/#.*//; s/^\\s*//; s/\\s*$//')\n\n # If the value is empty, return \"none\"\n if [ -z \"$value\" ]; then\n echo \"none\"\n else\n echo \"$value\"\n fi\n}\n\n# Check if the correct number of arguments are supplied\nif [ \"$#\" -ne 2 ]; then\n echo \"Usage: $0 <key> <file_path>\"\n exit 1\nfi\n\n# Get the key and file path from the arguments\nkey=\"$1\"\nfile_path=\"$2\"\n\n# Get and print the value\nget_value \"$key\" \"$file_path\"\n" ,
2026-07-29 11:56:08 +02:00
"store_secretScriptTemplateMaster" : "#!/bin/bash\nset -e\n\naz login --identity --client-id ${masterIdentityClientId} --allow-no-subscriptions > /dev/null\n\n# Modes: save, generate\n# save mode: save the secret in the secret manager\n# generate mode: generate a random password and save it in the secret manager\nMODE=\"$1\"\n\nif [[ \"$MODE\" == \"generate\" ]]; then\n SECRET_KEY_NAME=\"$2\"\n PREFIX=\"${3:-}\"\n LENGTH=\"${4:-44}\"\n RANDOM_PASSWORD=\"$(openssl rand -base64 64 | tr -d '+/=\\n' | cut -c -${LENGTH})\"\n RANDOM_PASSWORD=\"${PREFIX}${RANDOM_PASSWORD}\"\n az keyvault secret set --vault-name ${keyVaultName} --name $SECRET_KEY_NAME --value $RANDOM_PASSWORD > /dev/null\n if [[ $? -ne 0 ]]; then\n echo \"Error generating secret\"\n fi\n echo \"$RANDOM_PASSWORD\"\nelif [[ \"$MODE\" == \"save\" ]]; then\n SECRET_KEY_NAME=\"$2\"\n SECRET_VALUE=\"$3\"\n # If empty value, store it empty\n if [[ -z \"$SECRET_VALUE\" ]]; then\n az keyvault secret set --vault-name ${keyVaultName} --name $SECRET_KEY_NAME --file /dev/null > /dev/null\n else\n az keyvault secret set --vault-name ${keyVaultName} --name $SECRET_KEY_NAME --value $SECRET_VALUE > /dev/null\n fi\n if [[ $? -ne 0 ]]; then\n echo \"Error generating secret\"\n fi\n echo \"$SECRET_VALUE\"\nelse\n exit 1\nfi\n" ,
"get_public_ip" : "#!/bin/bash\naz login --identity --client-id ${masterIdentityClientId} --allow-no-subscriptions > /dev/null\n\naz network public-ip show \\\n --id ${publicIPId} \\\n --query \"ipAddress\" -o tsv\n" ,
2026-07-08 17:32:27 +02:00
"check_app_readyScriptMaster" : "#!/bin/bash\nset -e\nMAX_WAIT=1200\nWAIT_INTERVAL=5\nELAPSED_TIME=0\nwhile true; do\n HTTP_STATUS=$(curl -Ik http://localhost:7880/health/caddy | head -n1 | awk '{print $2}')\n if [ \"$HTTP_STATUS\" = \"200\" ]; then\n break\n fi\n ELAPSED_TIME=$((ELAPSED_TIME + WAIT_INTERVAL))\n if [ $ELAPSED_TIME -ge $MAX_WAIT ]; then\n exit 1\n fi\n sleep 5\ndone\n" ,
2025-04-22 11:46:24 +02:00
"restartScriptMaster" : "#!/bin/bash\nset -e\n# Stop all services\nsystemctl stop openvidu\n\n# Update config from secret\n/usr/local/bin/update_config_from_secret.sh\n\n# Start all services\nsystemctl start openvidu\n" ,
2026-07-29 11:56:08 +02:00
"config_blobStorageTemplate" : "#!/bin/bash\nset -e\n\n# Install dir and config dir\nINSTALL_DIR=\"/opt/openvidu\"\nCLUSTER_CONFIG_DIR=\"${INSTALL_DIR}/config/cluster\"\n\n# Retry login + storage key fetch to allow the Contributor role assignment to propagate\nMAX_WAIT=300\nWAIT_INTERVAL=1\nELAPSED_TIME=0\nset +e\nwhile true; do\n az login --identity --client-id ${masterIdentityClientId}\n\n # Config azure blob storage\n AZURE_ACCOUNT_NAME=\"${storageAccountName}\"\n AZURE_ACCOUNT_KEY=$(az storage account keys list --account-name ${storageAccountName} --query '[0].value' -o tsv)\n\n # If the key was fetched successfully, exit the loop\n if [ $? -eq 0 ]; then\n break\n fi\n\n # If not, wait and check again incrementing the time\n ELAPSED_TIME=$((ELAPSED_TIME + WAIT_INTERVAL))\n\n # If exceeded the maximum time, exit with error\n if [ $ELAPSED_TIME -ge $MAX_WAIT ]; then\n exit 1\n fi\n\n # Esperar antes de la próxima comprobación\n sleep $WAIT_INTERVAL\ndone\nset -e\nAZURE_CONTAINER_NAME=\"${storageAccountContainerName}\"\n\nsed -i \"s|AZURE_ACCOUNT_NAME=.*|AZURE_ACCOUNT_NAME=$AZURE_ACCOUNT_NAME|\" \"${CLUSTER_CONFIG_DIR}/openvidu.env\"\nsed -i \"s|AZURE_ACCOUNT_KEY=.*|AZURE_ACCOUNT_KEY=$AZURE_ACCOUNT_KEY|\" \"${CLUSTER_CONFIG_DIR}/openvidu.env\"\nsed -i \"s|AZURE_CONTAINER_NAME=.*|AZURE_CONTAINER_NAME=$AZURE_CONTAINER_NAME|\" \"${CLUSTER_CONFIG_DIR}/openvidu.env\"\n" ,
2025-04-22 11:46:24 +02:00
"base64get_value_from_configMaster" : "[base64(variables('get_value_from_configScriptMaster'))]" ,
"base64check_app_readyMaster" : "[base64(variables('check_app_readyScriptMaster'))]" ,
"base64restartMaster" : "[base64(variables('restartScriptMaster'))]" ,
2026-09-07 16:58:47 +02:00
"userDataTemplateMasterNode" : "#!/bin/bash -x\nset -eu -o pipefail\n\n# Introduce the scripts in the instance\n# install.sh\necho ${base64install} | base64 -d > /usr/local/bin/install.sh\nchmod +x /usr/local/bin/install.sh\n\n# after_install.sh\necho ${base64after_install} | base64 -d > /usr/local/bin/after_install.sh\nchmod +x /usr/local/bin/after_install.sh\n\n# get_public_ip.sh\necho ${base64get_public_ip} | base64 -d > /usr/local/bin/get_public_ip.sh\nchmod +x /usr/local/bin/get_public_ip.sh\n\n# update_config_from_secret.sh\necho ${base64update_config_from_secret} | base64 -d > /usr/local/bin/update_config_from_secret.sh\nchmod +x /usr/local/bin/update_config_from_secret.sh\n\n# update_secret_from_config.sh\necho ${base64update_secret_from_config} | base64 -d > /usr/local/bin/update_secret_from_config.sh\nchmod +x /usr/local/bin/update_secret_from_config.sh\n\n# get_value_from_config.sh\necho ${base64get_value_from_config} | base64 -d > /usr/local/bin/get_value_from_config.sh\nchmod +x /usr/local/bin/get_value_from_config.sh\n\n# store_secret.sh\necho ${base64store_secret} | base64 -d > /usr/local/bin/store_secret.sh\nchmod +x /usr/local/bin/store_secret.sh\n\n# check_app_ready.sh\necho ${base64check_app_ready} | base64 -d > /usr/local/bin/check_app_ready.sh\nchmod +x /usr/local/bin/check_app_ready.sh\n\n# restart.sh\necho ${base64restart} | base64 -d > /usr/local/bin/restart.sh\nchmod +x /usr/local/bin/restart.sh\n\necho ${base64config_blobStorage} | base64 -d > /usr/local/bin/config_blobStorage.sh\nchmod +x /usr/local/bin/config_blobStorage.sh\n\n# Install azure cli\nAZURE_CLI_VERSION=2.90.0\napt-get install -y apt-transport-https ca-certificates gnupg lsb-release\ncurl -sLS https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor -o /etc/apt/trusted.gpg.d/microsoft.gpg\necho \"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/trusted.gpg.d/microsoft.gpg] https://packages.microsoft.com/repos/azure-cli/ $(lsb_release -cs) main\" | tee /etc/apt/sources.list.d/azure-cli.list\napt-get update\napt-get install -y azure-cli=${AZURE_CLI_VERSION}-1~$(lsb_release -cs)\n\naz login --identity --client-id ${masterIdentityClientId} --allow-no-subscriptions\n\necho \"DPkg::Lock::Timeout \\\"-1\\\";\" > /etc/apt/apt.conf.d/99timeout\n\nexport HOME=\"/root\"\n\n# Install OpenVidu\n/usr/local/bin/install.sh || { echo \"[OpenVidu] error installing OpenVidu\"; exit 1; }\n\n#Config blob storage\n/usr/local/bin/config_blobStorage.sh || { echo \"[OpenVidu] error configuring Blob Storage\"; exit 1; }\n\n# Start OpenVidu\nsystemctl start openvidu || { echo \"[OpenVidu] error starting OpenVidu\"; exit 1; }\n\n# Update shared secret\n/usr/local/bin/after_install.sh || { echo \"[OpenVidu] error updating shared secret\"; exit 1; }\n\n# Launch on reboot\necho \"@reboot /usr/local/bin/restart.sh >> /var/log/openvidu-restart.log\" 2>&1 | crontab\n\n# check_app_ready.sh internally caps its wait at 1200s\n/usr/local/bin/check_app_ready.sh || { echo \"[OpenVidu] master node did not become healthy\"; exit 1; }\n\naz keyvault secret set --vault-name ${keyVaultName} --name FINISH-MASTER-NODE --value \"true\"\n\nMASTER_NODE_NUM=${masterNodeNum}\nif [[ $MASTER_NODE_NUM -eq 4 ]]; then\n # Creating scale in lock\n set +e\n az storage blob upload --account-name ${storageAccountName} --container-name automation-locks --name lock.txt --file /dev/null --auth-mode key\n set -e\nfi\n" ,
2026-10-23 14:57:54 +02:00
"installScriptTemplateMedia" : "#!/bin/bash -x\nDOMAIN=\n\n# Install dependencies\necho \"DPkg::Lock::Timeout \\\"-1\\\";\" > /etc/apt/apt.conf.d/99timeout\n\napt-get update && apt-get install -y \\\n curl \\\n unzip \\\n jq \\\n wget\n\n# Get own private IP\nPRIVATE_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\")\n\n# Gate 1: wait for master secrets and IPs before installing\nWAIT_INTERVAL=5\nMAX_RETRIES=360\nRETRIES=0\nset +e\nwhile true; do\n ALL_SECRETS_GENERATED=$(az keyvault secret show --vault-name ${keyVaultName} --name ALL-SECRETS-GENERATED --query value -o tsv 2>/dev/null)\n MASTER_NODE_1_PRIVATE_IP=$(az keyvault secret show --vault-name ${keyVaultName} --name MASTER-NODE-1-PRIVATE-IP --query value -o tsv 2>/dev/null)\n MASTER_NODE_2_PRIVATE_IP=$(az keyvault secret show --vault-name ${keyVaultName} --name MASTER-NODE-2-PRIVATE-IP --query value -o tsv 2>/dev/null)\n MASTER_NODE_3_PRIVATE_IP=$(az keyvault secret show --vault-name ${keyVaultName} --name MASTER-NODE-3-PRIVATE-IP --query value -o tsv 2>/dev/null)\n MASTER_NODE_4_PRIVATE_IP=$(az keyvault secret show --vault-name ${keyVaultName} --name MASTER-NODE-4-PRIVATE-IP --query value -o tsv 2>/dev/null)\n\n if [ \"$ALL_SECRETS_GENERATED\" == \"true\" ] &&\n [ \"$MASTER_NODE_1_PRIVATE_IP\" != \"\" ] &&\n [ \"$MASTER_NODE_2_PRIVATE_IP\" != \"\" ] &&\n [ \"$MASTER_NODE_3_PRIVATE_IP\" != \"\" ] &&\n [ \"$MASTER_NODE_4_PRIVATE_IP\" != \"\" ]; then\n break\n fi\n\n RETRIES=$((RETRIES + 1))\n if [ $RETRIES -ge $MAX_RETRIES ]; then\n echo \"[OpenVidu] timed out after 30 min waiting for master nodes to generate secrets and publish their IPs\"\n exit 1\n fi\n\n sleep $WAIT_INTERVAL\ndone\nset -e\n\nMASTER_NODE_1_PRIVATE_IP=$(az keyvault secret show --vault-name ${keyVaultName} --name MASTER-NODE-1-PRIVATE-IP --query value -o tsv)\nMASTER_NODE_2_PRIVATE_IP=$(az keyvault secret show --vault-name ${keyVaultName} --name MASTER-NODE-2-PRIVATE-IP --query value -o tsv)\nMASTER_NODE_3_PRIVATE_IP=$(az keyvault secret show --vault-name ${keyVaultName} --name MASTER-NODE-3-PRIVATE-IP --query value -o tsv)\nMASTER_NODE_4_PRIVATE_IP=$(az keyvault secret show --vault-name ${keyVaultName} --name MASTER-NODE-4-PRIVATE-IP --query value -o tsv)\nMASTER_NODE_PRIVATE_IP_LIST=\"$MASTER_NODE_1_PRIVATE_IP,$MASTER_NODE_2_PRIVATE_IP,$MASTER_NODE_3_PRIVATE_IP,$MASTER_NODE_4_PRIVATE_IP\"\nREDIS_PASSWORD=$(az keyvault secret show --vault-name ${keyVaultName} --name REDIS-PASSWORD --query value -o tsv)\nENABLED_MODULES=$(az keyvault secret show --vault-name ${keyVaultName} --name ENABLED-MODULES --query value -o tsv)\nOPENVIDU_VERSION=$(az keyvault secret show --vault-name ${keyVaultName} --name OPENVIDU-VERSION --query value -o tsv)\n\n# Download first: sh <(curl ...) would silently run an empty script on a transient curl failure\nINSTALLER_SCRIPT=\"/tmp/install_ov_media_node.sh\"\ncurl -fsSL --retry 8 --retry-all-errors --retry-delay 5 -o \"$INSTALLER_SCRIPT\" \"https://get.openvidu.io/pro/ha/$OPENVIDU_VERSION/install_ov_media_node.sh\"\nif [ ! -s \"$INSTALLER_SCRIPT\" ]; then\n echo \"[OpenVidu] failed to download the media node installer script\"\n exit 1\nfi\n\n# Base command\nINSTALL_COMMAND=\"sh $INSTALLER_SCRIPT\"\n\n# Common arguments\nCOMMON_ARGS=(\n\"--no-tty\"\n\"--install\"\n\"--environment=azure\"\n\"--deployment-type='ha'\"\n\"--node-role='media-node'\"\n\"--master-node-private-ip-list=$MASTER_NODE_PRIVATE_IP_LIST\"\n\"--private-ip=$PRIVATE_IP\"\n\"--enabled-modules='$ENABLED_MODULES'\"\n\"--redis-password=$REDIS_PASSWORD\"\n)\n\n# Construct the final command with all arguments\nFINAL_COMMAND=\"$INSTALL_COMMAND $(printf \"%s \" \"${COMMON_ARGS[@]}\")\"\n\n# Install OpenVidu\nexec bash -c \"$FINAL_COMMAND\"\n" ,
openvidu-deployment: replace unhealthy Media Nodes (AWS, GCP, Azure elastic and HA)
Every Media Node runs openvidu-media-health, a watchdog that asks the cloud to
replace it with a new node (keeping the Media Node count) when its bootstrap
failed or never finished, or when LiveKit fails its health check on :7880 for
5 minutes (down) or 10 minutes (errors or timeouts; not counted while the CPU is
saturated). Nothing is replaced while no master answers a Redis PING, so a
master outage cannot replace the whole media fleet. Drain scripts pause the
watchdog, and skip waiting for sessions on a node being replaced.
- AWS: the node marks itself Unhealthy in its Auto Scaling group (the
lifecycle hook drains it and a new instance is launched). aws-cli is
installed before the watchdog starts; set_as_unhealthy.sh is gone.
- GCP: the node asks its managed instance group to recreate it. Media
autohealing (TCP 7880) now waits 3600 s before judging a new node and needs
5 minutes of failures; HA gets the same autohealing plus the firewall rule
for the health check ranges.
- Azure: the node reimages itself (new OS disk, bootstrap runs again, same
instance, scale-in protection kept) instead of deleting itself, which shrank
the scale set. The .json twins are rebuilt from the .bicep.
2026-09-30 17:30:23 +02:00
"stop_media_nodesScriptMediaTemplate" : "#!/bin/bash\nset -e\n\n# Pause the health watchdog before draining so it never reimages a node that is being\n# drained for scale-in (synchronous; the drain runs outside the watchdog's cgroup).\ntouch /run/openvidu-media-health.paused\nsystemctl stop openvidu-media-health 2>/dev/null || true\n\nif ! (set -o noclobber ; echo > /tmp/global.lock) ; then\n exit 1 # the global.lock already exists\nfi\n\n# Execute if docker is installed\nif [ -x \"$(command -v docker)\" ]; then\n\n echo \"Stopping media node services and waiting for termination...\"\n docker container kill --signal=SIGQUIT openvidu 2>/dev/null || true\n docker container kill --signal=SIGQUIT ingress 2>/dev/null || true\n docker container kill --signal=SIGQUIT egress 2>/dev/null || true\n for agent_container in $(docker ps --filter \"label=openvidu-agent=true\" --format '{{.Names}}' 2>/dev/null); do\n docker container kill --signal=SIGQUIT \"$agent_container\" 2>/dev/null || true\n done\n\n # If this node is being replaced because it is unhealthy, do not wait for sessions to end.\n if [ -f /run/openvidu-unhealthy ]; then\n docker ps -q | xargs -r docker kill >/dev/null 2>&1 || true\n fi\n\n # Wait for running containers to not be openvidu, ingress or egress\n while [ \"$(docker ps --filter 'label=openvidu-agent=true' -q 2>/dev/null | wc -l)\" -gt 0 ] || \\\n [ \"$(docker inspect -f '{{.State.Running}}' openvidu 2>/dev/null)\" = \"true\" ] || \\\n [ \"$(docker inspect -f '{{.State.Running}}' ingress 2>/dev/null)\" = \"true\" ] || \\\n [ \"$(docker inspect -f '{{.State.Running}}' egress 2>/dev/null)\" = \"true\" ]; do\n echo \"Waiting for containers to stop...\"\n sleep 5\n done\nfi\n\naz login --identity --client-id ${mediaIdentityClientId}\n\nRESOURCE_GROUP_NAME=${resourceGroupName}\nVM_SCALE_SET_NAME=${vmScaleSetName}\nSUBSCRIPTION_ID=${subscriptionId}\nBEFORE_INSTANCE_ID=$(curl -H Metadata:true --noproxy \"*\" \"http://169.254.169.254/metadata/instance?api-version=2021-02-01\" | jq -r '.compute.resourceId')\nINSTANCE_ID=$(echo $BEFORE_INSTANCE_ID | awk -F'/' '{print $NF}')\nRESOURCE_ID=/subscriptions/$SUBSCRIPTION_ID/resourcegroups/$RESOURCE_GROUP_NAME/providers/Microsoft.Compute/virtualMachineScaleSets/$VM_SCALE_SET_NAME\n\nTIMESTAMP=$(date -u +\"%Y-%m-%dT%H:%M:%SZ\")\naz tag update --resource-id $RESOURCE_ID --operation replace --tags \"STATUS\"=\"HEALTHY\" \"InstanceDeleteTime\"=\"$TIMESTAMP\" \"storageAccount\"=\"${storageAccountName}\"\n\naz vmss delete-instances --resource-group $RESOURCE_GROUP_NAME --name $VM_SCALE_SET_NAME --instance-ids $INSTANCE_ID\n" ,
2026-10-06 20:47:00 +02:00
"openviduMediaHealthScript" : "#!/bin/bash\n# OpenVidu Media Node health watchdog.\n#\n# Asks the cloud to replace this Media Node with a new one, keeping the number of\n# Media Nodes, through /usr/local/bin/openvidu-media-replace.sh, when:\n# - its bootstrap failed (the user-data wrote the bootstrap-failed marker): after a\n# cool-down, so a persistent failure does not churn nodes and can be inspected,\n# - its bootstrap did not finish BOOTSTRAP_DEADLINE_SEC after boot,\n# - LiveKit fails its health check (HTTP 200 on PROBE_URL): for HARD_UNHEALTHY_AFTER_SEC\n# while it is down (connection refused or closed), or for SOFT_UNHEALTHY_AFTER_SEC\n# while it answers an error or times out. Soft failures are not counted while the CPU\n# is saturated (an overloaded LiveKit reports Not Ready), and no failure is counted in\n# the WARMUP_SEC after openvidu.service (re)started.\n# Nothing is replaced while no master answers a Redis PING (MASTER_ADDRS): a master outage\n# fails the health check of every Media Node at once, and a new node could not bootstrap\n# either; LiveKit failures are not counted until a master answers again. Once requested,\n# the replacement is requested again every REPLACE_RETRY_SEC until the node goes away.\n#\n# Settings: /etc/openvidu/media-health.env (sourced every cycle; MASTER_ADDRS=\"ip:port ...\")\n# Pause: touch /etc/openvidu/media-health.disabled (drain scripts use /run/openvidu-media-health.paused)\n# Logs: journalctl -u openvidu-media-health\n#\n# This file is embedded verbatim in CloudFormation !Sub blocks, Bicep strings and\n# Terraform heredocs, so it must never contain a dollar sign or a percent sign followed\n# by an opening brace, nor two opening braces in a row.\n\nSTATE_DIR=/var/lib/openvidu-media-health\nINSTALLED_MARKER=\"$STATE_DIR/installed\"\nBOOTSTRAP_FAILED_MARKER=\"$STATE_DIR/bootstrap-failed\"\nREPLACE_SCRIPT=/usr/local/bin/openvidu-media-replace.sh\nENV_FILE=/etc/openvidu/media-health.env\nPAUSE_FILE=/etc/openvidu/media-health.disabled\nDRAIN_PAUSE_FILE=/run/openvidu-media-health.paused\n\nlog() { echo \"[media-health] $*\"; }\n\n# Seconds since boot: monotonic, unaffected by clock steps\nuptime_sec() { cut -d. -f1 /proc/uptime; }\n\n# Busy CPU percentage since the previous call, in BUSY (0 on the first call)\ncpu_busy() {\n read -r _ c_user c_nice c_system c_idle c_iowait c_irq c_softirq c_steal _ < /proc/stat\n total=$((c_user + c_nice + c_system + c_idle + c_iowait + c_irq + c_softirq + c_steal))\n idle=$((c_idle + c_iowait))\n BUSY=0\n if [ -n \"$PREV_TOTAL\" ] && [ \"$total\" -gt \"$PREV_TOTAL\" ]; then\n BUSY=$((100 * (total - PREV_TOTAL - idle + PREV_IDLE) / (total - PREV_TOTAL)))\n fi\n PREV_TOTAL=$total\n PREV_IDLE=$idle\n}\n\n# openvidu.service is running and entered the active state less than WARMUP_SEC ago\n# (a stopped or failed openvidu.service is not starting: no warm-up for it)\nopenvidu_starting() {\n case \"$(systemctl show -p ActiveState --value openvidu 2>/dev/null)\" in\n active|activating|reloading) ;;\n *) return 1 ;;\n esac\n since=$(systemctl show -p ActiveEnterTimestampMonotonic --value openvidu 2>/dev/null)\n case \"$since\" in ''|0|*[!0-9]*) return 1 ;; esac\n [ $(($(uptime_sec) - since / 1000000)) -lt \"$WARMUP_SEC\" ]\n}\n\n# A master answers a Redis PING (+PONG, or -NOAUTH when a password is required). A bare\n# TCP connect would also succeed against a wedged Redis.\nmaster_serving() {\n [ -n \"$MASTER_ADDRS\" ] || return 1\n for addr in $MASTER_ADDRS; do\n host=$(echo \"$addr\" | cut -d: -f1)\n port=$(echo \"$addr\" | cut -d: -f2)\n reply=$(timeout 5 bash -c 'exec 3<>\"/dev/tcp/$0/$1\" && printf \"PING\\r\\n\" >&3 && head -c 7 <&3' \"$host\" \"$port\" 2>/dev/null)\n case \"$reply\" i n \ n + P O N G * | - N O A U T H * ) r e t u r n 0 ; ; \ n e s a c \ n d o n e \ n r e t u r n 1 \ n } \ n \ n # R e q u e s t t h e r e p l a c e m e n t o f t h i s n o d e ( $ 1 = r e a s o n ) . G a t e d o n a m a s t e r a n s w e r i n g , e x c e p t \ n # i n t h e b o o t s t r a p p h a s e b e f o r e t h e m a s t e r a d d r e s s e s a r e k n o w n .
openvidu-deployment: replace unhealthy Media Nodes (AWS, GCP, Azure elastic and HA)
Every Media Node runs openvidu-media-health, a watchdog that asks the cloud to
replace it with a new node (keeping the Media Node count) when its bootstrap
failed or never finished, or when LiveKit fails its health check on :7880 for
5 minutes (down) or 10 minutes (errors or timeouts; not counted while the CPU is
saturated). Nothing is replaced while no master answers a Redis PING, so a
master outage cannot replace the whole media fleet. Drain scripts pause the
watchdog, and skip waiting for sessions on a node being replaced.
- AWS: the node marks itself Unhealthy in its Auto Scaling group (the
lifecycle hook drains it and a new instance is launched). aws-cli is
installed before the watchdog starts; set_as_unhealthy.sh is gone.
- GCP: the node asks its managed instance group to recreate it. Media
autohealing (TCP 7880) now waits 3600 s before judging a new node and needs
5 minutes of failures; HA gets the same autohealing plus the firewall rule
for the health check ranges.
- Azure: the node reimages itself (new OS disk, bootstrap runs again, same
instance, scale-in protection kept) instead of deleting itself, which shrank
the scale set. The .json twins are rebuilt from the .bicep.
2026-09-30 17:30:23 +02:00
"openviduMediaHealthUnit" : "[Unit]\nDescription=OpenVidu Media Node health watchdog\nAfter=network-online.target\nWants=network-online.target\n\n[Service]\nType=simple\nExecStart=/usr/local/bin/openvidu-media-health.sh\nRestart=always\nRestartSec=30\n\n[Install]\nWantedBy=multi-user.target\n" ,
2026-10-16 13:02:11 +02:00
"media_replaceScriptMediaTemplate" : "#!/bin/bash\n# openvidu-media-replace.sh \"<reason>\"\n# Requests the reimage of THIS Media Node, keeping the scale set capacity and the\n# scale-in protection: reimage provisions a new OS disk and the userData bootstrap runs\n# again on the same instance id. Called by the health watchdog; it never signals it.\n\nmkdir -p /var/lib/openvidu-media-health\necho \"$(date -u +%FT%TZ) $1\" >> /var/lib/openvidu-media-health/replace-requests.log\ntouch /run/openvidu-unhealthy\n# Withdrawn if the request fails: the node then stays, and if it recovers\n# a later scale-in must still wait for its sessions\ntrap '[ $? -eq 0 ] || rm -f /run/openvidu-unhealthy' EXIT\n\nRESOURCE_GROUP_NAME=${resourceGroupName}\nVM_SCALE_SET_NAME=${vmScaleSetName}\nINSTANCE_RESOURCE_ID=$(curl -s -H Metadata:true --noproxy \"*\" \"http://169.254.169.254/metadata/instance?api-version=2021-02-01\" | jq -r '.compute.resourceId')\nINSTANCE_ID=$(echo \"$INSTANCE_RESOURCE_ID\" | awk -F'/' '{print $NF}')\nif [ -z \"$INSTANCE_ID\" ]; then\n echo \"[media-replace] could not read this instance id from IMDS\"\n exit 1\nfi\n\nn=0\nwhile true; do\n n=$((n + 1))\n if az login --identity --client-id ${mediaIdentityClientId} >/dev/null 2>&1 && \\\n az vmss reimage --resource-group \"$RESOURCE_GROUP_NAME\" --name \"$VM_SCALE_SET_NAME\" --instance-ids \"$INSTANCE_ID\" --no-wait; then\n echo \"[media-replace] reimage requested for instance $INSTANCE_ID ($1)\"\n exit 0\n fi\n if [ \"$n\" -ge 5 ]; then\n echo \"[media-replace] reimage could not be requested after $n attempts\"\n exit 1\n fi\n echo \"[media-replace] reimage request failed (attempt $n), retrying in 12s\"\n sleep 12\ndone\n" ,
openvidu-deployment: replace unhealthy Media Nodes (AWS, GCP, Azure elastic and HA)
Every Media Node runs openvidu-media-health, a watchdog that asks the cloud to
replace it with a new node (keeping the Media Node count) when its bootstrap
failed or never finished, or when LiveKit fails its health check on :7880 for
5 minutes (down) or 10 minutes (errors or timeouts; not counted while the CPU is
saturated). Nothing is replaced while no master answers a Redis PING, so a
master outage cannot replace the whole media fleet. Drain scripts pause the
watchdog, and skip waiting for sessions on a node being replaced.
- AWS: the node marks itself Unhealthy in its Auto Scaling group (the
lifecycle hook drains it and a new instance is launched). aws-cli is
installed before the watchdog starts; set_as_unhealthy.sh is gone.
- GCP: the node asks its managed instance group to recreate it. Media
autohealing (TCP 7880) now waits 3600 s before judging a new node and needs
5 minutes of failures; HA gets the same autohealing plus the firewall rule
for the health check ranges.
- Azure: the node reimages itself (new OS disk, bootstrap runs again, same
instance, scale-in protection kept) instead of deleting itself, which shrank
the scale set. The .json twins are rebuilt from the .bicep.
2026-09-30 17:30:23 +02:00
"userDataMediaNodeTemplate" : "#!/bin/bash -x\nset -eu -o pipefail\n\n# Mark this media node's bootstrap as failed so the health watchdog replaces it\nov_media_bootstrap_failed() {\n echo \"[OpenVidu] media node bootstrap failed: $1\"\n mkdir -p /var/lib/openvidu-media-health\n echo \"$1\" > /var/lib/openvidu-media-health/bootstrap-failed\n exit 1\n}\n\n# Introduce the scripts in the instance\n# install.sh\necho ${base64install} | base64 -d > /usr/local/bin/install.sh\nchmod +x /usr/local/bin/install.sh\n\n# stop_media_nodes.sh\necho ${base64stop} | base64 -d > /usr/local/bin/stop_media_node.sh\nchmod +x /usr/local/bin/stop_media_node.sh\n\n# openvidu-media-health.sh (health watchdog), openvidu-media-replace.sh (reimage this node) and its unit\necho ${base64mediaHealth} | base64 -d > /usr/local/bin/openvidu-media-health.sh\nchmod +x /usr/local/bin/openvidu-media-health.sh\necho ${base64mediaReplace} | base64 -d > /usr/local/bin/openvidu-media-replace.sh\nchmod +x /usr/local/bin/openvidu-media-replace.sh\necho ${base64mediaHealthUnit} | base64 -d > /etc/systemd/system/openvidu-media-health.service\n\necho \"DPkg::Lock::Timeout \\\"-1\\\";\" > /etc/apt/apt.conf.d/99timeout\n\napt-get update && apt-get install -y jq\n\n# Install azure cli\nAZURE_CLI_VERSION=2.90.0\napt-get install -y apt-transport-https ca-certificates gnupg lsb-release\ncurl -sLS https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor -o /etc/apt/trusted.gpg.d/microsoft.gpg\necho \"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/trusted.gpg.d/microsoft.gpg] https://packages.microsoft.com/repos/azure-cli/ $(lsb_release -cs) main\" | tee /etc/apt/sources.list.d/azure-cli.list\napt-get update\napt-get install -y azure-cli=${AZURE_CLI_VERSION}-1~$(lsb_release -cs)\n\naz login --identity --client-id ${mediaIdentityClientId}\n\n# Protect from scale in actions\nRESOURCE_GROUP_NAME=${resourceGroupName}\nVM_SCALE_SET_NAME=${vmScaleSetName}\nBEFORE_INSTANCE_ID=$(curl -H Metadata:true --noproxy \"*\" \"http://169.254.169.254/metadata/instance?api-version=2021-02-01\" | jq -r '.compute.resourceId')\nINSTANCE_ID=$(echo $BEFORE_INSTANCE_ID | awk -F'/' '{print $NF}')\naz vmss update --resource-group $RESOURCE_GROUP_NAME --name $VM_SCALE_SET_NAME --instance-id $INSTANCE_ID --protect-from-scale-in true\n\nexport HOME=\"/root\"\n\n# Start the Media Node health watchdog before install.sh (az CLI is installed and the node\n# is scale-in protected): it backstops a hung or failed bootstrap and, once running, reimages\n# the node if LiveKit stays unhealthy. Write MASTER_ADDRS first so its master guard is armed.\nmkdir -p /etc/openvidu\necho 'MASTER_ADDRS=\"${privateIPMasterNode1}:7001 ${privateIPMasterNode2}:7001 ${privateIPMasterNode3}:7001 ${privateIPMasterNode4}:7001\"' > /etc/openvidu/media-health.env\nsystemctl daemon-reload\nsystemctl enable --now openvidu-media-health\n\n# Install OpenVidu\n/usr/local/bin/install.sh || ov_media_bootstrap_failed \"install.sh failed\"\n\n# Gate 2: wait for master readiness before starting\nWAIT_INTERVAL=5\nMAX_RETRIES=360\nRETRIES=0\nset +e\nwhile true; do\n FINISH_MASTER_NODE=$(az keyvault secret show --vault-name ${keyVaultName} --name FINISH-MASTER-NODE --query value -o tsv 2>/dev/null)\n if [ \"$FINISH_MASTER_NODE\" == \"true\" ]; then\n break\n fi\n RETRIES=$((RETRIES + 1))\n if [ $RETRIES -ge $MAX_RETRIES ]; then\n ov_media_bootstrap_failed \"timed out after 30 min waiting for FINISH-MASTER-NODE\"\n fi\n sleep $WAIT_INTERVAL\ndone\nset -e\n\n# Start OpenVidu\nsystemctl start openvidu || ov_media_bootstrap_failed \"systemctl start openvidu failed\"\n\n# Bootstrap finished: the watchdog switches from bootstrap-deadline mode to runtime health checks\nmkdir -p /var/lib/openvidu-media-health\ntouch /var/lib/openvidu-media-health/installed\n" ,
"base64openviduMediaHealth" : "[base64(variables('openviduMediaHealthScript'))]" ,
"base64openviduMediaHealthUnit" : "[base64(variables('openviduMediaHealthUnit'))]" ,
2025-05-27 11:18:05 +02:00
"isEmptyAutomationAccountName" : "[equals(parameters('automationAccountName'), '')]" ,
2025-04-22 11:46:24 +02:00
"lbName" : "[format('{0}-loadBalancer', parameters('stackName'))]" ,
"lbFrontEndName" : "LoadBalancerFrontEnd" ,
"lbBackendPoolNameMasterNode" : "LoadBalancerBackEndMasterNode" ,
2025-05-23 13:53:24 +02:00
"ipExists" : "[equals(parameters('publicIpAddressObject').newOrExistingOrNone, 'existing')]" ,
"ipNew" : "[equals(parameters('publicIpAddressObject').newOrExistingOrNone, 'new')]" ,
2025-09-09 22:06:36 +02:00
"publicIPId" : "[if(variables('ipNew'), resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIpAddressObject').name), if(variables('ipExists'), resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIpAddressObject').name), ''))]" ,
2025-04-22 11:46:24 +02:00
"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" : "[format('{0}-virtualNetwork', parameters('stackName'))]"
2025-04-23 13:26:33 +02:00
} ,
2025-05-13 11:01:22 +02:00
"isEmptyStorageAccountName" : "[equals(parameters('storageAccountName'), '')]" ,
2026-02-02 13:12:10 +01:00
"isEmptyAppDataContainerName" : "[equals(parameters('appDataContainerName'), '')]"
2025-04-22 11:46:24 +02:00
} ,
"resources" : [
2026-07-29 11:56:08 +02:00
{
"type" : "Microsoft.ManagedIdentity/userAssignedIdentities" ,
"apiVersion" : "2023-01-31" ,
"name" : "[format('{0}-master-identity', parameters('stackName'))]" ,
"location" : "[variables('location')]"
} ,
{
"type" : "Microsoft.ManagedIdentity/userAssignedIdentities" ,
"apiVersion" : "2023-01-31" ,
"name" : "[format('{0}-media-identity', parameters('stackName'))]" ,
"location" : "[variables('location')]"
} ,
2025-04-22 11:46:24 +02:00
{
"type" : "Microsoft.KeyVault/vaults" ,
"apiVersion" : "2023-07-01" ,
"name" : "[variables('keyVaultName')]" ,
"location" : "[variables('location')]" ,
"properties" : {
"enabledForDeployment" : true ,
"enabledForDiskEncryption" : false ,
"enabledForTemplateDeployment" : true ,
"tenantId" : "[variables('tenantId')]" ,
"enableSoftDelete" : false ,
"accessPolicies" : [
{
2026-07-29 11:56:08 +02:00
"objectId" : "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-master-identity', parameters('stackName'))), '2023-01-31').principalId]" ,
2025-04-22 11:46:24 +02:00
"tenantId" : "[variables('tenantId')]" ,
"permissions" : {
"secrets" : [
"get" ,
"set" ,
"list"
]
}
} ,
{
2026-07-29 11:56:08 +02:00
"objectId" : "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-media-identity', parameters('stackName'))), '2023-01-31').principalId]" ,
2025-04-22 11:46:24 +02:00
"tenantId" : "[variables('tenantId')]" ,
"permissions" : {
"secrets" : [
"get"
]
}
} ,
{
"objectId" : "[variables('deploymentUser')]" ,
"tenantId" : "[variables('tenantId')]" ,
"permissions" : {
"secrets" : [
"get" ,
"list" ,
"set" ,
"delete" ,
"recover" ,
"backup" ,
"restore"
]
}
}
] ,
"sku" : {
"name" : "standard" ,
"family" : "A"
} ,
"networkAcls" : {
"defaultAction" : "Allow" ,
"bypass" : "AzureServices"
}
} ,
"dependsOn" : [
2026-07-29 11:56:08 +02:00
"[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-master-identity', parameters('stackName')))]" ,
"[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-media-identity', parameters('stackName')))]"
2025-04-22 11:46:24 +02:00
]
} ,
{
"type" : "Microsoft.Compute/virtualMachines" ,
"apiVersion" : "2023-09-01" ,
"name" : "[format('{0}-VM-MasterNode1', parameters('stackName'))]" ,
"location" : "[variables('location')]" ,
"identity" : {
2026-07-29 11:56:08 +02:00
"type" : "UserAssigned" ,
"userAssignedIdentities" : {
"[format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-master-identity', parameters('stackName'))))]" : { }
}
2025-04-22 11:46:24 +02:00
} ,
"properties" : {
"hardwareProfile" : {
"vmSize" : "[parameters('masterNodeInstanceType')]"
} ,
"storageProfile" : {
"osDisk" : {
"createOption" : "FromImage" ,
"managedDisk" : {
"storageAccountType" : "[variables('masterNodeVMSettings').osDiskType]"
} ,
"diskSizeGB" : "[variables('masterNodeVMSettings').osDiskSize]"
} ,
"imageReference" : "[variables('masterNodeVMSettings').ubuntuOSVersion]"
} ,
"networkProfile" : {
"networkInterfaces" : [
{
"id" : "[resourceId('Microsoft.Network/networkInterfaces', format('{0}-masterNodeNetInterface1', parameters('stackName')))]"
}
]
} ,
"osProfile" : {
"computerName" : "[format('{0}-VM-MasterNode1', parameters('stackName'))]" ,
"adminUsername" : "[parameters('adminUsername')]" ,
"linuxConfiguration" : "[variables('masterNodeVMSettings').linuxConfiguration]"
} ,
2026-07-29 11:56:08 +02:00
"userData" : " [ b a s e 64 ( r e d u c e ( i t e m s ( c r e a t e O b j e c t ( ' b a s e 64 i n s t a l l ' , b a s e 64 ( r e d u c e ( i t e m s ( c r e a t e O b j e c t ( ' p u b l i c I P I d ' , v a r i a b l e s ( ' p u b l i c I P I d ' ) , ' d o m a i n N a m e ' , p a r a m e t e r s ( ' d o m a i n N a m e ' ) , ' c e r t i f i c a t e T y p e ' , p a r a m e t e r s ( ' c e r t i f i c a t e T y p e ' ) , ' o w n P u b l i c C e r t i f i c a t e ' , p a r a m e t e r s ( ' o w n P u b l i c C e r t i f i c a t e ' ) , ' o w n P r i v a t e C e r t i f i c a t e ' , p a r a m e t e r s ( ' o w n P r i v a t e C e r t i f i c a t e ' ) , ' o p e n v i d u L i c e n s e ' , p a r a m e t e r s ( ' o p e n v i d u L i c e n s e ' ) , ' r t c E n g i n e ' , p a r a m e t e r s ( ' r t c E n g i n e ' ) , ' i n i t i a l M e e t A d m i n P a s s w o r d ' , p a r a m e t e r s ( ' i n i t i a l M e e t A d m i n P a s s w o r d ' ) , ' i n i t i a l M e e t A p i K e y ' , p a r a m e t e r s ( ' i n i t i a l M e e t A p i K e y ' ) , ' k e y V a u l t N a m e ' , v a r i a b l e s ( ' k e y V a u l t N a m e ' ) , ' m a s t e r I d e n t i t y C l i e n t I d ' , r e f e r e n c e ( r e s o u r c e I d ( ' M i c r o s o f t . M a n a g e d I d e n t i t y / u s e r A s s i g n e d I d e n t i t i e s ' , f o r m a t ( ' { 0 } - m a s t e r - i d e n t i t y ' , p a r a m e t e r s ( ' s t a c k N a m e ' ) ) ) , ' 2023 -0 1 -31 ' ) . c l i e n t I d , ' m a s t e r N o d e N u m ' , ' 1 ' , ' a d d i t i o n a l I n s t a l l F l a g s ' , p a r a m e t e r s ( ' a d d i t i o n a l I n s t a l l F l a g s ' ) ) ) , c r e a t e O b j e c t ( ' v a l u e ' , v a r i a b l e s ( ' i n s t a l l S c r i p t T e m p l a t e M a s t e r ' ) ) , l a m b d a ( ' c u r r ' , ' n e x t ' , c r e a t e O b j e c t ( ' v a l u e ' , r e p l a c e ( l a m b d a V a r i a b l e s ( ' c u r r ' ) . v a l u e , f o r m a t ( ' $ { { { 0 } } } ' , l a m b d a V a r i a b l e s ( ' n e x t ' ) . k e y ) , l a m b d a V a r i a b l e s ( ' n e x t ' ) . v a l u e ) ) ) ) . v a l u e ) , ' b a s e 64 a f t e r _ i n s t a l l ' , b a s e 64 ( r e d u c e ( i t e m s ( c r e a t e O b j e c t ( ' p u b l i c I P I d ' , v a r i a b l e s ( ' p u b l i c I P I d ' ) , ' d o m a i n N a m e ' , p a r a m e t e r s ( ' d o m a i n N a m e ' ) , ' c e r t i f i c a t e T y p e ' , p a r a m e t e r s ( ' c e r t i f i c a t e T y p e ' ) , ' o w n P u b l i c C e r t i f i c a t e ' , p a r a m e t e r s ( ' o w n P u b l i c C e r t i f i c a t e ' ) , ' o w n P r i v a t e C e r t i f i c a t e ' , p a r a m e t e r s ( ' o w n P r i v a t e C e r t i f i c a t e ' ) , ' o p e n v i d u L i c e n s e ' , p a r a m e t e r s ( ' o p e n v i d u L i c e n s e ' ) , ' r t c E n g i n e ' , p a r a m e t e r s ( ' r t c E n g i n e ' ) , ' i n i t i a l M e e t A d m i n P a s s w o r d ' , p a r a m e t e r s ( ' i n i t i a l M e e t A d m i n P a s s w o r d ' ) , ' i n i t i a l M e e t A p i K e y ' , p a r a m e t e r s ( ' i n i t i a l M e e t A p i K e y ' ) , ' k e y V a u l t N a m e ' , v a r i a b l e s ( ' k e y V a u l t N a m e ' ) , ' m a s t e r I d e n t i t y C l i e n t I d ' , r e f e r e n c e ( r e s o u r c e I d ( ' M i c r o s o f t . M a n a g e d I d e n t i t y / u s e r A s s i g n e d I d e n t i t i e s ' , f o r m a t ( ' { 0 } - m a s t e r - i d e n t i t y ' , p a r a m e t e r s ( ' s t a c k N a m e ' ) ) ) , ' 2023 -0 1 -31 ' ) . c l i e n t I d , ' m a s t e r N o d e N u m ' , ' 1 ' , ' a d d i t i o n a l I n s t a l l F l a g s ' , p a r a m e t e r s ( ' a d d i t i o n a l I n s t a l l F l a g s ' ) ) ) , c r e a t e O b j e c t ( ' v a l u e ' , v a r i a b l e s ( ' a f t e r _ i n s t a l l S c r i p t T e m p l a t e M a s t e r ' ) ) , l a m b d a ( ' c u r r ' , ' n e x t ' , c r e a t e O b j e c t ( ' v a l u e ' , r e p l a c e ( l a m b d a V a r i a b l e s ( ' c u r r ' ) . v a l u e , f o r m a t ( ' $ { { { 0 } } } ' , l a m b d a V a r i a b l e s ( ' n e x t ' ) . k e y ) , l a m b d a V a r i a b l e s ( ' n e x t ' ) . v a l u e ) ) ) ) . v a l u e ) , ' b a s e 64 g e t _ p u b l i c _ i p ' , b a s e 64 ( r e d u c e ( i t e m s ( c r e a t e O b j e c t ( ' p u b l i c I P I d ' , v a r i a b l e s ( ' p u b l i c I P I d ' ) , ' d o m a i n N a m e ' , p a r a m e t e r s ( ' d o m a i n N a m e ' ) , ' c e r t i f i c a t e T y p e ' , p a r a m e t e r s ( ' c e r t i f i c a t e T y p e ' ) , ' o w n P u b l i c C e r t i f i c a t e ' , p a r a m e t e r s ( ' o w n P u b l i c C e r t i f i c a t e ' ) , ' o w n P r i v a t e C e r t i f i c a t e ' , p a r a m e t e r s ( ' o w n P r i v a t e C e r t i f i c a t e ' ) , ' o p e n v i d u L i c e n s e ' , p a r a m e t e r s ( ' o p e n v i d u L i c e n s e ' ) , ' r t c E n g i n e ' , p a r a m e t e r s ( ' r t c E n g i n e ' ) , ' i n i t i a l M e e t A d m i n P a s s w o r d ' , p a r a m e t e r s ( ' i n i t i a l M e e t A d m i n P a s s w o r d ' ) , ' i n i t i a l M e e t A p i K e y ' , p a r a m e t e r s ( ' i n i t i a l M e e t A p i K e y ' ) , ' k e y V a u l t N a m e ' , v a r i a b l e s ( ' k e y V a u l t N a m e ' ) , ' m a s t e r I d e n t i t y C l i e n t I d ' , r e f e r e n c e ( r e s o u r c e I d ( ' M i c r o s o f t . M a n a g e d I d e n t i t y / u s e r A s s i g n e d I d e n t i t i e s ' , f o r m a t ( ' { 0 } - m a s t e r - i d e n t i t y ' , p a r a m e t e r s ( ' s t a c k N a m e ' ) ) ) , ' 2023 -0 1 -31 ' ) . c l i e n t I d , ' m a s t e r N o d e N u m ' , ' 1 ' , ' a d d i t i o n a l I n s t a l l F l a g s ' , p a r a m e t e r s ( ' a d d i t i o n a l I n s t a l l F l a g s ' ) ) ) , c r e a t e O b j e c t ( ' v a l u e ' , v a r i a b l e s ( ' g e t _ p u b l i c _ i p ' ) ) , l a m b d a ( ' c u r r ' , ' n e x t ' , c r e a t e O b j e c t ( ' v a l u e ' , r e p l a c e ( l a m b d a V a r i a b l e s ( ' c u r r ' ) . v a l u e , f o r m a t ( ' $ { { { 0 } } } ' , l a m b d a V a r i a b l e s ( ' n e x t ' ) . k e y ) , l a m b d a V a r i a b l e s ( ' n e x t ' ) . v a l u e ) ) ) ) . v a l u e ) , ' b a s e 64 u p d a t e _ c o n f i g _ f r o m _ s e c r e t ' , b a s e 64 ( r e d u c e ( i t e m s ( c r e a t e O b j e c t ( ' p u b l i c I P I d ' , v a r i a b l e s ( ' p u b l i c I P I d ' ) , ' d o m a i n N a m e ' , p a r a m e t e r s ( ' d o m a i n N a m e ' ) , ' c e r t i f i c a t e T y p e ' , p a r a m e t e r s ( ' c e r t i f i c a t e T y p e ' ) , ' o w n P u b l i c C e r t i f i c a t e ' , p a r a m e t e r s ( ' o w n P u b l i c C e r t i f i c a t e ' ) , ' o w n P r i v a t e C e r t i f i c a t e ' , p a r a m e t e r s ( ' o w n P r i v a t e C e r t i f i c a t e ' ) , ' o p e n v i d u L i c e n s e ' , p a r a m e t e r s ( ' o p e n v i d u L i c e n s e ' ) , ' r t c E n g i n e ' , p a r a m e t e r s ( ' r t c E n g i n e ' ) , ' i n i t i a l M e e t A d m i n P a s s w o r d ' , p a r a m e t e r s ( ' i n i t i a l M e e t A d m i n P a s s w o r d ' ) , ' i n i t i a l M e e t A p i K e y ' , p a r a m e t e r s ( ' i n i t i a l M e e t A p i K e y ' ) , ' k e y V a u l t N a m e ' , v a r i a b l e s ( ' k e y V a u l t N a m e ' ) , ' m a s t e r I d e n t i t y C l i e n t I d ' , r e f e r e n c e ( r e s o u r c e I d ( ' M i c r o s o f t . M a n a g e d I d e n t i t y / u s e r A s s i g n e d I d e n t i t i e s ' , f o r m a t ( ' { 0 } - m a s t e r - i d e n t i t y ' , p a r a m e t e r s ( ' s t a c k N a m e ' ) ) ) , ' 2023 -0 1 -31 ' ) . c l i e n t I d , ' m a s t e r N o d e N u m ' , ' 1 ' , ' a d d i t i o n a l I n s t a l l F l a g s ' , p a r a m e t e r s ( ' a d d i t i o n a l I n s t a l l F l a g s ' ) ) ) , c r e a t e O b j e
2025-04-22 11:46:24 +02:00
} ,
"dependsOn" : [
2026-07-29 11:56:08 +02:00
"[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-master-identity', parameters('stackName')))]" ,
2025-05-29 16:51:59 +02:00
"[resourceId('Microsoft.Network/networkInterfaces', format('{0}-masterNodeNetInterface1', parameters('stackName')))]" ,
2025-09-09 22:06:36 +02:00
"[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIpAddressObject').name)]" ,
2025-05-29 16:51:59 +02:00
"[resourceId('Microsoft.Storage/storageAccounts', uniqueString(resourceGroup().id))]"
2025-04-22 11:46:24 +02:00
]
} ,
{
"type" : "Microsoft.Compute/virtualMachines" ,
"apiVersion" : "2023-09-01" ,
"name" : "[format('{0}-VM-MasterNode2', parameters('stackName'))]" ,
"location" : "[variables('location')]" ,
"identity" : {
2026-07-29 11:56:08 +02:00
"type" : "UserAssigned" ,
"userAssignedIdentities" : {
"[format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-master-identity', parameters('stackName'))))]" : { }
}
2025-04-22 11:46:24 +02:00
} ,
"properties" : {
"hardwareProfile" : {
"vmSize" : "[parameters('masterNodeInstanceType')]"
} ,
"storageProfile" : {
"osDisk" : {
"createOption" : "FromImage" ,
"managedDisk" : {
"storageAccountType" : "[variables('masterNodeVMSettings').osDiskType]"
} ,
"diskSizeGB" : "[variables('masterNodeVMSettings').osDiskSize]"
} ,
"imageReference" : "[variables('masterNodeVMSettings').ubuntuOSVersion]"
} ,
"networkProfile" : {
"networkInterfaces" : [
{
"id" : "[resourceId('Microsoft.Network/networkInterfaces', format('{0}-masterNodeNetInterface2', parameters('stackName')))]"
}
]
} ,
"osProfile" : {
"computerName" : "[format('{0}-VM-MasterNode2', parameters('stackName'))]" ,
"adminUsername" : "[parameters('adminUsername')]" ,
"linuxConfiguration" : "[variables('masterNodeVMSettings').linuxConfiguration]"
} ,
2026-07-29 11:56:08 +02:00
"userData" : " [ b a s e 64 ( r e d u c e ( i t e m s ( c r e a t e O b j e c t ( ' b a s e 64 i n s t a l l ' , b a s e 64 ( r e d u c e ( i t e m s ( c r e a t e O b j e c t ( ' p u b l i c I P I d ' , v a r i a b l e s ( ' p u b l i c I P I d ' ) , ' d o m a i n N a m e ' , p a r a m e t e r s ( ' d o m a i n N a m e ' ) , ' c e r t i f i c a t e T y p e ' , p a r a m e t e r s ( ' c e r t i f i c a t e T y p e ' ) , ' o w n P u b l i c C e r t i f i c a t e ' , p a r a m e t e r s ( ' o w n P u b l i c C e r t i f i c a t e ' ) , ' o w n P r i v a t e C e r t i f i c a t e ' , p a r a m e t e r s ( ' o w n P r i v a t e C e r t i f i c a t e ' ) , ' o p e n v i d u L i c e n s e ' , p a r a m e t e r s ( ' o p e n v i d u L i c e n s e ' ) , ' r t c E n g i n e ' , p a r a m e t e r s ( ' r t c E n g i n e ' ) , ' i n i t i a l M e e t A d m i n P a s s w o r d ' , p a r a m e t e r s ( ' i n i t i a l M e e t A d m i n P a s s w o r d ' ) , ' i n i t i a l M e e t A p i K e y ' , p a r a m e t e r s ( ' i n i t i a l M e e t A p i K e y ' ) , ' k e y V a u l t N a m e ' , v a r i a b l e s ( ' k e y V a u l t N a m e ' ) , ' m a s t e r I d e n t i t y C l i e n t I d ' , r e f e r e n c e ( r e s o u r c e I d ( ' M i c r o s o f t . M a n a g e d I d e n t i t y / u s e r A s s i g n e d I d e n t i t i e s ' , f o r m a t ( ' { 0 } - m a s t e r - i d e n t i t y ' , p a r a m e t e r s ( ' s t a c k N a m e ' ) ) ) , ' 2023 -0 1 -31 ' ) . c l i e n t I d , ' m a s t e r N o d e N u m ' , ' 2 ' , ' a d d i t i o n a l I n s t a l l F l a g s ' , p a r a m e t e r s ( ' a d d i t i o n a l I n s t a l l F l a g s ' ) ) ) , c r e a t e O b j e c t ( ' v a l u e ' , v a r i a b l e s ( ' i n s t a l l S c r i p t T e m p l a t e M a s t e r ' ) ) , l a m b d a ( ' c u r r ' , ' n e x t ' , c r e a t e O b j e c t ( ' v a l u e ' , r e p l a c e ( l a m b d a V a r i a b l e s ( ' c u r r ' ) . v a l u e , f o r m a t ( ' $ { { { 0 } } } ' , l a m b d a V a r i a b l e s ( ' n e x t ' ) . k e y ) , l a m b d a V a r i a b l e s ( ' n e x t ' ) . v a l u e ) ) ) ) . v a l u e ) , ' b a s e 64 a f t e r _ i n s t a l l ' , b a s e 64 ( r e d u c e ( i t e m s ( c r e a t e O b j e c t ( ' p u b l i c I P I d ' , v a r i a b l e s ( ' p u b l i c I P I d ' ) , ' d o m a i n N a m e ' , p a r a m e t e r s ( ' d o m a i n N a m e ' ) , ' c e r t i f i c a t e T y p e ' , p a r a m e t e r s ( ' c e r t i f i c a t e T y p e ' ) , ' o w n P u b l i c C e r t i f i c a t e ' , p a r a m e t e r s ( ' o w n P u b l i c C e r t i f i c a t e ' ) , ' o w n P r i v a t e C e r t i f i c a t e ' , p a r a m e t e r s ( ' o w n P r i v a t e C e r t i f i c a t e ' ) , ' o p e n v i d u L i c e n s e ' , p a r a m e t e r s ( ' o p e n v i d u L i c e n s e ' ) , ' r t c E n g i n e ' , p a r a m e t e r s ( ' r t c E n g i n e ' ) , ' i n i t i a l M e e t A d m i n P a s s w o r d ' , p a r a m e t e r s ( ' i n i t i a l M e e t A d m i n P a s s w o r d ' ) , ' i n i t i a l M e e t A p i K e y ' , p a r a m e t e r s ( ' i n i t i a l M e e t A p i K e y ' ) , ' k e y V a u l t N a m e ' , v a r i a b l e s ( ' k e y V a u l t N a m e ' ) , ' m a s t e r I d e n t i t y C l i e n t I d ' , r e f e r e n c e ( r e s o u r c e I d ( ' M i c r o s o f t . M a n a g e d I d e n t i t y / u s e r A s s i g n e d I d e n t i t i e s ' , f o r m a t ( ' { 0 } - m a s t e r - i d e n t i t y ' , p a r a m e t e r s ( ' s t a c k N a m e ' ) ) ) , ' 2023 -0 1 -31 ' ) . c l i e n t I d , ' m a s t e r N o d e N u m ' , ' 1 ' , ' a d d i t i o n a l I n s t a l l F l a g s ' , p a r a m e t e r s ( ' a d d i t i o n a l I n s t a l l F l a g s ' ) ) ) , c r e a t e O b j e c t ( ' v a l u e ' , v a r i a b l e s ( ' a f t e r _ i n s t a l l S c r i p t T e m p l a t e M a s t e r ' ) ) , l a m b d a ( ' c u r r ' , ' n e x t ' , c r e a t e O b j e c t ( ' v a l u e ' , r e p l a c e ( l a m b d a V a r i a b l e s ( ' c u r r ' ) . v a l u e , f o r m a t ( ' $ { { { 0 } } } ' , l a m b d a V a r i a b l e s ( ' n e x t ' ) . k e y ) , l a m b d a V a r i a b l e s ( ' n e x t ' ) . v a l u e ) ) ) ) . v a l u e ) , ' b a s e 64 g e t _ p u b l i c _ i p ' , b a s e 64 ( r e d u c e ( i t e m s ( c r e a t e O b j e c t ( ' p u b l i c I P I d ' , v a r i a b l e s ( ' p u b l i c I P I d ' ) , ' d o m a i n N a m e ' , p a r a m e t e r s ( ' d o m a i n N a m e ' ) , ' c e r t i f i c a t e T y p e ' , p a r a m e t e r s ( ' c e r t i f i c a t e T y p e ' ) , ' o w n P u b l i c C e r t i f i c a t e ' , p a r a m e t e r s ( ' o w n P u b l i c C e r t i f i c a t e ' ) , ' o w n P r i v a t e C e r t i f i c a t e ' , p a r a m e t e r s ( ' o w n P r i v a t e C e r t i f i c a t e ' ) , ' o p e n v i d u L i c e n s e ' , p a r a m e t e r s ( ' o p e n v i d u L i c e n s e ' ) , ' r t c E n g i n e ' , p a r a m e t e r s ( ' r t c E n g i n e ' ) , ' i n i t i a l M e e t A d m i n P a s s w o r d ' , p a r a m e t e r s ( ' i n i t i a l M e e t A d m i n P a s s w o r d ' ) , ' i n i t i a l M e e t A p i K e y ' , p a r a m e t e r s ( ' i n i t i a l M e e t A p i K e y ' ) , ' k e y V a u l t N a m e ' , v a r i a b l e s ( ' k e y V a u l t N a m e ' ) , ' m a s t e r I d e n t i t y C l i e n t I d ' , r e f e r e n c e ( r e s o u r c e I d ( ' M i c r o s o f t . M a n a g e d I d e n t i t y / u s e r A s s i g n e d I d e n t i t i e s ' , f o r m a t ( ' { 0 } - m a s t e r - i d e n t i t y ' , p a r a m e t e r s ( ' s t a c k N a m e ' ) ) ) , ' 2023 -0 1 -31 ' ) . c l i e n t I d , ' m a s t e r N o d e N u m ' , ' 1 ' , ' a d d i t i o n a l I n s t a l l F l a g s ' , p a r a m e t e r s ( ' a d d i t i o n a l I n s t a l l F l a g s ' ) ) ) , c r e a t e O b j e c t ( ' v a l u e ' , v a r i a b l e s ( ' g e t _ p u b l i c _ i p ' ) ) , l a m b d a ( ' c u r r ' , ' n e x t ' , c r e a t e O b j e c t ( ' v a l u e ' , r e p l a c e ( l a m b d a V a r i a b l e s ( ' c u r r ' ) . v a l u e , f o r m a t ( ' $ { { { 0 } } } ' , l a m b d a V a r i a b l e s ( ' n e x t ' ) . k e y ) , l a m b d a V a r i a b l e s ( ' n e x t ' ) . v a l u e ) ) ) ) . v a l u e ) , ' b a s e 64 u p d a t e _ c o n f i g _ f r o m _ s e c r e t ' , b a s e 64 ( r e d u c e ( i t e m s ( c r e a t e O b j e c t ( ' p u b l i c I P I d ' , v a r i a b l e s ( ' p u b l i c I P I d ' ) , ' d o m a i n N a m e ' , p a r a m e t e r s ( ' d o m a i n N a m e ' ) , ' c e r t i f i c a t e T y p e ' , p a r a m e t e r s ( ' c e r t i f i c a t e T y p e ' ) , ' o w n P u b l i c C e r t i f i c a t e ' , p a r a m e t e r s ( ' o w n P u b l i c C e r t i f i c a t e ' ) , ' o w n P r i v a t e C e r t i f i c a t e ' , p a r a m e t e r s ( ' o w n P r i v a t e C e r t i f i c a t e ' ) , ' o p e n v i d u L i c e n s e ' , p a r a m e t e r s ( ' o p e n v i d u L i c e n s e ' ) , ' r t c E n g i n e ' , p a r a m e t e r s ( ' r t c E n g i n e ' ) , ' i n i t i a l M e e t A d m i n P a s s w o r d ' , p a r a m e t e r s ( ' i n i t i a l M e e t A d m i n P a s s w o r d ' ) , ' i n i t i a l M e e t A p i K e y ' , p a r a m e t e r s ( ' i n i t i a l M e e t A p i K e y ' ) , ' k e y V a u l t N a m e ' , v a r i a b l e s ( ' k e y V a u l t N a m e ' ) , ' m a s t e r I d e n t i t y C l i e n t I d ' , r e f e r e n c e ( r e s o u r c e I d ( ' M i c r o s o f t . M a n a g e d I d e n t i t y / u s e r A s s i g n e d I d e n t i t i e s ' , f o r m a t ( ' { 0 } - m a s t e r - i d e n t i t y ' , p a r a m e t e r s ( ' s t a c k N a m e ' ) ) ) , ' 2023 -0 1 -31 ' ) . c l i e n t I d , ' m a s t e r N o d e N u m ' , ' 1 ' , ' a d d i t i o n a l I n s t a l l F l a g s ' , p a r a m e t e r s ( ' a d d i t i o n a l I n s t a l l F l a g s ' ) ) ) , c r e a t e O b j e
2025-04-22 11:46:24 +02:00
} ,
"dependsOn" : [
2026-07-29 11:56:08 +02:00
"[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-master-identity', parameters('stackName')))]" ,
2025-04-22 11:46:24 +02:00
"[resourceId('Microsoft.Network/networkInterfaces', format('{0}-masterNodeNetInterface2', parameters('stackName')))]" ,
2025-09-09 22:06:36 +02:00
"[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIpAddressObject').name)]" ,
2025-05-29 16:51:59 +02:00
"[resourceId('Microsoft.Storage/storageAccounts', uniqueString(resourceGroup().id))]"
2025-04-22 11:46:24 +02:00
]
} ,
{
"type" : "Microsoft.Compute/virtualMachines" ,
"apiVersion" : "2023-09-01" ,
"name" : "[format('{0}-VM-MasterNode3', parameters('stackName'))]" ,
"location" : "[variables('location')]" ,
"identity" : {
2026-07-29 11:56:08 +02:00
"type" : "UserAssigned" ,
"userAssignedIdentities" : {
"[format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-master-identity', parameters('stackName'))))]" : { }
}
2025-04-22 11:46:24 +02:00
} ,
"properties" : {
"hardwareProfile" : {
"vmSize" : "[parameters('masterNodeInstanceType')]"
} ,
"storageProfile" : {
"osDisk" : {
"createOption" : "FromImage" ,
"managedDisk" : {
"storageAccountType" : "[variables('masterNodeVMSettings').osDiskType]"
} ,
"diskSizeGB" : "[variables('masterNodeVMSettings').osDiskSize]"
} ,
"imageReference" : "[variables('masterNodeVMSettings').ubuntuOSVersion]"
} ,
"networkProfile" : {
"networkInterfaces" : [
{
"id" : "[resourceId('Microsoft.Network/networkInterfaces', format('{0}-masterNodeNetInterface3', parameters('stackName')))]"
}
]
} ,
"osProfile" : {
"computerName" : "[format('{0}-VM-MasterNode3', parameters('stackName'))]" ,
"adminUsername" : "[parameters('adminUsername')]" ,
"linuxConfiguration" : "[variables('masterNodeVMSettings').linuxConfiguration]"
} ,
2026-07-29 11:56:08 +02:00
"userData" : " [ b a s e 64 ( r e d u c e ( i t e m s ( c r e a t e O b j e c t ( ' b a s e 64 i n s t a l l ' , b a s e 64 ( r e d u c e ( i t e m s ( c r e a t e O b j e c t ( ' p u b l i c I P I d ' , v a r i a b l e s ( ' p u b l i c I P I d ' ) , ' d o m a i n N a m e ' , p a r a m e t e r s ( ' d o m a i n N a m e ' ) , ' c e r t i f i c a t e T y p e ' , p a r a m e t e r s ( ' c e r t i f i c a t e T y p e ' ) , ' o w n P u b l i c C e r t i f i c a t e ' , p a r a m e t e r s ( ' o w n P u b l i c C e r t i f i c a t e ' ) , ' o w n P r i v a t e C e r t i f i c a t e ' , p a r a m e t e r s ( ' o w n P r i v a t e C e r t i f i c a t e ' ) , ' o p e n v i d u L i c e n s e ' , p a r a m e t e r s ( ' o p e n v i d u L i c e n s e ' ) , ' r t c E n g i n e ' , p a r a m e t e r s ( ' r t c E n g i n e ' ) , ' i n i t i a l M e e t A d m i n P a s s w o r d ' , p a r a m e t e r s ( ' i n i t i a l M e e t A d m i n P a s s w o r d ' ) , ' i n i t i a l M e e t A p i K e y ' , p a r a m e t e r s ( ' i n i t i a l M e e t A p i K e y ' ) , ' k e y V a u l t N a m e ' , v a r i a b l e s ( ' k e y V a u l t N a m e ' ) , ' m a s t e r I d e n t i t y C l i e n t I d ' , r e f e r e n c e ( r e s o u r c e I d ( ' M i c r o s o f t . M a n a g e d I d e n t i t y / u s e r A s s i g n e d I d e n t i t i e s ' , f o r m a t ( ' { 0 } - m a s t e r - i d e n t i t y ' , p a r a m e t e r s ( ' s t a c k N a m e ' ) ) ) , ' 2023 -0 1 -31 ' ) . c l i e n t I d , ' m a s t e r N o d e N u m ' , ' 3 ' , ' a d d i t i o n a l I n s t a l l F l a g s ' , p a r a m e t e r s ( ' a d d i t i o n a l I n s t a l l F l a g s ' ) ) ) , c r e a t e O b j e c t ( ' v a l u e ' , v a r i a b l e s ( ' i n s t a l l S c r i p t T e m p l a t e M a s t e r ' ) ) , l a m b d a ( ' c u r r ' , ' n e x t ' , c r e a t e O b j e c t ( ' v a l u e ' , r e p l a c e ( l a m b d a V a r i a b l e s ( ' c u r r ' ) . v a l u e , f o r m a t ( ' $ { { { 0 } } } ' , l a m b d a V a r i a b l e s ( ' n e x t ' ) . k e y ) , l a m b d a V a r i a b l e s ( ' n e x t ' ) . v a l u e ) ) ) ) . v a l u e ) , ' b a s e 64 a f t e r _ i n s t a l l ' , b a s e 64 ( r e d u c e ( i t e m s ( c r e a t e O b j e c t ( ' p u b l i c I P I d ' , v a r i a b l e s ( ' p u b l i c I P I d ' ) , ' d o m a i n N a m e ' , p a r a m e t e r s ( ' d o m a i n N a m e ' ) , ' c e r t i f i c a t e T y p e ' , p a r a m e t e r s ( ' c e r t i f i c a t e T y p e ' ) , ' o w n P u b l i c C e r t i f i c a t e ' , p a r a m e t e r s ( ' o w n P u b l i c C e r t i f i c a t e ' ) , ' o w n P r i v a t e C e r t i f i c a t e ' , p a r a m e t e r s ( ' o w n P r i v a t e C e r t i f i c a t e ' ) , ' o p e n v i d u L i c e n s e ' , p a r a m e t e r s ( ' o p e n v i d u L i c e n s e ' ) , ' r t c E n g i n e ' , p a r a m e t e r s ( ' r t c E n g i n e ' ) , ' i n i t i a l M e e t A d m i n P a s s w o r d ' , p a r a m e t e r s ( ' i n i t i a l M e e t A d m i n P a s s w o r d ' ) , ' i n i t i a l M e e t A p i K e y ' , p a r a m e t e r s ( ' i n i t i a l M e e t A p i K e y ' ) , ' k e y V a u l t N a m e ' , v a r i a b l e s ( ' k e y V a u l t N a m e ' ) , ' m a s t e r I d e n t i t y C l i e n t I d ' , r e f e r e n c e ( r e s o u r c e I d ( ' M i c r o s o f t . M a n a g e d I d e n t i t y / u s e r A s s i g n e d I d e n t i t i e s ' , f o r m a t ( ' { 0 } - m a s t e r - i d e n t i t y ' , p a r a m e t e r s ( ' s t a c k N a m e ' ) ) ) , ' 2023 -0 1 -31 ' ) . c l i e n t I d , ' m a s t e r N o d e N u m ' , ' 1 ' , ' a d d i t i o n a l I n s t a l l F l a g s ' , p a r a m e t e r s ( ' a d d i t i o n a l I n s t a l l F l a g s ' ) ) ) , c r e a t e O b j e c t ( ' v a l u e ' , v a r i a b l e s ( ' a f t e r _ i n s t a l l S c r i p t T e m p l a t e M a s t e r ' ) ) , l a m b d a ( ' c u r r ' , ' n e x t ' , c r e a t e O b j e c t ( ' v a l u e ' , r e p l a c e ( l a m b d a V a r i a b l e s ( ' c u r r ' ) . v a l u e , f o r m a t ( ' $ { { { 0 } } } ' , l a m b d a V a r i a b l e s ( ' n e x t ' ) . k e y ) , l a m b d a V a r i a b l e s ( ' n e x t ' ) . v a l u e ) ) ) ) . v a l u e ) , ' b a s e 64 g e t _ p u b l i c _ i p ' , b a s e 64 ( r e d u c e ( i t e m s ( c r e a t e O b j e c t ( ' p u b l i c I P I d ' , v a r i a b l e s ( ' p u b l i c I P I d ' ) , ' d o m a i n N a m e ' , p a r a m e t e r s ( ' d o m a i n N a m e ' ) , ' c e r t i f i c a t e T y p e ' , p a r a m e t e r s ( ' c e r t i f i c a t e T y p e ' ) , ' o w n P u b l i c C e r t i f i c a t e ' , p a r a m e t e r s ( ' o w n P u b l i c C e r t i f i c a t e ' ) , ' o w n P r i v a t e C e r t i f i c a t e ' , p a r a m e t e r s ( ' o w n P r i v a t e C e r t i f i c a t e ' ) , ' o p e n v i d u L i c e n s e ' , p a r a m e t e r s ( ' o p e n v i d u L i c e n s e ' ) , ' r t c E n g i n e ' , p a r a m e t e r s ( ' r t c E n g i n e ' ) , ' i n i t i a l M e e t A d m i n P a s s w o r d ' , p a r a m e t e r s ( ' i n i t i a l M e e t A d m i n P a s s w o r d ' ) , ' i n i t i a l M e e t A p i K e y ' , p a r a m e t e r s ( ' i n i t i a l M e e t A p i K e y ' ) , ' k e y V a u l t N a m e ' , v a r i a b l e s ( ' k e y V a u l t N a m e ' ) , ' m a s t e r I d e n t i t y C l i e n t I d ' , r e f e r e n c e ( r e s o u r c e I d ( ' M i c r o s o f t . M a n a g e d I d e n t i t y / u s e r A s s i g n e d I d e n t i t i e s ' , f o r m a t ( ' { 0 } - m a s t e r - i d e n t i t y ' , p a r a m e t e r s ( ' s t a c k N a m e ' ) ) ) , ' 2023 -0 1 -31 ' ) . c l i e n t I d , ' m a s t e r N o d e N u m ' , ' 1 ' , ' a d d i t i o n a l I n s t a l l F l a g s ' , p a r a m e t e r s ( ' a d d i t i o n a l I n s t a l l F l a g s ' ) ) ) , c r e a t e O b j e c t ( ' v a l u e ' , v a r i a b l e s ( ' g e t _ p u b l i c _ i p ' ) ) , l a m b d a ( ' c u r r ' , ' n e x t ' , c r e a t e O b j e c t ( ' v a l u e ' , r e p l a c e ( l a m b d a V a r i a b l e s ( ' c u r r ' ) . v a l u e , f o r m a t ( ' $ { { { 0 } } } ' , l a m b d a V a r i a b l e s ( ' n e x t ' ) . k e y ) , l a m b d a V a r i a b l e s ( ' n e x t ' ) . v a l u e ) ) ) ) . v a l u e ) , ' b a s e 64 u p d a t e _ c o n f i g _ f r o m _ s e c r e t ' , b a s e 64 ( r e d u c e ( i t e m s ( c r e a t e O b j e c t ( ' p u b l i c I P I d ' , v a r i a b l e s ( ' p u b l i c I P I d ' ) , ' d o m a i n N a m e ' , p a r a m e t e r s ( ' d o m a i n N a m e ' ) , ' c e r t i f i c a t e T y p e ' , p a r a m e t e r s ( ' c e r t i f i c a t e T y p e ' ) , ' o w n P u b l i c C e r t i f i c a t e ' , p a r a m e t e r s ( ' o w n P u b l i c C e r t i f i c a t e ' ) , ' o w n P r i v a t e C e r t i f i c a t e ' , p a r a m e t e r s ( ' o w n P r i v a t e C e r t i f i c a t e ' ) , ' o p e n v i d u L i c e n s e ' , p a r a m e t e r s ( ' o p e n v i d u L i c e n s e ' ) , ' r t c E n g i n e ' , p a r a m e t e r s ( ' r t c E n g i n e ' ) , ' i n i t i a l M e e t A d m i n P a s s w o r d ' , p a r a m e t e r s ( ' i n i t i a l M e e t A d m i n P a s s w o r d ' ) , ' i n i t i a l M e e t A p i K e y ' , p a r a m e t e r s ( ' i n i t i a l M e e t A p i K e y ' ) , ' k e y V a u l t N a m e ' , v a r i a b l e s ( ' k e y V a u l t N a m e ' ) , ' m a s t e r I d e n t i t y C l i e n t I d ' , r e f e r e n c e ( r e s o u r c e I d ( ' M i c r o s o f t . M a n a g e d I d e n t i t y / u s e r A s s i g n e d I d e n t i t i e s ' , f o r m a t ( ' { 0 } - m a s t e r - i d e n t i t y ' , p a r a m e t e r s ( ' s t a c k N a m e ' ) ) ) , ' 2023 -0 1 -31 ' ) . c l i e n t I d , ' m a s t e r N o d e N u m ' , ' 1 ' , ' a d d i t i o n a l I n s t a l l F l a g s ' , p a r a m e t e r s ( ' a d d i t i o n a l I n s t a l l F l a g s ' ) ) ) , c r e a t e O b j e
2025-04-22 11:46:24 +02:00
} ,
"dependsOn" : [
2026-07-29 11:56:08 +02:00
"[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-master-identity', parameters('stackName')))]" ,
2025-04-22 11:46:24 +02:00
"[resourceId('Microsoft.Network/networkInterfaces', format('{0}-masterNodeNetInterface3', parameters('stackName')))]" ,
2025-09-09 22:06:36 +02:00
"[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIpAddressObject').name)]" ,
2025-05-29 16:51:59 +02:00
"[resourceId('Microsoft.Storage/storageAccounts', uniqueString(resourceGroup().id))]"
2025-04-22 11:46:24 +02:00
]
} ,
{
"type" : "Microsoft.Compute/virtualMachines" ,
"apiVersion" : "2023-09-01" ,
"name" : "[format('{0}-VM-MasterNode4', parameters('stackName'))]" ,
"location" : "[variables('location')]" ,
"identity" : {
2026-07-29 11:56:08 +02:00
"type" : "UserAssigned" ,
"userAssignedIdentities" : {
"[format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-master-identity', parameters('stackName'))))]" : { }
}
2025-04-22 11:46:24 +02:00
} ,
"properties" : {
"hardwareProfile" : {
"vmSize" : "[parameters('masterNodeInstanceType')]"
} ,
"storageProfile" : {
"osDisk" : {
"createOption" : "FromImage" ,
"managedDisk" : {
"storageAccountType" : "[variables('masterNodeVMSettings').osDiskType]"
} ,
"diskSizeGB" : "[variables('masterNodeVMSettings').osDiskSize]"
} ,
"imageReference" : "[variables('masterNodeVMSettings').ubuntuOSVersion]"
} ,
"networkProfile" : {
"networkInterfaces" : [
{
"id" : "[resourceId('Microsoft.Network/networkInterfaces', format('{0}-masterNodeNetInterface4', parameters('stackName')))]"
}
]
} ,
"osProfile" : {
"computerName" : "[format('{0}-VM-MasterNode4', parameters('stackName'))]" ,
"adminUsername" : "[parameters('adminUsername')]" ,
"linuxConfiguration" : "[variables('masterNodeVMSettings').linuxConfiguration]"
} ,
2026-07-29 11:56:08 +02:00
"userData" : " [ b a s e 64 ( r e d u c e ( i t e m s ( c r e a t e O b j e c t ( ' b a s e 64 i n s t a l l ' , b a s e 64 ( r e d u c e ( i t e m s ( c r e a t e O b j e c t ( ' p u b l i c I P I d ' , v a r i a b l e s ( ' p u b l i c I P I d ' ) , ' d o m a i n N a m e ' , p a r a m e t e r s ( ' d o m a i n N a m e ' ) , ' c e r t i f i c a t e T y p e ' , p a r a m e t e r s ( ' c e r t i f i c a t e T y p e ' ) , ' o w n P u b l i c C e r t i f i c a t e ' , p a r a m e t e r s ( ' o w n P u b l i c C e r t i f i c a t e ' ) , ' o w n P r i v a t e C e r t i f i c a t e ' , p a r a m e t e r s ( ' o w n P r i v a t e C e r t i f i c a t e ' ) , ' o p e n v i d u L i c e n s e ' , p a r a m e t e r s ( ' o p e n v i d u L i c e n s e ' ) , ' r t c E n g i n e ' , p a r a m e t e r s ( ' r t c E n g i n e ' ) , ' i n i t i a l M e e t A d m i n P a s s w o r d ' , p a r a m e t e r s ( ' i n i t i a l M e e t A d m i n P a s s w o r d ' ) , ' i n i t i a l M e e t A p i K e y ' , p a r a m e t e r s ( ' i n i t i a l M e e t A p i K e y ' ) , ' k e y V a u l t N a m e ' , v a r i a b l e s ( ' k e y V a u l t N a m e ' ) , ' m a s t e r I d e n t i t y C l i e n t I d ' , r e f e r e n c e ( r e s o u r c e I d ( ' M i c r o s o f t . M a n a g e d I d e n t i t y / u s e r A s s i g n e d I d e n t i t i e s ' , f o r m a t ( ' { 0 } - m a s t e r - i d e n t i t y ' , p a r a m e t e r s ( ' s t a c k N a m e ' ) ) ) , ' 2023 -0 1 -31 ' ) . c l i e n t I d , ' m a s t e r N o d e N u m ' , ' 4 ' , ' a d d i t i o n a l I n s t a l l F l a g s ' , p a r a m e t e r s ( ' a d d i t i o n a l I n s t a l l F l a g s ' ) ) ) , c r e a t e O b j e c t ( ' v a l u e ' , v a r i a b l e s ( ' i n s t a l l S c r i p t T e m p l a t e M a s t e r ' ) ) , l a m b d a ( ' c u r r ' , ' n e x t ' , c r e a t e O b j e c t ( ' v a l u e ' , r e p l a c e ( l a m b d a V a r i a b l e s ( ' c u r r ' ) . v a l u e , f o r m a t ( ' $ { { { 0 } } } ' , l a m b d a V a r i a b l e s ( ' n e x t ' ) . k e y ) , l a m b d a V a r i a b l e s ( ' n e x t ' ) . v a l u e ) ) ) ) . v a l u e ) , ' b a s e 64 a f t e r _ i n s t a l l ' , b a s e 64 ( r e d u c e ( i t e m s ( c r e a t e O b j e c t ( ' p u b l i c I P I d ' , v a r i a b l e s ( ' p u b l i c I P I d ' ) , ' d o m a i n N a m e ' , p a r a m e t e r s ( ' d o m a i n N a m e ' ) , ' c e r t i f i c a t e T y p e ' , p a r a m e t e r s ( ' c e r t i f i c a t e T y p e ' ) , ' o w n P u b l i c C e r t i f i c a t e ' , p a r a m e t e r s ( ' o w n P u b l i c C e r t i f i c a t e ' ) , ' o w n P r i v a t e C e r t i f i c a t e ' , p a r a m e t e r s ( ' o w n P r i v a t e C e r t i f i c a t e ' ) , ' o p e n v i d u L i c e n s e ' , p a r a m e t e r s ( ' o p e n v i d u L i c e n s e ' ) , ' r t c E n g i n e ' , p a r a m e t e r s ( ' r t c E n g i n e ' ) , ' i n i t i a l M e e t A d m i n P a s s w o r d ' , p a r a m e t e r s ( ' i n i t i a l M e e t A d m i n P a s s w o r d ' ) , ' i n i t i a l M e e t A p i K e y ' , p a r a m e t e r s ( ' i n i t i a l M e e t A p i K e y ' ) , ' k e y V a u l t N a m e ' , v a r i a b l e s ( ' k e y V a u l t N a m e ' ) , ' m a s t e r I d e n t i t y C l i e n t I d ' , r e f e r e n c e ( r e s o u r c e I d ( ' M i c r o s o f t . M a n a g e d I d e n t i t y / u s e r A s s i g n e d I d e n t i t i e s ' , f o r m a t ( ' { 0 } - m a s t e r - i d e n t i t y ' , p a r a m e t e r s ( ' s t a c k N a m e ' ) ) ) , ' 2023 -0 1 -31 ' ) . c l i e n t I d , ' m a s t e r N o d e N u m ' , ' 1 ' , ' a d d i t i o n a l I n s t a l l F l a g s ' , p a r a m e t e r s ( ' a d d i t i o n a l I n s t a l l F l a g s ' ) ) ) , c r e a t e O b j e c t ( ' v a l u e ' , v a r i a b l e s ( ' a f t e r _ i n s t a l l S c r i p t T e m p l a t e M a s t e r ' ) ) , l a m b d a ( ' c u r r ' , ' n e x t ' , c r e a t e O b j e c t ( ' v a l u e ' , r e p l a c e ( l a m b d a V a r i a b l e s ( ' c u r r ' ) . v a l u e , f o r m a t ( ' $ { { { 0 } } } ' , l a m b d a V a r i a b l e s ( ' n e x t ' ) . k e y ) , l a m b d a V a r i a b l e s ( ' n e x t ' ) . v a l u e ) ) ) ) . v a l u e ) , ' b a s e 64 g e t _ p u b l i c _ i p ' , b a s e 64 ( r e d u c e ( i t e m s ( c r e a t e O b j e c t ( ' p u b l i c I P I d ' , v a r i a b l e s ( ' p u b l i c I P I d ' ) , ' d o m a i n N a m e ' , p a r a m e t e r s ( ' d o m a i n N a m e ' ) , ' c e r t i f i c a t e T y p e ' , p a r a m e t e r s ( ' c e r t i f i c a t e T y p e ' ) , ' o w n P u b l i c C e r t i f i c a t e ' , p a r a m e t e r s ( ' o w n P u b l i c C e r t i f i c a t e ' ) , ' o w n P r i v a t e C e r t i f i c a t e ' , p a r a m e t e r s ( ' o w n P r i v a t e C e r t i f i c a t e ' ) , ' o p e n v i d u L i c e n s e ' , p a r a m e t e r s ( ' o p e n v i d u L i c e n s e ' ) , ' r t c E n g i n e ' , p a r a m e t e r s ( ' r t c E n g i n e ' ) , ' i n i t i a l M e e t A d m i n P a s s w o r d ' , p a r a m e t e r s ( ' i n i t i a l M e e t A d m i n P a s s w o r d ' ) , ' i n i t i a l M e e t A p i K e y ' , p a r a m e t e r s ( ' i n i t i a l M e e t A p i K e y ' ) , ' k e y V a u l t N a m e ' , v a r i a b l e s ( ' k e y V a u l t N a m e ' ) , ' m a s t e r I d e n t i t y C l i e n t I d ' , r e f e r e n c e ( r e s o u r c e I d ( ' M i c r o s o f t . M a n a g e d I d e n t i t y / u s e r A s s i g n e d I d e n t i t i e s ' , f o r m a t ( ' { 0 } - m a s t e r - i d e n t i t y ' , p a r a m e t e r s ( ' s t a c k N a m e ' ) ) ) , ' 2023 -0 1 -31 ' ) . c l i e n t I d , ' m a s t e r N o d e N u m ' , ' 1 ' , ' a d d i t i o n a l I n s t a l l F l a g s ' , p a r a m e t e r s ( ' a d d i t i o n a l I n s t a l l F l a g s ' ) ) ) , c r e a t e O b j e c t ( ' v a l u e ' , v a r i a b l e s ( ' g e t _ p u b l i c _ i p ' ) ) , l a m b d a ( ' c u r r ' , ' n e x t ' , c r e a t e O b j e c t ( ' v a l u e ' , r e p l a c e ( l a m b d a V a r i a b l e s ( ' c u r r ' ) . v a l u e , f o r m a t ( ' $ { { { 0 } } } ' , l a m b d a V a r i a b l e s ( ' n e x t ' ) . k e y ) , l a m b d a V a r i a b l e s ( ' n e x t ' ) . v a l u e ) ) ) ) . v a l u e ) , ' b a s e 64 u p d a t e _ c o n f i g _ f r o m _ s e c r e t ' , b a s e 64 ( r e d u c e ( i t e m s ( c r e a t e O b j e c t ( ' p u b l i c I P I d ' , v a r i a b l e s ( ' p u b l i c I P I d ' ) , ' d o m a i n N a m e ' , p a r a m e t e r s ( ' d o m a i n N a m e ' ) , ' c e r t i f i c a t e T y p e ' , p a r a m e t e r s ( ' c e r t i f i c a t e T y p e ' ) , ' o w n P u b l i c C e r t i f i c a t e ' , p a r a m e t e r s ( ' o w n P u b l i c C e r t i f i c a t e ' ) , ' o w n P r i v a t e C e r t i f i c a t e ' , p a r a m e t e r s ( ' o w n P r i v a t e C e r t i f i c a t e ' ) , ' o p e n v i d u L i c e n s e ' , p a r a m e t e r s ( ' o p e n v i d u L i c e n s e ' ) , ' r t c E n g i n e ' , p a r a m e t e r s ( ' r t c E n g i n e ' ) , ' i n i t i a l M e e t A d m i n P a s s w o r d ' , p a r a m e t e r s ( ' i n i t i a l M e e t A d m i n P a s s w o r d ' ) , ' i n i t i a l M e e t A p i K e y ' , p a r a m e t e r s ( ' i n i t i a l M e e t A p i K e y ' ) , ' k e y V a u l t N a m e ' , v a r i a b l e s ( ' k e y V a u l t N a m e ' ) , ' m a s t e r I d e n t i t y C l i e n t I d ' , r e f e r e n c e ( r e s o u r c e I d ( ' M i c r o s o f t . M a n a g e d I d e n t i t y / u s e r A s s i g n e d I d e n t i t i e s ' , f o r m a t ( ' { 0 } - m a s t e r - i d e n t i t y ' , p a r a m e t e r s ( ' s t a c k N a m e ' ) ) ) , ' 2023 -0 1 -31 ' ) . c l i e n t I d , ' m a s t e r N o d e N u m ' , ' 1 ' , ' a d d i t i o n a l I n s t a l l F l a g s ' , p a r a m e t e r s ( ' a d d i t i o n a l I n s t a l l F l a g s ' ) ) ) , c r e a t e O b j e
2025-04-22 11:46:24 +02:00
} ,
"dependsOn" : [
2026-07-29 11:56:08 +02:00
"[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-master-identity', parameters('stackName')))]" ,
2025-04-22 11:46:24 +02:00
"[resourceId('Microsoft.Network/networkInterfaces', format('{0}-masterNodeNetInterface4', parameters('stackName')))]" ,
2025-09-09 22:06:36 +02:00
"[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIpAddressObject').name)]" ,
2025-04-23 13:26:33 +02:00
"[resourceId('Microsoft.Storage/storageAccounts', uniqueString(resourceGroup().id))]"
2025-04-22 11:46:24 +02:00
]
} ,
{
"type" : "Microsoft.Compute/virtualMachineScaleSets" ,
"apiVersion" : "2024-07-01" ,
"name" : "[format('{0}-mediaNodeScaleSet', parameters('stackName'))]" ,
"location" : "[variables('location')]" ,
"tags" : {
"InstanceDeleteTime" : "[parameters('datetime')]" ,
2025-05-13 11:59:15 +02:00
"storageAccount" : "[if(variables('isEmptyStorageAccountName'), uniqueString(resourceGroup().id), parameters('storageAccountName'))]"
2025-04-22 11:46:24 +02:00
} ,
"identity" : {
2026-07-29 11:56:08 +02:00
"type" : "UserAssigned" ,
"userAssignedIdentities" : {
"[format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-media-identity', parameters('stackName'))))]" : { }
}
2025-04-22 11:46:24 +02:00
} ,
"sku" : {
"name" : "[parameters('mediaNodeInstanceType')]" ,
"tier" : "Standard" ,
"capacity" : "[parameters('initialNumberOfMediaNodes')]"
} ,
"properties" : {
"overprovision" : true ,
"upgradePolicy" : {
"mode" : "Automatic"
} ,
"singlePlacementGroup" : true ,
"platformFaultDomainCount" : 1 ,
"virtualMachineProfile" : {
"storageProfile" : {
"osDisk" : {
"createOption" : "FromImage" ,
"managedDisk" : {
"storageAccountType" : "[variables('mediaNodeVMSettings').osDiskType]"
} ,
"diskSizeGB" : 50
} ,
"imageReference" : "[variables('mediaNodeVMSettings').ubuntuOSVersion]"
} ,
"osProfile" : {
"computerNamePrefix" : "[variables('mediaNodeVMSettings').vmName]" ,
"adminUsername" : "[parameters('adminUsername')]" ,
"linuxConfiguration" : "[variables('mediaNodeVMSettings').linuxConfiguration]"
} ,
"networkProfile" : {
"networkInterfaceConfigurations" : [
{
"name" : "[format('{0}-mediaNodeNetInterface', parameters('stackName'))]" ,
"properties" : {
"primary" : true ,
"ipConfigurations" : [
{
"name" : "ipconfigMediaNode" ,
"properties" : {
"subnet" : {
"id" : "[reference(resourceId('Microsoft.Network/virtualNetworks', variables('networkSettings').vNetName), '2023-11-01').subnets[0].id]"
} ,
"applicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-mediaNodeASG', parameters('stackName')))]"
}
] ,
"publicIPAddressConfiguration" : {
"name" : "publicIPAddressMediaNode" ,
"properties" : {
"publicIPAddressVersion" : "IPv4"
}
}
}
}
] ,
"networkSecurityGroup" : {
"id" : "[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-mediaNodeNSG', parameters('stackName')))]"
}
}
}
]
} ,
openvidu-deployment: replace unhealthy Media Nodes (AWS, GCP, Azure elastic and HA)
Every Media Node runs openvidu-media-health, a watchdog that asks the cloud to
replace it with a new node (keeping the Media Node count) when its bootstrap
failed or never finished, or when LiveKit fails its health check on :7880 for
5 minutes (down) or 10 minutes (errors or timeouts; not counted while the CPU is
saturated). Nothing is replaced while no master answers a Redis PING, so a
master outage cannot replace the whole media fleet. Drain scripts pause the
watchdog, and skip waiting for sessions on a node being replaced.
- AWS: the node marks itself Unhealthy in its Auto Scaling group (the
lifecycle hook drains it and a new instance is launched). aws-cli is
installed before the watchdog starts; set_as_unhealthy.sh is gone.
- GCP: the node asks its managed instance group to recreate it. Media
autohealing (TCP 7880) now waits 3600 s before judging a new node and needs
5 minutes of failures; HA gets the same autohealing plus the firewall rule
for the health check ranges.
- Azure: the node reimages itself (new OS disk, bootstrap runs again, same
instance, scale-in protection kept) instead of deleting itself, which shrank
the scale set. The .json twins are rebuilt from the .bicep.
2026-09-30 17:30:23 +02:00
"userData" : " [ b a s e 64 ( r e d u c e ( i t e m s ( c r e a t e O b j e c t ( ' b a s e 64 i n s t a l l ' , b a s e 64 ( r e d u c e ( i t e m s ( c r e a t e O b j e c t ( ' p r i v a t e I P M a s t e r N o d e 1 ' , r e f e r e n c e ( r e s o u r c e I d ( ' M i c r o s o f t . N e t w o r k / n e t w o r k I n t e r f a c e s ' , f o r m a t ( ' { 0 } - m a s t e r N o d e N e t I n t e r f a c e 1 ' , p a r a m e t e r s ( ' s t a c k N a m e ' ) ) ) , ' 2023 -11 -0 1 ' ) . i p C o n f i g u r a t i o n s [ 0 ] . p r o p e r t i e s . p r i v a t e I P A d d r e s s , ' p r i v a t e I P M a s t e r N o d e 2 ' , r e f e r e n c e ( r e s o u r c e I d ( ' M i c r o s o f t . N e t w o r k / n e t w o r k I n t e r f a c e s ' , f o r m a t ( ' { 0 } - m a s t e r N o d e N e t I n t e r f a c e 2 ' , p a r a m e t e r s ( ' s t a c k N a m e ' ) ) ) , ' 2023 -11 -0 1 ' ) . i p C o n f i g u r a t i o n s [ 0 ] . p r o p e r t i e s . p r i v a t e I P A d d r e s s , ' p r i v a t e I P M a s t e r N o d e 3 ' , r e f e r e n c e ( r e s o u r c e I d ( ' M i c r o s o f t . N e t w o r k / n e t w o r k I n t e r f a c e s ' , f o r m a t ( ' { 0 } - m a s t e r N o d e N e t I n t e r f a c e 3 ' , p a r a m e t e r s ( ' s t a c k N a m e ' ) ) ) , ' 2023 -11 -0 1 ' ) . i p C o n f i g u r a t i o n s [ 0 ] . p r o p e r t i e s . p r i v a t e I P A d d r e s s , ' p r i v a t e I P M a s t e r N o d e 4 ' , r e f e r e n c e ( r e s o u r c e I d ( ' M i c r o s o f t . N e t w o r k / n e t w o r k I n t e r f a c e s ' , f o r m a t ( ' { 0 } - m a s t e r N o d e N e t I n t e r f a c e 4 ' , p a r a m e t e r s ( ' s t a c k N a m e ' ) ) ) , ' 2023 -11 -0 1 ' ) . i p C o n f i g u r a t i o n s [ 0 ] . p r o p e r t i e s . p r i v a t e I P A d d r e s s , ' k e y V a u l t N a m e ' , v a r i a b l e s ( ' k e y V a u l t N a m e ' ) ) ) , c r e a t e O b j e c t ( ' v a l u e ' , v a r i a b l e s ( ' i n s t a l l S c r i p t T e m p l a t e M e d i a ' ) ) , l a m b d a ( ' c u r r ' , ' n e x t ' , c r e a t e O b j e c t ( ' v a l u e ' , r e p l a c e ( l a m b d a V a r i a b l e s ( ' c u r r ' ) . v a l u e , f o r m a t ( ' $ { { { 0 } } } ' , l a m b d a V a r i a b l e s ( ' n e x t ' ) . k e y ) , l a m b d a V a r i a b l e s ( ' n e x t ' ) . v a l u e ) ) ) ) . v a l u e ) , ' b a s e 64 s t o p ' , b a s e 64 ( r e d u c e ( i t e m s ( c r e a t e O b j e c t ( ' m e d i a I d e n t i t y C l i e n t I d ' , r e f e r e n c e ( r e s o u r c e I d ( ' M i c r o s o f t . M a n a g e d I d e n t i t y / u s e r A s s i g n e d I d e n t i t i e s ' , f o r m a t ( ' { 0 } - m e d i a - i d e n t i t y ' , p a r a m e t e r s ( ' s t a c k N a m e ' ) ) ) , ' 2023 -0 1 -31 ' ) . c l i e n t I d , ' s u b s c r i p t i o n I d ' , s u b s c r i p t i o n ( ) . s u b s c r i p t i o n I d , ' r e s o u r c e G r o u p N a m e ' , r e s o u r c e G r o u p ( ) . n a m e , ' v m S c a l e S e t N a m e ' , f o r m a t ( ' { 0 } - m e d i a N o d e S c a l e S e t ' , p a r a m e t e r s ( ' s t a c k N a m e ' ) ) , ' s t o r a g e A c c o u n t N a m e ' , i f ( v a r i a b l e s ( ' i s E m p t y S t o r a g e A c c o u n t N a m e ' ) , u n i q u e S t r i n g ( r e s o u r c e G r o u p ( ) . i d ) , p a r a m e t e r s ( ' s t o r a g e A c c o u n t N a m e ' ) ) ) ) , c r e a t e O b j e c t ( ' v a l u e ' , v a r i a b l e s ( ' s t o p _ m e d i a _ n o d e s S c r i p t M e d i a T e m p l a t e ' ) ) , l a m b d a ( ' c u r r ' , ' n e x t ' , c r e a t e O b j e c t ( ' v a l u e ' , r e p l a c e ( l a m b d a V a r i a b l e s ( ' c u r r ' ) . v a l u e , f o r m a t ( ' $ { { { 0 } } } ' , l a m b d a V a r i a b l e s ( ' n e x t ' ) . k e y ) , l a m b d a V a r i a b l e s ( ' n e x t ' ) . v a l u e ) ) ) ) . v a l u e ) , ' b a s e 64 m e d i a H e a l t h ' , v a r i a b l e s ( ' b a s e 64 o p e n v i d u M e d i a H e a l t h ' ) , ' b a s e 64 m e d i a H e a l t h U n i t ' , v a r i a b l e s ( ' b a s e 64 o p e n v i d u M e d i a H e a l t h U n i t ' ) , ' b a s e 64 m e d i a R e p l a c e ' , b a s e 64 ( r e d u c e ( i t e m s ( c r e a t e O b j e c t ( ' m e d i a I d e n t i t y C l i e n t I d ' , r e f e r e n c e ( r e s o u r c e I d ( ' M i c r o s o f t . M a n a g e d I d e n t i t y / u s e r A s s i g n e d I d e n t i t i e s ' , f o r m a t ( ' { 0 } - m e d i a - i d e n t i t y ' , p a r a m e t e r s ( ' s t a c k N a m e ' ) ) ) , ' 2023 -0 1 -31 ' ) . c l i e n t I d , ' s u b s c r i p t i o n I d ' , s u b s c r i p t i o n ( ) . s u b s c r i p t i o n I d , ' r e s o u r c e G r o u p N a m e ' , r e s o u r c e G r o u p ( ) . n a m e , ' v m S c a l e S e t N a m e ' , f o r m a t ( ' { 0 } - m e d i a N o d e S c a l e S e t ' , p a r a m e t e r s ( ' s t a c k N a m e ' ) ) , ' s t o r a g e A c c o u n t N a m e ' , i f ( v a r i a b l e s ( ' i s E m p t y S t o r a g e A c c o u n t N a m e ' ) , u n i q u e S t r i n g ( r e s o u r c e G r o u p ( ) . i d ) , p a r a m e t e r s ( ' s t o r a g e A c c o u n t N a m e ' ) ) ) ) , c r e a t e O b j e c t ( ' v a l u e ' , v a r i a b l e s ( ' m e d i a _ r e p l a c e S c r i p t M e d i a T e m p l a t e ' ) ) , l a m b d a ( ' c u r r ' , ' n e x t ' , c r e a t e O b j e c t ( ' v a l u e ' , r e p l a c e ( l a m b d a V a r i a b l e s ( ' c u r r ' ) . v a l u e , f o r m a t ( ' $ { { { 0 } } } ' , l a m b d a V a r i a b l e s ( ' n e x t ' ) . k e y ) , l a m b d a V a r i a b l e s ( ' n e x t ' ) . v a l u e ) ) ) ) . v a l u e ) , ' p r i v a t e I P M a s t e r N o d e 1 ' , r e f e r e n c e ( r e s o u r c e I d ( ' M i c r o s o f t . N e t w o r k / n e t w o r k I n t e r f a c e s ' , f o r m a t ( ' { 0 } - m a s t e r N o d e N e t I n t e r f a c e 1 ' , p a r a m e t e r s ( ' s t a c k N a m e ' ) ) ) , ' 2023 -11 -0 1 ' ) . i p C o n f i g u r a t i o n s [ 0 ] . p r o p e r t i e s . p r i v a t e I P A d d r e s s , ' p r i v a t e I P M a s t e r N o d e 2 ' , r e f e r e n c e ( r e s o u r c e I d ( ' M i c r o s o f t . N e t w o r k / n e t w o r k I n t e r f a c e s ' , f o r m a t ( ' { 0 } - m a s t e r N o d e N e t I n t e r f a c e 2 ' , p a r a m e t e r s ( ' s t a c k N a m e ' ) ) ) , ' 2023 -11 -0 1 ' ) . i p C o n f i g u r a t i o n s [ 0 ] . p r o p e r t i e s . p r i v a t e I P A d d r e s s , ' p r i v a t e I P M a s t e r N o d e 3 ' , r e f e r e n c e ( r e s o u r c e I d ( ' M i c r o s o f t . N e t w o r k / n e t w o r k I n t e r f a c e s ' , f o r m a t ( ' { 0 } - m a s t e r N o d e N e t I n t e r f a c e 3 ' , p a r a m e t e r s ( ' s t a c k N a m e ' ) ) ) , ' 2023 -11 -0 1 ' ) . i p C o n f i g u r a t i o n s [ 0 ] . p r o p e r t i e s . p r i v a t e I P A d d r e s s , ' p r i v a t e I P M a s t e r N o d e 4 ' , r e f e r e n c e ( r e s o u r c e I d ( ' M i c r o s o f t . N e t w o r k / n e t w o r k I n t e r f a c e s ' , f o r m a t ( ' { 0 } - m a s t e r N o d e N e t I n t e r f a c e 4 ' , p a r a m e t e r s ( ' s t a c k N a m e ' ) ) ) , ' 2023 -11 -0 1 ' ) . i p C o n f i g u r a t i o n s [ 0 ] . p r o p e r t i e s . p r i v a t e I P A d d r e s s , ' m e d i a I d e n t i t y C l i e n t I d ' , r e f e r e n c e ( r e s o u r c e I d ( ' M i c r o s o f t . M a n a g e d I d e n t i t y / u s e r A s s i g n e d I d e n t i t i e s ' , f o r m a t ( ' { 0 } - m e d i a - i d e n t i t y ' , p a r a m e t e r s ( ' s t a c k N a m e ' ) ) ) , ' 2023 -0 1 -31 ' ) . c l i e n t I d , ' r e s o u r c e G r o u p N a m e ' , r e s o u r c e G r o u p ( ) . n a m e , ' v m S c a l e S e t N a m e ' , f o r m a t ( ' { 0 } - m e d i a N o d e S c a l e S e t ' , p a r a m e t e r s ( ' s t a c k N a m e ' ) ) , ' k e y V a u l t N a m e ' , v a r i a b
2025-04-22 11:46:24 +02:00
}
} ,
"dependsOn" : [
2026-07-29 11:56:08 +02:00
"[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-media-identity', parameters('stackName')))]" ,
2025-04-22 11:46:24 +02:00
"[resourceId('Microsoft.Network/networkInterfaces', format('{0}-masterNodeNetInterface1', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/networkInterfaces', format('{0}-masterNodeNetInterface2', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/networkInterfaces', format('{0}-masterNodeNetInterface3', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/networkInterfaces', format('{0}-masterNodeNetInterface4', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-mediaNodeASG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-mediaNodeNSG', parameters('stackName')))]" ,
2025-04-23 13:26:33 +02:00
"[resourceId('Microsoft.Storage/storageAccounts', uniqueString(resourceGroup().id))]" ,
2025-04-22 11:46:24 +02:00
"[resourceId('Microsoft.Network/virtualNetworks', variables('networkSettings').vNetName)]"
]
} ,
{
"type" : "Microsoft.Insights/autoscalesettings" ,
"apiVersion" : "2022-10-01" ,
"name" : "[format('{0}-autoscaleSettings', parameters('stackName'))]" ,
"location" : "[resourceGroup().location]" ,
"properties" : {
"profiles" : [
{
"name" : "openvidu-medianode-autoscale" ,
"capacity" : {
"minimum" : "[string(parameters('minNumberOfMediaNodes'))]" ,
"maximum" : "[string(parameters('maxNumberOfMediaNodes'))]" ,
"default" : "[string(parameters('initialNumberOfMediaNodes'))]"
} ,
"rules" : [
{
"metricTrigger" : {
"metricName" : "Percentage CPU" ,
"metricNamespace" : "Microsoft.Compute/virtualMachineScaleSets" ,
"metricResourceUri" : "[resourceId('Microsoft.Compute/virtualMachineScaleSets', format('{0}-mediaNodeScaleSet', parameters('stackName')))]" ,
"statistic" : "Average" ,
"operator" : "GreaterThan" ,
"threshold" : "[parameters('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" : "[resourceId('Microsoft.Compute/virtualMachineScaleSets', format('{0}-mediaNodeScaleSet', parameters('stackName')))]" ,
"statistic" : "Average" ,
"operator" : "LessThan" ,
"threshold" : "[parameters('scaleTargetCPU')]" ,
"timeAggregation" : "Average" ,
"timeWindow" : "PT5M" ,
"timeGrain" : "PT1M"
} ,
"scaleAction" : {
"direction" : "Decrease" ,
"type" : "ChangeCount" ,
"value" : "1" ,
"cooldown" : "PT5M"
}
}
]
}
] ,
"enabled" : true ,
"targetResourceUri" : "[resourceId('Microsoft.Compute/virtualMachineScaleSets', format('{0}-mediaNodeScaleSet', parameters('stackName')))]"
} ,
"dependsOn" : [
"[resourceId('Microsoft.Compute/virtualMachineScaleSets', format('{0}-mediaNodeScaleSet', parameters('stackName')))]"
]
} ,
2025-05-29 17:43:37 +02:00
{
"type" : "Microsoft.Authorization/roleAssignments" ,
"apiVersion" : "2022-04-01" ,
2026-07-29 11:56:08 +02:00
"name" : "[guid('roleAssignmentForMasterNodes', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-master-identity', parameters('stackName'))))]" ,
2025-04-22 11:46:24 +02:00
"properties" : {
"roleDefinitionId" : "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]" ,
2026-07-29 11:56:08 +02:00
"principalId" : "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-master-identity', parameters('stackName'))), '2023-01-31').principalId]" ,
2025-09-11 20:28:23 +02:00
"principalType" : "ServicePrincipal"
2025-04-22 11:46:24 +02:00
} ,
"dependsOn" : [
2026-07-29 11:56:08 +02:00
"[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-master-identity', parameters('stackName')))]"
2025-04-22 11:46:24 +02:00
]
} ,
{
"type" : "Microsoft.Authorization/roleAssignments" ,
"apiVersion" : "2022-04-01" ,
2026-07-29 11:56:08 +02:00
"name" : "[guid('roleAssignmentForScaleSet', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-media-identity', parameters('stackName'))))]" ,
2025-04-22 11:46:24 +02:00
"properties" : {
"roleDefinitionId" : "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]" ,
2026-07-29 11:56:08 +02:00
"principalId" : "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-media-identity', parameters('stackName'))), '2023-01-31').principalId]" ,
2025-09-11 20:28:23 +02:00
"principalType" : "ServicePrincipal"
2025-04-22 11:46:24 +02:00
} ,
"dependsOn" : [
2026-07-29 11:56:08 +02:00
"[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-media-identity', parameters('stackName')))]"
2025-04-22 11:46:24 +02:00
]
} ,
{
"type" : "Microsoft.Insights/actionGroups" ,
"apiVersion" : "2023-01-01" ,
2025-12-02 18:29:44 +01:00
"name" : "actiongroupScaleIn" ,
2025-04-22 11:46:24 +02:00
"location" : "global" ,
"properties" : {
"groupShortName" : "scaleinag" ,
"enabled" : true ,
"automationRunbookReceivers" : [
{
"name" : "scalein" ,
"useCommonAlertSchema" : false ,
2026-06-16 21:16:00 +02:00
"automationAccountId" : "[reference(resourceId('Microsoft.Resources/deployments', 'WebhookDeployment'), '2025-04-01').outputs.automationAccountId.value]" ,
2025-04-22 11:46:24 +02:00
"runbookName" : "scaleInRunbook" ,
2026-06-16 21:16:00 +02:00
"webhookResourceId" : "[reference(resourceId('Microsoft.Resources/deployments', 'WebhookDeployment'), '2025-04-01').outputs.webhookId.value]" ,
2025-04-22 11:46:24 +02:00
"isGlobalRunbook" : false ,
2026-06-16 21:16:00 +02:00
"serviceUri" : "[reference(resourceId('Microsoft.Resources/deployments', 'WebhookDeployment'), '2025-04-01').outputs.webhookUri.value]"
2025-04-22 11:46:24 +02:00
}
]
} ,
"dependsOn" : [
"[resourceId('Microsoft.Resources/deployments', 'WebhookDeployment')]"
]
} ,
{
"type" : "Microsoft.Insights/activityLogAlerts" ,
"apiVersion" : "2020-10-01" ,
"name" : "ScaleInAlertRule" ,
"location" : "global" ,
"properties" : {
"scopes" : [
"[resourceId('Microsoft.Compute/virtualMachineScaleSets', format('{0}-mediaNodeScaleSet', parameters('stackName')))]"
] ,
"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" : [
{
2025-12-02 18:29:44 +01:00
"actionGroupId" : "[resourceId('Microsoft.Insights/actionGroups', 'actiongroupScaleIn')]"
2025-04-22 11:46:24 +02:00
}
]
} ,
"enabled" : true
} ,
"dependsOn" : [
2025-12-02 18:29:44 +01:00
"[resourceId('Microsoft.Insights/actionGroups', 'actiongroupScaleIn')]" ,
2025-04-22 11:46:24 +02:00
"[resourceId('Microsoft.Compute/virtualMachineScaleSets', format('{0}-mediaNodeScaleSet', parameters('stackName')))]"
]
} ,
{
2025-09-09 22:06:36 +02:00
"condition" : "[equals(variables('ipNew'), true())]" ,
2025-04-22 11:46:24 +02:00
"type" : "Microsoft.Network/publicIPAddresses" ,
2025-09-09 22:06:36 +02:00
"apiVersion" : "2023-11-01" ,
"name" : "[parameters('publicIpAddressObject').name]" ,
2025-04-22 11:46:24 +02:00
"location" : "[variables('location')]" ,
"sku" : {
2025-09-09 22:06:36 +02:00
"name" : "Standard" ,
"tier" : "Regional"
2025-04-22 11:46:24 +02:00
} ,
"properties" : {
"publicIPAddressVersion" : "IPv4" ,
2025-09-09 22:06:36 +02:00
"publicIPAllocationMethod" : "Static" ,
"dnsSettings" : {
"domainNameLabel" : "[if(variables('isEmptyDomain'), toLower(format('{0}', parameters('stackName'))), null())]"
}
2025-04-22 11:46:24 +02:00
}
} ,
{
"type" : "Microsoft.Network/loadBalancers" ,
"apiVersion" : "2024-05-01" ,
"name" : "[variables('lbName')]" ,
"location" : "[variables('location')]" ,
"sku" : {
"name" : "Standard"
} ,
"properties" : {
"frontendIPConfigurations" : [
{
"name" : "[variables('lbFrontEndName')]" ,
"properties" : {
2025-09-09 22:06:36 +02:00
"publicIPAddress" : "[if(variables('isEmptyIp'), null(), createObject('id', if(variables('ipNew'), resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIpAddressObject').name), resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIpAddressObject').name))))]"
2025-04-22 11:46:24 +02:00
}
}
] ,
"backendAddressPools" : [
{
"name" : "[variables('lbBackendPoolNameMasterNode')]"
}
] ,
"loadBalancingRules" : [
{
"name" : "HTTPSRuleforMasterNode" ,
"properties" : {
"frontendIPConfiguration" : {
"id" : "[resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', variables('lbName'), variables('lbFrontEndName'))]"
} ,
"backendAddressPool" : {
"id" : "[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', variables('lbName'), variables('lbBackendPoolNameMasterNode'))]"
} ,
"frontendPort" : 443 ,
"backendPort" : 443 ,
"enableFloatingIP" : false ,
"protocol" : "Tcp" ,
"enableTcpReset" : true ,
"loadDistribution" : "Default" ,
"disableOutboundSnat" : true ,
"probe" : {
"id" : "[resourceId('Microsoft.Network/loadBalancers/probes', variables('lbName'), 'probeForHTTPSRuleMasterNode')]"
}
}
} ,
{
"name" : "RTMPRuleforMasterNode" ,
"properties" : {
"frontendIPConfiguration" : {
"id" : "[resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', variables('lbName'), variables('lbFrontEndName'))]"
} ,
"backendAddressPool" : {
"id" : "[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', variables('lbName'), variables('lbBackendPoolNameMasterNode'))]"
} ,
2026-10-06 20:40:28 +02:00
"frontendPort" : 1945 ,
2025-04-22 11:46:24 +02:00
"backendPort" : 1945 ,
"enableFloatingIP" : false ,
"protocol" : "Tcp" ,
"enableTcpReset" : true ,
"loadDistribution" : "Default" ,
"disableOutboundSnat" : true ,
"probe" : {
"id" : "[resourceId('Microsoft.Network/loadBalancers/probes', variables('lbName'), 'probeForRTMPRuleMasterNode')]"
}
}
} ,
{
"name" : "HTTPRuleforMasterNode" ,
"properties" : {
"frontendIPConfiguration" : {
"id" : "[resourceId('Microsoft.Network/loadBalancers/frontendIPConfigurations', variables('lbName'), variables('lbFrontEndName'))]"
} ,
"backendAddressPool" : {
"id" : "[resourceId('Microsoft.Network/loadBalancers/backendAddressPools', variables('lbName'), variables('lbBackendPoolNameMasterNode'))]"
} ,
"frontendPort" : 80 ,
"backendPort" : 80 ,
"enableFloatingIP" : false ,
"protocol" : "Tcp" ,
"enableTcpReset" : true ,
"loadDistribution" : "Default" ,
"disableOutboundSnat" : true ,
"probe" : {
"id" : "[resourceId('Microsoft.Network/loadBalancers/probes', variables('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" : [ ]
} ,
"dependsOn" : [
2025-09-09 22:06:36 +02:00
"[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIpAddressObject').name)]"
2025-04-22 11:46:24 +02:00
]
} ,
{
"type" : "Microsoft.Network/natGateways" ,
"apiVersion" : "2021-05-01" ,
"name" : "[format('{0}-natGateway', parameters('stackName'))]" ,
"location" : "[variables('location')]" ,
"sku" : {
"name" : "Standard"
} ,
"properties" : {
"idleTimeoutInMinutes" : 4 ,
"publicIpAddresses" : [
{
"id" : "[resourceId('Microsoft.Network/publicIPAddresses', format('{0}-publicIPnatGateway', parameters('stackName')))]"
}
]
} ,
"dependsOn" : [
"[resourceId('Microsoft.Network/publicIPAddresses', format('{0}-publicIPnatGateway', parameters('stackName')))]"
]
} ,
{
"type" : "Microsoft.Network/publicIPAddresses" ,
"apiVersion" : "2021-05-01" ,
"name" : "[format('{0}-publicIPnatGateway', parameters('stackName'))]" ,
"location" : "[variables('location')]" ,
"sku" : {
"name" : "Standard"
} ,
"properties" : {
"publicIPAddressVersion" : "IPv4" ,
"publicIPAllocationMethod" : "Static" ,
"idleTimeoutInMinutes" : 4
}
} ,
{
"type" : "Microsoft.Network/virtualNetworks" ,
"apiVersion" : "2023-11-01" ,
"name" : "[variables('networkSettings').vNetName]" ,
"location" : "[variables('location')]" ,
"properties" : {
"addressSpace" : {
"addressPrefixes" : [
"[variables('networkSettings').vNetAddressPrefix]"
]
} ,
"subnets" : [
{
"name" : "subnetForMediaNodes" ,
"properties" : {
"addressPrefix" : "[variables('networkSettings').subnetAddressPrefixMedia]" ,
"privateEndpointNetworkPolicies" : "Disabled" ,
"privateLinkServiceNetworkPolicies" : "Enabled" ,
"networkSecurityGroup" : {
"id" : "[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-mediaNodeNSG', parameters('stackName')))]"
}
}
}
]
} ,
"dependsOn" : [
"[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-mediaNodeNSG', parameters('stackName')))]"
]
} ,
{
"type" : "Microsoft.Network/virtualNetworks/subnets" ,
"apiVersion" : "2023-11-01" ,
"name" : "[format('{0}/{1}', variables('networkSettings').vNetName, 'firstSubnetForMasterNodes')]" ,
"properties" : {
"addressPrefix" : "[variables('networkSettings').subnetAddressPrefixMaster1]" ,
"privateEndpointNetworkPolicies" : "Disabled" ,
"privateLinkServiceNetworkPolicies" : "Enabled" ,
"natGateway" : {
"id" : "[resourceId('Microsoft.Network/natGateways', format('{0}-natGateway', parameters('stackName')))]"
}
} ,
"dependsOn" : [
"[resourceId('Microsoft.Network/natGateways', format('{0}-natGateway', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/virtualNetworks', variables('networkSettings').vNetName)]"
]
} ,
{
"type" : "Microsoft.Network/virtualNetworks/subnets" ,
"apiVersion" : "2023-11-01" ,
"name" : "[format('{0}/{1}', variables('networkSettings').vNetName, 'secondSubnetForMasterNodes')]" ,
"properties" : {
"addressPrefix" : "[variables('networkSettings').subnetAddressPrefixMaster2]" ,
"privateEndpointNetworkPolicies" : "Disabled" ,
"privateLinkServiceNetworkPolicies" : "Enabled" ,
"natGateway" : {
"id" : "[resourceId('Microsoft.Network/natGateways', format('{0}-natGateway', parameters('stackName')))]"
}
} ,
"dependsOn" : [
"[resourceId('Microsoft.Network/natGateways', format('{0}-natGateway', parameters('stackName')))]" ,
2025-06-10 12:17:10 +02:00
"[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('networkSettings').vNetName, 'firstSubnetForMasterNodes')]" ,
2025-04-22 11:46:24 +02:00
"[resourceId('Microsoft.Network/virtualNetworks', variables('networkSettings').vNetName)]"
]
} ,
{
"type" : "Microsoft.Network/networkInterfaces" ,
"apiVersion" : "2023-11-01" ,
"name" : "[format('{0}-masterNodeNetInterface1', parameters('stackName'))]" ,
"location" : "[variables('location')]" ,
"properties" : {
"ipConfigurations" : [
{
"name" : "primaryIPConfig" ,
"properties" : {
"privateIPAllocationMethod" : "Dynamic" ,
"subnet" : {
"id" : "[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('networkSettings').vNetName, 'firstSubnetForMasterNodes')]"
} ,
"applicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"loadBalancerBackendAddressPools" : [
{
"id" : "[reference(resourceId('Microsoft.Network/loadBalancers', variables('lbName')), '2024-05-01').backendAddressPools[0].id]"
}
]
}
}
] ,
"networkSecurityGroup" : {
"id" : "[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-masterNodeNSG', parameters('stackName')))]"
}
} ,
"dependsOn" : [
"[resourceId('Microsoft.Network/loadBalancers', variables('lbName'))]" ,
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-masterNodeNSG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('networkSettings').vNetName, 'firstSubnetForMasterNodes')]"
]
} ,
{
"type" : "Microsoft.Network/networkInterfaces" ,
"apiVersion" : "2023-11-01" ,
"name" : "[format('{0}-masterNodeNetInterface2', parameters('stackName'))]" ,
"location" : "[variables('location')]" ,
"properties" : {
"ipConfigurations" : [
{
"name" : "primaryIPConfig" ,
"properties" : {
"privateIPAllocationMethod" : "Dynamic" ,
"subnet" : {
"id" : "[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('networkSettings').vNetName, 'secondSubnetForMasterNodes')]"
} ,
"applicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"loadBalancerBackendAddressPools" : [
{
"id" : "[reference(resourceId('Microsoft.Network/loadBalancers', variables('lbName')), '2024-05-01').backendAddressPools[0].id]"
}
]
}
}
] ,
"networkSecurityGroup" : {
"id" : "[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-masterNodeNSG', parameters('stackName')))]"
}
} ,
"dependsOn" : [
"[resourceId('Microsoft.Network/loadBalancers', variables('lbName'))]" ,
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-masterNodeNSG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('networkSettings').vNetName, 'secondSubnetForMasterNodes')]"
]
} ,
{
"type" : "Microsoft.Network/networkInterfaces" ,
"apiVersion" : "2023-11-01" ,
"name" : "[format('{0}-masterNodeNetInterface3', parameters('stackName'))]" ,
"location" : "[variables('location')]" ,
"properties" : {
"ipConfigurations" : [
{
"name" : "primaryIPConfig" ,
"properties" : {
"privateIPAllocationMethod" : "Dynamic" ,
"subnet" : {
"id" : "[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('networkSettings').vNetName, 'firstSubnetForMasterNodes')]"
} ,
"applicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"loadBalancerBackendAddressPools" : [
{
"id" : "[reference(resourceId('Microsoft.Network/loadBalancers', variables('lbName')), '2024-05-01').backendAddressPools[0].id]"
}
]
}
}
] ,
"networkSecurityGroup" : {
"id" : "[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-masterNodeNSG', parameters('stackName')))]"
}
} ,
"dependsOn" : [
"[resourceId('Microsoft.Network/loadBalancers', variables('lbName'))]" ,
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-masterNodeNSG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('networkSettings').vNetName, 'firstSubnetForMasterNodes')]"
]
} ,
{
"type" : "Microsoft.Network/networkInterfaces" ,
"apiVersion" : "2023-11-01" ,
"name" : "[format('{0}-masterNodeNetInterface4', parameters('stackName'))]" ,
"location" : "[variables('location')]" ,
"properties" : {
"ipConfigurations" : [
{
"name" : "primaryIPConfig" ,
"properties" : {
"privateIPAllocationMethod" : "Dynamic" ,
"subnet" : {
"id" : "[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('networkSettings').vNetName, 'secondSubnetForMasterNodes')]"
} ,
"applicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"loadBalancerBackendAddressPools" : [
{
"id" : "[reference(resourceId('Microsoft.Network/loadBalancers', variables('lbName')), '2024-05-01').backendAddressPools[0].id]"
}
]
}
}
] ,
"networkSecurityGroup" : {
"id" : "[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-masterNodeNSG', parameters('stackName')))]"
}
} ,
"dependsOn" : [
"[resourceId('Microsoft.Network/loadBalancers', variables('lbName'))]" ,
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-masterNodeNSG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('networkSettings').vNetName, 'secondSubnetForMasterNodes')]"
]
} ,
{
"type" : "Microsoft.Network/networkSecurityGroups" ,
"apiVersion" : "2023-11-01" ,
"name" : "[format('{0}-masterNodeNSG', parameters('stackName'))]" ,
"location" : "[variables('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"
}
}
]
}
} ,
{
"type" : "Microsoft.Network/applicationSecurityGroups" ,
"apiVersion" : "2024-03-01" ,
"name" : "[format('{0}-masterNodeASG', parameters('stackName'))]" ,
"location" : "[variables('location')]"
} ,
{
"type" : "Microsoft.Network/networkSecurityGroups/securityRules" ,
"apiVersion" : "2023-11-01" ,
"name" : "[format('{0}/{1}', format('{0}-masterNodeNSG', parameters('stackName')), 'loadBalancer_to_masterNode_INGRESS')]" ,
"properties" : {
"protocol" : "Tcp" ,
"sourceAddressPrefix" : "*" ,
"sourcePortRange" : "*" ,
"destinationApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"destinationPortRange" : "443" ,
"access" : "Allow" ,
"priority" : 110 ,
"direction" : "Inbound"
} ,
"dependsOn" : [
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-masterNodeNSG', parameters('stackName')))]"
]
} ,
2026-10-06 20:40:28 +02:00
{
"type" : "Microsoft.Network/networkSecurityGroups/securityRules" ,
"apiVersion" : "2023-11-01" ,
"name" : "[format('{0}/{1}', format('{0}-masterNodeNSG', parameters('stackName')), 'loadBalancer_to_masterNode_RTMP_INGRESS')]" ,
"properties" : {
"protocol" : "Tcp" ,
"sourceAddressPrefix" : "*" ,
"sourcePortRange" : "*" ,
"destinationApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"destinationPortRange" : "1945" ,
"access" : "Allow" ,
"priority" : 111 ,
"direction" : "Inbound"
} ,
"dependsOn" : [
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-masterNodeNSG', parameters('stackName')))]"
]
} ,
2025-10-10 14:51:55 +02:00
{
"type" : "Microsoft.Network/networkSecurityGroups/securityRules" ,
"apiVersion" : "2023-11-01" ,
"name" : "[format('{0}/{1}', format('{0}-masterNodeNSG', parameters('stackName')), 'masterNode_to_masterNode_CLUSTER_INGRESS')]" ,
"properties" : {
"protocol" : "Tcp" ,
"sourceApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"sourcePortRange" : "7880" ,
"destinationApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"destinationPortRange" : "7880" ,
"access" : "Allow" ,
"priority" : 115 ,
"direction" : "Inbound"
} ,
"dependsOn" : [
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-masterNodeNSG', parameters('stackName')))]"
]
} ,
2025-04-22 11:46:24 +02:00
{
"type" : "Microsoft.Network/networkSecurityGroups/securityRules" ,
"apiVersion" : "2023-11-01" ,
"name" : "[format('{0}/{1}', format('{0}-masterNodeNSG', parameters('stackName')), 'masterNode_to_masterNode_REDIS_INGRESS')]" ,
"properties" : {
"protocol" : "Tcp" ,
"sourceApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"sourcePortRange" : "7000" ,
"destinationApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"destinationPortRange" : "7001" ,
"access" : "Allow" ,
"priority" : 120 ,
"direction" : "Inbound"
} ,
"dependsOn" : [
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-masterNodeNSG', parameters('stackName')))]"
]
} ,
{
"type" : "Microsoft.Network/networkSecurityGroups/securityRules" ,
"apiVersion" : "2023-11-01" ,
"name" : "[format('{0}/{1}', format('{0}-masterNodeNSG', parameters('stackName')), 'mediaNode_to_masterNode_REDIS_INGRESS')]" ,
"properties" : {
"protocol" : "Tcp" ,
"sourceApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-mediaNodeASG', parameters('stackName')))]"
}
] ,
"sourcePortRange" : "7000" ,
"destinationApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"destinationPortRange" : "7001" ,
"access" : "Allow" ,
"priority" : 130 ,
"direction" : "Inbound"
} ,
"dependsOn" : [
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-masterNodeNSG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-mediaNodeASG', parameters('stackName')))]"
]
} ,
{
"type" : "Microsoft.Network/networkSecurityGroups/securityRules" ,
"apiVersion" : "2023-11-01" ,
"name" : "[format('{0}/{1}', format('{0}-masterNodeNSG', parameters('stackName')), 'masterNode_to_masterNode_MINIO_INGRESS')]" ,
"properties" : {
"protocol" : "Tcp" ,
"sourceApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"sourcePortRange" : "*" ,
"destinationApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"destinationPortRange" : "9100" ,
"access" : "Allow" ,
"priority" : 140 ,
"direction" : "Inbound"
} ,
"dependsOn" : [
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-masterNodeNSG', parameters('stackName')))]"
]
} ,
{
"type" : "Microsoft.Network/networkSecurityGroups/securityRules" ,
"apiVersion" : "2023-11-01" ,
"name" : "[format('{0}/{1}', format('{0}-masterNodeNSG', parameters('stackName')), 'masterNode_to_masterNode_MINIO_CONSOLE_INGRESS')]" ,
"properties" : {
"protocol" : "Tcp" ,
"sourceApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"sourcePortRange" : "*" ,
"destinationApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"destinationPortRange" : "9101" ,
"access" : "Allow" ,
"priority" : 150 ,
"direction" : "Inbound"
} ,
"dependsOn" : [
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-masterNodeNSG', parameters('stackName')))]"
]
} ,
{
"type" : "Microsoft.Network/networkSecurityGroups/securityRules" ,
"apiVersion" : "2023-11-01" ,
"name" : "[format('{0}/{1}', format('{0}-masterNodeNSG', parameters('stackName')), 'mediaNode_to_masterNode_MINIO_INGRESS')]" ,
"properties" : {
"protocol" : "Tcp" ,
"sourceApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-mediaNodeASG', parameters('stackName')))]"
}
] ,
"sourcePortRange" : "*" ,
"destinationApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"destinationPortRange" : "9100" ,
"access" : "Allow" ,
"priority" : 160 ,
"direction" : "Inbound"
} ,
"dependsOn" : [
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-masterNodeNSG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-mediaNodeASG', parameters('stackName')))]"
]
} ,
{
"type" : "Microsoft.Network/networkSecurityGroups/securityRules" ,
"apiVersion" : "2023-11-01" ,
"name" : "[format('{0}/{1}', format('{0}-masterNodeNSG', parameters('stackName')), 'masterNode_to_masterNode_MONGO_INGRESS')]" ,
"properties" : {
"protocol" : "Tcp" ,
"sourceApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"sourcePortRange" : "*" ,
"destinationApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"destinationPortRange" : "20000" ,
"access" : "Allow" ,
"priority" : 170 ,
"direction" : "Inbound"
} ,
"dependsOn" : [
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-masterNodeNSG', parameters('stackName')))]"
]
} ,
{
"type" : "Microsoft.Network/networkSecurityGroups/securityRules" ,
"apiVersion" : "2023-11-01" ,
"name" : "[format('{0}/{1}', format('{0}-masterNodeNSG', parameters('stackName')), 'mediaNode_to_masterNode_MONGO_INGRESS')]" ,
"properties" : {
"protocol" : "Tcp" ,
"sourceApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-mediaNodeASG', parameters('stackName')))]"
}
] ,
"sourcePortRange" : "*" ,
"destinationApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"destinationPortRange" : "20000" ,
"access" : "Allow" ,
"priority" : 180 ,
"direction" : "Inbound"
} ,
"dependsOn" : [
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-masterNodeNSG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-mediaNodeASG', parameters('stackName')))]"
]
} ,
{
"type" : "Microsoft.Network/networkSecurityGroups/securityRules" ,
"apiVersion" : "2023-11-01" ,
"name" : "[format('{0}/{1}', format('{0}-masterNodeNSG', parameters('stackName')), 'masterNode_to_masterNode_MIMIRGRPC_INGRESS')]" ,
"properties" : {
"protocol" : "Tcp" ,
"sourceApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"sourcePortRange" : "*" ,
"destinationApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"destinationPortRange" : "9095" ,
"access" : "Allow" ,
"priority" : 190 ,
"direction" : "Inbound"
} ,
"dependsOn" : [
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-masterNodeNSG', parameters('stackName')))]"
]
} ,
{
"type" : "Microsoft.Network/networkSecurityGroups/securityRules" ,
"apiVersion" : "2023-11-01" ,
"name" : "[format('{0}/{1}', format('{0}-masterNodeNSG', parameters('stackName')), 'masterNode_to_masterNode_MIMIRGOSSIP_INGRESS')]" ,
"properties" : {
"protocol" : "Tcp" ,
"sourceApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"sourcePortRange" : "*" ,
"destinationApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"destinationPortRange" : "7946" ,
"access" : "Allow" ,
"priority" : 200 ,
"direction" : "Inbound"
} ,
"dependsOn" : [
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-masterNodeNSG', parameters('stackName')))]"
]
} ,
{
"type" : "Microsoft.Network/networkSecurityGroups/securityRules" ,
"apiVersion" : "2023-11-01" ,
"name" : "[format('{0}/{1}', format('{0}-masterNodeNSG', parameters('stackName')), 'mediaNode_to_masterNode_MIMIR_INGRESS')]" ,
"properties" : {
"protocol" : "Tcp" ,
"sourceApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-mediaNodeASG', parameters('stackName')))]"
}
] ,
"sourcePortRange" : "*" ,
"destinationApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"destinationPortRange" : "9009" ,
"access" : "Allow" ,
"priority" : 210 ,
"direction" : "Inbound"
} ,
"dependsOn" : [
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-masterNodeNSG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-mediaNodeASG', parameters('stackName')))]"
]
} ,
{
"type" : "Microsoft.Network/networkSecurityGroups/securityRules" ,
"apiVersion" : "2023-11-01" ,
"name" : "[format('{0}/{1}', format('{0}-masterNodeNSG', parameters('stackName')), 'masterNode_to_masterNode_LOKIGRPC_INGRESS')]" ,
"properties" : {
"protocol" : "Tcp" ,
"sourceApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"sourcePortRange" : "*" ,
"destinationApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"destinationPortRange" : "9096" ,
"access" : "Allow" ,
"priority" : 220 ,
"direction" : "Inbound"
} ,
"dependsOn" : [
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-masterNodeNSG', parameters('stackName')))]"
]
} ,
{
"type" : "Microsoft.Network/networkSecurityGroups/securityRules" ,
"apiVersion" : "2023-11-01" ,
"name" : "[format('{0}/{1}', format('{0}-masterNodeNSG', parameters('stackName')), 'masterNode_to_masterNode_LOKIGOSSIP_INGRESS')]" ,
"properties" : {
"protocol" : "Tcp" ,
"sourceApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"sourcePortRange" : "*" ,
"destinationApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"destinationPortRange" : "7947" ,
"access" : "Allow" ,
"priority" : 230 ,
"direction" : "Inbound"
} ,
"dependsOn" : [
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-masterNodeNSG', parameters('stackName')))]"
]
} ,
{
"type" : "Microsoft.Network/networkSecurityGroups/securityRules" ,
"apiVersion" : "2023-11-01" ,
"name" : "[format('{0}/{1}', format('{0}-masterNodeNSG', parameters('stackName')), 'masterNode_to_masterNode_DASHBOARD_INGRESS')]" ,
"properties" : {
"protocol" : "Tcp" ,
"sourceApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"sourcePortRange" : "*" ,
"destinationApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"destinationPortRange" : "5000" ,
"access" : "Allow" ,
"priority" : 240 ,
"direction" : "Inbound"
} ,
"dependsOn" : [
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-masterNodeNSG', parameters('stackName')))]"
]
} ,
{
"type" : "Microsoft.Network/networkSecurityGroups/securityRules" ,
"apiVersion" : "2023-11-01" ,
"name" : "[format('{0}/{1}', format('{0}-masterNodeNSG', parameters('stackName')), 'masterNode_to_masterNode_GRAFANA_INGRESS')]" ,
"properties" : {
"protocol" : "Tcp" ,
"sourceApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"sourcePortRange" : "*" ,
"destinationApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"destinationPortRange" : "3000" ,
"access" : "Allow" ,
"priority" : 250 ,
"direction" : "Inbound"
} ,
"dependsOn" : [
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-masterNodeNSG', parameters('stackName')))]"
]
} ,
{
"type" : "Microsoft.Network/networkSecurityGroups/securityRules" ,
"apiVersion" : "2023-11-01" ,
"name" : "[format('{0}/{1}', format('{0}-masterNodeNSG', parameters('stackName')), 'mediaNode_to_masterNode_LOKI_INGRESS')]" ,
"properties" : {
"protocol" : "Tcp" ,
"sourceApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-mediaNodeASG', parameters('stackName')))]"
}
] ,
"sourcePortRange" : "*" ,
"destinationApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"destinationPortRange" : "3100" ,
"access" : "Allow" ,
"priority" : 260 ,
"direction" : "Inbound"
} ,
"dependsOn" : [
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-masterNodeNSG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-mediaNodeASG', parameters('stackName')))]"
]
} ,
{
"type" : "Microsoft.Network/networkSecurityGroups/securityRules" ,
"apiVersion" : "2023-11-01" ,
"name" : "[format('{0}/{1}', format('{0}-masterNodeNSG', parameters('stackName')), 'masterNode_to_masterNode_V2COMPATIBILITY_INGRESS')]" ,
"properties" : {
"protocol" : "Tcp" ,
"sourceApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"sourcePortRange" : "*" ,
"destinationApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"destinationPortRange" : "4443" ,
"access" : "Allow" ,
"priority" : 270 ,
"direction" : "Inbound"
} ,
"dependsOn" : [
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-masterNodeNSG', parameters('stackName')))]"
]
} ,
{
"type" : "Microsoft.Network/networkSecurityGroups/securityRules" ,
"apiVersion" : "2023-11-01" ,
"name" : "[format('{0}/{1}', format('{0}-masterNodeNSG', parameters('stackName')), 'mediaNode_to_masterNode_V2COMPATIBILITY_WEBHOOK_INGRESS')]" ,
"properties" : {
"protocol" : "Tcp" ,
"sourceApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-mediaNodeASG', parameters('stackName')))]"
}
] ,
"sourcePortRange" : "*" ,
"destinationApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"destinationPortRange" : "4443" ,
"access" : "Allow" ,
"priority" : 280 ,
"direction" : "Inbound"
} ,
"dependsOn" : [
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-masterNodeNSG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-mediaNodeASG', parameters('stackName')))]"
]
} ,
{
"type" : "Microsoft.Network/networkSecurityGroups/securityRules" ,
"apiVersion" : "2023-11-01" ,
2025-07-22 14:08:25 +02:00
"name" : "[format('{0}/{1}', format('{0}-masterNodeNSG', parameters('stackName')), 'masterNode_to_masterNode_MEET_INGRESS')]" ,
2025-04-22 11:46:24 +02:00
"properties" : {
"protocol" : "Tcp" ,
"sourceApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"sourcePortRange" : "*" ,
"destinationApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
2026-02-05 12:30:22 +01:00
"destinationPortRange" : "9080" ,
2025-04-22 11:46:24 +02:00
"access" : "Allow" ,
"priority" : 290 ,
"direction" : "Inbound"
} ,
"dependsOn" : [
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-masterNodeNSG', parameters('stackName')))]"
]
} ,
2026-03-13 17:46:29 +01:00
{
"type" : "Microsoft.Network/networkSecurityGroups/securityRules" ,
"apiVersion" : "2023-11-01" ,
"name" : "[format('{0}/{1}', format('{0}-masterNodeNSG', parameters('stackName')), 'masterNode_to_masterNode_CUSTOM_APP_INGRESS')]" ,
"properties" : {
"protocol" : "Tcp" ,
"sourceApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"sourcePortRange" : "*" ,
"destinationApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"destinationPortRange" : "6080" ,
"access" : "Allow" ,
"priority" : 310 ,
"direction" : "Inbound"
} ,
"dependsOn" : [
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-masterNodeNSG', parameters('stackName')))]"
]
} ,
2025-04-22 11:46:24 +02:00
{
"type" : "Microsoft.Network/networkSecurityGroups/securityRules" ,
"apiVersion" : "2023-11-01" ,
2025-07-22 14:08:25 +02:00
"name" : "[format('{0}/{1}', format('{0}-masterNodeNSG', parameters('stackName')), 'mediaNode_to_masterNode_MEET_WEBHOOK_INGRESS')]" ,
2025-04-22 11:46:24 +02:00
"properties" : {
"protocol" : "Tcp" ,
"sourceApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-mediaNodeASG', parameters('stackName')))]"
}
] ,
"sourcePortRange" : "*" ,
"destinationApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
2026-02-05 12:30:22 +01:00
"destinationPortRange" : "9080" ,
2025-04-22 11:46:24 +02:00
"access" : "Allow" ,
"priority" : 300 ,
"direction" : "Inbound"
} ,
"dependsOn" : [
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-masterNodeNSG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-mediaNodeASG', parameters('stackName')))]"
]
} ,
2026-03-13 17:46:29 +01:00
{
"type" : "Microsoft.Network/networkSecurityGroups/securityRules" ,
"apiVersion" : "2023-11-01" ,
"name" : "[format('{0}/{1}', format('{0}-masterNodeNSG', parameters('stackName')), 'mediaNode_to_masterNode_CUSTOM_APP_WEBHOOK_INGRESS')]" ,
"properties" : {
"protocol" : "Tcp" ,
"sourceApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-mediaNodeASG', parameters('stackName')))]"
}
] ,
"sourcePortRange" : "*" ,
"destinationApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"destinationPortRange" : "6080" ,
"access" : "Allow" ,
"priority" : 320 ,
"direction" : "Inbound"
} ,
"dependsOn" : [
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-masterNodeNSG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-mediaNodeASG', parameters('stackName')))]"
]
} ,
2025-04-22 11:46:24 +02:00
{
"type" : "Microsoft.Network/networkSecurityGroups" ,
"apiVersion" : "2023-11-01" ,
"name" : "[format('{0}-mediaNodeNSG', parameters('stackName'))]" ,
"location" : "[variables('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" : "*" ,
2026-09-29 14:57:02 +02:00
"destinationPortRange" : "50000-60000" ,
2025-04-22 11:46:24 +02:00
"access" : "Allow" ,
"priority" : 140 ,
"direction" : "Inbound"
}
2025-07-11 21:33:05 +02:00
} ,
{
"name" : "WebRTC_traffic_TCP" ,
"properties" : {
"protocol" : "Tcp" ,
"sourceAddressPrefix" : "*" ,
"sourcePortRange" : "*" ,
"destinationAddressPrefix" : "*" ,
"destinationPortRanges" : [
"50000" ,
"60000"
] ,
"access" : "Allow" ,
"priority" : 150 ,
"direction" : "Inbound"
}
2025-04-22 11:46:24 +02:00
}
]
}
} ,
{
"type" : "Microsoft.Network/applicationSecurityGroups" ,
"apiVersion" : "2024-03-01" ,
"name" : "[format('{0}-mediaNodeASG', parameters('stackName'))]" ,
"location" : "[variables('location')]"
} ,
{
"type" : "Microsoft.Network/networkSecurityGroups/securityRules" ,
"apiVersion" : "2023-11-01" ,
"name" : "[format('{0}/{1}', format('{0}-mediaNodeNSG', parameters('stackName')), 'loadBalancer_to_mediaNode_RTMP_INGRESS')]" ,
"properties" : {
"protocol" : "Tcp" ,
"sourceAddressPrefix" : "AzureLoadBalancer" ,
"sourcePortRange" : "*" ,
"destinationApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-mediaNodeASG', parameters('stackName')))]"
}
] ,
"destinationPortRange" : "1945" ,
"access" : "Allow" ,
2025-07-22 13:52:54 +02:00
"priority" : 160 ,
2025-04-22 11:46:24 +02:00
"direction" : "Inbound"
} ,
"dependsOn" : [
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-mediaNodeASG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-mediaNodeNSG', parameters('stackName')))]"
]
} ,
{
"type" : "Microsoft.Network/networkSecurityGroups/securityRules" ,
"apiVersion" : "2023-11-01" ,
"name" : "[format('{0}/{1}', format('{0}-mediaNodeNSG', parameters('stackName')), 'loadBalancer_to_mediaNode_HEALTHCHECK_INGRESS')]" ,
"properties" : {
"protocol" : "Tcp" ,
"sourceAddressPrefix" : "AzureLoadBalancer" ,
"sourcePortRange" : "*" ,
"destinationApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-mediaNodeASG', parameters('stackName')))]"
}
] ,
"destinationPortRange" : "9092" ,
"access" : "Allow" ,
2025-07-22 13:52:54 +02:00
"priority" : 170 ,
2025-04-22 11:46:24 +02:00
"direction" : "Inbound"
} ,
"dependsOn" : [
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-mediaNodeASG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-mediaNodeNSG', parameters('stackName')))]"
]
} ,
{
"type" : "Microsoft.Network/networkSecurityGroups/securityRules" ,
"apiVersion" : "2023-11-01" ,
2026-01-27 22:41:33 +01:00
"name" : "[format('{0}/{1}', format('{0}-mediaNodeNSG', parameters('stackName')), 'masterNode_to_mediaNode_SERVER_INGRESS')]" ,
2025-04-22 11:46:24 +02:00
"properties" : {
"protocol" : "Tcp" ,
2026-01-27 22:41:33 +01:00
"sourceApplicationSecurityGroups" : [
2025-04-22 11:46:24 +02:00
{
2026-01-27 22:41:33 +01:00
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
2025-04-22 11:46:24 +02:00
}
] ,
"sourcePortRange" : "*" ,
"destinationApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-mediaNodeASG', parameters('stackName')))]"
}
] ,
"destinationPortRange" : "7880" ,
"access" : "Allow" ,
2026-01-27 22:41:33 +01:00
"priority" : 200 ,
2025-04-22 11:46:24 +02:00
"direction" : "Inbound"
} ,
"dependsOn" : [
2026-01-27 22:41:33 +01:00
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]" ,
2025-04-22 11:46:24 +02:00
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-mediaNodeASG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-mediaNodeNSG', parameters('stackName')))]"
]
} ,
{
"type" : "Microsoft.Network/networkSecurityGroups/securityRules" ,
"apiVersion" : "2023-11-01" ,
2026-01-27 22:41:33 +01:00
"name" : "[format('{0}/{1}', format('{0}-mediaNodeNSG', parameters('stackName')), 'masterNode_to_mediaNode_CLIENT_INGRESS')]" ,
2025-04-22 11:46:24 +02:00
"properties" : {
"protocol" : "Tcp" ,
"sourceApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"sourcePortRange" : "*" ,
"destinationApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-mediaNodeASG', parameters('stackName')))]"
}
] ,
2026-01-27 22:41:33 +01:00
"destinationPortRange" : "8080" ,
2025-04-22 11:46:24 +02:00
"access" : "Allow" ,
2026-01-27 22:41:33 +01:00
"priority" : 210 ,
2025-04-22 11:46:24 +02:00
"direction" : "Inbound"
} ,
"dependsOn" : [
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-mediaNodeASG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-mediaNodeNSG', parameters('stackName')))]"
]
} ,
{
"type" : "Microsoft.Network/networkSecurityGroups/securityRules" ,
"apiVersion" : "2023-11-01" ,
2026-01-27 22:41:33 +01:00
"name" : "[format('{0}/{1}', format('{0}-mediaNodeNSG', parameters('stackName')), 'masterNode_to_mediaNode_TURN_TLS_INGRESS')]" ,
2025-04-22 11:46:24 +02:00
"properties" : {
"protocol" : "Tcp" ,
"sourceApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]"
}
] ,
"sourcePortRange" : "*" ,
"destinationApplicationSecurityGroups" : [
{
"id" : "[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-mediaNodeASG', parameters('stackName')))]"
}
] ,
2026-01-27 22:41:33 +01:00
"destinationPortRange" : "5349" ,
2025-04-22 11:46:24 +02:00
"access" : "Allow" ,
2026-01-27 22:41:33 +01:00
"priority" : 220 ,
2025-04-22 11:46:24 +02:00
"direction" : "Inbound"
} ,
"dependsOn" : [
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-masterNodeASG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/applicationSecurityGroups', format('{0}-mediaNodeASG', parameters('stackName')))]" ,
"[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-mediaNodeNSG', parameters('stackName')))]"
]
} ,
{
2025-05-13 11:01:22 +02:00
"condition" : "[equals(variables('isEmptyStorageAccountName'), true())]" ,
2025-04-22 11:46:24 +02:00
"type" : "Microsoft.Storage/storageAccounts" ,
"apiVersion" : "2023-01-01" ,
2025-04-23 13:26:33 +02:00
"name" : "[uniqueString(resourceGroup().id)]" ,
2025-04-22 11:46:24 +02:00
"location" : "[resourceGroup().location]" ,
"sku" : {
"name" : "Standard_LRS"
} ,
"kind" : "StorageV2" ,
"properties" : {
"accessTier" : "Cool" ,
"supportsHttpsTrafficOnly" : true
}
} ,
{
2025-05-13 11:01:22 +02:00
"condition" : "[equals(variables('isEmptyStorageAccountName'), true())]" ,
2025-04-22 11:46:24 +02:00
"type" : "Microsoft.Storage/storageAccounts/blobServices/containers" ,
"apiVersion" : "2023-01-01" ,
2025-04-23 13:26:33 +02:00
"name" : "[format('{0}/default/automation-locks', uniqueString(resourceGroup().id))]" ,
"properties" : {
"publicAccess" : "None"
} ,
"dependsOn" : [
"[resourceId('Microsoft.Storage/storageAccounts', uniqueString(resourceGroup().id))]"
]
} ,
{
2025-05-13 11:01:22 +02:00
"condition" : "[equals(variables('isEmptyStorageAccountName'), true())]" ,
2025-04-23 13:26:33 +02:00
"type" : "Microsoft.Storage/storageAccounts/blobServices/containers" ,
"apiVersion" : "2023-01-01" ,
2025-06-10 16:30:41 +02:00
"name" : "[if(variables('isEmptyAppDataContainerName'), format('{0}/default/openvidu-appdata', uniqueString(resourceGroup().id)), format('{0}/default/{1}', uniqueString(resourceGroup().id), parameters('appDataContainerName')))]" ,
"properties" : {
"publicAccess" : "None"
} ,
"dependsOn" : [
"[resourceId('Microsoft.Storage/storageAccounts', uniqueString(resourceGroup().id))]"
]
} ,
2025-04-22 11:46:24 +02:00
{
"type" : "Microsoft.Resources/deployments" ,
2026-06-16 21:16:00 +02:00
"apiVersion" : "2025-04-01" ,
2025-04-22 11:46:24 +02:00
"name" : "WebhookDeployment" ,
"properties" : {
"expressionEvaluationOptions" : {
"scope" : "inner"
} ,
"mode" : "Incremental" ,
"parameters" : {
2025-05-27 11:18:05 +02:00
"automationAccountName" : "[if(variables('isEmptyAutomationAccountName'), createObject('value', uniqueString(resourceGroup().id, resourceId('Microsoft.Compute/virtualMachines', format('{0}-VM-MasterNode1', parameters('stackName'))))), createObject('value', parameters('automationAccountName')))]" ,
2025-04-22 11:46:24 +02:00
"runbookName" : {
"value" : "scaleInRunbook"
} ,
"webhookName" : {
"value" : "webhookForScaleIn"
} ,
"WebhookExpiryTime" : {
"value" : "2035-03-30T00:00:00Z"
} ,
"_artifactsLocation" : {
2025-04-23 13:26:33 +02:00
"value" : "https://raw.githubusercontent.com/OpenVidu/openvidu/refs/heads/master/openvidu-deployment/pro/shared/scaleInRunbook.ps1"
2025-04-22 11:46:24 +02:00
}
} ,
"template" : {
"$schema" : "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#" ,
"contentVersion" : "1.0.0.0" ,
"parameters" : {
"automationAccountName" : {
"type" : "String" ,
"metadata" : {
"description" : "Automation account name"
}
} ,
"webhookName" : {
"type" : "String" ,
"metadata" : {
"description" : "Webhook Name"
}
} ,
"runbookName" : {
"type" : "String" ,
"metadata" : {
"description" : "Runbook Name for which webhook will be created"
}
} ,
"WebhookExpiryTime" : {
"type" : "String" ,
"metadata" : {
"description" : "Webhook Expiry time"
}
} ,
"_artifactsLocation" : {
2025-04-23 13:26:33 +02:00
"defaultValue" : "https://raw.githubusercontent.com/OpenVidu/openvidu/refs/heads/master/openvidu-deployment/pro/shared/scaleInRunbook.ps1" ,
2025-04-22 11:46:24 +02:00
"type" : "String" ,
"metadata" : {
"description" : "URI to artifacts location"
}
}
} ,
"resources" : [
{
"type" : "Microsoft.Automation/automationAccounts" ,
"apiVersion" : "2020-01-13-preview" ,
"name" : "[parameters('automationAccountName')]" ,
"location" : "[resourceGroup().location]" ,
"identity" : {
"type" : "SystemAssigned"
} ,
"properties" : {
"sku" : {
"name" : "Basic"
}
} ,
"resources" : [
{
"type" : "runbooks" ,
"apiVersion" : "2018-06-30" ,
"name" : "[parameters('runbookName')]" ,
"location" : "[resourceGroup().location]" ,
"dependsOn" : [
"[parameters('automationAccountName')]"
] ,
"properties" : {
"runbookType" : "PowerShell72" ,
"logProgress" : "true" ,
"description" : "Scale In Runbook" ,
"publishContentLink" : {
"uri" : "[parameters('_artifactsLocation')]" ,
"version" : "1.0.0.0"
}
}
} ,
{
"type" : "webhooks" ,
"apiVersion" : "2018-06-30" ,
"name" : "[parameters('webhookName')]" ,
"dependsOn" : [
"[parameters('automationAccountName')]" ,
"[parameters('runbookName')]"
] ,
"properties" : {
"isEnabled" : true ,
"expiryTime" : "[parameters('WebhookExpiryTime')]" ,
"runbook" : {
"name" : "[parameters('runbookName')]"
}
}
}
]
} ,
{
"type" : "Microsoft.Authorization/roleAssignments" ,
"apiVersion" : "2022-04-01" ,
"name" : "[guid(format('roleAutomationContributorAssignmentAutomationAccount{0}', parameters('automationAccountName')))]" ,
"properties" : {
"roleDefinitionId" : "[subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]" ,
"principalId" : "[reference(resourceId('Microsoft.Automation/automationAccounts', parameters('automationAccountName')), '2023-11-01', 'full').identity.principalId]" ,
"principalType" : "ServicePrincipal"
} ,
"dependsOn" : [
"[resourceId('Microsoft.Automation/automationAccounts', parameters('automationAccountName'))]"
]
}
] ,
"outputs" : {
"webhookUri" : {
"type" : "String" ,
"value" : "[reference(parameters('webhookName')).uri]"
} ,
"automationAccountId" : {
"type" : "string" ,
"value" : "[resourceId('Microsoft.Automation/automationAccounts', parameters('automationAccountName'))]"
} ,
"webhookId" : {
"type" : "string" ,
"value" : "[resourceId('Microsoft.Automation/automationAccounts/webhooks', parameters('automationAccountName'), parameters('webhookName'))]"
}
}
}
2025-05-27 11:18:05 +02:00
} ,
"dependsOn" : [
"[resourceId('Microsoft.Compute/virtualMachines', format('{0}-VM-MasterNode1', parameters('stackName')))]"
]
2025-04-22 11:46:24 +02:00
}
]
}