openvidu/openvidu-deployment/pro/ha/azure/cf-openvidu-ha.json

2290 lines
183 KiB
JSON
Raw Normal View History

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",
"version": "0.46.1.21595",
openvidu-deployment: the same defaults and parameter names on every cloud The templates of the five clouds had drifted apart. On AWS, Azure and Google Cloud: - 200 GB disk for the single node and the Master Nodes, the default where the template sets its size: Azure and Google Cloud raise it from 100 GB, as do the AWS High Availability Master Nodes. On Google Cloud an existing single node or Elastic Master Node keeps its disk when the template is applied again. - Google Cloud boots the latest Ubuntu 24.04 (the ubuntu-2404-lts image family) instead of an image pinned in December 2024; an existing single node or Master Node is not recreated when a newer image is published. - Same parameter names on every Terraform and Bicep template, with the old names deprecated but still honored (they win when set): rtcEngine on the Google Cloud single node, whose documented name was not applied, and masterNodeInstanceType on Google Cloud High Availability. - Azure no longer opens port 9000 (MinIO) on the single node and Elastic: MinIO does not run there, and the other clouds closed it in 3.9.0. - The Media Nodes install with the same arguments on every cloud: Azure passes --master-node-private-ip and no longer --enabled-modules, and the user-data names the variable MASTER_NODE_PRIVATE_IP everywhere. The CHANGELOG also lists the DigitalOcean and Oracle Cloud changes made in their repositories (v2compatibility by default, masterNodeInstanceType, the Oracle single node size and disk, rtcEngine).
2026-10-27 19:11:15 +01:00
"templateHash": "128203215953929559"
2025-04-22 11:46:24 +02:00
}
},
"parameters": {
"stackName": {
"type": "string",
"minLength": 3,
"maxLength": 15,
2025-04-22 11:46:24 +02:00
"metadata": {
"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",
"defaultValue": "letsencrypt",
2025-04-22 11:46:24 +02:00
"allowedValues": [
"selfsigned",
"owncert",
"letsencrypt"
],
"metadata": {
"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",
"defaultValue": "",
2025-04-22 11:46:24 +02:00
"metadata": {
"description": "Domain name for the OpenVidu Deployment."
2025-04-22 11:46:24 +02:00
}
},
"ownPublicCertificate": {
"type": "string",
"defaultValue": "",
"metadata": {
"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": {
"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
}
},
"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",
"defaultValue": "mediasoup",
2025-04-22 11:46:24 +02:00
"allowedValues": [
"pion",
"mediasoup"
],
"metadata": {
"description": "RTCEngine media engine to use"
}
},
"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",
"defaultValue": "Standard_B4s",
2025-04-22 11:46:24 +02:00
"metadata": {
"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",
openvidu-deployment: the same defaults and parameter names on every cloud The templates of the five clouds had drifted apart. On AWS, Azure and Google Cloud: - 200 GB disk for the single node and the Master Nodes, the default where the template sets its size: Azure and Google Cloud raise it from 100 GB, as do the AWS High Availability Master Nodes. On Google Cloud an existing single node or Elastic Master Node keeps its disk when the template is applied again. - Google Cloud boots the latest Ubuntu 24.04 (the ubuntu-2404-lts image family) instead of an image pinned in December 2024; an existing single node or Master Node is not recreated when a newer image is published. - Same parameter names on every Terraform and Bicep template, with the old names deprecated but still honored (they win when set): rtcEngine on the Google Cloud single node, whose documented name was not applied, and masterNodeInstanceType on Google Cloud High Availability. - Azure no longer opens port 9000 (MinIO) on the single node and Elastic: MinIO does not run there, and the other clouds closed it in 3.9.0. - The Media Nodes install with the same arguments on every cloud: Azure passes --master-node-private-ip and no longer --enabled-modules, and the user-data names the variable MASTER_NODE_PRIVATE_IP everywhere. The CHANGELOG also lists the DigitalOcean and Oracle Cloud changes made in their repositories (v2compatibility by default, masterNodeInstanceType, the Oracle single node size and disk, rtcEngine).
2026-10-27 19:11:15 +01:00
"defaultValue": 200,
2025-04-22 11:46:24 +02:00
"metadata": {
"description": "Size of the disk in GB"
}
},
"mediaNodeInstanceType": {
"type": "string",
"defaultValue": "Standard_B4s",
2025-04-22 11:46:24 +02:00
"metadata": {
"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": {
"type": "secureObject",
2025-04-22 11:46:24 +02:00
"metadata": {
"description": "SSH Key for the Virtual Machine."
}
},
"initialNumberOfMediaNodes": {
"type": "int",
"defaultValue": 1,
"metadata": {
openvidu-deployment: a fixed number of Media Nodes on AWS, Azure and Google Cloud Elastic and High Availability deployments on AWS, Azure and Google Cloud can run a fixed number of Media Nodes, as DigitalOcean and Oracle Cloud already could, with the same parameter: FixedNumberOfMediaNodes on AWS, fixedNumberOfMediaNodes on the other clouds. 0, the default, keeps autoscaling; a value greater than 0 runs exactly that many Media Nodes and ignores the initial, minimum, maximum and target CPU parameters. A redeploy with another value changes the number or switches mode, both ways. - AWS: the Auto Scaling Group's min, max and desired are the fixed number and the scaling policy is not created. A Media Node the number no longer needs is drained by the existing lifecycle hook, as in a scale-in. - Google Cloud: no autoscaler; the scale-in function, which now runs in both modes, keeps the managed instance group at the fixed number, abandoning an extra Media Node so it drains and deletes itself, or resizing the group up. The group's target size is set at creation only, the function's source is named by its hash so a code change redeploys it, and `moved` blocks keep existing autoscalers when the template is applied again. - Azure: the autoscale setting stays, pinned to the fixed number (minimum = maximum = default). Azure refuses an autoscale minimum below the Media Nodes running, which protect themselves from scale-in, so lowering it is a manual drain first (manual scale, then stop_media_node.sh on each extra Media Node) and then the redeploy; the CHANGELOG and the documentation describe it. The portal's form gets the field. Verified live with ov-cloud-tester's sc-fixed-media-nodes scenario on Elastic (Google Cloud, AWS and Azure): deploy with 2, lower to 1 (the Media Node removed served its Room through a 1200 s hold and left once it was closed), back to autoscaling with no Media Node added or removed.
2026-10-27 19:09:59 +01:00
"description": "Number of initial media nodes to deploy. Ignored when fixedNumberOfMediaNodes > 0"
2025-04-22 11:46:24 +02:00
}
},
"minNumberOfMediaNodes": {
"type": "int",
"defaultValue": 1,
"metadata": {
openvidu-deployment: a fixed number of Media Nodes on AWS, Azure and Google Cloud Elastic and High Availability deployments on AWS, Azure and Google Cloud can run a fixed number of Media Nodes, as DigitalOcean and Oracle Cloud already could, with the same parameter: FixedNumberOfMediaNodes on AWS, fixedNumberOfMediaNodes on the other clouds. 0, the default, keeps autoscaling; a value greater than 0 runs exactly that many Media Nodes and ignores the initial, minimum, maximum and target CPU parameters. A redeploy with another value changes the number or switches mode, both ways. - AWS: the Auto Scaling Group's min, max and desired are the fixed number and the scaling policy is not created. A Media Node the number no longer needs is drained by the existing lifecycle hook, as in a scale-in. - Google Cloud: no autoscaler; the scale-in function, which now runs in both modes, keeps the managed instance group at the fixed number, abandoning an extra Media Node so it drains and deletes itself, or resizing the group up. The group's target size is set at creation only, the function's source is named by its hash so a code change redeploys it, and `moved` blocks keep existing autoscalers when the template is applied again. - Azure: the autoscale setting stays, pinned to the fixed number (minimum = maximum = default). Azure refuses an autoscale minimum below the Media Nodes running, which protect themselves from scale-in, so lowering it is a manual drain first (manual scale, then stop_media_node.sh on each extra Media Node) and then the redeploy; the CHANGELOG and the documentation describe it. The portal's form gets the field. Verified live with ov-cloud-tester's sc-fixed-media-nodes scenario on Elastic (Google Cloud, AWS and Azure): deploy with 2, lower to 1 (the Media Node removed served its Room through a 1200 s hold and left once it was closed), back to autoscaling with no Media Node added or removed.
2026-10-27 19:09:59 +01:00
"description": "Minimum number of media nodes to deploy. Ignored when fixedNumberOfMediaNodes > 0"
2025-04-22 11:46:24 +02:00
}
},
"maxNumberOfMediaNodes": {
"type": "int",
"defaultValue": 5,
"metadata": {
openvidu-deployment: a fixed number of Media Nodes on AWS, Azure and Google Cloud Elastic and High Availability deployments on AWS, Azure and Google Cloud can run a fixed number of Media Nodes, as DigitalOcean and Oracle Cloud already could, with the same parameter: FixedNumberOfMediaNodes on AWS, fixedNumberOfMediaNodes on the other clouds. 0, the default, keeps autoscaling; a value greater than 0 runs exactly that many Media Nodes and ignores the initial, minimum, maximum and target CPU parameters. A redeploy with another value changes the number or switches mode, both ways. - AWS: the Auto Scaling Group's min, max and desired are the fixed number and the scaling policy is not created. A Media Node the number no longer needs is drained by the existing lifecycle hook, as in a scale-in. - Google Cloud: no autoscaler; the scale-in function, which now runs in both modes, keeps the managed instance group at the fixed number, abandoning an extra Media Node so it drains and deletes itself, or resizing the group up. The group's target size is set at creation only, the function's source is named by its hash so a code change redeploys it, and `moved` blocks keep existing autoscalers when the template is applied again. - Azure: the autoscale setting stays, pinned to the fixed number (minimum = maximum = default). Azure refuses an autoscale minimum below the Media Nodes running, which protect themselves from scale-in, so lowering it is a manual drain first (manual scale, then stop_media_node.sh on each extra Media Node) and then the redeploy; the CHANGELOG and the documentation describe it. The portal's form gets the field. Verified live with ov-cloud-tester's sc-fixed-media-nodes scenario on Elastic (Google Cloud, AWS and Azure): deploy with 2, lower to 1 (the Media Node removed served its Room through a 1200 s hold and left once it was closed), back to autoscaling with no Media Node added or removed.
2026-10-27 19:09:59 +01:00
"description": "Maximum number of media nodes to deploy. Ignored when fixedNumberOfMediaNodes > 0"
2025-04-22 11:46:24 +02:00
}
},
"scaleTargetCPU": {
"type": "int",
"defaultValue": 50,
"metadata": {
openvidu-deployment: a fixed number of Media Nodes on AWS, Azure and Google Cloud Elastic and High Availability deployments on AWS, Azure and Google Cloud can run a fixed number of Media Nodes, as DigitalOcean and Oracle Cloud already could, with the same parameter: FixedNumberOfMediaNodes on AWS, fixedNumberOfMediaNodes on the other clouds. 0, the default, keeps autoscaling; a value greater than 0 runs exactly that many Media Nodes and ignores the initial, minimum, maximum and target CPU parameters. A redeploy with another value changes the number or switches mode, both ways. - AWS: the Auto Scaling Group's min, max and desired are the fixed number and the scaling policy is not created. A Media Node the number no longer needs is drained by the existing lifecycle hook, as in a scale-in. - Google Cloud: no autoscaler; the scale-in function, which now runs in both modes, keeps the managed instance group at the fixed number, abandoning an extra Media Node so it drains and deletes itself, or resizing the group up. The group's target size is set at creation only, the function's source is named by its hash so a code change redeploys it, and `moved` blocks keep existing autoscalers when the template is applied again. - Azure: the autoscale setting stays, pinned to the fixed number (minimum = maximum = default). Azure refuses an autoscale minimum below the Media Nodes running, which protect themselves from scale-in, so lowering it is a manual drain first (manual scale, then stop_media_node.sh on each extra Media Node) and then the redeploy; the CHANGELOG and the documentation describe it. The portal's form gets the field. Verified live with ov-cloud-tester's sc-fixed-media-nodes scenario on Elastic (Google Cloud, AWS and Azure): deploy with 2, lower to 1 (the Media Node removed served its Room through a 1200 s hold and left once it was closed), back to autoscaling with no Media Node added or removed.
2026-10-27 19:09:59 +01:00
"description": "Target CPU percentage to scale up or down. Ignored when fixedNumberOfMediaNodes > 0"
}
},
"fixedNumberOfMediaNodes": {
"type": "int",
"defaultValue": 0,
"minValue": 0,
"metadata": {
"description": "If > 0, keeps this fixed number of media nodes: autoscaling is pinned to it (minimum = maximum = this number) and initialNumberOfMediaNodes, minNumberOfMediaNodes, maxNumberOfMediaNodes and scaleTargetCPU are ignored. When it is lowered, the extra media nodes are drained gracefully, one at a time, by the scale-in runbook. If 0 (default), the deployment is elastic and autoscaling is enabled"
2025-04-22 11:46:24 +02:00
}
},
"additionalInstallFlags": {
"type": "string",
"defaultValue": ""
},
2025-04-22 11:46:24 +02:00
"datetime": {
"type": "string",
"defaultValue": "[utcNow('u')]"
},
"automationAccountName": {
"type": "string",
"defaultValue": "",
2025-04-22 11:46:24 +02:00
"metadata": {
"description": "Automation Account Name to create a runbook inside it for scale in"
}
},
"scaleInWebhookNameSeed": {
"type": "string",
"defaultValue": "[utcNow()]",
"metadata": {
"description": "Do not set it. Azure Automation returns the URI of a webhook only when it creates it, and the scale-in action group needs that URI on every deployment, so every deployment creates the scale-in webhook under a new name: from this value (a new one each time it is not given) and the name of the deployment (a new one on every redeploy from the portal)."
}
},
"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."
}
},
"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": {
"isEmptyIp": "[equals(parameters('publicIpAddressObject').newOrExistingOrNone, 'none')]",
"isEmptyDomain": "[equals(parameters('domainName'), '')]",
"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'))]",
"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",
"offer": "ubuntu-24_04-lts",
"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'))]",
"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",
"offer": "ubuntu-24_04-lts",
"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'))]",
"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]",
"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 \"dashbo
"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/\" \"${CLUSTE
"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# Update shared secret\naz keyvault secret set --vault-name ${keyVaultName} --name REDIS-PASSWORD --value $REDIS_PASSWORD\naz keyvault secret set --vault-name ${keyVaultName} --name DOMAIN-NAME --value $DOMAIN_NAME\naz keyvault secret set --vault-name ${keyVaultName} --name OPENVIDU-RTC-ENGINE --value $OPENVIDU_RTC_ENGINE\naz keyvault secret set --vault-name ${keyVaultName} --name OPENVIDU-PRO-LICENSE --value $OPENVIDU_PRO_LICENSE\naz keyvault secret set --vault-name ${keyVaultName} --name MONGO-ADMIN-USERNAME --value $MONGO_ADMIN_USERNAME\naz keyvault secret set --vault-name ${keyVaultName} --name MONGO-ADMIN-PASSWORD --value $MONGO_ADMIN_PASSWORD\naz keyvault secret set --vault-name ${keyVaultName} --name MONGO-REPLICA-SET-KEY --value $MONGO_REPLICA_SET_KEY\naz keyvault secret set --vault-name ${keyVaultName} --name MINIO-ACCESS-KEY --value $MINIO_ACCESS_KEY\naz keyvault secret set --vault-name ${keyVaultName} --name MINIO-SECRET-KEY --value $MINIO_SECRET_KEY\naz keyvault secret set --vault-name ${keyVaultName} --name DASHBOARD-ADMIN-USERNAME --value $DASHBOARD_ADMIN_USERNAME\naz keyvault secret set --vault-name ${keyVaultName} --name DASHBOARD-ADMIN-PASSWORD --value $DASHBOARD_ADMIN_
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",
"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",
"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",
"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'))]",
"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",
openvidu-deployment: the same defaults and parameter names on every cloud The templates of the five clouds had drifted apart. On AWS, Azure and Google Cloud: - 200 GB disk for the single node and the Master Nodes, the default where the template sets its size: Azure and Google Cloud raise it from 100 GB, as do the AWS High Availability Master Nodes. On Google Cloud an existing single node or Elastic Master Node keeps its disk when the template is applied again. - Google Cloud boots the latest Ubuntu 24.04 (the ubuntu-2404-lts image family) instead of an image pinned in December 2024; an existing single node or Master Node is not recreated when a newer image is published. - Same parameter names on every Terraform and Bicep template, with the old names deprecated but still honored (they win when set): rtcEngine on the Google Cloud single node, whose documented name was not applied, and masterNodeInstanceType on Google Cloud High Availability. - Azure no longer opens port 9000 (MinIO) on the single node and Elastic: MinIO does not run there, and the other clouds closed it in 3.9.0. - The Media Nodes install with the same arguments on every cloud: Azure passes --master-node-private-ip and no longer --enabled-modules, and the user-data names the variable MASTER_NODE_PRIVATE_IP everywhere. The CHANGELOG also lists the DigitalOcean and Oracle Cloud changes made in their repositories (v2compatibility by default, masterNodeInstanceType, the Oracle single node size and disk, rtcEngine).
2026-10-27 19:11:15 +01: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)\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\"--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",
"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",
"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\" in\n +PONG*|-NOAUTH*) return 0 ;;\n esac\n done\n return 1\n}\n\n# Request the replacement of this node ($1 = reason). Gated on a master answering, except\n# in the bootstrap phase before the master addresses are known.
"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",
"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",
"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'))]",
"isEmptyAutomationAccountName": "[equals(parameters('automationAccountName'), '')]",
2025-04-22 11:46:24 +02:00
"lbName": "[format('{0}-loadBalancer', parameters('stackName'))]",
"lbFrontEndName": "LoadBalancerFrontEnd",
"lbBackendPoolNameMasterNode": "LoadBalancerBackEndMasterNode",
"ipExists": "[equals(parameters('publicIpAddressObject').newOrExistingOrNone, 'existing')]",
"ipNew": "[equals(parameters('publicIpAddressObject').newOrExistingOrNone, 'new')]",
"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'))]"
},
"isEmptyStorageAccountName": "[equals(parameters('storageAccountName'), '')]",
"isEmptyAppDataContainerName": "[equals(parameters('appDataContainerName'), '')]"
2025-04-22 11:46:24 +02:00
},
"resources": [
{
"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": [
{
"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"
]
}
},
{
"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": [
"[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": {
"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]"
},
"userData": "[base64(reduce(items(createObject('base64install', base64(reduce(items(createObject('publicIPId', variables('publicIPId'), 'domainName', parameters('domainName'), 'certificateType', parameters('certificateType'), 'ownPublicCertificate', parameters('ownPublicCertificate'), 'ownPrivateCertificate', parameters('ownPrivateCertificate'), 'openviduLicense', parameters('openviduLicense'), 'rtcEngine', parameters('rtcEngine'), 'initialMeetAdminPassword', parameters('initialMeetAdminPassword'), 'initialMeetApiKey', parameters('initialMeetApiKey'), 'keyVaultName', variables('keyVaultName'), 'masterIdentityClientId', reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-master-identity', parameters('stackName'))), '2023-01-31').clientId, 'masterNodeNum', '1', 'additionalInstallFlags', parameters('additionalInstallFlags'))), createObject('value', variables('installScriptTemplateMaster')), lambda('curr', 'next', createObject('value', replace(lambdaVariables('curr').value, format('${{{0}}}', lambdaVariables('next').key), lambdaVariables('next').value)))).value), 'base64after_install', base64(reduce(items(createObject('publicIPId', variables('publicIPId'), 'domainName', parameters('domainName'), 'certificateType', parameters('certificateType'), 'ownPublicCertificate', parameters('ownPublicCertificate'), 'ownPrivateCertificate', parameters('ownPrivateCertificate'), 'openviduLicense', parameters('openviduLicense'), 'rtcEngine', parameters('rtcEngine'), 'initialMeetAdminPassword', parameters('initialMeetAdminPassword'), 'initialMeetApiKey', parameters('initialMeetApiKey'), 'keyVaultName', variables('keyVaultName'), 'masterIdentityClientId', reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-master-identity', parameters('stackName'))), '2023-01-31').clientId, 'masterNodeNum', '1', 'additionalInstallFlags', parameters('additionalInstallFlags'))), createObject('value', variables('after_installScriptTemplateMaster')), lambda('curr', 'next', createObject('value', replace(lambdaVariables('curr').value, format('${{{0}}}', lambdaVariables('next').key), lambdaVariables('next').value)))).value), 'base64get_public_ip', base64(reduce(items(createObject('publicIPId', variables('publicIPId'), 'domainName', parameters('domainName'), 'certificateType', parameters('certificateType'), 'ownPublicCertificate', parameters('ownPublicCertificate'), 'ownPrivateCertificate', parameters('ownPrivateCertificate'), 'openviduLicense', parameters('openviduLicense'), 'rtcEngine', parameters('rtcEngine'), 'initialMeetAdminPassword', parameters('initialMeetAdminPassword'), 'initialMeetApiKey', parameters('initialMeetApiKey'), 'keyVaultName', variables('keyVaultName'), 'masterIdentityClientId', reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-master-identity', parameters('stackName'))), '2023-01-31').clientId, 'masterNodeNum', '1', 'additionalInstallFlags', parameters('additionalInstallFlags'))), createObject('value', variables('get_public_ip')), lambda('curr', 'next', createObject('value', replace(lambdaVariables('curr').value, format('${{{0}}}', lambdaVariables('next').key), lambdaVariables('next').value)))).value), 'base64update_config_from_secret', base64(reduce(items(createObject('publicIPId', variables('publicIPId'), 'domainName', parameters('domainName'), 'certificateType', parameters('certificateType'), 'ownPublicCertificate', parameters('ownPublicCertificate'), 'ownPrivateCertificate', parameters('ownPrivateCertificate'), 'openviduLicense', parameters('openviduLicense'), 'rtcEngine', parameters('rtcEngine'), 'initialMeetAdminPassword', parameters('initialMeetAdminPassword'), 'initialMeetApiKey', parameters('initialMeetApiKey'), 'keyVaultName', variables('keyVaultName'), 'masterIdentityClientId', reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-master-identity', parameters('stackName'))), '2023-01-31').clientId, 'masterNodeNum', '1', 'additionalInstallFlags', parameters('additionalInstallFlags'))), createObje
2025-04-22 11:46:24 +02:00
},
"dependsOn": [
"[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-master-identity', parameters('stackName')))]",
"[resourceId('Microsoft.Network/networkInterfaces', format('{0}-masterNodeNetInterface1', parameters('stackName')))]",
"[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIpAddressObject').name)]",
"[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": {
"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]"
},
"userData": "[base64(reduce(items(createObject('base64install', base64(reduce(items(createObject('publicIPId', variables('publicIPId'), 'domainName', parameters('domainName'), 'certificateType', parameters('certificateType'), 'ownPublicCertificate', parameters('ownPublicCertificate'), 'ownPrivateCertificate', parameters('ownPrivateCertificate'), 'openviduLicense', parameters('openviduLicense'), 'rtcEngine', parameters('rtcEngine'), 'initialMeetAdminPassword', parameters('initialMeetAdminPassword'), 'initialMeetApiKey', parameters('initialMeetApiKey'), 'keyVaultName', variables('keyVaultName'), 'masterIdentityClientId', reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-master-identity', parameters('stackName'))), '2023-01-31').clientId, 'masterNodeNum', '2', 'additionalInstallFlags', parameters('additionalInstallFlags'))), createObject('value', variables('installScriptTemplateMaster')), lambda('curr', 'next', createObject('value', replace(lambdaVariables('curr').value, format('${{{0}}}', lambdaVariables('next').key), lambdaVariables('next').value)))).value), 'base64after_install', base64(reduce(items(createObject('publicIPId', variables('publicIPId'), 'domainName', parameters('domainName'), 'certificateType', parameters('certificateType'), 'ownPublicCertificate', parameters('ownPublicCertificate'), 'ownPrivateCertificate', parameters('ownPrivateCertificate'), 'openviduLicense', parameters('openviduLicense'), 'rtcEngine', parameters('rtcEngine'), 'initialMeetAdminPassword', parameters('initialMeetAdminPassword'), 'initialMeetApiKey', parameters('initialMeetApiKey'), 'keyVaultName', variables('keyVaultName'), 'masterIdentityClientId', reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-master-identity', parameters('stackName'))), '2023-01-31').clientId, 'masterNodeNum', '1', 'additionalInstallFlags', parameters('additionalInstallFlags'))), createObject('value', variables('after_installScriptTemplateMaster')), lambda('curr', 'next', createObject('value', replace(lambdaVariables('curr').value, format('${{{0}}}', lambdaVariables('next').key), lambdaVariables('next').value)))).value), 'base64get_public_ip', base64(reduce(items(createObject('publicIPId', variables('publicIPId'), 'domainName', parameters('domainName'), 'certificateType', parameters('certificateType'), 'ownPublicCertificate', parameters('ownPublicCertificate'), 'ownPrivateCertificate', parameters('ownPrivateCertificate'), 'openviduLicense', parameters('openviduLicense'), 'rtcEngine', parameters('rtcEngine'), 'initialMeetAdminPassword', parameters('initialMeetAdminPassword'), 'initialMeetApiKey', parameters('initialMeetApiKey'), 'keyVaultName', variables('keyVaultName'), 'masterIdentityClientId', reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-master-identity', parameters('stackName'))), '2023-01-31').clientId, 'masterNodeNum', '1', 'additionalInstallFlags', parameters('additionalInstallFlags'))), createObject('value', variables('get_public_ip')), lambda('curr', 'next', createObject('value', replace(lambdaVariables('curr').value, format('${{{0}}}', lambdaVariables('next').key), lambdaVariables('next').value)))).value), 'base64update_config_from_secret', base64(reduce(items(createObject('publicIPId', variables('publicIPId'), 'domainName', parameters('domainName'), 'certificateType', parameters('certificateType'), 'ownPublicCertificate', parameters('ownPublicCertificate'), 'ownPrivateCertificate', parameters('ownPrivateCertificate'), 'openviduLicense', parameters('openviduLicense'), 'rtcEngine', parameters('rtcEngine'), 'initialMeetAdminPassword', parameters('initialMeetAdminPassword'), 'initialMeetApiKey', parameters('initialMeetApiKey'), 'keyVaultName', variables('keyVaultName'), 'masterIdentityClientId', reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-master-identity', parameters('stackName'))), '2023-01-31').clientId, 'masterNodeNum', '1', 'additionalInstallFlags', parameters('additionalInstallFlags'))), createObje
2025-04-22 11:46:24 +02:00
},
"dependsOn": [
"[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')))]",
"[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIpAddressObject').name)]",
"[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": {
"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]"
},
"userData": "[base64(reduce(items(createObject('base64install', base64(reduce(items(createObject('publicIPId', variables('publicIPId'), 'domainName', parameters('domainName'), 'certificateType', parameters('certificateType'), 'ownPublicCertificate', parameters('ownPublicCertificate'), 'ownPrivateCertificate', parameters('ownPrivateCertificate'), 'openviduLicense', parameters('openviduLicense'), 'rtcEngine', parameters('rtcEngine'), 'initialMeetAdminPassword', parameters('initialMeetAdminPassword'), 'initialMeetApiKey', parameters('initialMeetApiKey'), 'keyVaultName', variables('keyVaultName'), 'masterIdentityClientId', reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-master-identity', parameters('stackName'))), '2023-01-31').clientId, 'masterNodeNum', '3', 'additionalInstallFlags', parameters('additionalInstallFlags'))), createObject('value', variables('installScriptTemplateMaster')), lambda('curr', 'next', createObject('value', replace(lambdaVariables('curr').value, format('${{{0}}}', lambdaVariables('next').key), lambdaVariables('next').value)))).value), 'base64after_install', base64(reduce(items(createObject('publicIPId', variables('publicIPId'), 'domainName', parameters('domainName'), 'certificateType', parameters('certificateType'), 'ownPublicCertificate', parameters('ownPublicCertificate'), 'ownPrivateCertificate', parameters('ownPrivateCertificate'), 'openviduLicense', parameters('openviduLicense'), 'rtcEngine', parameters('rtcEngine'), 'initialMeetAdminPassword', parameters('initialMeetAdminPassword'), 'initialMeetApiKey', parameters('initialMeetApiKey'), 'keyVaultName', variables('keyVaultName'), 'masterIdentityClientId', reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-master-identity', parameters('stackName'))), '2023-01-31').clientId, 'masterNodeNum', '1', 'additionalInstallFlags', parameters('additionalInstallFlags'))), createObject('value', variables('after_installScriptTemplateMaster')), lambda('curr', 'next', createObject('value', replace(lambdaVariables('curr').value, format('${{{0}}}', lambdaVariables('next').key), lambdaVariables('next').value)))).value), 'base64get_public_ip', base64(reduce(items(createObject('publicIPId', variables('publicIPId'), 'domainName', parameters('domainName'), 'certificateType', parameters('certificateType'), 'ownPublicCertificate', parameters('ownPublicCertificate'), 'ownPrivateCertificate', parameters('ownPrivateCertificate'), 'openviduLicense', parameters('openviduLicense'), 'rtcEngine', parameters('rtcEngine'), 'initialMeetAdminPassword', parameters('initialMeetAdminPassword'), 'initialMeetApiKey', parameters('initialMeetApiKey'), 'keyVaultName', variables('keyVaultName'), 'masterIdentityClientId', reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-master-identity', parameters('stackName'))), '2023-01-31').clientId, 'masterNodeNum', '1', 'additionalInstallFlags', parameters('additionalInstallFlags'))), createObject('value', variables('get_public_ip')), lambda('curr', 'next', createObject('value', replace(lambdaVariables('curr').value, format('${{{0}}}', lambdaVariables('next').key), lambdaVariables('next').value)))).value), 'base64update_config_from_secret', base64(reduce(items(createObject('publicIPId', variables('publicIPId'), 'domainName', parameters('domainName'), 'certificateType', parameters('certificateType'), 'ownPublicCertificate', parameters('ownPublicCertificate'), 'ownPrivateCertificate', parameters('ownPrivateCertificate'), 'openviduLicense', parameters('openviduLicense'), 'rtcEngine', parameters('rtcEngine'), 'initialMeetAdminPassword', parameters('initialMeetAdminPassword'), 'initialMeetApiKey', parameters('initialMeetApiKey'), 'keyVaultName', variables('keyVaultName'), 'masterIdentityClientId', reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-master-identity', parameters('stackName'))), '2023-01-31').clientId, 'masterNodeNum', '1', 'additionalInstallFlags', parameters('additionalInstallFlags'))), createObje
2025-04-22 11:46:24 +02:00
},
"dependsOn": [
"[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')))]",
"[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIpAddressObject').name)]",
"[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": {
"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]"
},
"userData": "[base64(reduce(items(createObject('base64install', base64(reduce(items(createObject('publicIPId', variables('publicIPId'), 'domainName', parameters('domainName'), 'certificateType', parameters('certificateType'), 'ownPublicCertificate', parameters('ownPublicCertificate'), 'ownPrivateCertificate', parameters('ownPrivateCertificate'), 'openviduLicense', parameters('openviduLicense'), 'rtcEngine', parameters('rtcEngine'), 'initialMeetAdminPassword', parameters('initialMeetAdminPassword'), 'initialMeetApiKey', parameters('initialMeetApiKey'), 'keyVaultName', variables('keyVaultName'), 'masterIdentityClientId', reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-master-identity', parameters('stackName'))), '2023-01-31').clientId, 'masterNodeNum', '4', 'additionalInstallFlags', parameters('additionalInstallFlags'))), createObject('value', variables('installScriptTemplateMaster')), lambda('curr', 'next', createObject('value', replace(lambdaVariables('curr').value, format('${{{0}}}', lambdaVariables('next').key), lambdaVariables('next').value)))).value), 'base64after_install', base64(reduce(items(createObject('publicIPId', variables('publicIPId'), 'domainName', parameters('domainName'), 'certificateType', parameters('certificateType'), 'ownPublicCertificate', parameters('ownPublicCertificate'), 'ownPrivateCertificate', parameters('ownPrivateCertificate'), 'openviduLicense', parameters('openviduLicense'), 'rtcEngine', parameters('rtcEngine'), 'initialMeetAdminPassword', parameters('initialMeetAdminPassword'), 'initialMeetApiKey', parameters('initialMeetApiKey'), 'keyVaultName', variables('keyVaultName'), 'masterIdentityClientId', reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-master-identity', parameters('stackName'))), '2023-01-31').clientId, 'masterNodeNum', '1', 'additionalInstallFlags', parameters('additionalInstallFlags'))), createObject('value', variables('after_installScriptTemplateMaster')), lambda('curr', 'next', createObject('value', replace(lambdaVariables('curr').value, format('${{{0}}}', lambdaVariables('next').key), lambdaVariables('next').value)))).value), 'base64get_public_ip', base64(reduce(items(createObject('publicIPId', variables('publicIPId'), 'domainName', parameters('domainName'), 'certificateType', parameters('certificateType'), 'ownPublicCertificate', parameters('ownPublicCertificate'), 'ownPrivateCertificate', parameters('ownPrivateCertificate'), 'openviduLicense', parameters('openviduLicense'), 'rtcEngine', parameters('rtcEngine'), 'initialMeetAdminPassword', parameters('initialMeetAdminPassword'), 'initialMeetApiKey', parameters('initialMeetApiKey'), 'keyVaultName', variables('keyVaultName'), 'masterIdentityClientId', reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-master-identity', parameters('stackName'))), '2023-01-31').clientId, 'masterNodeNum', '1', 'additionalInstallFlags', parameters('additionalInstallFlags'))), createObject('value', variables('get_public_ip')), lambda('curr', 'next', createObject('value', replace(lambdaVariables('curr').value, format('${{{0}}}', lambdaVariables('next').key), lambdaVariables('next').value)))).value), 'base64update_config_from_secret', base64(reduce(items(createObject('publicIPId', variables('publicIPId'), 'domainName', parameters('domainName'), 'certificateType', parameters('certificateType'), 'ownPublicCertificate', parameters('ownPublicCertificate'), 'ownPrivateCertificate', parameters('ownPrivateCertificate'), 'openviduLicense', parameters('openviduLicense'), 'rtcEngine', parameters('rtcEngine'), 'initialMeetAdminPassword', parameters('initialMeetAdminPassword'), 'initialMeetApiKey', parameters('initialMeetApiKey'), 'keyVaultName', variables('keyVaultName'), 'masterIdentityClientId', reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-master-identity', parameters('stackName'))), '2023-01-31').clientId, 'masterNodeNum', '1', 'additionalInstallFlags', parameters('additionalInstallFlags'))), createObje
2025-04-22 11:46:24 +02:00
},
"dependsOn": [
"[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')))]",
"[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIpAddressObject').name)]",
"[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')]",
"storageAccount": "[if(variables('isEmptyStorageAccountName'), uniqueString(resourceGroup().id), parameters('storageAccountName'))]"
2025-04-22 11:46:24 +02:00
},
"identity": {
"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"
2025-04-22 11:46:24 +02:00
},
"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')))]"
}
}
}
]
},
"userData": "[base64(reduce(items(createObject('base64install', base64(reduce(items(createObject('privateIPMasterNode1', reference(resourceId('Microsoft.Network/networkInterfaces', format('{0}-masterNodeNetInterface1', parameters('stackName'))), '2023-11-01').ipConfigurations[0].properties.privateIPAddress, 'privateIPMasterNode2', reference(resourceId('Microsoft.Network/networkInterfaces', format('{0}-masterNodeNetInterface2', parameters('stackName'))), '2023-11-01').ipConfigurations[0].properties.privateIPAddress, 'privateIPMasterNode3', reference(resourceId('Microsoft.Network/networkInterfaces', format('{0}-masterNodeNetInterface3', parameters('stackName'))), '2023-11-01').ipConfigurations[0].properties.privateIPAddress, 'privateIPMasterNode4', reference(resourceId('Microsoft.Network/networkInterfaces', format('{0}-masterNodeNetInterface4', parameters('stackName'))), '2023-11-01').ipConfigurations[0].properties.privateIPAddress, 'keyVaultName', variables('keyVaultName'))), createObject('value', variables('installScriptTemplateMedia')), lambda('curr', 'next', createObject('value', replace(lambdaVariables('curr').value, format('${{{0}}}', lambdaVariables('next').key), lambdaVariables('next').value)))).value), 'base64stop', base64(reduce(items(createObject('mediaIdentityClientId', reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-media-identity', parameters('stackName'))), '2023-01-31').clientId, 'subscriptionId', subscription().subscriptionId, 'resourceGroupName', resourceGroup().name, 'vmScaleSetName', format('{0}-mediaNodeScaleSet', parameters('stackName')), 'storageAccountName', if(variables('isEmptyStorageAccountName'), uniqueString(resourceGroup().id), parameters('storageAccountName')))), createObject('value', variables('stop_media_nodesScriptMediaTemplate')), lambda('curr', 'next', createObject('value', replace(lambdaVariables('curr').value, format('${{{0}}}', lambdaVariables('next').key), lambdaVariables('next').value)))).value), 'base64mediaHealth', variables('base64openviduMediaHealth'), 'base64mediaHealthUnit', variables('base64openviduMediaHealthUnit'), 'base64mediaReplace', base64(reduce(items(createObject('mediaIdentityClientId', reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-media-identity', parameters('stackName'))), '2023-01-31').clientId, 'subscriptionId', subscription().subscriptionId, 'resourceGroupName', resourceGroup().name, 'vmScaleSetName', format('{0}-mediaNodeScaleSet', parameters('stackName')), 'storageAccountName', if(variables('isEmptyStorageAccountName'), uniqueString(resourceGroup().id), parameters('storageAccountName')))), createObject('value', variables('media_replaceScriptMediaTemplate')), lambda('curr', 'next', createObject('value', replace(lambdaVariables('curr').value, format('${{{0}}}', lambdaVariables('next').key), lambdaVariables('next').value)))).value), 'privateIPMasterNode1', reference(resourceId('Microsoft.Network/networkInterfaces', format('{0}-masterNodeNetInterface1', parameters('stackName'))), '2023-11-01').ipConfigurations[0].properties.privateIPAddress, 'privateIPMasterNode2', reference(resourceId('Microsoft.Network/networkInterfaces', format('{0}-masterNodeNetInterface2', parameters('stackName'))), '2023-11-01').ipConfigurations[0].properties.privateIPAddress, 'privateIPMasterNode3', reference(resourceId('Microsoft.Network/networkInterfaces', format('{0}-masterNodeNetInterface3', parameters('stackName'))), '2023-11-01').ipConfigurations[0].properties.privateIPAddress, 'privateIPMasterNode4', reference(resourceId('Microsoft.Network/networkInterfaces', format('{0}-masterNodeNetInterface4', parameters('stackName'))), '2023-11-01').ipConfigurations[0].properties.privateIPAddress, 'mediaIdentityClientId', reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-media-identity', parameters('stackName'))), '2023-01-31').clientId, 'resourceGroupName', resourceGroup().name, 'vmScaleSetName', format('{0}-mediaNodeScaleSet', parameters('stackName')), 'keyVaultName', variab
2025-04-22 11:46:24 +02:00
}
},
"dependsOn": [
"[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')))]",
"[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": {
openvidu-deployment: a fixed number of Media Nodes on AWS, Azure and Google Cloud Elastic and High Availability deployments on AWS, Azure and Google Cloud can run a fixed number of Media Nodes, as DigitalOcean and Oracle Cloud already could, with the same parameter: FixedNumberOfMediaNodes on AWS, fixedNumberOfMediaNodes on the other clouds. 0, the default, keeps autoscaling; a value greater than 0 runs exactly that many Media Nodes and ignores the initial, minimum, maximum and target CPU parameters. A redeploy with another value changes the number or switches mode, both ways. - AWS: the Auto Scaling Group's min, max and desired are the fixed number and the scaling policy is not created. A Media Node the number no longer needs is drained by the existing lifecycle hook, as in a scale-in. - Google Cloud: no autoscaler; the scale-in function, which now runs in both modes, keeps the managed instance group at the fixed number, abandoning an extra Media Node so it drains and deletes itself, or resizing the group up. The group's target size is set at creation only, the function's source is named by its hash so a code change redeploys it, and `moved` blocks keep existing autoscalers when the template is applied again. - Azure: the autoscale setting stays, pinned to the fixed number (minimum = maximum = default). Azure refuses an autoscale minimum below the Media Nodes running, which protect themselves from scale-in, so lowering it is a manual drain first (manual scale, then stop_media_node.sh on each extra Media Node) and then the redeploy; the CHANGELOG and the documentation describe it. The portal's form gets the field. Verified live with ov-cloud-tester's sc-fixed-media-nodes scenario on Elastic (Google Cloud, AWS and Azure): deploy with 2, lower to 1 (the Media Node removed served its Room through a 1200 s hold and left once it was closed), back to autoscaling with no Media Node added or removed.
2026-10-27 19:09:59 +01:00
"minimum": "[string(if(greater(parameters('fixedNumberOfMediaNodes'), 0), parameters('fixedNumberOfMediaNodes'), parameters('minNumberOfMediaNodes')))]",
"maximum": "[string(if(greater(parameters('fixedNumberOfMediaNodes'), 0), parameters('fixedNumberOfMediaNodes'), parameters('maxNumberOfMediaNodes')))]",
"default": "[string(if(greater(parameters('fixedNumberOfMediaNodes'), 0), parameters('fixedNumberOfMediaNodes'), parameters('initialNumberOfMediaNodes')))]"
2025-04-22 11:46:24 +02:00
},
"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')))]"
]
},
{
"type": "Microsoft.Authorization/roleAssignments",
"apiVersion": "2022-04-01",
"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')]",
"principalId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-master-identity', parameters('stackName'))), '2023-01-31').principalId]",
"principalType": "ServicePrincipal"
2025-04-22 11:46:24 +02:00
},
"dependsOn": [
"[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",
"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')]",
"principalId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', format('{0}-media-identity', parameters('stackName'))), '2023-01-31').principalId]",
"principalType": "ServicePrincipal"
2025-04-22 11:46:24 +02:00
},
"dependsOn": [
"[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",
"name": "actiongroupScaleIn",
2025-04-22 11:46:24 +02:00
"location": "global",
"properties": {
"groupShortName": "scaleinag",
"enabled": true,
"automationRunbookReceivers": [
{
"name": "scalein",
"useCommonAlertSchema": false,
"automationAccountId": "[reference(resourceId('Microsoft.Resources/deployments', 'WebhookDeployment'), '2025-04-01').outputs.automationAccountId.value]",
2025-04-22 11:46:24 +02:00
"runbookName": "scaleInRunbook",
"webhookResourceId": "[reference(resourceId('Microsoft.Resources/deployments', 'WebhookDeployment'), '2025-04-01').outputs.webhookId.value]",
2025-04-22 11:46:24 +02:00
"isGlobalRunbook": false,
"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": [
{
"actionGroupId": "[resourceId('Microsoft.Insights/actionGroups', 'actiongroupScaleIn')]"
2025-04-22 11:46:24 +02:00
}
]
},
"enabled": true
},
"dependsOn": [
"[resourceId('Microsoft.Insights/actionGroups', 'actiongroupScaleIn')]",
2025-04-22 11:46:24 +02:00
"[resourceId('Microsoft.Compute/virtualMachineScaleSets', format('{0}-mediaNodeScaleSet', parameters('stackName')))]"
]
},
{
"condition": "[equals(variables('ipNew'), true())]",
2025-04-22 11:46:24 +02:00
"type": "Microsoft.Network/publicIPAddresses",
"apiVersion": "2023-11-01",
"name": "[parameters('publicIpAddressObject').name]",
2025-04-22 11:46:24 +02:00
"location": "[variables('location')]",
"sku": {
"name": "Standard",
"tier": "Regional"
2025-04-22 11:46:24 +02:00
},
"properties": {
"publicIPAddressVersion": "IPv4",
"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": {
"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'))]"
},
"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": [
"[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')))]"
}
}
},
{
"name": "firstSubnetForMasterNodes",
"properties": {
"addressPrefix": "[variables('networkSettings').subnetAddressPrefixMaster1]",
"privateEndpointNetworkPolicies": "Disabled",
"privateLinkServiceNetworkPolicies": "Enabled",
"natGateway": {
"id": "[resourceId('Microsoft.Network/natGateways', format('{0}-natGateway', parameters('stackName')))]"
}
}
},
{
"name": "secondSubnetForMasterNodes",
"properties": {
"addressPrefix": "[variables('networkSettings').subnetAddressPrefixMaster2]",
"privateEndpointNetworkPolicies": "Disabled",
"privateLinkServiceNetworkPolicies": "Enabled",
"natGateway": {
"id": "[resourceId('Microsoft.Network/natGateways', format('{0}-natGateway', parameters('stackName')))]"
}
}
2025-04-22 11:46:24 +02:00
}
]
},
"dependsOn": [
"[resourceId('Microsoft.Network/natGateways', format('{0}-natGateway', parameters('stackName')))]",
"[resourceId('Microsoft.Network/networkSecurityGroups', format('{0}-mediaNodeNSG', parameters('stackName')))]"
2025-04-22 11:46:24 +02:00
]
},
{
"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', variables('networkSettings').vNetName)]"
2025-04-22 11:46:24 +02:00
]
},
{
"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', variables('networkSettings').vNetName)]"
2025-04-22 11:46:24 +02:00
]
},
{
"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', variables('networkSettings').vNetName)]"
2025-04-22 11:46:24 +02:00
]
},
{
"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', variables('networkSettings').vNetName)]"
2025-04-22 11:46:24 +02:00
]
},
{
"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')))]"
]
},
{
"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')))]"
]
},
{
"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",
"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')))]"
}
],
"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')))]"
]
},
{
"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",
"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')))]"
}
],
"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')))]"
]
},
{
"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": "*",
"destinationPortRange": "50000-60000",
2025-04-22 11:46:24 +02:00
"access": "Allow",
"priority": 140,
"direction": "Inbound"
}
},
{
"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",
"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",
"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",
"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",
"sourceApplicationSecurityGroups": [
2025-04-22 11:46:24 +02: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",
"priority": 200,
2025-04-22 11:46:24 +02:00
"direction": "Inbound"
},
"dependsOn": [
"[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",
"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')))]"
}
],
"destinationPortRange": "8080",
2025-04-22 11:46:24 +02:00
"access": "Allow",
"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",
"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')))]"
}
],
"destinationPortRange": "5349",
2025-04-22 11:46:24 +02:00
"access": "Allow",
"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')))]"
]
},
{
"condition": "[equals(variables('isEmptyStorageAccountName'), true())]",
2025-04-22 11:46:24 +02:00
"type": "Microsoft.Storage/storageAccounts",
"apiVersion": "2023-01-01",
"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
}
},
{
"condition": "[equals(variables('isEmptyStorageAccountName'), true())]",
2025-04-22 11:46:24 +02:00
"type": "Microsoft.Storage/storageAccounts/blobServices/containers",
"apiVersion": "2023-01-01",
"name": "[format('{0}/default/automation-locks', uniqueString(resourceGroup().id))]",
"properties": {
"publicAccess": "None"
},
"dependsOn": [
"[resourceId('Microsoft.Storage/storageAccounts', uniqueString(resourceGroup().id))]"
]
},
{
"condition": "[equals(variables('isEmptyStorageAccountName'), true())]",
"type": "Microsoft.Storage/storageAccounts/blobServices/containers",
"apiVersion": "2023-01-01",
"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",
"apiVersion": "2025-04-01",
2025-04-22 11:46:24 +02:00
"name": "WebhookDeployment",
"properties": {
"expressionEvaluationOptions": {
"scope": "inner"
},
"mode": "Incremental",
"parameters": {
"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": "[format('webhookForScaleIn-{0}', uniqueString(deployment().name, parameters('scaleInWebhookNameSeed')))]"
2025-04-22 11:46:24 +02:00
},
"WebhookExpiryTime": {
"value": "2035-03-30T00:00:00Z"
},
"_artifactsLocation": {
"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": {
"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'))]"
}
}
}
},
"dependsOn": [
"[resourceId('Microsoft.Compute/virtualMachines', format('{0}-VM-MasterNode1', parameters('stackName')))]"
]
2025-04-22 11:46:24 +02:00
}
]
}