openvidu/openvidu-deployment/pro/elastic/azure/createUiDefinition.json

562 lines
31 KiB
JSON
Raw Normal View History

{
"$schema": "https://schema.management.azure.com/schemas/2021-09-09/uiFormDefinition.schema.json",
"view": {
"kind": "Form",
"properties": {
"title": "OpenVidu Elastic Deployment",
"steps": [
{
"name": "basics",
"label": "Provide a Resource Group and a Stack Name",
"elements": [
{
"name": "resourceScope",
"type": "Microsoft.Common.ResourceScope",
"location": {
"resourceTypes": [
"microsoft.insights/autoscalesettings",
"microsoft.storage/storageaccounts",
"microsoft.resources/resourcegroups"
]
}
},
{
"name": "stackName",
"type": "Microsoft.Common.TextBox",
"label": "Stack Name",
"subLabel": "",
"defaultValue": "",
"toolTip": "Stack name",
"constraints": {
"required": true,
"validations": [
{
"regex": "^[a-zA-Z][a-zA-Z0-9-]{1,13}[a-zA-Z0-9]$",
"message": "Must be 3-15 characters, start with a letter, contain only letters, numbers and hyphens, and not end with a hyphen. It is used as the Azure Key Vault name prefix (\"<stackName>-keyvault\"), which is limited to 24 characters."
}
]
},
"infoMessages": [],
"visible": true
}
]
},
{
"name": "parameters SSL",
"label": "Domain and SSL certificate configuration",
"elements": [
{
"name": "certificateType",
"type": "Microsoft.Common.DropDown",
"label": "Certificate Type",
"subLabel": "",
"defaultValue": "letsencrypt",
"toolTip": "[[selfsigned] Not recommended for production use. If you don't have a FQDN (DomainName parameter), you can use this option to generate a self-signed certificate.\n[owncert] Valid for production environments. If you have a FQDN (DomainName parameter) and an Elastic IP, you can use this option to use your own certificate.\n[letsencrypt] Valid for production environments. If you have a FQDN (DomainName parameter) and an Elastic IP, you can use this option to generate a Let's Encrypt certificate. If you don't have a FQDN, Let's Encrypt will issue a certificate for the public IP.\n",
"constraints": {
"required": false,
"allowedValues": [
{
"label": "selfsigned",
"value": "selfsigned"
},
{
"label": "owncert",
"value": "owncert"
},
{
"label": "letsencrypt",
"value": "letsencrypt"
}
],
"validations": []
},
"infoMessages": [],
"visible": true
},
{
"name": "publicIpAddressObject",
"type": "Microsoft.Network.PublicIpAddressCombo",
"label": {
"publicIpAddress": "Public IP address"
},
"toolTip": {
"publicIpAddress": "Previously created Public IP address for the OpenVidu Deployment. Blank will generate a public IP"
},
"defaultValue": {
"publicIpAddressName": "ov-publicIpAddress"
},
"options": {
"hideNone": true,
"hideDomainNameLabel": true,
"hideExisting": false
},
"visible": true
},
{
"name": "domainName",
"type": "Microsoft.Common.TextBox",
"label": "Domain Name",
"subLabel": "\nDomain name for the OpenVidu Deployment. Blank will generate default domain",
"defaultValue": "",
"toolTip": "",
"constraints": {
"required": false,
"regex": "^$|^(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\\.)+[a-z0-9][a-z0-9-]{0,61}[a-z0-9]$",
"validationMessage": "Please enter a valid domain name or leave it blank to generate",
"validations": [
{
"isValid": "[if(equals(steps('parameters SSL').certificateType, 'owncert'), not(empty(steps('parameters SSL').domainName)), true)]",
"message": "You need to fill this parameter because you've selected owncert certificate type."
}
]
},
"infoMessages": [],
"visible": true
},
{
"name": "ownPublicCertificate",
"type": "Microsoft.Common.TextBox",
"label": "Own Public Certificate",
"subLabel": "If certificate type is 'owncert', this parameter will be used to specify the public certificate in base64 format",
"defaultValue": "",
"toolTip": "",
"constraints": {
"required": false,
"regex": "",
"validationMessage": "",
"validations": [
{
"isValid": "[if(equals(steps('parameters SSL').certificateType, 'owncert'), not(empty(steps('parameters SSL').ownPublicCertificate)), true)]",
"message": "You need to fill this parameter because you've selected owncert certificate type."
}
]
},
"infoMessages": [],
"visible": true
},
{
"name": "ownPrivateCertificate",
"type": "Microsoft.Common.TextBox",
"label": "Own Private Certificate",
"subLabel": "If certificate type is 'owncert', this parameter will be used to specify the private certificate in base64 format",
"defaultValue": "",
"toolTip": "",
"constraints": {
"required": false,
"regex": "",
"validationMessage": "",
"validations": [
{
"isValid": "[if(equals(steps('parameters SSL').certificateType, 'owncert'), not(empty(steps('parameters SSL').ownPrivateCertificate)), true)]",
"message": "You need to fill this parameter because you've selected owncert certificate type."
}
]
},
"infoMessages": [],
"visible": true
}
]
},
{
"name": "parameters OPENVIDU",
"label": "OpenVidu Elastic configuration",
"elements": [
{
"name": "openviduLicense",
"type": "Microsoft.Common.PasswordBox",
"label": {
"password": "Openvidu License",
"confirmPassword": "Confirm password"
},
"defaultValue": "",
"toolTip": "Visit https://openvidu.io/account",
"constraints": {
"required": true,
"regex": "",
"validationMessage": "",
"validations": []
},
"options": {
"hideConfirmation": true
},
"visible": true
},
{
"name": "rtcEngine",
"type": "Microsoft.Common.DropDown",
"label": "Rtc Engine",
"subLabel": "RTCEngine media engine to use",
"defaultValue": "mediasoup",
"toolTip": "",
"constraints": {
"required": false,
"allowedValues": [
{
"label": "pion",
"value": "pion"
},
{
"label": "mediasoup",
"value": "mediasoup"
}
],
"validations": []
},
"infoMessages": [],
"visible": true
}
]
},
{
"name": "parameters MEET",
"label": "OpenVidu Meet configuration",
"elements": [
{
"name": "initialMeetAdminPassword",
"type": "Microsoft.Common.PasswordBox",
"label": {
"password": "Initial Meet Admin Password (Generated if empty, can be changed later from the Meet Console)",
"confirmPassword": "Confirm Password"
},
"toolTip": "Initial password for the 'admin' user in OpenVidu Meet. If not provided, a random password will be generated.",
"constraints": {
"required": false,
"regex": "^[A-Za-z0-9_-]*$",
"validationMessage": "Password can only contain letters, numbers, underscores and hyphens.",
"validations": []
},
"options": {
"hideConfirmation": true
},
"infoMessages": [],
"visible": true
},
{
"name": "initialMeetApiKey",
"type": "Microsoft.Common.PasswordBox",
"label": {
"password": "Initial API Key for OpenVidu Meet (optional, can be set later from the Meet Console)",
"confirmPassword": "Confirm API Key"
},
"toolTip": "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.",
"constraints": {
"required": false,
"regex": "^[A-Za-z0-9_-]*$",
"validationMessage": "API key can only contain letters, numbers, underscores and hyphens.",
"validations": []
},
"options": {
"hideConfirmation": true
},
"infoMessages": [],
"visible": true
}
]
},
{
"name": "parameters INSTANCE",
"label": "Master and Media node configuration",
"elements": [
{
"name": "masterNodeInstanceType",
"type": "Microsoft.Compute.SizeSelector",
"label": "Master Node Instance Type",
"toolTip": "Specifies the Azure instance type for your OpenVidu Master Node",
"recommendedSizes": [
"Standard_B4s",
"Standard_B4ms",
"Standard_D4ps_v5",
"Standard_D4pls_v5"
],
"constraints": {
"allowedSizes": [],
"excludedSizes": []
},
"options": {
"hideDiskTypeFilter": false
},
"osPlatform": "Linux",
"visible": true
},
{
"name": "mediaNodeInstanceType",
"type": "Microsoft.Compute.SizeSelector",
"label": "Media Node Instance Type",
"toolTip": "Specifies the Azure instance type for your OpenVidu Media Nodes",
"recommendedSizes": [
"Standard_B4s",
"Standard_B4ms",
"Standard_D4ps_v5",
"Standard_D4pls_v5"
],
"constraints": {
"allowedSizes": [],
"excludedSizes": []
},
"options": {
"hideDiskTypeFilter": false
},
"osPlatform": "Linux",
"visible": true
},
{
"name": "adminUsername",
"type": "Microsoft.Common.TextBox",
"label": "Admin Username",
"subLabel": "Username for the Virtual Machine.",
"defaultValue": "",
"toolTip": "",
"constraints": {
"required": true,
"regex": "",
"validationMessage": "",
"validations": [
{
"isValid": "[or(or(empty(steps('parameters INSTANCE').adminUsername),and(not(startsWith(steps('parameters INSTANCE').adminUsername,'[[')),startsWith(steps('parameters INSTANCE').adminUsername,'['),endsWith(steps('parameters INSTANCE').adminUsername,']'),greater(indexOf(steps('parameters INSTANCE').adminUsername,'('),-1),greater(indexOf(steps('parameters INSTANCE').adminUsername,')'),-1))),and(not(regex(steps('parameters INSTANCE').adminUsername,'/[\\\\/\\\"\\\"\\[\\]:|<>+=;,$ ?*@]+/')),not(endsWith(steps('parameters INSTANCE').adminUsername,'.'))),regex(steps('parameters INSTANCE').adminUsername,'^[a-zA-Z0-9-]+$'))]",
"message": "User name cannot contain special characters \\/\\\"[]:|<>+=;,?*@ or end with '.', and must be between 1 and 15 characters."
},
{
"isValid": "[or(or(empty(steps('parameters INSTANCE').adminUsername),and(not(startsWith(steps('parameters INSTANCE').adminUsername,'[[')),startsWith(steps('parameters INSTANCE').adminUsername,'['),endsWith(steps('parameters INSTANCE').adminUsername,']'),greater(indexOf(steps('parameters INSTANCE').adminUsername,'('),-1),greater(indexOf(steps('parameters INSTANCE').adminUsername,')'),-1))),equals(length(filter(parse('[\"administrator\", \"admin\", \"user\", \"user1\", \"test\", \"user2\", \"test1\", \"user3\", \"admin1\", \"1\", \"123\", \"a\", \"actuser\", \"adm\", \"admin2\", \"aspnet\", \"backup\", \"console\", \"david\", \"guest\", \"john\", \"owner\", \"root\", \"server\", \"sql\", \"support\", \"support_388945a0\", \"sys\", \"test2\", \"test3\", \"user4\", \"user5\"]'),(item) => equals(toLower(item),toLower(steps('parameters INSTANCE').adminUsername)))),0))]",
"message": "The specified username is not allowed. Please choose a different value."
}
]
},
"infoMessages": [],
"visible": true
},
{
"name": "adminSshKey",
"type": "Microsoft.Compute.CredentialsCombo",
"label": {
"authenticationType": "Authentication type",
"sshPublicKey": "SSH public key"
},
"toolTip": {
"authenticationType": "",
"sshPublicKey": "SSH Key for the Instances of Media and Master nodes."
},
"constraints": {
"required": true
},
"options": {
"hideConfirmation": true,
"hidePassword": true
},
"osPlatform": "Linux",
"visible": true
}
]
},
{
"name": "parameters SCALING",
"label": "Media Nodes Sacle Set configuration",
"elements": [
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
{
"name": "fixedNumberOfMediaNodes",
"type": "Microsoft.Common.TextBox",
"label": "Fixed Number Of Media Nodes",
"subLabel": "If > 0, deploys a fixed number of media nodes with no autoscaling (the Initial/Min/Max/Scale Target CPU values are ignored). If 0 (default), the deployment is elastic and autoscaling is enabled",
"defaultValue": "0",
"toolTip": "",
"constraints": {
"required": false,
"regex": "^[0-9]+$",
"validationMessage": "Must be an integer greater than or equal to 0",
"validations": []
},
"infoMessages": [],
"visible": true
},
{
"name": "initialNumberOfMediaNodes",
"type": "Microsoft.Common.TextBox",
"label": "Initial Number Of Media Nodes",
"subLabel": "Number of initial media nodes to deploy",
"defaultValue": "1",
"toolTip": "",
"constraints": {
"required": false,
"regex": "",
"validationMessage": "",
"validations": []
},
"infoMessages": [],
"visible": true
},
{
"name": "minNumberOfMediaNodes",
"type": "Microsoft.Common.TextBox",
"label": "Min Number Of Media Nodes",
"subLabel": "Minimum number of media nodes to deploy",
"defaultValue": "1",
"toolTip": "",
"constraints": {
"required": false,
"regex": "",
"validationMessage": "",
"validations": []
},
"infoMessages": [],
"visible": true
},
{
"name": "maxNumberOfMediaNodes",
"type": "Microsoft.Common.TextBox",
"label": "Max Number Of Media Nodes",
"subLabel": "Maximum number of media nodes to deploy",
"defaultValue": "5",
"toolTip": "",
"constraints": {
"required": false,
"regex": "",
"validationMessage": "",
"validations": []
},
"infoMessages": [],
"visible": true
},
{
"name": "scaleTargetCPU",
"type": "Microsoft.Common.TextBox",
"label": "Scale Target CPU",
"subLabel": "Target CPU percentage to scale up or down",
"defaultValue": "50",
"toolTip": "",
"constraints": {
"required": false,
"regex": "",
"validationMessage": "",
"validations": []
},
"infoMessages": [],
"visible": true
},
{
"name": "datetime",
"type": "Microsoft.Common.TextBox",
"label": "Datetime",
"subLabel": "",
"defaultValue": "[[utcNow('u')]",
"toolTip": "",
"constraints": {
"required": false,
"regex": "",
"validationMessage": "",
"validations": []
},
"infoMessages": [],
"visible": false
},
{
"name": "automationAccountName",
"type": "Microsoft.Common.TextBox",
"label": "Automation Account Name",
"subLabel": "Automation Account Name to create a runbook inside it for scale in",
"defaultValue": "",
"toolTip": "",
"constraints": {
"required": true,
"regex": "^[a-zA-Z0-9][a-zA-Z0-9\\-]{4,48}[a-zA-Z0-9]$",
"validationMessage": "The account name must be 6–50 characters, start and end with a letter or number, and contain only letters, numbers, or hyphens (–).",
"validations": []
},
"infoMessages": [],
"visible": true
}
]
},
{
"name": "parameters STORAGE",
"label": "Storage Account configuration",
"elements": [
{
"name": "storageAccountName",
"type": "Microsoft.Common.TextBox",
"label": "Storage Account Name",
"subLabel": "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.",
"defaultValue": "",
"toolTip": "",
"constraints": {
"required": false,
"regex": "",
"validationMessage": "",
"validations": []
},
"infoMessages": [],
"visible": true
},
{
"name": "containerName",
"type": "Microsoft.Common.TextBox",
"label": "Container Name",
"subLabel": "Name of the bucket where OpenVidu will store the recordings, if an Storage Account Name is specified this parameter will act as the container name for the recordings that exists in the Storage Account. If not specified, a default bucket will be created.",
"defaultValue": "",
"toolTip": "",
"constraints": {
"required": false,
"regex": "",
"validationMessage": "",
"validations": []
},
"infoMessages": [],
"visible": true
}
]
},
{
"name": "FLAGS",
"label": "(Optional) Additional Install Flags",
"elements": [
{
"name": "additionalInstallFlags",
"type": "Microsoft.Common.TextBox",
"label": "Additional Install Flags",
"subLabel": "Additional optional flags to pass to the OpenVidu installer (comma-separated, e.g., \"--flag1=value, --flag2\")",
"defaultValue": "",
"toolTip": "",
"constraints": {
"required": false,
"regex": "^[A-Za-z0-9, =_.\\-]*$",
"validationMessage": "Must be a comma-separated list of flags (for example, --flag=value, --bool-flag)",
"validations": []
},
"infoMessages": [],
"visible": true
}
]
}
]
},
"outputs": {
"kind": "ResourceGroup",
"location": "[steps('basics').resourceScope.location.name]",
"resourceGroupId": "[steps('basics').resourceScope.resourceGroup.id]",
"parameters": {
"stackName": "[steps('basics').stackName]",
"certificateType": "[steps('parameters SSL').certificateType]",
"publicIpAddressObject": "[steps('parameters SSL').publicIpAddressObject]",
"domainName": "[steps('parameters SSL').domainName]",
"ownPublicCertificate": "[steps('parameters SSL').ownPublicCertificate]",
"ownPrivateCertificate": "[steps('parameters SSL').ownPrivateCertificate]",
"openviduLicense": "[steps('parameters OPENVIDU').openviduLicense]",
"rtcEngine": "[steps('parameters OPENVIDU').rtcEngine]",
"initialMeetAdminPassword": "[steps('parameters MEET').initialMeetAdminPassword]",
"initialMeetApiKey": "[steps('parameters MEET').initialMeetApiKey]",
"masterNodeInstanceType": "[steps('parameters INSTANCE').masterNodeInstanceType]",
"mediaNodeInstanceType": "[steps('parameters INSTANCE').mediaNodeInstanceType]",
"adminUsername": "[steps('parameters INSTANCE').adminUsername]",
"adminSshKey": "[steps('parameters INSTANCE').adminSshKey]",
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
"fixedNumberOfMediaNodes": "[steps('parameters SCALING').fixedNumberOfMediaNodes]",
"initialNumberOfMediaNodes": "[steps('parameters SCALING').initialNumberOfMediaNodes]",
"minNumberOfMediaNodes": "[steps('parameters SCALING').minNumberOfMediaNodes]",
"maxNumberOfMediaNodes": "[steps('parameters SCALING').maxNumberOfMediaNodes]",
"scaleTargetCPU": "[steps('parameters SCALING').scaleTargetCPU]",
"datetime": "[steps('parameters SCALING').datetime]",
"automationAccountName": "[steps('parameters SCALING').automationAccountName]",
"storageAccountName": "[steps('parameters STORAGE').storageAccountName]",
"containerName": "[steps('parameters STORAGE').containerName]",
"additionalInstallFlags": "[steps('FLAGS').additionalInstallFlags]"
}
}
}
}