From d99d3ed7febfa187cad4ff40d2ea8c9d6f2e4a77 Mon Sep 17 00:00:00 2001 From: Alexander Sehr Date: Thu, 14 Jul 2022 18:32:51 +0200 Subject: [PATCH] [Modules] Added Web PubSub module (#1621) * [Modules] Added Web PubSub module (#1568) * feat(webpubsub): Added Web PubSub module * feat(webpubsub): Updated folders, added tests, updated readme * feat(webpubsub): Added nested rbac * fix: Applied suggestions from code review Co-authored-by: Alexander Sehr * fix(webpubsub): Integrated multiple review feedback * feat(webpubsub): Added json based test parameters * ci(webpubsub): Added devops and github workflow definitions * fix(webpubsub): Updated default sku Co-authored-by: Alexander Sehr * fix(webpubsub): Updated pipeline variables Co-authored-by: Alexander Sehr * fix(webpubsub): Updated pipeline variables Co-authored-by: Alexander Sehr * fix(webpubsub): Updated pipeline variables Co-authored-by: Alexander Sehr * fix(webpubsub): Deleted .test folder as requested Co-authored-by: Alexander Sehr * Moved mmodule to temp loc * Merged latest main & moved SignalR to target location * Updated SignalR pipelines to latest * Several smaller fixes Co-authored-by: Jan-Henrik Damaschke --- .../ms.signalrservice.webpubsub.yml | 55 ++ .../workflows/ms.signalrservice.webpubsub.yml | 142 +++++ .../webPubSub/.bicep/nested_rbac.bicep | 57 ++ .../webPubSub/.test/min.parameters.json | 9 + .../webPubSub/.test/parameters.json | 83 +++ .../webPubSub/deploy.bicep | 182 ++++++ .../webPubSub/readme.md | 538 ++++++++++++++++++ .../webPubSub/version.json | 4 + 8 files changed, 1070 insertions(+) create mode 100644 .azuredevops/modulePipelines/ms.signalrservice.webpubsub.yml create mode 100644 .github/workflows/ms.signalrservice.webpubsub.yml create mode 100644 modules/Microsoft.SignalRService/webPubSub/.bicep/nested_rbac.bicep create mode 100644 modules/Microsoft.SignalRService/webPubSub/.test/min.parameters.json create mode 100644 modules/Microsoft.SignalRService/webPubSub/.test/parameters.json create mode 100644 modules/Microsoft.SignalRService/webPubSub/deploy.bicep create mode 100644 modules/Microsoft.SignalRService/webPubSub/readme.md create mode 100644 modules/Microsoft.SignalRService/webPubSub/version.json diff --git a/.azuredevops/modulePipelines/ms.signalrservice.webpubsub.yml b/.azuredevops/modulePipelines/ms.signalrservice.webpubsub.yml new file mode 100644 index 0000000000..813ca67365 --- /dev/null +++ b/.azuredevops/modulePipelines/ms.signalrservice.webpubsub.yml @@ -0,0 +1,55 @@ +name: 'SignalRService - webPubSub' + +parameters: + - name: removeDeployment + displayName: Remove deployed module + type: boolean + default: true + - name: prerelease + displayName: Publish prerelease module + type: boolean + default: false + +pr: none + +trigger: + batch: true + branches: + include: + - main + paths: + include: + - '/.azuredevops/modulePipelines/ms.signalrservice.webpubsub.yml' + - '/.azuredevops/pipelineTemplates/*.yml' + - '/modules/Microsoft.SignalRService/webPubSub/*' + - '/modules/.global/global.module.tests.ps1' + exclude: + - '/**/*.md' + +variables: + - template: '../../global.variables.yml' + - group: 'PLATFORM_VARIABLES' + - name: modulePath + value: '/modules/Microsoft.SignalRService/webPubSub' + +stages: + - stage: Validation + displayName: Static validation + jobs: + - template: /.azuredevops/pipelineTemplates/jobs.validateModulePester.yml + + - stage: Deployment + displayName: Deployment validation + jobs: + - template: /.azuredevops/pipelineTemplates/jobs.validateModuleDeployment.yml + parameters: + removeDeployment: '${{ parameters.removeDeployment }}' + deploymentBlocks: + - path: $(modulePath)/.test/min.parameters.json + - path: $(modulePath)/.test/parameters.json + + - stage: Publishing + displayName: Publishing + condition: and(succeeded(), or(eq(variables['Build.SourceBranch'], 'refs/heads/main'), eq(variables['Build.SourceBranch'], 'refs/heads/master'), eq('${{ parameters.prerelease }}', 'true'))) + jobs: + - template: /.azuredevops/pipelineTemplates/jobs.publishModule.yml diff --git a/.github/workflows/ms.signalrservice.webpubsub.yml b/.github/workflows/ms.signalrservice.webpubsub.yml new file mode 100644 index 0000000000..18e9d8f22c --- /dev/null +++ b/.github/workflows/ms.signalrservice.webpubsub.yml @@ -0,0 +1,142 @@ +name: 'SignalRService: webPubSub' + +on: + workflow_dispatch: + inputs: + removeDeployment: + type: boolean + description: 'Remove deployed module' + required: false + default: true + prerelease: + type: boolean + description: 'Publish prerelease module' + required: false + default: false + push: + branches: + - main + paths: + - '.github/actions/templates/**' + - '.github/workflows/ms.signalrservice.webpubsub.yml' + - 'modules/Microsoft.SignalRService/webPubSub/**' + - 'modules/.global/global.module.tests.ps1' + - '!*/**/readme.md' + - 'utilities/pipelines/**' + - '!utilities/pipelines/dependencies/**' + +env: + variablesPath: 'global.variables.yml' + modulePath: 'modules/Microsoft.SignalRService/webPubSub' + workflowPath: '.github/workflows/ms.signalrservice.webpubsub.yml' + AZURE_CREDENTIALS: ${{ secrets.AZURE_CREDENTIALS }} + ARM_SUBSCRIPTION_ID: '${{ secrets.ARM_SUBSCRIPTION_ID }}' + ARM_MGMTGROUP_ID: '${{ secrets.ARM_MGMTGROUP_ID }}' + ARM_TENANT_ID: '${{ secrets.ARM_TENANT_ID }}' + DEPLOYMENT_SP_ID: '${{ secrets.DEPLOYMENT_SP_ID }}' + +jobs: + ########################### + # Initialize pipeline # + ########################### + job_initialize_pipeline: + runs-on: ubuntu-20.04 + name: 'Initialize pipeline' + steps: + - name: 'Checkout' + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: 'Set input parameters to output variables' + id: get-workflow-param + uses: ./.github/actions/templates/getWorkflowInput + with: + workflowPath: '${{ env.workflowPath}}' + - name: 'Get parameter file paths' + id: get-parameter-file-paths + uses: ./.github/actions/templates/getParameterFiles + with: + modulePath: '${{ env.modulePath }}' + outputs: + removeDeployment: ${{ steps.get-workflow-param.outputs.removeDeployment }} + parameterFilePaths: ${{ steps.get-parameter-file-paths.outputs.parameterFilePaths }} + + ######################### + # Static validation # + ######################### + job_module_pester_validation: + runs-on: ubuntu-20.04 + name: 'Static validation' + steps: + - name: 'Checkout' + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: 'Run tests' + uses: ./.github/actions/templates/validateModulePester + with: + modulePath: '${{ env.modulePath }}' + + ############################# + # Deployment validation # + ############################# + job_module_deploy_validation: + runs-on: ubuntu-20.04 + name: 'Deployment validation' + needs: + - job_initialize_pipeline + - job_module_pester_validation + strategy: + fail-fast: false + matrix: + parameterFilePaths: ${{ fromJSON(needs.job_initialize_pipeline.outputs.parameterFilePaths) }} + steps: + - name: 'Checkout' + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Set environment variables + uses: ./.github/actions/templates/setEnvironmentVariables + with: + variablesPath: ${{ env.variablesPath }} + - name: 'Using parameter file [${{ matrix.parameterFilePaths }}]' + uses: ./.github/actions/templates/validateModuleDeployment + with: + templateFilePath: '${{ env.modulePath }}/deploy.bicep' + parameterFilePath: '${{ env.modulePath }}/${{ matrix.parameterFilePaths }}' + location: '${{ env.location }}' + resourceGroupName: '${{ env.resourceGroupName }}' + subscriptionId: '${{ secrets.ARM_SUBSCRIPTION_ID }}' + managementGroupId: '${{ secrets.ARM_MGMTGROUP_ID }}' + removeDeployment: '${{ needs.job_initialize_pipeline.outputs.removeDeployment }}' + + ################## + # Publishing # + ################## + job_publish_module: + name: 'Publishing' + if: github.ref == 'refs/heads/main' || github.ref == 'refs/heads/master' || github.event.inputs.prerelease == 'true' + runs-on: ubuntu-20.04 + needs: + - job_module_deploy_validation + steps: + - name: 'Checkout' + uses: actions/checkout@v2 + with: + fetch-depth: 0 + - name: Set environment variables + uses: ./.github/actions/templates/setEnvironmentVariables + with: + variablesPath: ${{ env.variablesPath }} + - name: 'Publishing' + uses: ./.github/actions/templates/publishModule + with: + templateFilePath: '${{ env.modulePath }}/deploy.bicep' + templateSpecsRGName: '${{ env.templateSpecsRGName }}' + templateSpecsRGLocation: '${{ env.templateSpecsRGLocation }}' + templateSpecsDescription: '${{ env.templateSpecsDescription }}' + templateSpecsDoPublish: '${{ env.templateSpecsDoPublish }}' + bicepRegistryName: '${{ env.bicepRegistryName }}' + bicepRegistryRGName: '${{ env.bicepRegistryRGName }}' + bicepRegistryRgLocation: '${{ env.bicepRegistryRgLocation }}' + bicepRegistryDoPublish: '${{ env.bicepRegistryDoPublish }}' diff --git a/modules/Microsoft.SignalRService/webPubSub/.bicep/nested_rbac.bicep b/modules/Microsoft.SignalRService/webPubSub/.bicep/nested_rbac.bicep new file mode 100644 index 0000000000..5ee208ae94 --- /dev/null +++ b/modules/Microsoft.SignalRService/webPubSub/.bicep/nested_rbac.bicep @@ -0,0 +1,57 @@ +@sys.description('Required. The IDs of the principals to assign the role to.') +param principalIds array + +@sys.description('Required. The name of the role to assign. If it cannot be found you can specify the role definition ID instead.') +param roleDefinitionIdOrName string + +@sys.description('Required. The resource ID of the resource to apply the role assignment to.') +param resourceId string + +@sys.description('Optional. The principal type of the assigned principal ID.') +@allowed([ + 'ServicePrincipal' + 'Group' + 'User' + 'ForeignGroup' + 'Device' + '' +]) +param principalType string = '' + +@sys.description('Optional. The description of the role assignment.') +param description string = '' + +var builtInRoleNames = { + 'Owner': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8e3af657-a8ff-443c-a75c-2fe8c4bcb635') + 'Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c') + 'Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'acdd72a7-3385-48ef-bd42-f606fba81ae7') + 'Log Analytics Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293') + 'Log Analytics Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '73c42c96-874c-492b-b04d-ab87d138a893') + 'Managed Application Contributor Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '641177b8-a67a-45b9-a033-47bc880bb21e') + 'Managed Application Operator Role': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'c7393b34-138c-406f-901b-d8cf2b17e6ae') + 'Managed Applications Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'b9331d33-8a36-4f8c-b097-4f54124fdb44') + 'Monitoring Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '749f88d5-cbae-40b8-bcfc-e573ddc772fa') + 'Monitoring Metrics Publisher': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '3913510d-42f4-4e42-8a64-420c390055eb') + 'Monitoring Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '43d0d8ad-25c7-4714-9337-8ba259a9fe05') + 'Resource Policy Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '36243c78-bf99-498c-9df9-86d9f8d28608') + 'SignalR AccessKey Reader': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '04165923-9d83-45d5-8227-78b77b0a687e') + 'SignalR/Web PubSub Contributor': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '8cf5e20a-e4b2-4e9d-b3a1-5ceb692c2761') + 'User Access Administrator': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '18d7d88d-d35e-4fb5-a5c3-7773c20a72d9') + 'Web PubSub Service Owner (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', '12cf5a90-567b-43ae-8102-96cf46c7d9b4') + 'Web PubSub Service Reader (Preview)': subscriptionResourceId('Microsoft.Authorization/roleDefinitions', 'bfb1c7d2-fb1a-466b-b2ba-aee63b92deaf') +} + +resource webPubSub 'Microsoft.SignalRService/webPubSub@2021-10-01' existing = { + name: last(split(resourceId, '/')) +} + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2020-10-01-preview' = [for principalId in principalIds: { + name: guid(webPubSub.id, principalId, roleDefinitionIdOrName) + properties: { + description: description + roleDefinitionId: contains(builtInRoleNames, roleDefinitionIdOrName) ? builtInRoleNames[roleDefinitionIdOrName] : roleDefinitionIdOrName + principalId: principalId + principalType: !empty(principalType) ? any(principalType) : null + } + scope: webPubSub +}] diff --git a/modules/Microsoft.SignalRService/webPubSub/.test/min.parameters.json b/modules/Microsoft.SignalRService/webPubSub/.test/min.parameters.json new file mode 100644 index 0000000000..26f8284d23 --- /dev/null +++ b/modules/Microsoft.SignalRService/webPubSub/.test/min.parameters.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-pubsub-min-001" + } + } +} diff --git a/modules/Microsoft.SignalRService/webPubSub/.test/parameters.json b/modules/Microsoft.SignalRService/webPubSub/.test/parameters.json new file mode 100644 index 0000000000..fb763cd9f4 --- /dev/null +++ b/modules/Microsoft.SignalRService/webPubSub/.test/parameters.json @@ -0,0 +1,83 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "location": { + "value": "westeurope" + }, + "name": { + "value": "<>-az-pubsub-x-001" + }, + "capacity": { + "value": 2 + }, + "clientCertEnabled": { + "value": false + }, + "disableAadAuth": { + "value": false + }, + "disableLocalAuth": { + "value": true + }, + "lock": { + "value": "CanNotDelete" + }, + "sku": { + "value": "Standard_S1" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "networkAcls": { + "value": { + "defaultAction": "Allow", + "privateEndpoints": [ + { + "name": "pe-<>-az-pubsub-x-001-webpubsub-0", + "allow": [], + "deny": [ + "ServerConnection", + "Trace" + ] + } + ], + "publicNetwork": { + "allow": [], + "deny": [ + "RESTAPI", + "Trace" + ] + } + } + }, + "systemAssignedIdentity": { + "value": true + }, + "tags": { + "value": { + "purpose": "test" + } + }, + "resourceLogConfigurationsToEnable": { + "value": [ + "ConnectivityLogs" + ] + }, + "privateEndpoints": { + "value": [ + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "webpubsub" + } + ] + } + } +} diff --git a/modules/Microsoft.SignalRService/webPubSub/deploy.bicep b/modules/Microsoft.SignalRService/webPubSub/deploy.bicep new file mode 100644 index 0000000000..36b9c11e6c --- /dev/null +++ b/modules/Microsoft.SignalRService/webPubSub/deploy.bicep @@ -0,0 +1,182 @@ +@description('Optional. The location for all Web PubSub resources.') +param location string = resourceGroup().location + +@description('Required. The name of the Web PubSub resource.') +param name string + +@description('Optional. Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible.') +param privateEndpoints array = [] + +@allowed([ + '' + 'CanNotDelete' + 'ReadOnly' +]) +@description('Optional. Specify the type of lock.') +param lock string = '' + +@description('Optional. Array of role assignment objects that contain the \'roleDefinitionIdOrName\' and \'principalId\' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: \'/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11\'.') +param roleAssignments array = [] + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. The unit count of the resource. 1 by default.') +param capacity int = 1 + +@allowed([ + 'Free_F1' + 'Standard_S1' +]) +@description('Optional. Pricing tier of App Configuration.') +param sku string = 'Standard_S1' + +@description('Optional. Enables system assigned managed identity on the resource.') +param systemAssignedIdentity bool = false + +@description('Optional. The ID(s) to assign to the resource.') +param userAssignedIdentities object = {} + +@description('Optional. When set as true, connection with AuthType=aad won\'t work.') +param disableAadAuth bool = false + +@description('Optional. Disables all authentication methods other than AAD authentication. For security reasons, this value should be set to `true`.') +param disableLocalAuth bool = true + +@description('Optional. Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set.') +@allowed([ + '' + 'Enabled' + 'Disabled' +]) +param publicNetworkAccess string = '' + +@allowed([ + 'ConnectivityLogs' + 'MessagingLogs' +]) +@description('Optional. Control permission for data plane traffic coming from public networks while private endpoint is enabled.') +param resourceLogConfigurationsToEnable array = [ + 'ConnectivityLogs' + 'MessagingLogs' +] + +@description('Optional. Request client certificate during TLS handshake if enabled.') +param clientCertEnabled bool = false + +@description('Optional. Networks ACLs, this value contains IPs to whitelist and/or Subnet information. Can only be set if the \'SKU\' is not \'Free_F1\'. For security reasons, it is recommended to set the DefaultAction Deny.') +param networkAcls object = {} + +@description('Optional. Enable telemetry via the Customer Usage Attribution ID (GUID).') +param enableDefaultTelemetry bool = true + +var resourceLogConfiguration = [for configuration in resourceLogConfigurationsToEnable: { + name: configuration + enabled: 'true' +}] + +var identityType = systemAssignedIdentity ? 'SystemAssigned' : !empty(userAssignedIdentities) ? 'UserAssigned' : 'None' + +var identity = { + type: identityType + userAssignedIdentities: !empty(userAssignedIdentities) ? userAssignedIdentities : null +} + +resource defaultTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableDefaultTelemetry) { + name: 'pid-47ed15a6-730a-4827-bcb4-0fd963ffbd82-${uniqueString(deployment().name)}' + properties: { + mode: 'Incremental' + template: { + '$schema': 'https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#' + contentVersion: '1.0.0.0' + resources: [] + } + } +} + +resource webPubSub 'Microsoft.SignalRService/webPubSub@2021-10-01' = { + name: name + location: location + tags: tags + sku: { + capacity: capacity + name: sku + tier: sku == 'Standard_S1' ? 'Standard' : 'Free' + } + identity: identity + properties: { + disableAadAuth: disableAadAuth + disableLocalAuth: disableLocalAuth + networkACLs: !empty(networkAcls) ? any(networkAcls) : null + publicNetworkAccess: !empty(publicNetworkAccess) ? any(publicNetworkAccess) : (!empty(privateEndpoints) ? 'Disabled' : null) + resourceLogConfiguration: { + categories: resourceLogConfiguration + } + tls: { + clientCertEnabled: clientCertEnabled + } + } +} + +module webPubSub_privateEndpoints '../../Microsoft.Network/privateEndpoints/deploy.bicep' = [for (privateEndpoint, index) in privateEndpoints: { + name: '${uniqueString(deployment().name, location)}-appConfiguration-PrivateEndpoint-${index}' + params: { + groupIds: [ + privateEndpoint.service + ] + name: contains(privateEndpoint, 'name') ? privateEndpoint.name : 'pe-${last(split(webPubSub.id, '/'))}-${privateEndpoint.service}-${index}' + serviceResourceId: webPubSub.id + subnetResourceId: privateEndpoint.subnetResourceId + location: reference(split(privateEndpoint.subnetResourceId, '/subnets/')[0], '2020-06-01', 'Full').location + lock: contains(privateEndpoint, 'lock') ? privateEndpoint.lock : lock + privateDnsZoneGroups: contains(privateEndpoint, 'privateDnsZoneGroups') ? privateEndpoint.privateDnsZoneGroups : [] + roleAssignments: contains(privateEndpoint, 'roleAssignments') ? privateEndpoint.roleAssignments : [] + tags: contains(privateEndpoint, 'tags') ? privateEndpoint.tags : {} + manualPrivateLinkServiceConnections: contains(privateEndpoint, 'manualPrivateLinkServiceConnections') ? privateEndpoint.manualPrivateLinkServiceConnections : [] + customDnsConfigs: contains(privateEndpoint, 'customDnsConfigs') ? privateEndpoint.customDnsConfigs : [] + } +}] + +resource webPubSub_lock 'Microsoft.Authorization/locks@2017-04-01' = if (!empty(lock)) { + name: '${webPubSub.name}-${lock}-lock' + properties: { + level: any(lock) + notes: lock == 'CanNotDelete' ? 'Cannot delete resource or child resources.' : 'Cannot modify the resource or child resources.' + } + scope: webPubSub +} + +module webPubSub_rbac '.bicep/nested_rbac.bicep' = [for (roleAssignment, index) in roleAssignments: { + name: '${uniqueString(deployment().name, location)}-AppConfig-Rbac-${index}' + params: { + description: contains(roleAssignment, 'description') ? roleAssignment.description : '' + principalIds: roleAssignment.principalIds + principalType: contains(roleAssignment, 'principalType') ? roleAssignment.principalType : '' + roleDefinitionIdOrName: roleAssignment.roleDefinitionIdOrName + resourceId: webPubSub.id + } +}] + +@description('The Web PubSub name.') +output name string = webPubSub.name + +@description('The Web PubSub resource group.') +output resourceGroupName string = resourceGroup().name + +@description('The Web PubSub resource ID.') +output resourceId string = webPubSub.id + +@description('The Web PubSub externalIP.') +output externalIP string = webPubSub.properties.externalIP + +@description('The Web PubSub hostName.') +output hostName string = webPubSub.properties.hostName + +@description('The Web PubSub publicPort.') +output publicPort int = webPubSub.properties.publicPort + +@description('The Web PubSub serverPort.') +output serverPort int = webPubSub.properties.serverPort + +@description('The location the resource was deployed into.') +output location string = webPubSub.location diff --git a/modules/Microsoft.SignalRService/webPubSub/readme.md b/modules/Microsoft.SignalRService/webPubSub/readme.md new file mode 100644 index 0000000000..c25260dd6a --- /dev/null +++ b/modules/Microsoft.SignalRService/webPubSub/readme.md @@ -0,0 +1,538 @@ +# SignalRService `[Microsoft.SignalRService/webPubSub]` + +This module deploys a Web PubSub resource. + +## Navigation + +- [Resource Types](#Resource-Types) +- [Parameters](#Parameters) +- [Outputs](#Outputs) +- [Deployment examples](#Deployment-examples) + +## Resource Types + +| Resource Type | API Version | +| :-- | :-- | +| `Microsoft.Authorization/locks` | [2017-04-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2017-04-01/locks) | +| `Microsoft.Authorization/roleAssignments` | [2020-10-01-preview](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Authorization/2020-10-01-preview/roleAssignments) | +| `Microsoft.Network/privateEndpoints` | [2021-05-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-05-01/privateEndpoints) | +| `Microsoft.Network/privateEndpoints/privateDnsZoneGroups` | [2021-05-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.Network/2021-05-01/privateEndpoints/privateDnsZoneGroups) | +| `Microsoft.SignalRService/webPubSub` | [2021-10-01](https://docs.microsoft.com/en-us/azure/templates/Microsoft.SignalRService/2021-10-01/webPubSub) | + +## Parameters + +**Required parameters** +| Parameter Name | Type | Description | +| :-- | :-- | :-- | +| `name` | string | The name of the Web PubSub resource. | + +**Optional parameters** +| Parameter Name | Type | Default Value | Allowed Values | Description | +| :-- | :-- | :-- | :-- | :-- | +| `capacity` | int | `1` | | The unit count of the resource. 1 by default. | +| `clientCertEnabled` | bool | `False` | | Request client certificate during TLS handshake if enabled. | +| `disableAadAuth` | bool | `False` | | When set as true, connection with AuthType=aad won't work. | +| `disableLocalAuth` | bool | `True` | | Disables all authentication methods other than AAD authentication. For security reasons, this value should be set to `true`. | +| `enableDefaultTelemetry` | bool | `True` | | Enable telemetry via the Customer Usage Attribution ID (GUID). | +| `location` | string | `[resourceGroup().location]` | | The location for all Web PubSub resources. | +| `lock` | string | `''` | `[, CanNotDelete, ReadOnly]` | Specify the type of lock. | +| `networkAcls` | object | `{object}` | | Networks ACLs, this value contains IPs to whitelist and/or Subnet information. Can only be set if the 'SKU' is not 'Free_F1'. For security reasons, it is recommended to set the DefaultAction Deny. | +| `privateEndpoints` | array | `[]` | | Configuration details for private endpoints. For security reasons, it is recommended to use private endpoints whenever possible. | +| `publicNetworkAccess` | string | `''` | `[, Enabled, Disabled]` | Whether or not public network access is allowed for this resource. For security reasons it should be disabled. If not specified, it will be disabled by default if private endpoints are set. | +| `resourceLogConfigurationsToEnable` | array | `[ConnectivityLogs, MessagingLogs]` | `[ConnectivityLogs, MessagingLogs]` | Control permission for data plane traffic coming from public networks while private endpoint is enabled. | +| `roleAssignments` | array | `[]` | | Array of role assignment objects that contain the 'roleDefinitionIdOrName' and 'principalId' to define RBAC role assignments on this resource. In the roleDefinitionIdOrName attribute, you can provide either the display name of the role definition, or its fully qualified ID in the following format: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11'. | +| `sku` | string | `'Standard_S1'` | `[Free_F1, Standard_S1]` | Pricing tier of App Configuration. | +| `systemAssignedIdentity` | bool | `False` | | Enables system assigned managed identity on the resource. | +| `tags` | object | `{object}` | | Tags of the resource. | +| `userAssignedIdentities` | object | `{object}` | | The ID(s) to assign to the resource. | + + +### Parameter Usage: `privateEndpoints` + +To use Private Endpoint the following dependencies must be deployed: + +- Destination subnet must be created with the following configuration option - `"privateEndpointNetworkPolicies": "Disabled"`. Setting this option acknowledges that NSG rules are not applied to Private Endpoints (this capability is coming soon). A full example is available in the Virtual Network Module. +- Although not strictly required, it is highly recommended to first create a private DNS Zone to host Private Endpoint DNS records. See [Azure Private Endpoint DNS configuration](https://docs.microsoft.com/en-us/azure/private-link/private-endpoint-dns) for more information. + +
+ +Parameter JSON format + +```json +"privateEndpoints": { + "value": [ + // Example showing all available fields + { + "name": "sxx-az-pe", // Optional: Name will be automatically generated if one is not provided here + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/sxx-az-vnet-x-001/subnets/sxx-az-subnet-x-001", + "service": "<>", // e.g. vault, registry, file, blob, queue, table etc. + "privateDnsZoneResourceIds": [ // Optional: No DNS record will be created if a private DNS zone Resource ID is not specified + "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.blob.core.windows.net" + ], + "customDnsConfigs": [ // Optional + { + "fqdn": "customname.test.local", + "ipAddresses": [ + "10.10.10.10" + ] + } + ] + }, + // Example showing only mandatory fields + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/sxx-az-vnet-x-001/subnets/sxx-az-subnet-x-001", + "service": "<>" // e.g. vault, registry, file, blob, queue, table etc. + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +privateEndpoints: [ + // Example showing all available fields + { + name: 'sxx-az-pe' // Optional: Name will be automatically generated if one is not provided here + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/sxx-az-vnet-x-001/subnets/sxx-az-subnet-x-001' + service: '<>' // e.g. vault registry file blob queue table etc. + privateDnsZoneResourceIds: [ // Optional: No DNS record will be created if a private DNS zone Resource ID is not specified + '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/privateDnsZones/privatelink.blob.core.windows.net' + ] + // Optional + customDnsConfigs: [ + { + fqdn: 'customname.test.local' + ipAddresses: [ + '10.10.10.10' + ] + } + ] + } + // Example showing only mandatory fields + { + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/sxx-az-vnet-x-001/subnets/sxx-az-subnet-x-001' + service: '<>' // e.g. vault registry file blob queue table etc. + } +] +``` + +
+

+ +### Parameter Usage: `roleAssignments` + +Create a role assignment for the given resource. If you want to assign a service principal / managed identity that is created in the same deployment, make sure to also specify the `'principalType'` parameter and set it to `'ServicePrincipal'`. This will ensure the role assignment waits for the principal's propagation in Azure. + +

+ +Parameter JSON format + +```json +"roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "description": "Reader Role Assignment", + "principalIds": [ + "12345678-1234-1234-1234-123456789012", // object 1 + "78945612-1234-1234-1234-123456789012" // object 2 + ] + }, + { + "roleDefinitionIdOrName": "/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11", + "principalIds": [ + "12345678-1234-1234-1234-123456789012" // object 1 + ], + "principalType": "ServicePrincipal" + } + ] +} +``` + +
+ +
+ +Bicep format + +```bicep +roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + description: 'Reader Role Assignment' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + '78945612-1234-1234-1234-123456789012' // object 2 + ] + } + { + roleDefinitionIdOrName: '/providers/Microsoft.Authorization/roleDefinitions/c2f4ef07-c644-48eb-af81-4b1b4947fb11' + principalIds: [ + '12345678-1234-1234-1234-123456789012' // object 1 + ] + principalType: 'ServicePrincipal' + } +] +``` + +
+

+ +### Parameter Usage: `tags` + +Tag names and tag values can be provided as needed. A tag can be left without a value. + +

+ +Parameter JSON format + +```json +"tags": { + "value": { + "Environment": "Non-Prod", + "Contact": "test.user@testcompany.com", + "PurchaseOrder": "1234", + "CostCenter": "7890", + "ServiceName": "DeploymentValidation", + "Role": "DeploymentValidation" + } +} +``` + +
+ +
+ +Bicep format + +```bicep +tags: { + Environment: 'Non-Prod' + Contact: 'test.user@testcompany.com' + PurchaseOrder: '1234' + CostCenter: '7890' + ServiceName: 'DeploymentValidation' + Role: 'DeploymentValidation' +} +``` + +
+

+ +### Parameter Usage: `userAssignedIdentities` + +You can specify multiple user assigned identities to a resource by providing additional resource IDs using the following format: + +

+ +Parameter JSON format + +```json +"userAssignedIdentities": { + "value": { + "/subscriptions/12345678-1234-1234-1234-123456789012/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001": {}, + "/subscriptions/12345678-1234-1234-1234-123456789012/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002": {} + } +} +``` + +
+ +
+ +Bicep format + +```bicep +userAssignedIdentities: { + '/subscriptions/12345678-1234-1234-1234-123456789012/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-001': {} + '/subscriptions/12345678-1234-1234-1234-123456789012/resourcegroups/validation-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/adp-sxx-az-msi-x-002': {} +} +``` + +
+

+ +### Parameter Usage: `networkAcls` + +Using this object you can configure the service's firewall. Note, that the `defaultAction` either allows all / denies all communication via the `publicNetwork` and `privateEndpoints`. You can subsequently allow/deny individual actions using the corresponding arrays. + +Either block supports any array of values: +- 'ClientConnection' +- 'RESTAPI' +- 'ServerConnection' +- 'Trace' + +

+ +Parameter JSON format + +```json +"networkAcls": { + "value": { + "defaultAction": "Deny", + "privateEndpoints": [ + { + "name": "pe-<>-az-pubsub-x-001-webpubsub-0", + "allow": [ + "ServerConnection", + "Trace" + ], + "deny": [] + } + ], + "publicNetwork": { + "allow": [ + "RESTAPI", + "Trace" + ], + "deny": [] + } + } +} +``` + +
+ +
+ +Bicep format + +```bicep +networkAcls: { + defaultAction: 'Deny' + privateEndpoints: [ + { + name: 'pe-<>-az-pubsub-x-001-webpubsub-0' + allow: [ + 'ServerConnection' + 'Trace' + ], + deny: [] + } + ] + publicNetwork: { + allow: [ + 'RESTAPI' + 'Trace' + ] + deny: [] + } +} +``` + +
+

+ +## Outputs + +| Output Name | Type | Description | +| :-- | :-- | :-- | +| `externalIP` | string | The Web PubSub externalIP. | +| `hostName` | string | The Web PubSub hostName. | +| `location` | string | The location the resource was deployed into. | +| `name` | string | The Web PubSub name. | +| `publicPort` | int | The Web PubSub publicPort. | +| `resourceGroupName` | string | The Web PubSub resource group. | +| `resourceId` | string | The Web PubSub resource ID. | +| `serverPort` | int | The Web PubSub serverPort. | + +## Deployment examples + +

Example 1

+ +
+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "name": { + "value": "<>-az-pubsub-min-001" + } + } +} +``` + +
+ +
+ +via Bicep module + +```bicep +module webPubSub './Microsoft.SignalRService/webPubSub/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-webPubSub' + params: { + name: '<>-az-pubsub-min-001' + } +} +``` + +
+

+ +

Example 2

+ +
+ +via JSON Parameter file + +```json +{ + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "location": { + "value": "westeurope" + }, + "name": { + "value": "<>-az-pubsub-x-001" + }, + "capacity": { + "value": 2 + }, + "clientCertEnabled": { + "value": false + }, + "disableAadAuth": { + "value": false + }, + "disableLocalAuth": { + "value": true + }, + "lock": { + "value": "CanNotDelete" + }, + "sku": { + "value": "Standard_S1" + }, + "roleAssignments": { + "value": [ + { + "roleDefinitionIdOrName": "Reader", + "principalIds": [ + "<>" + ] + } + ] + }, + "networkAcls": { + "value": { + "defaultAction": "Allow", + "privateEndpoints": [ + { + "name": "pe-<>-az-pubsub-x-001-webpubsub-0", + "allow": [], + "deny": [ + "ServerConnection", + "Trace" + ] + } + ], + "publicNetwork": { + "allow": [], + "deny": [ + "RESTAPI", + "Trace" + ] + } + } + }, + "systemAssignedIdentity": { + "value": true + }, + "tags": { + "value": { + "purpose": "test" + } + }, + "resourceLogConfigurationsToEnable": { + "value": [ + "ConnectivityLogs" + ] + }, + "privateEndpoints": { + "value": [ + { + "subnetResourceId": "/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints", + "service": "webpubsub" + } + ] + } + } +} +``` + +
+ +
+ +via Bicep module + +```bicep +module webPubSub './Microsoft.SignalRService/webPubSub/deploy.bicep' = { + name: '${uniqueString(deployment().name)}-webPubSub' + params: { + location: 'westeurope' + name: '<>-az-pubsub-x-001' + capacity: 2 + clientCertEnabled: false + disableAadAuth: false + disableLocalAuth: true + lock: 'CanNotDelete' + sku: 'Standard_S1' + roleAssignments: [ + { + roleDefinitionIdOrName: 'Reader' + principalIds: [ + '<>' + ] + } + ] + networkAcls: { + defaultAction: 'Allow' + privateEndpoints: [ + { + name: 'pe-<>-az-pubsub-x-001-webpubsub-0' + allow: [] + deny: [ + 'ServerConnection' + 'Trace' + ] + } + ] + publicNetwork: { + allow: [] + deny: [ + 'RESTAPI' + 'Trace' + ] + } + } + systemAssignedIdentity: true + tags: { + purpose: 'test' + } + resourceLogConfigurationsToEnable: [ + 'ConnectivityLogs' + ] + privateEndpoints: [ + { + subnetResourceId: '/subscriptions/<>/resourceGroups/validation-rg/providers/Microsoft.Network/virtualNetworks/adp-<>-az-vnet-x-001/subnets/<>-az-subnet-x-005-privateEndpoints' + service: 'webpubsub' + } + ] + } +} +``` + +
+

diff --git a/modules/Microsoft.SignalRService/webPubSub/version.json b/modules/Microsoft.SignalRService/webPubSub/version.json new file mode 100644 index 0000000000..badc0a2285 --- /dev/null +++ b/modules/Microsoft.SignalRService/webPubSub/version.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", + "version": "0.5" +}