From cb012dfd0959bdac00dca172becf433c03b47138 Mon Sep 17 00:00:00 2001 From: tsweatman1 <155660739+tsweatman1@users.noreply.github.com> Date: Tue, 29 Oct 2024 18:57:31 -0400 Subject: [PATCH 1/9] 1111 mlz add ons update component order in naming convention (#1118) * issue 1111 * naming convention and prefix count * GitHub Action: Build Bicep to JSON * Fixed max length in regex * Alphabetized params * GitHub Action: Build Bicep to JSON * Alphabetized params & var value * GitHub Action: Build Bicep to JSON * Alphabetized param, Removed empty line * GitHub Action: Build Bicep to JSON --------- Co-authored-by: github-actions Co-authored-by: Jason Masten --- src/bicep/form/mlz.portal.json | 6 +- src/bicep/mlz.bicep | 6 +- src/bicep/mlz.json | 102 +++++++++++++----- src/bicep/modules/linux-virtual-machine.bicep | 3 +- src/bicep/modules/naming-convention.bicep | 15 ++- src/bicep/modules/storage-account.bicep | 40 +++++-- src/bicep/modules/storage.bicep | 6 +- 7 files changed, 128 insertions(+), 50 deletions(-) diff --git a/src/bicep/form/mlz.portal.json b/src/bicep/form/mlz.portal.json index 77cc1d799..aaccce588 100644 --- a/src/bicep/form/mlz.portal.json +++ b/src/bicep/form/mlz.portal.json @@ -229,13 +229,13 @@ "label": "Resource Naming Prefix", "type": "Microsoft.Common.TextBox", "defaultValue": "", - "toolTip": "Specify a prefix (min 3 and max 6 lowercase characters and numbers) to prepend to all resources.", + "toolTip": "Specify a prefix (min 1 and max 6 lowercase characters and numbers) to prepend to all resources.", "constraints": { "required": true, "validations": [ { - "regex": "^[a-z][a-z0-9]{1,5}$", - "message": "The prefix must contain alphanumeric characters, begin with a letter, letters must be lowercase, and the length must be between 3 to 6 characters." + "regex": "^[a-z0-9]{1,6}$", + "message": "The prefix must contain alphanumeric characters, letters must be lowercase, and the length must be between 1 to 6 characters." } ] } diff --git a/src/bicep/mlz.bicep b/src/bicep/mlz.bicep index 75ee30713..6d54067ed 100644 --- a/src/bicep/mlz.bicep +++ b/src/bicep/mlz.bicep @@ -7,9 +7,9 @@ targetScope = 'subscription' // REQUIRED PARAMETERS -@minLength(3) +@minLength(1) @maxLength(6) -@description('A prefix, 3-6 alphanumeric characters without whitespace, used to prefix resources and generate uniqueness for resources with globally unique naming requirements like Storage Accounts and Log Analytics Workspaces') +@description('A prefix, 1-6 alphanumeric characters without whitespace, used to prefix resources and generate uniqueness for resources with globally unique naming requirements like Storage Accounts and Log Analytics Workspaces') param resourcePrefix string @allowed([ @@ -725,10 +725,12 @@ module storage 'modules/storage.bicep' = { blobsPrivateDnsZoneResourceId: networking.outputs.privateDnsZoneResourceIds.blob //deployIdentity: deployIdentity deploymentNameSuffix: deploymentNameSuffix + filesPrivateDnsZoneResourceId: networking.outputs.privateDnsZoneResourceIds.file keyVaultUri: customerManagedKeys.outputs.keyVaultUri location: location logStorageSkuName: logStorageSkuName mlzTags: logic.outputs.mlzTags + queuesPrivateDnsZoneResourceId: networking.outputs.privateDnsZoneResourceIds.queue resourceGroupNames: resourceGroups.outputs.names serviceToken: logic.outputs.tokens.service storageEncryptionKeyName: customerManagedKeys.outputs.storageKeyName diff --git a/src/bicep/mlz.json b/src/bicep/mlz.json index 2dc902750..f56a0f152 100644 --- a/src/bicep/mlz.json +++ b/src/bicep/mlz.json @@ -5,16 +5,16 @@ "_generator": { "name": "bicep", "version": "0.30.23.60470", - "templateHash": "12938630250141528569" + "templateHash": "16541325671879840467" } }, "parameters": { "resourcePrefix": { "type": "string", - "minLength": 3, + "minLength": 1, "maxLength": 6, "metadata": { - "description": "A prefix, 3-6 alphanumeric characters without whitespace, used to prefix resources and generate uniqueness for resources with globally unique naming requirements like Storage Accounts and Log Analytics Workspaces" + "description": "A prefix, 1-6 alphanumeric characters without whitespace, used to prefix resources and generate uniqueness for resources with globally unique naming requirements like Storage Accounts and Log Analytics Workspaces" } }, "environmentAbbreviation": { @@ -888,7 +888,7 @@ "_generator": { "name": "bicep", "version": "0.30.23.60470", - "templateHash": "11424932506966599764" + "templateHash": "16220429751656280628" } }, "parameters": { @@ -968,7 +968,7 @@ "_generator": { "name": "bicep", "version": "0.30.23.60470", - "templateHash": "7616623366375139473" + "templateHash": "3270228280505109788" } }, "parameters": { @@ -994,6 +994,7 @@ "tokens": { "type": "object", "defaultValue": { + "purpose": "purpose_token", "resource": "resource_token", "service": "service_token" } @@ -1429,8 +1430,8 @@ "locations": "[variables('$fxv#0')[environment().name]]", "locationAbbreviation": "[variables('locations')[parameters('location')].abbreviation]", "resourceAbbreviations": "[variables('$fxv#1')]", - "namingConvention": "[format('{0}-{1}{2}-{3}-{4}-{5}', toLower(parameters('resourcePrefix')), if(empty(parameters('stampIndex')), '', format('{0}-', parameters('stampIndex'))), parameters('tokens').resource, parameters('networkName'), parameters('environmentAbbreviation'), variables('locationAbbreviation'))]", - "namingConvention_Service": "[format('{0}-{1}{2}-{3}-{4}-{5}-{6}', toLower(parameters('resourcePrefix')), if(empty(parameters('stampIndex')), '', format('{0}-', parameters('stampIndex'))), parameters('tokens').resource, parameters('tokens').service, parameters('networkName'), parameters('environmentAbbreviation'), variables('locationAbbreviation'))]", + "namingConvention": "[format('{0}-{1}{2}-{3}-{4}-{5}', toLower(parameters('resourcePrefix')), if(empty(parameters('stampIndex')), '', format('{0}-', parameters('stampIndex'))), parameters('tokens').resource, parameters('networkName'), variables('locationAbbreviation'), parameters('environmentAbbreviation'))]", + "namingConvention_Service": "[format('{0}-{1}{2}-{3}-{4}-{5}-{6}', toLower(parameters('resourcePrefix')), if(empty(parameters('stampIndex')), '', format('{0}-', parameters('stampIndex'))), parameters('tokens').resource, parameters('networkName'), parameters('tokens').service, variables('locationAbbreviation'), parameters('environmentAbbreviation'))]", "names": { "actionGroup": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').actionGroups)]", "applicationGroup": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').applicationGroups)]", @@ -1489,8 +1490,14 @@ "routeTable": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').routeTables)]", "storageAccount": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').storageAccounts), parameters('networkName'), parameters('networkShortName'))]", "storageAccountDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').storageAccounts))]", - "storageAccountNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').storageAccounts))]", - "storageAccountPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').storageAccounts))]", + "storageAccountBlobNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-blob', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountFileNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-file', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountQueueNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-queue', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountTableNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-table', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountBlobPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-blob', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountFilePrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-file', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountQueuePrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-queue', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountTablePrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-table', variables('resourceAbbreviations').storageAccounts))]", "subnet": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').subnets)]", "userAssignedIdentity": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').userAssignedIdentities)]", "virtualMachine": "[replace(replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').virtualMachines), parameters('environmentAbbreviation'), first(parameters('environmentAbbreviation'))), parameters('networkName'), ''), '-', '')]", @@ -5703,7 +5710,7 @@ "_generator": { "name": "bicep", "version": "0.30.23.60470", - "templateHash": "5548269850951921678" + "templateHash": "3503875367735395147" } }, "parameters": { @@ -6040,7 +6047,7 @@ "_generator": { "name": "bicep", "version": "0.30.23.60470", - "templateHash": "15035921308988452197" + "templateHash": "5686981733462823694" } }, "parameters": { @@ -6195,7 +6202,7 @@ }, { "type": "Microsoft.Compute/virtualMachines/extensions", - "apiVersion": "2021-03-01", + "apiVersion": "2024-03-01", "name": "[format('{0}/{1}', parameters('name'), 'GuestAttestation')]", "location": "[parameters('location')]", "tags": "[union(if(contains(parameters('tags'), 'Microsoft.Compute/virtualMachines'), parameters('tags')['Microsoft.Compute/virtualMachines'], createObject()), parameters('mlzTags'))]", @@ -6204,7 +6211,6 @@ "type": "GuestAttestation", "typeHandlerVersion": "1.0", "autoUpgradeMinorVersion": true, - "enableAutomaticUpgrade": true, "settings": { "AttestationConfig": { "MaaSettings": { @@ -6903,6 +6909,9 @@ "deploymentNameSuffix": { "value": "[parameters('deploymentNameSuffix')]" }, + "filesPrivateDnsZoneResourceId": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-networking-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.privateDnsZoneResourceIds.value.file]" + }, "keyVaultUri": { "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-cmk-hub-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.keyVaultUri.value]" }, @@ -6915,6 +6924,9 @@ "mlzTags": { "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.mlzTags.value]" }, + "queuesPrivateDnsZoneResourceId": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-networking-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.privateDnsZoneResourceIds.value.queue]" + }, "resourceGroupNames": { "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-resource-groups-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.names.value]" }, @@ -6944,7 +6956,7 @@ "_generator": { "name": "bicep", "version": "0.30.23.60470", - "templateHash": "15851262372533796124" + "templateHash": "11618493610681415173" } }, "parameters": { @@ -6954,6 +6966,9 @@ "deploymentNameSuffix": { "type": "string" }, + "filesPrivateDnsZoneResourceId": { + "type": "string" + }, "keyVaultUri": { "type": "string" }, @@ -6966,6 +6981,9 @@ "mlzTags": { "type": "object" }, + "queuesPrivateDnsZoneResourceId": { + "type": "string" + }, "resourceGroupNames": { "type": "array" }, @@ -7008,6 +7026,9 @@ "blobsPrivateDnsZoneResourceId": { "value": "[parameters('blobsPrivateDnsZoneResourceId')]" }, + "filesPrivateDnsZoneResourceId": { + "value": "[parameters('filesPrivateDnsZoneResourceId')]" + }, "keyVaultUri": { "value": "[parameters('keyVaultUri')]" }, @@ -7017,6 +7038,9 @@ "mlzTags": { "value": "[parameters('mlzTags')]" }, + "queuesPrivateDnsZoneResourceId": { + "value": "[parameters('queuesPrivateDnsZoneResourceId')]" + }, "serviceToken": { "value": "[parameters('serviceToken')]" }, @@ -7049,13 +7073,16 @@ "_generator": { "name": "bicep", "version": "0.30.23.60470", - "templateHash": "10231061264498799420" + "templateHash": "13359240625570837130" } }, "parameters": { "blobsPrivateDnsZoneResourceId": { "type": "string" }, + "filesPrivateDnsZoneResourceId": { + "type": "string" + }, "keyVaultUri": { "type": "string" }, @@ -7065,6 +7092,9 @@ "mlzTags": { "type": "object" }, + "queuesPrivateDnsZoneResourceId": { + "type": "string" + }, "serviceToken": { "type": "string" }, @@ -7091,9 +7121,27 @@ } }, "variables": { - "zones": [ - "[parameters('blobsPrivateDnsZoneResourceId')]", - "[parameters('tablesPrivateDnsZoneResourceId')]" + "subResources": [ + { + "id": "[parameters('blobsPrivateDnsZoneResourceId')]", + "nic": "[parameters('tier').namingConvention.storageAccountBlobNetworkInterface]", + "pe": "[parameters('tier').namingConvention.storageAccountBlobPrivateEndpoint]" + }, + { + "id": "[parameters('filesPrivateDnsZoneResourceId')]", + "nic": "[parameters('tier').namingConvention.storageAccountFileNetworkInterface]", + "pe": "[parameters('tier').namingConvention.storageAccountFilePrivateEndpoint]" + }, + { + "id": "[parameters('queuesPrivateDnsZoneResourceId')]", + "nic": "[parameters('tier').namingConvention.storageAccountQueueNetworkInterface]", + "pe": "[parameters('tier').namingConvention.storageAccountQueuePrivateEndpoint]" + }, + { + "id": "[parameters('tablesPrivateDnsZoneResourceId')]", + "nic": "[parameters('tier').namingConvention.storageAccountTableNetworkInterface]", + "pe": "[parameters('tier').namingConvention.storageAccountTablePrivateEndpoint]" + } ] }, "resources": [ @@ -7164,22 +7212,22 @@ { "copy": { "name": "privateEndpoints", - "count": "[length(variables('zones'))]" + "count": "[length(variables('subResources'))]" }, "type": "Microsoft.Network/privateEndpoints", "apiVersion": "2023-04-01", - "name": "[replace(parameters('tier').namingConvention.storageAccountPrivateEndpoint, parameters('serviceToken'), format('{0}-log', split(split(variables('zones')[copyIndex()], '/')[8], '.')[1]))]", + "name": "[variables('subResources')[copyIndex()].pe]", "location": "[parameters('location')]", "tags": "[union(if(contains(parameters('tags'), 'Microsoft.Network/privateEndpoints'), parameters('tags')['Microsoft.Network/privateEndpoints'], createObject()), parameters('mlzTags'))]", "properties": { - "customNetworkInterfaceName": "[replace(parameters('tier').namingConvention.storageAccountNetworkInterface, parameters('serviceToken'), format('{0}-log', split(split(variables('zones')[copyIndex()], '/')[8], '.')[1]))]", + "customNetworkInterfaceName": "[variables('subResources')[copyIndex()].nic]", "privateLinkServiceConnections": [ { - "name": "[replace(parameters('tier').namingConvention.storageAccountPrivateEndpoint, parameters('serviceToken'), format('{0}-log', split(split(variables('zones')[copyIndex()], '/')[8], '.')[1]))]", + "name": "[variables('subResources')[copyIndex()].pe]", "properties": { "privateLinkServiceId": "[resourceId('Microsoft.Storage/storageAccounts', uniqueString(replace(parameters('tier').namingConvention.storageAccount, parameters('serviceToken'), 'log'), resourceGroup().id))]", "groupIds": [ - "[split(split(variables('zones')[copyIndex()], '/')[8], '.')[1]]" + "[split(split(variables('subResources')[copyIndex()].id, '/')[8], '.')[1]]" ] } } @@ -7195,23 +7243,23 @@ { "copy": { "name": "privateDnsZoneGroups", - "count": "[length(variables('zones'))]" + "count": "[length(variables('subResources'))]" }, "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", "apiVersion": "2021-08-01", - "name": "[format('{0}/{1}', replace(parameters('tier').namingConvention.storageAccountPrivateEndpoint, parameters('serviceToken'), format('{0}-log', split(split(variables('zones')[copyIndex()], '/')[8], '.')[1])), uniqueString(replace(parameters('tier').namingConvention.storageAccount, parameters('serviceToken'), 'log'), resourceGroup().id))]", + "name": "[format('{0}/{1}', variables('subResources')[copyIndex()].pe, uniqueString(replace(parameters('tier').namingConvention.storageAccount, parameters('serviceToken'), 'log'), resourceGroup().id))]", "properties": { "privateDnsZoneConfigs": [ { "name": "ipconfig1", "properties": { - "privateDnsZoneId": "[variables('zones')[copyIndex()]]" + "privateDnsZoneId": "[variables('subResources')[copyIndex()].id]" } } ] }, "dependsOn": [ - "[resourceId('Microsoft.Network/privateEndpoints', replace(parameters('tier').namingConvention.storageAccountPrivateEndpoint, parameters('serviceToken'), format('{0}-log', split(split(variables('zones')[copyIndex()], '/')[8], '.')[1])))]", + "[resourceId('Microsoft.Network/privateEndpoints', variables('subResources')[copyIndex()].pe)]", "[resourceId('Microsoft.Storage/storageAccounts', uniqueString(replace(parameters('tier').namingConvention.storageAccount, parameters('serviceToken'), 'log'), resourceGroup().id))]" ] } diff --git a/src/bicep/modules/linux-virtual-machine.bicep b/src/bicep/modules/linux-virtual-machine.bicep index c8f586480..50cb5379b 100644 --- a/src/bicep/modules/linux-virtual-machine.bicep +++ b/src/bicep/modules/linux-virtual-machine.bicep @@ -118,7 +118,7 @@ resource virtualMachine 'Microsoft.Compute/virtualMachines@2021-04-01' = { } } -resource guestAttestationExtension 'Microsoft.Compute/virtualMachines/extensions@2021-03-01' = { +resource guestAttestationExtension 'Microsoft.Compute/virtualMachines/extensions@2024-03-01' = { parent: virtualMachine name: 'GuestAttestation' location: location @@ -128,7 +128,6 @@ resource guestAttestationExtension 'Microsoft.Compute/virtualMachines/extensions type: 'GuestAttestation' typeHandlerVersion: '1.0' autoUpgradeMinorVersion: true - enableAutomaticUpgrade: true settings: { AttestationConfig: { MaaSettings: { diff --git a/src/bicep/modules/naming-convention.bicep b/src/bicep/modules/naming-convention.bicep index 1fc98d9f4..6e310959d 100644 --- a/src/bicep/modules/naming-convention.bicep +++ b/src/bicep/modules/naming-convention.bicep @@ -12,6 +12,7 @@ param networkShortName string param resourcePrefix string param stampIndex string = '' // Optional: Added to support AVD deployments param tokens object = { + purpose:'purpose_token' resource: 'resource_token' service: 'service_token' } @@ -33,8 +34,8 @@ var resourceAbbreviations = loadJsonContent('../data/resourceAbbreviations.json' */ -var namingConvention = '${toLower(resourcePrefix)}-${empty(stampIndex) ? '' : '${stampIndex}-'}${tokens.resource}-${networkName}-${environmentAbbreviation}-${locationAbbreviation}' -var namingConvention_Service = '${toLower(resourcePrefix)}-${empty(stampIndex) ? '' : '${stampIndex}-'}${tokens.resource}-${tokens.service}-${networkName}-${environmentAbbreviation}-${locationAbbreviation}' +var namingConvention = '${toLower(resourcePrefix)}-${empty(stampIndex) ? '' : '${stampIndex}-'}${tokens.resource}-${networkName}-${locationAbbreviation}-${environmentAbbreviation}' +var namingConvention_Service = '${toLower(resourcePrefix)}-${empty(stampIndex) ? '' : '${stampIndex}-'}${tokens.resource}-${networkName}-${tokens.service}-${locationAbbreviation}-${environmentAbbreviation}' /* @@ -106,8 +107,14 @@ var names = { routeTable: replace(namingConvention, tokens.resource, resourceAbbreviations.routeTables) storageAccount: replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.storageAccounts), networkName, networkShortName) storageAccountDiagnosticSetting: replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.diagnosticSettings), tokens.service, '${tokens.service}-${resourceAbbreviations.storageAccounts}') - storageAccountNetworkInterface: replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.networkInterfaces), tokens.service, '${tokens.service}-${resourceAbbreviations.storageAccounts}') - storageAccountPrivateEndpoint: replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.privateEndpoints), tokens.service, '${tokens.service}-${resourceAbbreviations.storageAccounts}') + storageAccountBlobNetworkInterface: replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.networkInterfaces), tokens.service, '${resourceAbbreviations.storageAccounts}-blob') + storageAccountFileNetworkInterface: replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.networkInterfaces), tokens.service, '${resourceAbbreviations.storageAccounts}-file') + storageAccountQueueNetworkInterface: replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.networkInterfaces), tokens.service, '${resourceAbbreviations.storageAccounts}-queue') + storageAccountTableNetworkInterface: replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.networkInterfaces), tokens.service, '${resourceAbbreviations.storageAccounts}-table') + storageAccountBlobPrivateEndpoint: replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.privateEndpoints), tokens.service, '${resourceAbbreviations.storageAccounts}-blob') + storageAccountFilePrivateEndpoint: replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.privateEndpoints), tokens.service, '${resourceAbbreviations.storageAccounts}-file') + storageAccountQueuePrivateEndpoint: replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.privateEndpoints), tokens.service, '${resourceAbbreviations.storageAccounts}-queue') + storageAccountTablePrivateEndpoint: replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.privateEndpoints), tokens.service, '${resourceAbbreviations.storageAccounts}-table') subnet: replace(namingConvention, tokens.resource, resourceAbbreviations.subnets) userAssignedIdentity: replace(namingConvention_Service, tokens.resource, resourceAbbreviations.userAssignedIdentities) virtualMachine: replace(replace(replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.virtualMachines), environmentAbbreviation, first(environmentAbbreviation)), networkName, ''), '-', '') diff --git a/src/bicep/modules/storage-account.bicep b/src/bicep/modules/storage-account.bicep index 629122ea0..7e71ac73d 100644 --- a/src/bicep/modules/storage-account.bicep +++ b/src/bicep/modules/storage-account.bicep @@ -4,9 +4,11 @@ Licensed under the MIT License. */ param blobsPrivateDnsZoneResourceId string +param filesPrivateDnsZoneResourceId string param keyVaultUri string param location string param mlzTags object +param queuesPrivateDnsZoneResourceId string param serviceToken string param skuName string param storageEncryptionKeyName string @@ -16,9 +18,27 @@ param tags object param tier object param userAssignedIdentityResourceId string -var zones = [ - blobsPrivateDnsZoneResourceId - tablesPrivateDnsZoneResourceId +var subResources = [ + { + id: blobsPrivateDnsZoneResourceId + nic: tier.namingConvention.storageAccountBlobNetworkInterface + pe: tier.namingConvention.storageAccountBlobPrivateEndpoint + } + { + id: filesPrivateDnsZoneResourceId + nic: tier.namingConvention.storageAccountFileNetworkInterface + pe: tier.namingConvention.storageAccountFilePrivateEndpoint + } + { + id: queuesPrivateDnsZoneResourceId + nic: tier.namingConvention.storageAccountQueueNetworkInterface + pe: tier.namingConvention.storageAccountQueuePrivateEndpoint + } + { + id: tablesPrivateDnsZoneResourceId + nic: tier.namingConvention.storageAccountTableNetworkInterface + pe: tier.namingConvention.storageAccountTablePrivateEndpoint + } ] resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = { @@ -84,19 +104,19 @@ resource storageAccount 'Microsoft.Storage/storageAccounts@2023-01-01' = { } } -resource privateEndpoints 'Microsoft.Network/privateEndpoints@2023-04-01' = [for (zone, i) in zones: { - name: replace(tier.namingConvention.storageAccountPrivateEndpoint, serviceToken, '${split(split(zone, '/')[8], '.')[1]}-log') +resource privateEndpoints 'Microsoft.Network/privateEndpoints@2023-04-01' = [for (resource, i) in subResources: { + name: resource.pe location: location tags: union(contains(tags, 'Microsoft.Network/privateEndpoints') ? tags['Microsoft.Network/privateEndpoints'] : {}, mlzTags) properties: { - customNetworkInterfaceName: replace(tier.namingConvention.storageAccountNetworkInterface, serviceToken, '${split(split(zone, '/')[8], '.')[1]}-log') + customNetworkInterfaceName: resource.nic privateLinkServiceConnections: [ { - name: replace(tier.namingConvention.storageAccountPrivateEndpoint, serviceToken, '${split(split(zone, '/')[8], '.')[1]}-log') + name: resource.pe properties: { privateLinkServiceId: storageAccount.id groupIds: [ - split(split(zone, '/')[8], '.')[1] + split(split(resource.id, '/')[8], '.')[1] ] } } @@ -107,7 +127,7 @@ resource privateEndpoints 'Microsoft.Network/privateEndpoints@2023-04-01' = [for } }] -resource privateDnsZoneGroups 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2021-08-01' = [for (zone, i) in zones: { +resource privateDnsZoneGroups 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2021-08-01' = [for (resource, i) in subResources: { parent: privateEndpoints[i] name: storageAccount.name properties: { @@ -116,7 +136,7 @@ resource privateDnsZoneGroups 'Microsoft.Network/privateEndpoints/privateDnsZone name: 'ipconfig1' properties: { #disable-next-line use-resource-id-functions - privateDnsZoneId: zone + privateDnsZoneId: resource.id } } ] diff --git a/src/bicep/modules/storage.bicep b/src/bicep/modules/storage.bicep index b90645e37..66963b552 100644 --- a/src/bicep/modules/storage.bicep +++ b/src/bicep/modules/storage.bicep @@ -8,10 +8,12 @@ targetScope = 'subscription' param blobsPrivateDnsZoneResourceId string //param deployIdentity bool param deploymentNameSuffix string +param filesPrivateDnsZoneResourceId string param keyVaultUri string param location string param logStorageSkuName string param mlzTags object +param queuesPrivateDnsZoneResourceId string param resourceGroupNames array param serviceToken string param storageEncryptionKeyName string @@ -25,9 +27,11 @@ module storageAccount 'storage-account.bicep' = [for (tier, i) in tiers: { scope: resourceGroup(tier.subscriptionId, resourceGroupNames[i]) params: { blobsPrivateDnsZoneResourceId: blobsPrivateDnsZoneResourceId + filesPrivateDnsZoneResourceId: filesPrivateDnsZoneResourceId keyVaultUri: keyVaultUri location: location mlzTags: mlzTags + queuesPrivateDnsZoneResourceId: queuesPrivateDnsZoneResourceId serviceToken: serviceToken skuName: logStorageSkuName storageEncryptionKeyName: storageEncryptionKeyName @@ -40,5 +44,3 @@ module storageAccount 'storage-account.bicep' = [for (tier, i) in tiers: { }] output storageAccountResourceIds array = [for (tier, i) in tiers: storageAccount[i].outputs.id] - - From 29e47e9e8ada265075bd0feee11558f68c490437 Mon Sep 17 00:00:00 2001 From: tsweatman1 <155660739+tsweatman1@users.noreply.github.com> Date: Wed, 30 Oct 2024 15:21:58 -0400 Subject: [PATCH 2/9] added enableAutomaticUpgrade back into code since issues was OBE. (#1119) * added enableAutomaticUpgrade back into code since issues was OBE. * GitHub Action: Build Bicep to JSON --------- Co-authored-by: github-actions --- src/bicep/mlz.json | 7 ++++--- src/bicep/modules/linux-virtual-machine.bicep | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/bicep/mlz.json b/src/bicep/mlz.json index f56a0f152..f0e6c0753 100644 --- a/src/bicep/mlz.json +++ b/src/bicep/mlz.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.30.23.60470", - "templateHash": "16541325671879840467" + "templateHash": "11000848548598385857" } }, "parameters": { @@ -5710,7 +5710,7 @@ "_generator": { "name": "bicep", "version": "0.30.23.60470", - "templateHash": "3503875367735395147" + "templateHash": "208914367173865032" } }, "parameters": { @@ -6047,7 +6047,7 @@ "_generator": { "name": "bicep", "version": "0.30.23.60470", - "templateHash": "5686981733462823694" + "templateHash": "4932706162089134974" } }, "parameters": { @@ -6211,6 +6211,7 @@ "type": "GuestAttestation", "typeHandlerVersion": "1.0", "autoUpgradeMinorVersion": true, + "enableAutomaticUpgrade": true, "settings": { "AttestationConfig": { "MaaSettings": { diff --git a/src/bicep/modules/linux-virtual-machine.bicep b/src/bicep/modules/linux-virtual-machine.bicep index 50cb5379b..3acb93d55 100644 --- a/src/bicep/modules/linux-virtual-machine.bicep +++ b/src/bicep/modules/linux-virtual-machine.bicep @@ -128,6 +128,7 @@ resource guestAttestationExtension 'Microsoft.Compute/virtualMachines/extensions type: 'GuestAttestation' typeHandlerVersion: '1.0' autoUpgradeMinorVersion: true + enableAutomaticUpgrade: true settings: { AttestationConfig: { MaaSettings: { From fe62f9008fb6aa10647167508ccd50540427fc80 Mon Sep 17 00:00:00 2001 From: Jonathan Core <56272039+JCoreMS@users.noreply.github.com> Date: Fri, 1 Nov 2024 17:19:51 -0400 Subject: [PATCH 3/9] JCore-ZeroTrustImg-bug#1120 (#1121) * Update uiDefinition.json Changed file extension validation to use regex * Update uiDefinition.json Adjusted initial warning box from icon to style property per Schema. * Update uiDefinition.json Fix Log Analytics selector for Compliance Tab * Update uiDefinition.json Added additional info box in Networking for clarification on what is being deployed. New spoke VNet and Subnet. * Update uiDefinition.json Fix syntax errors * Fixed spacing & prereqs URL * Fixed text & URI in info boxes --------- Co-authored-by: Jason Masten --- src/bicep/add-ons/imaging/uiDefinition.json | 106 ++++++++++---------- 1 file changed, 55 insertions(+), 51 deletions(-) diff --git a/src/bicep/add-ons/imaging/uiDefinition.json b/src/bicep/add-ons/imaging/uiDefinition.json index 4c3039e9a..3f07eeaf5 100644 --- a/src/bicep/add-ons/imaging/uiDefinition.json +++ b/src/bicep/add-ons/imaging/uiDefinition.json @@ -13,9 +13,9 @@ "name": "prerequisites", "type": "Microsoft.Common.InfoBox", "options": { - "text": "Prior to deployment, make sure you meet the prerequisites outlined in the resource pre-reqs section in Zero Trust Image solution documentation.", - "uri": "https://github.com/mikedzikowski/ZTAImage#prequisites", - "icon": "Warning" + "style": "Warning", + "text": "Prior to deployment, make sure you meet the prerequisites outlined in the Zero Trust Image solution documentation.", + "uri": "https://github.com/Azure/missionlz/blob/main/src/bicep/add-ons/imaging/README.md#prequisites" } }, { @@ -546,10 +546,10 @@ "constraints": { "required": true, "validations": [ - { - "isValid": "[endsWith(steps('customizations').office, '.exe')]", - "message": "The file name must end with '.exe'." - } + { + "regex": ".*\\.exe$", + "message": "The file name must end with '.exe'." + } ] }, "visible": "[steps('customizations').office.installOffice]" @@ -817,10 +817,10 @@ "constraints": { "required": true, "validations": [ - { - "isValid": "[endsWith(steps('customizations').office.teamsBlob, '.msi')]", - "message": "The file name must end with '.msi'." - } + { + "regex": ".*\\.msi$", + "message": "The file name must end with '.msi'." + } ] }, "visible": "[steps('customizations').office.installTeams]" @@ -836,10 +836,10 @@ "constraints": { "required": true, "validations": [ - { - "isValid": "[endsWith(steps('customizations').office.msrdcwebrtcsvcInstaller, '.msi')]", - "message": "The file name must end with '.msi'." - } + { + "regex": ".*\\.msi$", + "message": "The file name must end with '.msi'." + } ] }, "visible": "[steps('customizations').office.installTeams]" @@ -854,11 +854,11 @@ "multiLine": false, "constraints": { "required": true, - "validations": [ - { - "isValid": "[endsWith(steps('customizations').office.vcRedistInstaller, '.exe')]", - "message": "The file name must end with '.exe'." - } + "validations": [ + { + "regex": ".*\\.exe$", + "message": "The file name must end with '.exe'." + } ] }, "visible": "[steps('customizations').office.installTeams]" @@ -969,7 +969,7 @@ { "name": "vDotBlob", "type": "Microsoft.Common.TextBox", - "label": "Microsoft Visual C++ Redistributable Installer (.zip)", + "label": "Virtual Desktop Optimization Tool File Name (.zip)", "defaultValue": "Virtual-Desktop-Optimization-Tool-main.zip", "toolTip": "Input the file / blob name for the VDOT installer zip.", "placeholder": "", @@ -977,10 +977,10 @@ "constraints": { "required": true, "validations": [ - { - "isValid": "[endsWith(steps('customizations').office.vDotBlob, '.zip')]", - "message": "The file name must end with '.zip'." - } + { + "regex": ".*\\.zip$", + "message": "The file name must end with '.zip'." + } ] }, "visible": "[steps('customizations').specialitySoftware.installVirtualDesktopOptimizationTool]" @@ -1016,10 +1016,10 @@ "constraints": { "required": true, "validations": [ - { - "isValid": "[endsWith(steps('customizations').specialitySoftware.arcGisBlob, '.zip')]", - "message": "The file name must end with '.zip'." - } + { + "regex": ".*\\.zip$", + "message": "The file name must end with '.zip'." + } ] }, "visible": "[steps('customizations').specialitySoftware.installArcGisPro]" @@ -1266,6 +1266,23 @@ "name": "networking", "label": "Networking", "elements": [ + { + "name": "prerequisitesNetworking", + "type": "Microsoft.Common.InfoBox", + "options": { + "style":"Warning", + "text": "Prior to deployment, make sure you meet the prerequisites outlined in the Zero Trust Image solution documentation.", + "uri": "https://github.com/Azure/missionlz/blob/main/src/bicep/add-ons/imaging/README.md#prequisites" + } + }, + { + "name": "prerequisitesNetworking2", + "type": "Microsoft.Common.InfoBox", + "options": { + "style":"Info", + "text": "This section applies to the deployment of a new Spoke or Tier 3 environment specifically for imaging which will be created for you. Choose a network and subnet range that doesn't conflict with the Hub or any other VNets in your MLZ." + } + }, { "name": "networking", "label": "Networking", @@ -1449,26 +1466,13 @@ "path": "[concat(steps('basics').hub.virtualNetwork, '?api-version=2023-09-01')]" } }, - { - "name": "logAnalyticsWorkspacesApi", - "type": "Microsoft.Solutions.ArmApiControl", - "request": { - "method": "GET", - "path": "[concat('/subscriptions/', first(skip(split(first(map(filter(steps('compliance').virtualNetworkApi.properties.virtualNetworkPeerings, (item) => contains(item.properties.remoteVirtualNetwork.id, 'operations')), (item) => item.properties.remoteVirtualNetwork.id)), '/'), 2)), '/resourcegroups/', first(skip(split(first(map(filter(steps('compliance').virtualNetworkApi.properties.virtualNetworkPeerings, (item) => contains(item.properties.remoteVirtualNetwork.id, 'operations')), (item) => item.properties.remoteVirtualNetwork.id)), '/'), 4)), '/providers/Microsoft.OperationalInsights/workspaces?api-version=2023-09-01')]" - } - }, - { - "name": "logAnalyticsWorkspace", - "type": "Microsoft.Common.DropDown", - "visible": true, - "label": "Existing Log Analytics Workspace for Central Logging", - "defaultValue": "[first(map(steps('compliance').logAnalyticsWorkspacesApi.value, (item) => item.name))]", - "filter": true, - "toolTip": "Select the existing Hub Azure firewall.", - "constraints": { - "required": true, - "allowedValues": "[map(steps('compliance').logAnalyticsWorkspacesApi.value, (item) => parse(concat('{\"label\":\"', item.name, '\",\"value\":\"', item.id, '\"}')))]" - } + { + "name": "logAnalyticsWorkspace", + "type": "Microsoft.Solutions.ResourceSelector", + "label": "Existing Log Analytics Workspace for Central Logging (Spoke)", + "visible": true, + "resourceType": "Microsoft.OperationalInsights/workspaces", + "toolTip": "Select the log analytics workspace to capture runbook log output." }, { "name": "diagnosticSettingsApi", @@ -1559,7 +1563,7 @@ "policy": "[if(steps('compliance').policySection.deployPolicy, steps('compliance').policySection.policy, '')]", "replicaCount": "[steps('image').destination.replicaCount]", "sourceImageType": "[steps('image').source.type]", - "spokelogAnalyticsWorkspaceResourceId": "[steps('compliance').logAnalyticsWorkspace]", + "spokelogAnalyticsWorkspaceResourceId": "[steps('compliance').logAnalyticsWorkspace.id]", "storageAccountResourceId": "[steps('basics').artifacts.storageSelector.id]", "subnetAddressPrefix": "[steps('networking').networking.subnetAddressCidrRange]", "tags": "[steps('tags').tags]", @@ -1576,4 +1580,4 @@ "subscriptionId": "[steps('basics').scope.subscription.id]" } } -} \ No newline at end of file +} From f3b845996520357d5ed47681a231fd076d241de0 Mon Sep 17 00:00:00 2001 From: Jason Masten Date: Wed, 6 Nov 2024 13:09:44 -0500 Subject: [PATCH 4/9] Fixed conditions in AVD Add-On & missing params in Tier3 for storage (#1123) * Fixed value in condition * Fixed value in condition & missing params * Fixed missing params * Fixed resource prefix length in documentation * Fixed token used for antivirus exclusions * Updated conditions * Fixed conditions for FSLogix * Compiled bicep changes * Fixed UI conditions for profiles --- docs/deployment-guides/command-line-tools.md | 2 +- .../modules/fslogix/fslogix.bicep | 4 +- .../sessionHosts/virtualMachines.bicep | 4 +- .../azure-virtual-desktop/solution.bicep | 10 +- .../azure-virtual-desktop/solution.json | 150 +++++++++++++----- .../azure-virtual-desktop/uiDefinition.json | 17 +- src/bicep/add-ons/imaging/solution.json | 93 ++++++++--- src/bicep/add-ons/tier3/modules/storage.bicep | 4 + src/bicep/add-ons/tier3/solution.bicep | 16 +- src/bicep/add-ons/tier3/solution.json | 91 ++++++++--- 10 files changed, 268 insertions(+), 123 deletions(-) diff --git a/docs/deployment-guides/command-line-tools.md b/docs/deployment-guides/command-line-tools.md index 1bcb690b7..e9789170a 100644 --- a/docs/deployment-guides/command-line-tools.md +++ b/docs/deployment-guides/command-line-tools.md @@ -32,7 +32,7 @@ The following prerequisites are required on the target Azure subscription(s): ### Decide on a Resource Prefix -Resource Groups and resource names are derived from the required parameter `resourcePrefix`. Pick a unqiue resource prefix that is 3-6 alphanumeric characters in length without whitespaces. +Resource Groups and resource names are derived from the required parameter `resourcePrefix`. Pick a unqiue resource prefix that is 1-6 alphanumeric characters in length without whitespaces. ### One Subscription or Multiple diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/fslogix/fslogix.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/fslogix/fslogix.bicep index 298b9c194..1366803f2 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/modules/fslogix/fslogix.bicep +++ b/src/bicep/add-ons/azure-virtual-desktop/modules/fslogix/fslogix.bicep @@ -49,7 +49,7 @@ var tagsNetAppAccount = union({'cm-resource-parent': '${subscription().id}}/reso var tagsVirtualMachines = union({'cm-resource-parent': '${subscription().id}}/resourceGroups/${resourceGroupControlPlane}/providers/Microsoft.DesktopVirtualization/hostpools/${hostPoolName}'}, contains(tags, 'Microsoft.Compute/virtualMachines') ? tags['Microsoft.Compute/virtualMachines'] : {}, mlzTags) // Azure NetApp Files for Fslogix -module azureNetAppFiles 'azureNetAppFiles.bicep' = if (storageService == 'AzureNetAppFiles' && contains(activeDirectorySolution, 'DomainServices')) { +module azureNetAppFiles 'azureNetAppFiles.bicep' = if (storageService == 'AzureNetAppFiles') { name: 'deploy-anf-${deploymentNameSuffix}' scope: resourceGroup(resourceGroupStorage) params: { @@ -78,7 +78,7 @@ module azureNetAppFiles 'azureNetAppFiles.bicep' = if (storageService == 'AzureN } // Azure Files for FSLogix -module azureFiles 'azureFiles/azureFiles.bicep' = if (storageService == 'AzureFiles' && contains(activeDirectorySolution, 'DomainServices')) { +module azureFiles 'azureFiles/azureFiles.bicep' = if (storageService == 'AzureFiles') { name: 'deploy-azure-files-${deploymentNameSuffix}' scope: resourceGroup(resourceGroupStorage) params: { diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/sessionHosts/virtualMachines.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/sessionHosts/virtualMachines.bicep index 4e7947f93..f9eb38713 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/modules/sessionHosts/virtualMachines.bicep +++ b/src/bicep/add-ons/azure-virtual-desktop/modules/sessionHosts/virtualMachines.bicep @@ -72,7 +72,7 @@ var imageReference = empty(imageVersionResourceId) ? { } : { id: imageVersionResourceId } -var intune = contains(activeDirectorySolution, 'intuneEnrollment') +var intune = contains(activeDirectorySolution, 'IntuneEnrollment') var nvidiaVmSize = contains(nvidiaVmSizes, virtualMachineSize) var nvidiaVmSizes = [ 'Standard_NV6' @@ -94,7 +94,7 @@ var nvidiaVmSizes = [ ] var pooledHostPool = (split(hostPoolType, ' ')[0] == 'Pooled') var sessionHostNamePrefix = replace(virtualMachineNamePrefix, serviceToken, '') -var storageAccountToken = take('${storageAccountPrefix}??${uniqueToken}', 24) +var storageAccountToken = '${storageAccountPrefix}??' // The token is used for AntiVirus exclusions. The '??' represents the two digits at the end of each storage account name. resource hostPool 'Microsoft.DesktopVirtualization/hostPools@2023-09-05' existing = { name: hostPoolName diff --git a/src/bicep/add-ons/azure-virtual-desktop/solution.bicep b/src/bicep/add-ons/azure-virtual-desktop/solution.bicep index 5a3120d54..ade3f2ac6 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/solution.bicep +++ b/src/bicep/add-ons/azure-virtual-desktop/solution.bicep @@ -114,7 +114,7 @@ param fslogixContainerType string = 'ProfileContainer' 'AzureNetAppFiles Standard' // ANF with the Standard SKU, 320,000 IOPS 'AzureFiles Premium' // Azure Files Premium with a Private Endpoint, 100,000 IOPS 'AzureFiles Standard' // Azure Files Standard with the Large File Share option and a Private Endpoint, 20,000 IOPS - 'None' + 'None' // Local Profiles ]) @description('Enable an Fslogix storage option to manage user profiles for the AVD session hosts. The selected service & SKU should provide sufficient IOPS for all of your users. https://docs.microsoft.com/en-us/azure/architecture/example-scenario/wvd/windows-virtual-desktop-fslogix#performance-requirements') param fslogixStorageService string = 'AzureFiles Standard' @@ -316,9 +316,7 @@ var availabilitySetsCount = length(range(beginAvSetRange, (endAvSetRange - begin // OTHER LOGIC & COMPUTED VALUES var customImageId = empty(imageVersionResourceId) ? 'null' : '"${imageVersionResourceId}"' -var deployFslogix = fslogixStorageService == 'None' || !contains(activeDirectorySolution, 'DomainServices') - ? false - : true +var deployFslogix = contains(fslogixStorageService, 'Azure') && contains(activeDirectorySolution, 'DomainServices') ? true : false var fileShareNames = { CloudCacheProfileContainer: [ 'profile-containers' @@ -622,7 +620,7 @@ module workspaces 'modules/sharedServices/sharedServices.bicep' = { } } -module fslogix 'modules/fslogix/fslogix.bicep' = { +module fslogix 'modules/fslogix/fslogix.bicep' = if (deployFslogix) { name: 'deploy-fslogix-${deploymentNameSuffix}' params: { activeDirectorySolution: activeDirectorySolution @@ -726,7 +724,7 @@ module sessionHosts 'modules/sessionHosts/sessionHosts.bicep' = { serviceToken: tier3_hosts.outputs.tokens.service sessionHostBatchCount: sessionHostBatchCount sessionHostIndex: sessionHostIndex - storageAccountNamePrefix: fslogix.outputs.storageAccountNamePrefix + storageAccountNamePrefix: deployFslogix ? fslogix.outputs.storageAccountNamePrefix : '' storageCount: storageCount storageIndex: storageIndex storageService: storageService diff --git a/src/bicep/add-ons/azure-virtual-desktop/solution.json b/src/bicep/add-ons/azure-virtual-desktop/solution.json index 85881aa3a..5a3c4c326 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/solution.json +++ b/src/bicep/add-ons/azure-virtual-desktop/solution.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.30.23.60470", - "templateHash": "9820015838062663350" + "templateHash": "5169206045336313112" } }, "parameters": { @@ -582,7 +582,7 @@ "endAvSetRange": "[div(add(parameters('sessionHostCount'), parameters('sessionHostIndex')), variables('maxAvSetMembers'))]", "availabilitySetsCount": "[length(range(variables('beginAvSetRange'), add(sub(variables('endAvSetRange'), variables('beginAvSetRange')), 1)))]", "customImageId": "[if(empty(parameters('imageVersionResourceId')), 'null', format('\"{0}\"', parameters('imageVersionResourceId')))]", - "deployFslogix": "[if(or(equals(parameters('fslogixStorageService'), 'None'), not(contains(parameters('activeDirectorySolution'), 'DomainServices'))), false(), true())]", + "deployFslogix": "[if(and(contains(parameters('fslogixStorageService'), 'Azure'), contains(parameters('activeDirectorySolution'), 'DomainServices')), true(), false())]", "fileShareNames": { "CloudCacheProfileContainer": [ "profile-containers" @@ -679,7 +679,7 @@ "_generator": { "name": "bicep", "version": "0.30.23.60470", - "templateHash": "7616623366375139473" + "templateHash": "3270228280505109788" } }, "parameters": { @@ -705,6 +705,7 @@ "tokens": { "type": "object", "defaultValue": { + "purpose": "purpose_token", "resource": "resource_token", "service": "service_token" } @@ -1140,8 +1141,8 @@ "locations": "[variables('$fxv#0')[environment().name]]", "locationAbbreviation": "[variables('locations')[parameters('location')].abbreviation]", "resourceAbbreviations": "[variables('$fxv#1')]", - "namingConvention": "[format('{0}-{1}{2}-{3}-{4}-{5}', toLower(parameters('resourcePrefix')), if(empty(parameters('stampIndex')), '', format('{0}-', parameters('stampIndex'))), parameters('tokens').resource, parameters('networkName'), parameters('environmentAbbreviation'), variables('locationAbbreviation'))]", - "namingConvention_Service": "[format('{0}-{1}{2}-{3}-{4}-{5}-{6}', toLower(parameters('resourcePrefix')), if(empty(parameters('stampIndex')), '', format('{0}-', parameters('stampIndex'))), parameters('tokens').resource, parameters('tokens').service, parameters('networkName'), parameters('environmentAbbreviation'), variables('locationAbbreviation'))]", + "namingConvention": "[format('{0}-{1}{2}-{3}-{4}-{5}', toLower(parameters('resourcePrefix')), if(empty(parameters('stampIndex')), '', format('{0}-', parameters('stampIndex'))), parameters('tokens').resource, parameters('networkName'), variables('locationAbbreviation'), parameters('environmentAbbreviation'))]", + "namingConvention_Service": "[format('{0}-{1}{2}-{3}-{4}-{5}-{6}', toLower(parameters('resourcePrefix')), if(empty(parameters('stampIndex')), '', format('{0}-', parameters('stampIndex'))), parameters('tokens').resource, parameters('networkName'), parameters('tokens').service, variables('locationAbbreviation'), parameters('environmentAbbreviation'))]", "names": { "actionGroup": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').actionGroups)]", "applicationGroup": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').applicationGroups)]", @@ -1200,8 +1201,14 @@ "routeTable": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').routeTables)]", "storageAccount": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').storageAccounts), parameters('networkName'), parameters('networkShortName'))]", "storageAccountDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').storageAccounts))]", - "storageAccountNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').storageAccounts))]", - "storageAccountPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').storageAccounts))]", + "storageAccountBlobNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-blob', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountFileNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-file', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountQueueNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-queue', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountTableNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-table', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountBlobPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-blob', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountFilePrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-file', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountQueuePrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-queue', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountTablePrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-table', variables('resourceAbbreviations').storageAccounts))]", "subnet": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').subnets)]", "userAssignedIdentity": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').userAssignedIdentities)]", "virtualMachine": "[replace(replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').virtualMachines), parameters('environmentAbbreviation'), first(parameters('environmentAbbreviation'))), parameters('networkName'), ''), '-', '')]", @@ -1278,7 +1285,7 @@ "_generator": { "name": "bicep", "version": "0.30.23.60470", - "templateHash": "7616623366375139473" + "templateHash": "3270228280505109788" } }, "parameters": { @@ -1304,6 +1311,7 @@ "tokens": { "type": "object", "defaultValue": { + "purpose": "purpose_token", "resource": "resource_token", "service": "service_token" } @@ -1739,8 +1747,8 @@ "locations": "[variables('$fxv#0')[environment().name]]", "locationAbbreviation": "[variables('locations')[parameters('location')].abbreviation]", "resourceAbbreviations": "[variables('$fxv#1')]", - "namingConvention": "[format('{0}-{1}{2}-{3}-{4}-{5}', toLower(parameters('resourcePrefix')), if(empty(parameters('stampIndex')), '', format('{0}-', parameters('stampIndex'))), parameters('tokens').resource, parameters('networkName'), parameters('environmentAbbreviation'), variables('locationAbbreviation'))]", - "namingConvention_Service": "[format('{0}-{1}{2}-{3}-{4}-{5}-{6}', toLower(parameters('resourcePrefix')), if(empty(parameters('stampIndex')), '', format('{0}-', parameters('stampIndex'))), parameters('tokens').resource, parameters('tokens').service, parameters('networkName'), parameters('environmentAbbreviation'), variables('locationAbbreviation'))]", + "namingConvention": "[format('{0}-{1}{2}-{3}-{4}-{5}', toLower(parameters('resourcePrefix')), if(empty(parameters('stampIndex')), '', format('{0}-', parameters('stampIndex'))), parameters('tokens').resource, parameters('networkName'), variables('locationAbbreviation'), parameters('environmentAbbreviation'))]", + "namingConvention_Service": "[format('{0}-{1}{2}-{3}-{4}-{5}-{6}', toLower(parameters('resourcePrefix')), if(empty(parameters('stampIndex')), '', format('{0}-', parameters('stampIndex'))), parameters('tokens').resource, parameters('networkName'), parameters('tokens').service, variables('locationAbbreviation'), parameters('environmentAbbreviation'))]", "names": { "actionGroup": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').actionGroups)]", "applicationGroup": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').applicationGroups)]", @@ -1799,8 +1807,14 @@ "routeTable": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').routeTables)]", "storageAccount": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').storageAccounts), parameters('networkName'), parameters('networkShortName'))]", "storageAccountDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').storageAccounts))]", - "storageAccountNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').storageAccounts))]", - "storageAccountPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').storageAccounts))]", + "storageAccountBlobNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-blob', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountFileNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-file', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountQueueNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-queue', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountTableNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-table', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountBlobPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-blob', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountFilePrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-file', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountQueuePrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-queue', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountTablePrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-table', variables('resourceAbbreviations').storageAccounts))]", "subnet": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').subnets)]", "userAssignedIdentity": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').userAssignedIdentities)]", "virtualMachine": "[replace(replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').virtualMachines), parameters('environmentAbbreviation'), first(parameters('environmentAbbreviation'))), parameters('networkName'), ''), '-', '')]", @@ -1922,7 +1936,7 @@ "_generator": { "name": "bicep", "version": "0.30.23.60470", - "templateHash": "5332647717093931418" + "templateHash": "16140764241259827428" } }, "parameters": { @@ -2199,7 +2213,7 @@ "_generator": { "name": "bicep", "version": "0.30.23.60470", - "templateHash": "11424932506966599764" + "templateHash": "16220429751656280628" } }, "parameters": { @@ -2279,7 +2293,7 @@ "_generator": { "name": "bicep", "version": "0.30.23.60470", - "templateHash": "7616623366375139473" + "templateHash": "3270228280505109788" } }, "parameters": { @@ -2305,6 +2319,7 @@ "tokens": { "type": "object", "defaultValue": { + "purpose": "purpose_token", "resource": "resource_token", "service": "service_token" } @@ -2740,8 +2755,8 @@ "locations": "[variables('$fxv#0')[environment().name]]", "locationAbbreviation": "[variables('locations')[parameters('location')].abbreviation]", "resourceAbbreviations": "[variables('$fxv#1')]", - "namingConvention": "[format('{0}-{1}{2}-{3}-{4}-{5}', toLower(parameters('resourcePrefix')), if(empty(parameters('stampIndex')), '', format('{0}-', parameters('stampIndex'))), parameters('tokens').resource, parameters('networkName'), parameters('environmentAbbreviation'), variables('locationAbbreviation'))]", - "namingConvention_Service": "[format('{0}-{1}{2}-{3}-{4}-{5}-{6}', toLower(parameters('resourcePrefix')), if(empty(parameters('stampIndex')), '', format('{0}-', parameters('stampIndex'))), parameters('tokens').resource, parameters('tokens').service, parameters('networkName'), parameters('environmentAbbreviation'), variables('locationAbbreviation'))]", + "namingConvention": "[format('{0}-{1}{2}-{3}-{4}-{5}', toLower(parameters('resourcePrefix')), if(empty(parameters('stampIndex')), '', format('{0}-', parameters('stampIndex'))), parameters('tokens').resource, parameters('networkName'), variables('locationAbbreviation'), parameters('environmentAbbreviation'))]", + "namingConvention_Service": "[format('{0}-{1}{2}-{3}-{4}-{5}-{6}', toLower(parameters('resourcePrefix')), if(empty(parameters('stampIndex')), '', format('{0}-', parameters('stampIndex'))), parameters('tokens').resource, parameters('networkName'), parameters('tokens').service, variables('locationAbbreviation'), parameters('environmentAbbreviation'))]", "names": { "actionGroup": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').actionGroups)]", "applicationGroup": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').applicationGroups)]", @@ -2800,8 +2815,14 @@ "routeTable": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').routeTables)]", "storageAccount": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').storageAccounts), parameters('networkName'), parameters('networkShortName'))]", "storageAccountDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').storageAccounts))]", - "storageAccountNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').storageAccounts))]", - "storageAccountPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').storageAccounts))]", + "storageAccountBlobNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-blob', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountFileNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-file', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountQueueNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-queue', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountTableNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-table', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountBlobPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-blob', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountFilePrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-file', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountQueuePrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-queue', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountTablePrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-table', variables('resourceAbbreviations').storageAccounts))]", "subnet": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').subnets)]", "userAssignedIdentity": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').userAssignedIdentities)]", "virtualMachine": "[replace(replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').virtualMachines), parameters('environmentAbbreviation'), first(parameters('environmentAbbreviation'))), parameters('networkName'), ''), '-', '')]", @@ -4858,6 +4879,9 @@ "blobsPrivateDnsZoneResourceId": { "value": "[resourceId(variables('hubSubscriptionId'), variables('hubResourceGroupName'), 'Microsoft.Network/privateDnsZones', format('privatelink.blob.{0}', environment().suffixes.storage))]" }, + "filesPrivateDnsZoneResourceId": { + "value": "[resourceId(variables('hubSubscriptionId'), variables('hubResourceGroupName'), 'Microsoft.Network/privateDnsZones', format('privatelink.file.{0}', environment().suffixes.storage))]" + }, "keyVaultUri": { "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-cmk-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.keyVaultUri.value]" }, @@ -4873,6 +4897,9 @@ "network": { "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tiers.value[0]]" }, + "queuesPrivateDnsZoneResourceId": { + "value": "[resourceId(variables('hubSubscriptionId'), variables('hubResourceGroupName'), 'Microsoft.Network/privateDnsZones', format('privatelink.queue.{0}', environment().suffixes.storage))]" + }, "resourceGroupName": { "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.name.value]" }, @@ -4905,13 +4932,16 @@ "_generator": { "name": "bicep", "version": "0.30.23.60470", - "templateHash": "17995428208905585384" + "templateHash": "2377877844306490130" } }, "parameters": { "blobsPrivateDnsZoneResourceId": { "type": "string" }, + "filesPrivateDnsZoneResourceId": { + "type": "string" + }, "keyVaultUri": { "type": "string" }, @@ -4927,6 +4957,9 @@ "network": { "type": "object" }, + "queuesPrivateDnsZoneResourceId": { + "type": "string" + }, "resourceGroupName": { "type": "string" }, @@ -4968,6 +5001,9 @@ "blobsPrivateDnsZoneResourceId": { "value": "[parameters('blobsPrivateDnsZoneResourceId')]" }, + "filesPrivateDnsZoneResourceId": { + "value": "[parameters('filesPrivateDnsZoneResourceId')]" + }, "keyVaultUri": { "value": "[parameters('keyVaultUri')]" }, @@ -4977,6 +5013,9 @@ "mlzTags": { "value": "[parameters('mlzTags')]" }, + "queuesPrivateDnsZoneResourceId": { + "value": "[parameters('queuesPrivateDnsZoneResourceId')]" + }, "serviceToken": { "value": "[parameters('serviceToken')]" }, @@ -5009,13 +5048,16 @@ "_generator": { "name": "bicep", "version": "0.30.23.60470", - "templateHash": "10231061264498799420" + "templateHash": "13359240625570837130" } }, "parameters": { "blobsPrivateDnsZoneResourceId": { "type": "string" }, + "filesPrivateDnsZoneResourceId": { + "type": "string" + }, "keyVaultUri": { "type": "string" }, @@ -5025,6 +5067,9 @@ "mlzTags": { "type": "object" }, + "queuesPrivateDnsZoneResourceId": { + "type": "string" + }, "serviceToken": { "type": "string" }, @@ -5051,9 +5096,27 @@ } }, "variables": { - "zones": [ - "[parameters('blobsPrivateDnsZoneResourceId')]", - "[parameters('tablesPrivateDnsZoneResourceId')]" + "subResources": [ + { + "id": "[parameters('blobsPrivateDnsZoneResourceId')]", + "nic": "[parameters('tier').namingConvention.storageAccountBlobNetworkInterface]", + "pe": "[parameters('tier').namingConvention.storageAccountBlobPrivateEndpoint]" + }, + { + "id": "[parameters('filesPrivateDnsZoneResourceId')]", + "nic": "[parameters('tier').namingConvention.storageAccountFileNetworkInterface]", + "pe": "[parameters('tier').namingConvention.storageAccountFilePrivateEndpoint]" + }, + { + "id": "[parameters('queuesPrivateDnsZoneResourceId')]", + "nic": "[parameters('tier').namingConvention.storageAccountQueueNetworkInterface]", + "pe": "[parameters('tier').namingConvention.storageAccountQueuePrivateEndpoint]" + }, + { + "id": "[parameters('tablesPrivateDnsZoneResourceId')]", + "nic": "[parameters('tier').namingConvention.storageAccountTableNetworkInterface]", + "pe": "[parameters('tier').namingConvention.storageAccountTablePrivateEndpoint]" + } ] }, "resources": [ @@ -5124,22 +5187,22 @@ { "copy": { "name": "privateEndpoints", - "count": "[length(variables('zones'))]" + "count": "[length(variables('subResources'))]" }, "type": "Microsoft.Network/privateEndpoints", "apiVersion": "2023-04-01", - "name": "[replace(parameters('tier').namingConvention.storageAccountPrivateEndpoint, parameters('serviceToken'), format('{0}-log', split(split(variables('zones')[copyIndex()], '/')[8], '.')[1]))]", + "name": "[variables('subResources')[copyIndex()].pe]", "location": "[parameters('location')]", "tags": "[union(if(contains(parameters('tags'), 'Microsoft.Network/privateEndpoints'), parameters('tags')['Microsoft.Network/privateEndpoints'], createObject()), parameters('mlzTags'))]", "properties": { - "customNetworkInterfaceName": "[replace(parameters('tier').namingConvention.storageAccountNetworkInterface, parameters('serviceToken'), format('{0}-log', split(split(variables('zones')[copyIndex()], '/')[8], '.')[1]))]", + "customNetworkInterfaceName": "[variables('subResources')[copyIndex()].nic]", "privateLinkServiceConnections": [ { - "name": "[replace(parameters('tier').namingConvention.storageAccountPrivateEndpoint, parameters('serviceToken'), format('{0}-log', split(split(variables('zones')[copyIndex()], '/')[8], '.')[1]))]", + "name": "[variables('subResources')[copyIndex()].pe]", "properties": { "privateLinkServiceId": "[resourceId('Microsoft.Storage/storageAccounts', uniqueString(replace(parameters('tier').namingConvention.storageAccount, parameters('serviceToken'), 'log'), resourceGroup().id))]", "groupIds": [ - "[split(split(variables('zones')[copyIndex()], '/')[8], '.')[1]]" + "[split(split(variables('subResources')[copyIndex()].id, '/')[8], '.')[1]]" ] } } @@ -5155,23 +5218,23 @@ { "copy": { "name": "privateDnsZoneGroups", - "count": "[length(variables('zones'))]" + "count": "[length(variables('subResources'))]" }, "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", "apiVersion": "2021-08-01", - "name": "[format('{0}/{1}', replace(parameters('tier').namingConvention.storageAccountPrivateEndpoint, parameters('serviceToken'), format('{0}-log', split(split(variables('zones')[copyIndex()], '/')[8], '.')[1])), uniqueString(replace(parameters('tier').namingConvention.storageAccount, parameters('serviceToken'), 'log'), resourceGroup().id))]", + "name": "[format('{0}/{1}', variables('subResources')[copyIndex()].pe, uniqueString(replace(parameters('tier').namingConvention.storageAccount, parameters('serviceToken'), 'log'), resourceGroup().id))]", "properties": { "privateDnsZoneConfigs": [ { "name": "ipconfig1", "properties": { - "privateDnsZoneId": "[variables('zones')[copyIndex()]]" + "privateDnsZoneId": "[variables('subResources')[copyIndex()].id]" } } ] }, "dependsOn": [ - "[resourceId('Microsoft.Network/privateEndpoints', replace(parameters('tier').namingConvention.storageAccountPrivateEndpoint, parameters('serviceToken'), format('{0}-log', split(split(variables('zones')[copyIndex()], '/')[8], '.')[1])))]", + "[resourceId('Microsoft.Network/privateEndpoints', variables('subResources')[copyIndex()].pe)]", "[resourceId('Microsoft.Storage/storageAccounts', uniqueString(replace(parameters('tier').namingConvention.storageAccount, parameters('serviceToken'), 'log'), resourceGroup().id))]" ] } @@ -9064,9 +9127,9 @@ } }, "dependsOn": [ + "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', variables('resourceGroupServices')[2], parameters('deploymentNameSuffix')))]", "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', variables('resourceGroupServices')[1], parameters('deploymentNameSuffix')))]", "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', variables('resourceGroupServices')[0], parameters('deploymentNameSuffix')))]", - "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', variables('resourceGroupServices')[2], parameters('deploymentNameSuffix')))]", "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix')))]" ] }, @@ -10861,6 +10924,7 @@ ] }, { + "condition": "[variables('deployFslogix')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('deploy-fslogix-{0}', parameters('deploymentNameSuffix'))]", @@ -10997,7 +11061,7 @@ "_generator": { "name": "bicep", "version": "0.30.23.60470", - "templateHash": "4149294850802164869" + "templateHash": "3321762213194063115" } }, "parameters": { @@ -11129,7 +11193,7 @@ }, "resources": [ { - "condition": "[and(equals(parameters('storageService'), 'AzureNetAppFiles'), contains(parameters('activeDirectorySolution'), 'DomainServices'))]", + "condition": "[equals(parameters('storageService'), 'AzureNetAppFiles')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('deploy-anf-{0}', parameters('deploymentNameSuffix'))]", @@ -11487,7 +11551,7 @@ } }, { - "condition": "[and(equals(parameters('storageService'), 'AzureFiles'), contains(parameters('activeDirectorySolution'), 'DomainServices'))]", + "condition": "[equals(parameters('storageService'), 'AzureFiles')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('deploy-azure-files-{0}', parameters('deploymentNameSuffix'))]", @@ -12594,9 +12658,7 @@ "sessionHostIndex": { "value": "[parameters('sessionHostIndex')]" }, - "storageAccountNamePrefix": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-fslogix-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.storageAccountNamePrefix.value]" - }, + "storageAccountNamePrefix": "[if(variables('deployFslogix'), createObject('value', reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-fslogix-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.storageAccountNamePrefix.value), createObject('value', ''))]", "storageCount": { "value": "[parameters('storageCount')]" }, @@ -12632,7 +12694,7 @@ "_generator": { "name": "bicep", "version": "0.30.23.60470", - "templateHash": "15054116968510662395" + "templateHash": "14352967056938252569" } }, "parameters": { @@ -13123,7 +13185,7 @@ "_generator": { "name": "bicep", "version": "0.30.23.60470", - "templateHash": "10616160910124347908" + "templateHash": "1759351975000742065" } }, "parameters": { @@ -13296,7 +13358,7 @@ "fslogixOfficeShare": "[format('\\\\{0}.file.{1}\\office-containers\\*\\*.VHDX', variables('storageAccountToken'), parameters('storageSuffix'))]", "fslogixProfileShare": "[format('\\\\{0}.file.{1}\\profile-containers\\*\\*.VHDX', variables('storageAccountToken'), parameters('storageSuffix'))]", "imageReference": "[if(empty(parameters('imageVersionResourceId')), createObject('publisher', parameters('imagePublisher'), 'offer', parameters('imageOffer'), 'sku', parameters('imageSku'), 'version', 'latest'), createObject('id', parameters('imageVersionResourceId')))]", - "intune": "[contains(parameters('activeDirectorySolution'), 'intuneEnrollment')]", + "intune": "[contains(parameters('activeDirectorySolution'), 'IntuneEnrollment')]", "nvidiaVmSize": "[contains(variables('nvidiaVmSizes'), parameters('virtualMachineSize'))]", "nvidiaVmSizes": [ "Standard_NV6", @@ -13318,7 +13380,7 @@ ], "pooledHostPool": "[equals(split(parameters('hostPoolType'), ' ')[0], 'Pooled')]", "sessionHostNamePrefix": "[replace(parameters('virtualMachineNamePrefix'), parameters('serviceToken'), '')]", - "storageAccountToken": "[take(format('{0}??{1}', parameters('storageAccountPrefix'), parameters('uniqueToken')), 24)]" + "storageAccountToken": "[format('{0}??', parameters('storageAccountPrefix'))]" }, "resources": [ { diff --git a/src/bicep/add-ons/azure-virtual-desktop/uiDefinition.json b/src/bicep/add-ons/azure-virtual-desktop/uiDefinition.json index 7e6cd692e..92ed99d8e 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/uiDefinition.json +++ b/src/bicep/add-ons/azure-virtual-desktop/uiDefinition.json @@ -1020,28 +1020,19 @@ "visible": "[equals(steps('hosts').identity.solution, 'MicrosoftEntraId')]", "options": { "style": "Info", - "text": "This solution currently does not support FSLogix with Entra joined session hosts." + "text": "Only local profiles may be selected with Entra joined session hosts due to the security risks." } }, { "name": "profileSolution", "type": "Microsoft.Common.DropDown", "label": "Profile Solution", - "visible": "[contains(steps('hosts').identity.solution, 'DomainServices')]", - "defaultValue": "Local", + "visible": true, + "defaultValue": "Local Profiles", "toolTip": "Select the user profile solution for your end users.", "constraints": { "required": true, - "allowedValues": [ - { - "label": "FSLogix", - "value": "fslogix" - }, - { - "label": "Local", - "value": "local" - } - ] + "allowedValues": "[if(equals(steps('hosts').identity.solution, 'MicrosoftEntraId'), parse('[{\"label\":\"Local Profiles\",\"value\":\"local\"}]'), parse('[{\"label\":\"Local Profiles\",\"value\":\"local\"},{\"label\":\"FSLogix\",\"value\":\"fslogix\"}]'))]" } }, { diff --git a/src/bicep/add-ons/imaging/solution.json b/src/bicep/add-ons/imaging/solution.json index 70b71996e..b13319935 100644 --- a/src/bicep/add-ons/imaging/solution.json +++ b/src/bicep/add-ons/imaging/solution.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.30.23.60470", - "templateHash": "7574127122781673801" + "templateHash": "13460307319082964405" } }, "parameters": { @@ -601,7 +601,7 @@ "_generator": { "name": "bicep", "version": "0.30.23.60470", - "templateHash": "5332647717093931418" + "templateHash": "16140764241259827428" } }, "parameters": { @@ -878,7 +878,7 @@ "_generator": { "name": "bicep", "version": "0.30.23.60470", - "templateHash": "11424932506966599764" + "templateHash": "16220429751656280628" } }, "parameters": { @@ -958,7 +958,7 @@ "_generator": { "name": "bicep", "version": "0.30.23.60470", - "templateHash": "7616623366375139473" + "templateHash": "3270228280505109788" } }, "parameters": { @@ -984,6 +984,7 @@ "tokens": { "type": "object", "defaultValue": { + "purpose": "purpose_token", "resource": "resource_token", "service": "service_token" } @@ -1419,8 +1420,8 @@ "locations": "[variables('$fxv#0')[environment().name]]", "locationAbbreviation": "[variables('locations')[parameters('location')].abbreviation]", "resourceAbbreviations": "[variables('$fxv#1')]", - "namingConvention": "[format('{0}-{1}{2}-{3}-{4}-{5}', toLower(parameters('resourcePrefix')), if(empty(parameters('stampIndex')), '', format('{0}-', parameters('stampIndex'))), parameters('tokens').resource, parameters('networkName'), parameters('environmentAbbreviation'), variables('locationAbbreviation'))]", - "namingConvention_Service": "[format('{0}-{1}{2}-{3}-{4}-{5}-{6}', toLower(parameters('resourcePrefix')), if(empty(parameters('stampIndex')), '', format('{0}-', parameters('stampIndex'))), parameters('tokens').resource, parameters('tokens').service, parameters('networkName'), parameters('environmentAbbreviation'), variables('locationAbbreviation'))]", + "namingConvention": "[format('{0}-{1}{2}-{3}-{4}-{5}', toLower(parameters('resourcePrefix')), if(empty(parameters('stampIndex')), '', format('{0}-', parameters('stampIndex'))), parameters('tokens').resource, parameters('networkName'), variables('locationAbbreviation'), parameters('environmentAbbreviation'))]", + "namingConvention_Service": "[format('{0}-{1}{2}-{3}-{4}-{5}-{6}', toLower(parameters('resourcePrefix')), if(empty(parameters('stampIndex')), '', format('{0}-', parameters('stampIndex'))), parameters('tokens').resource, parameters('networkName'), parameters('tokens').service, variables('locationAbbreviation'), parameters('environmentAbbreviation'))]", "names": { "actionGroup": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').actionGroups)]", "applicationGroup": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').applicationGroups)]", @@ -1479,8 +1480,14 @@ "routeTable": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').routeTables)]", "storageAccount": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').storageAccounts), parameters('networkName'), parameters('networkShortName'))]", "storageAccountDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').storageAccounts))]", - "storageAccountNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').storageAccounts))]", - "storageAccountPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').storageAccounts))]", + "storageAccountBlobNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-blob', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountFileNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-file', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountQueueNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-queue', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountTableNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-table', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountBlobPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-blob', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountFilePrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-file', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountQueuePrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-queue', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountTablePrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-table', variables('resourceAbbreviations').storageAccounts))]", "subnet": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').subnets)]", "userAssignedIdentity": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').userAssignedIdentities)]", "virtualMachine": "[replace(replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').virtualMachines), parameters('environmentAbbreviation'), first(parameters('environmentAbbreviation'))), parameters('networkName'), ''), '-', '')]", @@ -3537,6 +3544,9 @@ "blobsPrivateDnsZoneResourceId": { "value": "[resourceId(variables('hubSubscriptionId'), variables('hubResourceGroupName'), 'Microsoft.Network/privateDnsZones', format('privatelink.blob.{0}', environment().suffixes.storage))]" }, + "filesPrivateDnsZoneResourceId": { + "value": "[resourceId(variables('hubSubscriptionId'), variables('hubResourceGroupName'), 'Microsoft.Network/privateDnsZones', format('privatelink.file.{0}', environment().suffixes.storage))]" + }, "keyVaultUri": { "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-cmk-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.keyVaultUri.value]" }, @@ -3552,6 +3562,9 @@ "network": { "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tiers.value[0]]" }, + "queuesPrivateDnsZoneResourceId": { + "value": "[resourceId(variables('hubSubscriptionId'), variables('hubResourceGroupName'), 'Microsoft.Network/privateDnsZones', format('privatelink.queue.{0}', environment().suffixes.storage))]" + }, "resourceGroupName": { "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.name.value]" }, @@ -3584,13 +3597,16 @@ "_generator": { "name": "bicep", "version": "0.30.23.60470", - "templateHash": "17995428208905585384" + "templateHash": "2377877844306490130" } }, "parameters": { "blobsPrivateDnsZoneResourceId": { "type": "string" }, + "filesPrivateDnsZoneResourceId": { + "type": "string" + }, "keyVaultUri": { "type": "string" }, @@ -3606,6 +3622,9 @@ "network": { "type": "object" }, + "queuesPrivateDnsZoneResourceId": { + "type": "string" + }, "resourceGroupName": { "type": "string" }, @@ -3647,6 +3666,9 @@ "blobsPrivateDnsZoneResourceId": { "value": "[parameters('blobsPrivateDnsZoneResourceId')]" }, + "filesPrivateDnsZoneResourceId": { + "value": "[parameters('filesPrivateDnsZoneResourceId')]" + }, "keyVaultUri": { "value": "[parameters('keyVaultUri')]" }, @@ -3656,6 +3678,9 @@ "mlzTags": { "value": "[parameters('mlzTags')]" }, + "queuesPrivateDnsZoneResourceId": { + "value": "[parameters('queuesPrivateDnsZoneResourceId')]" + }, "serviceToken": { "value": "[parameters('serviceToken')]" }, @@ -3688,13 +3713,16 @@ "_generator": { "name": "bicep", "version": "0.30.23.60470", - "templateHash": "10231061264498799420" + "templateHash": "13359240625570837130" } }, "parameters": { "blobsPrivateDnsZoneResourceId": { "type": "string" }, + "filesPrivateDnsZoneResourceId": { + "type": "string" + }, "keyVaultUri": { "type": "string" }, @@ -3704,6 +3732,9 @@ "mlzTags": { "type": "object" }, + "queuesPrivateDnsZoneResourceId": { + "type": "string" + }, "serviceToken": { "type": "string" }, @@ -3730,9 +3761,27 @@ } }, "variables": { - "zones": [ - "[parameters('blobsPrivateDnsZoneResourceId')]", - "[parameters('tablesPrivateDnsZoneResourceId')]" + "subResources": [ + { + "id": "[parameters('blobsPrivateDnsZoneResourceId')]", + "nic": "[parameters('tier').namingConvention.storageAccountBlobNetworkInterface]", + "pe": "[parameters('tier').namingConvention.storageAccountBlobPrivateEndpoint]" + }, + { + "id": "[parameters('filesPrivateDnsZoneResourceId')]", + "nic": "[parameters('tier').namingConvention.storageAccountFileNetworkInterface]", + "pe": "[parameters('tier').namingConvention.storageAccountFilePrivateEndpoint]" + }, + { + "id": "[parameters('queuesPrivateDnsZoneResourceId')]", + "nic": "[parameters('tier').namingConvention.storageAccountQueueNetworkInterface]", + "pe": "[parameters('tier').namingConvention.storageAccountQueuePrivateEndpoint]" + }, + { + "id": "[parameters('tablesPrivateDnsZoneResourceId')]", + "nic": "[parameters('tier').namingConvention.storageAccountTableNetworkInterface]", + "pe": "[parameters('tier').namingConvention.storageAccountTablePrivateEndpoint]" + } ] }, "resources": [ @@ -3803,22 +3852,22 @@ { "copy": { "name": "privateEndpoints", - "count": "[length(variables('zones'))]" + "count": "[length(variables('subResources'))]" }, "type": "Microsoft.Network/privateEndpoints", "apiVersion": "2023-04-01", - "name": "[replace(parameters('tier').namingConvention.storageAccountPrivateEndpoint, parameters('serviceToken'), format('{0}-log', split(split(variables('zones')[copyIndex()], '/')[8], '.')[1]))]", + "name": "[variables('subResources')[copyIndex()].pe]", "location": "[parameters('location')]", "tags": "[union(if(contains(parameters('tags'), 'Microsoft.Network/privateEndpoints'), parameters('tags')['Microsoft.Network/privateEndpoints'], createObject()), parameters('mlzTags'))]", "properties": { - "customNetworkInterfaceName": "[replace(parameters('tier').namingConvention.storageAccountNetworkInterface, parameters('serviceToken'), format('{0}-log', split(split(variables('zones')[copyIndex()], '/')[8], '.')[1]))]", + "customNetworkInterfaceName": "[variables('subResources')[copyIndex()].nic]", "privateLinkServiceConnections": [ { - "name": "[replace(parameters('tier').namingConvention.storageAccountPrivateEndpoint, parameters('serviceToken'), format('{0}-log', split(split(variables('zones')[copyIndex()], '/')[8], '.')[1]))]", + "name": "[variables('subResources')[copyIndex()].pe]", "properties": { "privateLinkServiceId": "[resourceId('Microsoft.Storage/storageAccounts', uniqueString(replace(parameters('tier').namingConvention.storageAccount, parameters('serviceToken'), 'log'), resourceGroup().id))]", "groupIds": [ - "[split(split(variables('zones')[copyIndex()], '/')[8], '.')[1]]" + "[split(split(variables('subResources')[copyIndex()].id, '/')[8], '.')[1]]" ] } } @@ -3834,23 +3883,23 @@ { "copy": { "name": "privateDnsZoneGroups", - "count": "[length(variables('zones'))]" + "count": "[length(variables('subResources'))]" }, "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", "apiVersion": "2021-08-01", - "name": "[format('{0}/{1}', replace(parameters('tier').namingConvention.storageAccountPrivateEndpoint, parameters('serviceToken'), format('{0}-log', split(split(variables('zones')[copyIndex()], '/')[8], '.')[1])), uniqueString(replace(parameters('tier').namingConvention.storageAccount, parameters('serviceToken'), 'log'), resourceGroup().id))]", + "name": "[format('{0}/{1}', variables('subResources')[copyIndex()].pe, uniqueString(replace(parameters('tier').namingConvention.storageAccount, parameters('serviceToken'), 'log'), resourceGroup().id))]", "properties": { "privateDnsZoneConfigs": [ { "name": "ipconfig1", "properties": { - "privateDnsZoneId": "[variables('zones')[copyIndex()]]" + "privateDnsZoneId": "[variables('subResources')[copyIndex()].id]" } } ] }, "dependsOn": [ - "[resourceId('Microsoft.Network/privateEndpoints', replace(parameters('tier').namingConvention.storageAccountPrivateEndpoint, parameters('serviceToken'), format('{0}-log', split(split(variables('zones')[copyIndex()], '/')[8], '.')[1])))]", + "[resourceId('Microsoft.Network/privateEndpoints', variables('subResources')[copyIndex()].pe)]", "[resourceId('Microsoft.Storage/storageAccounts', uniqueString(replace(parameters('tier').namingConvention.storageAccount, parameters('serviceToken'), 'log'), resourceGroup().id))]" ] } diff --git a/src/bicep/add-ons/tier3/modules/storage.bicep b/src/bicep/add-ons/tier3/modules/storage.bicep index b78d37d41..8580e034d 100644 --- a/src/bicep/add-ons/tier3/modules/storage.bicep +++ b/src/bicep/add-ons/tier3/modules/storage.bicep @@ -6,11 +6,13 @@ Licensed under the MIT License. targetScope = 'subscription' param blobsPrivateDnsZoneResourceId string +param filesPrivateDnsZoneResourceId string param keyVaultUri string param logStorageSkuName string param location string param mlzTags object param network object +param queuesPrivateDnsZoneResourceId string param resourceGroupName string param serviceToken string param storageEncryptionKeyName string @@ -25,9 +27,11 @@ module storageAccount '../../../modules/storage-account.bicep' = { scope: resourceGroup(network.subscriptionId, resourceGroupName) params: { blobsPrivateDnsZoneResourceId: blobsPrivateDnsZoneResourceId + filesPrivateDnsZoneResourceId: filesPrivateDnsZoneResourceId keyVaultUri: keyVaultUri location: location mlzTags: mlzTags + queuesPrivateDnsZoneResourceId: queuesPrivateDnsZoneResourceId serviceToken: serviceToken skuName: logStorageSkuName storageEncryptionKeyName: storageEncryptionKeyName diff --git a/src/bicep/add-ons/tier3/solution.bicep b/src/bicep/add-ons/tier3/solution.bicep index 7e75f3fec..b30644948 100644 --- a/src/bicep/add-ons/tier3/solution.bicep +++ b/src/bicep/add-ons/tier3/solution.bicep @@ -237,27 +237,19 @@ module customerManagedKeys '../../modules/customer-managed-keys.bicep' = if (!(e module storage 'modules/storage.bicep' = if (!(empty(virtualNetworkAddressPrefix))) { name: 'deploy-storage-${workloadShortName}-${deploymentNameSuffix}' params: { - blobsPrivateDnsZoneResourceId: resourceId( - hubSubscriptionId, - hubResourceGroupName, - 'Microsoft.Network/privateDnsZones', - 'privatelink.blob.${environment().suffixes.storage}' - ) + blobsPrivateDnsZoneResourceId: resourceId(hubSubscriptionId, hubResourceGroupName, 'Microsoft.Network/privateDnsZones', 'privatelink.blob.${environment().suffixes.storage}') + filesPrivateDnsZoneResourceId: resourceId(hubSubscriptionId, hubResourceGroupName, 'Microsoft.Network/privateDnsZones', 'privatelink.file.${environment().suffixes.storage}') keyVaultUri: customerManagedKeys.outputs.keyVaultUri location: location logStorageSkuName: logStorageSkuName mlzTags: logic.outputs.mlzTags network: logic.outputs.tiers[0] + queuesPrivateDnsZoneResourceId: resourceId(hubSubscriptionId, hubResourceGroupName, 'Microsoft.Network/privateDnsZones', 'privatelink.queue.${environment().suffixes.storage}') resourceGroupName: rg.outputs.name serviceToken: logic.outputs.tokens.service storageEncryptionKeyName: customerManagedKeys.outputs.storageKeyName subnetResourceId: networking.outputs.subnets[0].id - tablesPrivateDnsZoneResourceId: resourceId( - hubSubscriptionId, - hubResourceGroupName, - 'Microsoft.Network/privateDnsZones', - 'privatelink.table.${environment().suffixes.storage}' - ) + tablesPrivateDnsZoneResourceId: resourceId(hubSubscriptionId, hubResourceGroupName, 'Microsoft.Network/privateDnsZones', 'privatelink.table.${environment().suffixes.storage}') tags: tags tier: logic.outputs.tiers[0] userAssignedIdentityResourceId: customerManagedKeys.outputs.userAssignedIdentityResourceId diff --git a/src/bicep/add-ons/tier3/solution.json b/src/bicep/add-ons/tier3/solution.json index 3dfe2b817..43f0099ae 100644 --- a/src/bicep/add-ons/tier3/solution.json +++ b/src/bicep/add-ons/tier3/solution.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.30.23.60470", - "templateHash": "5332647717093931418" + "templateHash": "16140764241259827428" } }, "parameters": { @@ -282,7 +282,7 @@ "_generator": { "name": "bicep", "version": "0.30.23.60470", - "templateHash": "11424932506966599764" + "templateHash": "16220429751656280628" } }, "parameters": { @@ -362,7 +362,7 @@ "_generator": { "name": "bicep", "version": "0.30.23.60470", - "templateHash": "7616623366375139473" + "templateHash": "3270228280505109788" } }, "parameters": { @@ -388,6 +388,7 @@ "tokens": { "type": "object", "defaultValue": { + "purpose": "purpose_token", "resource": "resource_token", "service": "service_token" } @@ -823,8 +824,8 @@ "locations": "[variables('$fxv#0')[environment().name]]", "locationAbbreviation": "[variables('locations')[parameters('location')].abbreviation]", "resourceAbbreviations": "[variables('$fxv#1')]", - "namingConvention": "[format('{0}-{1}{2}-{3}-{4}-{5}', toLower(parameters('resourcePrefix')), if(empty(parameters('stampIndex')), '', format('{0}-', parameters('stampIndex'))), parameters('tokens').resource, parameters('networkName'), parameters('environmentAbbreviation'), variables('locationAbbreviation'))]", - "namingConvention_Service": "[format('{0}-{1}{2}-{3}-{4}-{5}-{6}', toLower(parameters('resourcePrefix')), if(empty(parameters('stampIndex')), '', format('{0}-', parameters('stampIndex'))), parameters('tokens').resource, parameters('tokens').service, parameters('networkName'), parameters('environmentAbbreviation'), variables('locationAbbreviation'))]", + "namingConvention": "[format('{0}-{1}{2}-{3}-{4}-{5}', toLower(parameters('resourcePrefix')), if(empty(parameters('stampIndex')), '', format('{0}-', parameters('stampIndex'))), parameters('tokens').resource, parameters('networkName'), variables('locationAbbreviation'), parameters('environmentAbbreviation'))]", + "namingConvention_Service": "[format('{0}-{1}{2}-{3}-{4}-{5}-{6}', toLower(parameters('resourcePrefix')), if(empty(parameters('stampIndex')), '', format('{0}-', parameters('stampIndex'))), parameters('tokens').resource, parameters('networkName'), parameters('tokens').service, variables('locationAbbreviation'), parameters('environmentAbbreviation'))]", "names": { "actionGroup": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').actionGroups)]", "applicationGroup": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').applicationGroups)]", @@ -883,8 +884,14 @@ "routeTable": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').routeTables)]", "storageAccount": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').storageAccounts), parameters('networkName'), parameters('networkShortName'))]", "storageAccountDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').storageAccounts))]", - "storageAccountNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').storageAccounts))]", - "storageAccountPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').storageAccounts))]", + "storageAccountBlobNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-blob', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountFileNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-file', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountQueueNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-queue', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountTableNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-table', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountBlobPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-blob', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountFilePrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-file', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountQueuePrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-queue', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountTablePrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-table', variables('resourceAbbreviations').storageAccounts))]", "subnet": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').subnets)]", "userAssignedIdentity": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').userAssignedIdentities)]", "virtualMachine": "[replace(replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').virtualMachines), parameters('environmentAbbreviation'), first(parameters('environmentAbbreviation'))), parameters('networkName'), ''), '-', '')]", @@ -2941,6 +2948,9 @@ "blobsPrivateDnsZoneResourceId": { "value": "[resourceId(variables('hubSubscriptionId'), variables('hubResourceGroupName'), 'Microsoft.Network/privateDnsZones', format('privatelink.blob.{0}', environment().suffixes.storage))]" }, + "filesPrivateDnsZoneResourceId": { + "value": "[resourceId(variables('hubSubscriptionId'), variables('hubResourceGroupName'), 'Microsoft.Network/privateDnsZones', format('privatelink.file.{0}', environment().suffixes.storage))]" + }, "keyVaultUri": { "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-cmk-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.keyVaultUri.value]" }, @@ -2956,6 +2966,9 @@ "network": { "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tiers.value[0]]" }, + "queuesPrivateDnsZoneResourceId": { + "value": "[resourceId(variables('hubSubscriptionId'), variables('hubResourceGroupName'), 'Microsoft.Network/privateDnsZones', format('privatelink.queue.{0}', environment().suffixes.storage))]" + }, "resourceGroupName": { "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.name.value]" }, @@ -2988,13 +3001,16 @@ "_generator": { "name": "bicep", "version": "0.30.23.60470", - "templateHash": "17995428208905585384" + "templateHash": "2377877844306490130" } }, "parameters": { "blobsPrivateDnsZoneResourceId": { "type": "string" }, + "filesPrivateDnsZoneResourceId": { + "type": "string" + }, "keyVaultUri": { "type": "string" }, @@ -3010,6 +3026,9 @@ "network": { "type": "object" }, + "queuesPrivateDnsZoneResourceId": { + "type": "string" + }, "resourceGroupName": { "type": "string" }, @@ -3051,6 +3070,9 @@ "blobsPrivateDnsZoneResourceId": { "value": "[parameters('blobsPrivateDnsZoneResourceId')]" }, + "filesPrivateDnsZoneResourceId": { + "value": "[parameters('filesPrivateDnsZoneResourceId')]" + }, "keyVaultUri": { "value": "[parameters('keyVaultUri')]" }, @@ -3060,6 +3082,9 @@ "mlzTags": { "value": "[parameters('mlzTags')]" }, + "queuesPrivateDnsZoneResourceId": { + "value": "[parameters('queuesPrivateDnsZoneResourceId')]" + }, "serviceToken": { "value": "[parameters('serviceToken')]" }, @@ -3092,13 +3117,16 @@ "_generator": { "name": "bicep", "version": "0.30.23.60470", - "templateHash": "10231061264498799420" + "templateHash": "13359240625570837130" } }, "parameters": { "blobsPrivateDnsZoneResourceId": { "type": "string" }, + "filesPrivateDnsZoneResourceId": { + "type": "string" + }, "keyVaultUri": { "type": "string" }, @@ -3108,6 +3136,9 @@ "mlzTags": { "type": "object" }, + "queuesPrivateDnsZoneResourceId": { + "type": "string" + }, "serviceToken": { "type": "string" }, @@ -3134,9 +3165,27 @@ } }, "variables": { - "zones": [ - "[parameters('blobsPrivateDnsZoneResourceId')]", - "[parameters('tablesPrivateDnsZoneResourceId')]" + "subResources": [ + { + "id": "[parameters('blobsPrivateDnsZoneResourceId')]", + "nic": "[parameters('tier').namingConvention.storageAccountBlobNetworkInterface]", + "pe": "[parameters('tier').namingConvention.storageAccountBlobPrivateEndpoint]" + }, + { + "id": "[parameters('filesPrivateDnsZoneResourceId')]", + "nic": "[parameters('tier').namingConvention.storageAccountFileNetworkInterface]", + "pe": "[parameters('tier').namingConvention.storageAccountFilePrivateEndpoint]" + }, + { + "id": "[parameters('queuesPrivateDnsZoneResourceId')]", + "nic": "[parameters('tier').namingConvention.storageAccountQueueNetworkInterface]", + "pe": "[parameters('tier').namingConvention.storageAccountQueuePrivateEndpoint]" + }, + { + "id": "[parameters('tablesPrivateDnsZoneResourceId')]", + "nic": "[parameters('tier').namingConvention.storageAccountTableNetworkInterface]", + "pe": "[parameters('tier').namingConvention.storageAccountTablePrivateEndpoint]" + } ] }, "resources": [ @@ -3207,22 +3256,22 @@ { "copy": { "name": "privateEndpoints", - "count": "[length(variables('zones'))]" + "count": "[length(variables('subResources'))]" }, "type": "Microsoft.Network/privateEndpoints", "apiVersion": "2023-04-01", - "name": "[replace(parameters('tier').namingConvention.storageAccountPrivateEndpoint, parameters('serviceToken'), format('{0}-log', split(split(variables('zones')[copyIndex()], '/')[8], '.')[1]))]", + "name": "[variables('subResources')[copyIndex()].pe]", "location": "[parameters('location')]", "tags": "[union(if(contains(parameters('tags'), 'Microsoft.Network/privateEndpoints'), parameters('tags')['Microsoft.Network/privateEndpoints'], createObject()), parameters('mlzTags'))]", "properties": { - "customNetworkInterfaceName": "[replace(parameters('tier').namingConvention.storageAccountNetworkInterface, parameters('serviceToken'), format('{0}-log', split(split(variables('zones')[copyIndex()], '/')[8], '.')[1]))]", + "customNetworkInterfaceName": "[variables('subResources')[copyIndex()].nic]", "privateLinkServiceConnections": [ { - "name": "[replace(parameters('tier').namingConvention.storageAccountPrivateEndpoint, parameters('serviceToken'), format('{0}-log', split(split(variables('zones')[copyIndex()], '/')[8], '.')[1]))]", + "name": "[variables('subResources')[copyIndex()].pe]", "properties": { "privateLinkServiceId": "[resourceId('Microsoft.Storage/storageAccounts', uniqueString(replace(parameters('tier').namingConvention.storageAccount, parameters('serviceToken'), 'log'), resourceGroup().id))]", "groupIds": [ - "[split(split(variables('zones')[copyIndex()], '/')[8], '.')[1]]" + "[split(split(variables('subResources')[copyIndex()].id, '/')[8], '.')[1]]" ] } } @@ -3238,23 +3287,23 @@ { "copy": { "name": "privateDnsZoneGroups", - "count": "[length(variables('zones'))]" + "count": "[length(variables('subResources'))]" }, "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", "apiVersion": "2021-08-01", - "name": "[format('{0}/{1}', replace(parameters('tier').namingConvention.storageAccountPrivateEndpoint, parameters('serviceToken'), format('{0}-log', split(split(variables('zones')[copyIndex()], '/')[8], '.')[1])), uniqueString(replace(parameters('tier').namingConvention.storageAccount, parameters('serviceToken'), 'log'), resourceGroup().id))]", + "name": "[format('{0}/{1}', variables('subResources')[copyIndex()].pe, uniqueString(replace(parameters('tier').namingConvention.storageAccount, parameters('serviceToken'), 'log'), resourceGroup().id))]", "properties": { "privateDnsZoneConfigs": [ { "name": "ipconfig1", "properties": { - "privateDnsZoneId": "[variables('zones')[copyIndex()]]" + "privateDnsZoneId": "[variables('subResources')[copyIndex()].id]" } } ] }, "dependsOn": [ - "[resourceId('Microsoft.Network/privateEndpoints', replace(parameters('tier').namingConvention.storageAccountPrivateEndpoint, parameters('serviceToken'), format('{0}-log', split(split(variables('zones')[copyIndex()], '/')[8], '.')[1])))]", + "[resourceId('Microsoft.Network/privateEndpoints', variables('subResources')[copyIndex()].pe)]", "[resourceId('Microsoft.Storage/storageAccounts', uniqueString(replace(parameters('tier').namingConvention.storageAccount, parameters('serviceToken'), 'log'), resourceGroup().id))]" ] } From 6089ef9eafaee3763b1ce2aa7e773d4ec397ae93 Mon Sep 17 00:00:00 2001 From: Jason Masten Date: Thu, 7 Nov 2024 10:37:41 -0500 Subject: [PATCH 5/9] AVD Add-On: Updated configuration zip file & fixed Entra join bug (#1126) * Added param for AVD config file with default value * Updated condition * Added back VM ID to fix entra join --- .../modules/sessionHosts/sessionHosts.bicep | 2 + .../sessionHosts/virtualMachines.bicep | 10 +- .../azure-virtual-desktop/solution.bicep | 4 + .../azure-virtual-desktop/solution.json | 370 ++++++++++-------- 4 files changed, 209 insertions(+), 177 deletions(-) diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/sessionHosts/sessionHosts.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/sessionHosts/sessionHosts.bicep index 9345521f5..acf2ec221 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/modules/sessionHosts/sessionHosts.bicep +++ b/src/bicep/add-ons/azure-virtual-desktop/modules/sessionHosts/sessionHosts.bicep @@ -5,6 +5,7 @@ param availability string param availabilitySetsCount int param availabilitySetsIndex int param availabilityZones array +param avdConfigurationZipFileName string param dataCollectionRuleResourceId string param deployFslogix bool param deploymentNameSuffix string @@ -111,6 +112,7 @@ module virtualMachines 'virtualMachines.bicep' = [for i in range(1, sessionHostB availability: availability availabilitySetNamePrefix: availabilitySetNamePrefix availabilityZones: availabilityZones + avdConfigurationZipFileName: avdConfigurationZipFileName batchCount: i dataCollectionRuleAssociationName: namingConvention.dataCollectionRuleAssociation dataCollectionRuleResourceId: dataCollectionRuleResourceId diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/sessionHosts/virtualMachines.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/sessionHosts/virtualMachines.bicep index f9eb38713..5d328b564 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/modules/sessionHosts/virtualMachines.bicep +++ b/src/bicep/add-ons/azure-virtual-desktop/modules/sessionHosts/virtualMachines.bicep @@ -2,6 +2,7 @@ param activeDirectorySolution string param availability string param availabilitySetNamePrefix string param availabilityZones array +param avdConfigurationZipFileName string param batchCount int param dataCollectionRuleAssociationName string param dataCollectionRuleResourceId string @@ -128,6 +129,9 @@ resource virtualMachine 'Microsoft.Compute/virtualMachines@2021-03-01' = [for i name: '${sessionHostNamePrefix}${padLeft((i + sessionHostIndex), 4, '0')}' location: location tags: tagsVirtualMachines + identity: { + type: 'SystemAssigned' // Required for Entra join + } zones: availability == 'AvailabilityZones' ? [ availabilityZones[i % length(availabilityZones)] ] : null @@ -352,7 +356,7 @@ resource installAvdAgents 'Microsoft.Compute/virtualMachines/extensions@2021-03- typeHandlerVersion: '2.73' autoUpgradeMinorVersion: true settings: { - modulesUrl: 'https://wvdportalstorageblob.blob.${environment().suffixes.storage}/galleryartifacts/Configuration_1.0.02721.349.zip' + modulesUrl: 'https://wvdportalstorageblob.blob.${environment().suffixes.storage}/galleryartifacts/${avdConfigurationZipFileName}' configurationFunction: 'Configuration.ps1\\AddSessionHost' properties: { hostPoolName: hostPoolName @@ -360,7 +364,7 @@ resource installAvdAgents 'Microsoft.Compute/virtualMachines/extensions@2021-03- UserName: 'PLACEHOLDER_DO_NOT_USE' Password: 'PrivateSettingsRef:RegistrationInfoToken' } - aadJoin: !contains(activeDirectorySolution, 'DomainServices') + aadJoin: contains(activeDirectorySolution, 'EntraId') UseAgentDownloadEndpoint: false mdmId: intune ? '0000000a-0000-0000-c000-000000000000' : '' } @@ -462,7 +466,7 @@ resource extension_JsonADDomainExtension 'Microsoft.Compute/virtualMachines/exte ] }] -resource extension_AADLoginForWindows 'Microsoft.Compute/virtualMachines/extensions@2021-03-01' = [for i in range(0, sessionHostCount): if (!contains(activeDirectorySolution, 'DomainServices')) { +resource extension_AADLoginForWindows 'Microsoft.Compute/virtualMachines/extensions@2021-03-01' = [for i in range(0, sessionHostCount): if (contains(activeDirectorySolution, 'EntraId')) { parent: virtualMachine[i] name: 'AADLoginForWindows' location: location diff --git a/src/bicep/add-ons/azure-virtual-desktop/solution.bicep b/src/bicep/add-ons/azure-virtual-desktop/solution.bicep index ade3f2ac6..b05c5d759 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/solution.bicep +++ b/src/bicep/add-ons/azure-virtual-desktop/solution.bicep @@ -20,6 +20,9 @@ param availability string = 'AvailabilityZones' @description('The availability zones allowed for the AVD session hosts deployment location.') param availabilityZones array +@description('The file name for the ZIP file containing the AVD agents and DSC configuration.') +param avdConfigurationZipFileName string = 'Configuration_1.0.02790.438.zip' + @description('The object ID for the Azure Virtual Desktop enterprise application in Microsoft Entra ID. The object ID can found by selecting Microsoft Applications using the Application type filter in the Enterprise Applications blade of Microsoft Entra ID.') param avdObjectId string @@ -678,6 +681,7 @@ module sessionHosts 'modules/sessionHosts/sessionHosts.bicep' = { availabilitySetsCount: availabilitySetsCount availabilitySetsIndex: beginAvSetRange availabilityZones: availabilityZones + avdConfigurationZipFileName: avdConfigurationZipFileName dataCollectionRuleResourceId: management.outputs.dataCollectionRuleResourceId deployFslogix: deployFslogix deploymentNameSuffix: deploymentNameSuffix diff --git a/src/bicep/add-ons/azure-virtual-desktop/solution.json b/src/bicep/add-ons/azure-virtual-desktop/solution.json index 5a3c4c326..e03845ad3 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/solution.json +++ b/src/bicep/add-ons/azure-virtual-desktop/solution.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "5169206045336313112" + "version": "0.31.34.60546", + "templateHash": "8432968336067453174" } }, "parameters": { @@ -39,6 +39,13 @@ "description": "The availability zones allowed for the AVD session hosts deployment location." } }, + "avdConfigurationZipFileName": { + "type": "string", + "defaultValue": "Configuration_1.0.02790.438.zip", + "metadata": { + "description": "The file name for the ZIP file containing the AVD agents and DSC configuration." + } + }, "avdObjectId": { "type": "string", "metadata": { @@ -678,8 +685,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "3270228280505109788" + "version": "0.31.34.60546", + "templateHash": "8357935203009683307" } }, "parameters": { @@ -1284,8 +1291,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "3270228280505109788" + "version": "0.31.34.60546", + "templateHash": "8357935203009683307" } }, "parameters": { @@ -1935,8 +1942,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "16140764241259827428" + "version": "0.31.34.60546", + "templateHash": "3242090622764524003" } }, "parameters": { @@ -2212,8 +2219,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "16220429751656280628" + "version": "0.31.34.60546", + "templateHash": "16255426588501076488" } }, "parameters": { @@ -2292,8 +2299,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "3270228280505109788" + "version": "0.31.34.60546", + "templateHash": "8357935203009683307" } }, "parameters": { @@ -2883,8 +2890,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "4151572700986885014" + "version": "0.31.34.60546", + "templateHash": "2502930168536032010" } }, "parameters": { @@ -3023,8 +3030,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "10109660736443081073" + "version": "0.31.34.60546", + "templateHash": "17445726037807437290" } }, "parameters": { @@ -3152,8 +3159,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "2001402118470594055" + "version": "0.31.34.60546", + "templateHash": "1068007055123133889" } }, "parameters": { @@ -3288,8 +3295,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "3535186238457701125" + "version": "0.31.34.60546", + "templateHash": "11567108053836979012" } }, "parameters": { @@ -3406,8 +3413,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "6722359274420487391" + "version": "0.31.34.60546", + "templateHash": "2196017082128829477" } }, "parameters": { @@ -3489,8 +3496,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "12652296496577802490" + "version": "0.31.34.60546", + "templateHash": "8417416771003518918" } }, "parameters": { @@ -3592,8 +3599,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "17592952825859536181" + "version": "0.31.34.60546", + "templateHash": "4071285799602638747" } }, "parameters": { @@ -3669,8 +3676,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "7007835755326231171" + "version": "0.31.34.60546", + "templateHash": "3465065949976146403" } }, "parameters": { @@ -3810,8 +3817,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "3832300165614083813" + "version": "0.31.34.60546", + "templateHash": "15799890372401066181" } }, "parameters": { @@ -3863,8 +3870,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "2015499370398293300" + "version": "0.31.34.60546", + "templateHash": "5031620623183573702" } }, "parameters": { @@ -3937,8 +3944,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "9502319494004310539" + "version": "0.31.34.60546", + "templateHash": "6489616383757058493" } }, "parameters": { @@ -3990,8 +3997,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "2015499370398293300" + "version": "0.31.34.60546", + "templateHash": "5031620623183573702" } }, "parameters": { @@ -4090,8 +4097,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "10999644342250296299" + "version": "0.31.34.60546", + "templateHash": "7410232214960945771" } }, "parameters": { @@ -4148,8 +4155,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "2757774880390840506" + "version": "0.31.34.60546", + "templateHash": "12405780209119797551" } }, "parameters": { @@ -4251,8 +4258,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "11152278840466293351" + "version": "0.31.34.60546", + "templateHash": "6386888682917235118" } }, "parameters": { @@ -4340,8 +4347,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "1874197755006172394" + "version": "0.31.34.60546", + "templateHash": "419730844167715947" } }, "parameters": { @@ -4598,8 +4605,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "17613382135787640077" + "version": "0.31.34.60546", + "templateHash": "7946295394028911325" } }, "parameters": { @@ -4678,8 +4685,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "8372265102618018066" + "version": "0.31.34.60546", + "templateHash": "967013811257719495" } }, "parameters": { @@ -4773,8 +4780,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "6675708379514380442" + "version": "0.31.34.60546", + "templateHash": "9115514582672423063" } }, "parameters": { @@ -4931,8 +4938,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "2377877844306490130" + "version": "0.31.34.60546", + "templateHash": "11229301381209629588" } }, "parameters": { @@ -5047,8 +5054,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "13359240625570837130" + "version": "0.31.34.60546", + "templateHash": "10245709494871874056" } }, "parameters": { @@ -5328,8 +5335,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "8480968961555779332" + "version": "0.31.34.60546", + "templateHash": "13756267312445220554" } }, "parameters": { @@ -5403,8 +5410,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "8389358797157027271" + "version": "0.31.34.60546", + "templateHash": "5964906331561259426" } }, "parameters": { @@ -5493,8 +5500,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "3320497816733398371" + "version": "0.31.34.60546", + "templateHash": "13721817451936402949" } }, "parameters": { @@ -5567,8 +5574,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "9851134383266019486" + "version": "0.31.34.60546", + "templateHash": "5533529544253509904" } }, "parameters": { @@ -5645,8 +5652,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "7358671180047253284" + "version": "0.31.34.60546", + "templateHash": "169088610601729285" } }, "parameters": { @@ -5736,8 +5743,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "7046499602359448652" + "version": "0.31.34.60546", + "templateHash": "2239324965165269347" } }, "parameters": { @@ -5793,8 +5800,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "16614224552992880134" + "version": "0.31.34.60546", + "templateHash": "16715470716191237776" } }, "parameters": { @@ -5969,8 +5976,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "8372265102618018066" + "version": "0.31.34.60546", + "templateHash": "967013811257719495" } }, "parameters": { @@ -6054,8 +6061,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "3588347827815061814" + "version": "0.31.34.60546", + "templateHash": "2575529544484247921" } }, "parameters": { @@ -6380,8 +6387,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "10109660736443081073" + "version": "0.31.34.60546", + "templateHash": "17445726037807437290" } }, "parameters": { @@ -6587,8 +6594,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "5604402442599402109" + "version": "0.31.34.60546", + "templateHash": "5374213534709080562" } }, "parameters": { @@ -6787,8 +6794,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "15818789439914368510" + "version": "0.31.34.60546", + "templateHash": "5378203314803052603" } }, "parameters": { @@ -6887,8 +6894,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "11917989921453943443" + "version": "0.31.34.60546", + "templateHash": "702699492047562635" } }, "parameters": { @@ -6968,8 +6975,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "17841511129945171695" + "version": "0.31.34.60546", + "templateHash": "6289202887333913438" } }, "parameters": { @@ -7045,8 +7052,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "11961291129443849893" + "version": "0.31.34.60546", + "templateHash": "12664463677760204409" } }, "parameters": { @@ -7118,8 +7125,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "15359000832124286075" + "version": "0.31.34.60546", + "templateHash": "1315528727979495191" } }, "parameters": { @@ -7229,8 +7236,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "12440906326725869878" + "version": "0.31.34.60546", + "templateHash": "7486697218882140009" } }, "parameters": { @@ -7561,8 +7568,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "13280657809752810735" + "version": "0.31.34.60546", + "templateHash": "12055090228824682687" } }, "parameters": { @@ -7756,8 +7763,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "4414484880639231715" + "version": "0.31.34.60546", + "templateHash": "8149225552603313421" } }, "parameters": { @@ -7838,8 +7845,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "4414484880639231715" + "version": "0.31.34.60546", + "templateHash": "8149225552603313421" } }, "parameters": { @@ -8003,8 +8010,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "15398537477314996235" + "version": "0.31.34.60546", + "templateHash": "13808224309643091125" } }, "parameters": { @@ -8547,8 +8554,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "4414484880639231715" + "version": "0.31.34.60546", + "templateHash": "8149225552603313421" } }, "parameters": { @@ -8634,8 +8641,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "15359000832124286075" + "version": "0.31.34.60546", + "templateHash": "1315528727979495191" } }, "parameters": { @@ -8696,8 +8703,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "4556566909157652727" + "version": "0.31.34.60546", + "templateHash": "6271853608623957112" } }, "parameters": { @@ -8762,8 +8769,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "10076124156656611378" + "version": "0.31.34.60546", + "templateHash": "9646048614586675609" } }, "parameters": { @@ -8876,8 +8883,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "18245536038700947918" + "version": "0.31.34.60546", + "templateHash": "12830230504350465325" } }, "parameters": { @@ -9127,8 +9134,8 @@ } }, "dependsOn": [ - "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', variables('resourceGroupServices')[2], parameters('deploymentNameSuffix')))]", "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', variables('resourceGroupServices')[1], parameters('deploymentNameSuffix')))]", + "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', variables('resourceGroupServices')[2], parameters('deploymentNameSuffix')))]", "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', variables('resourceGroupServices')[0], parameters('deploymentNameSuffix')))]", "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix')))]" ] @@ -9246,8 +9253,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "8101049597382289461" + "version": "0.31.34.60546", + "templateHash": "7947199422402016062" } }, "parameters": { @@ -9459,8 +9466,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "12277474104461216961" + "version": "0.31.34.60546", + "templateHash": "1177554211568337094" } }, "parameters": { @@ -9712,8 +9719,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "13010021933042504102" + "version": "0.31.34.60546", + "templateHash": "17578802300051756694" } }, "parameters": { @@ -9851,8 +9858,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "17500708096438980911" + "version": "0.31.34.60546", + "templateHash": "5317974790875443937" } }, "parameters": { @@ -10049,8 +10056,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "3599289091308371499" + "version": "0.31.34.60546", + "templateHash": "6641458391732609020" } }, "parameters": { @@ -10177,8 +10184,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "10109660736443081073" + "version": "0.31.34.60546", + "templateHash": "17445726037807437290" } }, "parameters": { @@ -10265,8 +10272,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "10739312966612375627" + "version": "0.31.34.60546", + "templateHash": "977555068735579352" } }, "parameters": { @@ -10385,8 +10392,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "10109660736443081073" + "version": "0.31.34.60546", + "templateHash": "17445726037807437290" } }, "parameters": { @@ -10467,8 +10474,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "15359000832124286075" + "version": "0.31.34.60546", + "templateHash": "1315528727979495191" } }, "parameters": { @@ -10528,8 +10535,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "15359000832124286075" + "version": "0.31.34.60546", + "templateHash": "1315528727979495191" } }, "parameters": { @@ -10642,8 +10649,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "226606255218378017" + "version": "0.31.34.60546", + "templateHash": "11735323070375319176" } }, "parameters": { @@ -10850,8 +10857,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "17500708096438980911" + "version": "0.31.34.60546", + "templateHash": "5317974790875443937" } }, "parameters": { @@ -11060,8 +11067,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "3321762213194063115" + "version": "0.31.34.60546", + "templateHash": "11179081376326675216" } }, "parameters": { @@ -11274,8 +11281,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "14014510525859848056" + "version": "0.31.34.60546", + "templateHash": "2650625569344244687" } }, "parameters": { @@ -11476,8 +11483,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "5010063642680411887" + "version": "0.31.34.60546", + "templateHash": "4329443301777514730" } }, "parameters": { @@ -11680,8 +11687,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "6082593095415470269" + "version": "0.31.34.60546", + "templateHash": "15055411149553856815" } }, "parameters": { @@ -12002,8 +12009,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "11815683316027655066" + "version": "0.31.34.60546", + "templateHash": "6140806447963417872" } }, "parameters": { @@ -12143,8 +12150,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "5010063642680411887" + "version": "0.31.34.60546", + "templateHash": "4329443301777514730" } }, "parameters": { @@ -12255,8 +12262,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "476825279659345843" + "version": "0.31.34.60546", + "templateHash": "7916275533870259687" } }, "parameters": { @@ -12342,8 +12349,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "5833668119144644566" + "version": "0.31.34.60546", + "templateHash": "8886215231939195200" } }, "parameters": { @@ -12433,8 +12440,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "3901504540519683161" + "version": "0.31.34.60546", + "templateHash": "15160754302365390942" } }, "parameters": { @@ -12534,6 +12541,9 @@ "availabilityZones": { "value": "[parameters('availabilityZones')]" }, + "avdConfigurationZipFileName": { + "value": "[parameters('avdConfigurationZipFileName')]" + }, "dataCollectionRuleResourceId": { "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.dataCollectionRuleResourceId.value]" }, @@ -12693,8 +12703,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "14352967056938252569" + "version": "0.31.34.60546", + "templateHash": "15064449949268346552" } }, "parameters": { @@ -12713,6 +12723,9 @@ "availabilityZones": { "type": "array" }, + "avdConfigurationZipFileName": { + "type": "string" + }, "dataCollectionRuleResourceId": { "type": "string" }, @@ -12917,8 +12930,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "4247819304130517352" + "version": "0.31.34.60546", + "templateHash": "13909498964093142621" } }, "parameters": { @@ -12993,8 +13006,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "15359000832124286075" + "version": "0.31.34.60546", + "templateHash": "1315528727979495191" } }, "parameters": { @@ -13052,6 +13065,9 @@ "availabilityZones": { "value": "[parameters('availabilityZones')]" }, + "avdConfigurationZipFileName": { + "value": "[parameters('avdConfigurationZipFileName')]" + }, "batchCount": { "value": "[range(1, parameters('sessionHostBatchCount'))[copyIndex()]]" }, @@ -13184,8 +13200,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "1759351975000742065" + "version": "0.31.34.60546", + "templateHash": "7274055108234907992" } }, "parameters": { @@ -13201,6 +13217,9 @@ "availabilityZones": { "type": "array" }, + "avdConfigurationZipFileName": { + "type": "string" + }, "batchCount": { "type": "int" }, @@ -13421,6 +13440,9 @@ "name": "[format('{0}{1}', variables('sessionHostNamePrefix'), padLeft(add(range(0, parameters('sessionHostCount'))[copyIndex()], parameters('sessionHostIndex')), 4, '0'))]", "location": "[parameters('location')]", "tags": "[parameters('tagsVirtualMachines')]", + "identity": { + "type": "SystemAssigned" + }, "zones": "[if(equals(parameters('availability'), 'AvailabilityZones'), createArray(parameters('availabilityZones')[mod(range(0, parameters('sessionHostCount'))[copyIndex()], length(parameters('availabilityZones')))]), null())]", "properties": { "availabilitySet": "[if(equals(parameters('availability'), 'AvailabilitySets'), createObject('id', resourceId('Microsoft.Compute/availabilitySets', format('{0}-{1}', parameters('availabilitySetNamePrefix'), padLeft(div(add(range(0, parameters('sessionHostCount'))[copyIndex()], parameters('sessionHostIndex')), 200), 2, '0')))), null())]", @@ -13605,7 +13627,7 @@ "typeHandlerVersion": "2.73", "autoUpgradeMinorVersion": true, "settings": { - "modulesUrl": "[format('https://wvdportalstorageblob.blob.{0}/galleryartifacts/Configuration_1.0.02721.349.zip', environment().suffixes.storage)]", + "modulesUrl": "[format('https://wvdportalstorageblob.blob.{0}/galleryartifacts/{1}', environment().suffixes.storage, parameters('avdConfigurationZipFileName'))]", "configurationFunction": "Configuration.ps1\\AddSessionHost", "properties": { "hostPoolName": "[parameters('hostPoolName')]", @@ -13613,7 +13635,7 @@ "UserName": "PLACEHOLDER_DO_NOT_USE", "Password": "PrivateSettingsRef:RegistrationInfoToken" }, - "aadJoin": "[not(contains(parameters('activeDirectorySolution'), 'DomainServices'))]", + "aadJoin": "[contains(parameters('activeDirectorySolution'), 'EntraId')]", "UseAgentDownloadEndpoint": false, "mdmId": "[if(variables('intune'), '0000000a-0000-0000-c000-000000000000', '')]" } @@ -13667,7 +13689,7 @@ "name": "extension_AADLoginForWindows", "count": "[length(range(0, parameters('sessionHostCount')))]" }, - "condition": "[not(contains(parameters('activeDirectorySolution'), 'DomainServices'))]", + "condition": "[contains(parameters('activeDirectorySolution'), 'EntraId')]", "type": "Microsoft.Compute/virtualMachines/extensions", "apiVersion": "2021-03-01", "name": "[format('{0}/{1}', format('{0}{1}', variables('sessionHostNamePrefix'), padLeft(add(range(0, parameters('sessionHostCount'))[range(0, parameters('sessionHostCount'))[copyIndex()]], parameters('sessionHostIndex')), 4, '0')), 'AADLoginForWindows')]", @@ -13821,8 +13843,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "17500708096438980911" + "version": "0.31.34.60546", + "templateHash": "5317974790875443937" } }, "parameters": { @@ -13956,8 +13978,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "17500708096438980911" + "version": "0.31.34.60546", + "templateHash": "5317974790875443937" } }, "parameters": { @@ -14074,8 +14096,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "12693991481187214678" + "version": "0.31.34.60546", + "templateHash": "3032901180833561492" } }, "parameters": { @@ -14160,8 +14182,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "10260725559165503719" + "version": "0.31.34.60546", + "templateHash": "15863312965086940829" } }, "parameters": { @@ -14257,8 +14279,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "3901504540519683161" + "version": "0.31.34.60546", + "templateHash": "15160754302365390942" } }, "parameters": { @@ -14343,8 +14365,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "3323446775924256571" + "version": "0.31.34.60546", + "templateHash": "6161798997000105603" } }, "parameters": { @@ -14427,8 +14449,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "17500708096438980911" + "version": "0.31.34.60546", + "templateHash": "5317974790875443937" } }, "parameters": { From 3ef418d8c63574fb8220fb0e72e73b2621d0e014 Mon Sep 17 00:00:00 2001 From: tsweatman1 <155660739+tsweatman1@users.noreply.github.com> Date: Fri, 8 Nov 2024 17:24:57 -0500 Subject: [PATCH 6/9] updated window remote access vm extension (#1127) * updated window remote access vm extension * GitHub Action: Build Bicep to JSON * Updated symbolic names for extension resources for consistency --------- Co-authored-by: github-actions Co-authored-by: Jason Masten --- src/bicep/mlz.json | 298 ++++++++++-------- src/bicep/modules/remote-access.bicep | 1 + .../modules/windows-virtual-machine.bicep | 65 ++-- 3 files changed, 213 insertions(+), 151 deletions(-) diff --git a/src/bicep/mlz.json b/src/bicep/mlz.json index f0e6c0753..bfcf44a4e 100644 --- a/src/bicep/mlz.json +++ b/src/bicep/mlz.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "11000848548598385857" + "version": "0.31.34.60546", + "templateHash": "9379117934026664616" } }, "parameters": { @@ -887,8 +887,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "16220429751656280628" + "version": "0.31.34.60546", + "templateHash": "16255426588501076488" } }, "parameters": { @@ -967,8 +967,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "3270228280505109788" + "version": "0.31.34.60546", + "templateHash": "8357935203009683307" } }, "parameters": { @@ -1558,8 +1558,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "4151572700986885014" + "version": "0.31.34.60546", + "templateHash": "2502930168536032010" } }, "parameters": { @@ -1703,8 +1703,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "11616529434755756375" + "version": "0.31.34.60546", + "templateHash": "12782100486021815060" } }, "parameters": { @@ -1763,8 +1763,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "10109660736443081073" + "version": "0.31.34.60546", + "templateHash": "17445726037807437290" } }, "parameters": { @@ -1904,8 +1904,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "11238215245345532485" + "version": "0.31.34.60546", + "templateHash": "11630019985682391796" } }, "parameters": { @@ -2081,8 +2081,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "246030726359755329" + "version": "0.31.34.60546", + "templateHash": "12984958392421507149" } }, "parameters": { @@ -2349,8 +2349,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "6722359274420487391" + "version": "0.31.34.60546", + "templateHash": "2196017082128829477" } }, "parameters": { @@ -2428,8 +2428,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "6722359274420487391" + "version": "0.31.34.60546", + "templateHash": "2196017082128829477" } }, "parameters": { @@ -2509,8 +2509,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "12652296496577802490" + "version": "0.31.34.60546", + "templateHash": "8417416771003518918" } }, "parameters": { @@ -2610,8 +2610,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "17592952825859536181" + "version": "0.31.34.60546", + "templateHash": "4071285799602638747" } }, "parameters": { @@ -2679,8 +2679,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "7007835755326231171" + "version": "0.31.34.60546", + "templateHash": "3465065949976146403" } }, "parameters": { @@ -2793,8 +2793,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "13031612584115851814" + "version": "0.31.34.60546", + "templateHash": "1730170030487163899" } }, "parameters": { @@ -2883,8 +2883,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "13031612584115851814" + "version": "0.31.34.60546", + "templateHash": "1730170030487163899" } }, "parameters": { @@ -2997,8 +2997,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "16308694041027148637" + "version": "0.31.34.60546", + "templateHash": "5808375390237466838" } }, "parameters": { @@ -3398,8 +3398,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "3535186238457701125" + "version": "0.31.34.60546", + "templateHash": "11567108053836979012" } }, "parameters": { @@ -3516,8 +3516,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "6722359274420487391" + "version": "0.31.34.60546", + "templateHash": "2196017082128829477" } }, "parameters": { @@ -3599,8 +3599,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "12652296496577802490" + "version": "0.31.34.60546", + "templateHash": "8417416771003518918" } }, "parameters": { @@ -3702,8 +3702,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "17592952825859536181" + "version": "0.31.34.60546", + "templateHash": "4071285799602638747" } }, "parameters": { @@ -3779,8 +3779,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "7007835755326231171" + "version": "0.31.34.60546", + "templateHash": "3465065949976146403" } }, "parameters": { @@ -3927,8 +3927,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "9502319494004310539" + "version": "0.31.34.60546", + "templateHash": "6489616383757058493" } }, "parameters": { @@ -3980,8 +3980,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "2015499370398293300" + "version": "0.31.34.60546", + "templateHash": "5031620623183573702" } }, "parameters": { @@ -4059,8 +4059,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "3832300165614083813" + "version": "0.31.34.60546", + "templateHash": "15799890372401066181" } }, "parameters": { @@ -4112,8 +4112,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "2015499370398293300" + "version": "0.31.34.60546", + "templateHash": "5031620623183573702" } }, "parameters": { @@ -4189,8 +4189,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "1136616241283939780" + "version": "0.31.34.60546", + "templateHash": "16841222955467860758" } }, "parameters": { @@ -4266,8 +4266,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "2757774880390840506" + "version": "0.31.34.60546", + "templateHash": "12405780209119797551" } }, "parameters": { @@ -4432,8 +4432,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "11152278840466293351" + "version": "0.31.34.60546", + "templateHash": "6386888682917235118" } }, "parameters": { @@ -4521,8 +4521,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "1874197755006172394" + "version": "0.31.34.60546", + "templateHash": "419730844167715947" } }, "parameters": { @@ -4779,8 +4779,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "17613382135787640077" + "version": "0.31.34.60546", + "templateHash": "7946295394028911325" } }, "parameters": { @@ -4859,8 +4859,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "8372265102618018066" + "version": "0.31.34.60546", + "templateHash": "967013811257719495" } }, "parameters": { @@ -4954,8 +4954,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "6675708379514380442" + "version": "0.31.34.60546", + "templateHash": "9115514582672423063" } }, "parameters": { @@ -5099,8 +5099,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "17664628665088454883" + "version": "0.31.34.60546", + "templateHash": "366255573709691198" } }, "parameters": { @@ -5185,8 +5185,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "15985115749591574535" + "version": "0.31.34.60546", + "templateHash": "11337913680813675353" } }, "parameters": { @@ -5353,8 +5353,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "4040791601769189381" + "version": "0.31.34.60546", + "templateHash": "6648745810362038848" } }, "parameters": { @@ -5481,8 +5481,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "9321128177359823831" + "version": "0.31.34.60546", + "templateHash": "18318483669505051648" } }, "parameters": { @@ -5709,8 +5709,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "208914367173865032" + "version": "0.31.34.60546", + "templateHash": "12373038860746323248" } }, "parameters": { @@ -5886,8 +5886,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "3093428281213661074" + "version": "0.31.34.60546", + "templateHash": "10895428597231985214" } }, "parameters": { @@ -6046,8 +6046,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "4932706162089134974" + "version": "0.31.34.60546", + "templateHash": "13001914044795164689" } }, "parameters": { @@ -6368,8 +6368,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "6795024125500853154" + "version": "0.31.34.60546", + "templateHash": "8788916623054227962" } }, "parameters": { @@ -6519,6 +6519,9 @@ "subnetResourceId": { "value": "[parameters('hubSubnetResourceId')]" }, + "supportedClouds": { + "value": "[parameters('supportedClouds')]" + }, "tags": { "value": "[parameters('tags')]" }, @@ -6532,8 +6535,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "8382745830446158671" + "version": "0.31.34.60546", + "templateHash": "16352081091904770478" } }, "parameters": { @@ -6567,8 +6570,7 @@ "type": "string" }, "mlzTags": { - "type": "object", - "defaultValue": {} + "type": "object" }, "name": { "type": "string" @@ -6600,9 +6602,11 @@ "subnetResourceId": { "type": "string" }, + "supportedClouds": { + "type": "array" + }, "tags": { - "type": "object", - "defaultValue": {} + "type": "object" }, "version": { "type": "string" @@ -6680,7 +6684,7 @@ }, { "type": "Microsoft.Compute/virtualMachines/extensions", - "apiVersion": "2021-03-01", + "apiVersion": "2024-03-01", "name": "[format('{0}/{1}', parameters('name'), 'GuestAttestation')]", "location": "[parameters('location')]", "properties": { @@ -6688,6 +6692,7 @@ "type": "GuestAttestation", "typeHandlerVersion": "1.0", "autoUpgradeMinorVersion": true, + "enableAutomaticUpgrade": true, "settings": { "AttestationConfig": { "MaaSettings": { @@ -6710,13 +6715,15 @@ { "type": "Microsoft.Compute/virtualMachines/extensions", "apiVersion": "2021-04-01", - "name": "[format('{0}/{1}', parameters('name'), 'DependencyAgentWindows')]", + "name": "[format('{0}/{1}', parameters('name'), 'AzurePolicyforWindows')]", "location": "[parameters('location')]", + "tags": "[union(if(contains(parameters('tags'), 'Microsoft.Compute/virtualMachines'), parameters('tags')['Microsoft.Compute/virtualMachines'], createObject()), parameters('mlzTags'))]", "properties": { - "publisher": "Microsoft.Azure.Monitoring.DependencyAgent", - "type": "DependencyAgentWindows", - "typeHandlerVersion": "9.5", - "autoUpgradeMinorVersion": true + "publisher": "Microsoft.GuestConfiguration", + "type": "ConfigurationforWindows", + "typeHandlerVersion": "1.0", + "autoUpgradeMinorVersion": true, + "enableAutomaticUpgrade": true }, "dependsOn": [ "[resourceId('Microsoft.Compute/virtualMachines', parameters('name'))]" @@ -6725,11 +6732,29 @@ { "type": "Microsoft.Compute/virtualMachines/extensions", "apiVersion": "2021-04-01", - "name": "[format('{0}/{1}', parameters('name'), 'AzurePolicyforWindows')]", + "name": "[format('{0}/{1}', parameters('name'), 'Microsoft.Azure.NetworkWatcher')]", "location": "[parameters('location')]", + "tags": "[union(if(contains(parameters('tags'), 'Microsoft.Compute/virtualMachines'), parameters('tags')['Microsoft.Compute/virtualMachines'], createObject()), parameters('mlzTags'))]", "properties": { - "publisher": "Microsoft.GuestConfiguration", - "type": "ConfigurationforWindows", + "publisher": "Microsoft.Azure.NetworkWatcher", + "type": "NetworkWatcherAgentWindows", + "typeHandlerVersion": "1.4" + }, + "dependsOn": [ + "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('name'), 'AzurePolicyforWindows')]", + "[resourceId('Microsoft.Compute/virtualMachines', parameters('name'))]" + ] + }, + { + "condition": "[contains(parameters('supportedClouds'), environment().name)]", + "type": "Microsoft.Compute/virtualMachines/extensions", + "apiVersion": "2023-03-01", + "name": "[format('{0}/{1}', parameters('name'), 'AzureMonitorWindowsAgent')]", + "location": "[parameters('location')]", + "tags": "[union(if(contains(parameters('tags'), 'Microsoft.Compute/virtualMachines'), parameters('tags')['Microsoft.Compute/virtualMachines'], createObject()), parameters('mlzTags'))]", + "properties": { + "publisher": "Microsoft.Azure.Monitor", + "type": "AzureMonitorWindowsAgent", "typeHandlerVersion": "1.0", "autoUpgradeMinorVersion": true, "enableAutomaticUpgrade": true @@ -6739,10 +6764,12 @@ ] }, { + "condition": "[not(contains(parameters('supportedClouds'), environment().name))]", "type": "Microsoft.Compute/virtualMachines/extensions", "apiVersion": "2021-04-01", "name": "[format('{0}/{1}', parameters('name'), 'MMAExtension')]", "location": "[parameters('location')]", + "tags": "[union(if(contains(parameters('tags'), 'Microsoft.Compute/virtualMachines'), parameters('tags')['Microsoft.Compute/virtualMachines'], createObject()), parameters('mlzTags'))]", "properties": { "publisher": "Microsoft.EnterpriseCloud.Monitoring", "type": "MicrosoftMonitoringAgent", @@ -6756,20 +6783,25 @@ } }, "dependsOn": [ + "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('name'), 'Microsoft.Azure.NetworkWatcher')]", "[resourceId('Microsoft.Compute/virtualMachines', parameters('name'))]" ] }, { + "condition": "[not(contains(parameters('supportedClouds'), environment().name))]", "type": "Microsoft.Compute/virtualMachines/extensions", "apiVersion": "2021-04-01", - "name": "[format('{0}/{1}', parameters('name'), 'Microsoft.Azure.NetworkWatcher')]", + "name": "[format('{0}/{1}', parameters('name'), 'DependencyAgentWindows')]", "location": "[parameters('location')]", + "tags": "[union(if(contains(parameters('tags'), 'Microsoft.Compute/virtualMachines'), parameters('tags')['Microsoft.Compute/virtualMachines'], createObject()), parameters('mlzTags'))]", "properties": { - "publisher": "Microsoft.Azure.NetworkWatcher", - "type": "NetworkWatcherAgentWindows", - "typeHandlerVersion": "1.4" + "publisher": "Microsoft.Azure.Monitoring.DependencyAgent", + "type": "DependencyAgentWindows", + "typeHandlerVersion": "9.5", + "autoUpgradeMinorVersion": true }, "dependsOn": [ + "[resourceId('Microsoft.Compute/virtualMachines/extensions', parameters('name'), 'MMAExtension')]", "[resourceId('Microsoft.Compute/virtualMachines', parameters('name'))]" ] }, @@ -6811,8 +6843,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "6795024125500853154" + "version": "0.31.34.60546", + "templateHash": "8788916623054227962" } }, "parameters": { @@ -6956,8 +6988,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "11618493610681415173" + "version": "0.31.34.60546", + "templateHash": "15180554823445581333" } }, "parameters": { @@ -7073,8 +7105,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "13359240625570837130" + "version": "0.31.34.60546", + "templateHash": "10245709494871874056" } }, "parameters": { @@ -7357,8 +7389,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "5804308759558531770" + "version": "0.31.34.60546", + "templateHash": "11013785870428129557" } }, "parameters": { @@ -7443,8 +7475,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "8389358797157027271" + "version": "0.31.34.60546", + "templateHash": "5964906331561259426" } }, "parameters": { @@ -7530,8 +7562,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "8734968617443229139" + "version": "0.31.34.60546", + "templateHash": "3200577753830159376" } }, "parameters": { @@ -7617,8 +7649,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "9851134383266019486" + "version": "0.31.34.60546", + "templateHash": "5533529544253509904" } }, "parameters": { @@ -7699,8 +7731,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "7358671180047253284" + "version": "0.31.34.60546", + "templateHash": "169088610601729285" } }, "parameters": { @@ -7781,8 +7813,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "17520636026466541947" + "version": "0.31.34.60546", + "templateHash": "5104130163491279218" } }, "parameters": { @@ -7859,8 +7891,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "16750249074235971016" + "version": "0.31.34.60546", + "templateHash": "8958046244399156747" } }, "parameters": { @@ -7940,8 +7972,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "3320497816733398371" + "version": "0.31.34.60546", + "templateHash": "13721817451936402949" } }, "parameters": { @@ -8012,8 +8044,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "12248054724275593988" + "version": "0.31.34.60546", + "templateHash": "16505356842938617427" } }, "parameters": { @@ -8098,8 +8130,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "11524143102332195763" + "version": "0.31.34.60546", + "templateHash": "7145459344230861166" } }, "parameters": { @@ -8155,8 +8187,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "10349924076266160595" + "version": "0.31.34.60546", + "templateHash": "4206571908443996627" } }, "parameters": { @@ -8331,8 +8363,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "8372265102618018066" + "version": "0.31.34.60546", + "templateHash": "967013811257719495" } }, "parameters": { @@ -8429,8 +8461,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "3268860743787686" + "version": "0.31.34.60546", + "templateHash": "4104174025531546645" } }, "parameters": { @@ -8493,8 +8525,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "3588347827815061814" + "version": "0.31.34.60546", + "templateHash": "2575529544484247921" } }, "parameters": { diff --git a/src/bicep/modules/remote-access.bicep b/src/bicep/modules/remote-access.bicep index d07f380d8..b2fa98acf 100644 --- a/src/bicep/modules/remote-access.bicep +++ b/src/bicep/modules/remote-access.bicep @@ -123,6 +123,7 @@ module windowsVirtualMachine '../modules/windows-virtual-machine.bicep' = sku: windowsVmSku storageAccountType: windowsVmStorageAccountType subnetResourceId: hubSubnetResourceId + supportedClouds: supportedClouds tags: tags version: windowsVmVersion } diff --git a/src/bicep/modules/windows-virtual-machine.bicep b/src/bicep/modules/windows-virtual-machine.bicep index 21769be0d..fd386f283 100644 --- a/src/bicep/modules/windows-virtual-machine.bicep +++ b/src/bicep/modules/windows-virtual-machine.bicep @@ -14,7 +14,7 @@ param diskName string param hybridUseBenefit bool param location string param logAnalyticsWorkspaceId string -param mlzTags object = {} +param mlzTags object param name string param networkInterfaceName string param networkSecurityGroupResourceId string @@ -25,7 +25,8 @@ param size string param sku string param storageAccountType string param subnetResourceId string -param tags object = {} +param supportedClouds array +param tags object param version string module networkInterface '../modules/network-interface.bicep' = { @@ -106,7 +107,7 @@ resource virtualMachine 'Microsoft.Compute/virtualMachines@2021-04-01' = { } } -resource guestAttestationExtension 'Microsoft.Compute/virtualMachines/extensions@2021-03-01' = { +resource extension_GuestAttestation 'Microsoft.Compute/virtualMachines/extensions@2024-03-01' = { parent: virtualMachine name: 'GuestAttestation' location: location @@ -115,6 +116,7 @@ resource guestAttestationExtension 'Microsoft.Compute/virtualMachines/extensions type: 'GuestAttestation' typeHandlerVersion: '1.0' autoUpgradeMinorVersion: true + enableAutomaticUpgrade: true settings: { AttestationConfig: { MaaSettings: { @@ -132,35 +134,54 @@ resource guestAttestationExtension 'Microsoft.Compute/virtualMachines/extensions } } -resource dependencyAgent 'Microsoft.Compute/virtualMachines/extensions@2021-04-01' = { +resource extension_GuestConfiguration 'Microsoft.Compute/virtualMachines/extensions@2021-04-01' = { parent: virtualMachine - name: 'DependencyAgentWindows' + name: 'AzurePolicyforWindows' location: location + tags: union(contains(tags, 'Microsoft.Compute/virtualMachines') ? tags['Microsoft.Compute/virtualMachines'] : {}, mlzTags) properties: { - publisher: 'Microsoft.Azure.Monitoring.DependencyAgent' - type: 'DependencyAgentWindows' - typeHandlerVersion: '9.5' + publisher: 'Microsoft.GuestConfiguration' + type: 'ConfigurationforWindows' + typeHandlerVersion: '1.0' autoUpgradeMinorVersion: true + enableAutomaticUpgrade: true } } -resource policyExtension 'Microsoft.Compute/virtualMachines/extensions@2021-04-01' = { +resource extension_NetworkWatcher 'Microsoft.Compute/virtualMachines/extensions@2021-04-01' = { parent: virtualMachine - name: 'AzurePolicyforWindows' + name: 'Microsoft.Azure.NetworkWatcher' location: location + tags: union(contains(tags, 'Microsoft.Compute/virtualMachines') ? tags['Microsoft.Compute/virtualMachines'] : {}, mlzTags) properties: { - publisher: 'Microsoft.GuestConfiguration' - type: 'ConfigurationforWindows' + publisher: 'Microsoft.Azure.NetworkWatcher' + type: 'NetworkWatcherAgentWindows' + typeHandlerVersion: '1.4' + } + dependsOn: [ + extension_GuestConfiguration + ] +} + +resource extension_AzureMonitorWindowsAgent 'Microsoft.Compute/virtualMachines/extensions@2023-03-01' = if (contains(supportedClouds, environment().name)) { + parent: virtualMachine + name: 'AzureMonitorWindowsAgent' + location: location + tags: union(contains(tags, 'Microsoft.Compute/virtualMachines') ? tags['Microsoft.Compute/virtualMachines'] : {}, mlzTags) + properties: { + publisher: 'Microsoft.Azure.Monitor' + type: 'AzureMonitorWindowsAgent' typeHandlerVersion: '1.0' autoUpgradeMinorVersion: true enableAutomaticUpgrade: true } } -resource mmaExtension 'Microsoft.Compute/virtualMachines/extensions@2021-04-01' = { +resource extension_MicrosoftMonitoringAgent 'Microsoft.Compute/virtualMachines/extensions@2021-04-01' = if (!contains(supportedClouds, environment().name)) { parent: virtualMachine name: 'MMAExtension' location: location + tags: union(contains(tags, 'Microsoft.Compute/virtualMachines') ? tags['Microsoft.Compute/virtualMachines'] : {}, mlzTags) properties: { publisher: 'Microsoft.EnterpriseCloud.Monitoring' type: 'MicrosoftMonitoringAgent' @@ -173,15 +194,23 @@ resource mmaExtension 'Microsoft.Compute/virtualMachines/extensions@2021-04-01' workspaceKey: listKeys(logAnalyticsWorkspaceId , '2015-11-01-preview').primarySharedKey } } + dependsOn: [ + extension_NetworkWatcher + ] } -resource networkWatcher 'Microsoft.Compute/virtualMachines/extensions@2021-04-01' = { +resource extension_DependencyAgent 'Microsoft.Compute/virtualMachines/extensions@2021-04-01' = if (!contains(supportedClouds, environment().name)) { parent: virtualMachine - name: 'Microsoft.Azure.NetworkWatcher' + name: 'DependencyAgentWindows' location: location + tags: union(contains(tags, 'Microsoft.Compute/virtualMachines') ? tags['Microsoft.Compute/virtualMachines'] : {}, mlzTags) properties: { - publisher: 'Microsoft.Azure.NetworkWatcher' - type: 'NetworkWatcherAgentWindows' - typeHandlerVersion: '1.4' + publisher: 'Microsoft.Azure.Monitoring.DependencyAgent' + type: 'DependencyAgentWindows' + typeHandlerVersion: '9.5' + autoUpgradeMinorVersion: true } + dependsOn: [ + extension_MicrosoftMonitoringAgent + ] } From 054f6f92ed903803647bdf817cd0684662ea80b9 Mon Sep 17 00:00:00 2001 From: Jason Masten Date: Wed, 13 Nov 2024 14:24:18 -0500 Subject: [PATCH 7/9] AVD Add-On: Added Scaling Plans, Removed Scaling Tool (#1128) * Removed module for scaling tool * Added names for scaling plan resources * Added abbreviation for scaling plan * Updated scaling elements & params for scaling plan * Updated comments, Removed scaling tool refs, Added params for scaling plan * Added module for scaling plan * Removed scaling tool refs * Added params & scaling plan deployment * Compiled bicep changes * Removed unnecessary files * Moved file to mgmt folder, Updated params * Updated params & role assignment * Updated params & file location * Updated params, comments, & role assignment * Updated params & role assignment * Compiled bicep changes * Removed elements for scaling tool, Added elements for scaling plan * Simplified element for AVD object ID * Removed unnecessary conditions, Fixed role definition reference * Removed params * Compiled bicep changes * Removed param * Fixed conditions, resource names, & deployment names * Removed param * Compiled bicep changes * Fixed formatting & names, Updated vars * Compiled bicep changes * Updated default value for host pool type * Updated allowed values for param, Removed var & param * Updated output for host pool type * Removed param, var, & output * Removed RDP property, Updated value & conditions * Removed params, Updated conditions * Removed param & var, Updated condition * Compiled bicep changes * Updated exclusion tag value * Added exclusion tag to VM * Compiled bicep changes * Removed exclusion tag * Added dependencies to scaling plan deployment * Compiled bicep changes * Updated dependencies * Complied bicep changes * Moved scaling plan schedules to separate deployments * GitHub Action: Build Bicep to JSON * Updated documentation for AVD Autoscale * Updated links to MSFT Learn * Created new parameters file with correct params & default values * Removed scaling tool scripts * Created script to disable scaling plan * Added deployment to disable existing scaling plan * Added param * Added param & role assignment * Added param & role assignment * Updated role assignments * Added param & role assignment * Added params * Added param & role assignment * Added params * Compiled bicep changes * Fixed exclusion tag * Updated dependencies * Updated symbolic name, Fixed scope, Added dependency --------- Co-authored-by: github-actions --- .../artifacts/Disable-Autoscale.ps1 | 41 + .../artifacts/scaling-tool/profile.ps1 | 1 - .../artifacts/scaling-tool/requirements.psd1 | 8 - .../artifacts/scaling-tool/run.ps1 | 759 -------------- .../azure-virtual-desktop/docs/features.md | 2 +- .../autoIncreasePremiumFileShareQuota.md | 2 +- .../features/{scalingTool.md => autoscale.md} | 15 +- .../docs/features/backups.md | 4 +- .../docs/features/drainMode.md | 4 +- .../docs/features/fslogix.md | 4 +- .../docs/features/gpu.md | 4 +- .../docs/features/highAvailability.md | 4 +- .../docs/features/monitoring.md | 4 +- .../docs/features/serverSideEncryption.md | 2 +- .../docs/features/smbMultiChannel.md | 4 +- .../docs/features/startVmOnConnect.md | 4 +- .../docs/features/trustedLaunch.md | 4 +- .../docs/features/validation.md | 2 +- .../docs/prerequisites.md | 2 +- .../controlPlane/applicationGroup.bicep | 20 +- .../modules/controlPlane/controlPlane.bicep | 3 + .../modules/controlPlane/hostPool.bicep | 20 +- .../fslogix/azureFiles/azureFiles.bicep | 20 +- .../modules/fslogix/fslogix.bicep | 2 - .../azure-virtual-desktop/modules/logic.bicep | 3 - .../management/automationAccount.bicep | 126 --- .../modules/management/functionApp.bicep | 116 +-- .../modules/management/jobSchedules.bicep | 56 -- .../modules/management/management.bicep | 99 +- .../modules/management/scalingPlan.bicep | 276 +++++ .../modules/management/scalingTool.bicep | 561 ----------- .../modules/management/schedules.bicep | 37 - .../modules/management/virtualMachine.bicep | 11 + .../modules/sessionHosts/sessionHosts.bicep | 86 +- .../sessionHosts/virtualMachines.bicep | 4 +- .../azure-virtual-desktop/parameters.json | 222 ----- .../azure-virtual-desktop/solution.bicep | 174 ++-- .../azure-virtual-desktop/solution.json | 941 ++++++++++++------ .../solution.parameters.json | 207 ++++ .../azure-virtual-desktop/uiDefinition.json | 139 +-- src/bicep/data/resourceAbbreviations.json | 1 + src/bicep/mlz.json | 9 +- src/bicep/modules/naming-convention.bicep | 2 + 43 files changed, 1561 insertions(+), 2444 deletions(-) create mode 100644 src/bicep/add-ons/azure-virtual-desktop/artifacts/Disable-Autoscale.ps1 delete mode 100644 src/bicep/add-ons/azure-virtual-desktop/artifacts/scaling-tool/profile.ps1 delete mode 100644 src/bicep/add-ons/azure-virtual-desktop/artifacts/scaling-tool/requirements.psd1 delete mode 100644 src/bicep/add-ons/azure-virtual-desktop/artifacts/scaling-tool/run.ps1 rename src/bicep/add-ons/azure-virtual-desktop/docs/features/{scalingTool.md => autoscale.md} (58%) delete mode 100644 src/bicep/add-ons/azure-virtual-desktop/modules/management/automationAccount.bicep delete mode 100644 src/bicep/add-ons/azure-virtual-desktop/modules/management/jobSchedules.bicep create mode 100644 src/bicep/add-ons/azure-virtual-desktop/modules/management/scalingPlan.bicep delete mode 100644 src/bicep/add-ons/azure-virtual-desktop/modules/management/scalingTool.bicep delete mode 100644 src/bicep/add-ons/azure-virtual-desktop/modules/management/schedules.bicep delete mode 100644 src/bicep/add-ons/azure-virtual-desktop/parameters.json create mode 100644 src/bicep/add-ons/azure-virtual-desktop/solution.parameters.json diff --git a/src/bicep/add-ons/azure-virtual-desktop/artifacts/Disable-Autoscale.ps1 b/src/bicep/add-ons/azure-virtual-desktop/artifacts/Disable-Autoscale.ps1 new file mode 100644 index 000000000..15d9eb3e6 --- /dev/null +++ b/src/bicep/add-ons/azure-virtual-desktop/artifacts/Disable-Autoscale.ps1 @@ -0,0 +1,41 @@ +Param( + [string]$HostPoolResourceId, + [string]$ResourceGroupName, + [string]$ResourceManagerUri, + [string]$ScalingPlanName, + [string]$SubscriptionId, + [string]$UserAssignedIdentityClientId +) + +$ErrorActionPreference = 'Stop' +$WarningPreference = 'SilentlyContinue' + +# Fix the resource manager URI since only AzureCloud contains a trailing slash +$ResourceManagerUriFixed = if($ResourceManagerUri[-1] -eq '/'){$ResourceManagerUri} else {$ResourceManagerUri + '/'} + +# Get an access token for Azure resources +$AzureManagementAccessToken = (Invoke-RestMethod ` + -Headers @{Metadata="true"} ` + -Uri $('http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=' + $ResourceManagerUriFixed + '&client_id=' + $UserAssignedIdentityClientId)).access_token + +# Set header for Azure Management API +$AzureManagementHeader = @{ + 'Content-Type'='application/json' + 'Authorization'='Bearer ' + $AzureManagementAccessToken +} + +# Check if the scaling plan exists: https://learn.microsoft.com/rest/api/desktopvirtualization/scaling-plans/list-by-resource-group +$ScalingPlanExists = (Invoke-RestMethod ` + -Headers $AzureManagementHeader ` + -Method 'GET' ` + -Uri $($ResourceManagerUriFixed + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $ResourceGroupName + '/providers/Microsoft.DesktopVirtualization/scalingPlans?api-version=2024-04-03')).value | Where-Object {$_.name -eq $ScalingPlanName} + +# Disable autoscale for the host pool: https://learn.microsoft.com/rest/api/desktopvirtualization/scaling-plans/update +if ($ScalingPlanExists) +{ + Invoke-RestMethod ` + -Body (@{properties = @{hostPoolReferences = @(@{hostPoolArmPath = $HostPoolResourceId; scalingPlanEnabled = $false})}} | ConvertTo-Json) ` + -Headers $AzureManagementHeader ` + -Method 'PATCH' ` + -Uri $($ResourceManagerUriFixed + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $ResourceGroupName + '/providers/Microsoft.DesktopVirtualization/scalingPlans/' + $ScalingPlanName + '?api-version=2024-04-03') | Out-Null +} \ No newline at end of file diff --git a/src/bicep/add-ons/azure-virtual-desktop/artifacts/scaling-tool/profile.ps1 b/src/bicep/add-ons/azure-virtual-desktop/artifacts/scaling-tool/profile.ps1 deleted file mode 100644 index 33267a961..000000000 --- a/src/bicep/add-ons/azure-virtual-desktop/artifacts/scaling-tool/profile.ps1 +++ /dev/null @@ -1 +0,0 @@ -# Authentication is provided in the script \ No newline at end of file diff --git a/src/bicep/add-ons/azure-virtual-desktop/artifacts/scaling-tool/requirements.psd1 b/src/bicep/add-ons/azure-virtual-desktop/artifacts/scaling-tool/requirements.psd1 deleted file mode 100644 index 6bf9b0f28..000000000 --- a/src/bicep/add-ons/azure-virtual-desktop/artifacts/scaling-tool/requirements.psd1 +++ /dev/null @@ -1,8 +0,0 @@ -# This file enables modules to be automatically managed by the Functions service. -# See https://aka.ms/functionsmanageddependency for additional information. -# -@{ - # For latest supported version, go to 'https://www.powershellgallery.com/packages/Az'. - # To use the Az module in your function app, please uncomment the line below. - # 'Az' = '7.*' -} \ No newline at end of file diff --git a/src/bicep/add-ons/azure-virtual-desktop/artifacts/scaling-tool/run.ps1 b/src/bicep/add-ons/azure-virtual-desktop/artifacts/scaling-tool/run.ps1 deleted file mode 100644 index 48f428d1c..000000000 --- a/src/bicep/add-ons/azure-virtual-desktop/artifacts/scaling-tool/run.ps1 +++ /dev/null @@ -1,759 +0,0 @@ -param($Timer) - -try -{ - [string]$BeginPeakTime = $env:BeginPeakTime - [string]$EndPeakTime = $env:EndPeakTime - [string]$EnvironmentName = $env:EnvironmentName - [string]$HostPoolName = $env:HostPoolName - [string]$HostPoolResourceGroupName = $env:HostPoolResourceGroupName - [int]$LimitSecondsToForceLogOffUser = $env:LimitSecondsToForceLogOffUser - [string]$LogOffMessageBody = $env:LogOffMessageBody - [string]$LogOffMessageTitle = $env:LogOffMessageTitle - [string]$MaintenanceTagName = $env:MaintenanceTagName - [int]$MinimumNumberOfRDSH = $env:MinimumNumberOfRDSH - [string]$ResourceManagerUrl = $env:ResourceManagerUrl - [double]$SessionThresholdPerCPU = $env:SessionThresholdPerCPU - [string]$SubscriptionId = $env:SubscriptionId - [string]$TenantId = $env:TenantId - [string]$TimeDifference = $env:TimeDifference - [string[]]$DesiredRunningStates = @('Available', 'NeedsAssistance') - [string[]]$TimeDiffHrsMin = "$($TimeDifference):0".Split(':') - - - #region Functions - function Get-LocalDateTime - { - return (Get-Date).ToUniversalTime().AddHours($TimeDiffHrsMin[0]).AddMinutes($TimeDiffHrsMin[1]) - } - - function Write-Log - { - [CmdletBinding()] - param ( - [Parameter(Mandatory = $false)] - [switch]$Err, - - [Parameter(Mandatory = $true)] - [string]$HostPoolName, - - [Parameter(Mandatory = $true)] - [string]$Message, - - [Parameter(Mandatory = $false)] - [switch]$Warn - ) - - [string]$MessageTimeStamp = (Get-LocalDateTime).ToString('yyyy-MM-dd HH:mm:ss') - $Message = "[$($MyInvocation.ScriptLineNumber)] [$($HostPoolName)] $Message" - [string]$WriteMessage = "[$($MessageTimeStamp)] $Message" - - if ($Err) - { - Write-Error $WriteMessage - $Message = "ERROR: $Message" - } - elseif ($Warn) - { - Write-Warning $WriteMessage - $Message = "WARN: $Message" - } - else - { - Write-Output $WriteMessage - } - } - - function Set-nVMsToStartOrStop - { - param ( - [Parameter(Mandatory = $true)] - [string]$HostPoolName, - - [Parameter(Mandatory = $false)] - [switch]$InPeakHours, - - [Parameter(Mandatory = $true)] - [int]$MaxUserSessionsPerVM, - - [Parameter(Mandatory = $true)] - [int]$nRunningCores, - - [Parameter(Mandatory = $true)] - [int]$nRunningVMs, - - [Parameter(Mandatory = $true)] - [int]$nUserSessions, - - [Parameter(Mandatory = $true)] - [hashtable]$Res - ) - - # check if need to adjust min num of running session hosts required if the number of user sessions is close to the max allowed by the min num of running session hosts required - [double]$MaxUserSessionsThreshold = 0.9 - [int]$MaxUserSessionsThresholdCapacity = [math]::Floor($MinimumNumberOfRDSH * $MaxUserSessionsPerVM * $MaxUserSessionsThreshold) - if ($nUserSessions -gt $MaxUserSessionsThresholdCapacity) - { - $MinimumNumberOfRDSH = [math]::Ceiling($nUserSessions / ($MaxUserSessionsPerVM * $MaxUserSessionsThreshold)) - Write-Log -HostPoolName $HostPoolName -Message "Number of user sessions is more than $($MaxUserSessionsThreshold * 100) % of the max number of sessions allowed with minimum number of running session hosts required ($MaxUserSessionsThresholdCapacity). Adjusted minimum number of running session hosts required to $MinimumNumberOfRDSH" - } - - # Check if minimum number of session hosts are running - if ($nRunningVMs -lt $MinimumNumberOfRDSH) - { - $res.nVMsToStart = $MinimumNumberOfRDSH - $nRunningVMs - Write-Log -HostPoolName $HostPoolName -Message "Number of running session host is less than minimum required. Need to start $($res.nVMsToStart) VMs" - } - - if ($InPeakHours) - { - [double]$nUserSessionsPerCore = $nUserSessions / $nRunningCores - # In peak hours: check if current capacity is meeting the user demands - if ($nUserSessionsPerCore -gt $SessionThresholdPerCPU) - { - $res.nCoresToStart = [math]::Ceiling(($nUserSessions / $SessionThresholdPerCPU) - $nRunningCores) - Write-Log -HostPoolName $HostPoolName -Message "[In peak hours] Number of user sessions per Core is more than the threshold. Need to start $($res.nCoresToStart) cores" - } - - return - } - - if ($nRunningVMs -gt $MinimumNumberOfRDSH) - { - # Calculate the number of session hosts to stop - $res.nVMsToStop = $nRunningVMs - $MinimumNumberOfRDSH - Write-Log -HostPoolName $HostPoolName -Message "[Off peak hours] Number of running session host is greater than minimum required. Need to stop $($res.nVMsToStop) VMs" - } - } - - function TryUpdateSessionHostDrainMode - { - [CmdletBinding()] - param ( - [Parameter(Mandatory = $true)] - [bool]$AllowNewSession, - - [Parameter(Mandatory = $true)] - [hashtable]$Header, - - [Parameter(Mandatory = $true)] - [string]$HostPoolName, - - [Parameter(Mandatory = $true)] - [string]$HostPoolResourceGroupName, - - [Parameter(Mandatory = $true)] - [string]$ResourceManagerUrl, - - [Parameter(Mandatory = $true)] - [string]$SessionHostName, - - [Parameter(Mandatory = $true)] - [string]$SubscriptionId - ) - Begin { } - Process - { - Write-Log -HostPoolName $HostPoolName -Message "Update session host '$SessionHostName' to set allow new sessions to $AllowNewSession" - try - { - $Uri = $ResourceManagerUrl + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $HostPoolResourceGroupName + '/providers/Microsoft.DesktopVirtualization/hostPools/' + $HostPoolName + '/sessionHosts/' + $SessionHostName + '?api-version=2022-02-10-preview' - Invoke-RestMethod -Headers $Header -Body (@{properties = @{allowNewSession = $AllowNewSession}} | ConvertTo-Json) -Method 'Patch' -Uri $Uri | Out-Null - } - catch - { - Write-Log -HostPoolName $HostPoolName -Warn -Message "Failed to update the session host '$SessionHostName' to set allow new sessions to $($AllowNewSession): $($PSItem | Format-List -Force | Out-String)" - } - } - End { } - } - - function TryForceLogOffUser - { - [CmdletBinding()] - param ( - [Parameter(Mandatory = $true)] - [hashtable]$Header, - - [Parameter(Mandatory = $true)] - [string]$HostPoolName, - - [Parameter(Mandatory = $true)] - [string]$HostPoolResourceGroupName, - - [Parameter(Mandatory = $true)] - [string]$ResourceManagerUrl, - - [Parameter(Mandatory = $true, ValueFromPipeline = $true)] - $Session, - - [Parameter(Mandatory = $true)] - [string]$SubscriptionId - ) - Begin { } - Process - { - [string[]]$Toks = $Session.Name.Split('/') - [string]$SessionHostName = $Toks[1] - [string]$SessionID = $Toks[-1] - [string]$User = $Session.ActiveDirectoryUserName - - try - { - Write-Log -HostPoolName $HostPoolName -Message "Force log off user: '$User', session ID: $SessionID" - - $Uri = $ResourceManagerUrl + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $HostPoolResourceGroupName + '/providers/Microsoft.DesktopVirtualization/hostPools/' + $HostPoolName + '/sessionHosts/' + $SessionHostName + '/userSessions/' + $SessionID + '?api-version=2022-02-10-preview&force=True' - Invoke-RestMethod -Headers $Header -Method 'Delete' -Uri $Uri | Out-Null - } - catch - { - Write-Log -HostPoolName $HostPoolName -Warn -Message "Failed to force log off user: '$User', session ID: $SessionID $($PSItem | Format-List -Force | Out-String)" - } - } - End { } - } - - function TryResetSessionHostDrainModeAndUserSessions - { - [CmdletBinding()] - param ( - [Parameter(Mandatory = $true)] - [hashtable]$Header, - - [Parameter(Mandatory = $true)] - [string]$HostPoolName, - - [Parameter(Mandatory = $true)] - [string]$HostPoolResourceGroupName, - - [Parameter(Mandatory = $true)] - [string]$ResourceManagerUrl, - - [Parameter(Mandatory = $true)] - [string]$SessionHostName, - - [Parameter(Mandatory = $true)] - [int]$SessionHostSessions, - - [Parameter(Mandatory = $true)] - [string]$SubscriptionId - ) - Begin { } - Process - { - TryUpdateSessionHostDrainMode -AllowNewSession $true -Header $Header -HostPoolName $HostPoolName -HostPoolResourceGroupName $HostPoolResourceGroupName -ResourceManagerUrl $ResourceManagerUrl -SessionHostName $SessionHostName -SubscriptionId $SubscriptionId - - if ($SessionHostSessions -eq 0) - { - return - } - - Write-Log -HostPoolName $HostPoolName -Warn -Message "Session host '$SessionHostName' still has $SessionHostSessions) sessions left behind in broker DB" - - Write-Log -HostPoolName $HostPoolName -Message "Get all user sessions from session host '$SessionHostName'" - try - { - $Uri = $ResourceManagerUrl + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $HostPoolResourceGroupName + '/providers/Microsoft.DesktopVirtualization/hostPools/' + $HostPoolName + '/sessionHosts/' + $SessionHostName + '/userSessions?api-version=2022-02-10-preview' - $UserSessions = Invoke-RestMethod -Headers $Header -Method 'Get' -Uri $Uri - } - catch - { - Write-Log -HostPoolName $HostPoolName -Warn -Message "Failed to retrieve user sessions of session host '$SessionHostName': $($PSItem | Format-List -Force | Out-String)" - return - } - - Write-Log -HostPoolName $HostPoolName -Message "Force log off $($UserSessions.Count) users on session host: '$SessionHostName'" - $UserSessions | TryForceLogOffUser -Header $Header -HostPoolName $HostPoolName -HostPoolResourceGroupName $HostPoolResourceGroupName -ResourceManagerUrl $ResourceManagerUrl -SubscriptionId $SubscriptionId - } - End { } - } - #endregion Functions - - - # Note: https://stackoverflow.com/questions/41674518/powershell-setting-security-protocol-to-tls-1-2 - [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 - - - #region Azure Authentication - $AccessToken = $null - try - { - $TokenAuthURI = $env:IDENTITY_ENDPOINT + '?resource=' + $ResourceManagerUrl + '&api-version=2019-08-01' - $TokenResponse = Invoke-RestMethod -Method Get -Headers @{"X-IDENTITY-HEADER"="$env:IDENTITY_HEADER"} -Uri $TokenAuthURI - $AccessToken = $TokenResponse.access_token - $Header = @{ - 'Content-Type'='application/json' - 'Authorization'='Bearer ' + $AccessToken - } - } - catch - { - throw [System.Exception]::new('Failed to authenticate Azure with application ID, tenant ID, subscription ID', $PSItem.Exception) - } - Write-Log -HostPoolName $HostPoolName -Message "Successfully authenticated with Azure using a managed identity" - #endregion Azure Authentication - - - #region validate host pool, validate / update HostPool load balancer type, ensure there is at least 1 session host, get num of user sessions - # Validate and get host pool info - $HostPool = $null - try - { - Write-Log -HostPoolName $HostPoolName -Message "Get host pool information" - $Uri = $ResourceManagerUrl + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $HostPoolResourceGroupName + '/providers/Microsoft.DesktopVirtualization/hostPools/' + $HostPoolName + '?api-version=2022-02-10-preview' - $HostPool = Invoke-RestMethod -Headers $Header -Method 'Get' -Uri $Uri - - if (!$HostPool) - { - throw $HostPool - } - } - catch - { - throw [System.Exception]::new("Failed to get host pool info of '$HostPoolName' in resource group '$HostPoolResourceGroupName'. Ensure that you have entered the correct values", $PSItem.Exception) - } - - # Ensure HostPool load balancer type is not persistent - if ($HostPool.properties.loadBalancerType -ieq 'Persistent') - { - throw "HostPool '$HostPoolName' is configured with 'Persistent' load balancer type. Scaling tool only supports these load balancer types: BreadthFirst, DepthFirst" - } - - Write-Log -HostPoolName $HostPoolName -Message 'Get session hosts' - $Uri = $ResourceManagerUrl + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $HostPoolResourceGroupName + '/providers/Microsoft.DesktopVirtualization/hostPools/' + $HostPoolName + '/sessionHosts?api-version=2022-02-10-preview' - $SessionHosts = (Invoke-RestMethod -Headers $Header -Method 'Get' -Uri $Uri).value - - if (!$SessionHosts) - { - Write-Log -HostPoolName $HostPoolName -Message "There are no session hosts in the host pool '$HostPoolName'. Ensure that hostpool has session hosts" - Write-Log -HostPoolName $HostPoolName -Message 'End' - return - } - - Write-Log -HostPoolName $HostPoolName -Message 'Get number of user sessions in host pool' - $Uri = $ResourceManagerUrl + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $HostPoolResourceGroupName + '/providers/Microsoft.DesktopVirtualization/hostPools/' + $HostPoolName + '/userSessions?api-version=2022-02-10-preview' - [int]$nUserSessions = (Invoke-RestMethod -Headers $Header -Method 'Get' -Uri $Uri).value.Count - - # Set up breadth 1st load balacing type - # Note: breadth 1st is enforced on AND off peak hours to simplify the things with scaling in the start/end of peak hours - if (!$SkipUpdateLoadBalancerType -and $HostPool.properties.loadBalancerType -ine 'BreadthFirst') - { - Write-Log -HostPoolName $HostPoolName -Message "Update HostPool with 'BreadthFirst' load balancer type (current: '$($HostPool.properties.loadBalancerType)')" - - $Body = @{ - properties = @{ - loadBalancerType = 'BreadthFirst' - } - } - $Uri = $ResourceManagerUrl + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $HostPoolResourceGroupName + '/providers/Microsoft.DesktopVirtualization/hostPools/' + $HostPoolName + '?api-version=2022-02-10-preview' - $HostPool = Invoke-RestMethod -Headers $Header -Body $($Body | ConvertTo-Json) -Method 'Patch' -Uri $Uri - } - Write-Log -HostPoolName $HostPoolName -Message "Number of session hosts in the HostPool: $($SessionHosts.Count)" - #endregion - - - # region Peak Hours # - # Convert local time, begin peak time & end peak time from UTC to local time - $CurrentDateTime = Get-LocalDateTime - $BeginPeakDateTime = [datetime]::Parse($CurrentDateTime.ToShortDateString() + ' ' + $BeginPeakTime) - $EndPeakDateTime = [datetime]::Parse($CurrentDateTime.ToShortDateString() + ' ' + $EndPeakTime) - - # Adjust peak times to make sure begin peak time is always before end peak time - if ($EndPeakDateTime -lt $BeginPeakDateTime) - { - if ($CurrentDateTime -lt $EndPeakDateTime) - { - $BeginPeakDateTime = $BeginPeakDateTime.AddDays(-1) - } - else - { - $EndPeakDateTime = $EndPeakDateTime.AddDays(1) - } - } - - Write-Log -HostPoolName $HostPoolName -Message "Using current time: $($CurrentDateTime.ToString('yyyy-MM-dd HH:mm:ss')), begin peak time: $($BeginPeakDateTime.ToString('yyyy-MM-dd HH:mm:ss')), end peak time: $($EndPeakDateTime.ToString('yyyy-MM-dd HH:mm:ss'))" - - [bool]$InPeakHours = ($BeginPeakDateTime -le $CurrentDateTime -and $CurrentDateTime -le $EndPeakDateTime) - if ($InPeakHours) - { - Write-Log -HostPoolName $HostPoolName -Message 'In peak hours' - } - else - { - Write-Log -HostPoolName $HostPoolName -Message 'Off peak hours' - } - # endregion Peak Hours # - - - #region get session hosts, VMs & user sessions info and compute workload - # Note: session host is considered "running" if its running AND is in desired states AND allowing new sessions - # Number of session hosts that are running, are in desired states and allowing new sessions - [int]$nRunningVMs = 0 - # Number of cores that are running, are in desired states and allowing new sessions - [int]$nRunningCores = 0 - # Array that contains all the virtual machine objects that are session hosts except the ones that are tagged for maintenance - $VMs = @() - # Object that contains the number of cores for each VM size SKU - $VMSizeCores = @{} - # Number of user sessions reported by each session host that is running, is in desired state and allowing new sessions - [int]$nUserSessionsFromAllRunningVMs = 0 - - # Populate all session hosts objects - foreach ($SessionHost in $SessionHosts) - { - [string]$VirtualMachineResourceId = $SessionHost.properties.resourceId - [string]$VirtualMachineName = $VirtualMachineResourceId.Split('/')[8] - [string]$VirtualMachineResourceGroupName = $VirtualMachineResourceId.Split('/')[4] - $Uri = $ResourceManagerUrl + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $VirtualMachineResourceGroupName + '/providers/Microsoft.Compute/virtualMachines/' + $VirtualMachineName + '?api-version=2024-03-01&$expand=instanceView' - $VirtualMachine = Invoke-RestMethod -Headers $Header -Method 'Get' -Uri $Uri - - # Throw an error if the virtual machine for the session host does not exist - if ($VirtualMachine.error) - { - throw "The virtual machine for session host '$VirtualMachineName' does not exist" - } - # Ignore session hosts tagged for maintenance or missing virtual machine - elseif($VirtualMachine.tags.Keys -contains $MaintenanceTagName) - { - Write-Log -HostPoolName $HostPoolName -Message "VM '$VirtualMachineName' is in maintenance and will be ignored" - continue - } - else - { - $VMs += $VirtualMachine - } - - $PowerState = $VirtualMachine.properties.instanceView.statuses[1].displayStatus - Write-Log -HostPoolName $HostPoolName -Message "Session host: '$($VirtualMachineName)', power state: '$PowerState', status: '$($SessionHost.properties.status)', update state: '$($SessionHost.properties.updateState)', sessions: $($SessionHost.properties.sessions), allow new session: $($SessionHost.properties.allowNewSession)" - - # Get the number of cores for VM size SKU - if (!$VMSizeCores.ContainsKey($VirtualMachine.properties.hardwareProfile.vmSize)) - { - Write-Log -HostPoolName $HostPoolName -Message "Get VM sizes in $($VirtualMachine.location)" - - $Uri = $ResourceManagerUrl + 'subscriptions/' + $SubscriptionId + '/providers/Microsoft.Compute/locations/' + $VirtualMachine.location + '/vmSizes?api-version=2024-03-01' - $VMSizes = (Invoke-RestMethod -Headers $Header -Method 'Get' -Uri $Uri).value - - foreach ($VMSize in $VMSizes) - { - if (!$VMSizeCores.ContainsKey($VMSize.name)) - { - $VMSizeCores.Add($VMSize.name, $VMSize.numberOfCores) - } - } - } - - if ($PowerState -ieq 'VM running') - { - if ($SessionHost.properties.status -notin $DesiredRunningStates) - { - Write-Log -HostPoolName $HostPoolName -Warn -Message 'VM is in running state but session host is not and so it will be ignored (this could be because the VM was just started and has not connected to broker yet)' - } - if (!$SessionHost.properties.allowNewSession) - { - Write-Log -HostPoolName $HostPoolName -Warn -Message 'VM is in running state but session host is not allowing new sessions and so it will be ignored' - } - - if ($SessionHost.properties.status -in $DesiredRunningStates -and $SessionHost.properties.allowNewSession) - { - ++$nRunningVMs - $nRunningCores += $VMSizeCores[$VirtualMachine.properties.hardwareProfile.vmSize] - $nUserSessionsFromAllRunningVMs += $SessionHost.properties.sessions - } - } - else - { - if ($SessionHost.properties.status -in $DesiredRunningStates) - { - Write-Log -HostPoolName $HostPoolName -Warn -Message "VM is not in running state but session host is (this could be because the VM was just stopped and broker doesn't know that yet)" - } - } - } - - if ($nUserSessionsFromAllRunningVMs -ne $nUserSessions) - { - Write-Log -HostPoolName $HostPoolName -Warn -Message "Sum of user sessions reported by every running session host ($nUserSessionsFromAllRunningVMs) is not equal to the total number of user sessions reported by the host pool ($nUserSessions)" - } - - - if (!$nRunningCores) - { - $nRunningCores = 1 - } - - Write-Log -HostPoolName $HostPoolName -Message "Number of running session hosts: $nRunningVMs of total $($VMs.Count)" - Write-Log -HostPoolName $HostPoolName -Message "Number of user sessions: $nUserSessions of total allowed $($nRunningVMs * $HostPool.properties.maxSessionLimit)" - Write-Log -HostPoolName $HostPoolName -Message "Number of user sessions per Core: $($nUserSessions / $nRunningCores), threshold: $SessionThresholdPerCPU" - Write-Log -HostPoolName $HostPoolName -Message "Minimum number of running session hosts required: $MinimumNumberOfRDSH" - - # Check if minimum num of running session hosts required is higher than max allowed - if ($VMs.Count -le $MinimumNumberOfRDSH) - { - Write-Log -HostPoolName $HostPoolName -Warn -Message 'Minimum number of RDSH is set higher than or equal to total number of session hosts' - } - #endregion - - - #region determine number of session hosts to start/stop if any - # Now that we have all the info about the session hosts & their usage, figure how many session hosts to start/stop depending on in/off peak hours and the demand [Ops = operations to perform] - $Ops = @{ - nVMsToStart = 0 - nCoresToStart = 0 - nVMsToStop = 0 - } - - Set-nVMsToStartOrStop -HostPoolName $HostPoolName -nRunningVMs $nRunningVMs -nRunningCores $nRunningCores -nUserSessions $nUserSessions -MaxUserSessionsPerVM $HostPool.properties.maxSessionLimit -InPeakHours:$InPeakHours -Res $Ops - #endregion - - - #region start any session hosts if need to - # Check if we have any session hosts to start - if ($Ops.nVMsToStart -or $Ops.nCoresToStart) - { - if ($nRunningVMs -eq $VMs.Count) - { - Write-Log -HostPoolName $HostPoolName -Message 'All session hosts are running' - Write-Log -HostPoolName $HostPoolName -Message 'End' - return - } - - # Object that contains names of session hosts that will be started - # $StartSessionHostFullNames = @{ } - # Array that contains jobs of starting the session hosts - [array]$StartedVMs = @() - - Write-Log -HostPoolName $HostPoolName -Message 'Find session hosts that are stopped and allowing new sessions' - foreach ($SessionHost in $SessionHosts) - { - $VM = $VMs | Where-Object { $_.id -ieq $SessionHost.properties.resourceId } - if (!$Ops.nVMsToStart -and !$Ops.nCoresToStart) - { - # Done with starting session hosts that needed to be - break - } - if ($VM.properties.instanceView.statuses[1].displayStatus -ieq 'VM running') - { - continue - } - if ($SessionHost.properties.updateState -ine 'Succeeded') - { - Write-Log -HostPoolName $HostPoolName -Warn -Message "Session host '$($VM.name)' may not be healthy" - } - - if (!$SessionHost.properties.allowNewSession) - { - Write-Log -HostPoolName $HostPoolName -Warn -Message "Session host '$($VM.name)' is not allowing new sessions and so it will not be started" - continue - } - - Write-Log -HostPoolName $HostPoolName -Message "Start session host '$($VM.name)'" - - $Uri = $ResourceManagerUrl + $VM.id.TrimStart('/') + '/start?api-version=2023-09-01' - Invoke-RestMethod -Headers $Header -Method 'Post' -Uri $Uri | Out-Null - $StartedVMs += $VM - - --$Ops.nVMsToStart - if ($Ops.nVMsToStart -lt 0) - { - $Ops.nVMsToStart = 0 - } - - $Ops.nCoresToStart -= $VMSizeCores[$VM.properties.hardwareProfile.vmSize] - if ($Ops.nCoresToStart -lt 0) - { - $Ops.nCoresToStart = 0 - } - } - - # Check if there were enough number of session hosts to start - if ($Ops.nVMsToStart -or $Ops.nCoresToStart) - { - Write-Log -HostPoolName $HostPoolName -Warn -Message "Not enough session hosts to start. Still need to start maximum of either $($Ops.nVMsToStart) VMs or $($Ops.nCoresToStart) cores" - } - - # Wait for session hosts to start - while($StartedVMs.Count -gt 0) - { - foreach($StartedVM in $StartedVMs) - { - $Uri = $ResourceManagerUrl + $StartedVM.id.TrimStart('/') + '?api-version=2024-03-01' - $VMAgentStatus = (Invoke-RestMethod -Headers $Header -Method 'Get' -Uri $Uri).properties.instanceView.vmAgent - if ($VMAgentStatus) - { - Write-Log -HostPoolName $HostPoolName -Message "Session host '$($StartedVM.name)' is running" - $StartedVMs = $StartedVMs -ne $StartedVM - } - } - Start-Sleep -Seconds 30 - } - - Write-Log -HostPoolName $HostPoolName -Message 'All session hosts have started' - Write-Log -HostPoolName $HostPoolName -Message 'End' - return - } - #endregion - - - #region stop any session hosts if need to - if (!$Ops.nVMsToStop) - { - Write-Log -HostPoolName $HostPoolName -Message 'No need to start/stop any session hosts' - Write-Log -HostPoolName $HostPoolName -Message 'End' - return - } - - # Object that contains names of session hosts that will be stopped - $VMsToStop = @() - [array]$VMsToStopAfterLogOffTimeOut = @() - - Write-Log -HostPoolName $HostPoolName -Message 'Find session hosts that are running and allowing new sessions, sort them by number of user sessions' - foreach ($SessionHost in ($SessionHosts | Where-Object { $_.properties.allowNewSession } | Sort-Object { $_.properties.sessions })) - { - $VM = $VMs | Where-Object { $_.id -ieq $SessionHost.properties.resourceId } - if ($VM.properties.instanceView.statuses[1].displayStatus -ieq 'VM running') - { - if (!$Ops.nVMsToStop) - { - # Done with stopping session hosts that needed to be - break - } - - if ($SessionHost.properties.sessions -gt 0 -and !$LimitSecondsToForceLogOffUser) - { - Write-Log -HostPoolName $HostPoolName -Warn -Message "Session host '$($VM.name)' has $($SessionHost.properties.sessions) sessions but limit seconds to force log off user is set to 0, so will not stop any more session hosts (https://aka.ms/wvdscale#how-the-scaling-tool-works)" - # Note: why break ? Because the list this loop iterates through is sorted by number of sessions, if it hits this, the rest of items in the loop will also hit this - break - } - - TryUpdateSessionHostDrainMode -AllowNewSession $false -Header $Header -HostPoolName $HostPoolName -HostPoolResourceGroupName $HostPoolResourceGroupName -ResourceManagerUrl $ResourceManagerUrl -SessionHostName $VM.name -SubscriptionId $SubscriptionId - - # Note: check if there were new user sessions since session host info was 1st fetched - if ($SessionHost.properties.sessions -gt 0 -and !$LimitSecondsToForceLogOffUser) - { - Write-Log -HostPoolName $HostPoolName -Warn -Message "Session host '$($VM.name)' has $($SessionHost.properties.sessions) sessions but limit seconds to force log off user is set to 0, so will not stop any more session hosts (https://aka.ms/wvdscale#how-the-scaling-tool-works)" - TryUpdateSessionHostDrainMode -AllowNewSession $true -Header $Header -HostPoolName $HostPoolName -HostPoolResourceGroupName $HostPoolResourceGroupName -ResourceManagerUrl $ResourceManagerUrl -SessionHostName $VM.name -SubscriptionId $SubscriptionId - continue - } - - if ($SessionHost.properties.sessions -gt 0) - { - Write-Log -HostPoolName $HostPoolName -Message "Get all user sessions from session host '$($VM.name)'" - try - { - $Uri = $ResourceManagerUrl + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $HostPoolResourceGroupName + '/providers/Microsoft.DesktopVirtualization/hostPools/' + $HostPoolName + '/sessionHosts/' + $VM.name + '/userSessions?api-version=2022-02-10-preview' - $UserSessions = Invoke-RestMethod -Headers $Header -Method 'Get' -Uri $Uri - } - catch - { - Write-Log -HostPoolName $HostPoolName -Warn -Message "Failed to retrieve user sessions of session host '$($VM.name)': $($PSItem | Format-List -Force | Out-String)" - } - - Write-Log -HostPoolName $HostPoolName -Message "Send log off message to active user sessions on session host: '$($VM.name)'" - foreach ($UserSession in $UserSessions) - { - if($UserSession.properties.sessionState -ine 'Active') - { - continue - } - - [string]$SessionID = $UserSession.name.Split('/')[-1] - [string]$User = $UserSession.properties.activeDirectoryUserName - - try - { - Write-Log -HostPoolName $HostPoolName -Message "Send a log off message to user: '$User', session ID: $SessionID" - - $Uri = $ResourceManagerUrl + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $HostPoolResourceGroupName + '/providers/Microsoft.DesktopVirtualization/hostPools/' + $HostPoolName + '/sessionHosts/' + $VM.name + '/userSessions/' + $SessionID + '/sendMessage?api-version=2022-02-10-preview' - Invoke-RestMethod -Headers $Header -Method 'Post' -Uri $Uri -Body (@{ 'messageTitle' = $LogOffMessageTitle; 'messageBody' = "$LogOffMessageBody You will be logged off in $LimitSecondsToForceLogOffUser seconds" } | ConvertTo-Json) | Out-Null - } - catch - { - Write-Log -HostPoolName $HostPoolName -Warn -Message "Failed to send a log off message to user: '$User', session ID: $SessionID $($PSItem | Format-List -Force | Out-String)" - } - } - $VMsToStopAfterLogOffTimeOut += $VM - } - else - { - Write-Log -HostPoolName $HostPoolName -Message "Stop session host '$($VM.name)'" - $Uri = $ResourceManagerUrl + $VM.id.TrimStart('/') + '/deallocate?api-version=2023-09-01' - Invoke-RestMethod -Headers $Header -Method 'Post' -Uri $Uri | Out-Null - $VMsToStop += $VM - } - - --$Ops.nVMsToStop - if ($Ops.nVMsToStop -lt 0) { - $Ops.nVMsToStop = 0 - } - } - } - - if ($VMsToStopAfterLogOffTimeOut) - { - Write-Log -HostPoolName $HostPoolName -Message "Wait $LimitSecondsToForceLogOffUser seconds for users to log off" - - Start-Sleep -Seconds $LimitSecondsToForceLogOffUser - - Write-Log -HostPoolName $HostPoolName -Message "Force log off users and stop remaining $($VMsToStopAfterLogOffTimeOut.Count) session hosts" - foreach ($VM in $VMsToStopAfterLogOffTimeOut) - { - $SessionHost = $SessionHosts | Where-Object { $_.properties.resourceId -ieq $VM.id } - Write-Log -HostPoolName $HostPoolName -Message "Force log off $($SessionHost.sessions) users on session host: '$($VM.name)'" - $VM.UserSessions | TryForceLogOffUser -Header $Header -HostPoolName $HostPoolName -HostPoolResourceGroupName $HostPoolResourceGroupName -ResourceManagerUrl $ResourceManagerUrl -SubscriptionId $SubscriptionId - - Write-Log -HostPoolName $HostPoolName -Message "Stop session host '$($VM.name)'" - $Uri = $ResourceManagerUrl + $VM.id.TrimStart('/') + '/deallocate?api-version=2023-09-01' - Invoke-RestMethod -Headers $Header -Method 'Post' -Uri $Uri | Out-Null - $VMsToStop += $VM - } - } - - # Check if there were enough number of session hosts to stop - if ($Ops.nVMsToStop) - { - Write-Log -HostPoolName $HostPoolName -Warn -Message "Not enough session hosts to stop. Still need to stop $($Ops.nVMsToStop) VMs" - } - - # Wait for the session hosts to stop / deallocate - Write-Log -HostPoolName $HostPoolName -Message "Wait for session hosts to stop / deallocate" - while($VMsToStop.Count -gt 0) - { - foreach($VMToStop in $VMsToStop) - { - $Uri = $ResourceManagerUrl + $VMToStop.id.TrimStart('/') + '?$expand=instanceView&api-version=2024-03-01' - $VMPowerState = (Invoke-RestMethod -Headers $Header -Method 'Get' -Uri $Uri).properties.instanceView.statuses[1].displayStatus - if ($VMPowerState -eq 'VM deallocated') - { - Write-Log -HostPoolName $HostPoolName -Message "Session host '$($VMToStop.name)' is stopping" - $SessionHost = $SessionHosts | Where-Object { $_.properties.resourceId -ieq $VMToStop.id } - TryResetSessionHostDrainModeAndUserSessions -Header $Header -HostPoolName $HostPoolName -HostPoolResourceGroupName $HostPoolResourceGroupName -ResourceManagerUrl $ResourceManagerUrl -SessionHostName $VMToStop.name -SessionHostSessions $SessionHost.properties.sessions -SubscriptionId $SubscriptionId - $VMsToStop = $VMsToStop -ne $VMToStop - } - } - Start-Sleep -Seconds 30 - } - - Write-Log -HostPoolName $HostPoolName -Message 'All required session hosts have stopped.' - Write-Log -HostPoolName $HostPoolName -Message 'End' - return - #endregion -} -catch -{ - $ErrContainer = $PSItem - # $ErrContainer = $_ - - [string]$ErrMsg = $ErrContainer | Format-List -Force | Out-String - $ErrMsg += "Version: $Version`n" - - if (Get-Command 'Write-Log' -ErrorAction:SilentlyContinue) - { - Write-Log -HostPoolName $HostPoolName -Err -Message $ErrMsg -ErrorAction:Continue - } - else - { - Write-Error $ErrMsg -ErrorAction:Continue - } - - throw [System.Exception]::new($ErrMsg, $ErrContainer.Exception) -} \ No newline at end of file diff --git a/src/bicep/add-ons/azure-virtual-desktop/docs/features.md b/src/bicep/add-ons/azure-virtual-desktop/docs/features.md index e8e997b56..7c9b52175 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/docs/features.md +++ b/src/bicep/add-ons/azure-virtual-desktop/docs/features.md @@ -5,13 +5,13 @@ ## Features - [**Auto Increase Premium File Share Quota**](./features/autoIncreasePremiumFileShareQuota.md#auto-increase-premium-file-share-quota) +- [**Autoscale**](./features/autoscale.md#autoscale) - [**Backups**](./features/backups.md#backups) - [**Drain Mode**](./features/drainMode.md#drain-mode) - [**FSLogix**](./features/fslogix.md#fslogix) - [**GPU Drivers & Settings**](./features/gpu.md#gpu-drivers--settings) - [**High Availability**](./features/highAvailability.md#high-availability) - [**Monitoring**](./features/monitoring.md#monitoring) -- [**Scaling Tool**](./features/scalingTool.md#scaling-tool) - [**Server-Side Encryption with Customer Managed Keys**](./features/serverSideEncryption.md#server-side-encryption) - [**SMB Multichannel**](./features/smbMultiChannel.md#smb-multichannel) - [**Start VM On Connect**](./features/startVmOnConnect.md#start-vm-on-connect) diff --git a/src/bicep/add-ons/azure-virtual-desktop/docs/features/autoIncreasePremiumFileShareQuota.md b/src/bicep/add-ons/azure-virtual-desktop/docs/features/autoIncreasePremiumFileShareQuota.md index 11ef532bc..619931fcd 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/docs/features/autoIncreasePremiumFileShareQuota.md +++ b/src/bicep/add-ons/azure-virtual-desktop/docs/features/autoIncreasePremiumFileShareQuota.md @@ -5,13 +5,13 @@ ## Features - [**Auto Increase Premium File Share Quota**](./autoIncreasePremiumFileShareQuota.md#auto-increase-premium-file-share-quota) +- [**Autoscale**](./autoscale.md#autoscale) - [**Backups**](./backups.md#backups) - [**Drain Mode**](./drainMode.md#drain-mode) - [**FSLogix**](./fslogix.md#fslogix) - [**GPU Drivers & Settings**](./gpu.md#gpu-drivers--settings) - [**High Availability**](./highAvailability.md#high-availability) - [**Monitoring**](./monitoring.md#monitoring) -- [**Scaling Tool**](./scalingTool.md#scaling-tool) - [**Server-Side Encryption with Customer Managed Keys**](./serverSideEncryption.md#server-side-encryption) - [**SMB Multichannel**](./smbMultiChannel.md#smb-multichannel) - [**Start VM On Connect**](./startVmOnConnect.md#start-vm-on-connect) diff --git a/src/bicep/add-ons/azure-virtual-desktop/docs/features/scalingTool.md b/src/bicep/add-ons/azure-virtual-desktop/docs/features/autoscale.md similarity index 58% rename from src/bicep/add-ons/azure-virtual-desktop/docs/features/scalingTool.md rename to src/bicep/add-ons/azure-virtual-desktop/docs/features/autoscale.md index 1c1b5b3b2..accccdc53 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/docs/features/scalingTool.md +++ b/src/bicep/add-ons/azure-virtual-desktop/docs/features/autoscale.md @@ -5,31 +5,26 @@ ## Features - [**Auto Increase Premium File Share Quota**](./autoIncreasePremiumFileShareQuota.md#auto-increase-premium-file-share-quota) +- [**Autoscale**](./autoscale.md#autoscale) - [**Backups**](./backups.md#backups) - [**Drain Mode**](./drainMode.md#drain-mode) - [**FSLogix**](./fslogix.md#fslogix) - [**GPU Drivers & Settings**](./gpu.md#gpu-drivers--settings) - [**High Availability**](./highAvailability.md#high-availability) - [**Monitoring**](./monitoring.md#monitoring) -- [**Scaling Tool**](./scalingTool.md#scaling-tool) - [**Server-Side Encryption with Customer Managed Keys**](./serverSideEncryption.md#server-side-encryption) - [**SMB Multichannel**](./smbMultiChannel.md#smb-multichannel) - [**Start VM On Connect**](./startVmOnConnect.md#start-vm-on-connect) - [**Trusted Launch**](./trustedLaunch.md#trusted-launch) - [**Validation**](./validation.md#validation) -### Scaling Tool +### Autoscale -This feature is deployed if selected to help save on cost. Based on the desired configuration, session hosts will scale up during peak hours and shutdown after peak hours. It is recommended to use policies to manage idle and disconnected over using the built-in capability in this tool. In this solution, a managed identity is deployed on the Automation Account to reduce the privileges needed for tool. +This feature is deployed to help reduce the cost of your pooled or personal host pools by turning off session hosts during your off peak hours. It is recommended to use policies to manage idle and disconnected sessions, over using the built-in capability in this tool. Many of the settings have been preconfigured. Adjust the scaling plan resource post deployment to fine the settings. -**Reference:** [Scaling Tool - Microsoft Docs](https://docs.microsoft.com/en-us/azure/virtual-desktop/scaling-automation-logic-apps) +**Reference:** [Autoscale - Microsoft Docs](https://learn.microsoft.com/azure/virtual-desktop/autoscale-scenarios) **Deployed Resources:** -- Automation Account - - Diagnostic Setting (optional) - - Job Schedules - - Runbook - - Schedules - - System Assigned Identity +- Scaling Plan - Role Assignment diff --git a/src/bicep/add-ons/azure-virtual-desktop/docs/features/backups.md b/src/bicep/add-ons/azure-virtual-desktop/docs/features/backups.md index db7d05855..ce2e1eae2 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/docs/features/backups.md +++ b/src/bicep/add-ons/azure-virtual-desktop/docs/features/backups.md @@ -5,13 +5,13 @@ ## Features - [**Auto Increase Premium File Share Quota**](./autoIncreasePremiumFileShareQuota.md#auto-increase-premium-file-share-quota) +- [**Autoscale**](./autoscale.md#autoscale) - [**Backups**](./backups.md#backups) - [**Drain Mode**](./drainMode.md#drain-mode) - [**FSLogix**](./fslogix.md#fslogix) - [**GPU Drivers & Settings**](./gpu.md#gpu-drivers--settings) - [**High Availability**](./highAvailability.md#high-availability) - [**Monitoring**](./monitoring.md#monitoring) -- [**Scaling Tool**](./scalingTool.md#scaling-tool) - [**Server-Side Encryption with Customer Managed Keys**](./serverSideEncryption.md#server-side-encryption) - [**SMB Multichannel**](./smbMultiChannel.md#smb-multichannel) - [**Start VM On Connect**](./startVmOnConnect.md#start-vm-on-connect) @@ -22,7 +22,7 @@ This optional feature enables backups to protect user profile data. When selected, if the host pool is "pooled" and the storage solution is Azure Files, the solution will protect the file share. If the host pool is "personal", the solution will protect the virtual machines. -**Reference:** [Azure Backup - Microsoft Docs](https://docs.microsoft.com/en-us/azure/backup/backup-overview) +**Reference:** [Azure Backup - Microsoft Docs](https://learn.microsoft.com/azure/backup/backup-overview) **Deployed Resources:** diff --git a/src/bicep/add-ons/azure-virtual-desktop/docs/features/drainMode.md b/src/bicep/add-ons/azure-virtual-desktop/docs/features/drainMode.md index dcce7240a..501457147 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/docs/features/drainMode.md +++ b/src/bicep/add-ons/azure-virtual-desktop/docs/features/drainMode.md @@ -5,13 +5,13 @@ ## Features - [**Auto Increase Premium File Share Quota**](./autoIncreasePremiumFileShareQuota.md#auto-increase-premium-file-share-quota) +- [**Autoscale**](./autoscale.md#autoscale) - [**Backups**](./backups.md#backups) - [**Drain Mode**](./drainMode.md#drain-mode) - [**FSLogix**](./fslogix.md#fslogix) - [**GPU Drivers & Settings**](./gpu.md#gpu-drivers--settings) - [**High Availability**](./highAvailability.md#high-availability) - [**Monitoring**](./monitoring.md#monitoring) -- [**Scaling Tool**](./scalingTool.md#scaling-tool) - [**Server-Side Encryption with Customer Managed Keys**](./serverSideEncryption.md#server-side-encryption) - [**SMB Multichannel**](./smbMultiChannel.md#smb-multichannel) - [**Start VM On Connect**](./startVmOnConnect.md#start-vm-on-connect) @@ -22,7 +22,7 @@ When this optional feature is deployed, the sessions hosts will be put in drain mode to ensure the end users cannot access them until they have been validated. -**Reference:** [Drain Mode - Microsoft Docs](https://docs.microsoft.com/en-us/azure/virtual-desktop/drain-mode) +**Reference:** [Drain Mode - Microsoft Docs](https://learn.microsoft.com/azure/virtual-desktop/drain-mode) **Deployed Resources:** diff --git a/src/bicep/add-ons/azure-virtual-desktop/docs/features/fslogix.md b/src/bicep/add-ons/azure-virtual-desktop/docs/features/fslogix.md index cd49b4a07..90c2d0deb 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/docs/features/fslogix.md +++ b/src/bicep/add-ons/azure-virtual-desktop/docs/features/fslogix.md @@ -5,13 +5,13 @@ ## Features - [**Auto Increase Premium File Share Quota**](./autoIncreasePremiumFileShareQuota.md#auto-increase-premium-file-share-quota) +- [**Autoscale**](./autoscale.md#autoscale) - [**Backups**](./backups.md#backups) - [**Drain Mode**](./drainMode.md#drain-mode) - [**FSLogix**](./fslogix.md#fslogix) - [**GPU Drivers & Settings**](./gpu.md#gpu-drivers--settings) - [**High Availability**](./highAvailability.md#high-availability) - [**Monitoring**](./monitoring.md#monitoring) -- [**Scaling Tool**](./scalingTool.md#scaling-tool) - [**Server-Side Encryption with Customer Managed Keys**](./serverSideEncryption.md#server-side-encryption) - [**SMB Multichannel**](./smbMultiChannel.md#smb-multichannel) - [**Start VM On Connect**](./startVmOnConnect.md#start-vm-on-connect) @@ -29,7 +29,7 @@ Azure Files and Azure NetApp Files are the only two SMB storage services availab - Profile Container (Recommended) - Profile & Office Container -**Reference:** [FSLogix - Microsoft Docs](https://docs.microsoft.com/en-us/fslogix/overview) +**Reference:** [FSLogix - Microsoft Docs](https://learn.microsoft.com/fslogix/overview) **Deployed Resources:** diff --git a/src/bicep/add-ons/azure-virtual-desktop/docs/features/gpu.md b/src/bicep/add-ons/azure-virtual-desktop/docs/features/gpu.md index 39ee0e90f..e7742e699 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/docs/features/gpu.md +++ b/src/bicep/add-ons/azure-virtual-desktop/docs/features/gpu.md @@ -5,13 +5,13 @@ ## Features - [**Auto Increase Premium File Share Quota**](./autoIncreasePremiumFileShareQuota.md#auto-increase-premium-file-share-quota) +- [**Autoscale**](./autoscale.md#autoscale) - [**Backups**](./backups.md#backups) - [**Drain Mode**](./drainMode.md#drain-mode) - [**FSLogix**](./fslogix.md#fslogix) - [**GPU Drivers & Settings**](./gpu.md#gpu-drivers--settings) - [**High Availability**](./highAvailability.md#high-availability) - [**Monitoring**](./monitoring.md#monitoring) -- [**Scaling Tool**](./scalingTool.md#scaling-tool) - [**Server-Side Encryption with Customer Managed Keys**](./serverSideEncryption.md#server-side-encryption) - [**SMB Multichannel**](./smbMultiChannel.md#smb-multichannel) - [**Start VM On Connect**](./startVmOnConnect.md#start-vm-on-connect) @@ -22,7 +22,7 @@ When an appropriate VM size (Nv, Nvv3, Nvv4, or NCasT4_v3 series) is selected, this solution will automatically deploy the appropriate virtual machine extension to install the graphics driver and configure the recommended registry settings. -**Reference:** [Configure GPU Acceleration - Microsoft Docs](https://docs.microsoft.com/en-us/azure/virtual-desktop/configure-vm-gpu) +**Reference:** [Configure GPU Acceleration - Microsoft Docs](https://learn.microsoft.com/azure/virtual-desktop/configure-vm-gpu) **Deployed Resources:** diff --git a/src/bicep/add-ons/azure-virtual-desktop/docs/features/highAvailability.md b/src/bicep/add-ons/azure-virtual-desktop/docs/features/highAvailability.md index a359b7380..f1ac3779c 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/docs/features/highAvailability.md +++ b/src/bicep/add-ons/azure-virtual-desktop/docs/features/highAvailability.md @@ -5,13 +5,13 @@ ## Features - [**Auto Increase Premium File Share Quota**](./autoIncreasePremiumFileShareQuota.md#auto-increase-premium-file-share-quota) +- [**Autoscale**](./autoscale.md#autoscale) - [**Backups**](./backups.md#backups) - [**Drain Mode**](./drainMode.md#drain-mode) - [**FSLogix**](./fslogix.md#fslogix) - [**GPU Drivers & Settings**](./gpu.md#gpu-drivers--settings) - [**High Availability**](./highAvailability.md#high-availability) - [**Monitoring**](./monitoring.md#monitoring) -- [**Scaling Tool**](./scalingTool.md#scaling-tool) - [**Server-Side Encryption with Customer Managed Keys**](./serverSideEncryption.md#server-side-encryption) - [**SMB Multichannel**](./smbMultiChannel.md#smb-multichannel) - [**Start VM On Connect**](./startVmOnConnect.md#start-vm-on-connect) @@ -22,7 +22,7 @@ This optional feature will deploy the selected availability option and only provides high availability for "pooled" host pools since it is a load balanced solution. Virtual machines can be deployed in either Availability Zones or Availability Sets, to provide a higher SLA for your solution. SLA: 99.99% for Availability Zones, 99.95% for Availability Sets. -**Reference:** [Availability options for Azure Virtual Machines - Microsoft Docs](https://docs.microsoft.com/en-us/azure/virtual-machines/availability) +**Reference:** [Availability options for Azure Virtual Machines - Microsoft Docs](https://learn.microsoft.com/azure/virtual-machines/availability) **Deployed Resources:** diff --git a/src/bicep/add-ons/azure-virtual-desktop/docs/features/monitoring.md b/src/bicep/add-ons/azure-virtual-desktop/docs/features/monitoring.md index 3edd151df..a93a988c6 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/docs/features/monitoring.md +++ b/src/bicep/add-ons/azure-virtual-desktop/docs/features/monitoring.md @@ -5,13 +5,13 @@ ## Features - [**Auto Increase Premium File Share Quota**](./autoIncreasePremiumFileShareQuota.md#auto-increase-premium-file-share-quota) +- [**Autoscale**](./autoscale.md#autoscale) - [**Backups**](./backups.md#backups) - [**Drain Mode**](./drainMode.md#drain-mode) - [**FSLogix**](./fslogix.md#fslogix) - [**GPU Drivers & Settings**](./gpu.md#gpu-drivers--settings) - [**High Availability**](./highAvailability.md#high-availability) - [**Monitoring**](./monitoring.md#monitoring) -- [**Scaling Tool**](./scalingTool.md#scaling-tool) - [**Server-Side Encryption with Customer Managed Keys**](./serverSideEncryption.md#server-side-encryption) - [**SMB Multichannel**](./smbMultiChannel.md#smb-multichannel) - [**Start VM On Connect**](./startVmOnConnect.md#start-vm-on-connect) @@ -22,7 +22,7 @@ This feature deploys the required resources to enable the Insights workbook in the Azure Virtual Desktop blade in the Azure Portal. -**Reference:** [Azure Monitor for AVD - Microsoft Docs](https://docs.microsoft.com/en-us/azure/virtual-desktop/azure-monitor) +**Reference:** [Azure Monitor for AVD - Microsoft Docs](https://learn.microsoft.com/azure/virtual-desktop/azure-monitor) **Deployed Resources:** diff --git a/src/bicep/add-ons/azure-virtual-desktop/docs/features/serverSideEncryption.md b/src/bicep/add-ons/azure-virtual-desktop/docs/features/serverSideEncryption.md index 6bc21e374..31e7ac125 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/docs/features/serverSideEncryption.md +++ b/src/bicep/add-ons/azure-virtual-desktop/docs/features/serverSideEncryption.md @@ -5,13 +5,13 @@ ## Features - [**Auto Increase Premium File Share Quota**](./autoIncreasePremiumFileShareQuota.md#auto-increase-premium-file-share-quota) +- [**Autoscale**](./autoscale.md#autoscale) - [**Backups**](./backups.md#backups) - [**Drain Mode**](./drainMode.md#drain-mode) - [**FSLogix**](./fslogix.md#fslogix) - [**GPU Drivers & Settings**](./gpu.md#gpu-drivers--settings) - [**High Availability**](./highAvailability.md#high-availability) - [**Monitoring**](./monitoring.md#monitoring) -- [**Scaling Tool**](./scalingTool.md#scaling-tool) - [**Server-Side Encryption with Customer Managed Keys**](./serverSideEncryption.md#server-side-encryption) - [**SMB Multichannel**](./smbMultiChannel.md#smb-multichannel) - [**Start VM On Connect**](./startVmOnConnect.md#start-vm-on-connect) diff --git a/src/bicep/add-ons/azure-virtual-desktop/docs/features/smbMultiChannel.md b/src/bicep/add-ons/azure-virtual-desktop/docs/features/smbMultiChannel.md index 91a5e0db4..d82d8df0b 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/docs/features/smbMultiChannel.md +++ b/src/bicep/add-ons/azure-virtual-desktop/docs/features/smbMultiChannel.md @@ -5,13 +5,13 @@ ## Features - [**Auto Increase Premium File Share Quota**](./autoIncreasePremiumFileShareQuota.md#auto-increase-premium-file-share-quota) +- [**Autoscale**](./autoscale.md#autoscale) - [**Backups**](./backups.md#backups) - [**Drain Mode**](./drainMode.md#drain-mode) - [**FSLogix**](./fslogix.md#fslogix) - [**GPU Drivers & Settings**](./gpu.md#gpu-drivers--settings) - [**High Availability**](./highAvailability.md#high-availability) - [**Monitoring**](./monitoring.md#monitoring) -- [**Scaling Tool**](./scalingTool.md#scaling-tool) - [**Server-Side Encryption with Customer Managed Keys**](./serverSideEncryption.md#server-side-encryption) - [**SMB Multichannel**](./smbMultiChannel.md#smb-multichannel) - [**Start VM On Connect**](./startVmOnConnect.md#start-vm-on-connect) @@ -22,4 +22,4 @@ This feature is automatically enabled when Azure Files Premium is selected for FSLogix storage. This feature is only supported with Azure Files Premium and it allows multiple connections to an SMB share from an SMB client. -**Reference:** [SMB Multichannel Performance - Microsoft Docs](https://docs.microsoft.com/en-us/azure/storage/files/storage-files-smb-multichannel-performance) +**Reference:** [SMB Multichannel Performance - Microsoft Docs](https://learn.microsoft.com/azure/storage/files/storage-files-smb-multichannel-performance) diff --git a/src/bicep/add-ons/azure-virtual-desktop/docs/features/startVmOnConnect.md b/src/bicep/add-ons/azure-virtual-desktop/docs/features/startVmOnConnect.md index cefb92fd5..4fef2021e 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/docs/features/startVmOnConnect.md +++ b/src/bicep/add-ons/azure-virtual-desktop/docs/features/startVmOnConnect.md @@ -5,13 +5,13 @@ ## Features - [**Auto Increase Premium File Share Quota**](./autoIncreasePremiumFileShareQuota.md#auto-increase-premium-file-share-quota) +- [**Autoscale**](./autoscale.md#autoscale) - [**Backups**](./backups.md#backups) - [**Drain Mode**](./drainMode.md#drain-mode) - [**FSLogix**](./fslogix.md#fslogix) - [**GPU Drivers & Settings**](./gpu.md#gpu-drivers--settings) - [**High Availability**](./highAvailability.md#high-availability) - [**Monitoring**](./monitoring.md#monitoring) -- [**Scaling Tool**](./scalingTool.md#scaling-tool) - [**Server-Side Encryption with Customer Managed Keys**](./serverSideEncryption.md#server-side-encryption) - [**SMB Multichannel**](./smbMultiChannel.md#smb-multichannel) - [**Start VM On Connect**](./startVmOnConnect.md#start-vm-on-connect) @@ -22,7 +22,7 @@ This optional feature allows your end users to turn on a session host when all the session hosts have been stopped / deallocated. This is done automatically when the end user opens the AVD client and attempts to access a resource. Start VM On Connect compliments scaling solutions by ensuring the session hosts can be turned off to reduce cost but made available when needed. -**Reference:** [Start VM On Connect - Microsoft Docs](https://docs.microsoft.com/en-us/azure/virtual-desktop/start-virtual-machine-connect?tabs=azure-portal) +**Reference:** [Start VM On Connect - Microsoft Docs](https://learn.microsoft.com/azure/virtual-desktop/start-virtual-machine-connect?tabs=azure-portal) **Deployed Resources:** diff --git a/src/bicep/add-ons/azure-virtual-desktop/docs/features/trustedLaunch.md b/src/bicep/add-ons/azure-virtual-desktop/docs/features/trustedLaunch.md index 3c6ff14ec..890efeade 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/docs/features/trustedLaunch.md +++ b/src/bicep/add-ons/azure-virtual-desktop/docs/features/trustedLaunch.md @@ -5,13 +5,13 @@ ## Features - [**Auto Increase Premium File Share Quota**](./autoIncreasePremiumFileShareQuota.md#auto-increase-premium-file-share-quota) +- [**Autoscale**](./autoscale.md#autoscale) - [**Backups**](./backups.md#backups) - [**Drain Mode**](./drainMode.md#drain-mode) - [**FSLogix**](./fslogix.md#fslogix) - [**GPU Drivers & Settings**](./gpu.md#gpu-drivers--settings) - [**High Availability**](./highAvailability.md#high-availability) - [**Monitoring**](./monitoring.md#monitoring) -- [**Scaling Tool**](./scalingTool.md#scaling-tool) - [**Server-Side Encryption with Customer Managed Keys**](./serverSideEncryption.md#server-side-encryption) - [**SMB Multichannel**](./smbMultiChannel.md#smb-multichannel) - [**Start VM On Connect**](./startVmOnConnect.md#start-vm-on-connect) @@ -31,7 +31,7 @@ It is a security best practice to enable this feature to protect your virtual ma - rootkits - kernel-level malware -**Reference:** [Trusted Launch - Microsoft Docs](https://docs.microsoft.com/en-us/azure/virtual-machines/trusted-launch) +**Reference:** [Trusted Launch - Microsoft Docs](https://learn.microsoft.com/azure/virtual-machines/trusted-launch) **Deployed Resources:** diff --git a/src/bicep/add-ons/azure-virtual-desktop/docs/features/validation.md b/src/bicep/add-ons/azure-virtual-desktop/docs/features/validation.md index 72a54046c..e394c81cb 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/docs/features/validation.md +++ b/src/bicep/add-ons/azure-virtual-desktop/docs/features/validation.md @@ -5,13 +5,13 @@ ## Features - [**Auto Increase Premium File Share Quota**](./autoIncreasePremiumFileShareQuota.md#auto-increase-premium-file-share-quota) +- [**Autoscale**](./autoscale.md#autoscale) - [**Backups**](./backups.md#backups) - [**Drain Mode**](./drainMode.md#drain-mode) - [**FSLogix**](./fslogix.md#fslogix) - [**GPU Drivers & Settings**](./gpu.md#gpu-drivers--settings) - [**High Availability**](./highAvailability.md#high-availability) - [**Monitoring**](./monitoring.md#monitoring) -- [**Scaling Tool**](./scalingTool.md#scaling-tool) - [**Server-Side Encryption with Customer Managed Keys**](./serverSideEncryption.md#server-side-encryption) - [**SMB Multichannel**](./smbMultiChannel.md#smb-multichannel) - [**Start VM On Connect**](./startVmOnConnect.md#start-vm-on-connect) diff --git a/src/bicep/add-ons/azure-virtual-desktop/docs/prerequisites.md b/src/bicep/add-ons/azure-virtual-desktop/docs/prerequisites.md index f213f93fb..0822f1d24 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/docs/prerequisites.md +++ b/src/bicep/add-ons/azure-virtual-desktop/docs/prerequisites.md @@ -8,7 +8,7 @@ To successfully deploy this solution, you will need to ensure the following prer ### Required -- **Licenses:** ensure you have the [required licensing for AVD](https://learn.microsoft.com/en-us/azure/virtual-desktop/overview#requirements). +- **Licenses:** ensure you have the [required licensing for AVD](https://learn.microsoft.com/azure/virtual-desktop/overview#requirements). - **Azure Permissions:** ensure the principal deploying the solution has "Owner" and "Key Vault Administrator" roles assigned on the target Azure subscription. This solution contains many role assignments at different scopes and deploys a key vault with keys and secrets to enhance security. - **Security Group:** create a security group for your AVD users. - AD DS: create the group in ADUC and ensure the group has synchronized to Azure AD. diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/controlPlane/applicationGroup.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/controlPlane/applicationGroup.bicep index bfb966310..9093c1d72 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/modules/controlPlane/applicationGroup.bicep +++ b/src/bicep/add-ons/azure-virtual-desktop/modules/controlPlane/applicationGroup.bicep @@ -1,5 +1,6 @@ param deploymentNameSuffix string param deploymentUserAssignedIdentityClientId string +param deploymentUserAssignedIdentityPrincipalId string param desktopApplicationGroupName string param desktopFriendlyName string param hostPoolResourceId string @@ -12,7 +13,7 @@ param securityPrincipalObjectIds array param tags object param virtualMachineName string -resource applicationGroup 'Microsoft.DesktopVirtualization/applicationGroups@2021-03-09-preview' = { +resource applicationGroup 'Microsoft.DesktopVirtualization/applicationGroups@2024-04-03' = { name: desktopApplicationGroupName location: locationControlPlane tags: union({ @@ -24,6 +25,16 @@ resource applicationGroup 'Microsoft.DesktopVirtualization/applicationGroups@202 } } +resource roleAssignment_ManagedIdentity 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(deploymentUserAssignedIdentityPrincipalId, '86240b0e-9422-4c43-887b-b61143f32ba8', applicationGroup.id) + scope: applicationGroup + properties: { + roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', '86240b0e-9422-4c43-887b-b61143f32ba8') // Desktop Virtualization Application Group Contributor (Purpose: updates the friendly name for the desktop) + principalId: deploymentUserAssignedIdentityPrincipalId + principalType: 'ServicePrincipal' + } +} + // Adds a friendly name to the SessionDesktop application for the desktop application group module applicationFriendlyName '../common/runCommand.bicep' = if (!empty(desktopFriendlyName)) { scope: resourceGroup(resourceGroupManagement) @@ -54,7 +65,7 @@ module applicationFriendlyName '../common/runCommand.bicep' = if (!empty(desktop } { name: 'UserAssignedIdentityClientId' - value:deploymentUserAssignedIdentityClientId + value: deploymentUserAssignedIdentityClientId } ] script: loadTextContent('../../artifacts/Update-AvdDesktop.ps1') @@ -67,9 +78,12 @@ module applicationFriendlyName '../common/runCommand.bicep' = if (!empty(desktop ) virtualMachineName: virtualMachineName } + dependsOn: [ + roleAssignment_ManagedIdentity + ] } -resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for i in range(0, length(securityPrincipalObjectIds)): { +resource roleAssignment_Users 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for i in range(0, length(securityPrincipalObjectIds)): { scope: applicationGroup name: guid(securityPrincipalObjectIds[i], roleDefinitions.DesktopVirtualizationUser, desktopApplicationGroupName) properties: { diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/controlPlane/controlPlane.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/controlPlane/controlPlane.bicep index a244f5168..441ac9f53 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/modules/controlPlane/controlPlane.bicep +++ b/src/bicep/add-ons/azure-virtual-desktop/modules/controlPlane/controlPlane.bicep @@ -6,6 +6,7 @@ param customImageId string param customRdpProperty string param deploymentNameSuffix string param deploymentUserAssignedIdentityClientId string +param deploymentUserAssignedIdentityPrincipalId string param desktopFriendlyName string param diskSku string param domainName string @@ -49,6 +50,7 @@ module hostPool 'hostPool.bicep' = { avdPrivateDnsZoneResourceId: avdPrivateDnsZoneResourceId customImageId: customImageId customRdpProperty: customRdpProperty + deploymentUserAssignedIdentityPrincipalId: deploymentUserAssignedIdentityPrincipalId diskSku: diskSku domainName: domainName enableAvdInsights: enableAvdInsights @@ -81,6 +83,7 @@ module applicationGroup 'applicationGroup.bicep' = { params: { deploymentNameSuffix: deploymentNameSuffix deploymentUserAssignedIdentityClientId: deploymentUserAssignedIdentityClientId + deploymentUserAssignedIdentityPrincipalId: deploymentUserAssignedIdentityPrincipalId desktopApplicationGroupName: replace(namingConvention.applicationGroup, serviceToken, 'desktop') hostPoolResourceId: hostPool.outputs.resourceId locationControlPlane: locationControlPlane diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/controlPlane/hostPool.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/controlPlane/hostPool.bicep index e289ee130..e98bf306c 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/modules/controlPlane/hostPool.bicep +++ b/src/bicep/add-ons/azure-virtual-desktop/modules/controlPlane/hostPool.bicep @@ -1,5 +1,6 @@ param activeDirectorySolution string param avdPrivateDnsZoneResourceId string +param deploymentUserAssignedIdentityPrincipalId string param customImageId string param customRdpProperty string param diskSku string @@ -27,7 +28,7 @@ param time string = utcNow('u') param validationEnvironment bool param virtualMachineSize string -var customRdpProperty_Complete = contains(activeDirectorySolution, 'MicrosoftEntraId') ? '${customRdpProperty}targetisaadjoined:i:1;enablerdsaadauth:i:1;' : customRdpProperty +var customRdpProperty_Complete = contains(activeDirectorySolution, 'MicrosoftEntraId') ? '${customRdpProperty}enablerdsaadauth:i:1;' : customRdpProperty resource hostPool 'Microsoft.DesktopVirtualization/hostPools@2023-09-05' = { name: hostPoolName @@ -37,10 +38,10 @@ resource hostPool 'Microsoft.DesktopVirtualization/hostPools@2023-09-05' = { }, contains(tags, 'Microsoft.DesktopVirtualization/hostPools') ? tags['Microsoft.DesktopVirtualization/hostPools'] : {}, mlzTags) properties: { customRdpProperty: customRdpProperty_Complete - hostPoolType: split(hostPoolType, ' ')[0] - loadBalancerType: contains(hostPoolType, 'Pooled') ? split(hostPoolType, ' ')[1] : 'Persistent' + hostPoolType: hostPoolType + loadBalancerType: hostPoolType == 'Pooled' ? 'DepthFirst' : 'Persistent' maxSessionLimit: maxSessionLimit - personalDesktopAssignmentType: contains(hostPoolType, 'Personal') ? split(hostPoolType, ' ')[1] : null + personalDesktopAssignmentType: hostPoolType == 'Personal' ? 'Automatic' : null preferredAppGroupType: 'Desktop' publicNetworkAccess: hostPoolPublicNetworkAccess registrationInfo: { @@ -50,7 +51,6 @@ resource hostPool 'Microsoft.DesktopVirtualization/hostPools@2023-09-05' = { startVMOnConnect: true validationEnvironment: validationEnvironment vmTemplate: '{"domain":"${domainName}","galleryImageOffer":${galleryImageOffer},"galleryImagePublisher":${galleryImagePublisher},"galleryImageSKU":${galleryImageSku},"imageType":${imageType},"customImageId":${customImageId},"namePrefix":"${sessionHostNamePrefix}","osDiskType":"${diskSku}","vmSize":{"id":"${virtualMachineSize}","cores":null,"ram":null,"rdmaEnabled": false,"supportsMemoryPreservingMaintenance": true},"galleryItemId":${galleryItemId},"hibernate":false,"diskSizeGB":0,"securityType":"TrustedLaunch","secureBoot":true,"vTPM":true,"vmInfrastructureType":"Cloud","virtualProcessorCount":null,"memoryGB":null,"maximumMemoryGB":null,"minimumMemoryGB":null,"dynamicMemoryConfig":false}' - } } @@ -94,6 +94,16 @@ resource privateDnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneG } } +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(deploymentUserAssignedIdentityPrincipalId, '2ad6aaab-ead9-4eaa-8ac5-da422f562408', hostPool.id) + scope: hostPool + properties: { + roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', '2ad6aaab-ead9-4eaa-8ac5-da422f562408') // Desktop Virtualization Session Host Operator (Purpose: sets drain mode on the AVD session hosts) + principalId: deploymentUserAssignedIdentityPrincipalId + principalType: 'ServicePrincipal' + } +} + resource diagnosticSetting 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (enableAvdInsights) { name: hostPoolDiagnosticSettingName scope: hostPool diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/fslogix/azureFiles/azureFiles.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/fslogix/azureFiles/azureFiles.bicep index 43ba76a2e..4d2cc6277 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/modules/fslogix/azureFiles/azureFiles.bicep +++ b/src/bicep/add-ons/azure-virtual-desktop/modules/fslogix/azureFiles/azureFiles.bicep @@ -15,7 +15,6 @@ param fslogixContainerType string param fslogixStorageService string param functionAppName string param hostPoolName string -param hostPoolType string param keyVaultUri string param location string param managementVirtualMachineName string @@ -161,19 +160,20 @@ module shares 'shares.bicep' = [for i in range(0, storageCount): { storageSku: storageSku } dependsOn: [ + fileServices roleAssignment ] }] resource privateEndpoints 'Microsoft.Network/privateEndpoints@2023-04-01' = [for i in range(0, storageCount): { - name: '${replace(namingConvention.storageAccountPrivateEndpoint, serviceToken, 'file-fslogix')}-${padLeft(i + storageIndex, 2, '0')}' + name: '${namingConvention.storageAccountFilePrivateEndpoint}-${padLeft(i + storageIndex, 2, '0')}' location: location tags: tagsPrivateEndpoints properties: { - customNetworkInterfaceName: '${replace(namingConvention.storageAccountNetworkInterface, serviceToken, 'file-fslogix')}-${padLeft(i + storageIndex, 2, '0')}' + customNetworkInterfaceName: '${namingConvention.storageAccountFileNetworkInterface}-${padLeft(i + storageIndex, 2, '0')}' privateLinkServiceConnections: [ { - name: '${replace(namingConvention.storageAccountPrivateEndpoint, serviceToken, 'file-fslogix')}-${padLeft(i + storageIndex, 2, '0')}' + name: '${namingConvention.storageAccountFilePrivateEndpoint}-${padLeft(i + storageIndex, 2, '0')}' properties: { privateLinkServiceId: storageAccounts[i].id groupIds: [ @@ -206,8 +206,8 @@ resource privateDnsZoneGroups 'Microsoft.Network/privateEndpoints/privateDnsZone ] }] -module ntfsPermissions '../runCommand.bicep' = if (contains(activeDirectorySolution, 'DomainServices')) { - name: 'deploy-fslogix-ntfs-permissions-${deploymentNameSuffix}' +module ntfsPermissions '../runCommand.bicep' = { + name: 'set-ntfs-permissions-${deploymentNameSuffix}' scope: resourceGroup(resourceGroupManagement) params: { domainJoinPassword: domainJoinPassword @@ -283,8 +283,8 @@ module ntfsPermissions '../runCommand.bicep' = if (contains(activeDirectorySolut ] } -module recoveryServices 'recoveryServices.bicep' = if (enableRecoveryServices && contains(hostPoolType, 'Pooled')) { - name: 'deploy-backup-azure-files-${deploymentNameSuffix}' +module recoveryServices 'recoveryServices.bicep' = if (enableRecoveryServices) { + name: 'deploy-backup-${deploymentNameSuffix}' scope: resourceGroup(resourceGroupManagement) params: { deploymentNameSuffix: deploymentNameSuffix @@ -298,7 +298,7 @@ module recoveryServices 'recoveryServices.bicep' = if (enableRecoveryServices && tagsRecoveryServicesVault: tagsRecoveryServicesVault } dependsOn: [ - shares + ntfsPermissions ] } @@ -316,7 +316,7 @@ module autoIncreaseStandardFileShareQuota '../../common/function.bicep' = if (fs schedule: '0 */15 * * * *' } dependsOn: [ - ntfsPermissions + recoveryServices ] } diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/fslogix/fslogix.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/fslogix/fslogix.bicep index 1366803f2..d4555ce84 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/modules/fslogix/fslogix.bicep +++ b/src/bicep/add-ons/azure-virtual-desktop/modules/fslogix/fslogix.bicep @@ -19,7 +19,6 @@ param fslogixShareSizeInGB int param fslogixContainerType string param fslogixStorageService string param functionAppName string -param hostPoolType string param keyVaultUri string param location string param managementVirtualMachineName string @@ -96,7 +95,6 @@ module azureFiles 'azureFiles/azureFiles.bicep' = if (storageService == 'AzureFi fslogixShareSizeInGB: fslogixShareSizeInGB fslogixStorageService: fslogixStorageService functionAppName: functionAppName - hostPoolType: hostPoolType keyVaultUri: keyVaultUri location: location managementVirtualMachineName: managementVirtualMachineName diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/logic.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/logic.bicep index f158639b3..1ef2d017e 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/modules/logic.bicep +++ b/src/bicep/add-ons/azure-virtual-desktop/modules/logic.bicep @@ -7,7 +7,6 @@ param domainName string param fileShareNames object param fslogixContainerType string param fslogixStorageService string -param hostPoolType string param imageOffer string param imagePublisher string param imageSku string @@ -52,7 +51,6 @@ var galleryImageSku = empty(imageVersionResourceId) ? '"${imageSku}"' : 'null' var galleryItemId = empty(imageVersionResourceId) ? '"${imagePublisher}.${imageOffer}${imageSku}"' : 'null' var imageType = empty(imageVersionResourceId) ? '"Gallery"' : '"CustomImage"' var netbios = split(domainName, '.')[0] -var pooledHostPool = split(hostPoolType, ' ')[0] == 'Pooled' ? true : false var resourceGroups = union(resourceGroupsCommon, resourceGroupsNetworking, resourceGroupsStorage) var resourceGroupsCommon = [ resourceGroupControlPlane @@ -89,7 +87,6 @@ output fileShares array = fileShares output fslogix bool = fslogix output maxResourcesPerTemplateDeployment int = maxResourcesPerTemplateDeployment output netbios string = netbios -output pooledHostPool bool = pooledHostPool output resourceGroups array = resourceGroups output roleDefinitions object = roleDefinitions output sessionHostBatchCount int = sessionHostBatchCount diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/management/automationAccount.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/management/automationAccount.bicep deleted file mode 100644 index 927ad90c3..000000000 --- a/src/bicep/add-ons/azure-virtual-desktop/modules/management/automationAccount.bicep +++ /dev/null @@ -1,126 +0,0 @@ -param automationAccountDiagnosticSettingName string -param automationAccountName string -param automationAccountNetworkInterfaceName string -param automationAccountPrivateDnsZoneResourceId string -param automationAccountPrivateEndpointName string -param hostPoolName string -param location string -param logAnalyticsWorkspaceResourceId string -param mlzTags object -param monitoring bool -param resourceGroupControlPlane string -param subnetResourceId string -param tags object -param virtualMachineName string - -resource virtualMachine 'Microsoft.Compute/virtualMachines@2023-07-01' existing = { - name: virtualMachineName -} - -resource automationAccount 'Microsoft.Automation/automationAccounts@2021-06-22' = { - name: automationAccountName - location: location - tags: union({'cm-resource-parent': '${subscription().id}}/resourceGroups/${resourceGroupControlPlane}/providers/Microsoft.DesktopVirtualization/hostpools/${hostPoolName}'}, contains(tags, 'Microsoft.Automation/automationAccounts') ? tags['Microsoft.Automation/automationAccounts'] : {}, mlzTags) - identity: { - type: 'SystemAssigned' - } - properties: { - sku: { - name: 'Free' - } - } -} - -resource privateEndpoint 'Microsoft.Network/privateEndpoints@2023-04-01' = { - name: automationAccountPrivateEndpointName - location: location - tags: union({'cm-resource-parent': '${subscription().id}}/resourceGroups/${resourceGroupControlPlane}/providers/Microsoft.DesktopVirtualization/hostpools/${hostPoolName}'}, contains(tags, 'Microsoft.Network/privateEndpoints') ? tags['Microsoft.Network/privateEndpoints'] : {}, mlzTags) - properties: { - customNetworkInterfaceName: automationAccountNetworkInterfaceName - privateLinkServiceConnections: [ - { - name: automationAccountPrivateEndpointName - properties: { - privateLinkServiceId: automationAccount.id - groupIds: [ - 'DSCAndHybridWorker' - ] - } - } - ] - subnet: { - id: subnetResourceId - } - } -} - -resource privateDnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2023-05-01' = { - parent: privateEndpoint - name: 'default' - properties: { - privateDnsZoneConfigs: [ - { - name: replace(split(automationAccountPrivateDnsZoneResourceId, '/')[8], '.', '-') - properties: { - privateDnsZoneId: automationAccountPrivateDnsZoneResourceId - } - } - ] - } -} - -resource hybridRunbookWorkerGroup 'Microsoft.Automation/automationAccounts/hybridRunbookWorkerGroups@2022-08-08' = { - parent: automationAccount - name: 'Scaling Tool' -} - -resource hybridRunbookWorker 'Microsoft.Automation/automationAccounts/hybridRunbookWorkerGroups/hybridRunbookWorkers@2022-08-08' = { - parent: hybridRunbookWorkerGroup - name: guid(hybridRunbookWorkerGroup.id) - properties: { - vmResourceId: virtualMachine.id - } -} - -resource extension_HybridWorker 'Microsoft.Compute/virtualMachines/extensions@2022-03-01' = { - parent: virtualMachine - name: 'HybridWorkerForWindows' - location: location - tags: union({'cm-resource-parent': '${subscription().id}}/resourceGroups/${resourceGroupControlPlane}/providers/Microsoft.DesktopVirtualization/hostpools/${hostPoolName}'}, contains(tags, 'Microsoft.Compute/virtualMachines') ? tags['Microsoft.Compute/virtualMachines'] : {}, mlzTags) - properties: { - publisher: 'Microsoft.Azure.Automation.HybridWorker' - type: 'HybridWorkerForWindows' - typeHandlerVersion: '1.1' - autoUpgradeMinorVersion: true - enableAutomaticUpgrade: true - settings: { - AutomationAccountURL: automationAccount.properties.automationHybridServiceUrl - } - } -} - -// Enables logging in a log analytics workspace for alerting and dashboards -resource diagnosticSetting 'Microsoft.Insights/diagnosticsettings@2017-05-01-preview' = if (monitoring) { - scope: automationAccount - name: automationAccountDiagnosticSettingName - properties: { - logs: [ - { - category: 'DscNodeStatus' - enabled: true - } - { - category: 'JobLogs' - enabled: true - } - { - category: 'JobStreams' - enabled: true - } - ] - workspaceId: logAnalyticsWorkspaceResourceId - } -} - -output name string = automationAccount.name -output hybridRunbookWorkerGroupName string = hybridRunbookWorkerGroup.name diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/management/functionApp.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/management/functionApp.bicep index bf2487656..f4af029e1 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/modules/management/functionApp.bicep +++ b/src/bicep/add-ons/azure-virtual-desktop/modules/management/functionApp.bicep @@ -1,5 +1,4 @@ param delegatedSubnetResourceId string -param deployFslogix bool param deploymentNameSuffix string param enableApplicationInsights bool param environmentAbbreviation string @@ -13,50 +12,41 @@ param privateDnsZones array param privateLinkScopeResourceId string param resourceAbbreviations object param resourceGroupControlPlane string -param resourceGroupHosts string param resourceGroupStorage string -param scalingBeginPeakTime string -param scalingEndPeakTime string -param scalingLimitSecondsToForceLogOffUser string -param scalingMinimumNumberOfRdsh string -param scalingSessionThresholdPerCPU string param serviceToken string param subnetResourceId string param tags object param timeDifference string var cloudSuffix = replace(replace(environment().resourceManager, 'https://management.', ''), '/', '') -var functionAppKeyword = environment().name == 'AzureCloud' || environment().name == 'AzureUSGovernment' - ? 'azurewebsites' - : 'appservice' +var functionAppKeyword = environment().name == 'AzureCloud' || environment().name == 'AzureUSGovernment' ? 'azurewebsites' : 'appservice' var functionAppScmPrivateDnsZoneResourceId = '${privateDnsZoneResourceIdPrefix}scm.${filter(privateDnsZones, name => contains(name, functionAppKeyword))[0]}' -var roleAssignments = union([ +var service = 'aipfsq' +var storageSubResources = [ { - roleDefinitionId: '40c5ff49-9181-41f8-ae61-143b0e78555e' // Desktop Virtualization Power On Off Contributor - scope: resourceGroupControlPlane + name: 'blob' + id: '${privateDnsZoneResourceIdPrefix}${filter(privateDnsZones, name => contains(name, 'blob'))[0]}' + nic: namingConvention.storageAccountBlobNetworkInterface + pe: namingConvention.storageAccountBlobPrivateEndpoint } { - roleDefinitionId: '40c5ff49-9181-41f8-ae61-143b0e78555e' // Desktop Virtualization Power On Off Contributor - scope: resourceGroupHosts + name: 'file' + id: '${privateDnsZoneResourceIdPrefix}${filter(privateDnsZones, name => contains(name, 'file'))[0]}' + nic: namingConvention.storageAccountFileNetworkInterface + pe: namingConvention.storageAccountFilePrivateEndpoint } -], deployFslogix ? [ { - roleDefinitionId: '17d1049b-9a84-46fb-8f53-869881c3d3ab' // Storage Account Contributor - scope: resourceGroupStorage + name: 'queue' + id: '${privateDnsZoneResourceIdPrefix}${filter(privateDnsZones, name => contains(name, 'queue'))[0]}' + nic: namingConvention.storageAccountQueueNetworkInterface + pe: namingConvention.storageAccountQueuePrivateEndpoint + } + { + name: 'table' + id: '${privateDnsZoneResourceIdPrefix}${filter(privateDnsZones, name => contains(name, 'table'))[0]}' + nic: namingConvention.storageAccountTableNetworkInterface + pe: namingConvention.storageAccountTablePrivateEndpoint } -] : []) -var service = 'mgmt' -var storagePrivateDnsZoneResourceIds = [ - '${privateDnsZoneResourceIdPrefix}${filter(privateDnsZones, name => contains(name, 'blob'))[0]}' - '${privateDnsZoneResourceIdPrefix}${filter(privateDnsZones, name => contains(name, 'file'))[0]}' - '${privateDnsZoneResourceIdPrefix}${filter(privateDnsZones, name => contains(name, 'queue'))[0]}' - '${privateDnsZoneResourceIdPrefix}${filter(privateDnsZones, name => contains(name, 'table'))[0]}' -] -var storageSubResources = [ - 'blob' - 'file' - 'queue' - 'table' ] resource userAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { @@ -66,7 +56,7 @@ resource userAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@ } resource vault 'Microsoft.KeyVault/vaults@2022-07-01' = { - name: '${resourceAbbreviations.keyVaults}${uniqueString(replace(namingConvention.keyVault, service, 'cmk'), resourceGroup().id)}' + name: '${resourceAbbreviations.keyVaults}${uniqueString(replace(namingConvention.keyVault, serviceToken, service), resourceGroup().id)}' location: location tags: tags[?'Microsoft.KeyVault/vaults'] ?? {} properties: { @@ -255,30 +245,18 @@ resource blobService 'Microsoft.Storage/storageAccounts/blobServices@2021-09-01' resource privateEndpoints_storage 'Microsoft.Network/privateEndpoints@2023-04-01' = [ for resource in storageSubResources: { - name: replace( - namingConvention.storageAccountPrivateEndpoint, - '${serviceToken}-${resourceAbbreviations.storageAccounts}', - '${resource}-${resourceAbbreviations.storageAccounts}-scale' - ) + name: replace(resource.pe, serviceToken, service) location: location tags: tags[?'Microsoft.Network/privateEndpoints'] ?? {} properties: { - customNetworkInterfaceName: replace( - namingConvention.storageAccountNetworkInterface, - '${serviceToken}-${resourceAbbreviations.storageAccounts}', - '${resource}-${resourceAbbreviations.storageAccounts}-scale' - ) + customNetworkInterfaceName: replace(resource.nic, serviceToken, service) privateLinkServiceConnections: [ { - name: replace( - namingConvention.storageAccountPrivateEndpoint, - '${serviceToken}-${resourceAbbreviations.storageAccounts}', - '${resource}-${resourceAbbreviations.storageAccounts}-scale' - ) + name: replace(resource.pe, serviceToken, service) properties: { privateLinkServiceId: storageAccount.id groupIds: [ - resource + resource.name ] } } @@ -300,7 +278,7 @@ resource privateDnsZoneGroups_storage 'Microsoft.Network/privateEndpoints/privat name: 'ipconfig1' properties: { #disable-next-line use-resource-id-functions - privateDnsZoneId: storagePrivateDnsZoneResourceIds[i] + privateDnsZoneId: resource.id } } ] @@ -411,14 +389,6 @@ resource functionApp 'Microsoft.Web/sites@2023-01-01' = { name: 'WEBSITE_LOAD_USER_PROFILE' value: '1' } - { - name: 'BeginPeakTime' - value: scalingBeginPeakTime - } - { - name: 'EndPeakTime' - value: scalingEndPeakTime - } { name: 'EnvironmentName' value: environment().name @@ -435,10 +405,6 @@ resource functionApp 'Microsoft.Web/sites@2023-01-01' = { name: 'HostPoolResourceGroupName' value: resourceGroupControlPlane } - { - name: 'LimitSecondsToForceLogOffUser' - value: scalingLimitSecondsToForceLogOffUser - } { name: 'LogOffMessageBody' value: 'This session is about to be logged off. Please save your work.' @@ -451,10 +417,6 @@ resource functionApp 'Microsoft.Web/sites@2023-01-01' = { name: 'MaintenanceTagName' value: 'Maintenance' } - { - name: 'MinimumNumberOfRDSH' - value: scalingMinimumNumberOfRdsh - } { name: 'ResourceGroupName' value: resourceGroupStorage @@ -466,10 +428,6 @@ resource functionApp 'Microsoft.Web/sites@2023-01-01' = { ? environment().resourceManager : '${environment().resourceManager}/' } - { - name: 'SessionThresholdPerCPU' - value: scalingSessionThresholdPerCPU - } { name: 'StorageSuffix' value: environment().suffixes.storage @@ -550,18 +508,18 @@ resource privateDnsZoneGroup_functionApp 'Microsoft.Network/privateEndpoints/pri } } -module roleAssignments_resourceGroups '../common/roleAssignments/resourceGroup.bicep' = [ - for i in range(0, length(roleAssignments)): { - name: 'set-role-assignment-${i}-${deploymentNameSuffix}' - scope: resourceGroup(roleAssignments[i].scope) - params: { - principalId: functionApp.identity.principalId - principalType: 'ServicePrincipal' - roleDefinitionId: roleAssignments[i].roleDefinitionId - } +// Required role assignment for the funciton to manage the quota on Azure Files Premium +module roleAssignments_resourceGroups '../common/roleAssignments/resourceGroup.bicep' = { + name: 'set-role-assignment-${deploymentNameSuffix}' + scope: resourceGroup(resourceGroupStorage) + params: { + principalId: functionApp.identity.principalId + principalType: 'ServicePrincipal' + roleDefinitionId: '17d1049b-9a84-46fb-8f53-869881c3d3ab' // Storage Account Contributor } -] +} +// Required role assignment to support the zero trust deployment of a function app module roleAssignment_storageAccount '../common/roleAssignments/storageAccount.bicep' = { name: 'set-role-assignment-storage-${deploymentNameSuffix}' params: { diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/management/jobSchedules.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/management/jobSchedules.bicep deleted file mode 100644 index 0e5efb72e..000000000 --- a/src/bicep/add-ons/azure-virtual-desktop/modules/management/jobSchedules.bicep +++ /dev/null @@ -1,56 +0,0 @@ -param automationAccountName string -param environment string -param fslogixContainerType string -param resourceGroupName string -param runbookName string -param storageAccountName string -param subscriptionId string -param timestamp string = utcNow('yyyyMMddHHmmss') - -resource automationAccount 'Microsoft.Automation/automationAccounts@2022-08-08' existing = { - name: automationAccountName -} - -resource jobSchedules_ProfileContainers 'Microsoft.Automation/automationAccounts/jobSchedules@2022-08-08' = [for i in range(0, 4): { - parent: automationAccount - #disable-next-line use-stable-resource-identifiers - name: guid(timestamp, runbookName, storageAccountName, 'ProfileContainers', string(i)) - properties: { - parameters: { - environment: environment - FileShareName: 'profile-containers' - resourceGroupName: resourceGroupName - storageAccountName: storageAccountName - subscriptionId: subscriptionId - } - runbook: { - name: runbookName - } - runOn: null - schedule: { - name: '${storageAccountName}_ProfileContainers_${(i + 1) * 15}min' - } - } -}] - -resource jobSchedules_OfficeContainers 'Microsoft.Automation/automationAccounts/jobSchedules@2022-08-08' = [for i in range(0, 4): if (contains(fslogixContainerType, 'Office')) { - parent: automationAccount - #disable-next-line use-stable-resource-identifiers - name: guid(timestamp, runbookName, storageAccountName, 'OfficeContainers', string(i)) - properties: { - parameters: { - environment: environment - FileShareName: 'office-containers' - resourceGroupName: resourceGroupName - storageAccountName: storageAccountName - subscriptionId: subscriptionId - } - runbook: { - name: runbookName - } - runOn: null - schedule: { - name: '${storageAccountName}_OfficeContainers_${(i + 1) * 15}min' - } - } -}] diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/management/management.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/management/management.bicep index c5838a94f..6102b23f9 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/modules/management/management.bicep +++ b/src/bicep/add-ons/azure-virtual-desktop/modules/management/management.bicep @@ -1,6 +1,5 @@ targetScope = 'subscription' -param activeDirectorySolution string param avdObjectId string param deployFslogix bool param deploymentNameSuffix string @@ -14,7 +13,6 @@ param enableApplicationInsights bool param enableAvdInsights bool param environmentAbbreviation string param fslogixStorageService string -param hostPoolType string param locationVirtualMachines string param logAnalyticsWorkspaceRetention int param logAnalyticsWorkspaceSku string @@ -32,12 +30,6 @@ param resourceGroupHosts string param resourceGroupManagement string param resourceGroupStorage string param roleDefinitions object -param scalingBeginPeakTime string -param scalingEndPeakTime string -param scalingLimitSecondsToForceLogOffUser string -param scalingMinimumNumberOfRdsh string -param scalingSessionThresholdPerCPU string -param scalingTool bool param serviceToken string param storageService string param subnetResourceId string @@ -50,33 +42,15 @@ param virtualMachinePassword string param virtualMachineUsername string var hostPoolName = namingConvention.hostPool -var roleAssignments = union( +var resourceGroups = union( [ - { - roleDefinitionId: '86240b0e-9422-4c43-887b-b61143f32ba8' // Desktop Virtualization Application Group Contributor (Purpose: updates the friendly name for the desktop) - resourceGroup: resourceGroupControlPlane - subscription: subscription().subscriptionId - } - { - roleDefinitionId: '2ad6aaab-ead9-4eaa-8ac5-da422f562408' // Desktop Virtualization Session Host Operator (Purpose: sets drain mode on the AVD session hosts) - resourceGroup: resourceGroupControlPlane - subscription: subscription().subscriptionId - } - { - roleDefinitionId: 'a959dbd1-f747-45e3-8ba6-dd80f235f97c' // Desktop Virtualization Virtual Machine Contributor (Purpose: remove the management virtual machine) - resourceGroup: resourceGroupManagement - subscription: subscription().subscriptionId - } + resourceGroupControlPlane + resourceGroupHosts + resourceGroupManagement ], - deployFslogix - ? [ - { - roleDefinitionId: '17d1049b-9a84-46fb-8f53-869881c3d3ab' // Storage Account Contributor (Purpose: domain join storage account & set NTFS permissions on the file share) - resourceGroup: resourceGroupStorage - subscription: subscription().subscriptionId - } - ] - : [] + deployFslogix ? [ + resourceGroupStorage + ] : [] ) var userAssignedIdentityNamePrefix = namingConvention.userAssignedIdentity var virtualNetworkName = split(subnetResourceId, '/')[8] @@ -124,24 +98,40 @@ module deploymentUserAssignedIdentity 'userAssignedIdentity.bicep' = { } } + +// Role Assignments: Reader on the resource groups +//Purpose: domain join storage account(s) & set NTFS permissions on the file share(s) module roleAssignments_deployment '../common/roleAssignments/resourceGroup.bicep' = [ - for i in range(0, length(roleAssignments)): { - scope: resourceGroup(roleAssignments[i].subscription, roleAssignments[i].resourceGroup) + for i in range(0, length(resourceGroups)): { + scope: resourceGroup(resourceGroups[i]) name: 'deploy-role-assignment-${i}-${deploymentNameSuffix}' params: { principalId: deploymentUserAssignedIdentity.outputs.principalId principalType: 'ServicePrincipal' - roleDefinitionId: roleAssignments[i].roleDefinitionId + roleDefinitionId: 'acdd72a7-3385-48ef-bd42-f606fba81ae7' // Reader } } ] +// Role Assignment: Storage Account Contributor on the storage resource group +// Purpose: domain join storage account(s) & set NTFS permissions on the file share(s) +module roleAssignment_StorageAccountContributor '../common/roleAssignments/resourceGroup.bicep' = { + scope: resourceGroup(resourceGroupStorage) + name: 'deploy-role-assignment-${deploymentNameSuffix}' + params: { + principalId: deploymentUserAssignedIdentity.outputs.principalId + principalType: 'ServicePrincipal' + roleDefinitionId: '17d1049b-9a84-46fb-8f53-869881c3d3ab' // Storage Account Contributor + } +} + // Management VM // The management VM is required to execute PowerShell scripts. module virtualMachine 'virtualMachine.bicep' = { name: 'deploy-mgmt-vm-${deploymentNameSuffix}' scope: resourceGroup(resourceGroupManagement) params: { + deploymentUserAssignedIdentityPrincipalId: deploymentUserAssignedIdentity.outputs.principalId deploymentUserAssignedIdentityResourceId: deploymentUserAssignedIdentity.outputs.resourceId diskEncryptionSetResourceId: diskEncryptionSetResourceId diskName: replace(namingConvention.virtualMachineDisk, serviceToken, 'mgt') @@ -165,14 +155,11 @@ module virtualMachine 'virtualMachine.bicep' = { } } -// Role Assignment required for Start VM On Connect +// Role Assignment required for Scaling Plan resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - name: guid(avdObjectId, roleDefinitions.DesktopVirtualizationPowerOnContributor, subscription().id) + name: guid(avdObjectId, roleDefinitions.DesktopVirtualizationPowerOnOffContributor, subscription().id) properties: { - roleDefinitionId: resourceId( - 'Microsoft.Authorization/roleDefinitions', - roleDefinitions.DesktopVirtualizationPowerOnContributor - ) + roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions',roleDefinitions.DesktopVirtualizationPowerOnOffContributor) principalId: avdObjectId } } @@ -198,12 +185,12 @@ module monitoring 'monitoring.bicep' = if (enableApplicationInsights || enableAv } } -module functionApp 'functionApp.bicep' = if (scalingTool || fslogixStorageService == 'AzureFiles Premium') { +// Deploys the Auto Increase Premium File Share Quota solution on an Azure Function App +module functionApp 'functionApp.bicep' = if (deployFslogix && fslogixStorageService == 'AzureFiles Premium') { name: 'deploy-function-app-${deploymentNameSuffix}' scope: resourceGroup(resourceGroupManagement) params: { delegatedSubnetResourceId: filter(subnets, subnet => contains(subnet.name, 'FunctionAppOutbound'))[0].id - deployFslogix: deployFslogix deploymentNameSuffix: deploymentNameSuffix enableApplicationInsights: enableApplicationInsights environmentAbbreviation: environmentAbbreviation @@ -215,13 +202,7 @@ module functionApp 'functionApp.bicep' = if (scalingTool || fslogixStorageServic privateLinkScopeResourceId: privateLinkScopeResourceId resourceAbbreviations: resourceAbbreviations resourceGroupControlPlane: resourceGroupControlPlane - resourceGroupHosts: resourceGroupHosts resourceGroupStorage: resourceGroupStorage - scalingBeginPeakTime: scalingBeginPeakTime - scalingEndPeakTime:scalingEndPeakTime - scalingLimitSecondsToForceLogOffUser: scalingLimitSecondsToForceLogOffUser - scalingMinimumNumberOfRdsh: scalingMinimumNumberOfRdsh - scalingSessionThresholdPerCPU: scalingSessionThresholdPerCPU serviceToken: serviceToken subnetResourceId: subnetResourceId tags: tags @@ -229,13 +210,7 @@ module functionApp 'functionApp.bicep' = if (scalingTool || fslogixStorageServic } } -module recoveryServicesVault 'recoveryServicesVault.bicep' = if (recoveryServices && ((contains( - activeDirectorySolution, - 'DomainServices' -) && contains(hostPoolType, 'Pooled') && contains(fslogixStorageService, 'AzureFiles')) || contains( - hostPoolType, - 'Personal' -))) { +module recoveryServicesVault 'recoveryServicesVault.bicep' = if (recoveryServices) { name: 'deploy-rsv-${deploymentNameSuffix}' scope: resourceGroup(resourceGroupManagement) params: { @@ -261,13 +236,9 @@ output dataCollectionRuleResourceId string = enableAvdInsights ? monitoring.outp output deploymentUserAssignedIdentityClientId string = deploymentUserAssignedIdentity.outputs.clientId output deploymentUserAssignedIdentityPrincipalId string = deploymentUserAssignedIdentity.outputs.principalId output deploymentUserAssignedIdentityResourceId string = deploymentUserAssignedIdentity.outputs.resourceId -output functionAppName string = scalingTool || fslogixStorageService == 'AzureFiles Premium' ? functionApp.outputs.functionAppName : '' +output functionAppName string = fslogixStorageService == 'AzureFiles Premium' ? functionApp.outputs.functionAppName : '' output logAnalyticsWorkspaceName string = enableApplicationInsights || enableAvdInsights ? monitoring.outputs.logAnalyticsWorkspaceName : '' -output logAnalyticsWorkspaceResourceId string = enableApplicationInsights || enableAvdInsights - ? monitoring.outputs.logAnalyticsWorkspaceResourceId - : '' -output recoveryServicesVaultName string = recoveryServices && ((contains(activeDirectorySolution, 'DomainServices') && contains(hostPoolType,'Pooled') && contains(fslogixStorageService, 'AzureFiles')) || contains(hostPoolType, 'Personal')) - ? recoveryServicesVault.outputs.name - : '' +output logAnalyticsWorkspaceResourceId string = enableApplicationInsights || enableAvdInsights ? monitoring.outputs.logAnalyticsWorkspaceResourceId : '' +output recoveryServicesVaultName string = recoveryServices ? recoveryServicesVault.outputs.name : '' output virtualMachineName string = virtualMachine.outputs.name output virtualMachineResourceId string = virtualMachine.outputs.resourceId diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/management/scalingPlan.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/management/scalingPlan.bicep new file mode 100644 index 000000000..44b851d2f --- /dev/null +++ b/src/bicep/add-ons/azure-virtual-desktop/modules/management/scalingPlan.bicep @@ -0,0 +1,276 @@ +@description('Required. Principal ID of the user assigned identity.') +param deploymentUserAssignedIdentityPrincipalId string + +@description('Optional. Enable AVD Insights.') +param enableAvdInsights bool = true + +@description('Required. Host pool resource ID for the Scaling Plan.') +param hostPoolResourceId string + +@description('Required. Location of the Scaling Plan. The location must match the location of the host pool.') +param location string + +@description('Optional. Resource ID of the Log Analytics workspace for the Scaling Plan.') +param logAnalyticsWorkspaceResourceId string = '' + +@allowed([ + 'Personal' + 'Pooled' +]) +@description('Required. Host pool type of the Scaling Plan.') +param hostPoolType string + +@description('Required. Name of the diagnostic setting for the Scaling Plan.') +param scalingPlanDiagnosticSettingName string + +@description('Required. Name of the Scaling Plan.') +param scalingPlanName string + +@description('Optional. Supported clouds.') +param supportedClouds array = [ + 'AzureCloud' + 'AzureUSGovernment' +] + +@description('Optional. Tags of the resource.') +param tags object = {} + +@description('Optional. Time zone of the Scaling Plan. Defaults to UTC.') +param timeZone string = 'UTC' + +@description('Required. Off peak start time for weekdays in HH:mm format.') +param weekdaysOffPeakStartTime string + +@description('Required. Off peak start time for weekends in HH:mm format.') +param weekendsOffPeakStartTime string + +@description('Required. Peak start time for weekdays in HH:mm format.') +param weekdaysPeakStartTime string + +@description('Required. Peak start time for weekends in HH:mm format.') +param weekendsPeakStartTime string + +var schedules = hostPoolType == 'Pooled' ? [ + { + daysOfWeek: [ + 'Monday' + 'Tuesday' + 'Wednesday' + 'Thursday' + 'Friday' + ] + offPeakLoadBalancingAlgorithm: 'DepthFirst' + offPeakStartTime: { + hour: split(weekdaysOffPeakStartTime, ':')[0] + minute: split(weekdaysOffPeakStartTime, ':')[1] + } + peakLoadBalancingAlgorithm: 'BreadthFirst' + peakStartTime: { + hour: split(weekdaysPeakStartTime, ':')[0] + minute: split(weekdaysPeakStartTime, ':')[1] + } + rampDownCapacityThresholdPct: 90 + rampDownForceLogoffUsers: false + rampDownLoadBalancingAlgorithm: 'DepthFirst' + rampDownMinimumHostsPct: 0 + rampDownNotificationMessage: 'Ramping down the AVD session hosts to support low demand.' + rampDownStartTime: { + hour: int(split(weekdaysOffPeakStartTime, ':')[0]) - 1 + minute: split(weekdaysOffPeakStartTime, ':')[1] + } + rampDownStopHostsWhen: 'ZeroSessions' + rampDownWaitTimeMinutes: 0 + rampUpCapacityThresholdPct: 70 + rampUpLoadBalancingAlgorithm: 'BreadthFirst' + rampUpMinimumHostsPct: 25 + rampUpStartTime: { + hour: int(split(weekdaysPeakStartTime, ':')[0]) - 1 + minute: split(weekdaysPeakStartTime, ':')[1] + } + } + { + daysOfWeek: [ + 'Saturday' + 'Sunday' + ] + offPeakLoadBalancingAlgorithm: 'DepthFirst' + offPeakStartTime: { + hour: split(weekendsOffPeakStartTime, ':')[0] + minute: split(weekendsOffPeakStartTime, ':')[1] + } + peakLoadBalancingAlgorithm: 'BreadthFirst' + peakStartTime: { + hour: split(weekendsPeakStartTime, ':')[0] + minute: split(weekendsPeakStartTime, ':')[1] + } + rampDownCapacityThresholdPct: 90 + rampDownForceLogoffUsers: false + rampDownLoadBalancingAlgorithm: 'DepthFirst' + rampDownMinimumHostsPct: 0 + rampDownNotificationMessage: 'Ramping down the AVD session hosts to support low demand.' + rampDownStartTime: { + hour: int(split(weekendsOffPeakStartTime, ':')[0]) - 1 + minute: split(weekendsOffPeakStartTime, ':')[1] + } + rampDownStopHostsWhen: 'ZeroSessions' + rampDownWaitTimeMinutes: 0 + rampUpCapacityThresholdPct: 90 + rampUpLoadBalancingAlgorithm: 'BreadthFirst' + rampUpMinimumHostsPct: 25 + rampUpStartTime: { + hour: int(split(weekendsPeakStartTime, ':')[0]) - 1 + minute: split(weekendsPeakStartTime, ':')[1] + } + } +] : [ + { + daysOfWeek: [ + 'Monday' + 'Tuesday' + 'Wednesday' + 'Thursday' + 'Friday' + ] + offPeakActionOnDisconnect: 'None' + offPeakActionOnLogoff: 'Deallocate' + offPeakMinutesToWaitOnDisconnect: 0 + offPeakMinutesToWaitOnLogoff: 0 + offPeakStartTime: { + hour: split(weekdaysOffPeakStartTime, ':')[0] + minute: split(weekdaysOffPeakStartTime, ':')[1] + } + offPeakStartVMOnConnect: 'Enable' + peakActionOnDisconnect: 'None' + peakActionOnLogoff: 'Deallocate' + peakMinutesToWaitOnDisconnect: 0 + peakMinutesToWaitOnLogoff: 0 + peakStartTime: { + hour: split(weekdaysPeakStartTime, ':')[0] + minute: split(weekdaysPeakStartTime, ':')[1] + } + peakStartVMOnConnect: 'Enable' + rampDownActionOnDisconnect: 'None' + rampDownActionOnLogoff: 'Deallocate' + rampDownMinutesToWaitOnDisconnect: 0 + rampDownMinutesToWaitOnLogoff: 0 + rampDownStartTime: { + hour: int(split(weekdaysOffPeakStartTime, ':')[0]) - 1 + minute: split(weekdaysOffPeakStartTime, ':')[1] + } + rampDownStartVMOnConnect: 'Enable' + rampUpActionOnDisconnect: 'None' + rampUpActionOnLogoff: 'None' + rampUpAutoStartHosts: 'WithAssignedUser' + rampUpMinutesToWaitOnDisconnect: 0 + rampUpMinutesToWaitOnLogoff: 0 + rampUpStartTime: { + hour: int(split(weekdaysPeakStartTime, ':')[0]) - 1 + minute: split(weekdaysPeakStartTime, ':')[1] + } + rampUpStartVMOnConnect: 'Enable' + } + { + daysOfWeek: [ + 'Saturday' + 'Sunday' + ] + rampUpStartTime: { + hour: int(split(weekendsPeakStartTime, ':')[0]) - 1 + minute: split(weekendsPeakStartTime, ':')[1] + } + peakStartTime: { + hour: split(weekendsPeakStartTime, ':')[0] + minute: split(weekendsPeakStartTime, ':')[1] + } + peakMinutesToWaitOnDisconnect: 0 + peakActionOnDisconnect: 'None' + peakMinutesToWaitOnLogoff: 0 + peakActionOnLogoff: 'Deallocate' + peakStartVMOnConnect: 'Enable' + rampDownStartTime: { + hour: int(split(weekendsOffPeakStartTime, ':')[0]) - 1 + minute: split(weekendsOffPeakStartTime, ':')[1] + } + rampDownMinutesToWaitOnDisconnect: 0 + rampDownActionOnDisconnect: 'None' + rampDownMinutesToWaitOnLogoff: 0 + rampDownActionOnLogoff: 'Deallocate' + rampDownStartVMOnConnect: 'Enable' + rampUpAutoStartHosts: 'WithAssignedUser' + rampUpStartVMOnConnect: 'Enable' + rampUpMinutesToWaitOnDisconnect: 0 + rampUpActionOnDisconnect: 'None' + rampUpMinutesToWaitOnLogoff: 0 + rampUpActionOnLogoff: 'None' + offPeakStartTime: { + hour: split(weekendsOffPeakStartTime, ':')[0] + minute: split(weekendsOffPeakStartTime, ':')[1] + } + offPeakMinutesToWaitOnDisconnect: 0 + offPeakActionOnDisconnect: 'None' + offPeakMinutesToWaitOnLogoff: 0 + offPeakActionOnLogoff: 'Deallocate' + offPeakStartVMOnConnect: 'Enable' + } +] + +resource scalingPlan 'Microsoft.DesktopVirtualization/scalingPlans@2023-09-05' = { + name: scalingPlanName + location: location + tags: tags + properties: { + timeZone: timeZone + hostPoolType: hostPoolType + exclusionTag: 'excludeFromAutoscale' + schedules: [] + hostPoolReferences: [ + { + hostPoolArmPath: hostPoolResourceId + scalingPlanEnabled: true + } + ] + } +} + +resource schedules_Pooled 'Microsoft.DesktopVirtualization/scalingPlans/pooledSchedules@2024-04-03' = [for i in range(0, length(schedules)): if (hostPoolType == 'Pooled') { + name: i == 0 ? 'Weekdays' : 'Weekends' + parent: scalingPlan + properties: schedules[i] +}] + +resource schedule_Personal 'Microsoft.DesktopVirtualization/scalingPlans/personalSchedules@2024-04-03' = [for i in range(0, length(schedules)): if (hostPoolType == 'Personal') { + name: i == 0 ? 'Weekdays' : 'Weekends' + parent: scalingPlan + properties: schedules[i] +}] + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(deploymentUserAssignedIdentityPrincipalId, '082f0a83-3be5-4ba1-904c-961cca79b387', scalingPlan.id) + scope: scalingPlan + properties: { + roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', '082f0a83-3be5-4ba1-904c-961cca79b387') // Desktop Virtualization Contributor (Purpose: disable scaling plan when adding new hosts) + principalId: deploymentUserAssignedIdentityPrincipalId + principalType: 'ServicePrincipal' + } + dependsOn: [ + schedule_Personal + schedules_Pooled + ] +} + +resource diagnosticSetting 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (enableAvdInsights && contains(supportedClouds, environment().name)) { + name: scalingPlanDiagnosticSettingName + properties: { + logs: [ + { + categoryGroup: 'allLogs' + enabled: true + } + ] + workspaceId: logAnalyticsWorkspaceResourceId + } + scope: scalingPlan + dependsOn: [ + roleAssignment + ] +} diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/management/scalingTool.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/management/scalingTool.bicep deleted file mode 100644 index cbdc32d71..000000000 --- a/src/bicep/add-ons/azure-virtual-desktop/modules/management/scalingTool.bicep +++ /dev/null @@ -1,561 +0,0 @@ -param beginPeakTime string = '08:00' -param delegatedSubnetResourceId string -param endPeakTime string = '17:00' -param environmentAbbreviation string -param hostPoolName string -param hostPoolResourceGroupName string -param keyExpirationInDays int = 30 -param limitSecondsToForceLogOffUser string = '0' -param location string = resourceGroup().location -param logAnalyticsWorkspaceResourceId string = '' -param minimumNumberOfRdsh string = '0' -param namingConvention object -param privateDnsZoneResourceIdPrefix string -param privateDnsZones array -param privateLinkScopeResourceId string = '' -param resourceAbbreviations object -param serviceToken string -param sessionHostsResourceGroupName string -param sessionThresholdPerCPU string = '1' -param subnetResourceId string -param tags object -param timeDifference string -param timestamp string = utcNow('yyyyMMddhhmmss') - -var fileShareName = 'function-app' -var functionAppKeyword = environment().name == 'AzureCloud' || environment().name == 'AzureUSGovernment' ? 'azurewebsites' : 'appservice' -var privateLinkScopeResourceGroupName = empty(logAnalyticsWorkspaceResourceId) ? resourceGroup().name : split(privateLinkScopeResourceId, '/')[4] -var privateLinkScopeSubscriptionId = empty(logAnalyticsWorkspaceResourceId) ? subscription().subscriptionId : split(privateLinkScopeResourceId, '/')[2] -var roleAssignments = [ - hostPoolResourceGroupName - sessionHostsResourceGroupName -] -var storagePrivateDnsZoneResourceIds = [ - '${privateDnsZoneResourceIdPrefix}${filter(privateDnsZones, name => contains(name, 'blob'))[0]}' - '${privateDnsZoneResourceIdPrefix}${filter(privateDnsZones, name => contains(name, 'file'))[0]}' - '${privateDnsZoneResourceIdPrefix}${filter(privateDnsZones, name => contains(name, 'queue'))[0]}' - '${privateDnsZoneResourceIdPrefix}${filter(privateDnsZones, name => contains(name, 'table'))[0]}' -] -var storageSubResources = [ - 'blob' - 'file' - 'queue' - 'table' -] - -resource userAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { - name: replace(namingConvention.userAssignedIdentityName, serviceToken, 'scale') - location: location - tags: contains(tags, 'Microsoft.ManagedIdentity/userAssignedIdentities') - ? tags['Microsoft.ManagedIdentity/userAssignedIdentities'] - : {} -} - -resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - name: guid(userAssignedIdentity.id, 'e147488a-f6f5-4113-8e2d-b22465e65bf6', resourceGroup().id) - properties: { - roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', 'e147488a-f6f5-4113-8e2d-b22465e65bf6') // Key Vault Crypto Service Encryption User - principalId: userAssignedIdentity.properties.principalId - principalType: 'ServicePrincipal' - } -} - -resource vault 'Microsoft.KeyVault/vaults@2022-07-01' = { - name: replace(namingConvention.keyVaultName, serviceToken, 'scale') - location: location - tags: contains(tags, 'Microsoft.KeyVault/vaults') ? tags['Microsoft.KeyVault/vaults'] : {} - properties: { - enabledForDeployment: false - enabledForDiskEncryption: false - enabledForTemplateDeployment: false - enablePurgeProtection: true - enableRbacAuthorization: true - enableSoftDelete: true - networkAcls: { - bypass: 'AzureServices' - defaultAction: 'Deny' - ipRules: [] - virtualNetworkRules: [] - } - publicNetworkAccess: 'Disabled' - sku: { - family: 'A' - name: 'premium' - } - softDeleteRetentionInDays: environmentAbbreviation == 'dev' || environmentAbbreviation == 'test' ? 7 : 90 - tenantId: subscription().tenantId - } -} - -resource privateEndpoint_vault 'Microsoft.Network/privateEndpoints@2023-04-01' = { - name: replace(namingConvention.keyVaultPrivateEndpoint, serviceToken, 'scale') - location: location - tags: contains(tags, 'Microsoft.Network/privateEndpoints') ? tags['Microsoft.Network/privateEndpoints'] : {} - properties: { - customNetworkInterfaceName: replace(namingConvention.keyVaultNetworkInterface, serviceToken, 'scale') - privateLinkServiceConnections: [ - { - name: replace(namingConvention.keyVaultPrivateEndpoint, serviceToken, 'scale') - properties: { - privateLinkServiceId: vault.id - groupIds: [ - 'vault' - ] - } - } - ] - subnet: { - id: subnetResourceId - } - } -} - -resource privateDnsZoneGroup_vault 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2021-08-01' = { - parent: privateEndpoint_vault - name: vault.name - properties: { - privateDnsZoneConfigs: [ - { - name: 'ipconfig1' - properties: { - #disable-next-line use-resource-id-functions - privateDnsZoneId: '${privateDnsZoneResourceIdPrefix}${filter(privateDnsZones, name => contains(name, 'vaultcore'))[0]}' - } - } - ] - } -} - -resource key_storageAccount 'Microsoft.KeyVault/vaults/keys@2022-07-01' = { - parent: vault - name: 'StorageEncryptionKey' - properties: { - attributes: { - enabled: true - } - keySize: 4096 - kty: 'RSA' - rotationPolicy: { - attributes: { - expiryTime: 'P${string(keyExpirationInDays)}D' - } - lifetimeActions: [ - { - action: { - type: 'Notify' - } - trigger: { - timeBeforeExpiry: 'P10D' - } - } - { - action: { - type: 'Rotate' - } - trigger: { - timeAfterCreate: 'P${string(keyExpirationInDays - 7)}D' - } - } - ] - } - } -} - -resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = { - name: replace(namingConvention.storageAccountName, serviceToken, 'scale') - location: location - tags: contains(tags, 'Microsoft.Storage/storageAccounts') ? tags['Microsoft.Storage/storageAccounts'] : {} - sku: { - name: 'Standard_LRS' - } - kind: 'StorageV2' - identity: { - type: 'UserAssigned' - userAssignedIdentities: { - '${userAssignedIdentity.id}': {} - } - } - properties: { - accessTier: 'Hot' - allowBlobPublicAccess: false - allowCrossTenantReplication: false - allowedCopyScope: 'PrivateLink' - allowSharedKeyAccess: true - azureFilesIdentityBasedAuthentication: { - directoryServiceOptions: 'None' - } - defaultToOAuthAuthentication: false - dnsEndpointType: 'Standard' - encryption: { - identity: { - userAssignedIdentity: userAssignedIdentity.id - } - requireInfrastructureEncryption: true - keyvaultproperties: { - keyvaulturi: vault.properties.vaultUri - keyname: key_storageAccount.name - } - services: { - file: { - keyType: 'Account' - enabled: true - } - table: { - keyType: 'Account' - enabled: true - } - queue: { - keyType: 'Account' - enabled: true - } - blob: { - keyType: 'Account' - enabled: true - } - } - keySource: 'Microsoft.KeyVault' - } - largeFileSharesState: 'Disabled' - minimumTlsVersion: 'TLS1_2' - networkAcls: { - bypass: 'AzureServices' - virtualNetworkRules: [] - ipRules: [] - defaultAction: 'Deny' - } - publicNetworkAccess: 'Disabled' - supportsHttpsTrafficOnly: true - } -} - -resource blobService 'Microsoft.Storage/storageAccounts/blobServices@2021-09-01' = { - parent: storageAccount - name: 'default' -} - -resource fileServices 'Microsoft.Storage/storageAccounts/fileServices@2022-09-01' = { - parent: storageAccount - name: 'default' - properties: { - protocolSettings: { - smb: { - versions: 'SMB3.1.1;' - authenticationMethods: 'NTLMv2;' - channelEncryption: 'AES-128-GCM;AES-256-GCM;' - } - } - shareDeleteRetentionPolicy: { - enabled: false - } - } -} - -resource share 'Microsoft.Storage/storageAccounts/fileServices/shares@2022-09-01' = { - parent: fileServices - name: fileShareName - properties: { - accessTier: 'TransactionOptimized' - shareQuota: 5120 - enabledProtocols: 'SMB' - } -} - -resource privateEndpoints_storage 'Microsoft.Network/privateEndpoints@2023-04-01' = [ - for resource in storageSubResources: { - name: replace(namingConvention.storageAccountPrivateEndpoint, '${serviceToken}-${resourceAbbreviations.storageAccounts}', '${resource}-${resourceAbbreviations.storageAccounts}-scale') - location: location - tags: contains(tags, 'Microsoft.Network/privateEndpoints') ? tags['Microsoft.Network/privateEndpoints'] : {} - properties: { - customNetworkInterfaceName: replace(namingConvention.storageAccountNetworkInterface, '${serviceToken}-${resourceAbbreviations.storageAccounts}', '${resource}-${resourceAbbreviations.storageAccounts}-scale') - privateLinkServiceConnections: [ - { - name: replace(namingConvention.storageAccountPrivateEndpoint, '${serviceToken}-${resourceAbbreviations.storageAccounts}', '${resource}-${resourceAbbreviations.storageAccounts}-scale') - properties: { - privateLinkServiceId: storageAccount.id - groupIds: [ - resource - ] - } - } - ] - subnet: { - id: subnetResourceId - } - } - } -] - -resource privateDnsZoneGroups_storage 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2021-08-01' = [ - for (resource, i) in storageSubResources: { - parent: privateEndpoints_storage[i] - name: storageAccount.name - properties: { - privateDnsZoneConfigs: [ - { - name: 'ipconfig1' - properties: { - #disable-next-line use-resource-id-functions - privateDnsZoneId: storagePrivateDnsZoneResourceIds[i] - } - } - ] - } - } -] - -resource diagnosticSetting_storage_blob 'Microsoft.Insights/diagnosticsettings@2017-05-01-preview' = - if (!empty(logAnalyticsWorkspaceResourceId)) { - scope: blobService - name: replace(namingConvention.storageAccountDiagnosticSetting, '${serviceToken}-${resourceAbbreviations.storageAccounts}', 'blob-${resourceAbbreviations.storageAccounts}-scale') - properties: { - logs: [ - { - category: 'StorageWrite' - enabled: true - } - ] - metrics: [ - { - category: 'Transaction' - enabled: true - } - ] - workspaceId: logAnalyticsWorkspaceResourceId - } - } - -resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = { - name: replace(namingConvention.applicationInsightsName, serviceToken, 'scale') - location: location - tags: contains(tags, 'Microsoft.Insights/components') ? tags['Microsoft.Insights/components'] : {} - properties: { - Application_Type: 'web' - } - kind: 'web' -} - -module privateLinkScope '../management/privateLinkScope.bicep' = { - name: 'PrivateLinkScope_${timestamp}' - scope: resourceGroup(privateLinkScopeSubscriptionId, privateLinkScopeResourceGroupName) - params: { - applicationInsightsName: applicationInsights.name - applicationInsightsResourceId: applicationInsights.id - privateLinkScopeResourceId: privateLinkScopeResourceId - } -} - -resource appServicePlan 'Microsoft.Web/serverfarms@2023-01-01' = { - name: replace(namingConvention.appServicePlanName, serviceToken, 'scale') - location: location - tags: contains(tags, 'Microsoft.Web/serverfarms') ? tags['Microsoft.Web/serverfarms'] : {} - sku: { - tier: 'ElasticPremium' - name: 'EP1' - } - kind: 'functionapp' - properties: { - targetWorkerSizeId: 3 - targetWorkerCount: 1 - maximumElasticWorkerCount: 20 - zoneRedundant: false - } - dependsOn: [ - privateEndpoints_storage - privateDnsZoneGroups_storage - ] -} - -resource functionApp 'Microsoft.Web/sites@2023-01-01' = { - name: replace(namingConvention.functionAppName, serviceToken, 'scale') - location: location - tags: contains(tags, 'Microsoft.Web/sites') ? tags['Microsoft.Web/sites'] : {} - kind: 'functionapp' - identity: { - type: 'SystemAssigned' - } - properties: { - clientAffinityEnabled: false - httpsOnly: true - publicNetworkAccess: 'Disabled' - serverFarmId: appServicePlan.id - siteConfig: { - appSettings: [ - { - name: 'FUNCTIONS_EXTENSION_VERSION' - value: '~4' - } - { - name: 'FUNCTIONS_WORKER_RUNTIME' - value: 'powershell' - } - { - name: 'APPLICATIONINSIGHTS_CONNECTION_STRING' - value: applicationInsights.properties.ConnectionString - } - { - name: 'AzureWebJobsStorage' - value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${listKeys(storageAccount.id,'2019-06-01').keys[0].value};EndpointSuffix=${environment().suffixes.storage}' - } - { - name: 'WEBSITE_CONTENTAZUREFILECONNECTIONSTRING' - value: 'DefaultEndpointsProtocol=https;AccountName=${storageAccount.name};AccountKey=${listKeys(storageAccount.id,'2019-06-01').keys[0].value};EndpointSuffix=${environment().suffixes.storage}' - } - { - name: 'WEBSITE_CONTENTSHARE' - value: fileShareName - } - { - name: 'BeginPeakTime' - value: beginPeakTime - } - { - name: 'EndPeakTime' - value: endPeakTime - } - { - name: 'EnvironmentName' - value: environment().name - } - { - name: 'HostPoolName' - value: hostPoolName - } - { - name: 'HostPoolResourceGroupName' - value: hostPoolResourceGroupName - } - { - name: 'LimitSecondsToForceLogOffUser' - value: limitSecondsToForceLogOffUser - } - { - name: 'LogOffMessageBody' - value: 'This session is about to be logged off. Please save your work.' - } - { - name: 'LogOffMessageTitle' - value: 'Session Log Off' - } - { - name: 'MaintenanceTagName' - value: 'Maintenance' - } - { - name: 'MinimumNumberOfRDSH' - value: minimumNumberOfRdsh - } - { - name: 'ResourceManagerUrl' - // This workaround is needed because the environment().resourceManager value is missing the trailing slash for some Azure environments - value: endsWith(environment().resourceManager, '/') ? environment().resourceManager : '${environment().resourceManager}/' - } - { - name: 'SessionThresholdPerCPU' - value: sessionThresholdPerCPU - } - { - name: 'SubscriptionId' - value: subscription().subscriptionId - } - { - name: 'TenantId' - value: subscription().tenantId - } - { - name: 'TimeDifference' - value: timeDifference - } - ] - cors: { - allowedOrigins: [ - environment().portal - ] - } - ftpsState: 'FtpsOnly' - netFrameworkVersion: 'v6.0' - powerShellVersion: '7.2' - use32BitWorkerProcess: false - } - virtualNetworkSubnetId: delegatedSubnetResourceId - vnetContentShareEnabled: true - vnetRouteAllEnabled: true - } -} - -resource privateEndpoint_functionApp 'Microsoft.Network/privateEndpoints@2023-04-01' = { - name: replace(namingConvention.functionAppPrivateEndpoint, serviceToken, 'scale') - location: location - properties: { - customNetworkInterfaceName: replace(namingConvention.functionAppNetworkInterface, serviceToken, 'scale') - privateLinkServiceConnections: [ - { - name: replace(namingConvention.functionAppPrivateEndpoint, serviceToken, 'scale') - properties: { - privateLinkServiceId: functionApp.id - groupIds: [ - 'sites' - ] - } - } - ] - subnet: { - id: subnetResourceId - } - } -} - -resource privateDnsZoneGroup_functionApp 'Microsoft.Network/privateEndpoints/privateDnsZoneGroups@2021-08-01' = { - parent: privateEndpoint_functionApp - name: 'default' - properties: { - privateDnsZoneConfigs: [ - { - name: 'ipconfig1' - properties: { - #disable-next-line use-resource-id-functions - privateDnsZoneId: '${privateDnsZoneResourceIdPrefix}${filter(privateDnsZones, name => contains(name, functionAppKeyword))[0]}' - } - } - ] - } -} - -resource function 'Microsoft.Web/sites/functions@2020-12-01' = { - parent: functionApp - name: 'avd-scaling-tool' - properties: { - config: { - disabled: false - bindings: [ - { - name: 'Timer' - type: 'timerTrigger' - direction: 'in' - schedule: '0 */15 * * * *' - } - ] - } - files: { - 'requirements.psd1': loadTextContent('../../artifacts/scaling-tool/requirements.psd1') - 'run.ps1': loadTextContent('../../artifacts/scaling-tool/run.ps1') - '../profile.ps1': loadTextContent('../../artifacts/scaling-tool/profile.ps1') - } - } - dependsOn: [ - privateEndpoint_functionApp - privateDnsZoneGroup_functionApp - ] -} - -// Gives the function app the "Desktop Virtualization Power On Off Contributor" role on the resource groups containing the hosts and host pool -module roleAssignments_ResourceGroups '../common/roleAssignment.bicep' = [ - for i in range(0, length(roleAssignments)): { - name: 'RoleAssignment_${roleAssignments[i]}_${timestamp}' - scope: resourceGroup(roleAssignments[i]) - params: { - principalId: functionApp.identity.principalId - principalType: 'ServicePrincipal' - roleDefinitionId: '40c5ff49-9181-41f8-ae61-143b0e78555e' // Desktop Virtualization Power On Off Contributor - } - } -] diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/management/schedules.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/management/schedules.bicep deleted file mode 100644 index f4e6f5325..000000000 --- a/src/bicep/add-ons/azure-virtual-desktop/modules/management/schedules.bicep +++ /dev/null @@ -1,37 +0,0 @@ -param automationAccountName string -param fslogixContainerType string -param storageAccountName string -param time string = utcNow() -param timeZone string - -resource automationAccount 'Microsoft.Automation/automationAccounts@2022-08-08' existing = { - name: automationAccountName -} - -resource schedules_ProfileContainers 'Microsoft.Automation/automationAccounts/schedules@2022-08-08' = [for i in range(0, 4): { - parent: automationAccount - name: '${storageAccountName}_ProfileContainers_${(i + 1) * 15}min' - properties: { - advancedSchedule: {} - description: null - expiryTime: null - frequency: 'Hour' - interval: 1 - startTime: dateTimeAdd(time, 'PT${(i + 1) * 15}M') - timeZone: timeZone - } -}] - -resource schedules_OfficeContainers 'Microsoft.Automation/automationAccounts/schedules@2022-08-08' = [for i in range(0, 4): if (contains(fslogixContainerType, 'Office')) { - parent: automationAccount - name: '${storageAccountName}_OfficeContainers_${(i + 1) * 15}min' - properties: { - advancedSchedule: {} - description: null - expiryTime: null - frequency: 'Hour' - interval: 1 - startTime: dateTimeAdd(time, 'PT${(i + 1) * 15}M') - timeZone: timeZone - } -}] diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/management/virtualMachine.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/management/virtualMachine.bicep index c2f293828..ff432a965 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/modules/management/virtualMachine.bicep +++ b/src/bicep/add-ons/azure-virtual-desktop/modules/management/virtualMachine.bicep @@ -1,3 +1,4 @@ +param deploymentUserAssignedIdentityPrincipalId string param deploymentUserAssignedIdentityResourceId string param diskEncryptionSetResourceId string param diskName string @@ -200,5 +201,15 @@ resource extension_JsonADDomainExtension 'Microsoft.Compute/virtualMachines/exte } } +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(deploymentUserAssignedIdentityPrincipalId, 'a959dbd1-f747-45e3-8ba6-dd80f235f97c', virtualMachine.id) + scope: virtualMachine + properties: { + roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c') // Desktop Virtualization Virtual Machine Contributor (Purpose: remove the management virtual machine) + principalId: deploymentUserAssignedIdentityPrincipalId + principalType: 'ServicePrincipal' + } +} + output name string = virtualMachine.name output resourceId string = virtualMachine.id diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/sessionHosts/sessionHosts.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/sessionHosts/sessionHosts.bicep index acf2ec221..05662d833 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/modules/sessionHosts/sessionHosts.bicep +++ b/src/bicep/add-ons/azure-virtual-desktop/modules/sessionHosts/sessionHosts.bicep @@ -10,6 +10,7 @@ param dataCollectionRuleResourceId string param deployFslogix bool param deploymentNameSuffix string param deploymentUserAssignedIdentityClientId string +param deploymentUserAssignedIdentityPrincipalId string param diskEncryptionSetResourceId string param diskSku string param divisionRemainderValue int @@ -22,8 +23,8 @@ param enableAcceleratedNetworking bool param enableAvdInsights bool param environmentAbbreviation string param fslogixContainerType string -param functionAppName string param hostPoolName string +param hostPoolResourceId string param hostPoolType string param identifier string param imageOffer string @@ -31,20 +32,23 @@ param imagePublisher string param imageSku string param imageVersionResourceId string param location string +param logAnalyticsWorkspaceResourceId string param managementVirtualMachineName string param maxResourcesPerTemplateDeployment int param mlzTags object param namingConvention object param netAppFileShares array param organizationalUnitPath string -param pooledHostPool bool param enableRecoveryServices bool -param enableScalingTool bool param recoveryServicesVaultName string param resourceGroupControlPlane string param resourceGroupHosts string param resourceGroupManagement string param roleDefinitions object +param scalingWeekdaysOffPeakStartTime string +param scalingWeekdaysPeakStartTime string +param scalingWeekendsOffPeakStartTime string +param scalingWeekendsPeakStartTime string param securityPrincipalObjectIds array param serviceToken string param sessionHostBatchCount int @@ -56,6 +60,7 @@ param storageService string param storageSuffix string param subnetResourceId string param tags object +param timeZone string @secure() param virtualMachinePassword string param virtualMachineSize string @@ -69,7 +74,7 @@ var tagsVirtualMachines = union({'cm-resource-parent': '${subscription().id}/res var uniqueToken = uniqueString(identifier, environmentAbbreviation, subscription().subscriptionId) var virtualMachineNamePrefix = replace(namingConvention.virtualMachine, serviceToken, '') -module availabilitySets 'availabilitySets.bicep' = if (pooledHostPool && availability == 'AvailabilitySets') { +module availabilitySets 'availabilitySets.bicep' = if (hostPoolType == 'Pooled' && availability == 'AvailabilitySets') { name: 'deploy-avail-${deploymentNameSuffix}' scope: resourceGroup(resourceGroupHosts) params: { @@ -103,6 +108,45 @@ resource image 'Microsoft.Compute/galleries/images@2023-07-03' existing = if (em name: split(imageVersionResourceId, '/')[10] } +// Disable Autoscale if adding new session hosts to an existing host pool +module disableAutoscale '../common/runCommand.bicep' = { + name: 'deploy-disable-autoscale-${deploymentNameSuffix}' + scope: resourceGroup(resourceGroupManagement) + params: { + location: location + name: 'Disable-Autoscale' + parameters: [ + { + name: 'HostPoolResourceId' + value: hostPoolResourceId + } + { + name: 'ResourceGroupName' + value: resourceGroupManagement + } + { + name: 'ResourceManagerUri' + value: environment().resourceManager + } + { + name: 'ScalingPlanName' + value: namingConvention.scalingPlan + } + { + name: 'SubscriptionId' + value: subscription().subscriptionId + } + { + name: 'UserAssignedidentityClientId' + value: deploymentUserAssignedIdentityClientId + } + ] + script: loadTextContent('../../artifacts/Disable-Autoscale.ps1') + tags: tagsVirtualMachines + virtualMachineName: managementVirtualMachineName + } +} + @batchSize(1) module virtualMachines 'virtualMachines.bicep' = [for i in range(1, sessionHostBatchCount): { name: 'deploy-vms-${i - 1}-${deploymentNameSuffix}' @@ -130,7 +174,6 @@ module virtualMachines 'virtualMachines.bicep' = [for i in range(1, sessionHostB enableDrainMode: drainMode fslogixContainerType: fslogixContainerType hostPoolName: hostPoolName - hostPoolType: hostPoolType imageVersionResourceId: imageVersionResourceId imageOffer: empty(imageVersionResourceId) ? imageOffer : image.properties.purchasePlan.product imagePublisher: empty(imageVersionResourceId) ? imagePublisher: image.properties.purchasePlan.publisher @@ -161,10 +204,11 @@ module virtualMachines 'virtualMachines.bicep' = [for i in range(1, sessionHostB } dependsOn: [ availabilitySets + disableAutoscale ] }] -module recoveryServices 'recoveryServices.bicep' = if (enableRecoveryServices && contains(hostPoolType, 'Personal')) { +module recoveryServices 'recoveryServices.bicep' = if (enableRecoveryServices && hostPoolType == 'Personal') { name: 'deploy-recovery-services-${deploymentNameSuffix}' scope: resourceGroup(resourceGroupManagement) params: { @@ -186,17 +230,27 @@ module recoveryServices 'recoveryServices.bicep' = if (enableRecoveryServices && ] } -module scalingTool '../common/function.bicep' = if (enableScalingTool && pooledHostPool) { - name: 'deploy-scaling-tool-${deploymentNameSuffix}' +module scalingPlan '../management/scalingPlan.bicep' = { + name: 'deploy-scaling-plan-${deploymentNameSuffix}' scope: resourceGroup(resourceGroupManagement) params: { - files: { - 'requirements.psd1': loadTextContent('../../artifacts/scaling-tool/requirements.psd1') - 'run.ps1': loadTextContent('../../artifacts/scaling-tool/run.ps1') - '../profile.ps1': loadTextContent('../../artifacts/scaling-tool/profile.ps1') - } - functionAppName: functionAppName - functionName: 'avd-scaling-tool' - schedule: '0 */15 * * * *' + deploymentUserAssignedIdentityPrincipalId: deploymentUserAssignedIdentityPrincipalId + enableAvdInsights: enableAvdInsights + hostPoolResourceId: hostPoolResourceId + hostPoolType: hostPoolType + location: location + logAnalyticsWorkspaceResourceId: logAnalyticsWorkspaceResourceId + scalingPlanDiagnosticSettingName: namingConvention.scalingPlanDiagnosticSetting + scalingPlanName: namingConvention.scalingPlan + tags: tags + timeZone: timeZone + weekdaysOffPeakStartTime: scalingWeekdaysOffPeakStartTime + weekdaysPeakStartTime: scalingWeekdaysPeakStartTime + weekendsOffPeakStartTime: scalingWeekendsOffPeakStartTime + weekendsPeakStartTime: scalingWeekendsPeakStartTime } + dependsOn: [ + recoveryServices + virtualMachines + ] } diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/sessionHosts/virtualMachines.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/sessionHosts/virtualMachines.bicep index 5d328b564..6cba20cb0 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/modules/sessionHosts/virtualMachines.bicep +++ b/src/bicep/add-ons/azure-virtual-desktop/modules/sessionHosts/virtualMachines.bicep @@ -21,7 +21,6 @@ param enableAvdInsights bool param enableDrainMode bool param fslogixContainerType string param hostPoolName string -param hostPoolType string param imageOffer string param imagePublisher string param imageSku string @@ -93,7 +92,6 @@ var nvidiaVmSizes = [ 'Standard_NV36adms_A10_v5' 'Standard_NV72ads_A10_v5' ] -var pooledHostPool = (split(hostPoolType, ' ')[0] == 'Pooled') var sessionHostNamePrefix = replace(virtualMachineNamePrefix, serviceToken, '') var storageAccountToken = '${storageAccountPrefix}??' // The token is used for AntiVirus exclusions. The '??' represents the two digits at the end of each storage account name. @@ -382,7 +380,7 @@ resource installAvdAgents 'Microsoft.Compute/virtualMachines/extensions@2021-03- ] // Enables drain mode on the session hosts so users cannot login to the hosts immediately after the deployment -module drainMode '../common/runCommand.bicep' = if (enableDrainMode && pooledHostPool) { +module drainMode '../common/runCommand.bicep' = if (enableDrainMode) { name: 'deploy-drain-mode-${batchCount}-${deploymentNameSuffix}' scope: resourceGroup(resourceGroupManagement) params: { diff --git a/src/bicep/add-ons/azure-virtual-desktop/parameters.json b/src/bicep/add-ons/azure-virtual-desktop/parameters.json deleted file mode 100644 index d5fe535eb..000000000 --- a/src/bicep/add-ons/azure-virtual-desktop/parameters.json +++ /dev/null @@ -1,222 +0,0 @@ -{ - "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", - "contentVersion": "1.0.0.0", - "parameters": { - "activeDirectorySolution": { - "value": "ActiveDirectoryDomainServices" - }, - "artifactsContainerName": { - "value": "artifacts" - }, - "artifactsStorageAccountResourceId": { - "value": "" - }, - "automationAccountPrivateDnsZoneResourceId": { - "value": "" - }, - "availability": { - "value": "AvailabilityZones" - }, - "avdAgentMsiName": { - "value": "Microsoft.RDInfra.RDAgent.Installer-x64-1.0.7539.8300.msi" - }, - "avdAgentBootLoaderMsiName": { - "value": "Microsoft.RDInfra.RDAgentBootLoader.Installer-x64 (5).msi" - }, - "avdObjectId": { - "value": "cdcfb416-e2fe-41e2-be12-33813c1cd427" - }, - "avdPrivateDnsZoneResourceId": { - "value": "/subscriptions/3764b123-4849-4395-8e6e-ca6d68d8d4b4/resourceGroups/rg-net-d-eu/providers/Microsoft.Network/privateDnsZones/privatelink.wvd.microsoft.com" - }, - "azureFilesPrivateDnsZoneResourceId": { - "value": "/subscriptions/3764b123-4849-4395-8e6e-ca6d68d8d4b4/resourceGroups/rg-net-d-eu/providers/Microsoft.Network/privateDnsZones/privatelink.file.core.windows.net" - }, - "azurePowerShellModuleMsiName": { - "value": "Az-Cmdlets-10.2.0.37547-x64.msi" - }, - "customRdpProperty": { - "value": "audiocapturemode:i:1;camerastoredirect:s:*;use multimon:i:0;drivestoredirect:s:;" - }, - "disableBgpRoutePropagation": { - "value": false - }, - "diskEncryption": { - "value": true - }, - "diskSku": { - "value": "Premium_LRS" - }, - "domainJoinPassword": { - "reference": { - "keyVault": { - "id": "/subscriptions/3764b123-4849-4395-8e6e-ca6d68d8d4b4/resourceGroups/rg-core-d-eu/providers/Microsoft.KeyVault/vaults/kv-core-d-eu" - }, - "secretName": "DomainJoinPassword" - } - }, - "domainJoinUserPrincipalName": { - "reference": { - "keyVault": { - "id": "/subscriptions/3764b123-4849-4395-8e6e-ca6d68d8d4b4/resourceGroups/rg-core-d-eu/providers/Microsoft.KeyVault/vaults/kv-core-d-eu" - }, - "secretName": "DomainJoinUsername" - } - }, - "domainName": { - "value": "jasonmasten.com" - }, - "drainMode": { - "value": true - }, - "environment": { - "value": "d" - }, - "fslogixShareSizeInGB": { - "value": 100 - }, - "fslogixSolution": { - "value": "ProfileContainer" - }, - "fslogixStorage": { - "value": "AzureFiles Premium" - }, - "globalWorkspacePrivateDnsZoneResourceId": { - "value": "/subscriptions/3764b123-4849-4395-8e6e-ca6d68d8d4b4/resourceGroups/rg-net-d-eu/providers/Microsoft.Network/privateDnsZones/privatelink-global.wvd.microsoft.com" - }, - "hostPoolPublicNetworkAccess": { - "value": "Enabled" - }, - "hostPoolType": { - "value": "Pooled DepthFirst" - }, - "hubAzureFirewallResourceId": { - "value": "" - }, - "hubVirtualNetworkResourceId": { - "value": "" - }, - "identifier": { - "value": "jnm" - }, - "imageDefinitionResourceId": { - "value": "" - }, - "imageOffer": { - "value": "office-365" - }, - "imagePublisher": { - "value": "MicrosoftWindowsDesktop" - }, - "imageSku": { - "value": "win11-22h2-avd-m365" - }, - "logAnalyticsWorkspaceRetention": { - "value": 30 - }, - "logAnalyticsWorkspaceSku": { - "value": "PerGB2018" - }, - "maxSessionLimit": { - "value": 8 - }, - "monitoring": { - "value": true - }, - "organizationalUnitPath": { - "value": "OU=AVD,DC=jasonmasten,DC=com" - }, - "recoveryServices": { - "value": false - }, - "scalingBeginPeakTime": { - "value": "7:00" - }, - "scalingEndPeakTime": { - "value": "18:00" - }, - "scalingLimitSecondsToForceLogOffUser": { - "value": "0" - }, - "scalingMinimumNumberOfRdsh": { - "value": "0" - }, - "scalingSessionThresholdPerCPU": { - "value": "1" - }, - "scalingTool": { - "value": true - }, - "securityLogAnalyticsWorkspaceResourceId": { - "value": "" - }, - "securityPrincipalObjectIds": { - "value": [ - "06c4afa5-b6dc-4719-8034-947887108e29" - ] - }, - "securityPrincipals": { - "value": [] - }, - "sessionDesktopFriendlyName": { - "value": "" - }, - "sessionHostCount": { - "value": 1 - }, - "sessionHostIndex": { - "value": 0 - }, - "stampIndex": { - "value": 0 - }, - "storageCount": { - "value": 1 - }, - "storageIndex": { - "value": 0 - }, - "subnetAddressPrefix": { - "value": "10.0.8.0/24" - }, - "subnetResourceId": { - "value": "/subscriptions/3764b123-4849-4395-8e6e-ca6d68d8d4b4/resourceGroups/rg-net-d-eu/providers/Microsoft.Network/virtualNetworks/vnet-net-d-eu/subnets/Clients" - }, - "tags": { - "value": {} - }, - "validationEnvironment": { - "value": false - }, - "virtualMachinePassword": { - "reference": { - "keyVault": { - "id": "/subscriptions/3764b123-4849-4395-8e6e-ca6d68d8d4b4/resourceGroups/rg-core-d-eu/providers/Microsoft.KeyVault/vaults/kv-core-d-eu" - }, - "secretName": "VmPassword" - } - }, - "virtualMachineSize": { - "value": "Standard_D4ads_v5" - }, - "virtualMachineUsername": { - "reference": { - "keyVault": { - "id": "/subscriptions/3764b123-4849-4395-8e6e-ca6d68d8d4b4/resourceGroups/rg-core-d-eu/providers/Microsoft.KeyVault/vaults/kv-core-d-eu" - }, - "secretName": "VmUsername" - } - }, - "virtualNetworkAddressPrefix": { - "value": [ - "10.0.8.0/24" - ] - }, - "workspaceFriendlyName": { - "value": "Jason Masten" - }, - "workspacePublicNetworkAccess": { - "value": "Enabled" - } - } -} \ No newline at end of file diff --git a/src/bicep/add-ons/azure-virtual-desktop/solution.bicep b/src/bicep/add-ons/azure-virtual-desktop/solution.bicep index b05c5d759..8ec64a176 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/solution.bicep +++ b/src/bicep/add-ons/azure-virtual-desktop/solution.bicep @@ -77,7 +77,7 @@ param emailSecurityContact string @description('Determine whether to enable accelerated networking on the AVD session hosts. https://learn.microsoft.com/en-us/azure/virtual-network/accelerated-networking-overview') param enableAcceleratedNetworking bool -@description('Deploys the required resources to monitor the function app for the Scaling Tool and Auto Increase Premium File Share solutions.') +@description('Deploys the required resources to monitor the function app for the Auto Increase Premium File Share solutions.') param enableApplicationInsights bool = true @description('Deploys the required monitoring resources to enable AVD Insights.') @@ -122,7 +122,7 @@ param fslogixContainerType string = 'ProfileContainer' @description('Enable an Fslogix storage option to manage user profiles for the AVD session hosts. The selected service & SKU should provide sufficient IOPS for all of your users. https://docs.microsoft.com/en-us/azure/architecture/example-scenario/wvd/windows-virtual-desktop-fslogix#performance-requirements') param fslogixStorageService string = 'AzureFiles Standard' -@description('The subnet address prefix for the delegated subnet for the Azure Function App. This subnet is required for the Scaling Tool and the Auto Increase Premium File Share Quotas tool.') +@description('The subnet address prefix for the delegated subnet for the Azure Function App. This subnet is required for the Auto Increase Premium File Share Quotas tool.') param functionAppSubnetAddressPrefix string = '' @allowed([ @@ -135,13 +135,11 @@ param functionAppSubnetAddressPrefix string = '' param hostPoolPublicNetworkAccess string @allowed([ - 'Pooled DepthFirst' - 'Pooled BreadthFirst' - 'Personal Automatic' - 'Personal Direct' + 'Pooled' + 'Personal' ]) -@description('These options specify the host pool type and depending on the type provides the load balancing options and assignment types.') -param hostPoolType string = 'Pooled DepthFirst' +@description('The type of AVD host pool.') +param hostPoolType string = 'Pooled' @description('The resource ID for the Azure Firewall in the HUB subscription') param hubAzureFirewallResourceId string @@ -207,23 +205,17 @@ param profile string = 'Generic' @description('Enable backups to an Azure Recovery Services vault. For a pooled host pool this will enable backups on the Azure file share. For a personal host pool this will enable backups on the AVD sessions hosts.') param recoveryServices bool = false -@description('The time when session hosts will scale up and continue to stay on to support peak demand; Format 24 hours e.g. 9:00 for 9am') -param scalingBeginPeakTime string = '9:00' +@description('Off peak start time for weekdays in HH:mm format.') +param scalingWeekdaysOffPeakStartTime string = '17:00' -@description('The time when session hosts will scale down and stay off to support low demand; Format 24 hours e.g. 17:00 for 5pm') -param scalingEndPeakTime string = '17:00' +@description('Off peak start time for weekends in HH:mm format.') +param scalingWeekdaysPeakStartTime string = '09:00' -@description('The number of seconds to wait before automatically signing out users. If set to 0 any session host that has user sessions will be left untouched') -param scalingLimitSecondsToForceLogOffUser string = '0' +@description('Peak start time for weekdays in HH:mm format.') +param scalingWeekendsOffPeakStartTime string = '17:00' -@description('The minimum number of session host VMs to keep running during off-peak hours. The scaling tool will not work if all virtual machines are turned off and the Start VM On Connect solution is not enabled.') -param scalingMinimumNumberOfRdsh string = '0' - -@description('The maximum number of sessions per CPU that will be used as a threshold to determine when new session host VMs need to be started during peak hours') -param scalingSessionThresholdPerCPU string = '1' - -@description('Deploys the required resources for the Scaling Tool. https://docs.microsoft.com/en-us/azure/virtual-desktop/scaling-automation-logic-apps') -param scalingTool bool = false +@description('Peak start time for weekends in HH:mm format.') +param scalingWeekendsPeakStartTime string = '09:00' @description('The array of Security Principals with their object IDs and display names to assign to the AVD Application Group and FSLogix Storage.') param securityPrincipals array @@ -319,7 +311,9 @@ var availabilitySetsCount = length(range(beginAvSetRange, (endAvSetRange - begin // OTHER LOGIC & COMPUTED VALUES var customImageId = empty(imageVersionResourceId) ? 'null' : '"${imageVersionResourceId}"' -var deployFslogix = contains(fslogixStorageService, 'Azure') && contains(activeDirectorySolution, 'DomainServices') ? true : false +var deployFslogix = contains(fslogixStorageService, 'Azure') && contains(activeDirectorySolution, 'DomainServices') + ? true + : false var fileShareNames = { CloudCacheProfileContainer: [ 'profile-containers' @@ -338,7 +332,6 @@ var fileShareNames = { } var fileShares = fileShareNames[fslogixContainerType] var netbios = split(domainName, '.')[0] -var pooledHostPool = split(hostPoolType, ' ')[0] == 'Pooled' ? true : false var privateDnsZoneResourceIdPrefix = '/subscriptions/${split(hubVirtualNetworkResourceId, '/')[2]}/resourceGroups/${split(hubVirtualNetworkResourceId, '/')[4]}/providers/Microsoft.Network/privateDnsZones/' var resourceGroupServices = union( [ @@ -353,7 +346,7 @@ var resourceGroupServices = union( : [] ) var roleDefinitions = { - DesktopVirtualizationPowerOnContributor: '489581de-a3bd-480d-9518-53dea7416b33' + DesktopVirtualizationPowerOnOffContributor: '40c5ff49-9181-41f8-ae61-143b0e78555e' DesktopVirtualizationUser: '1d18fff3-a72a-46b5-b4a9-0b38a3cd7e63' Reader: 'acdd72a7-3385-48ef-bd42-f606fba81ae7' VirtualMachineUserLogin: 'fb879df8-f326-4884-b1cf-06f3ad86be52' @@ -380,7 +373,7 @@ var subnets = { } ] : [] - functionApp: scalingTool || fslogixStorageService == 'AzureFiles Premium' + functionApp: fslogixStorageService == 'AzureFiles Premium' ? [ { name: 'FunctionAppOutbound' @@ -491,7 +484,6 @@ module rgs '../../modules/resource-group.bicep' = [ module management 'modules/management/management.bicep' = { name: 'deploy-management-${deploymentNameSuffix}' params: { - activeDirectorySolution: activeDirectorySolution avdObjectId: avdObjectId deployFslogix: deployFslogix deploymentNameSuffix: deploymentNameSuffix @@ -504,7 +496,6 @@ module management 'modules/management/management.bicep' = { enableAvdInsights: enableAvdInsights environmentAbbreviation: environmentAbbreviation fslogixStorageService: fslogixStorageService - hostPoolType: hostPoolType locationVirtualMachines: locationVirtualMachines logAnalyticsWorkspaceRetention: logAnalyticsWorkspaceRetention logAnalyticsWorkspaceSku: logAnalyticsWorkspaceSku @@ -520,14 +511,10 @@ module management 'modules/management/management.bicep' = { resourceGroupControlPlane: rgs[0].outputs.name resourceGroupHosts: rgs[1].outputs.name resourceGroupManagement: rgs[2].outputs.name - resourceGroupStorage: deployFslogix ? replace(tier3_hosts.outputs.namingConvention.resourceGroup, tier3_hosts.outputs.tokens.service, 'storage') : '' + resourceGroupStorage: deployFslogix + ? replace(tier3_hosts.outputs.namingConvention.resourceGroup, tier3_hosts.outputs.tokens.service, 'storage') + : '' roleDefinitions: roleDefinitions - scalingBeginPeakTime: scalingBeginPeakTime - scalingEndPeakTime: scalingEndPeakTime - scalingLimitSecondsToForceLogOffUser: scalingLimitSecondsToForceLogOffUser - scalingMinimumNumberOfRdsh: scalingMinimumNumberOfRdsh - scalingSessionThresholdPerCPU: scalingSessionThresholdPerCPU - scalingTool: scalingTool serviceToken: tier3_hosts.outputs.tokens.service storageService: storageService subnetResourceId: tier3_hosts.outputs.subnets[0].id @@ -551,6 +538,7 @@ module controlPlane 'modules/controlPlane/controlPlane.bicep' = { customRdpProperty: customRdpProperty deploymentNameSuffix: deploymentNameSuffix deploymentUserAssignedIdentityClientId: management.outputs.deploymentUserAssignedIdentityClientId + deploymentUserAssignedIdentityPrincipalId: management.outputs.deploymentUserAssignedIdentityPrincipalId desktopFriendlyName: empty(desktopFriendlyName) ? string(stampIndex) : desktopFriendlyName diskSku: diskSku domainName: domainName @@ -573,7 +561,11 @@ module controlPlane 'modules/controlPlane/controlPlane.bicep' = { roleDefinitions: roleDefinitions securityPrincipalObjectIds: map(securityPrincipals, item => item.objectId) serviceToken: naming_controlPlane.outputs.tokens.service - sessionHostNamePrefix: replace(tier3_hosts.outputs.namingConvention.virtualMachine, tier3_hosts.outputs.tokens.service, '') + sessionHostNamePrefix: replace( + tier3_hosts.outputs.namingConvention.virtualMachine, + tier3_hosts.outputs.tokens.service, + '' + ) subnetResourceId: tier3_hosts.outputs.subnets[1].id tags: tags validationEnvironment: validationEnvironment @@ -596,7 +588,9 @@ module workspaces 'modules/sharedServices/sharedServices.bicep' = { deploymentUserAssignedIdentityClientId: management.outputs.deploymentUserAssignedIdentityClientId deploymentUserAssignedIdentityPrincipalId: management.outputs.deploymentUserAssignedIdentityPrincipalId enableAvdInsights: enableAvdInsights - existingApplicationGroupReferences: empty(existingFeedWorkspaceResourceId) ? [] : workspace.properties.applicationGroupReferences + existingApplicationGroupReferences: empty(existingFeedWorkspaceResourceId) + ? [] + : workspace.properties.applicationGroupReferences existingFeedWorkspaceResourceId: existingFeedWorkspaceResourceId existingWorkspace: !empty(existingFeedWorkspaceResourceId) hostPoolName: controlPlane.outputs.hostPoolName @@ -608,17 +602,83 @@ module workspaces 'modules/sharedServices/sharedServices.bicep' = { resourceGroupManagement: rgs[2].outputs.name sharedServicesSubnetResourceId: sharedServicesSubnetResourceId tags: tags - workspaceFeedDiagnoticSettingName: replace(replace(naming_hub.outputs.names.workspaceFeedDiagnosticSetting,naming_hub.outputs.tokens.service,'feed'),'-${stampIndex}','') - workspaceFeedName: replace(replace(naming_controlPlane.outputs.names.workspaceFeed,naming_controlPlane.outputs.tokens.service,'feed'),'-${stampIndex}','') - workspaceFeedNetworkInterfaceName: replace(replace(naming_hub.outputs.names.workspaceFeedNetworkInterface,naming_hub.outputs.tokens.service,'feed'),'-${stampIndex}','') - workspaceFeedPrivateEndpointName: replace(replace(naming_hub.outputs.names.workspaceFeedPrivateEndpoint,naming_hub.outputs.tokens.service,'feed'),'-${stampIndex}','') - workspaceFeedResourceGroupName: replace(replace(naming_controlPlane.outputs.names.resourceGroup, naming_controlPlane.outputs.tokens.service, 'feedWorkspace'), '-${stampIndex}', '') - workspaceFriendlyName: empty(workspaceFriendlyName) ? replace(replace(naming_controlPlane.outputs.names.workspaceFeed,'-${naming_controlPlane.outputs.tokens.service}',''), '-${stampIndex}', '') : '${workspaceFriendlyName} (${virtualNetwork.location})' - workspaceGlobalName: replace(replace(replace(naming_controlPlane.outputs.names.workspaceGlobal,naming_controlPlane.outputs.tokens.service,'global'),'-${stampIndex}',''),identifier,virtualNetwork.tags.resourcePrefix) - workspaceGlobalNetworkInterfaceName: replace(replace(replace(naming_hub.outputs.names.workspaceGlobalNetworkInterface,naming_hub.outputs.tokens.service,'global'),'-${stampIndex}',''),identifier,virtualNetwork.tags.resourcePrefix) + workspaceFeedDiagnoticSettingName: replace( + replace(naming_hub.outputs.names.workspaceFeedDiagnosticSetting, naming_hub.outputs.tokens.service, 'feed'), + '-${stampIndex}', + '' + ) + workspaceFeedName: replace( + replace(naming_controlPlane.outputs.names.workspaceFeed, naming_controlPlane.outputs.tokens.service, 'feed'), + '-${stampIndex}', + '' + ) + workspaceFeedNetworkInterfaceName: replace( + replace(naming_hub.outputs.names.workspaceFeedNetworkInterface, naming_hub.outputs.tokens.service, 'feed'), + '-${stampIndex}', + '' + ) + workspaceFeedPrivateEndpointName: replace( + replace(naming_hub.outputs.names.workspaceFeedPrivateEndpoint, naming_hub.outputs.tokens.service, 'feed'), + '-${stampIndex}', + '' + ) + workspaceFeedResourceGroupName: replace( + replace( + naming_controlPlane.outputs.names.resourceGroup, + naming_controlPlane.outputs.tokens.service, + 'feedWorkspace' + ), + '-${stampIndex}', + '' + ) + workspaceFriendlyName: empty(workspaceFriendlyName) + ? replace( + replace(naming_controlPlane.outputs.names.workspaceFeed, '-${naming_controlPlane.outputs.tokens.service}', ''), + '-${stampIndex}', + '' + ) + : '${workspaceFriendlyName} (${virtualNetwork.location})' + workspaceGlobalName: replace( + replace( + replace(naming_controlPlane.outputs.names.workspaceGlobal, naming_controlPlane.outputs.tokens.service, 'global'), + '-${stampIndex}', + '' + ), + identifier, + virtualNetwork.tags.resourcePrefix + ) + workspaceGlobalNetworkInterfaceName: replace( + replace( + replace(naming_hub.outputs.names.workspaceGlobalNetworkInterface, naming_hub.outputs.tokens.service, 'global'), + '-${stampIndex}', + '' + ), + identifier, + virtualNetwork.tags.resourcePrefix + ) workspaceGlobalPrivateDnsZoneResourceId: '${privateDnsZoneResourceIdPrefix}${filter(tier3_hosts.outputs.privateDnsZones, name => startsWith(name, 'privatelink-global.wvd'))[0]}' - workspaceGlobalPrivateEndpointName: replace(replace(replace(naming_hub.outputs.names.workspaceGlobalPrivateEndpoint,naming_hub.outputs.tokens.service,'global'),'-${stampIndex}',''),identifier,virtualNetwork.tags.resourcePrefix) - workspaceGlobalResourceGroupName: replace(replace(replace(naming_controlPlane.outputs.names.resourceGroup,naming_controlPlane.outputs.tokens.service,'globalWorkspace'),'-${stampIndex}',''),identifier,virtualNetwork.tags.resourcePrefix) + workspaceGlobalPrivateEndpointName: replace( + replace( + replace(naming_hub.outputs.names.workspaceGlobalPrivateEndpoint, naming_hub.outputs.tokens.service, 'global'), + '-${stampIndex}', + '' + ), + identifier, + virtualNetwork.tags.resourcePrefix + ) + workspaceGlobalResourceGroupName: replace( + replace( + replace( + naming_controlPlane.outputs.names.resourceGroup, + naming_controlPlane.outputs.tokens.service, + 'globalWorkspace' + ), + '-${stampIndex}', + '' + ), + identifier, + virtualNetwork.tags.resourcePrefix + ) workspacePublicNetworkAccess: workspacePublicNetworkAccess } } @@ -642,7 +702,6 @@ module fslogix 'modules/fslogix/fslogix.bicep' = if (deployFslogix) { fslogixShareSizeInGB: fslogixShareSizeInGB fslogixStorageService: fslogixStorageService functionAppName: management.outputs.functionAppName - hostPoolType: hostPoolType keyVaultUri: tier3_hosts.outputs.keyVaultUri location: locationVirtualMachines managementVirtualMachineName: management.outputs.virtualMachineName @@ -686,6 +745,7 @@ module sessionHosts 'modules/sessionHosts/sessionHosts.bicep' = { deployFslogix: deployFslogix deploymentNameSuffix: deploymentNameSuffix deploymentUserAssignedIdentityClientId: management.outputs.deploymentUserAssignedIdentityClientId + deploymentUserAssignedIdentityPrincipalId: management.outputs.deploymentUserAssignedIdentityPrincipalId diskEncryptionSetResourceId: tier3_hosts.outputs.diskEncryptionSetResourceId diskSku: diskSku divisionRemainderValue: divisionRemainderValue @@ -696,11 +756,10 @@ module sessionHosts 'modules/sessionHosts/sessionHosts.bicep' = { enableAcceleratedNetworking: enableAcceleratedNetworking enableAvdInsights: enableAvdInsights enableRecoveryServices: recoveryServices - enableScalingTool: scalingTool environmentAbbreviation: environmentAbbreviation fslogixContainerType: fslogixContainerType - functionAppName: management.outputs.functionAppName hostPoolName: controlPlane.outputs.hostPoolName + hostPoolResourceId: controlPlane.outputs.hostPoolResourceId hostPoolType: hostPoolType identifier: identifier imageOffer: imageOffer @@ -708,22 +767,24 @@ module sessionHosts 'modules/sessionHosts/sessionHosts.bicep' = { imageSku: imageSku imageVersionResourceId: imageVersionResourceId location: locationVirtualMachines + logAnalyticsWorkspaceResourceId: management.outputs.logAnalyticsWorkspaceResourceId managementVirtualMachineName: management.outputs.virtualMachineName maxResourcesPerTemplateDeployment: maxResourcesPerTemplateDeployment mlzTags: tier3_hosts.outputs.mlzTags namingConvention: tier3_hosts.outputs.namingConvention - netAppFileShares: deployFslogix - ? fslogix.outputs.netAppShares - : [ - 'None' - ] + netAppFileShares: deployFslogix ? fslogix.outputs.netAppShares : [ + 'None' + ] organizationalUnitPath: organizationalUnitPath - pooledHostPool: pooledHostPool recoveryServicesVaultName: management.outputs.recoveryServicesVaultName resourceGroupControlPlane: rgs[0].outputs.name resourceGroupHosts: rgs[1].outputs.name resourceGroupManagement: rgs[2].outputs.name roleDefinitions: roleDefinitions + scalingWeekdaysOffPeakStartTime: scalingWeekdaysOffPeakStartTime + scalingWeekdaysPeakStartTime: scalingWeekdaysPeakStartTime + scalingWeekendsOffPeakStartTime: scalingWeekendsOffPeakStartTime + scalingWeekendsPeakStartTime: scalingWeekendsPeakStartTime securityPrincipalObjectIds: map(securityPrincipals, item => item.objectId) serviceToken: tier3_hosts.outputs.tokens.service sessionHostBatchCount: sessionHostBatchCount @@ -735,6 +796,7 @@ module sessionHosts 'modules/sessionHosts/sessionHosts.bicep' = { storageSuffix: storageSuffix subnetResourceId: tier3_hosts.outputs.subnets[0].id tags: tags + timeZone: tier3_hosts.outputs.locationProperties.timeZone virtualMachinePassword: virtualMachinePassword virtualMachineSize: virtualMachineSize virtualMachineUsername: virtualMachineUsername diff --git a/src/bicep/add-ons/azure-virtual-desktop/solution.json b/src/bicep/add-ons/azure-virtual-desktop/solution.json index e03845ad3..10c417380 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/solution.json +++ b/src/bicep/add-ons/azure-virtual-desktop/solution.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.31.34.60546", - "templateHash": "8432968336067453174" + "templateHash": "495118660330348734" } }, "parameters": { @@ -160,7 +160,7 @@ "type": "bool", "defaultValue": true, "metadata": { - "description": "Deploys the required resources to monitor the function app for the Scaling Tool and Auto Increase Premium File Share solutions." + "description": "Deploys the required resources to monitor the function app for the Auto Increase Premium File Share solutions." } }, "enableAvdInsights": { @@ -241,7 +241,7 @@ "type": "string", "defaultValue": "", "metadata": { - "description": "The subnet address prefix for the delegated subnet for the Azure Function App. This subnet is required for the Scaling Tool and the Auto Increase Premium File Share Quotas tool." + "description": "The subnet address prefix for the delegated subnet for the Azure Function App. This subnet is required for the Auto Increase Premium File Share Quotas tool." } }, "hostPoolPublicNetworkAccess": { @@ -258,15 +258,13 @@ }, "hostPoolType": { "type": "string", - "defaultValue": "Pooled DepthFirst", + "defaultValue": "Pooled", "allowedValues": [ - "Pooled DepthFirst", - "Pooled BreadthFirst", - "Personal Automatic", - "Personal Direct" + "Pooled", + "Personal" ], "metadata": { - "description": "These options specify the host pool type and depending on the type provides the load balancing options and assignment types." + "description": "The type of AVD host pool." } }, "hubAzureFirewallResourceId": { @@ -393,46 +391,32 @@ "description": "Enable backups to an Azure Recovery Services vault. For a pooled host pool this will enable backups on the Azure file share. For a personal host pool this will enable backups on the AVD sessions hosts." } }, - "scalingBeginPeakTime": { - "type": "string", - "defaultValue": "9:00", - "metadata": { - "description": "The time when session hosts will scale up and continue to stay on to support peak demand; Format 24 hours e.g. 9:00 for 9am" - } - }, - "scalingEndPeakTime": { + "scalingWeekdaysOffPeakStartTime": { "type": "string", "defaultValue": "17:00", "metadata": { - "description": "The time when session hosts will scale down and stay off to support low demand; Format 24 hours e.g. 17:00 for 5pm" + "description": "Off peak start time for weekdays in HH:mm format." } }, - "scalingLimitSecondsToForceLogOffUser": { + "scalingWeekdaysPeakStartTime": { "type": "string", - "defaultValue": "0", + "defaultValue": "09:00", "metadata": { - "description": "The number of seconds to wait before automatically signing out users. If set to 0 any session host that has user sessions will be left untouched" + "description": "Off peak start time for weekends in HH:mm format." } }, - "scalingMinimumNumberOfRdsh": { + "scalingWeekendsOffPeakStartTime": { "type": "string", - "defaultValue": "0", + "defaultValue": "17:00", "metadata": { - "description": "The minimum number of session host VMs to keep running during off-peak hours. The scaling tool will not work if all virtual machines are turned off and the Start VM On Connect solution is not enabled." + "description": "Peak start time for weekdays in HH:mm format." } }, - "scalingSessionThresholdPerCPU": { + "scalingWeekendsPeakStartTime": { "type": "string", - "defaultValue": "1", - "metadata": { - "description": "The maximum number of sessions per CPU that will be used as a threshold to determine when new session host VMs need to be started during peak hours" - } - }, - "scalingTool": { - "type": "bool", - "defaultValue": false, + "defaultValue": "09:00", "metadata": { - "description": "Deploys the required resources for the Scaling Tool. https://docs.microsoft.com/en-us/azure/virtual-desktop/scaling-automation-logic-apps" + "description": "Peak start time for weekends in HH:mm format." } }, "securityPrincipals": { @@ -608,11 +592,10 @@ }, "fileShares": "[variables('fileShareNames')[parameters('fslogixContainerType')]]", "netbios": "[split(parameters('domainName'), '.')[0]]", - "pooledHostPool": "[if(equals(split(parameters('hostPoolType'), ' ')[0], 'Pooled'), true(), false())]", "privateDnsZoneResourceIdPrefix": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Network/privateDnsZones/', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4])]", "resourceGroupServices": "[union(createArray('controlPlane', 'hosts', 'management'), if(variables('deployFslogix'), createArray('storage'), createArray()))]", "roleDefinitions": { - "DesktopVirtualizationPowerOnContributor": "489581de-a3bd-480d-9518-53dea7416b33", + "DesktopVirtualizationPowerOnOffContributor": "40c5ff49-9181-41f8-ae61-143b0e78555e", "DesktopVirtualizationUser": "1d18fff3-a72a-46b5-b4a9-0b38a3cd7e63", "Reader": "acdd72a7-3385-48ef-bd42-f606fba81ae7", "VirtualMachineUserLogin": "fb879df8-f326-4884-b1cf-06f3ad86be52" @@ -630,7 +613,7 @@ } ], "azureNetAppFiles": "[if(and(contains(parameters('fslogixStorageService'), 'AzureNetAppFiles'), not(empty(parameters('azureNetAppFilesSubnetAddressPrefix')))), createArray(createObject('name', 'AzureNetAppFiles', 'properties', createObject('addressPrefix', parameters('azureNetAppFilesSubnetAddressPrefix')))), createArray())]", - "functionApp": "[if(or(parameters('scalingTool'), equals(parameters('fslogixStorageService'), 'AzureFiles Premium')), createArray(createObject('name', 'FunctionAppOutbound', 'properties', createObject('addressPrefix', parameters('functionAppSubnetAddressPrefix')))), createArray())]" + "functionApp": "[if(equals(parameters('fslogixStorageService'), 'AzureFiles Premium'), createArray(createObject('name', 'FunctionAppOutbound', 'properties', createObject('addressPrefix', parameters('functionAppSubnetAddressPrefix')))), createArray())]" } }, "resources": [ @@ -686,7 +669,7 @@ "_generator": { "name": "bicep", "version": "0.31.34.60546", - "templateHash": "8357935203009683307" + "templateHash": "11992572431768878515" } }, "parameters": { @@ -1138,6 +1121,7 @@ "remoteApplicationGroups": "vdag", "resourceGroups": "rg", "routeTables": "rt", + "scalingPlans": "vdscaling", "storageAccounts": "st", "subnets": "snet", "userAssignedIdentities": "id", @@ -1206,6 +1190,8 @@ "recoveryServicesVaultPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, variables('resourceAbbreviations').recoveryServicesVaults)]", "resourceGroup": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').resourceGroups)]", "routeTable": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').routeTables)]", + "scalingPlan": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').scalingPlans)]", + "scalingPlanDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, variables('resourceAbbreviations').scalingPlans)]", "storageAccount": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').storageAccounts), parameters('networkName'), parameters('networkShortName'))]", "storageAccountDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').storageAccounts))]", "storageAccountBlobNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-blob', variables('resourceAbbreviations').storageAccounts))]", @@ -1292,7 +1278,7 @@ "_generator": { "name": "bicep", "version": "0.31.34.60546", - "templateHash": "8357935203009683307" + "templateHash": "11992572431768878515" } }, "parameters": { @@ -1744,6 +1730,7 @@ "remoteApplicationGroups": "vdag", "resourceGroups": "rg", "routeTables": "rt", + "scalingPlans": "vdscaling", "storageAccounts": "st", "subnets": "snet", "userAssignedIdentities": "id", @@ -1812,6 +1799,8 @@ "recoveryServicesVaultPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, variables('resourceAbbreviations').recoveryServicesVaults)]", "resourceGroup": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').resourceGroups)]", "routeTable": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').routeTables)]", + "scalingPlan": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').scalingPlans)]", + "scalingPlanDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, variables('resourceAbbreviations').scalingPlans)]", "storageAccount": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').storageAccounts), parameters('networkName'), parameters('networkShortName'))]", "storageAccountDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').storageAccounts))]", "storageAccountBlobNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-blob', variables('resourceAbbreviations').storageAccounts))]", @@ -1943,7 +1932,7 @@ "_generator": { "name": "bicep", "version": "0.31.34.60546", - "templateHash": "3242090622764524003" + "templateHash": "9467495710159860030" } }, "parameters": { @@ -2220,7 +2209,7 @@ "_generator": { "name": "bicep", "version": "0.31.34.60546", - "templateHash": "16255426588501076488" + "templateHash": "10924801470287352076" } }, "parameters": { @@ -2300,7 +2289,7 @@ "_generator": { "name": "bicep", "version": "0.31.34.60546", - "templateHash": "8357935203009683307" + "templateHash": "11992572431768878515" } }, "parameters": { @@ -2752,6 +2741,7 @@ "remoteApplicationGroups": "vdag", "resourceGroups": "rg", "routeTables": "rt", + "scalingPlans": "vdscaling", "storageAccounts": "st", "subnets": "snet", "userAssignedIdentities": "id", @@ -2820,6 +2810,8 @@ "recoveryServicesVaultPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, variables('resourceAbbreviations').recoveryServicesVaults)]", "resourceGroup": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').resourceGroups)]", "routeTable": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').routeTables)]", + "scalingPlan": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').scalingPlans)]", + "scalingPlanDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, variables('resourceAbbreviations').scalingPlans)]", "storageAccount": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').storageAccounts), parameters('networkName'), parameters('networkShortName'))]", "storageAccountDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').storageAccounts))]", "storageAccountBlobNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-blob', variables('resourceAbbreviations').storageAccounts))]", @@ -6451,9 +6443,6 @@ }, "mode": "Incremental", "parameters": { - "activeDirectorySolution": { - "value": "[parameters('activeDirectorySolution')]" - }, "avdObjectId": { "value": "[parameters('avdObjectId')]" }, @@ -6490,9 +6479,6 @@ "fslogixStorageService": { "value": "[parameters('fslogixStorageService')]" }, - "hostPoolType": { - "value": "[parameters('hostPoolType')]" - }, "locationVirtualMachines": { "value": "[parameters('locationVirtualMachines')]" }, @@ -6542,24 +6528,6 @@ "roleDefinitions": { "value": "[variables('roleDefinitions')]" }, - "scalingBeginPeakTime": { - "value": "[parameters('scalingBeginPeakTime')]" - }, - "scalingEndPeakTime": { - "value": "[parameters('scalingEndPeakTime')]" - }, - "scalingLimitSecondsToForceLogOffUser": { - "value": "[parameters('scalingLimitSecondsToForceLogOffUser')]" - }, - "scalingMinimumNumberOfRdsh": { - "value": "[parameters('scalingMinimumNumberOfRdsh')]" - }, - "scalingSessionThresholdPerCPU": { - "value": "[parameters('scalingSessionThresholdPerCPU')]" - }, - "scalingTool": { - "value": "[parameters('scalingTool')]" - }, "serviceToken": { "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value.service]" }, @@ -6595,13 +6563,10 @@ "_generator": { "name": "bicep", "version": "0.31.34.60546", - "templateHash": "5374213534709080562" + "templateHash": "12558774705370628238" } }, "parameters": { - "activeDirectorySolution": { - "type": "string" - }, "avdObjectId": { "type": "string" }, @@ -6638,9 +6603,6 @@ "fslogixStorageService": { "type": "string" }, - "hostPoolType": { - "type": "string" - }, "locationVirtualMachines": { "type": "string" }, @@ -6692,24 +6654,6 @@ "roleDefinitions": { "type": "object" }, - "scalingBeginPeakTime": { - "type": "string" - }, - "scalingEndPeakTime": { - "type": "string" - }, - "scalingLimitSecondsToForceLogOffUser": { - "type": "string" - }, - "scalingMinimumNumberOfRdsh": { - "type": "string" - }, - "scalingSessionThresholdPerCPU": { - "type": "string" - }, - "scalingTool": { - "type": "bool" - }, "serviceToken": { "type": "string" }, @@ -6740,7 +6684,7 @@ }, "variables": { "hostPoolName": "[parameters('namingConvention').hostPool]", - "roleAssignments": "[union(createArray(createObject('roleDefinitionId', '86240b0e-9422-4c43-887b-b61143f32ba8', 'resourceGroup', parameters('resourceGroupControlPlane'), 'subscription', subscription().subscriptionId), createObject('roleDefinitionId', '2ad6aaab-ead9-4eaa-8ac5-da422f562408', 'resourceGroup', parameters('resourceGroupControlPlane'), 'subscription', subscription().subscriptionId), createObject('roleDefinitionId', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c', 'resourceGroup', parameters('resourceGroupManagement'), 'subscription', subscription().subscriptionId)), if(parameters('deployFslogix'), createArray(createObject('roleDefinitionId', '17d1049b-9a84-46fb-8f53-869881c3d3ab', 'resourceGroup', parameters('resourceGroupStorage'), 'subscription', subscription().subscriptionId)), createArray()))]", + "resourceGroups": "[union(createArray(parameters('resourceGroupControlPlane'), parameters('resourceGroupHosts'), parameters('resourceGroupManagement')), if(parameters('deployFslogix'), createArray(parameters('resourceGroupStorage')), createArray()))]", "userAssignedIdentityNamePrefix": "[parameters('namingConvention').userAssignedIdentity]", "virtualNetworkName": "[split(parameters('subnetResourceId'), '/')[8]]", "virtualNetworkResourceGroupName": "[split(parameters('subnetResourceId'), '/')[4]]" @@ -6749,9 +6693,9 @@ { "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2022-04-01", - "name": "[guid(parameters('avdObjectId'), parameters('roleDefinitions').DesktopVirtualizationPowerOnContributor, subscription().id)]", + "name": "[guid(parameters('avdObjectId'), parameters('roleDefinitions').DesktopVirtualizationPowerOnOffContributor, subscription().id)]", "properties": { - "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitions').DesktopVirtualizationPowerOnContributor)]", + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitions').DesktopVirtualizationPowerOnOffContributor)]", "principalId": "[parameters('avdObjectId')]" } }, @@ -7096,13 +7040,12 @@ { "copy": { "name": "roleAssignments_deployment", - "count": "[length(range(0, length(variables('roleAssignments'))))]" + "count": "[length(range(0, length(variables('resourceGroups'))))]" }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('deploy-role-assignment-{0}-{1}', range(0, length(variables('roleAssignments')))[copyIndex()], parameters('deploymentNameSuffix'))]", - "subscriptionId": "[variables('roleAssignments')[range(0, length(variables('roleAssignments')))[copyIndex()]].subscription]", - "resourceGroup": "[variables('roleAssignments')[range(0, length(variables('roleAssignments')))[copyIndex()]].resourceGroup]", + "name": "[format('deploy-role-assignment-{0}-{1}', range(0, length(variables('resourceGroups')))[copyIndex()], parameters('deploymentNameSuffix'))]", + "resourceGroup": "[variables('resourceGroups')[range(0, length(variables('resourceGroups')))[copyIndex()]]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -7116,7 +7059,67 @@ "value": "ServicePrincipal" }, "roleDefinitionId": { - "value": "[variables('roleAssignments')[range(0, length(variables('roleAssignments')))[copyIndex()]].roleDefinitionId]" + "value": "acdd72a7-3385-48ef-bd42-f606fba81ae7" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.34.60546", + "templateHash": "1315528727979495191" + } + }, + "parameters": { + "principalId": { + "type": "string" + }, + "principalType": { + "type": "string" + }, + "roleDefinitionId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(parameters('principalId'), parameters('roleDefinitionId'), resourceGroup().id)]", + "properties": { + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitionId'))]", + "principalId": "[parameters('principalId')]", + "principalType": "[parameters('principalType')]" + } + } + ] + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-id-deployment-{0}', parameters('deploymentNameSuffix')))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('deploy-role-assignment-{0}', parameters('deploymentNameSuffix'))]", + "resourceGroup": "[parameters('resourceGroupStorage')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "principalId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-id-deployment-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.principalId.value]" + }, + "principalType": { + "value": "ServicePrincipal" + }, + "roleDefinitionId": { + "value": "17d1049b-9a84-46fb-8f53-869881c3d3ab" } }, "template": { @@ -7169,6 +7172,9 @@ }, "mode": "Incremental", "parameters": { + "deploymentUserAssignedIdentityPrincipalId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-id-deployment-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.principalId.value]" + }, "deploymentUserAssignedIdentityResourceId": { "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-id-deployment-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceId.value]" }, @@ -7237,10 +7243,13 @@ "_generator": { "name": "bicep", "version": "0.31.34.60546", - "templateHash": "7486697218882140009" + "templateHash": "16276650963509010370" } }, "parameters": { + "deploymentUserAssignedIdentityPrincipalId": { + "type": "string" + }, "deploymentUserAssignedIdentityResourceId": { "type": "string" }, @@ -7495,6 +7504,20 @@ "dependsOn": [ "[resourceId('Microsoft.Compute/virtualMachines', parameters('virtualMachineName'))]" ] + }, + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Compute/virtualMachines/{0}', parameters('virtualMachineName'))]", + "name": "[guid(parameters('deploymentUserAssignedIdentityPrincipalId'), 'a959dbd1-f747-45e3-8ba6-dd80f235f97c', resourceId('Microsoft.Compute/virtualMachines', parameters('virtualMachineName')))]", + "properties": { + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c')]", + "principalId": "[parameters('deploymentUserAssignedIdentityPrincipalId')]", + "principalType": "ServicePrincipal" + }, + "dependsOn": [ + "[resourceId('Microsoft.Compute/virtualMachines', parameters('virtualMachineName'))]" + ] } ], "outputs": { @@ -7920,7 +7943,7 @@ } }, { - "condition": "[or(parameters('scalingTool'), equals(parameters('fslogixStorageService'), 'AzureFiles Premium'))]", + "condition": "[and(parameters('deployFslogix'), equals(parameters('fslogixStorageService'), 'AzureFiles Premium'))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('deploy-function-app-{0}', parameters('deploymentNameSuffix'))]", @@ -7934,9 +7957,6 @@ "delegatedSubnetResourceId": { "value": "[filter(parameters('subnets'), lambda('subnet', contains(lambdaVariables('subnet').name, 'FunctionAppOutbound')))[0].id]" }, - "deployFslogix": { - "value": "[parameters('deployFslogix')]" - }, "deploymentNameSuffix": { "value": "[parameters('deploymentNameSuffix')]" }, @@ -7970,27 +7990,9 @@ "resourceGroupControlPlane": { "value": "[parameters('resourceGroupControlPlane')]" }, - "resourceGroupHosts": { - "value": "[parameters('resourceGroupHosts')]" - }, "resourceGroupStorage": { "value": "[parameters('resourceGroupStorage')]" }, - "scalingBeginPeakTime": { - "value": "[parameters('scalingBeginPeakTime')]" - }, - "scalingEndPeakTime": { - "value": "[parameters('scalingEndPeakTime')]" - }, - "scalingLimitSecondsToForceLogOffUser": { - "value": "[parameters('scalingLimitSecondsToForceLogOffUser')]" - }, - "scalingMinimumNumberOfRdsh": { - "value": "[parameters('scalingMinimumNumberOfRdsh')]" - }, - "scalingSessionThresholdPerCPU": { - "value": "[parameters('scalingSessionThresholdPerCPU')]" - }, "serviceToken": { "value": "[parameters('serviceToken')]" }, @@ -8011,16 +8013,13 @@ "_generator": { "name": "bicep", "version": "0.31.34.60546", - "templateHash": "13808224309643091125" + "templateHash": "13334192291900135747" } }, "parameters": { "delegatedSubnetResourceId": { "type": "string" }, - "deployFslogix": { - "type": "bool" - }, "deploymentNameSuffix": { "type": "string" }, @@ -8062,27 +8061,9 @@ "resourceGroupControlPlane": { "type": "string" }, - "resourceGroupHosts": { - "type": "string" - }, "resourceGroupStorage": { "type": "string" }, - "scalingBeginPeakTime": { - "type": "string" - }, - "scalingEndPeakTime": { - "type": "string" - }, - "scalingLimitSecondsToForceLogOffUser": { - "type": "string" - }, - "scalingMinimumNumberOfRdsh": { - "type": "string" - }, - "scalingSessionThresholdPerCPU": { - "type": "string" - }, "serviceToken": { "type": "string" }, @@ -8100,19 +8081,32 @@ "cloudSuffix": "[replace(replace(environment().resourceManager, 'https://management.', ''), '/', '')]", "functionAppKeyword": "[if(or(equals(environment().name, 'AzureCloud'), equals(environment().name, 'AzureUSGovernment')), 'azurewebsites', 'appservice')]", "functionAppScmPrivateDnsZoneResourceId": "[format('{0}scm.{1}', parameters('privateDnsZoneResourceIdPrefix'), filter(parameters('privateDnsZones'), lambda('name', contains(lambdaVariables('name'), variables('functionAppKeyword'))))[0])]", - "roleAssignments": "[union(createArray(createObject('roleDefinitionId', '40c5ff49-9181-41f8-ae61-143b0e78555e', 'scope', parameters('resourceGroupControlPlane')), createObject('roleDefinitionId', '40c5ff49-9181-41f8-ae61-143b0e78555e', 'scope', parameters('resourceGroupHosts'))), if(parameters('deployFslogix'), createArray(createObject('roleDefinitionId', '17d1049b-9a84-46fb-8f53-869881c3d3ab', 'scope', parameters('resourceGroupStorage'))), createArray()))]", - "service": "mgmt", - "storagePrivateDnsZoneResourceIds": [ - "[format('{0}{1}', parameters('privateDnsZoneResourceIdPrefix'), filter(parameters('privateDnsZones'), lambda('name', contains(lambdaVariables('name'), 'blob')))[0])]", - "[format('{0}{1}', parameters('privateDnsZoneResourceIdPrefix'), filter(parameters('privateDnsZones'), lambda('name', contains(lambdaVariables('name'), 'file')))[0])]", - "[format('{0}{1}', parameters('privateDnsZoneResourceIdPrefix'), filter(parameters('privateDnsZones'), lambda('name', contains(lambdaVariables('name'), 'queue')))[0])]", - "[format('{0}{1}', parameters('privateDnsZoneResourceIdPrefix'), filter(parameters('privateDnsZones'), lambda('name', contains(lambdaVariables('name'), 'table')))[0])]" - ], + "service": "aipfsq", "storageSubResources": [ - "blob", - "file", - "queue", - "table" + { + "name": "blob", + "id": "[format('{0}{1}', parameters('privateDnsZoneResourceIdPrefix'), filter(parameters('privateDnsZones'), lambda('name', contains(lambdaVariables('name'), 'blob')))[0])]", + "nic": "[parameters('namingConvention').storageAccountBlobNetworkInterface]", + "pe": "[parameters('namingConvention').storageAccountBlobPrivateEndpoint]" + }, + { + "name": "file", + "id": "[format('{0}{1}', parameters('privateDnsZoneResourceIdPrefix'), filter(parameters('privateDnsZones'), lambda('name', contains(lambdaVariables('name'), 'file')))[0])]", + "nic": "[parameters('namingConvention').storageAccountFileNetworkInterface]", + "pe": "[parameters('namingConvention').storageAccountFilePrivateEndpoint]" + }, + { + "name": "queue", + "id": "[format('{0}{1}', parameters('privateDnsZoneResourceIdPrefix'), filter(parameters('privateDnsZones'), lambda('name', contains(lambdaVariables('name'), 'queue')))[0])]", + "nic": "[parameters('namingConvention').storageAccountQueueNetworkInterface]", + "pe": "[parameters('namingConvention').storageAccountQueuePrivateEndpoint]" + }, + { + "name": "table", + "id": "[format('{0}{1}', parameters('privateDnsZoneResourceIdPrefix'), filter(parameters('privateDnsZones'), lambda('name', contains(lambdaVariables('name'), 'table')))[0])]", + "nic": "[parameters('namingConvention').storageAccountTableNetworkInterface]", + "pe": "[parameters('namingConvention').storageAccountTablePrivateEndpoint]" + } ] }, "resources": [ @@ -8126,7 +8120,7 @@ { "type": "Microsoft.KeyVault/vaults", "apiVersion": "2022-07-01", - "name": "[format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, variables('service'), 'cmk'), resourceGroup().id))]", + "name": "[format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id))]", "location": "[parameters('location')]", "tags": "[coalesce(tryGet(parameters('tags'), 'Microsoft.KeyVault/vaults'), createObject())]", "properties": { @@ -8154,8 +8148,8 @@ { "type": "Microsoft.Authorization/roleAssignments", "apiVersion": "2020-04-01-preview", - "scope": "[format('Microsoft.KeyVault/vaults/{0}', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, variables('service'), 'cmk'), resourceGroup().id)))]", - "name": "[guid(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', replace(parameters('namingConvention').userAssignedIdentity, parameters('serviceToken'), variables('service'))), 'e147488a-f6f5-4113-8e2d-b22465e65bf6', resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, variables('service'), 'cmk'), resourceGroup().id))))]", + "scope": "[format('Microsoft.KeyVault/vaults/{0}', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id)))]", + "name": "[guid(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', replace(parameters('namingConvention').userAssignedIdentity, parameters('serviceToken'), variables('service'))), 'e147488a-f6f5-4113-8e2d-b22465e65bf6', resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id))))]", "properties": { "principalId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', replace(parameters('namingConvention').userAssignedIdentity, parameters('serviceToken'), variables('service'))), '2023-01-31').principalId]", "principalType": "ServicePrincipal", @@ -8163,7 +8157,7 @@ }, "dependsOn": [ "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', replace(parameters('namingConvention').userAssignedIdentity, parameters('serviceToken'), variables('service')))]", - "[resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, variables('service'), 'cmk'), resourceGroup().id)))]" + "[resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id)))]" ] }, { @@ -8178,7 +8172,7 @@ { "name": "[replace(parameters('namingConvention').keyVaultPrivateEndpoint, parameters('serviceToken'), variables('service'))]", "properties": { - "privateLinkServiceId": "[resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, variables('service'), 'cmk'), resourceGroup().id)))]", + "privateLinkServiceId": "[resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id)))]", "groupIds": [ "vault" ] @@ -8190,13 +8184,13 @@ } }, "dependsOn": [ - "[resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, variables('service'), 'cmk'), resourceGroup().id)))]" + "[resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id)))]" ] }, { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", "apiVersion": "2021-08-01", - "name": "[format('{0}/{1}', replace(parameters('namingConvention').keyVaultPrivateEndpoint, parameters('serviceToken'), variables('service')), format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, variables('service'), 'cmk'), resourceGroup().id)))]", + "name": "[format('{0}/{1}', replace(parameters('namingConvention').keyVaultPrivateEndpoint, parameters('serviceToken'), variables('service')), format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id)))]", "properties": { "privateDnsZoneConfigs": [ { @@ -8209,13 +8203,13 @@ }, "dependsOn": [ "[resourceId('Microsoft.Network/privateEndpoints', replace(parameters('namingConvention').keyVaultPrivateEndpoint, parameters('serviceToken'), variables('service')))]", - "[resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, variables('service'), 'cmk'), resourceGroup().id)))]" + "[resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id)))]" ] }, { "type": "Microsoft.KeyVault/vaults/keys", "apiVersion": "2022-07-01", - "name": "[format('{0}/{1}', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, variables('service'), 'cmk'), resourceGroup().id)), 'StorageEncryptionKey')]", + "name": "[format('{0}/{1}', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id)), 'StorageEncryptionKey')]", "properties": { "attributes": { "enabled": true @@ -8247,7 +8241,7 @@ } }, "dependsOn": [ - "[resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, variables('service'), 'cmk'), resourceGroup().id)))]" + "[resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id)))]" ] }, { @@ -8283,7 +8277,7 @@ }, "requireInfrastructureEncryption": true, "keyvaultproperties": { - "keyvaulturi": "[reference(resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, variables('service'), 'cmk'), resourceGroup().id))), '2022-07-01').vaultUri]", + "keyvaulturi": "[reference(resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id))), '2022-07-01').vaultUri]", "keyname": "StorageEncryptionKey" }, "services": { @@ -8318,12 +8312,12 @@ "supportsHttpsTrafficOnly": true }, "dependsOn": [ - "[resourceId('Microsoft.KeyVault/vaults/keys', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, variables('service'), 'cmk'), resourceGroup().id)), 'StorageEncryptionKey')]", - "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', replace(parameters('namingConvention').keyVaultPrivateEndpoint, parameters('serviceToken'), variables('service')), format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, variables('service'), 'cmk'), resourceGroup().id)))]", + "[resourceId('Microsoft.KeyVault/vaults/keys', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id)), 'StorageEncryptionKey')]", + "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', replace(parameters('namingConvention').keyVaultPrivateEndpoint, parameters('serviceToken'), variables('service')), format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id)))]", "[resourceId('Microsoft.Network/privateEndpoints', replace(parameters('namingConvention').keyVaultPrivateEndpoint, parameters('serviceToken'), variables('service')))]", - "[extensionResourceId(resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, variables('service'), 'cmk'), resourceGroup().id))), 'Microsoft.Authorization/roleAssignments', guid(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', replace(parameters('namingConvention').userAssignedIdentity, parameters('serviceToken'), variables('service'))), 'e147488a-f6f5-4113-8e2d-b22465e65bf6', resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, variables('service'), 'cmk'), resourceGroup().id)))))]", + "[extensionResourceId(resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id))), 'Microsoft.Authorization/roleAssignments', guid(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', replace(parameters('namingConvention').userAssignedIdentity, parameters('serviceToken'), variables('service'))), 'e147488a-f6f5-4113-8e2d-b22465e65bf6', resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id)))))]", "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', replace(parameters('namingConvention').userAssignedIdentity, parameters('serviceToken'), variables('service')))]", - "[resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, variables('service'), 'cmk'), resourceGroup().id)))]" + "[resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id)))]" ] }, { @@ -8341,18 +8335,18 @@ }, "type": "Microsoft.Network/privateEndpoints", "apiVersion": "2023-04-01", - "name": "[replace(parameters('namingConvention').storageAccountPrivateEndpoint, format('{0}-{1}', parameters('serviceToken'), parameters('resourceAbbreviations').storageAccounts), format('{0}-{1}-scale', variables('storageSubResources')[copyIndex()], parameters('resourceAbbreviations').storageAccounts))]", + "name": "[replace(variables('storageSubResources')[copyIndex()].pe, parameters('serviceToken'), variables('service'))]", "location": "[parameters('location')]", "tags": "[coalesce(tryGet(parameters('tags'), 'Microsoft.Network/privateEndpoints'), createObject())]", "properties": { - "customNetworkInterfaceName": "[replace(parameters('namingConvention').storageAccountNetworkInterface, format('{0}-{1}', parameters('serviceToken'), parameters('resourceAbbreviations').storageAccounts), format('{0}-{1}-scale', variables('storageSubResources')[copyIndex()], parameters('resourceAbbreviations').storageAccounts))]", + "customNetworkInterfaceName": "[replace(variables('storageSubResources')[copyIndex()].nic, parameters('serviceToken'), variables('service'))]", "privateLinkServiceConnections": [ { - "name": "[replace(parameters('namingConvention').storageAccountPrivateEndpoint, format('{0}-{1}', parameters('serviceToken'), parameters('resourceAbbreviations').storageAccounts), format('{0}-{1}-scale', variables('storageSubResources')[copyIndex()], parameters('resourceAbbreviations').storageAccounts))]", + "name": "[replace(variables('storageSubResources')[copyIndex()].pe, parameters('serviceToken'), variables('service'))]", "properties": { "privateLinkServiceId": "[resourceId('Microsoft.Storage/storageAccounts', uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id))]", "groupIds": [ - "[variables('storageSubResources')[copyIndex()]]" + "[variables('storageSubResources')[copyIndex()].name]" ] } } @@ -8372,19 +8366,19 @@ }, "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", "apiVersion": "2021-08-01", - "name": "[format('{0}/{1}', replace(parameters('namingConvention').storageAccountPrivateEndpoint, format('{0}-{1}', parameters('serviceToken'), parameters('resourceAbbreviations').storageAccounts), format('{0}-{1}-scale', variables('storageSubResources')[copyIndex()], parameters('resourceAbbreviations').storageAccounts)), uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id))]", + "name": "[format('{0}/{1}', replace(variables('storageSubResources')[copyIndex()].pe, parameters('serviceToken'), variables('service')), uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id))]", "properties": { "privateDnsZoneConfigs": [ { "name": "ipconfig1", "properties": { - "privateDnsZoneId": "[variables('storagePrivateDnsZoneResourceIds')[copyIndex()]]" + "privateDnsZoneId": "[variables('storageSubResources')[copyIndex()].id]" } } ] }, "dependsOn": [ - "[resourceId('Microsoft.Network/privateEndpoints', replace(parameters('namingConvention').storageAccountPrivateEndpoint, format('{0}-{1}', parameters('serviceToken'), parameters('resourceAbbreviations').storageAccounts), format('{0}-{1}-scale', variables('storageSubResources')[copyIndex()], parameters('resourceAbbreviations').storageAccounts)))]", + "[resourceId('Microsoft.Network/privateEndpoints', replace(variables('storageSubResources')[copyIndex()].pe, parameters('serviceToken'), variables('service')))]", "[resourceId('Microsoft.Storage/storageAccounts', uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id))]" ] }, @@ -8459,7 +8453,7 @@ "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', replace(parameters('namingConvention').appServicePlan, parameters('serviceToken'), variables('service')))]", "siteConfig": { "alwaysOn": true, - "appSettings": "[union(createArray(createObject('name', 'AzureWebJobsStorage__blobServiceUri', 'value', format('https://{0}.blob.{1}', uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id), environment().suffixes.storage)), createObject('name', 'AzureWebJobsStorage__credential', 'value', 'managedidentity'), createObject('name', 'AzureWebJobsStorage__queueServiceUri', 'value', format('https://{0}.queue.{1}', uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id), environment().suffixes.storage)), createObject('name', 'AzureWebJobsStorage__tableServiceUri', 'value', format('https://{0}.table.{1}', uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id), environment().suffixes.storage)), createObject('name', 'FUNCTIONS_EXTENSION_VERSION', 'value', '~4'), createObject('name', 'FUNCTIONS_WORKER_RUNTIME', 'value', 'powershell'), createObject('name', 'WEBSITE_LOAD_USER_PROFILE', 'value', '1'), createObject('name', 'BeginPeakTime', 'value', parameters('scalingBeginPeakTime')), createObject('name', 'EndPeakTime', 'value', parameters('scalingEndPeakTime')), createObject('name', 'EnvironmentName', 'value', environment().name), createObject('name', 'FileShareName', 'value', 'profile-containers'), createObject('name', 'HostPoolName', 'value', parameters('hostPoolName')), createObject('name', 'HostPoolResourceGroupName', 'value', parameters('resourceGroupControlPlane')), createObject('name', 'LimitSecondsToForceLogOffUser', 'value', parameters('scalingLimitSecondsToForceLogOffUser')), createObject('name', 'LogOffMessageBody', 'value', 'This session is about to be logged off. Please save your work.'), createObject('name', 'LogOffMessageTitle', 'value', 'Session Log Off'), createObject('name', 'MaintenanceTagName', 'value', 'Maintenance'), createObject('name', 'MinimumNumberOfRDSH', 'value', parameters('scalingMinimumNumberOfRdsh')), createObject('name', 'ResourceGroupName', 'value', parameters('resourceGroupStorage')), createObject('name', 'ResourceManagerUrl', 'value', if(endsWith(environment().resourceManager, '/'), environment().resourceManager, format('{0}/', environment().resourceManager))), createObject('name', 'SessionThresholdPerCPU', 'value', parameters('scalingSessionThresholdPerCPU')), createObject('name', 'StorageSuffix', 'value', environment().suffixes.storage), createObject('name', 'SubscriptionId', 'value', subscription().subscriptionId), createObject('name', 'TenantId', 'value', subscription().tenantId), createObject('name', 'TimeDifference', 'value', parameters('timeDifference'))), if(parameters('enableApplicationInsights'), createArray(createObject('name', 'APPLICATIONINSIGHTS_CONNECTION_STRING', 'value', reference(resourceId('Microsoft.Insights/components', replace(parameters('namingConvention').applicationInsights, parameters('serviceToken'), variables('service'))), '2020-02-02').ConnectionString)), createArray()))]", + "appSettings": "[union(createArray(createObject('name', 'AzureWebJobsStorage__blobServiceUri', 'value', format('https://{0}.blob.{1}', uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id), environment().suffixes.storage)), createObject('name', 'AzureWebJobsStorage__credential', 'value', 'managedidentity'), createObject('name', 'AzureWebJobsStorage__queueServiceUri', 'value', format('https://{0}.queue.{1}', uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id), environment().suffixes.storage)), createObject('name', 'AzureWebJobsStorage__tableServiceUri', 'value', format('https://{0}.table.{1}', uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id), environment().suffixes.storage)), createObject('name', 'FUNCTIONS_EXTENSION_VERSION', 'value', '~4'), createObject('name', 'FUNCTIONS_WORKER_RUNTIME', 'value', 'powershell'), createObject('name', 'WEBSITE_LOAD_USER_PROFILE', 'value', '1'), createObject('name', 'EnvironmentName', 'value', environment().name), createObject('name', 'FileShareName', 'value', 'profile-containers'), createObject('name', 'HostPoolName', 'value', parameters('hostPoolName')), createObject('name', 'HostPoolResourceGroupName', 'value', parameters('resourceGroupControlPlane')), createObject('name', 'LogOffMessageBody', 'value', 'This session is about to be logged off. Please save your work.'), createObject('name', 'LogOffMessageTitle', 'value', 'Session Log Off'), createObject('name', 'MaintenanceTagName', 'value', 'Maintenance'), createObject('name', 'ResourceGroupName', 'value', parameters('resourceGroupStorage')), createObject('name', 'ResourceManagerUrl', 'value', if(endsWith(environment().resourceManager, '/'), environment().resourceManager, format('{0}/', environment().resourceManager))), createObject('name', 'StorageSuffix', 'value', environment().suffixes.storage), createObject('name', 'SubscriptionId', 'value', subscription().subscriptionId), createObject('name', 'TenantId', 'value', subscription().tenantId), createObject('name', 'TimeDifference', 'value', parameters('timeDifference'))), if(parameters('enableApplicationInsights'), createArray(createObject('name', 'APPLICATIONINSIGHTS_CONNECTION_STRING', 'value', reference(resourceId('Microsoft.Insights/components', replace(parameters('namingConvention').applicationInsights, parameters('serviceToken'), variables('service'))), '2020-02-02').ConnectionString)), createArray()))]", "cors": { "allowedOrigins": [ "[format('{0}', environment().portal)]", @@ -8611,14 +8605,10 @@ ] }, { - "copy": { - "name": "roleAssignments_resourceGroups", - "count": "[length(range(0, length(variables('roleAssignments'))))]" - }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('set-role-assignment-{0}-{1}', range(0, length(variables('roleAssignments')))[copyIndex()], parameters('deploymentNameSuffix'))]", - "resourceGroup": "[variables('roleAssignments')[range(0, length(variables('roleAssignments')))[copyIndex()]].scope]", + "name": "[format('set-role-assignment-{0}', parameters('deploymentNameSuffix'))]", + "resourceGroup": "[parameters('resourceGroupStorage')]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -8632,7 +8622,7 @@ "value": "ServicePrincipal" }, "roleDefinitionId": { - "value": "[variables('roleAssignments')[range(0, length(variables('roleAssignments')))[copyIndex()]].roleDefinitionId]" + "value": "17d1049b-9a84-46fb-8f53-869881c3d3ab" } }, "template": { @@ -8820,7 +8810,7 @@ ] }, { - "condition": "[and(parameters('recoveryServices'), or(and(and(contains(parameters('activeDirectorySolution'), 'DomainServices'), contains(parameters('hostPoolType'), 'Pooled')), contains(parameters('fslogixStorageService'), 'AzureFiles')), contains(parameters('hostPoolType'), 'Personal')))]", + "condition": "[parameters('recoveryServices')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('deploy-rsv-{0}', parameters('deploymentNameSuffix'))]", @@ -9108,7 +9098,7 @@ }, "functionAppName": { "type": "string", - "value": "[if(or(parameters('scalingTool'), equals(parameters('fslogixStorageService'), 'AzureFiles Premium')), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-function-app-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.functionAppName.value, '')]" + "value": "[if(equals(parameters('fslogixStorageService'), 'AzureFiles Premium'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-function-app-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.functionAppName.value, '')]" }, "logAnalyticsWorkspaceName": { "type": "string", @@ -9120,7 +9110,7 @@ }, "recoveryServicesVaultName": { "type": "string", - "value": "[if(and(parameters('recoveryServices'), or(and(and(contains(parameters('activeDirectorySolution'), 'DomainServices'), contains(parameters('hostPoolType'), 'Pooled')), contains(parameters('fslogixStorageService'), 'AzureFiles')), contains(parameters('hostPoolType'), 'Personal'))), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-rsv-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.name.value, '')]" + "value": "[if(parameters('recoveryServices'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-rsv-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.name.value, '')]" }, "virtualMachineName": { "type": "string", @@ -9134,9 +9124,9 @@ } }, "dependsOn": [ + "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', variables('resourceGroupServices')[0], parameters('deploymentNameSuffix')))]", "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', variables('resourceGroupServices')[1], parameters('deploymentNameSuffix')))]", "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', variables('resourceGroupServices')[2], parameters('deploymentNameSuffix')))]", - "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', variables('resourceGroupServices')[0], parameters('deploymentNameSuffix')))]", "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix')))]" ] }, @@ -9169,6 +9159,9 @@ "deploymentUserAssignedIdentityClientId": { "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.deploymentUserAssignedIdentityClientId.value]" }, + "deploymentUserAssignedIdentityPrincipalId": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.deploymentUserAssignedIdentityPrincipalId.value]" + }, "desktopFriendlyName": "[if(empty(parameters('desktopFriendlyName')), createObject('value', string(parameters('stampIndex'))), createObject('value', parameters('desktopFriendlyName')))]", "diskSku": { "value": "[parameters('diskSku')]" @@ -9254,7 +9247,7 @@ "_generator": { "name": "bicep", "version": "0.31.34.60546", - "templateHash": "7947199422402016062" + "templateHash": "16583821302504592892" } }, "parameters": { @@ -9276,6 +9269,9 @@ "deploymentUserAssignedIdentityClientId": { "type": "string" }, + "deploymentUserAssignedIdentityPrincipalId": { + "type": "string" + }, "desktopFriendlyName": { "type": "string" }, @@ -9390,6 +9386,9 @@ "customRdpProperty": { "value": "[parameters('customRdpProperty')]" }, + "deploymentUserAssignedIdentityPrincipalId": { + "value": "[parameters('deploymentUserAssignedIdentityPrincipalId')]" + }, "diskSku": { "value": "[parameters('diskSku')]" }, @@ -9467,7 +9466,7 @@ "_generator": { "name": "bicep", "version": "0.31.34.60546", - "templateHash": "1177554211568337094" + "templateHash": "9719336724197777662" } }, "parameters": { @@ -9477,6 +9476,9 @@ "avdPrivateDnsZoneResourceId": { "type": "string" }, + "deploymentUserAssignedIdentityPrincipalId": { + "type": "string" + }, "customImageId": { "type": "string" }, @@ -9558,7 +9560,7 @@ } }, "variables": { - "customRdpProperty_Complete": "[if(contains(parameters('activeDirectorySolution'), 'MicrosoftEntraId'), format('{0}targetisaadjoined:i:1;enablerdsaadauth:i:1;', parameters('customRdpProperty')), parameters('customRdpProperty'))]" + "customRdpProperty_Complete": "[if(contains(parameters('activeDirectorySolution'), 'MicrosoftEntraId'), format('{0}enablerdsaadauth:i:1;', parameters('customRdpProperty')), parameters('customRdpProperty'))]" }, "resources": [ { @@ -9569,10 +9571,10 @@ "tags": "[union(createObject('cm-resource-parent', format('{0}}}/resourceGroups/{1}/providers/Microsoft.DesktopVirtualization/hostpools/{2}', subscription().id, resourceGroup().name, parameters('hostPoolName'))), if(contains(parameters('tags'), 'Microsoft.DesktopVirtualization/hostPools'), parameters('tags')['Microsoft.DesktopVirtualization/hostPools'], createObject()), parameters('mlzTags'))]", "properties": { "customRdpProperty": "[variables('customRdpProperty_Complete')]", - "hostPoolType": "[split(parameters('hostPoolType'), ' ')[0]]", - "loadBalancerType": "[if(contains(parameters('hostPoolType'), 'Pooled'), split(parameters('hostPoolType'), ' ')[1], 'Persistent')]", + "hostPoolType": "[parameters('hostPoolType')]", + "loadBalancerType": "[if(equals(parameters('hostPoolType'), 'Pooled'), 'DepthFirst', 'Persistent')]", "maxSessionLimit": "[parameters('maxSessionLimit')]", - "personalDesktopAssignmentType": "[if(contains(parameters('hostPoolType'), 'Personal'), split(parameters('hostPoolType'), ' ')[1], null())]", + "personalDesktopAssignmentType": "[if(equals(parameters('hostPoolType'), 'Personal'), 'Automatic', null())]", "preferredAppGroupType": "Desktop", "publicNetworkAccess": "[parameters('hostPoolPublicNetworkAccess')]", "registrationInfo": { @@ -9629,6 +9631,20 @@ "[resourceId('Microsoft.Network/privateEndpoints', parameters('hostPoolPrivateEndpointName'))]" ] }, + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.DesktopVirtualization/hostPools/{0}', parameters('hostPoolName'))]", + "name": "[guid(parameters('deploymentUserAssignedIdentityPrincipalId'), '2ad6aaab-ead9-4eaa-8ac5-da422f562408', resourceId('Microsoft.DesktopVirtualization/hostPools', parameters('hostPoolName')))]", + "properties": { + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', '2ad6aaab-ead9-4eaa-8ac5-da422f562408')]", + "principalId": "[parameters('deploymentUserAssignedIdentityPrincipalId')]", + "principalType": "ServicePrincipal" + }, + "dependsOn": [ + "[resourceId('Microsoft.DesktopVirtualization/hostPools', parameters('hostPoolName'))]" + ] + }, { "condition": "[parameters('enableAvdInsights')]", "type": "Microsoft.Insights/diagnosticSettings", @@ -9679,6 +9695,9 @@ "deploymentUserAssignedIdentityClientId": { "value": "[parameters('deploymentUserAssignedIdentityClientId')]" }, + "deploymentUserAssignedIdentityPrincipalId": { + "value": "[parameters('deploymentUserAssignedIdentityPrincipalId')]" + }, "desktopApplicationGroupName": { "value": "[replace(parameters('namingConvention').applicationGroup, parameters('serviceToken'), 'desktop')]" }, @@ -9720,7 +9739,7 @@ "_generator": { "name": "bicep", "version": "0.31.34.60546", - "templateHash": "17578802300051756694" + "templateHash": "10581081897017236090" } }, "parameters": { @@ -9730,6 +9749,9 @@ "deploymentUserAssignedIdentityClientId": { "type": "string" }, + "deploymentUserAssignedIdentityPrincipalId": { + "type": "string" + }, "desktopApplicationGroupName": { "type": "string" }, @@ -9770,7 +9792,7 @@ "resources": [ { "type": "Microsoft.DesktopVirtualization/applicationGroups", - "apiVersion": "2021-03-09-preview", + "apiVersion": "2024-04-03", "name": "[parameters('desktopApplicationGroupName')]", "location": "[parameters('locationControlPlane')]", "tags": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), if(contains(parameters('tags'), 'Microsoft.DesktopVirtualization/applicationGroups'), parameters('tags')['Microsoft.DesktopVirtualization/applicationGroups'], createObject()), parameters('mlzTags'))]", @@ -9779,9 +9801,23 @@ "applicationGroupType": "Desktop" } }, + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.DesktopVirtualization/applicationGroups/{0}', parameters('desktopApplicationGroupName'))]", + "name": "[guid(parameters('deploymentUserAssignedIdentityPrincipalId'), '86240b0e-9422-4c43-887b-b61143f32ba8', resourceId('Microsoft.DesktopVirtualization/applicationGroups', parameters('desktopApplicationGroupName')))]", + "properties": { + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', '86240b0e-9422-4c43-887b-b61143f32ba8')]", + "principalId": "[parameters('deploymentUserAssignedIdentityPrincipalId')]", + "principalType": "ServicePrincipal" + }, + "dependsOn": [ + "[resourceId('Microsoft.DesktopVirtualization/applicationGroups', parameters('desktopApplicationGroupName'))]" + ] + }, { "copy": { - "name": "roleAssignment", + "name": "roleAssignment_Users", "count": "[length(range(0, length(parameters('securityPrincipalObjectIds'))))]" }, "type": "Microsoft.Authorization/roleAssignments", @@ -9911,7 +9947,8 @@ } }, "dependsOn": [ - "[resourceId('Microsoft.DesktopVirtualization/applicationGroups', parameters('desktopApplicationGroupName'))]" + "[resourceId('Microsoft.DesktopVirtualization/applicationGroups', parameters('desktopApplicationGroupName'))]", + "[extensionResourceId(resourceId('Microsoft.DesktopVirtualization/applicationGroups', parameters('desktopApplicationGroupName')), 'Microsoft.Authorization/roleAssignments', guid(parameters('deploymentUserAssignedIdentityPrincipalId'), '86240b0e-9422-4c43-887b-b61143f32ba8', resourceId('Microsoft.DesktopVirtualization/applicationGroups', parameters('desktopApplicationGroupName'))))]" ] } ], @@ -10990,9 +11027,6 @@ "functionAppName": { "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.functionAppName.value]" }, - "hostPoolType": { - "value": "[parameters('hostPoolType')]" - }, "keyVaultUri": { "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.keyVaultUri.value]" }, @@ -11068,7 +11102,7 @@ "_generator": { "name": "bicep", "version": "0.31.34.60546", - "templateHash": "11179081376326675216" + "templateHash": "17423392577111200184" } }, "parameters": { @@ -11123,9 +11157,6 @@ "functionAppName": { "type": "string" }, - "hostPoolType": { - "type": "string" - }, "keyVaultUri": { "type": "string" }, @@ -11611,9 +11642,6 @@ "functionAppName": { "value": "[parameters('functionAppName')]" }, - "hostPoolType": { - "value": "[parameters('hostPoolType')]" - }, "keyVaultUri": { "value": "[parameters('keyVaultUri')]" }, @@ -11688,7 +11716,7 @@ "_generator": { "name": "bicep", "version": "0.31.34.60546", - "templateHash": "15055411149553856815" + "templateHash": "10921134071123326707" } }, "parameters": { @@ -11737,9 +11765,6 @@ "hostPoolName": { "type": "string" }, - "hostPoolType": { - "type": "string" - }, "keyVaultUri": { "type": "string" }, @@ -11929,14 +11954,14 @@ }, "type": "Microsoft.Network/privateEndpoints", "apiVersion": "2023-04-01", - "name": "[format('{0}-{1}', replace(parameters('namingConvention').storageAccountPrivateEndpoint, parameters('serviceToken'), 'file-fslogix'), padLeft(add(range(0, parameters('storageCount'))[copyIndex()], parameters('storageIndex')), 2, '0'))]", + "name": "[format('{0}-{1}', parameters('namingConvention').storageAccountFilePrivateEndpoint, padLeft(add(range(0, parameters('storageCount'))[copyIndex()], parameters('storageIndex')), 2, '0'))]", "location": "[parameters('location')]", "tags": "[variables('tagsPrivateEndpoints')]", "properties": { - "customNetworkInterfaceName": "[format('{0}-{1}', replace(parameters('namingConvention').storageAccountNetworkInterface, parameters('serviceToken'), 'file-fslogix'), padLeft(add(range(0, parameters('storageCount'))[copyIndex()], parameters('storageIndex')), 2, '0'))]", + "customNetworkInterfaceName": "[format('{0}-{1}', parameters('namingConvention').storageAccountFileNetworkInterface, padLeft(add(range(0, parameters('storageCount'))[copyIndex()], parameters('storageIndex')), 2, '0'))]", "privateLinkServiceConnections": [ { - "name": "[format('{0}-{1}', replace(parameters('namingConvention').storageAccountPrivateEndpoint, parameters('serviceToken'), 'file-fslogix'), padLeft(add(range(0, parameters('storageCount'))[copyIndex()], parameters('storageIndex')), 2, '0'))]", + "name": "[format('{0}-{1}', parameters('namingConvention').storageAccountFilePrivateEndpoint, padLeft(add(range(0, parameters('storageCount'))[copyIndex()], parameters('storageIndex')), 2, '0'))]", "properties": { "privateLinkServiceId": "[resourceId('Microsoft.Storage/storageAccounts', take(format('{0}{1}', variables('storageAccountNamePrefix'), padLeft(add(range(0, parameters('storageCount'))[range(0, parameters('storageCount'))[copyIndex()]], parameters('storageIndex')), 2, '0')), 15))]", "groupIds": [ @@ -11960,7 +11985,7 @@ }, "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", "apiVersion": "2021-08-01", - "name": "[format('{0}/{1}', format('{0}-{1}', replace(parameters('namingConvention').storageAccountPrivateEndpoint, parameters('serviceToken'), 'file-fslogix'), padLeft(add(range(0, parameters('storageCount'))[range(0, parameters('storageCount'))[copyIndex()]], parameters('storageIndex')), 2, '0')), take(format('{0}{1}', variables('storageAccountNamePrefix'), padLeft(add(range(0, parameters('storageCount'))[range(0, parameters('storageCount'))[copyIndex()]], parameters('storageIndex')), 2, '0')), 15))]", + "name": "[format('{0}/{1}', format('{0}-{1}', parameters('namingConvention').storageAccountFilePrivateEndpoint, padLeft(add(range(0, parameters('storageCount'))[range(0, parameters('storageCount'))[copyIndex()]], parameters('storageIndex')), 2, '0')), take(format('{0}{1}', variables('storageAccountNamePrefix'), padLeft(add(range(0, parameters('storageCount'))[range(0, parameters('storageCount'))[copyIndex()]], parameters('storageIndex')), 2, '0')), 15))]", "properties": { "privateDnsZoneConfigs": [ { @@ -11972,7 +11997,7 @@ ] }, "dependsOn": [ - "[resourceId('Microsoft.Network/privateEndpoints', format('{0}-{1}', replace(parameters('namingConvention').storageAccountPrivateEndpoint, parameters('serviceToken'), 'file-fslogix'), padLeft(add(range(0, parameters('storageCount'))[range(0, parameters('storageCount'))[copyIndex()]], parameters('storageIndex')), 2, '0')))]", + "[resourceId('Microsoft.Network/privateEndpoints', format('{0}-{1}', parameters('namingConvention').storageAccountFilePrivateEndpoint, padLeft(add(range(0, parameters('storageCount'))[range(0, parameters('storageCount'))[copyIndex()]], parameters('storageIndex')), 2, '0')))]", "storageAccounts" ] }, @@ -12046,15 +12071,15 @@ } }, "dependsOn": [ + "fileServices", "roleAssignment", "[resourceId('Microsoft.Storage/storageAccounts', take(format('{0}{1}', variables('storageAccountNamePrefix'), padLeft(add(range(0, parameters('storageCount'))[range(0, parameters('storageCount'))[copyIndex()]], parameters('storageIndex')), 2, '0')), 15))]" ] }, { - "condition": "[contains(parameters('activeDirectorySolution'), 'DomainServices')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('deploy-fslogix-ntfs-permissions-{0}', parameters('deploymentNameSuffix'))]", + "name": "[format('set-ntfs-permissions-{0}', parameters('deploymentNameSuffix'))]", "resourceGroup": "[parameters('resourceGroupManagement')]", "properties": { "expressionEvaluationOptions": { @@ -12217,10 +12242,10 @@ ] }, { - "condition": "[and(parameters('enableRecoveryServices'), contains(parameters('hostPoolType'), 'Pooled'))]", + "condition": "[parameters('enableRecoveryServices')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('deploy-backup-azure-files-{0}', parameters('deploymentNameSuffix'))]", + "name": "[format('deploy-backup-{0}', parameters('deploymentNameSuffix'))]", "resourceGroup": "[parameters('resourceGroupManagement')]", "properties": { "expressionEvaluationOptions": { @@ -12402,7 +12427,7 @@ } }, "dependsOn": [ - "shares" + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('set-ntfs-permissions-{0}', parameters('deploymentNameSuffix')))]" ] }, { @@ -12482,7 +12507,7 @@ } }, "dependsOn": [ - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-fslogix-ntfs-permissions-{0}', parameters('deploymentNameSuffix')))]" + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-backup-{0}', parameters('deploymentNameSuffix')))]" ] } ], @@ -12556,6 +12581,9 @@ "deploymentUserAssignedIdentityClientId": { "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.deploymentUserAssignedIdentityClientId.value]" }, + "deploymentUserAssignedIdentityPrincipalId": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.deploymentUserAssignedIdentityPrincipalId.value]" + }, "diskEncryptionSetResourceId": { "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.diskEncryptionSetResourceId.value]" }, @@ -12586,21 +12614,18 @@ "enableRecoveryServices": { "value": "[parameters('recoveryServices')]" }, - "enableScalingTool": { - "value": "[parameters('scalingTool')]" - }, "environmentAbbreviation": { "value": "[parameters('environmentAbbreviation')]" }, "fslogixContainerType": { "value": "[parameters('fslogixContainerType')]" }, - "functionAppName": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.functionAppName.value]" - }, "hostPoolName": { "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-control-plane-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.hostPoolName.value]" }, + "hostPoolResourceId": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-control-plane-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.hostPoolResourceId.value]" + }, "hostPoolType": { "value": "[parameters('hostPoolType')]" }, @@ -12622,6 +12647,9 @@ "location": { "value": "[parameters('locationVirtualMachines')]" }, + "logAnalyticsWorkspaceResourceId": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.logAnalyticsWorkspaceResourceId.value]" + }, "managementVirtualMachineName": { "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.virtualMachineName.value]" }, @@ -12638,9 +12666,6 @@ "organizationalUnitPath": { "value": "[parameters('organizationalUnitPath')]" }, - "pooledHostPool": { - "value": "[variables('pooledHostPool')]" - }, "recoveryServicesVaultName": { "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.recoveryServicesVaultName.value]" }, @@ -12656,6 +12681,18 @@ "roleDefinitions": { "value": "[variables('roleDefinitions')]" }, + "scalingWeekdaysOffPeakStartTime": { + "value": "[parameters('scalingWeekdaysOffPeakStartTime')]" + }, + "scalingWeekdaysPeakStartTime": { + "value": "[parameters('scalingWeekdaysPeakStartTime')]" + }, + "scalingWeekendsOffPeakStartTime": { + "value": "[parameters('scalingWeekendsOffPeakStartTime')]" + }, + "scalingWeekendsPeakStartTime": { + "value": "[parameters('scalingWeekendsPeakStartTime')]" + }, "securityPrincipalObjectIds": { "value": "[map(parameters('securityPrincipals'), lambda('item', lambdaVariables('item').objectId))]" }, @@ -12687,6 +12724,9 @@ "tags": { "value": "[parameters('tags')]" }, + "timeZone": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.locationProperties.value.timeZone]" + }, "virtualMachinePassword": { "value": "[parameters('virtualMachinePassword')]" }, @@ -12704,7 +12744,7 @@ "_generator": { "name": "bicep", "version": "0.31.34.60546", - "templateHash": "15064449949268346552" + "templateHash": "1578632995564263199" } }, "parameters": { @@ -12738,6 +12778,9 @@ "deploymentUserAssignedIdentityClientId": { "type": "string" }, + "deploymentUserAssignedIdentityPrincipalId": { + "type": "string" + }, "diskEncryptionSetResourceId": { "type": "string" }, @@ -12771,10 +12814,10 @@ "fslogixContainerType": { "type": "string" }, - "functionAppName": { + "hostPoolName": { "type": "string" }, - "hostPoolName": { + "hostPoolResourceId": { "type": "string" }, "hostPoolType": { @@ -12798,6 +12841,9 @@ "location": { "type": "string" }, + "logAnalyticsWorkspaceResourceId": { + "type": "string" + }, "managementVirtualMachineName": { "type": "string" }, @@ -12816,15 +12862,9 @@ "organizationalUnitPath": { "type": "string" }, - "pooledHostPool": { - "type": "bool" - }, "enableRecoveryServices": { "type": "bool" }, - "enableScalingTool": { - "type": "bool" - }, "recoveryServicesVaultName": { "type": "string" }, @@ -12840,6 +12880,18 @@ "roleDefinitions": { "type": "object" }, + "scalingWeekdaysOffPeakStartTime": { + "type": "string" + }, + "scalingWeekdaysPeakStartTime": { + "type": "string" + }, + "scalingWeekendsOffPeakStartTime": { + "type": "string" + }, + "scalingWeekendsPeakStartTime": { + "type": "string" + }, "securityPrincipalObjectIds": { "type": "array" }, @@ -12873,6 +12925,9 @@ "tags": { "type": "object" }, + "timeZone": { + "type": "string" + }, "virtualMachinePassword": { "type": "securestring" }, @@ -12884,9 +12939,7 @@ } }, "variables": { - "$fxv#0": "# This file enables modules to be automatically managed by the Functions service.\r\n# See https://aka.ms/functionsmanageddependency for additional information.\r\n#\r\n@{\r\n # For latest supported version, go to 'https://www.powershellgallery.com/packages/Az'. \r\n # To use the Az module in your function app, please uncomment the line below.\r\n # 'Az' = '7.*'\r\n}", - "$fxv#1": "param($Timer)\r\n\r\ntry\r\n{\r\n\t[string]$BeginPeakTime = $env:BeginPeakTime\r\n\t[string]$EndPeakTime = $env:EndPeakTime\r\n\t[string]$EnvironmentName = $env:EnvironmentName\r\n\t[string]$HostPoolName = $env:HostPoolName\r\n\t[string]$HostPoolResourceGroupName = $env:HostPoolResourceGroupName\r\n\t[int]$LimitSecondsToForceLogOffUser = $env:LimitSecondsToForceLogOffUser\r\n\t[string]$LogOffMessageBody = $env:LogOffMessageBody\r\n\t[string]$LogOffMessageTitle = $env:LogOffMessageTitle\r\n\t[string]$MaintenanceTagName = $env:MaintenanceTagName\r\n\t[int]$MinimumNumberOfRDSH = $env:MinimumNumberOfRDSH\r\n\t[string]$ResourceManagerUrl = $env:ResourceManagerUrl\r\n\t[double]$SessionThresholdPerCPU = $env:SessionThresholdPerCPU\r\n\t[string]$SubscriptionId = $env:SubscriptionId\r\n\t[string]$TenantId = $env:TenantId\r\n\t[string]$TimeDifference = $env:TimeDifference\r\n\t[string[]]$DesiredRunningStates = @('Available', 'NeedsAssistance')\r\n\t[string[]]$TimeDiffHrsMin = \"$($TimeDifference):0\".Split(':')\r\n\r\n\r\n\t#region Functions\r\n\tfunction Get-LocalDateTime\r\n {\r\n\t\treturn (Get-Date).ToUniversalTime().AddHours($TimeDiffHrsMin[0]).AddMinutes($TimeDiffHrsMin[1])\r\n\t}\r\n\r\n\tfunction Write-Log \r\n {\r\n\t\t[CmdletBinding()]\r\n\t\tparam (\r\n\t\t\t[Parameter(Mandatory = $false)]\r\n\t\t\t[switch]$Err,\r\n\r\n\t\t\t[Parameter(Mandatory = $true)]\r\n\t\t\t[string]$HostPoolName,\r\n\r\n\t\t\t[Parameter(Mandatory = $true)]\r\n\t\t\t[string]$Message,\r\n\r\n\t\t\t[Parameter(Mandatory = $false)]\r\n\t\t\t[switch]$Warn\r\n\t\t)\r\n\r\n\t\t[string]$MessageTimeStamp = (Get-LocalDateTime).ToString('yyyy-MM-dd HH:mm:ss')\r\n\t\t$Message = \"[$($MyInvocation.ScriptLineNumber)] [$($HostPoolName)] $Message\"\r\n\t\t[string]$WriteMessage = \"[$($MessageTimeStamp)] $Message\"\r\n\r\n\t\tif ($Err)\r\n {\r\n\t\t\tWrite-Error $WriteMessage\r\n\t\t\t$Message = \"ERROR: $Message\"\r\n\t\t}\r\n\t\telseif ($Warn)\r\n {\r\n\t\t\tWrite-Warning $WriteMessage\r\n\t\t\t$Message = \"WARN: $Message\"\r\n\t\t}\r\n\t\telse \r\n {\r\n\t\t\tWrite-Output $WriteMessage\r\n\t\t}\r\n\t}\r\n\r\n\tfunction Set-nVMsToStartOrStop \r\n {\r\n\t\tparam (\r\n\t\t\t[Parameter(Mandatory = $true)]\r\n\t\t\t[string]$HostPoolName,\r\n\r\n\t\t\t[Parameter(Mandatory = $false)]\r\n\t\t\t[switch]$InPeakHours,\r\n\r\n\t\t\t[Parameter(Mandatory = $true)]\r\n\t\t\t[int]$MaxUserSessionsPerVM,\r\n\t\t\r\n\t\t\t[Parameter(Mandatory = $true)]\r\n\t\t\t[int]$nRunningCores,\r\n\r\n\t\t\t[Parameter(Mandatory = $true)]\r\n\t\t\t[int]$nRunningVMs,\r\n\t\t\t\r\n\t\t\t[Parameter(Mandatory = $true)]\r\n\t\t\t[int]$nUserSessions,\r\n\r\n\t\t\t[Parameter(Mandatory = $true)]\r\n\t\t\t[hashtable]$Res\r\n\t\t)\r\n\r\n\t\t# check if need to adjust min num of running session hosts required if the number of user sessions is close to the max allowed by the min num of running session hosts required\r\n\t\t[double]$MaxUserSessionsThreshold = 0.9\r\n\t\t[int]$MaxUserSessionsThresholdCapacity = [math]::Floor($MinimumNumberOfRDSH * $MaxUserSessionsPerVM * $MaxUserSessionsThreshold)\r\n\t\tif ($nUserSessions -gt $MaxUserSessionsThresholdCapacity)\r\n {\r\n\t\t\t$MinimumNumberOfRDSH = [math]::Ceiling($nUserSessions / ($MaxUserSessionsPerVM * $MaxUserSessionsThreshold))\r\n\t\t\tWrite-Log -HostPoolName $HostPoolName -Message \"Number of user sessions is more than $($MaxUserSessionsThreshold * 100) % of the max number of sessions allowed with minimum number of running session hosts required ($MaxUserSessionsThresholdCapacity). Adjusted minimum number of running session hosts required to $MinimumNumberOfRDSH\"\r\n\t\t}\r\n\r\n\t\t# Check if minimum number of session hosts are running\r\n\t\tif ($nRunningVMs -lt $MinimumNumberOfRDSH)\r\n {\r\n\t\t\t$res.nVMsToStart = $MinimumNumberOfRDSH - $nRunningVMs\r\n\t\t\tWrite-Log -HostPoolName $HostPoolName -Message \"Number of running session host is less than minimum required. Need to start $($res.nVMsToStart) VMs\"\r\n\t\t}\r\n\t\t\r\n\t\tif ($InPeakHours)\r\n {\r\n\t\t\t[double]$nUserSessionsPerCore = $nUserSessions / $nRunningCores\r\n\t\t\t# In peak hours: check if current capacity is meeting the user demands\r\n\t\t\tif ($nUserSessionsPerCore -gt $SessionThresholdPerCPU)\r\n {\r\n\t\t\t\t$res.nCoresToStart = [math]::Ceiling(($nUserSessions / $SessionThresholdPerCPU) - $nRunningCores)\r\n\t\t\t\tWrite-Log -HostPoolName $HostPoolName -Message \"[In peak hours] Number of user sessions per Core is more than the threshold. Need to start $($res.nCoresToStart) cores\"\r\n\t\t\t}\r\n\r\n\t\t\treturn\r\n\t\t}\r\n\r\n\t\tif ($nRunningVMs -gt $MinimumNumberOfRDSH)\r\n {\r\n\t\t\t# Calculate the number of session hosts to stop\r\n\t\t\t$res.nVMsToStop = $nRunningVMs - $MinimumNumberOfRDSH\r\n\t\t\tWrite-Log -HostPoolName $HostPoolName -Message \"[Off peak hours] Number of running session host is greater than minimum required. Need to stop $($res.nVMsToStop) VMs\"\r\n\t\t}\r\n\t}\r\n\r\n\tfunction TryUpdateSessionHostDrainMode\r\n {\r\n\t\t[CmdletBinding()]\r\n\t\tparam (\r\n\t\t\t[Parameter(Mandatory = $true)]\r\n\t\t\t[bool]$AllowNewSession,\r\n\r\n\t\t\t[Parameter(Mandatory = $true)]\r\n\t\t\t[hashtable]$Header,\r\n\r\n\t\t\t[Parameter(Mandatory = $true)]\r\n\t\t\t[string]$HostPoolName,\r\n\r\n\t\t\t[Parameter(Mandatory = $true)]\r\n\t\t\t[string]$HostPoolResourceGroupName,\r\n\r\n\t\t\t[Parameter(Mandatory = $true)]\r\n\t\t\t[string]$ResourceManagerUrl,\r\n\r\n\t\t\t[Parameter(Mandatory = $true)]\r\n\t\t\t[string]$SessionHostName,\r\n\r\n\t\t\t[Parameter(Mandatory = $true)]\r\n\t\t\t[string]$SubscriptionId\r\n\t\t)\r\n\t\tBegin { }\r\n\t\tProcess \r\n {\r\n\t\t\tWrite-Log -HostPoolName $HostPoolName -Message \"Update session host '$SessionHostName' to set allow new sessions to $AllowNewSession\"\r\n\t\t\ttry \r\n\t\t\t{\r\n\t\t\t\t$Uri = $ResourceManagerUrl + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $HostPoolResourceGroupName + '/providers/Microsoft.DesktopVirtualization/hostPools/' + $HostPoolName + '/sessionHosts/' + $SessionHostName + '?api-version=2022-02-10-preview'\r\n\t\t\t\tInvoke-RestMethod -Headers $Header -Body (@{properties = @{allowNewSession = $AllowNewSession}} | ConvertTo-Json) -Method 'Patch' -Uri $Uri | Out-Null\r\n\t\t\t}\r\n\t\t\tcatch\r\n\t\t\t{\r\n\t\t\t\tWrite-Log -HostPoolName $HostPoolName -Warn -Message \"Failed to update the session host '$SessionHostName' to set allow new sessions to $($AllowNewSession): $($PSItem | Format-List -Force | Out-String)\"\r\n\t\t\t}\r\n\t\t}\r\n\t\tEnd { }\r\n\t}\r\n\r\n\tfunction TryForceLogOffUser\r\n {\r\n\t\t[CmdletBinding()]\r\n\t\tparam (\r\n\t\t\t[Parameter(Mandatory = $true)]\r\n\t\t\t[hashtable]$Header,\r\n\r\n\t\t\t[Parameter(Mandatory = $true)]\r\n\t\t\t[string]$HostPoolName,\r\n\r\n\t\t\t[Parameter(Mandatory = $true)]\r\n\t\t\t[string]$HostPoolResourceGroupName,\r\n\r\n\t\t\t[Parameter(Mandatory = $true)]\r\n\t\t\t[string]$ResourceManagerUrl,\r\n\r\n\t\t\t[Parameter(Mandatory = $true, ValueFromPipeline = $true)]\r\n\t\t\t$Session,\r\n\r\n\t\t\t[Parameter(Mandatory = $true)]\r\n\t\t\t[string]$SubscriptionId\r\n\t\t)\r\n\t\tBegin { }\r\n\t\tProcess\r\n {\r\n [string[]]$Toks = $Session.Name.Split('/')\r\n [string]$SessionHostName = $Toks[1]\r\n [string]$SessionID = $Toks[-1]\r\n [string]$User = $Session.ActiveDirectoryUserName\r\n\r\n\t\t\ttry \r\n\t\t\t{\r\n\t\t\t\tWrite-Log -HostPoolName $HostPoolName -Message \"Force log off user: '$User', session ID: $SessionID\"\r\n\r\n\t\t\t\t$Uri = $ResourceManagerUrl + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $HostPoolResourceGroupName + '/providers/Microsoft.DesktopVirtualization/hostPools/' + $HostPoolName + '/sessionHosts/' + $SessionHostName + '/userSessions/' + $SessionID + '?api-version=2022-02-10-preview&force=True'\r\n\t\t\t\tInvoke-RestMethod -Headers $Header -Method 'Delete' -Uri $Uri | Out-Null\r\n\t\t\t}\r\n\t\t\tcatch \r\n\t\t\t{\r\n\t\t\t\tWrite-Log -HostPoolName $HostPoolName -Warn -Message \"Failed to force log off user: '$User', session ID: $SessionID $($PSItem | Format-List -Force | Out-String)\"\r\n\t\t\t}\r\n\t\t}\r\n\t\tEnd { }\r\n\t}\r\n\r\n\tfunction TryResetSessionHostDrainModeAndUserSessions\r\n {\r\n\t\t[CmdletBinding()]\r\n\t\tparam (\r\n\t\t\t[Parameter(Mandatory = $true)]\r\n\t\t\t[hashtable]$Header,\r\n\r\n\t\t\t[Parameter(Mandatory = $true)]\r\n\t\t\t[string]$HostPoolName,\r\n\r\n\t\t\t[Parameter(Mandatory = $true)]\r\n\t\t\t[string]$HostPoolResourceGroupName,\r\n\r\n\t\t\t[Parameter(Mandatory = $true)]\r\n\t\t\t[string]$ResourceManagerUrl,\r\n\r\n\t\t\t[Parameter(Mandatory = $true)]\r\n\t\t\t[string]$SessionHostName,\r\n\r\n\t\t\t[Parameter(Mandatory = $true)]\r\n\t\t\t[int]$SessionHostSessions,\r\n\r\n\t\t\t[Parameter(Mandatory = $true)]\r\n\t\t\t[string]$SubscriptionId\r\n\t\t)\r\n\t\tBegin { }\r\n\t\tProcess \r\n {\r\n\t\t\tTryUpdateSessionHostDrainMode -AllowNewSession $true -Header $Header -HostPoolName $HostPoolName -HostPoolResourceGroupName $HostPoolResourceGroupName -ResourceManagerUrl $ResourceManagerUrl -SessionHostName $SessionHostName -SubscriptionId $SubscriptionId\r\n\r\n\t\t\tif ($SessionHostSessions -eq 0)\r\n {\r\n\t\t\t\treturn\r\n\t\t\t}\r\n\r\n\t\t\tWrite-Log -HostPoolName $HostPoolName -Warn -Message \"Session host '$SessionHostName' still has $SessionHostSessions) sessions left behind in broker DB\"\r\n\r\n\t\t\tWrite-Log -HostPoolName $HostPoolName -Message \"Get all user sessions from session host '$SessionHostName'\"\r\n\t\t\ttry \r\n\t\t\t{\r\n\t\t\t\t$Uri = $ResourceManagerUrl + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $HostPoolResourceGroupName + '/providers/Microsoft.DesktopVirtualization/hostPools/' + $HostPoolName + '/sessionHosts/' + $SessionHostName + '/userSessions?api-version=2022-02-10-preview'\r\n\t\t\t\t$UserSessions = Invoke-RestMethod -Headers $Header -Method 'Get' -Uri $Uri\r\n\t\t\t}\r\n\t\t\tcatch \r\n\t\t\t{\r\n\t\t\t\tWrite-Log -HostPoolName $HostPoolName -Warn -Message \"Failed to retrieve user sessions of session host '$SessionHostName': $($PSItem | Format-List -Force | Out-String)\"\r\n\t\t\t\treturn\r\n\t\t\t}\r\n\r\n\t\t\tWrite-Log -HostPoolName $HostPoolName -Message \"Force log off $($UserSessions.Count) users on session host: '$SessionHostName'\"\r\n\t\t\t$UserSessions | TryForceLogOffUser -Header $Header -HostPoolName $HostPoolName -HostPoolResourceGroupName $HostPoolResourceGroupName -ResourceManagerUrl $ResourceManagerUrl -SubscriptionId $SubscriptionId\r\n\t\t}\r\n\t\tEnd { }\r\n\t}\r\n\t#endregion Functions\r\n\r\n\r\n\t# Note: https://stackoverflow.com/questions/41674518/powershell-setting-security-protocol-to-tls-1-2\r\n\t[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12\r\n\r\n\r\n\t#region Azure Authentication\r\n $AccessToken = $null\r\n try\r\n {\r\n\t\t$TokenAuthURI = $env:IDENTITY_ENDPOINT + '?resource=' + $ResourceManagerUrl + '&api-version=2019-08-01'\r\n\t\t$TokenResponse = Invoke-RestMethod -Method Get -Headers @{\"X-IDENTITY-HEADER\"=\"$env:IDENTITY_HEADER\"} -Uri $TokenAuthURI\r\n\t\t$AccessToken = $TokenResponse.access_token\r\n\t\t$Header = @{\r\n\t\t\t'Content-Type'='application/json'\r\n\t\t\t'Authorization'='Bearer ' + $AccessToken\r\n\t\t}\r\n }\r\n catch\r\n {\r\n throw [System.Exception]::new('Failed to authenticate Azure with application ID, tenant ID, subscription ID', $PSItem.Exception)\r\n }\r\n Write-Log -HostPoolName $HostPoolName -Message \"Successfully authenticated with Azure using a managed identity\"\r\n\t#endregion Azure Authentication\r\n\r\n\r\n\t#region validate host pool, validate / update HostPool load balancer type, ensure there is at least 1 session host, get num of user sessions\r\n\t# Validate and get host pool info\r\n\t$HostPool = $null\r\n\ttry \r\n\t{\r\n\t\tWrite-Log -HostPoolName $HostPoolName -Message \"Get host pool information\"\r\n\t\t$Uri = $ResourceManagerUrl + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $HostPoolResourceGroupName + '/providers/Microsoft.DesktopVirtualization/hostPools/' + $HostPoolName + '?api-version=2022-02-10-preview'\r\n\t\t$HostPool = Invoke-RestMethod -Headers $Header -Method 'Get' -Uri $Uri\r\n\r\n\t\tif (!$HostPool) \r\n\t\t{\r\n\t\t\tthrow $HostPool\r\n\t\t}\r\n\t}\r\n\tcatch \r\n\t{\r\n\t\tthrow [System.Exception]::new(\"Failed to get host pool info of '$HostPoolName' in resource group '$HostPoolResourceGroupName'. Ensure that you have entered the correct values\", $PSItem.Exception)\r\n\t}\r\n\r\n\t# Ensure HostPool load balancer type is not persistent\r\n\tif ($HostPool.properties.loadBalancerType -ieq 'Persistent')\r\n {\r\n\t\tthrow \"HostPool '$HostPoolName' is configured with 'Persistent' load balancer type. Scaling tool only supports these load balancer types: BreadthFirst, DepthFirst\"\r\n\t}\r\n\r\n\tWrite-Log -HostPoolName $HostPoolName -Message 'Get session hosts'\r\n\t$Uri = $ResourceManagerUrl + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $HostPoolResourceGroupName + '/providers/Microsoft.DesktopVirtualization/hostPools/' + $HostPoolName + '/sessionHosts?api-version=2022-02-10-preview'\r\n\t$SessionHosts = (Invoke-RestMethod -Headers $Header -Method 'Get' -Uri $Uri).value\r\n\r\n\tif (!$SessionHosts)\r\n {\r\n\t\tWrite-Log -HostPoolName $HostPoolName -Message \"There are no session hosts in the host pool '$HostPoolName'. Ensure that hostpool has session hosts\"\r\n\t\tWrite-Log -HostPoolName $HostPoolName -Message 'End'\r\n\t\treturn\r\n\t}\r\n\r\n\tWrite-Log -HostPoolName $HostPoolName -Message 'Get number of user sessions in host pool'\r\n\t$Uri = $ResourceManagerUrl + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $HostPoolResourceGroupName + '/providers/Microsoft.DesktopVirtualization/hostPools/' + $HostPoolName + '/userSessions?api-version=2022-02-10-preview'\r\n\t[int]$nUserSessions = (Invoke-RestMethod -Headers $Header -Method 'Get' -Uri $Uri).value.Count\r\n\r\n\t# Set up breadth 1st load balacing type\r\n\t# Note: breadth 1st is enforced on AND off peak hours to simplify the things with scaling in the start/end of peak hours\r\n\tif (!$SkipUpdateLoadBalancerType -and $HostPool.properties.loadBalancerType -ine 'BreadthFirst')\r\n {\r\n\t\tWrite-Log -HostPoolName $HostPoolName -Message \"Update HostPool with 'BreadthFirst' load balancer type (current: '$($HostPool.properties.loadBalancerType)')\"\r\n\r\n\t\t$Body = @{\r\n\t\t\tproperties = @{\r\n\t\t\t\tloadBalancerType = 'BreadthFirst'\r\n\t\t\t}\r\n\t\t}\r\n\t\t$Uri = $ResourceManagerUrl + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $HostPoolResourceGroupName + '/providers/Microsoft.DesktopVirtualization/hostPools/' + $HostPoolName + '?api-version=2022-02-10-preview'\r\n\t\t$HostPool = Invoke-RestMethod -Headers $Header -Body $($Body | ConvertTo-Json) -Method 'Patch' -Uri $Uri\r\n\t}\r\n\tWrite-Log -HostPoolName $HostPoolName -Message \"Number of session hosts in the HostPool: $($SessionHosts.Count)\"\r\n\t#endregion\r\n\t\r\n\r\n\t# region Peak Hours #\r\n\t# Convert local time, begin peak time & end peak time from UTC to local time\r\n\t$CurrentDateTime = Get-LocalDateTime\r\n\t$BeginPeakDateTime = [datetime]::Parse($CurrentDateTime.ToShortDateString() + ' ' + $BeginPeakTime)\r\n\t$EndPeakDateTime = [datetime]::Parse($CurrentDateTime.ToShortDateString() + ' ' + $EndPeakTime)\r\n\r\n\t# Adjust peak times to make sure begin peak time is always before end peak time\r\n\tif ($EndPeakDateTime -lt $BeginPeakDateTime)\r\n {\r\n\t\tif ($CurrentDateTime -lt $EndPeakDateTime)\r\n {\r\n\t\t\t$BeginPeakDateTime = $BeginPeakDateTime.AddDays(-1)\r\n\t\t}\r\n\t\telse\r\n {\r\n\t\t\t$EndPeakDateTime = $EndPeakDateTime.AddDays(1)\r\n\t\t}\r\n\t}\r\n\r\n\tWrite-Log -HostPoolName $HostPoolName -Message \"Using current time: $($CurrentDateTime.ToString('yyyy-MM-dd HH:mm:ss')), begin peak time: $($BeginPeakDateTime.ToString('yyyy-MM-dd HH:mm:ss')), end peak time: $($EndPeakDateTime.ToString('yyyy-MM-dd HH:mm:ss'))\"\r\n\r\n\t[bool]$InPeakHours = ($BeginPeakDateTime -le $CurrentDateTime -and $CurrentDateTime -le $EndPeakDateTime)\r\n\tif ($InPeakHours)\r\n {\r\n\t\tWrite-Log -HostPoolName $HostPoolName -Message 'In peak hours'\r\n\t}\r\n\telse\r\n {\r\n\t\tWrite-Log -HostPoolName $HostPoolName -Message 'Off peak hours'\r\n\t}\r\n\t# endregion Peak Hours #\r\n\r\n\r\n\t#region get session hosts, VMs & user sessions info and compute workload\r\n\t# Note: session host is considered \"running\" if its running AND is in desired states AND allowing new sessions\r\n\t# Number of session hosts that are running, are in desired states and allowing new sessions\r\n\t[int]$nRunningVMs = 0\r\n\t# Number of cores that are running, are in desired states and allowing new sessions\r\n\t[int]$nRunningCores = 0\r\n\t# Array that contains all the virtual machine objects that are session hosts except the ones that are tagged for maintenance\r\n\t$VMs = @()\r\n\t# Object that contains the number of cores for each VM size SKU\r\n\t$VMSizeCores = @{}\r\n\t# Number of user sessions reported by each session host that is running, is in desired state and allowing new sessions\r\n\t[int]$nUserSessionsFromAllRunningVMs = 0\r\n\r\n\t# Populate all session hosts objects\r\n\tforeach ($SessionHost in $SessionHosts)\r\n {\r\n\t\t[string]$VirtualMachineResourceId = $SessionHost.properties.resourceId\r\n\t\t[string]$VirtualMachineName = $VirtualMachineResourceId.Split('/')[8]\r\n\t\t[string]$VirtualMachineResourceGroupName = $VirtualMachineResourceId.Split('/')[4]\r\n\t\t$Uri = $ResourceManagerUrl + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $VirtualMachineResourceGroupName + '/providers/Microsoft.Compute/virtualMachines/' + $VirtualMachineName + '?api-version=2024-03-01&$expand=instanceView'\r\n\t\t$VirtualMachine = Invoke-RestMethod -Headers $Header -Method 'Get' -Uri $Uri\r\n\r\n\t\t# Throw an error if the virtual machine for the session host does not exist\r\n\t\tif ($VirtualMachine.error)\r\n\t\t{\r\n\t\t\tthrow \"The virtual machine for session host '$VirtualMachineName' does not exist\"\r\n\t\t}\r\n\t\t# Ignore session hosts tagged for maintenance or missing virtual machine\r\n\t\telseif($VirtualMachine.tags.Keys -contains $MaintenanceTagName)\r\n {\r\n\t\t\tWrite-Log -HostPoolName $HostPoolName -Message \"VM '$VirtualMachineName' is in maintenance and will be ignored\"\r\n\t\t\tcontinue\r\n\t\t}\r\n\t\telse \r\n\t\t{\r\n\t\t\t$VMs += $VirtualMachine\r\n\t\t}\r\n\r\n\t\t$PowerState = $VirtualMachine.properties.instanceView.statuses[1].displayStatus\r\n\t\tWrite-Log -HostPoolName $HostPoolName -Message \"Session host: '$($VirtualMachineName)', power state: '$PowerState', status: '$($SessionHost.properties.status)', update state: '$($SessionHost.properties.updateState)', sessions: $($SessionHost.properties.sessions), allow new session: $($SessionHost.properties.allowNewSession)\"\r\n\t\t\r\n\t\t# Get the number of cores for VM size SKU\r\n\t\tif (!$VMSizeCores.ContainsKey($VirtualMachine.properties.hardwareProfile.vmSize))\r\n {\r\n\t\t\tWrite-Log -HostPoolName $HostPoolName -Message \"Get VM sizes in $($VirtualMachine.location)\"\r\n\r\n\t\t\t$Uri = $ResourceManagerUrl + 'subscriptions/' + $SubscriptionId + '/providers/Microsoft.Compute/locations/' + $VirtualMachine.location + '/vmSizes?api-version=2024-03-01'\r\n\t\t\t$VMSizes = (Invoke-RestMethod -Headers $Header -Method 'Get' -Uri $Uri).value\r\n\r\n\t\t\tforeach ($VMSize in $VMSizes)\r\n {\r\n\t\t\t\tif (!$VMSizeCores.ContainsKey($VMSize.name))\r\n {\r\n\t\t\t\t\t$VMSizeCores.Add($VMSize.name, $VMSize.numberOfCores)\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif ($PowerState -ieq 'VM running')\r\n {\r\n\t\t\tif ($SessionHost.properties.status -notin $DesiredRunningStates)\r\n {\r\n\t\t\t\tWrite-Log -HostPoolName $HostPoolName -Warn -Message 'VM is in running state but session host is not and so it will be ignored (this could be because the VM was just started and has not connected to broker yet)'\r\n\t\t\t}\r\n\t\t\tif (!$SessionHost.properties.allowNewSession)\r\n {\r\n\t\t\t\tWrite-Log -HostPoolName $HostPoolName -Warn -Message 'VM is in running state but session host is not allowing new sessions and so it will be ignored'\r\n\t\t\t}\r\n\r\n\t\t\tif ($SessionHost.properties.status -in $DesiredRunningStates -and $SessionHost.properties.allowNewSession)\r\n {\r\n\t\t\t\t++$nRunningVMs\r\n\t\t\t\t$nRunningCores += $VMSizeCores[$VirtualMachine.properties.hardwareProfile.vmSize]\r\n\t\t\t\t$nUserSessionsFromAllRunningVMs += $SessionHost.properties.sessions\r\n\t\t\t}\r\n\t\t}\r\n\t\telse \r\n {\r\n\t\t\tif ($SessionHost.properties.status -in $DesiredRunningStates)\r\n {\r\n\t\t\t\tWrite-Log -HostPoolName $HostPoolName -Warn -Message \"VM is not in running state but session host is (this could be because the VM was just stopped and broker doesn't know that yet)\"\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tif ($nUserSessionsFromAllRunningVMs -ne $nUserSessions)\r\n {\r\n\t\tWrite-Log -HostPoolName $HostPoolName -Warn -Message \"Sum of user sessions reported by every running session host ($nUserSessionsFromAllRunningVMs) is not equal to the total number of user sessions reported by the host pool ($nUserSessions)\"\r\n\t}\r\n\r\n\r\n\tif (!$nRunningCores)\r\n {\r\n\t\t$nRunningCores = 1\r\n\t}\r\n\r\n\tWrite-Log -HostPoolName $HostPoolName -Message \"Number of running session hosts: $nRunningVMs of total $($VMs.Count)\"\r\n\tWrite-Log -HostPoolName $HostPoolName -Message \"Number of user sessions: $nUserSessions of total allowed $($nRunningVMs * $HostPool.properties.maxSessionLimit)\"\r\n\tWrite-Log -HostPoolName $HostPoolName -Message \"Number of user sessions per Core: $($nUserSessions / $nRunningCores), threshold: $SessionThresholdPerCPU\"\r\n\tWrite-Log -HostPoolName $HostPoolName -Message \"Minimum number of running session hosts required: $MinimumNumberOfRDSH\"\r\n\r\n\t# Check if minimum num of running session hosts required is higher than max allowed\r\n\tif ($VMs.Count -le $MinimumNumberOfRDSH)\r\n {\r\n\t\tWrite-Log -HostPoolName $HostPoolName -Warn -Message 'Minimum number of RDSH is set higher than or equal to total number of session hosts'\r\n\t}\r\n\t#endregion\r\n\r\n\r\n\t#region determine number of session hosts to start/stop if any\r\n\t# Now that we have all the info about the session hosts & their usage, figure how many session hosts to start/stop depending on in/off peak hours and the demand [Ops = operations to perform]\r\n\t$Ops = @{\r\n\t\tnVMsToStart = 0\r\n\t\tnCoresToStart = 0\r\n\t\tnVMsToStop = 0\r\n\t}\r\n\r\n\tSet-nVMsToStartOrStop -HostPoolName $HostPoolName -nRunningVMs $nRunningVMs -nRunningCores $nRunningCores -nUserSessions $nUserSessions -MaxUserSessionsPerVM $HostPool.properties.maxSessionLimit -InPeakHours:$InPeakHours -Res $Ops\r\n\t#endregion\r\n\r\n\r\n\t#region start any session hosts if need to\r\n\t# Check if we have any session hosts to start\r\n\tif ($Ops.nVMsToStart -or $Ops.nCoresToStart)\r\n {\r\n\t\tif ($nRunningVMs -eq $VMs.Count)\r\n {\r\n\t\t\tWrite-Log -HostPoolName $HostPoolName -Message 'All session hosts are running'\r\n\t\t\tWrite-Log -HostPoolName $HostPoolName -Message 'End'\r\n\t\t\treturn\r\n\t\t}\r\n\r\n\t\t# Object that contains names of session hosts that will be started\r\n\t\t# $StartSessionHostFullNames = @{ }\r\n\t\t# Array that contains jobs of starting the session hosts\r\n\t\t[array]$StartedVMs = @()\r\n\r\n\t\tWrite-Log -HostPoolName $HostPoolName -Message 'Find session hosts that are stopped and allowing new sessions'\r\n\t\tforeach ($SessionHost in $SessionHosts)\r\n {\r\n\t\t\t$VM = $VMs | Where-Object { $_.id -ieq $SessionHost.properties.resourceId }\r\n\t\t\tif (!$Ops.nVMsToStart -and !$Ops.nCoresToStart)\r\n {\r\n\t\t\t\t# Done with starting session hosts that needed to be\r\n\t\t\t\tbreak\r\n\t\t\t}\r\n\t\t\tif ($VM.properties.instanceView.statuses[1].displayStatus -ieq 'VM running')\r\n {\r\n\t\t\t\tcontinue\r\n\t\t\t}\r\n\t\t\tif ($SessionHost.properties.updateState -ine 'Succeeded')\r\n {\r\n\t\t\t\tWrite-Log -HostPoolName $HostPoolName -Warn -Message \"Session host '$($VM.name)' may not be healthy\"\r\n\t\t\t}\r\n\r\n\t\t\tif (!$SessionHost.properties.allowNewSession)\r\n {\r\n\t\t\t\tWrite-Log -HostPoolName $HostPoolName -Warn -Message \"Session host '$($VM.name)' is not allowing new sessions and so it will not be started\"\r\n\t\t\t\tcontinue\r\n\t\t\t}\r\n\r\n\t\t\tWrite-Log -HostPoolName $HostPoolName -Message \"Start session host '$($VM.name)'\"\r\n\r\n\t\t\t$Uri = $ResourceManagerUrl + $VM.id.TrimStart('/') + '/start?api-version=2023-09-01'\r\n\t\t\tInvoke-RestMethod -Headers $Header -Method 'Post' -Uri $Uri | Out-Null\r\n\t\t\t$StartedVMs += $VM\r\n\t\t\t\t\r\n\t\t\t--$Ops.nVMsToStart\r\n\t\t\tif ($Ops.nVMsToStart -lt 0)\r\n {\r\n\t\t\t\t$Ops.nVMsToStart = 0\r\n\t\t\t}\r\n\r\n\t\t\t$Ops.nCoresToStart -= $VMSizeCores[$VM.properties.hardwareProfile.vmSize]\r\n\t\t\tif ($Ops.nCoresToStart -lt 0)\r\n {\r\n\t\t\t\t$Ops.nCoresToStart = 0\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\t# Check if there were enough number of session hosts to start\r\n\t\tif ($Ops.nVMsToStart -or $Ops.nCoresToStart)\r\n {\r\n\t\t\tWrite-Log -HostPoolName $HostPoolName -Warn -Message \"Not enough session hosts to start. Still need to start maximum of either $($Ops.nVMsToStart) VMs or $($Ops.nCoresToStart) cores\"\r\n\t\t}\r\n\r\n\t\t# Wait for session hosts to start\r\n\t\twhile($StartedVMs.Count -gt 0)\r\n\t\t{\r\n\t\t\tforeach($StartedVM in $StartedVMs)\r\n\t\t\t{\r\n\t\t\t\t$Uri = $ResourceManagerUrl + $StartedVM.id.TrimStart('/') + '?api-version=2024-03-01'\r\n\t\t\t\t$VMAgentStatus = (Invoke-RestMethod -Headers $Header -Method 'Get' -Uri $Uri).properties.instanceView.vmAgent\r\n\t\t\t\tif ($VMAgentStatus)\r\n\t\t\t\t{\r\n\t\t\t\t\tWrite-Log -HostPoolName $HostPoolName -Message \"Session host '$($StartedVM.name)' is running\"\r\n\t\t\t\t\t$StartedVMs = $StartedVMs -ne $StartedVM\r\n\t\t\t\t}\r\n\t\t\t}\r\n\t\t\tStart-Sleep -Seconds 30\r\n\t\t}\r\n\r\n\t\tWrite-Log -HostPoolName $HostPoolName -Message 'All session hosts have started'\r\n\t\tWrite-Log -HostPoolName $HostPoolName -Message 'End'\r\n\t\treturn\r\n\t}\r\n\t#endregion\r\n\r\n\r\n\t#region stop any session hosts if need to\r\n\tif (!$Ops.nVMsToStop)\r\n {\r\n\t\tWrite-Log -HostPoolName $HostPoolName -Message 'No need to start/stop any session hosts'\r\n\t\tWrite-Log -HostPoolName $HostPoolName -Message 'End'\r\n\t\treturn\r\n\t}\r\n\r\n\t# Object that contains names of session hosts that will be stopped\r\n\t$VMsToStop = @()\r\n\t[array]$VMsToStopAfterLogOffTimeOut = @()\r\n\r\n\tWrite-Log -HostPoolName $HostPoolName -Message 'Find session hosts that are running and allowing new sessions, sort them by number of user sessions'\r\n\tforeach ($SessionHost in ($SessionHosts | Where-Object { $_.properties.allowNewSession } | Sort-Object { $_.properties.sessions }))\r\n {\r\n\t\t$VM = $VMs | Where-Object { $_.id -ieq $SessionHost.properties.resourceId }\r\n\t\tif ($VM.properties.instanceView.statuses[1].displayStatus -ieq 'VM running')\r\n\t\t{\r\n\t\t\tif (!$Ops.nVMsToStop)\r\n\t\t\t{\r\n\t\t\t\t# Done with stopping session hosts that needed to be\r\n\t\t\t\tbreak\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\tif ($SessionHost.properties.sessions -gt 0 -and !$LimitSecondsToForceLogOffUser)\r\n\t\t\t{\r\n\t\t\t\tWrite-Log -HostPoolName $HostPoolName -Warn -Message \"Session host '$($VM.name)' has $($SessionHost.properties.sessions) sessions but limit seconds to force log off user is set to 0, so will not stop any more session hosts (https://aka.ms/wvdscale#how-the-scaling-tool-works)\"\r\n\t\t\t\t# Note: why break ? Because the list this loop iterates through is sorted by number of sessions, if it hits this, the rest of items in the loop will also hit this\r\n\t\t\t\tbreak\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\tTryUpdateSessionHostDrainMode -AllowNewSession $false -Header $Header -HostPoolName $HostPoolName -HostPoolResourceGroupName $HostPoolResourceGroupName -ResourceManagerUrl $ResourceManagerUrl -SessionHostName $VM.name -SubscriptionId $SubscriptionId\r\n\r\n\t\t\t# Note: check if there were new user sessions since session host info was 1st fetched\r\n\t\t\tif ($SessionHost.properties.sessions -gt 0 -and !$LimitSecondsToForceLogOffUser)\r\n\t\t\t{\r\n\t\t\t\tWrite-Log -HostPoolName $HostPoolName -Warn -Message \"Session host '$($VM.name)' has $($SessionHost.properties.sessions) sessions but limit seconds to force log off user is set to 0, so will not stop any more session hosts (https://aka.ms/wvdscale#how-the-scaling-tool-works)\"\r\n\t\t\t\tTryUpdateSessionHostDrainMode -AllowNewSession $true -Header $Header -HostPoolName $HostPoolName -HostPoolResourceGroupName $HostPoolResourceGroupName -ResourceManagerUrl $ResourceManagerUrl -SessionHostName $VM.name -SubscriptionId $SubscriptionId \r\n\t\t\t\tcontinue\r\n\t\t\t}\r\n\r\n\t\t\tif ($SessionHost.properties.sessions -gt 0)\r\n\t\t\t{\r\n\t\t\t\tWrite-Log -HostPoolName $HostPoolName -Message \"Get all user sessions from session host '$($VM.name)'\"\r\n\t\t\t\ttry \r\n\t\t\t\t{\r\n\t\t\t\t\t$Uri = $ResourceManagerUrl + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $HostPoolResourceGroupName + '/providers/Microsoft.DesktopVirtualization/hostPools/' + $HostPoolName + '/sessionHosts/' + $VM.name + '/userSessions?api-version=2022-02-10-preview'\r\n\t\t\t\t\t$UserSessions = Invoke-RestMethod -Headers $Header -Method 'Get' -Uri $Uri\r\n\t\t\t\t}\r\n\t\t\t\tcatch\r\n\t\t\t\t{\r\n\t\t\t\t\tWrite-Log -HostPoolName $HostPoolName -Warn -Message \"Failed to retrieve user sessions of session host '$($VM.name)': $($PSItem | Format-List -Force | Out-String)\"\r\n\t\t\t\t}\r\n\r\n\t\t\t\tWrite-Log -HostPoolName $HostPoolName -Message \"Send log off message to active user sessions on session host: '$($VM.name)'\"\r\n\t\t\t\tforeach ($UserSession in $UserSessions)\r\n\t\t\t\t{\r\n\t\t\t\t\tif($UserSession.properties.sessionState -ine 'Active')\r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tcontinue\r\n\t\t\t\t\t}\r\n\r\n\t\t\t\t\t[string]$SessionID = $UserSession.name.Split('/')[-1]\r\n\t\t\t\t\t[string]$User = $UserSession.properties.activeDirectoryUserName\r\n\t\t\t\t\t\r\n\t\t\t\t\ttry \r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tWrite-Log -HostPoolName $HostPoolName -Message \"Send a log off message to user: '$User', session ID: $SessionID\"\r\n\r\n\t\t\t\t\t\t$Uri = $ResourceManagerUrl + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $HostPoolResourceGroupName + '/providers/Microsoft.DesktopVirtualization/hostPools/' + $HostPoolName + '/sessionHosts/' + $VM.name + '/userSessions/' + $SessionID + '/sendMessage?api-version=2022-02-10-preview'\r\n\t\t\t\t\t\tInvoke-RestMethod -Headers $Header -Method 'Post' -Uri $Uri -Body (@{ 'messageTitle' = $LogOffMessageTitle; 'messageBody' = \"$LogOffMessageBody You will be logged off in $LimitSecondsToForceLogOffUser seconds\" } | ConvertTo-Json) | Out-Null\r\n\t\t\t\t\t}\r\n\t\t\t\t\tcatch \r\n\t\t\t\t\t{\r\n\t\t\t\t\t\tWrite-Log -HostPoolName $HostPoolName -Warn -Message \"Failed to send a log off message to user: '$User', session ID: $SessionID $($PSItem | Format-List -Force | Out-String)\"\r\n\t\t\t\t\t}\r\n\t\t\t\t}\r\n\t\t\t\t$VMsToStopAfterLogOffTimeOut += $VM\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tWrite-Log -HostPoolName $HostPoolName -Message \"Stop session host '$($VM.name)'\"\r\n\t\t\t\t$Uri = $ResourceManagerUrl + $VM.id.TrimStart('/') + '/deallocate?api-version=2023-09-01'\r\n\t\t\t\tInvoke-RestMethod -Headers $Header -Method 'Post' -Uri $Uri | Out-Null\r\n\t\t\t\t$VMsToStop += $VM\r\n\t\t\t}\r\n\r\n\t\t\t--$Ops.nVMsToStop\r\n\t\t\tif ($Ops.nVMsToStop -lt 0) {\r\n\t\t\t\t$Ops.nVMsToStop = 0\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tif ($VMsToStopAfterLogOffTimeOut)\r\n {\r\n\t\tWrite-Log -HostPoolName $HostPoolName -Message \"Wait $LimitSecondsToForceLogOffUser seconds for users to log off\"\r\n\r\n\t\tStart-Sleep -Seconds $LimitSecondsToForceLogOffUser\r\n\r\n\t\tWrite-Log -HostPoolName $HostPoolName -Message \"Force log off users and stop remaining $($VMsToStopAfterLogOffTimeOut.Count) session hosts\"\r\n\t\tforeach ($VM in $VMsToStopAfterLogOffTimeOut)\r\n {\r\n\t\t\t$SessionHost = $SessionHosts | Where-Object { $_.properties.resourceId -ieq $VM.id }\r\n\t\t\tWrite-Log -HostPoolName $HostPoolName -Message \"Force log off $($SessionHost.sessions) users on session host: '$($VM.name)'\"\r\n\t\t\t$VM.UserSessions | TryForceLogOffUser -Header $Header -HostPoolName $HostPoolName -HostPoolResourceGroupName $HostPoolResourceGroupName -ResourceManagerUrl $ResourceManagerUrl -SubscriptionId $SubscriptionId\r\n\t\t\t\r\n\t\t\tWrite-Log -HostPoolName $HostPoolName -Message \"Stop session host '$($VM.name)'\"\r\n\t\t\t$Uri = $ResourceManagerUrl + $VM.id.TrimStart('/') + '/deallocate?api-version=2023-09-01'\r\n\t\t\tInvoke-RestMethod -Headers $Header -Method 'Post' -Uri $Uri | Out-Null\r\n\t\t\t$VMsToStop += $VM\r\n\t\t}\r\n\t}\r\n\r\n\t# Check if there were enough number of session hosts to stop\r\n\tif ($Ops.nVMsToStop)\r\n {\r\n\t\tWrite-Log -HostPoolName $HostPoolName -Warn -Message \"Not enough session hosts to stop. Still need to stop $($Ops.nVMsToStop) VMs\"\r\n\t}\r\n\r\n\t# Wait for the session hosts to stop / deallocate\r\n\tWrite-Log -HostPoolName $HostPoolName -Message \"Wait for session hosts to stop / deallocate\"\r\n\twhile($VMsToStop.Count -gt 0)\r\n\t{\r\n\t\tforeach($VMToStop in $VMsToStop)\r\n\t\t{\r\n\t\t\t$Uri = $ResourceManagerUrl + $VMToStop.id.TrimStart('/') + '?$expand=instanceView&api-version=2024-03-01'\r\n\t\t\t$VMPowerState = (Invoke-RestMethod -Headers $Header -Method 'Get' -Uri $Uri).properties.instanceView.statuses[1].displayStatus\r\n\t\t\tif ($VMPowerState -eq 'VM deallocated')\r\n\t\t\t{\r\n\t\t\t\tWrite-Log -HostPoolName $HostPoolName -Message \"Session host '$($VMToStop.name)' is stopping\"\r\n\t\t\t\t$SessionHost = $SessionHosts | Where-Object { $_.properties.resourceId -ieq $VMToStop.id }\r\n\t\t\t\tTryResetSessionHostDrainModeAndUserSessions -Header $Header -HostPoolName $HostPoolName -HostPoolResourceGroupName $HostPoolResourceGroupName -ResourceManagerUrl $ResourceManagerUrl -SessionHostName $VMToStop.name -SessionHostSessions $SessionHost.properties.sessions -SubscriptionId $SubscriptionId\r\n\t\t\t\t$VMsToStop = $VMsToStop -ne $VMToStop\r\n\t\t\t}\r\n\t\t}\r\n\t\tStart-Sleep -Seconds 30\r\n\t}\r\n\r\n\tWrite-Log -HostPoolName $HostPoolName -Message 'All required session hosts have stopped.'\r\n\tWrite-Log -HostPoolName $HostPoolName -Message 'End'\r\n\treturn\r\n\t#endregion\r\n}\r\ncatch \r\n{\r\n\t$ErrContainer = $PSItem\r\n\t# $ErrContainer = $_\r\n\r\n\t[string]$ErrMsg = $ErrContainer | Format-List -Force | Out-String\r\n\t$ErrMsg += \"Version: $Version`n\"\r\n\r\n\tif (Get-Command 'Write-Log' -ErrorAction:SilentlyContinue)\r\n {\r\n\t\tWrite-Log -HostPoolName $HostPoolName -Err -Message $ErrMsg -ErrorAction:Continue\r\n\t}\r\n\telse\r\n {\r\n\t\tWrite-Error $ErrMsg -ErrorAction:Continue\r\n\t}\r\n\r\n\tthrow [System.Exception]::new($ErrMsg, $ErrContainer.Exception)\r\n}", - "$fxv#2": "# Authentication is provided in the script", + "$fxv#0": "Param(\r\n [string]$HostPoolResourceId,\r\n [string]$ResourceGroupName,\r\n [string]$ResourceManagerUri,\r\n [string]$ScalingPlanName,\r\n [string]$SubscriptionId,\r\n [string]$UserAssignedIdentityClientId\r\n)\r\n\r\n$ErrorActionPreference = 'Stop'\r\n$WarningPreference = 'SilentlyContinue'\r\n\r\n# Fix the resource manager URI since only AzureCloud contains a trailing slash\r\n$ResourceManagerUriFixed = if($ResourceManagerUri[-1] -eq '/'){$ResourceManagerUri} else {$ResourceManagerUri + '/'}\r\n\r\n# Get an access token for Azure resources\r\n$AzureManagementAccessToken = (Invoke-RestMethod `\r\n -Headers @{Metadata=\"true\"} `\r\n -Uri $('http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=' + $ResourceManagerUriFixed + '&client_id=' + $UserAssignedIdentityClientId)).access_token\r\n\r\n# Set header for Azure Management API\r\n$AzureManagementHeader = @{\r\n 'Content-Type'='application/json'\r\n 'Authorization'='Bearer ' + $AzureManagementAccessToken\r\n}\r\n\r\n# Check if the scaling plan exists: https://learn.microsoft.com/rest/api/desktopvirtualization/scaling-plans/list-by-resource-group\r\n$ScalingPlanExists = (Invoke-RestMethod `\r\n -Headers $AzureManagementHeader `\r\n -Method 'GET' `\r\n -Uri $($ResourceManagerUriFixed + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $ResourceGroupName + '/providers/Microsoft.DesktopVirtualization/scalingPlans?api-version=2024-04-03')).value | Where-Object {$_.name -eq $ScalingPlanName}\r\n\r\n# Disable autoscale for the host pool: https://learn.microsoft.com/rest/api/desktopvirtualization/scaling-plans/update\r\nif ($ScalingPlanExists)\r\n{\r\n Invoke-RestMethod `\r\n -Body (@{properties = @{hostPoolReferences = @(@{hostPoolArmPath = $HostPoolResourceId; scalingPlanEnabled = $false})}} | ConvertTo-Json) `\r\n -Headers $AzureManagementHeader `\r\n -Method 'PATCH' `\r\n -Uri $($ResourceManagerUriFixed + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $ResourceGroupName + '/providers/Microsoft.DesktopVirtualization/scalingPlans/' + $ScalingPlanName + '?api-version=2024-04-03') | Out-Null\r\n}", "availabilitySetNamePrefix": "[parameters('namingConvention').availabilitySet]", "tagsAvailabilitySets": "[union(createObject('cm-resource-parent', format('{0}/resourceGroups/{1}/providers/Microsoft.DesktopVirtualization/hostpools/{2}', subscription().id, parameters('resourceGroupManagement'), parameters('hostPoolName'))), if(contains(parameters('tags'), 'Microsoft.Compute/availabilitySets'), parameters('tags')['Microsoft.Compute/availabilitySets'], createObject()), parameters('mlzTags'))]", "tagsNetworkInterfaces": "[union(createObject('cm-resource-parent', format('{0}/resourceGroups/{1}/providers/Microsoft.DesktopVirtualization/hostpools/{2}', subscription().id, parameters('resourceGroupManagement'), parameters('hostPoolName'))), if(contains(parameters('tags'), 'Microsoft.Network/networkInterfaces'), parameters('tags')['Microsoft.Network/networkInterfaces'], createObject()), parameters('mlzTags'))]", @@ -12897,7 +12950,7 @@ }, "resources": [ { - "condition": "[and(parameters('pooledHostPool'), equals(parameters('availability'), 'AvailabilitySets'))]", + "condition": "[and(equals(parameters('hostPoolType'), 'Pooled'), equals(parameters('availability'), 'AvailabilitySets'))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('deploy-avail-{0}', parameters('deploymentNameSuffix'))]", @@ -13036,6 +13089,120 @@ } } }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('deploy-disable-autoscale-{0}', parameters('deploymentNameSuffix'))]", + "resourceGroup": "[parameters('resourceGroupManagement')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "name": { + "value": "Disable-Autoscale" + }, + "parameters": { + "value": [ + { + "name": "HostPoolResourceId", + "value": "[parameters('hostPoolResourceId')]" + }, + { + "name": "ResourceGroupName", + "value": "[parameters('resourceGroupManagement')]" + }, + { + "name": "ResourceManagerUri", + "value": "[environment().resourceManager]" + }, + { + "name": "ScalingPlanName", + "value": "[parameters('namingConvention').scalingPlan]" + }, + { + "name": "SubscriptionId", + "value": "[subscription().subscriptionId]" + }, + { + "name": "UserAssignedidentityClientId", + "value": "[parameters('deploymentUserAssignedIdentityClientId')]" + } + ] + }, + "script": { + "value": "[variables('$fxv#0')]" + }, + "tags": { + "value": "[variables('tagsVirtualMachines')]" + }, + "virtualMachineName": { + "value": "[parameters('managementVirtualMachineName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.34.60546", + "templateHash": "5317974790875443937" + } + }, + "parameters": { + "asyncExecution": { + "type": "bool", + "defaultValue": false + }, + "location": { + "type": "string" + }, + "name": { + "type": "string" + }, + "parameters": { + "type": "array", + "defaultValue": [] + }, + "script": { + "type": "string" + }, + "tags": { + "type": "object" + }, + "treatFailureAsDeploymentFailure": { + "type": "bool", + "defaultValue": true + }, + "virtualMachineName": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Compute/virtualMachines/runCommands", + "apiVersion": "2023-09-01", + "name": "[format('{0}/{1}', parameters('virtualMachineName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "asyncExecution": "[parameters('asyncExecution')]", + "parameters": "[parameters('parameters')]", + "source": { + "script": "[parameters('script')]" + }, + "treatFailureAsDeploymentFailure": "[parameters('treatFailureAsDeploymentFailure')]" + } + } + ] + } + } + }, { "copy": { "name": "virtualMachines", @@ -13119,9 +13286,6 @@ "hostPoolName": { "value": "[parameters('hostPoolName')]" }, - "hostPoolType": { - "value": "[parameters('hostPoolType')]" - }, "imageVersionResourceId": { "value": "[parameters('imageVersionResourceId')]" }, @@ -13201,7 +13365,7 @@ "_generator": { "name": "bicep", "version": "0.31.34.60546", - "templateHash": "7274055108234907992" + "templateHash": "16117911452479927621" } }, "parameters": { @@ -13271,9 +13435,6 @@ "hostPoolName": { "type": "string" }, - "hostPoolType": { - "type": "string" - }, "imageOffer": { "type": "string" }, @@ -13397,7 +13558,6 @@ "Standard_NV36adms_A10_v5", "Standard_NV72ads_A10_v5" ], - "pooledHostPool": "[equals(split(parameters('hostPoolType'), ' ')[0], 'Pooled')]", "sessionHostNamePrefix": "[replace(parameters('virtualMachineNamePrefix'), parameters('serviceToken'), '')]", "storageAccountToken": "[format('{0}??', parameters('storageAccountPrefix'))]" }, @@ -13901,7 +14061,7 @@ ] }, { - "condition": "[and(parameters('enableDrainMode'), variables('pooledHostPool'))]", + "condition": "[parameters('enableDrainMode')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('deploy-drain-mode-{0}-{1}', parameters('batchCount'), parameters('deploymentNameSuffix'))]", @@ -14038,11 +14198,12 @@ } }, "dependsOn": [ - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupHosts')), 'Microsoft.Resources/deployments', format('deploy-avail-{0}', parameters('deploymentNameSuffix')))]" + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupHosts')), 'Microsoft.Resources/deployments', format('deploy-avail-{0}', parameters('deploymentNameSuffix')))]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-disable-autoscale-{0}', parameters('deploymentNameSuffix')))]" ] }, { - "condition": "[and(parameters('enableRecoveryServices'), contains(parameters('hostPoolType'), 'Personal'))]", + "condition": "[and(parameters('enableRecoveryServices'), equals(parameters('hostPoolType'), 'Personal'))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('deploy-recovery-services-{0}', parameters('deploymentNameSuffix'))]", @@ -14245,10 +14406,9 @@ ] }, { - "condition": "[and(parameters('enableScalingTool'), parameters('pooledHostPool'))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('deploy-scaling-tool-{0}', parameters('deploymentNameSuffix'))]", + "name": "[format('deploy-scaling-plan-{0}', parameters('deploymentNameSuffix'))]", "resourceGroup": "[parameters('resourceGroupManagement')]", "properties": { "expressionEvaluationOptions": { @@ -14256,21 +14416,47 @@ }, "mode": "Incremental", "parameters": { - "files": { - "value": { - "requirements.psd1": "[variables('$fxv#0')]", - "run.ps1": "[variables('$fxv#1')]", - "../profile.ps1": "[variables('$fxv#2')]" - } + "deploymentUserAssignedIdentityPrincipalId": { + "value": "[parameters('deploymentUserAssignedIdentityPrincipalId')]" }, - "functionAppName": { - "value": "[parameters('functionAppName')]" + "enableAvdInsights": { + "value": "[parameters('enableAvdInsights')]" + }, + "hostPoolResourceId": { + "value": "[parameters('hostPoolResourceId')]" + }, + "hostPoolType": { + "value": "[parameters('hostPoolType')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "logAnalyticsWorkspaceResourceId": { + "value": "[parameters('logAnalyticsWorkspaceResourceId')]" + }, + "scalingPlanDiagnosticSettingName": { + "value": "[parameters('namingConvention').scalingPlanDiagnosticSetting]" + }, + "scalingPlanName": { + "value": "[parameters('namingConvention').scalingPlan]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "timeZone": { + "value": "[parameters('timeZone')]" }, - "functionName": { - "value": "avd-scaling-tool" + "weekdaysOffPeakStartTime": { + "value": "[parameters('scalingWeekdaysOffPeakStartTime')]" }, - "schedule": { - "value": "0 */15 * * * *" + "weekdaysPeakStartTime": { + "value": "[parameters('scalingWeekdaysPeakStartTime')]" + }, + "weekendsOffPeakStartTime": { + "value": "[parameters('scalingWeekendsOffPeakStartTime')]" + }, + "weekendsPeakStartTime": { + "value": "[parameters('scalingWeekendsPeakStartTime')]" } }, "template": { @@ -14280,46 +14466,207 @@ "_generator": { "name": "bicep", "version": "0.31.34.60546", - "templateHash": "15160754302365390942" + "templateHash": "665492750417866100" } }, "parameters": { - "files": { - "type": "object" + "deploymentUserAssignedIdentityPrincipalId": { + "type": "string", + "metadata": { + "description": "Required. Principal ID of the user assigned identity." + } }, - "functionAppName": { - "type": "string" + "enableAvdInsights": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Optional. Enable AVD Insights." + } }, - "functionName": { - "type": "string" + "hostPoolResourceId": { + "type": "string", + "metadata": { + "description": "Required. Host pool resource ID for the Scaling Plan." + } }, - "schedule": { - "type": "string" + "location": { + "type": "string", + "metadata": { + "description": "Required. Location of the Scaling Plan. The location must match the location of the host pool." + } + }, + "logAnalyticsWorkspaceResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Optional. Resource ID of the Log Analytics workspace for the Scaling Plan." + } + }, + "hostPoolType": { + "type": "string", + "allowedValues": [ + "Personal", + "Pooled" + ], + "metadata": { + "description": "Required. Host pool type of the Scaling Plan." + } + }, + "scalingPlanDiagnosticSettingName": { + "type": "string", + "metadata": { + "description": "Required. Name of the diagnostic setting for the Scaling Plan." + } + }, + "scalingPlanName": { + "type": "string", + "metadata": { + "description": "Required. Name of the Scaling Plan." + } + }, + "supportedClouds": { + "type": "array", + "defaultValue": [ + "AzureCloud", + "AzureUSGovernment" + ], + "metadata": { + "description": "Optional. Supported clouds." + } + }, + "tags": { + "type": "object", + "defaultValue": {}, + "metadata": { + "description": "Optional. Tags of the resource." + } + }, + "timeZone": { + "type": "string", + "defaultValue": "UTC", + "metadata": { + "description": "Optional. Time zone of the Scaling Plan. Defaults to UTC." + } + }, + "weekdaysOffPeakStartTime": { + "type": "string", + "metadata": { + "description": "Required. Off peak start time for weekdays in HH:mm format." + } + }, + "weekendsOffPeakStartTime": { + "type": "string", + "metadata": { + "description": "Required. Off peak start time for weekends in HH:mm format." + } + }, + "weekdaysPeakStartTime": { + "type": "string", + "metadata": { + "description": "Required. Peak start time for weekdays in HH:mm format." + } + }, + "weekendsPeakStartTime": { + "type": "string", + "metadata": { + "description": "Required. Peak start time for weekends in HH:mm format." + } } }, + "variables": { + "schedules": "[if(equals(parameters('hostPoolType'), 'Pooled'), createArray(createObject('daysOfWeek', createArray('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'), 'offPeakLoadBalancingAlgorithm', 'DepthFirst', 'offPeakStartTime', createObject('hour', split(parameters('weekdaysOffPeakStartTime'), ':')[0], 'minute', split(parameters('weekdaysOffPeakStartTime'), ':')[1]), 'peakLoadBalancingAlgorithm', 'BreadthFirst', 'peakStartTime', createObject('hour', split(parameters('weekdaysPeakStartTime'), ':')[0], 'minute', split(parameters('weekdaysPeakStartTime'), ':')[1]), 'rampDownCapacityThresholdPct', 90, 'rampDownForceLogoffUsers', false(), 'rampDownLoadBalancingAlgorithm', 'DepthFirst', 'rampDownMinimumHostsPct', 0, 'rampDownNotificationMessage', 'Ramping down the AVD session hosts to support low demand.', 'rampDownStartTime', createObject('hour', sub(int(split(parameters('weekdaysOffPeakStartTime'), ':')[0]), 1), 'minute', split(parameters('weekdaysOffPeakStartTime'), ':')[1]), 'rampDownStopHostsWhen', 'ZeroSessions', 'rampDownWaitTimeMinutes', 0, 'rampUpCapacityThresholdPct', 70, 'rampUpLoadBalancingAlgorithm', 'BreadthFirst', 'rampUpMinimumHostsPct', 25, 'rampUpStartTime', createObject('hour', sub(int(split(parameters('weekdaysPeakStartTime'), ':')[0]), 1), 'minute', split(parameters('weekdaysPeakStartTime'), ':')[1])), createObject('daysOfWeek', createArray('Saturday', 'Sunday'), 'offPeakLoadBalancingAlgorithm', 'DepthFirst', 'offPeakStartTime', createObject('hour', split(parameters('weekendsOffPeakStartTime'), ':')[0], 'minute', split(parameters('weekendsOffPeakStartTime'), ':')[1]), 'peakLoadBalancingAlgorithm', 'BreadthFirst', 'peakStartTime', createObject('hour', split(parameters('weekendsPeakStartTime'), ':')[0], 'minute', split(parameters('weekendsPeakStartTime'), ':')[1]), 'rampDownCapacityThresholdPct', 90, 'rampDownForceLogoffUsers', false(), 'rampDownLoadBalancingAlgorithm', 'DepthFirst', 'rampDownMinimumHostsPct', 0, 'rampDownNotificationMessage', 'Ramping down the AVD session hosts to support low demand.', 'rampDownStartTime', createObject('hour', sub(int(split(parameters('weekendsOffPeakStartTime'), ':')[0]), 1), 'minute', split(parameters('weekendsOffPeakStartTime'), ':')[1]), 'rampDownStopHostsWhen', 'ZeroSessions', 'rampDownWaitTimeMinutes', 0, 'rampUpCapacityThresholdPct', 90, 'rampUpLoadBalancingAlgorithm', 'BreadthFirst', 'rampUpMinimumHostsPct', 25, 'rampUpStartTime', createObject('hour', sub(int(split(parameters('weekendsPeakStartTime'), ':')[0]), 1), 'minute', split(parameters('weekendsPeakStartTime'), ':')[1]))), createArray(createObject('daysOfWeek', createArray('Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'), 'offPeakActionOnDisconnect', 'None', 'offPeakActionOnLogoff', 'Deallocate', 'offPeakMinutesToWaitOnDisconnect', 0, 'offPeakMinutesToWaitOnLogoff', 0, 'offPeakStartTime', createObject('hour', split(parameters('weekdaysOffPeakStartTime'), ':')[0], 'minute', split(parameters('weekdaysOffPeakStartTime'), ':')[1]), 'offPeakStartVMOnConnect', 'Enable', 'peakActionOnDisconnect', 'None', 'peakActionOnLogoff', 'Deallocate', 'peakMinutesToWaitOnDisconnect', 0, 'peakMinutesToWaitOnLogoff', 0, 'peakStartTime', createObject('hour', split(parameters('weekdaysPeakStartTime'), ':')[0], 'minute', split(parameters('weekdaysPeakStartTime'), ':')[1]), 'peakStartVMOnConnect', 'Enable', 'rampDownActionOnDisconnect', 'None', 'rampDownActionOnLogoff', 'Deallocate', 'rampDownMinutesToWaitOnDisconnect', 0, 'rampDownMinutesToWaitOnLogoff', 0, 'rampDownStartTime', createObject('hour', sub(int(split(parameters('weekdaysOffPeakStartTime'), ':')[0]), 1), 'minute', split(parameters('weekdaysOffPeakStartTime'), ':')[1]), 'rampDownStartVMOnConnect', 'Enable', 'rampUpActionOnDisconnect', 'None', 'rampUpActionOnLogoff', 'None', 'rampUpAutoStartHosts', 'WithAssignedUser', 'rampUpMinutesToWaitOnDisconnect', 0, 'rampUpMinutesToWaitOnLogoff', 0, 'rampUpStartTime', createObject('hour', sub(int(split(parameters('weekdaysPeakStartTime'), ':')[0]), 1), 'minute', split(parameters('weekdaysPeakStartTime'), ':')[1]), 'rampUpStartVMOnConnect', 'Enable'), createObject('daysOfWeek', createArray('Saturday', 'Sunday'), 'rampUpStartTime', createObject('hour', sub(int(split(parameters('weekendsPeakStartTime'), ':')[0]), 1), 'minute', split(parameters('weekendsPeakStartTime'), ':')[1]), 'peakStartTime', createObject('hour', split(parameters('weekendsPeakStartTime'), ':')[0], 'minute', split(parameters('weekendsPeakStartTime'), ':')[1]), 'peakMinutesToWaitOnDisconnect', 0, 'peakActionOnDisconnect', 'None', 'peakMinutesToWaitOnLogoff', 0, 'peakActionOnLogoff', 'Deallocate', 'peakStartVMOnConnect', 'Enable', 'rampDownStartTime', createObject('hour', sub(int(split(parameters('weekendsOffPeakStartTime'), ':')[0]), 1), 'minute', split(parameters('weekendsOffPeakStartTime'), ':')[1]), 'rampDownMinutesToWaitOnDisconnect', 0, 'rampDownActionOnDisconnect', 'None', 'rampDownMinutesToWaitOnLogoff', 0, 'rampDownActionOnLogoff', 'Deallocate', 'rampDownStartVMOnConnect', 'Enable', 'rampUpAutoStartHosts', 'WithAssignedUser', 'rampUpStartVMOnConnect', 'Enable', 'rampUpMinutesToWaitOnDisconnect', 0, 'rampUpActionOnDisconnect', 'None', 'rampUpMinutesToWaitOnLogoff', 0, 'rampUpActionOnLogoff', 'None', 'offPeakStartTime', createObject('hour', split(parameters('weekendsOffPeakStartTime'), ':')[0], 'minute', split(parameters('weekendsOffPeakStartTime'), ':')[1]), 'offPeakMinutesToWaitOnDisconnect', 0, 'offPeakActionOnDisconnect', 'None', 'offPeakMinutesToWaitOnLogoff', 0, 'offPeakActionOnLogoff', 'Deallocate', 'offPeakStartVMOnConnect', 'Enable')))]" + }, "resources": [ { - "type": "Microsoft.Web/sites/functions", - "apiVersion": "2020-12-01", - "name": "[format('{0}/{1}', parameters('functionAppName'), parameters('functionName'))]", + "type": "Microsoft.DesktopVirtualization/scalingPlans", + "apiVersion": "2023-09-05", + "name": "[parameters('scalingPlanName')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", "properties": { - "config": { - "disabled": false, - "bindings": [ - { - "name": "Timer", - "type": "timerTrigger", - "direction": "in", - "schedule": "[parameters('schedule')]" - } - ] - }, - "files": "[parameters('files')]" + "timeZone": "[parameters('timeZone')]", + "hostPoolType": "[parameters('hostPoolType')]", + "exclusionTag": "excludeFromAutoscale", + "schedules": [], + "hostPoolReferences": [ + { + "hostPoolArmPath": "[parameters('hostPoolResourceId')]", + "scalingPlanEnabled": true + } + ] } + }, + { + "copy": { + "name": "schedules_Pooled", + "count": "[length(range(0, length(variables('schedules'))))]" + }, + "condition": "[equals(parameters('hostPoolType'), 'Pooled')]", + "type": "Microsoft.DesktopVirtualization/scalingPlans/pooledSchedules", + "apiVersion": "2024-04-03", + "name": "[format('{0}/{1}', parameters('scalingPlanName'), if(equals(range(0, length(variables('schedules')))[copyIndex()], 0), 'Weekdays', 'Weekends'))]", + "properties": "[variables('schedules')[range(0, length(variables('schedules')))[copyIndex()]]]", + "dependsOn": [ + "[resourceId('Microsoft.DesktopVirtualization/scalingPlans', parameters('scalingPlanName'))]" + ] + }, + { + "copy": { + "name": "schedule_Personal", + "count": "[length(range(0, length(variables('schedules'))))]" + }, + "condition": "[equals(parameters('hostPoolType'), 'Personal')]", + "type": "Microsoft.DesktopVirtualization/scalingPlans/personalSchedules", + "apiVersion": "2024-04-03", + "name": "[format('{0}/{1}', parameters('scalingPlanName'), if(equals(range(0, length(variables('schedules')))[copyIndex()], 0), 'Weekdays', 'Weekends'))]", + "properties": "[variables('schedules')[range(0, length(variables('schedules')))[copyIndex()]]]", + "dependsOn": [ + "[resourceId('Microsoft.DesktopVirtualization/scalingPlans', parameters('scalingPlanName'))]" + ] + }, + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.DesktopVirtualization/scalingPlans/{0}', parameters('scalingPlanName'))]", + "name": "[guid(parameters('deploymentUserAssignedIdentityPrincipalId'), '082f0a83-3be5-4ba1-904c-961cca79b387', resourceId('Microsoft.DesktopVirtualization/scalingPlans', parameters('scalingPlanName')))]", + "properties": { + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', '082f0a83-3be5-4ba1-904c-961cca79b387')]", + "principalId": "[parameters('deploymentUserAssignedIdentityPrincipalId')]", + "principalType": "ServicePrincipal" + }, + "dependsOn": [ + "[resourceId('Microsoft.DesktopVirtualization/scalingPlans', parameters('scalingPlanName'))]", + "schedule_Personal", + "schedules_Pooled" + ] + }, + { + "condition": "[and(parameters('enableAvdInsights'), contains(parameters('supportedClouds'), environment().name))]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.DesktopVirtualization/scalingPlans/{0}', parameters('scalingPlanName'))]", + "name": "[parameters('scalingPlanDiagnosticSettingName')]", + "properties": { + "logs": [ + { + "categoryGroup": "allLogs", + "enabled": true + } + ], + "workspaceId": "[parameters('logAnalyticsWorkspaceResourceId')]" + }, + "dependsOn": [ + "[extensionResourceId(resourceId('Microsoft.DesktopVirtualization/scalingPlans', parameters('scalingPlanName')), 'Microsoft.Authorization/roleAssignments', guid(parameters('deploymentUserAssignedIdentityPrincipalId'), '082f0a83-3be5-4ba1-904c-961cca79b387', resourceId('Microsoft.DesktopVirtualization/scalingPlans', parameters('scalingPlanName'))))]", + "[resourceId('Microsoft.DesktopVirtualization/scalingPlans', parameters('scalingPlanName'))]" + ] } ] } - } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-recovery-services-{0}', parameters('deploymentNameSuffix')))]", + "virtualMachines" + ] } ] } diff --git a/src/bicep/add-ons/azure-virtual-desktop/solution.parameters.json b/src/bicep/add-ons/azure-virtual-desktop/solution.parameters.json new file mode 100644 index 000000000..187a39a0d --- /dev/null +++ b/src/bicep/add-ons/azure-virtual-desktop/solution.parameters.json @@ -0,0 +1,207 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "activeDirectorySolution": { + "value": "ActiveDirectoryDomainServices" + }, + "availability": { + "value": "AvailabilityZones" + }, + "availabilityZones": { + "value": ["1","2","3"] + }, + "avdConfigurationZipFileName": { + "value": "Configuration_1.0.02790.438.zip" + }, + "avdObjectId": { + "value": "cdcfb416-e2fe-41e2-be12-33813c1cd427" + }, + "azureNetAppFilesSubnetAddressPrefix": { + "value": "" + }, + "customRdpProperty": { + "value": "audiocapturemode:i:1;camerastoredirect:s:*;use multimon:i:0;drivestoredirect:s:;encode redirected video capture:i:1;redirected video capture encoding quality:i:1;audiomode:i:0;devicestoredirect:s:;redirectclipboard:i:0;redirectcomports:i:0;redirectlocation:i:1;redirectprinters:i:0;redirectsmartcards:i:1;redirectwebauthn:i:1;usbdevicestoredirect:s:;keyboardhook:i:2;" + }, + "deployActivityLogDiagnosticSetting": { + "value": true + }, + "deployDefender": { + "value": true + }, + "deployNetworkWatcher": { + "value": true + }, + "deployPolicy": { + "value": true + }, + "desktopFriendlyName": { + "value": "" + }, + "diskSku": { + "value": "Premium_LRS" + }, + "domainJoinUserPrincipalName": { + "value": "xadmin" + }, + "domainName": { + "value": "contoso.com" + }, + "drainMode": { + "value": true + }, + "emailSecurityContact": { + "value": "email@contoso.com" + }, + "enableAcceleratedNetworking": { + "value": true + }, + "enableApplicationInsights": { + "value": true + }, + "enableAvdInsights": { + "value": true + }, + "enableTelemetry": { + "value": false + }, + "environmentAbbreviation": { + "value": "dev" + }, + "existingSharedActiveDirectoryConnection": { + "value": false + }, + "existingFeedWorkspaceResourceId": { + "value": "" + }, + "fslogixShareSizeInGB": { + "value": 100 + }, + "fslogixContainerType": { + "value": "ProfileContainer" + }, + "fslogixStorageService": { + "value": "AzureFiles Premium" + }, + "functionAppSubnetAddressPrefix": { + "value": "10.0.141.128/26" + }, + "hostPoolPublicNetworkAccess": { + "value": "Enabled" + }, + "hostPoolType": { + "value": "Pooled" + }, + "hubAzureFirewallResourceId": { + "value": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mlz-rg-network-hub-dev-use2/providers/Microsoft.Network/azureFirewalls/mlz-afw-hub-dev-use2" + }, + "hubVirtualNetworkResourceId": { + "value": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mlz-rg-network-hub-dev-use2/providers/Microsoft.Network/virtualNetworks/mlz-vnet-hub-dev-use2" + }, + "identifier": { + "value": "mlz" + }, + "imageOffer": { + "value": "windows-11" + }, + "imagePublisher": { + "value": "MicrosoftWindowsDesktop" + }, + "imageSku": { + "value": "win11-23h2-avd" + }, + "imageVersionResourceId": { + "value": "" + }, + "locationVirtualMachines": { + "value": "eastus2" + }, + "logAnalyticsWorkspaceRetention": { + "value": 30 + }, + "logAnalyticsWorkspaceSku": { + "value": "PerGB2018" + }, + "operationsLogAnalyticsWorkspaceResourceId": { + "value": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mlz-rg-network-operations-dev-use2/providers/Microsoft.OperationalInsights/workspaces/mlz-log-operations-dev-use2" + }, + "organizationalUnitPath": { + "value": "OU=Computers,OU=AVD,DC=contoso,DC=com" + }, + "policy": { + "value": "NISTRev4" + }, + "privateLinkScopeResourceId": { + "value": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mlz-rg-network-operations-dev-use2/providers/microsoft.insights/privatelinkscopes/mlz-pls-operations-dev-use2" + }, + "profile": { + "value": "Generic" + }, + "recoveryServices": { + "value": true + }, + "scalingWeekdaysOffPeakStartTime": { + "value": "17:00" + }, + "scalingWeekdaysPeakStartTime": { + "value": "09:00" + }, + "scalingWeekendsOffPeakStartTime": { + "value": "17:00" + }, + "scalingWeekendsPeakStartTime": { + "value": "09:00" + }, + "securityPrincipals": { + "value": [{"name":"AVD","objectId":"00000000-0000-0000-0000-000000000000"}] + }, + "sessionHostCount": { + "value": 1 + }, + "sessionHostIndex": { + "value": 0 + }, + "sharedServicesSubnetResourceId": { + "value": "/subscriptions/00000000-0000-0000-0000-000000000000/resourceGroups/mlz-rg-network-sharedServices-dev-use2/providers/Microsoft.Network/virtualNetworks/mlz-vnet-sharedServices-dev-use2/subnets/mlz-snet-sharedServices-dev-use2" + }, + "stampIndex": { + "value": 0 + }, + "storageCount": { + "value": 1 + }, + "storageIndex": { + "value": 0 + }, + "subnetAddressPrefixes": { + "value": ["10.0.140.0/24","10.0.141.0/26"] + }, + "tags": { + "value": {} + }, + "usersPerCore": { + "value": 2 + }, + "validationEnvironment": { + "value": false + }, + "virtualMachineSize": { + "value": "Standard_D4ads_v5" + }, + "virtualMachineUsername": { + "value": "xadmin" + }, + "virtualMachineVirtualCpuCount": { + "value": 4 + }, + "virtualNetworkAddressPrefixes": { + "value": ["10.0.140.0/23"] + }, + "workspaceFriendlyName": { + "value": "mlz-vdws-avd-use2-dev" + }, + "workspacePublicNetworkAccess": { + "value": "Enabled" + } + } +} \ No newline at end of file diff --git a/src/bicep/add-ons/azure-virtual-desktop/uiDefinition.json b/src/bicep/add-ons/azure-virtual-desktop/uiDefinition.json index 92ed99d8e..cce9c2ef3 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/uiDefinition.json +++ b/src/bicep/add-ons/azure-virtual-desktop/uiDefinition.json @@ -1122,32 +1122,6 @@ "path": "/v1.0/serviceprincipals?$filter=appId eq '9cdead84-a844-4324-93f2-b2e6bb768d07'" } }, - { - "name": "startVmOnConnect", - "type": "Microsoft.Common.Section", - "label": "Start VM On Connect", - "visible": "[empty(steps('management').servicePrincipalsApi)]", - "elements": [ - { - "name": "objectId", - "type": "Microsoft.Common.TextBox", - "label": "AVD Object ID", - "visible": true, - "defaultValue": "", - "placeholder": "", - "toolTip": "Input the object ID for the Azure Virtual Desktop enterprise application in Entra ID. The application ID for the principal is 9cdead84-a844-4324-93f2-b2e6bb768d07.", - "constraints": { - "required": true, - "validations": [ - { - "regex": "^[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}$", - "message": "The value must be a globally unique ID." - } - ] - } - } - ] - }, { "name": "drainMode", "type": "Microsoft.Common.Section", @@ -1158,114 +1132,81 @@ "name": "enableDrainMode", "type": "Microsoft.Common.CheckBox", "label": "Enable drain mode?", - "defaultValue": false, + "defaultValue": true, "toolTip": "Enables drain mode on the AVD session hosts so the virtual machines cannot be accessed until they have been validated." } ] }, { - "name": "scalingTool", - "label": "Scaling Tool", + "name": "scalingPlan", + "label": "Scaling Plan", "type": "Microsoft.Common.Section", - "visible": "[equals(steps('controlPlane').hostPool.hostPoolType, 'Pooled')]", + "visible": true, "elements": [ { - "name": "deployScalingTool", - "type": "Microsoft.Common.CheckBox", - "label": "Deploy scaling tool?", - "visible": true, - "defaultValue": false, - "toolTip": "Choose whether to deploy the required resources to enable the Scaling Tool. This tool should only be used when scaling plans are not available. It has been enhanced for zero-trust compliance and does not rely on AZ modules." - }, - { - "name": "beginPeakTime", + "name": "weekdaysPeakStartTime", "type": "Microsoft.Common.TextBox", - "label": "Begin Peak Time", - "visible": "[steps('management').scalingTool.deployScalingTool]", - "defaultValue": "8:00", - "placeholder": "", - "toolTip": "Input the time when peak hours begin for your end users.", - "constraints": { - "required": true, - "validations": [ - { - "regex": "^[012]?[0-9]:[0-5][0-9]$", - "message": "The value must be in a proper time format with a two digit hour and two digit minutes, e.g. 12am should be input as 00:00." - } - ] - } - }, - { - "name": "endPeakTime", - "type": "Microsoft.Common.TextBox", - "label": "End Peak Time", - "visible": "[steps('management').scalingTool.deployScalingTool]", - "defaultValue": "17:00", - "placeholder": "", + "label": "Weekdays - Peak Start Time", + "visible": true, + "defaultValue": "09:00", "toolTip": "Input the time when peak hours end for your end users.", "constraints": { "required": true, "validations": [ { - "regex": "^[012]?[0-9]:[0-5][0-9]$", + "regex": "^[012][0-9]:[0-5][0-9]$", "message": "The value must be in a proper time format with a two digit hour and two digit minutes, e.g. 8pm should be input as 20:00." } ] } }, { - "name": "forceLogOff", + "name": "weekdaysOffPeakStartTime", "type": "Microsoft.Common.TextBox", - "label": "Force Log Off (Seconds)", - "visible": "[steps('management').scalingTool.deployScalingTool]", - "defaultValue": "0", - "toolTip": "Use this setting to force logoff users if session time limit settings cannot be used.", - "placeholder": "", - "multiLine": false, + "label": "Weekdays - Off Peak Start Time", + "visible": true, + "defaultValue": "17:00", + "toolTip": "Input the time when peak hours begin for your end users during the work week, Monday - Friday.", "constraints": { "required": true, "validations": [ { - "regex": "^[0-9]{1,3}$", - "message": "The value must be between 1 and 3 digits." + "regex": "^[012][0-9]:[0-5][0-9]$", + "message": "The value must be in a proper time format with a two digit hour and two digit minutes, e.g. 12am should be input as 00:00." } ] } }, { - "name": "minimumHosts", + "name": "weekendsPeakStartTime", "type": "Microsoft.Common.TextBox", - "label": "Minimum Number of Session Hosts", - "visible": "[steps('management').scalingTool.deployScalingTool]", - "defaultValue": "0", - "toolTip": "Use this setting to determine the minimum number of sessions hosts to keep online.", - "placeholder": "", - "multiLine": false, + "label": "Weekends - Peak Start Time", + "visible": true, + "defaultValue": "09:00", + "toolTip": "Input the time when peak hours end for your end users.", "constraints": { "required": true, "validations": [ { - "regex": "^[0-9]{1,4}$", - "message": "The value must be between 1 and 4 digits." + "regex": "^[012][0-9]:[0-5][0-9]$", + "message": "The value must be in a proper time format with a two digit hour and two digit minutes, e.g. 8pm should be input as 20:00." } ] } }, { - "name": "cpuThreshold", + "name": "weekendsOffPeakStartTime", "type": "Microsoft.Common.TextBox", - "label": "Session Threshold Per CPU", - "visible": "[steps('management').scalingTool.deployScalingTool]", - "defaultValue": "1", - "toolTip": "Use this setting to determine the number of sessions per CPU before turning on another host.", - "placeholder": "", - "multiLine": false, + "label": "Weekends - Off Peak Start Time", + "visible": true, + "defaultValue": "17:00", + "toolTip": "Input the time when peak hours begin for your end users during the work week, Monday - Friday.", "constraints": { "required": true, "validations": [ { - "regex": "^[0-9]{0,1}(?:\\.[0-9]{0,2})?$", - "message": "The value must be a number with 1 whole number and, if desired, a single or two digit decimal number." + "regex": "^[012][0-9]:[0-5][0-9]$", + "message": "The value must be in a proper time format with a two digit hour and two digit minutes, e.g. 12am should be input as 00:00." } ] } @@ -1305,10 +1246,10 @@ { "name": "enableApplicationInsights", "type": "Microsoft.Common.CheckBox", - "visible": "[or(steps('management').scalingTool.deployScalingTool, equals(steps('profiles').storage.service, 'AzureFiles Premium'))]", + "visible": "[equals(steps('profiles').storage.service, 'AzureFiles Premium')]", "label": "Enable Application Insights?", "defaultValue": true, - "toolTip": "Deploy the required resources to monitor the Scaling Tool and / or the Auto Increase File Share Solution." + "toolTip": "Deploy the required resources to monitor the Auto Increase File Share Solution." } ] } @@ -1442,7 +1383,7 @@ "name": "functionAppSubnetAddressCidrRange", "label": "Function App Subnet CIDR range", "type": "Microsoft.Common.TextBox", - "visible": "[or(steps('management').scalingTool.deployScalingTool, equals(steps('profiles').storage.service, 'AzureFiles Premium'))]", + "visible": "[equals(steps('profiles').storage.service, 'AzureFiles Premium')]", "defaultValue": "[concat('10.0.1', string(add(41, mul(2, steps('basics').naming.stampIndex))), '.128/26')]", "toolTip": "Specify a CIDR range for the Function App subnet within the AVD spoke virtual network range [24,28] for the Scaling Tool and / or the Auto Increase Premium File Share Qutoa tool.", "constraints": { @@ -1660,7 +1601,7 @@ "activeDirectorySolution": "[if(and(equals(steps('hosts').identity.solution, 'MicrosoftEntraId'), steps('hosts').identity.intune), 'MicrosoftEntraIdIntuneEnrollment', steps('hosts').identity.solution)]", "availability": "[steps('hosts').virtualMachines.availability]", "availabilityZones": "[first(map(first(map(filter(steps('hosts').virtualMachines.resourceSkusApi.value, (item) => contains(item.name, if(and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('controlPlane').hostPool.hostPoolType, 'Pooled')), steps('hosts').virtualMachines.sizeArcGisProMulti, if(and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('controlPlane').hostPool.hostPoolType, 'Personal'), equals(steps('controlPlane').hostPool.workloadType, 6)), steps('hosts').virtualMachines.sizeArcGisProSingleLight, if(and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('controlPlane').hostPool.hostPoolType, 'Personal'), equals(steps('controlPlane').hostPool.workloadType, 4)), steps('hosts').virtualMachines.sizeArcGisProSingleMedium, if(and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('controlPlane').hostPool.hostPoolType, 'Personal'), equals(steps('controlPlane').hostPool.workloadType, 3)), steps('hosts').virtualMachines.sizeArcGisProSingleHeavy, steps('hosts').virtualMachines.sizeGeneric)))))), (item) => item.locationInfo)), (item) => item.zones))]", - "avdObjectId": "[if(empty(steps('management').servicePrincipalsApi), steps('management').startVmOnConnect.objectId, first(map(steps('management').servicePrincipalsApi.value, (item) => item.id)))]", + "avdObjectId": "[first(map(steps('management').servicePrincipalsApi.value, (item) => item.id))]", "azureNetAppFilesSubnetAddressPrefix": "[steps('network').vnet.azureNetAppFilesSubnetAddressCidrRange]", "customRdpProperty": "[steps('controlPlane').hostPool.customRdpProperties]", "deployActivityLogDiagnosticSetting": "[empty(steps('compliance').diagnosticSettingsApi.value)]", @@ -1686,7 +1627,7 @@ "fslogixStorageService": "[if(equals(steps('profiles').profileSolution, 'local'), 'None', steps('profiles').storage.service)]", "functionAppSubnetAddressPrefix": "[steps('network').vnet.functionAppSubnetAddressCidrRange]", "hostPoolPublicNetworkAccess": "[steps('controlPlane').hostPool.publicNetworkAccess]", - "hostPoolType": "[if(equals(steps('controlPlane').hostPool.hostPoolType, 'Pooled'), 'Pooled DepthFirst', 'Personal Automatic')]", + "hostPoolType": "[steps('controlPlane').hostPool.hostPoolType]", "hubAzureFirewallResourceId": "[steps('basics').hub.azureFirewall]", "hubVirtualNetworkResourceId": "[steps('basics').hub.virtualNetwork]", "identifier": "[steps('basics').naming.identifier]", @@ -1703,12 +1644,10 @@ "privateLinkScopeResourceId": "[steps('basics').operations.privateLinkScope]", "profile": "[steps('basics').scenario.profile]", "recoveryServices": "[steps('management').backup.recoveryServices]", - "scalingBeginPeakTime": "[steps('management').scalingTool.beginPeakTime]", - "scalingEndPeakTime": "[steps('management').scalingTool.endPeakTime]", - "scalingLimitSecondsToForceLogOffUser": "[steps('management').scalingTool.forceLogOff]", - "scalingMinimumNumberOfRdsh": "[steps('management').scalingTool.minimumHosts]", - "scalingSessionThresholdPerCPU": "[steps('management').scalingTool.cpuThreshold]", - "scalingTool": "[steps('management').scalingTool.deployScalingTool]", + "scalingWeekdaysOffPeakStartTime": "[steps('management').scalingPlan.weekdaysOffPeakStartTime]", + "scalingWeekdaysPeakStartTime": "[steps('management').scalingPlan.weekdaysPeakStartTime]", + "scalingWeekendsOffPeakStartTime": "[steps('management').scalingPlan.weekendsOffPeakStartTime]", + "scalingWeekendsPeakStartTime": "[steps('management').scalingPlan.weekendsPeakStartTime]", "securityPrincipals": "[steps('controlPlane').assignment.groups]", "sessionHostCount": "[steps('hosts').virtualMachines.count]", "sessionHostIndex": 0, diff --git a/src/bicep/data/resourceAbbreviations.json b/src/bicep/data/resourceAbbreviations.json index d21898d97..13a981832 100644 --- a/src/bicep/data/resourceAbbreviations.json +++ b/src/bicep/data/resourceAbbreviations.json @@ -33,6 +33,7 @@ "remoteApplicationGroups": "vdag", "resourceGroups": "rg", "routeTables": "rt", + "scalingPlans": "vdscaling", "storageAccounts": "st", "subnets": "snet", "userAssignedIdentities": "id", diff --git a/src/bicep/mlz.json b/src/bicep/mlz.json index bfcf44a4e..0dc43b61c 100644 --- a/src/bicep/mlz.json +++ b/src/bicep/mlz.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.31.34.60546", - "templateHash": "9379117934026664616" + "templateHash": "15343776706780459746" } }, "parameters": { @@ -888,7 +888,7 @@ "_generator": { "name": "bicep", "version": "0.31.34.60546", - "templateHash": "16255426588501076488" + "templateHash": "10924801470287352076" } }, "parameters": { @@ -968,7 +968,7 @@ "_generator": { "name": "bicep", "version": "0.31.34.60546", - "templateHash": "8357935203009683307" + "templateHash": "11992572431768878515" } }, "parameters": { @@ -1420,6 +1420,7 @@ "remoteApplicationGroups": "vdag", "resourceGroups": "rg", "routeTables": "rt", + "scalingPlans": "vdscaling", "storageAccounts": "st", "subnets": "snet", "userAssignedIdentities": "id", @@ -1488,6 +1489,8 @@ "recoveryServicesVaultPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, variables('resourceAbbreviations').recoveryServicesVaults)]", "resourceGroup": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').resourceGroups)]", "routeTable": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').routeTables)]", + "scalingPlan": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').scalingPlans)]", + "scalingPlanDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, variables('resourceAbbreviations').scalingPlans)]", "storageAccount": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').storageAccounts), parameters('networkName'), parameters('networkShortName'))]", "storageAccountDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').storageAccounts))]", "storageAccountBlobNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-blob', variables('resourceAbbreviations').storageAccounts))]", diff --git a/src/bicep/modules/naming-convention.bicep b/src/bicep/modules/naming-convention.bicep index 6e310959d..0a2680fea 100644 --- a/src/bicep/modules/naming-convention.bicep +++ b/src/bicep/modules/naming-convention.bicep @@ -105,6 +105,8 @@ var names = { recoveryServicesVaultPrivateEndpoint: replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.privateEndpoints), tokens.service, resourceAbbreviations.recoveryServicesVaults) resourceGroup: replace(namingConvention_Service, tokens.resource, resourceAbbreviations.resourceGroups) routeTable: replace(namingConvention, tokens.resource, resourceAbbreviations.routeTables) + scalingPlan: replace(namingConvention, tokens.resource, resourceAbbreviations.scalingPlans) + scalingPlanDiagnosticSetting: replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.diagnosticSettings), tokens.service, resourceAbbreviations.scalingPlans) storageAccount: replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.storageAccounts), networkName, networkShortName) storageAccountDiagnosticSetting: replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.diagnosticSettings), tokens.service, '${tokens.service}-${resourceAbbreviations.storageAccounts}') storageAccountBlobNetworkInterface: replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.networkInterfaces), tokens.service, '${resourceAbbreviations.storageAccounts}-blob') From b7010237f7dedd3e7b8f44544178e1b1970424b1 Mon Sep 17 00:00:00 2001 From: Brooke Steele Date: Tue, 19 Nov 2024 12:10:11 -0500 Subject: [PATCH 8/9] MLZ VPN Gateway Addon (#1130) * vpn gateway and connection added * updates * moved to solution model * cleaned up with uidefinition * added short readme * readme added * updated readme.md * removed sh * vnet peerings updates added * peering property update ordering complete * various updates * updates * works no firewall rules yet * update * update * update * documentation updates * update * final * GitHub Action: Build Bicep to JSON * removed solution.parameters.json * updated mlz from main * GitHub Action: Build Bicep to JSON * GitHub Action: Build Bicep to JSON --------- Co-authored-by: brsteel@microsoft.com Co-authored-by: github-actions Co-authored-by: Jason Masten --- .../add-ons/virtual-network-gateway/README.md | 199 +++ .../modules/associate-route-table.bicep | 29 + .../modules/firewall-rules.bicep | 70 + .../modules/local-network-gateway.bicep | 17 + .../modules/retrieve-existing.bicep | 58 + .../modules/route-definition.bicep | 18 + .../modules/route-table.bicep | 12 + .../modules/routes.bicep | 30 + .../modules/vnet-peerings.bicep | 35 + .../modules/vpn-connection.bicep | 52 + .../modules/vpn-gateway.bicep | 81 + .../sample.solution.parameters.json | 60 + .../virtual-network-gateway/solution.bicep | 276 ++++ .../virtual-network-gateway/solution.json | 1443 +++++++++++++++++ src/bicep/mlz.json | 232 +-- 15 files changed, 2496 insertions(+), 116 deletions(-) create mode 100644 src/bicep/add-ons/virtual-network-gateway/README.md create mode 100644 src/bicep/add-ons/virtual-network-gateway/modules/associate-route-table.bicep create mode 100644 src/bicep/add-ons/virtual-network-gateway/modules/firewall-rules.bicep create mode 100644 src/bicep/add-ons/virtual-network-gateway/modules/local-network-gateway.bicep create mode 100644 src/bicep/add-ons/virtual-network-gateway/modules/retrieve-existing.bicep create mode 100644 src/bicep/add-ons/virtual-network-gateway/modules/route-definition.bicep create mode 100644 src/bicep/add-ons/virtual-network-gateway/modules/route-table.bicep create mode 100644 src/bicep/add-ons/virtual-network-gateway/modules/routes.bicep create mode 100644 src/bicep/add-ons/virtual-network-gateway/modules/vnet-peerings.bicep create mode 100644 src/bicep/add-ons/virtual-network-gateway/modules/vpn-connection.bicep create mode 100644 src/bicep/add-ons/virtual-network-gateway/modules/vpn-gateway.bicep create mode 100644 src/bicep/add-ons/virtual-network-gateway/sample.solution.parameters.json create mode 100644 src/bicep/add-ons/virtual-network-gateway/solution.bicep create mode 100644 src/bicep/add-ons/virtual-network-gateway/solution.json diff --git a/src/bicep/add-ons/virtual-network-gateway/README.md b/src/bicep/add-ons/virtual-network-gateway/README.md new file mode 100644 index 000000000..bb8105ce6 --- /dev/null +++ b/src/bicep/add-ons/virtual-network-gateway/README.md @@ -0,0 +1,199 @@ +# VPN Gateway MLZ Add-On + +## Introduction + +This document provides details on a Bicep script that deploys a VPN Gateway, Local Network Gateway, VPN connection, and related resources in Azure, integrating into an existing MLZ network deployment. It includes descriptions of all parameters, required parameters, instructions on building and deploying the ARM template, and steps to create a template specification from the Bicep script. + +The deployment is intended to provide an on-prem VPN gateway to connect into the MLZ network through the Hub vNet and route to all spokes. A route table is created for the vpn gateway and attached to the GatewaySubnet in the Hub network, and firewall rules added to allow connectivity. + +This allows for the firewall to serve as a protection between the on-prem internal network and the Azure spoke networks. + +Additionally, it covers the modules used within the script and their roles in the deployment process. + +--- + +## Parameters + +### 1. **vgwName** (string) - Required + +- **Description:** The name of the VPN Gateway. Provided as an input parameter to the solution when deployed. + +### 2. **vgwLocation** (string) - Optional (default: location of the resource group) + +- **Description:** The Azure region for deploying the VPN Gateway. If not provided, it defaults to the location of the resource group. Provided as an input parameter to the solution when deployed. + +### 3. **vgwPublicIpAddressNames** (array) - Required + +- **Description:** The names of the public IP addresses associated with the VPN Gateway. Requires two for redundancy. Provided as an input parameter to the solution when deployed. + +### 4. **vgwSku** (string) - Optional (default: `'VpnGw2'`) + +- **Description:** The SKU (size) of the VPN Gateway. Allowed values: `VpnGw2`, `VpnGw3`, `VpnGw4`, `VpnGw5`. The default can be changed in the "solution.bicep" file. + +### 5. **localNetworkGatewayName** (string) - Required + +- **Description:** The name of the Local Network Gateway. Provided as an input parameter to the solution when deployed. + +### 6. **localGatewayIpAddress** (string) - Required + +- **Description:** The IP address of the Local Network Gateway. This must be a public IP address or a reachable IP from the Azure environment. Provided as an input parameter to the solution when deployed. + +### 7. **allowedAzureAddressPrefixes** (array) - Required + +- **Description:** A list of address prefixes of the peered spoke networks that will be allowed to access the networks through the VPN gateway. This is used in an Azure firewall rule. Provided as an input parameter to the solution when deployed. + +### 8. **localAddressPrefixes** (array) - Required + +- **Description:** A list of address prefixes of the local network routable through the VPN Gateway. This controls what networks can be accessed from Azure through the VPN Gateway. This is also used in an Azure firewall rule. Provided as an input parameter to the solution when deployed. + +### 9. **useSharedKey** (bool) - Required + +- **Description:** Indicates whether to use a shared key or a Key Vault certificate URI for the VPN connection. If false, a URL to a pre-existing keyvault stored certificate must be used instead. Provided as an input parameter to the solution when deployed. + +### 10. **sharedKey** (string) - Required if `useSharedKey = true` + +- **Description:** The shared key for the VPN connection. This parameter is secured. A "true" value uses shared key which is provided in the portal or command prompt at deployment. A "false" value requires that a keyVaultCertificateUri is provided. Remove this from the parameters file before deployment to ensure the deployment will prompt for the value to avoid storing the secret in the file. + +### 11. **keyVaultCertificateUri** (string) - Optional (default: `''`) + +- **Description:** The URI of the Key Vault certificate for the VPN connection. Only used if `useSharedKey = false`. Must be a valid URI starting with `https://` and containing `/secrets/`. Provided as an input parameter to the solution when deployed. + +### 12. **hubVirtualNetworkResourceId** (string) - Required + +- **Description:** The resource ID of the hub virtual network. Can be found on the "Properties" blade on the vNet in the Azure portal. Provided as an input parameter to the solution when deployed. + +### 13. **vnetResourceIdList** (array) - Required + +- **Description:** A list of peered virtual networks that will use the VPN Gateway. The peerings will be updated to allow gateway transit and use. Can be found on the "Properties" blade on the vNet in the Azure portal. Provided as an input parameter to the solution when deployed. + +### 14. **azureFirewallName** (string) - Required + +- **Description:** The name of the Azure firewall in the hub network used to control all traffic through the VPN gateway and all spoke networks. Provided as an input parameter to the solution when deployed. + +### 14. **routeTableName** (string) - Required + +- **Description:** The name of the VPN Gateway route table that is used to control the gateway subnet routing overrides necessary to push all traffic through the Azure firewall. Provided as an input parameter to the solution when deployed. + +--- + +## Modules Used in the Script + +This Bicep script calls several external modules to deploy resources efficiently and modularly. Here's an overview of each module and what it does: + +### 1. **VPN Gateway Module** + +- **File:** `modules/vpn-gateway.bicep` +- **Description:** This module deploys the Virtual Network Gateway (VPN Gateway) in a specified resource group. The VPN Gateway enables secure cross-premises connectivity. +- **Parameters:** + - `vgwName`: The name of the VPN Gateway. Provided as an input parameter to the solution when deployed. + - `vgwLocation`: The location where the VPN Gateway will be deployed. Provided as an input parameter to the solution when deployed. + - `publicIpAddressNames`: The names of the public IP addresses associated with the VPN Gateway. Provided as an input parameter to the solution when deployed. + - `vgwsku`: The SKU of the VPN to be deployed. Provided as an input parameter to the solution when deployed. + - `vnetName`: The name of the hub virtual network to which the VPN Gateway will be connected. Derived from the hub virtual network resource id provided as an input parameter when deployed. + +### 2. **Local Network Gateway Module** + +- **File:** `modules/local-network-gateway.bicep` +- **Description:** This module deploys the Local Network Gateway, which defines the on-premises network's configuration and connectivity to Azure. It includes the on-premises gateway's public IP address and the network address ranges to route through the VPN connection. +- **Parameters:** + - `vgwlocation`: The location of the Local Network Gateway. Provided as an input parameter to the solution when deployed. + - `localNetworkGatewayName`: The name of the Local Network Gateway. Provided as an input parameter to the solution when deployed. + - `gatewayIpAddress`: The public IP address of the Local Network Gateway. Provided as an input parameter to the solution when deployed. + - `addressPrefixes`: The local address prefixes (network ranges) of the on-premises network. Provided as an input parameter to the solution when deployed. + +### 3. **VPN Connection Module** + +The VPN connection module contains these most commonly used IPSEC configuration settings: +`` + saLifeTimeSeconds: 3600 + saDataSizeKilobytes: 102400000 + ipsecEncryption: 'AES256' + ipsecIntegrity: 'SHA256' + ikeEncryption: 'AES256' + ikeIntegrity: 'SHA256' + dhGroup: 'DHGroup2' + pfsGroup: 'PFS2' +`` +Change these in the module file directly to modify connection settings for deployment. + +- **File:** `modules/vpn-connection.bicep` +- **Description:** This module creates the VPN connection between the VPN Gateway in Azure and the Local Network Gateway (on-premises network). It can use either a shared key or a Key Vault certificate for secure authentication. +- **Parameters:** + - `vpnConnectionName`: The name of the VPN connection. Provided as an input parameter to the solution when deployed. + - `vgwlocation`: The location of the VPN Gateway. Provided as an input parameter to the solution when deployed. + - `vpnGatewayName`: The name of the VPN Gateway. Provided as an input parameter to the solution when deployed. + - `vpnGatewayResourceGroupName`: The resource group where the VPN Gateway is deployed. Gateway is placed in the hub virtual network resource group, the name is extracted from the hub virtual network resource group id provided in the parameters when deployed. + - `sharedKey`: The shared key for the VPN connection (if using shared key authentication). Provided as an input parameter to the solution when deployed. Ensure the shared key and value are not provided in the parameters file before deployment to ensure prompting for the value at deployment time. + - `keyVaultCertificateUri`: The URI of the Key Vault certificate (if using certificate-based authentication). Provided as an input parameter to the solution when deployed, if shared key is not used. + - `localNetworkGatewayName`: The name of the Local Network Gateway. Provided as an input parameter to the solution when deployed. + +### 4. **Retrieve Existing Module** + +- **File:** `modules/retrieve-existing.bicep` +- **Description:** This module retrieves the list of virtual network peerings associated with a virtual network. The peerings allow networks to communicate securely with each other within the same Azure region or across regions. This module is also used to retrieve information from other existing resources depending on the parameters used. +- **Parameters:** + - `vnetResourceId`: The resource ID of the virtual network for which peerings are being retrieved. Provided as an input parameter to the solution when deployed. + +### 5. **VNet Peerings Module** + +- **File:** `modules/vnet-peerings.bicep` +- **Description:** After retrieving the peerings for a virtual network, this module updates the peerings to reflect the new VPN Gateway configuration. This allows peered networks to utilize the VPN Gateway for cross-premises connectivity. +- **Parameters:** + - `vnetResourceId`: The resource ID of the virtual network. Provided as an input parameter to the solution when deployed. + - `peeringsList`: The list of virtual network peerings to be updated. Returned values from the retrieve-existing.bicep module. + +### 6. **Route Table Module** + +- **File:** `modules/route-table.bicep` +- **Description:** This module creates the route table for the VPN gateway. +- **Parameters:** + - `routeTableName`: The route table name. Provided as an input parameter to the solution when deployed. + +### 7. **Route Definition** + +- **File:** `modules/route-definition.bicep` +- **Description:** This module builds the route construct to be used when adding the route, as multiple routes need to be added. Virtual appliance is hard coded as the next hop type. +- **Parameters:** + - `firewallIpAddress`: The IP address of the firewall, used as the next hop IP address. Returned value from the retrieve-existing.bicep module. + - `addressPrefixes`: The address prefixes used in the route being built. Provided as an input parameter to the solution when deployed. + +### 8. **Routes Module** + +- **File:** `modules/routes.bicep` +- **Description:** This module creates the routes in a route table. +- **Parameters:** + - `routeTableName`: The route table name. Provided as an input parameter to the solution when deployed. + - `routeName`: The name of the route. Value defaulted in the solution.bicep file, the value is: "route-#" with an increment number depending on the number of routes being added. + - `addressSpace`: The CIDR address prefix being routed. Provided as an input parameter to the solution when deployed. + - `nextHopType`: The type of next hop, defaulted to appliance in the solution.bicep file. + - `nextHopIpAddress`: The IP address of the next hop. In this implementation, the firewall IP address. Provided as an input parameter to the solution when deployed. + +### 8. **Firewall Rules Module** + +- **File:** `modules/firewall-rules.bicep ` +- **Description:** This module creates the firewall rules to allow spoke and vpn address prefixes access to eachother. +- **Parameters:** + - `allowVnetAddressSpaces`: The CIDR address prefixes of peered Azure spoke vnets. Provided as an input parameter to the solution when deployed. + - `onPremAddressSpaces`: The CIDR address prefixes of the onprem networks to be allowed. Provided as an input parameter to the solution when deployed. + - `firewallPolicyId`: The firewall policy attached to the hub firewall. The solution will dynamically retrieve this value. + - `priorityValue`: The priority value for the firewall rule. The solution defines this at a value of 300 in the main bicep named solution.bicep. + + The rule is hardcoded to allow any protocol to any address in the rule. Customize firewall-rules.bicep to change behavior. + +## Removal of VPN Gateway + +1. Delete the VPN Connection in the MLZ Hub resource group. +2. Delete the Local Network Gateway in the MLZ Hub resource group. +3. Delete the VPN Gateway in the MLZ Hub resource group. +4. Navigate to the Hub vNet, and go to Peerings: + a. Open each peering in the list. + b. Uncheck "Allow gateway or route server in '' to forward traffic to ''. + c. Click "save". +5. Navigate to each spoke vNet represented in the peerings list. + a. Open the peering to the Hub network. + b. Uncheck "Enable '' to use '' remote gateway. + c. Click "save". +6. Navigate to each spoke network resource group. + a. Open the Route table in the group. + b. Choose "Routes". + c. Delete the VPN routes in the list. diff --git a/src/bicep/add-ons/virtual-network-gateway/modules/associate-route-table.bicep b/src/bicep/add-ons/virtual-network-gateway/modules/associate-route-table.bicep new file mode 100644 index 000000000..abee9df67 --- /dev/null +++ b/src/bicep/add-ons/virtual-network-gateway/modules/associate-route-table.bicep @@ -0,0 +1,29 @@ + +@description('virtual network resource ID that holds the subnet') +param vnetResourceId string + +@description('route table resource ID to associate with the subnet') +param routeTableResourceId string + +@description('name of the subnet to associate with the route table') +param subnetName string + +@description('address prefix of the gateway subnet') +param subnetAddressPrefix string + +// Reference the existing Virtual Network +resource existingVnet 'Microsoft.Network/virtualNetworks@2023-11-01' existing = { + name: last(split(vnetResourceId, '/')) +} + +// Update the GatewaySubnet to associate the existing Route Table +resource gatewaySubnet 'Microsoft.Network/virtualNetworks/subnets@2023-11-01' = { + parent: existingVnet + name: subnetName + properties: { + addressPrefix: subnetAddressPrefix + routeTable: { + id: resourceId('Microsoft.Network/routeTables', last(split(routeTableResourceId, '/'))) + } + } +} diff --git a/src/bicep/add-ons/virtual-network-gateway/modules/firewall-rules.bicep b/src/bicep/add-ons/virtual-network-gateway/modules/firewall-rules.bicep new file mode 100644 index 000000000..3785a1989 --- /dev/null +++ b/src/bicep/add-ons/virtual-network-gateway/modules/firewall-rules.bicep @@ -0,0 +1,70 @@ +@description('The list of virtual network resource IDs to be used as the source IP groups') +param allowVnetAddressSpaces array + +@description('Address prefixes of the on-premises network') +param onPremAddressSpaces array + +@description('Name of the firewall policy') +param firewallPolicyId string + +@description('The priority value for the rule collection') +@minValue(300) +@maxValue(65000) +param priorityValue int + + +// Define the firewall policy reference +resource firewallPolicy 'Microsoft.Network/firewallPolicies@2023-04-01' existing = { + name: last(split(firewallPolicyId, '/')) +} + +// First rule set: Source = allowedIpGroup, Destination = onPremIpGroup +var outboundRules = { + name: 'AllowAzureToOnPremRule' // Unique rule name using index + ruleType: 'NetworkRule' + sourceAddresses: allowVnetAddressSpaces + destinationAddresses: onPremAddressSpaces + destinationPorts: [ + '*' // Modify this as needed + ] + ipProtocols: [ + 'Any' // Modify this as needed + ] +} + +// Second rule set (reverse): Source = onPremIpGroup, Destination = allowedIpGroup +var inboundRules = { + name: 'AllowOnPremToAzureRule' // Unique rule name using index + ruleType: 'NetworkRule' + sourceAddresses: onPremAddressSpaces + destinationAddresses: allowVnetAddressSpaces + destinationPorts: [ + '*' // Modify this as needed + ] + ipProtocols: [ + 'Any' // Modify this as needed + ] +} + +// Define the rule collection group, referencing existing IP groups for source and destination +resource allowVgwCollection 'Microsoft.Network/firewallPolicies/ruleCollectionGroups@2023-04-01' = { + name: 'VgwNetworkRuleCollectionGroup' + parent: firewallPolicy + properties: { + priority: priorityValue + ruleCollections: [ + { + name: 'AllowVgw' + priority: priorityValue + ruleCollectionType: 'FirewallPolicyFilterRuleCollection' + action: { + type: 'Allow' + } + rules: [ + outboundRules + inboundRules + ] + } + ] + } +} diff --git a/src/bicep/add-ons/virtual-network-gateway/modules/local-network-gateway.bicep b/src/bicep/add-ons/virtual-network-gateway/modules/local-network-gateway.bicep new file mode 100644 index 000000000..37c8d33cb --- /dev/null +++ b/src/bicep/add-ons/virtual-network-gateway/modules/local-network-gateway.bicep @@ -0,0 +1,17 @@ +param vgwlocation string = resourceGroup().location +param localNetworkGatewayName string +param gatewayIpAddress string +param addressPrefixes array + + +// Local Network Gateway configuration +resource localNetworkGateway 'Microsoft.Network/localNetworkGateways@2023-02-01' = { + name: localNetworkGatewayName + location: vgwlocation + properties: { + gatewayIpAddress: gatewayIpAddress + localNetworkAddressSpace: { + addressPrefixes: addressPrefixes + } + } +} diff --git a/src/bicep/add-ons/virtual-network-gateway/modules/retrieve-existing.bicep b/src/bicep/add-ons/virtual-network-gateway/modules/retrieve-existing.bicep new file mode 100644 index 000000000..7dccaff7e --- /dev/null +++ b/src/bicep/add-ons/virtual-network-gateway/modules/retrieve-existing.bicep @@ -0,0 +1,58 @@ +@description('Name of the Azure Firewall (optional)') +param azureFirewallName string = '' + +@description('Name of the subnet (optional)') +param subnetName string = '' + +@description('The resource ID of the existing spoke virtual network (optional)') +param vnetResourceId string = '' + +@description('The name of the route table associated with the hub virtual network (optional)') +param routeTableName string = '' + +resource vnetRouteTable 'Microsoft.Network/routeTables@2020-11-01' existing = if (!empty(routeTableName) && !empty(vnetResourceId)) { + scope: resourceGroup() + name: routeTableName +} + +// Retrieve internal address of the firewall, conditionally +resource azureFirewall 'Microsoft.Network/azureFirewalls@2020-11-01' existing = if (!empty(azureFirewallName) && !empty(vnetResourceId)) { + scope: resourceGroup(split(vnetResourceId, '/')[2], split(vnetResourceId, '/')[4]) + name: azureFirewallName +} + +// Reference the existing Virtual Network using its resource ID, conditionally +resource vnetInfo 'Microsoft.Network/virtualNetworks@2020-11-01' existing = if (!empty(vnetResourceId)) { + scope: resourceGroup() + name: last(split(vnetResourceId, '/')) // Extract the VNet name from the resource ID +} + +// Loop through the subnets to find the specified subnet, conditionally +resource subnet 'Microsoft.Network/virtualNetworks/subnets@2020-11-01' existing = if (!empty(subnetName) && !empty(vnetResourceId)) { + parent: vnetInfo + name: subnetName +} + +// Output the route table ID of the hub virtual network, if the route table name is provided +output routeTableId string = !empty(routeTableName) ? vnetRouteTable.id : 'N/A' + +// Output the internal IP address of the firewall, if firewall parameters are provided +output firewallPrivateIp string = (!empty(azureFirewallName) && !empty(vnetResourceId)) ? azureFirewall.properties.ipConfigurations[0].properties.privateIPAddress : 'N/A' + +// Output the firewall policy id attached to the firewall +output firewallPolicyId string = !empty(azureFirewallName) ? azureFirewall.properties.firewallPolicy.id : 'N/A' + +// Output the address prefix of the GatewaySubnet, if the parameters are provided +output subnetAddressPrefix string = (!empty(subnetName) && !empty(vnetResourceId)) ? subnet.properties.addressPrefix : 'N/A' + +// Output the address space of the VNet, if the VNet resource ID is provided +output vnetAddressSpace array = !empty(vnetResourceId) ? vnetInfo.properties.addressSpace.addressPrefixes : [] + +// Output the list of peerings from the VNet, if the VNet resource ID is provided +output peeringsData object = !empty(vnetResourceId) ? { + vnetResourceId: vnetResourceId + peeringsList: vnetInfo.properties.virtualNetworkPeerings +} : { + vnetResourceId: 'N/A' + peeringsList: [] +} diff --git a/src/bicep/add-ons/virtual-network-gateway/modules/route-definition.bicep b/src/bicep/add-ons/virtual-network-gateway/modules/route-definition.bicep new file mode 100644 index 000000000..a0cdc6e68 --- /dev/null +++ b/src/bicep/add-ons/virtual-network-gateway/modules/route-definition.bicep @@ -0,0 +1,18 @@ +// Assume hubVnetAddressSpace is a parameter or variable from another part of your script +@description('Address space prefixes of the virtual network') +param addressPrefixes array + +@description('Private IP address of the Azure Firewall') +param firewallPrivateIp string + +// Create a variable with the route definitions +output routes array = [ + for i in range(0, length(addressPrefixes)): { + name: 'mlzToOnPrem-${i}' // Ensure unique route names + addressPrefix: addressPrefixes[i] + nextHopType: 'VirtualAppliance' + nextHopIpAddress: firewallPrivateIp + } +] + + diff --git a/src/bicep/add-ons/virtual-network-gateway/modules/route-table.bicep b/src/bicep/add-ons/virtual-network-gateway/modules/route-table.bicep new file mode 100644 index 000000000..38112a970 --- /dev/null +++ b/src/bicep/add-ons/virtual-network-gateway/modules/route-table.bicep @@ -0,0 +1,12 @@ +@description('Name of the route table to create') +param routeTableName string + +resource routeTable 'Microsoft.Network/routeTables@2021-02-01' = { + name: routeTableName + location: resourceGroup().location + properties: { + disableBgpRoutePropagation: true + } +} + +output routeTableId string = routeTable.id diff --git a/src/bicep/add-ons/virtual-network-gateway/modules/routes.bicep b/src/bicep/add-ons/virtual-network-gateway/modules/routes.bicep new file mode 100644 index 000000000..6da4f8f6d --- /dev/null +++ b/src/bicep/add-ons/virtual-network-gateway/modules/routes.bicep @@ -0,0 +1,30 @@ +@description('Name of the route table to create') +param routeTableName string + +@description('Name of the route') +param routeName string + +@description('CIDR prefixes for the route') +param addressSpace array + +@description('The next hop type for the route') +param nextHopType string + +@description('The next hop IP address for the route') +param nextHopIpAddress string + +resource routeTable 'Microsoft.Network/routeTables@2021-02-01' existing = { + name: routeTableName + scope: resourceGroup() +} + +// Loop over the address spaces and create routes +resource routes 'Microsoft.Network/routeTables/routes@2023-04-01' = [for (cidr, i) in addressSpace: { + parent: routeTable + name: '${routeName}-${i}' + properties: { + addressPrefix: cidr + nextHopType: nextHopType + nextHopIpAddress: nextHopIpAddress != '' ? nextHopIpAddress : null + } +}] diff --git a/src/bicep/add-ons/virtual-network-gateway/modules/vnet-peerings.bicep b/src/bicep/add-ons/virtual-network-gateway/modules/vnet-peerings.bicep new file mode 100644 index 000000000..a094975c0 --- /dev/null +++ b/src/bicep/add-ons/virtual-network-gateway/modules/vnet-peerings.bicep @@ -0,0 +1,35 @@ +@description('The list of peerings to update') +param peeringsList array + +@description('The resource ID of the existing virtual network') +param vnetResourceId string + +// Extract the virtual network name from the resource ID +var vnetName = last(split(vnetResourceId, '/')) + +// Generate the list of updated peerings +var updatedPeerings = [for peering in peeringsList: { + name: last(split(peering.id, '/')) // Extract the peering name from the ID + properties: { + allowGatewayTransit: contains(vnetName, '-hub-') ? true : peering.properties.allowGatewayTransit + useRemoteGateways: !contains(vnetName, '-hub-') ? true : peering.properties.useRemoteGateways + // allowGatewayTransit: contains(split(peering.id, '/')[8], '-hub-') ? true : peering.properties.allowGatewayTransit + // useRemoteGateways: !contains(split(peering.id, '/')[8], '-hub-') ? true : peering.properties.useRemoteGateways + allowForwardedTraffic: peering.properties.allowForwardedTraffic == null ? true : peering.properties.allowForwardedTraffic // Preserve existing value or set to true + remoteVirtualNetwork: peering.properties.remoteVirtualNetwork + } +}] + +// Define the parent virtual network resource +resource vnet 'Microsoft.Network/virtualNetworks@2022-07-01' existing = { + name: vnetName +} + +// Create or update the peerings within the virtual network context +resource peeringUpdates 'Microsoft.Network/virtualNetworks/virtualNetworkPeerings@2021-02-01' = [for (peering, i) in updatedPeerings: { + parent: vnet + name: peering.name + properties: peering.properties +}] + + diff --git a/src/bicep/add-ons/virtual-network-gateway/modules/vpn-connection.bicep b/src/bicep/add-ons/virtual-network-gateway/modules/vpn-connection.bicep new file mode 100644 index 000000000..64179e6bc --- /dev/null +++ b/src/bicep/add-ons/virtual-network-gateway/modules/vpn-connection.bicep @@ -0,0 +1,52 @@ +param vpnConnectionName string +param vgwlocation string +param vpnGatewayName string +param vpnGatewayResourceGroupName string +param sharedKey string +param keyVaultCertificateUri string +param localNetworkGatewayName string + +// Determine if either sharedKey or keyVaultCertificateUri is provided +var useSharedKey = !empty(sharedKey) +var useKeyVaultCertificate = !empty(keyVaultCertificateUri) + +// Conditional validation through variables +var errorMsg = (useSharedKey && useKeyVaultCertificate) ? 'Cannot provide both sharedKey and keyVaultCertificateUri' : '' +var connectionSharedKey = useSharedKey ? sharedKey : null +var connectionIpsecPolicies = useKeyVaultCertificate ? [ + { + saLifeTimeSeconds: 3600 + saDataSizeKilobytes: 102400000 + ipsecEncryption: 'AES256' + ipsecIntegrity: 'SHA256' + ikeEncryption: 'AES256' + ikeIntegrity: 'SHA256' + dhGroup: 'DHGroup2' + pfsGroup: 'PFS2' + } +] : null + +// Deploy the VPN connection only if the conditions are met +resource vpnConnection 'Microsoft.Network/connections@2023-02-01' = if (empty(errorMsg)) { + name: vpnConnectionName + location: vgwlocation + properties: { + virtualNetworkGateway1: { + id: resourceId(vpnGatewayResourceGroupName, 'Microsoft.Network/virtualNetworkGateways', vpnGatewayName) + } + localNetworkGateway2: { + id: resourceId(vpnGatewayResourceGroupName, 'Microsoft.Network/localNetworkGateways', localNetworkGatewayName) + } + connectionType: 'IPsec' + routingWeight: 10 + + sharedKey: connectionSharedKey + + // Use ipsecPolicies if Key Vault certificate URI is provided + ipsecPolicies: connectionIpsecPolicies + + // Additional properties as required + enableBgp: false + usePolicyBasedTrafficSelectors: false + } +} diff --git a/src/bicep/add-ons/virtual-network-gateway/modules/vpn-gateway.bicep b/src/bicep/add-ons/virtual-network-gateway/modules/vpn-gateway.bicep new file mode 100644 index 000000000..661f9ea3e --- /dev/null +++ b/src/bicep/add-ons/virtual-network-gateway/modules/vpn-gateway.bicep @@ -0,0 +1,81 @@ +param vgwname string +param vgwlocation string = resourceGroup().location +param publicIpAddressNames array +param vgwsku string +param vnetName string + +// Existing Virtual Network and Subnet +resource vnet 'Microsoft.Network/virtualNetworks@2023-02-01' existing = { + name: vnetName +} + +// Reference the existing subnet within the specified Virtual Network +resource gatewaySubnet 'Microsoft.Network/virtualNetworks/subnets@2023-02-01' existing = { + parent: vnet + name: 'GatewaySubnet' +} + +var gatewaySubnetId = gatewaySubnet.id + +// Public IP Addresses +resource publicIpAddresses 'Microsoft.Network/publicIPAddresses@2023-02-01' = [for (pipname, index) in publicIpAddressNames: { + name: pipname + location: vgwlocation + sku: { + name: 'Standard' + } + properties: { + publicIPAllocationMethod: 'Static' + } +}] + +var firstPublicIpAddressId = publicIpAddresses[0].id +var secondPublicIpAddressId = publicIpAddresses[1].id + +// VPN Gateway +resource vpnGateway 'Microsoft.Network/virtualNetworkGateways@2023-02-01' = { + name: vgwname + location: vgwlocation + properties: { + gatewayType: 'Vpn' + ipConfigurations: [ + { + name: 'first' + properties: { + privateIPAllocationMethod: 'Dynamic' + subnet: { + id: gatewaySubnetId + } + publicIPAddress: { + id: firstPublicIpAddressId + } + } + } + { + name: 'second' + properties: { + privateIPAllocationMethod: 'Dynamic' + publicIPAddress: { + id: secondPublicIpAddressId + } + subnet: { + id: gatewaySubnetId + } + } + } + ] + activeActive: true + vpnType: 'RouteBased' + vpnGatewayGeneration: 'Generation2' + enableBgp: false + enablePrivateIpAddress: false + sku: { + name: vgwsku + tier: vgwsku + } + } +} + + + + diff --git a/src/bicep/add-ons/virtual-network-gateway/sample.solution.parameters.json b/src/bicep/add-ons/virtual-network-gateway/sample.solution.parameters.json new file mode 100644 index 000000000..8364d4a01 --- /dev/null +++ b/src/bicep/add-ons/virtual-network-gateway/sample.solution.parameters.json @@ -0,0 +1,60 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#", + "contentVersion": "1.0.0.0", + "parameters": { + "vgwName": { + "value": "" + }, + "vgwLocation": { + "value": "usgovvirginia" + }, + "vgwPublicIpAddressNames": { + "value": [ + "", + "" + }, + "localGatewayIpAddress": { + "value": "" + }, + "allowedAzureAddressPrefixes": { + "value": [ + "10.0.130.0/24", + "10.0.131.0/24", + "10.0.132.0/24", + "10.0.128.0/23" + ] + }, + "localAddressPrefixes": { + "value": [ + "192.168.0.0/24", + "192.168.1.0/24" + ] + }, + "useSharedKey": { + "value": true + }, + "hubVirtualNetworkResourceId": { + "value": "/subscriptions//resourceGroups//providers/Microsoft.Network/virtualNetworks/" + }, + "vnetResourceIdList": { + "value": [ + "/subscriptions//resourceGroups//providers/Microsoft.Network/virtualNetworks//resourceGroups//providers/Microsoft.Network/virtualNetworks//resourceGroups//providers/Microsoft.Network/virtualNetworks/" + }, + "vgwRouteTableName": { + "value": "" + }, + "hubVnetRouteTableName": { + "value": "" + } + } +} \ No newline at end of file diff --git a/src/bicep/add-ons/virtual-network-gateway/solution.bicep b/src/bicep/add-ons/virtual-network-gateway/solution.bicep new file mode 100644 index 000000000..f356e0441 --- /dev/null +++ b/src/bicep/add-ons/virtual-network-gateway/solution.bicep @@ -0,0 +1,276 @@ +targetScope = 'subscription' + +@description('The name of the VPN Gateway.') +param vgwName string + +@description('The Azure region location of the VPN Gateway.') +param vgwLocation string + +@description('The names of the public IP addresses to use for the VPN Gateway.') +param vgwPublicIpAddressNames array + +@description('The SKU of the VPN Gateway.') +@allowed(['VpnGw2', 'VpnGw3', 'VpnGw4', 'VpnGw5']) +param vgwSku string + +@description('Local Network Gateway Name') +param localNetworkGatewayName string + +@description('IP Address of the Local Network Gateway, must be a public IP address or be able to be connected to from MLZ network') +param localGatewayIpAddress string + +@description('Azure address prefixes allowed to communicate to VPN Gateway to on-premises network') +param allowedAzureAddressPrefixes array + +@description('Address prefixes of the Local Network which will be routable through the VPN Gateway') +param localAddressPrefixes array + +@description('Choose whether to use a shared key or Key Vault certificate URI for the VPN connection.') +param useSharedKey bool + +@description('The shared key to use for the VPN connection. If using the shared key, this must be provided.') +@secure() +param sharedKey string + +@description('The URI of the Key Vault certificate to use for the VPN connection. If using a Key Vault certificate, this must be a valid URI.') +param keyVaultCertificateUri string = '' + +@description('A suffix to use for naming deployments uniquely.') +param deploymentNameSuffix string = utcNow() + +@description('The resource ID of the hub virtual network.') +param hubVirtualNetworkResourceId string +// Extracting the resource group name and virtual network name from the hub virtual network resource ID +var hubResourceGroupName = split(hubVirtualNetworkResourceId, '/')[4] +var hubVnetName = split(hubVirtualNetworkResourceId, '/')[8] + +@description('List of peered networks that should use the VPN Gateway once configured.') +param vnetResourceIdList array + +@description('The name of the Azure Firewall to retrieve the internal IP address from.') +param azureFirewallResourceId string +var azureFirewallName = split(azureFirewallResourceId, '/')[8] + +@description('The name of the vgw route table to create') +param vgwRouteTableName string + +@description('The name of the gateway subnet') +param gatewaySubnetName string = 'GatewaySubnet' + +@description('The name of the hub virtual network route table') +param hubVnetRouteTableResourceId string +var hubVnetRouteTableName = split(hubVnetRouteTableResourceId, '/')[8] + +// Conditional parameter assignment for VPN connection module +var vpnSharedKey = useSharedKey ? sharedKey : '' +var vpnKeyVaultUri = !useSharedKey ? keyVaultCertificateUri : '' + +// Parameter validation +var isValidUri = contains(keyVaultCertificateUri, 'https://') && contains(keyVaultCertificateUri, '/secrets/') + +// Conditional validation to ensure either sharedKey or keyVaultCertificateUri is used correctly +resource validateKeyOrUri 'Microsoft.Resources/deployments@2021-04-01' = if (!useSharedKey && !isValidUri) { + name: 'InvalidKeyVaultCertificateUri-${deploymentNameSuffix}' + properties: { + mode: 'Incremental' + parameters: { + message: { + value: 'Invalid Key Vault Certificate URI. It must start with "https://" and contain "/secrets/".' + } + } + templateLink: { + uri: 'https://validatemessage.com' // Placeholder for validation message, replace if needed + } + } +} + +// calling Virtual Network Gateway Module +module vpnGatewayModule 'modules/vpn-gateway.bicep' = { + name: 'vpnGateway-${deploymentNameSuffix}' + scope: resourceGroup(hubResourceGroupName) + params: { + vgwname: vgwName + vgwlocation: vgwLocation + publicIpAddressNames: vgwPublicIpAddressNames + vgwsku: vgwSku + vnetName: hubVnetName + } +} + +// calling Local Network Gateway Module +module localNetworkGatewayModule 'modules/local-network-gateway.bicep' = { + name: 'localNetworkGateway-${deploymentNameSuffix}' + scope: resourceGroup(hubResourceGroupName) + params: { + vgwlocation: vgwLocation + localNetworkGatewayName: localNetworkGatewayName + gatewayIpAddress: localGatewayIpAddress + addressPrefixes: localAddressPrefixes + } +} + +// calling VPN Connection Module +module vpnConnectionModule 'modules/vpn-connection.bicep' = { + name: 'vpnConnection-${deploymentNameSuffix}' + scope: resourceGroup(hubResourceGroupName) + params: { + vpnConnectionName: '${vgwName}-to-${localNetworkGatewayName}' + vgwlocation: vgwLocation + vpnGatewayName: vgwName + vpnGatewayResourceGroupName: hubResourceGroupName + sharedKey: vpnSharedKey + keyVaultCertificateUri: vpnKeyVaultUri + localNetworkGatewayName: localNetworkGatewayName + } + dependsOn: [ + vpnGatewayModule + localNetworkGatewayModule + validateKeyOrUri + ] +} + +// Loop through the vnetResourceIdList and to retrieve the peerings for each VNet +module retrieveVnetInfo 'modules/retrieve-existing.bicep' = [for (vnetId, i) in vnetResourceIdList: { + name: 'retrieveVnetPeerings-${deploymentNameSuffix}-${i}' + scope: resourceGroup(split(vnetId, '/')[2], split(vnetId, '/')[4]) + params: { + vnetResourceId: vnetId + } + dependsOn: [ + vpnConnectionModule + ] +}] + +// Get the hub virtual network peerings +module retrieveHubVnetInfo 'modules/retrieve-existing.bicep' = { + name: 'retrieveHubVnetPeerings-${deploymentNameSuffix}' + scope: resourceGroup(split(hubVirtualNetworkResourceId, '/')[2], split(hubVirtualNetworkResourceId, '/')[4]) + params: { + vnetResourceId: hubVirtualNetworkResourceId + } + dependsOn: [ + vpnConnectionModule + ] +} + +// retrieve the route table information for the hub vnet including the firewall private IP and gateway subnet address space info to be used for the new vgw route table and routes +module retrieveRouteTableInfo 'modules/retrieve-existing.bicep' = { + name: 'retrieveRouteTableInfo-${deploymentNameSuffix}' + scope: resourceGroup(split(hubVirtualNetworkResourceId, '/')[2], split(hubVirtualNetworkResourceId, '/')[4]) + params: { + vnetResourceId: hubVirtualNetworkResourceId + azureFirewallName: azureFirewallName + subnetName: gatewaySubnetName + } + dependsOn: [ + updatePeerings + ] +} + +// Call update the Hub peerings first to enable spokes to use the VPN Gateway, if not done first, spokes will fail their update +module updateHubPeerings 'modules/vnet-peerings.bicep' = { + name: 'updateHubPeerings-${deploymentNameSuffix}' + scope: resourceGroup(split(hubVirtualNetworkResourceId, '/')[2], split(hubVirtualNetworkResourceId, '/')[4]) + params: { + vnetResourceId: retrieveHubVnetInfo.outputs.peeringsData.vnetResourceId + peeringsList: retrieveHubVnetInfo.outputs.peeringsData.peeringsList + } + dependsOn: [ + retrieveHubVnetInfo + retrieveVnetInfo + ] +} + + +// Update the peerings for each spoke VNet to use the VPN Gateway +module updatePeerings 'modules/vnet-peerings.bicep' = [for (vnetId, i) in vnetResourceIdList: { + name: 'updatePeerings-${deploymentNameSuffix}-${i}' + scope: resourceGroup(split(vnetId, '/')[2], split(vnetId, '/')[4]) + params: { + vnetResourceId: retrieveVnetInfo[i].outputs.peeringsData.vnetResourceId + peeringsList: retrieveVnetInfo[i].outputs.peeringsData.peeringsList + } + dependsOn: [ + retrieveVnetInfo + updateHubPeerings + ] +}] + +// Create the route table for the VPN Gateway subnet, will route spoke vnets to through the firewall, overriding default behavior +module createRouteTable 'modules/route-table.bicep' = { + name: 'createVgwRouteTable-${deploymentNameSuffix}' + scope: resourceGroup(split(hubVirtualNetworkResourceId, '/')[2], split(hubVirtualNetworkResourceId, '/')[4]) + params: { + routeTableName: vgwRouteTableName + } + dependsOn: [ + retrieveVnetInfo + retrieveRouteTableInfo + updateHubPeerings + updatePeerings + ] +} + +// Create the routes to the firewall for the spoke vnets, if vnet is not provided in the "allowedAzureAddressPrefixes" then the spoke will not be able to use the VPN Gateway +module createRoutes 'modules/routes.bicep' = [for (vnetResourceId, i) in vnetResourceIdList: { + name: 'createRoute-${i}-${deploymentNameSuffix}' + scope: resourceGroup(split(hubVirtualNetworkResourceId, '/')[2], split(hubVirtualNetworkResourceId, '/')[4]) + params: { + routeTableName: vgwRouteTableName + addressSpace: retrieveVnetInfo[i].outputs.vnetAddressSpace + routeName: 'route-${i}' + nextHopType: 'VirtualAppliance' + nextHopIpAddress: retrieveRouteTableInfo.outputs.firewallPrivateIp + } + dependsOn: [ + createRouteTable + ] +}] + +// Create the routes to the firewall for the hub vnet as and override to the onprem networks +module createHubRoutesToOnPrem 'modules/routes.bicep' = { + name: 'createOverrideRoutes-${deploymentNameSuffix}' + scope: resourceGroup(split(hubVirtualNetworkResourceId, '/')[2], split(hubVirtualNetworkResourceId, '/')[4]) + params: { + routeTableName: hubVnetRouteTableName + addressSpace: localAddressPrefixes + routeName: 'route-onprem-override' + nextHopType: 'VirtualAppliance' + nextHopIpAddress: retrieveRouteTableInfo.outputs.firewallPrivateIp + } + dependsOn: [ + createRouteTable + ] +} + + +// Associate the vgw route table with the gateway subnet so the gateway routes traffic destined for spokes through the firewall +module associateRouteTable 'modules/associate-route-table.bicep' = { + name: 'associateRouteTable-${deploymentNameSuffix}' + scope: resourceGroup(split(hubVirtualNetworkResourceId, '/')[2], split(hubVirtualNetworkResourceId, '/')[4]) + params: { + vnetResourceId: hubVirtualNetworkResourceId + routeTableResourceId: createRouteTable.outputs.routeTableId + subnetName: gatewaySubnetName + subnetAddressPrefix: retrieveRouteTableInfo.outputs.subnetAddressPrefix + } + dependsOn: [ + createRouteTable + ] +} + + +// Create the firewall rules +module firewallRules 'modules/firewall-rules.bicep' = { + name: 'firewallRules-${deploymentNameSuffix}' + scope: resourceGroup(split(hubVirtualNetworkResourceId, '/')[2], split(hubVirtualNetworkResourceId, '/')[4]) + params: { + allowVnetAddressSpaces: allowedAzureAddressPrefixes + onPremAddressSpaces: localAddressPrefixes + firewallPolicyId: retrieveRouteTableInfo.outputs.firewallPolicyId + priorityValue: 300 + } + dependsOn: [ + associateRouteTable + ] +} diff --git a/src/bicep/add-ons/virtual-network-gateway/solution.json b/src/bicep/add-ons/virtual-network-gateway/solution.json new file mode 100644 index 000000000..92531d82f --- /dev/null +++ b/src/bicep/add-ons/virtual-network-gateway/solution.json @@ -0,0 +1,1443 @@ +{ + "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.34.60546", + "templateHash": "7891133147346503241" + } + }, + "parameters": { + "vgwName": { + "type": "string", + "metadata": { + "description": "The name of the VPN Gateway." + } + }, + "vgwLocation": { + "type": "string", + "metadata": { + "description": "The Azure region location of the VPN Gateway." + } + }, + "vgwPublicIpAddressNames": { + "type": "array", + "metadata": { + "description": "The names of the public IP addresses to use for the VPN Gateway." + } + }, + "vgwSku": { + "type": "string", + "allowedValues": [ + "VpnGw2", + "VpnGw3", + "VpnGw4", + "VpnGw5" + ], + "metadata": { + "description": "The SKU of the VPN Gateway." + } + }, + "localNetworkGatewayName": { + "type": "string", + "metadata": { + "description": "Local Network Gateway Name" + } + }, + "localGatewayIpAddress": { + "type": "string", + "metadata": { + "description": "IP Address of the Local Network Gateway, must be a public IP address or be able to be connected to from MLZ network" + } + }, + "allowedAzureAddressPrefixes": { + "type": "array", + "metadata": { + "description": "Azure address prefixes allowed to communicate to VPN Gateway to on-premises network" + } + }, + "localAddressPrefixes": { + "type": "array", + "metadata": { + "description": "Address prefixes of the Local Network which will be routable through the VPN Gateway" + } + }, + "useSharedKey": { + "type": "bool", + "metadata": { + "description": "Choose whether to use a shared key or Key Vault certificate URI for the VPN connection." + } + }, + "sharedKey": { + "type": "securestring", + "metadata": { + "description": "The shared key to use for the VPN connection. If using the shared key, this must be provided." + } + }, + "keyVaultCertificateUri": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The URI of the Key Vault certificate to use for the VPN connection. If using a Key Vault certificate, this must be a valid URI." + } + }, + "deploymentNameSuffix": { + "type": "string", + "defaultValue": "[utcNow()]", + "metadata": { + "description": "A suffix to use for naming deployments uniquely." + } + }, + "hubVirtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the hub virtual network." + } + }, + "vnetResourceIdList": { + "type": "array", + "metadata": { + "description": "List of peered networks that should use the VPN Gateway once configured." + } + }, + "azureFirewallResourceId": { + "type": "string", + "metadata": { + "description": "The name of the Azure Firewall to retrieve the internal IP address from." + } + }, + "vgwRouteTableName": { + "type": "string", + "metadata": { + "description": "The name of the vgw route table to create" + } + }, + "gatewaySubnetName": { + "type": "string", + "defaultValue": "GatewaySubnet", + "metadata": { + "description": "The name of the gateway subnet" + } + }, + "hubVnetRouteTableResourceId": { + "type": "string", + "metadata": { + "description": "The name of the hub virtual network route table" + } + } + }, + "variables": { + "hubResourceGroupName": "[split(parameters('hubVirtualNetworkResourceId'), '/')[4]]", + "hubVnetName": "[split(parameters('hubVirtualNetworkResourceId'), '/')[8]]", + "azureFirewallName": "[split(parameters('azureFirewallResourceId'), '/')[8]]", + "hubVnetRouteTableName": "[split(parameters('hubVnetRouteTableResourceId'), '/')[8]]", + "vpnSharedKey": "[if(parameters('useSharedKey'), parameters('sharedKey'), '')]", + "vpnKeyVaultUri": "[if(not(parameters('useSharedKey')), parameters('keyVaultCertificateUri'), '')]", + "isValidUri": "[and(contains(parameters('keyVaultCertificateUri'), 'https://'), contains(parameters('keyVaultCertificateUri'), '/secrets/'))]" + }, + "resources": [ + { + "condition": "[and(not(parameters('useSharedKey')), not(variables('isValidUri')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2021-04-01", + "name": "[format('InvalidKeyVaultCertificateUri-{0}', parameters('deploymentNameSuffix'))]", + "properties": { + "mode": "Incremental", + "parameters": { + "message": { + "value": "Invalid Key Vault Certificate URI. It must start with \"https://\" and contain \"/secrets/\"." + } + }, + "templateLink": { + "uri": "https://validatemessage.com" + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('vpnGateway-{0}', parameters('deploymentNameSuffix'))]", + "resourceGroup": "[variables('hubResourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "vgwname": { + "value": "[parameters('vgwName')]" + }, + "vgwlocation": { + "value": "[parameters('vgwLocation')]" + }, + "publicIpAddressNames": { + "value": "[parameters('vgwPublicIpAddressNames')]" + }, + "vgwsku": { + "value": "[parameters('vgwSku')]" + }, + "vnetName": { + "value": "[variables('hubVnetName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.34.60546", + "templateHash": "3589752526565849732" + } + }, + "parameters": { + "vgwname": { + "type": "string" + }, + "vgwlocation": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "publicIpAddressNames": { + "type": "array" + }, + "vgwsku": { + "type": "string" + }, + "vnetName": { + "type": "string" + } + }, + "variables": { + "gatewaySubnetId": "[resourceId('Microsoft.Network/virtualNetworks/subnets', parameters('vnetName'), 'GatewaySubnet')]", + "firstPublicIpAddressId": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIpAddressNames')[0])]", + "secondPublicIpAddressId": "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIpAddressNames')[1])]" + }, + "resources": [ + { + "copy": { + "name": "publicIpAddresses", + "count": "[length(parameters('publicIpAddressNames'))]" + }, + "type": "Microsoft.Network/publicIPAddresses", + "apiVersion": "2023-02-01", + "name": "[parameters('publicIpAddressNames')[copyIndex()]]", + "location": "[parameters('vgwlocation')]", + "sku": { + "name": "Standard" + }, + "properties": { + "publicIPAllocationMethod": "Static" + } + }, + { + "type": "Microsoft.Network/virtualNetworkGateways", + "apiVersion": "2023-02-01", + "name": "[parameters('vgwname')]", + "location": "[parameters('vgwlocation')]", + "properties": { + "gatewayType": "Vpn", + "ipConfigurations": [ + { + "name": "first", + "properties": { + "privateIPAllocationMethod": "Dynamic", + "subnet": { + "id": "[variables('gatewaySubnetId')]" + }, + "publicIPAddress": { + "id": "[variables('firstPublicIpAddressId')]" + } + } + }, + { + "name": "second", + "properties": { + "privateIPAllocationMethod": "Dynamic", + "publicIPAddress": { + "id": "[variables('secondPublicIpAddressId')]" + }, + "subnet": { + "id": "[variables('gatewaySubnetId')]" + } + } + } + ], + "activeActive": true, + "vpnType": "RouteBased", + "vpnGatewayGeneration": "Generation2", + "enableBgp": false, + "enablePrivateIpAddress": false, + "sku": { + "name": "[parameters('vgwsku')]", + "tier": "[parameters('vgwsku')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIpAddressNames')[1])]", + "[resourceId('Microsoft.Network/publicIPAddresses', parameters('publicIpAddressNames')[0])]" + ] + } + ] + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('localNetworkGateway-{0}', parameters('deploymentNameSuffix'))]", + "resourceGroup": "[variables('hubResourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "vgwlocation": { + "value": "[parameters('vgwLocation')]" + }, + "localNetworkGatewayName": { + "value": "[parameters('localNetworkGatewayName')]" + }, + "gatewayIpAddress": { + "value": "[parameters('localGatewayIpAddress')]" + }, + "addressPrefixes": { + "value": "[parameters('localAddressPrefixes')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.34.60546", + "templateHash": "17093542793626818819" + } + }, + "parameters": { + "vgwlocation": { + "type": "string", + "defaultValue": "[resourceGroup().location]" + }, + "localNetworkGatewayName": { + "type": "string" + }, + "gatewayIpAddress": { + "type": "string" + }, + "addressPrefixes": { + "type": "array" + } + }, + "resources": [ + { + "type": "Microsoft.Network/localNetworkGateways", + "apiVersion": "2023-02-01", + "name": "[parameters('localNetworkGatewayName')]", + "location": "[parameters('vgwlocation')]", + "properties": { + "gatewayIpAddress": "[parameters('gatewayIpAddress')]", + "localNetworkAddressSpace": { + "addressPrefixes": "[parameters('addressPrefixes')]" + } + } + } + ] + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('vpnConnection-{0}', parameters('deploymentNameSuffix'))]", + "resourceGroup": "[variables('hubResourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "vpnConnectionName": { + "value": "[format('{0}-to-{1}', parameters('vgwName'), parameters('localNetworkGatewayName'))]" + }, + "vgwlocation": { + "value": "[parameters('vgwLocation')]" + }, + "vpnGatewayName": { + "value": "[parameters('vgwName')]" + }, + "vpnGatewayResourceGroupName": { + "value": "[variables('hubResourceGroupName')]" + }, + "sharedKey": { + "value": "[variables('vpnSharedKey')]" + }, + "keyVaultCertificateUri": { + "value": "[variables('vpnKeyVaultUri')]" + }, + "localNetworkGatewayName": { + "value": "[parameters('localNetworkGatewayName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.34.60546", + "templateHash": "17808641870371981197" + } + }, + "parameters": { + "vpnConnectionName": { + "type": "string" + }, + "vgwlocation": { + "type": "string" + }, + "vpnGatewayName": { + "type": "string" + }, + "vpnGatewayResourceGroupName": { + "type": "string" + }, + "sharedKey": { + "type": "string" + }, + "keyVaultCertificateUri": { + "type": "string" + }, + "localNetworkGatewayName": { + "type": "string" + } + }, + "variables": { + "useSharedKey": "[not(empty(parameters('sharedKey')))]", + "useKeyVaultCertificate": "[not(empty(parameters('keyVaultCertificateUri')))]", + "errorMsg": "[if(and(variables('useSharedKey'), variables('useKeyVaultCertificate')), 'Cannot provide both sharedKey and keyVaultCertificateUri', '')]", + "connectionSharedKey": "[if(variables('useSharedKey'), parameters('sharedKey'), null())]", + "connectionIpsecPolicies": "[if(variables('useKeyVaultCertificate'), createArray(createObject('saLifeTimeSeconds', 3600, 'saDataSizeKilobytes', 102400000, 'ipsecEncryption', 'AES256', 'ipsecIntegrity', 'SHA256', 'ikeEncryption', 'AES256', 'ikeIntegrity', 'SHA256', 'dhGroup', 'DHGroup2', 'pfsGroup', 'PFS2')), null())]" + }, + "resources": [ + { + "condition": "[empty(variables('errorMsg'))]", + "type": "Microsoft.Network/connections", + "apiVersion": "2023-02-01", + "name": "[parameters('vpnConnectionName')]", + "location": "[parameters('vgwlocation')]", + "properties": { + "virtualNetworkGateway1": { + "id": "[resourceId(parameters('vpnGatewayResourceGroupName'), 'Microsoft.Network/virtualNetworkGateways', parameters('vpnGatewayName'))]" + }, + "localNetworkGateway2": { + "id": "[resourceId(parameters('vpnGatewayResourceGroupName'), 'Microsoft.Network/localNetworkGateways', parameters('localNetworkGatewayName'))]" + }, + "connectionType": "IPsec", + "routingWeight": 10, + "sharedKey": "[variables('connectionSharedKey')]", + "ipsecPolicies": "[variables('connectionIpsecPolicies')]", + "enableBgp": false, + "usePolicyBasedTrafficSelectors": false + } + } + ] + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('hubResourceGroupName')), 'Microsoft.Resources/deployments', format('localNetworkGateway-{0}', parameters('deploymentNameSuffix')))]", + "[subscriptionResourceId('Microsoft.Resources/deployments', format('InvalidKeyVaultCertificateUri-{0}', parameters('deploymentNameSuffix')))]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('hubResourceGroupName')), 'Microsoft.Resources/deployments', format('vpnGateway-{0}', parameters('deploymentNameSuffix')))]" + ] + }, + { + "copy": { + "name": "retrieveVnetInfo", + "count": "[length(parameters('vnetResourceIdList'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('retrieveVnetPeerings-{0}-{1}', parameters('deploymentNameSuffix'), copyIndex())]", + "subscriptionId": "[split(parameters('vnetResourceIdList')[copyIndex()], '/')[2]]", + "resourceGroup": "[split(parameters('vnetResourceIdList')[copyIndex()], '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "vnetResourceId": { + "value": "[parameters('vnetResourceIdList')[copyIndex()]]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.34.60546", + "templateHash": "8229271046057649788" + } + }, + "parameters": { + "azureFirewallName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Name of the Azure Firewall (optional)" + } + }, + "subnetName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Name of the subnet (optional)" + } + }, + "vnetResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The resource ID of the existing spoke virtual network (optional)" + } + }, + "routeTableName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the route table associated with the hub virtual network (optional)" + } + } + }, + "resources": [], + "outputs": { + "routeTableId": { + "type": "string", + "value": "[if(not(empty(parameters('routeTableName'))), resourceId('Microsoft.Network/routeTables', parameters('routeTableName')), 'N/A')]" + }, + "firewallPrivateIp": { + "type": "string", + "value": "[if(and(not(empty(parameters('azureFirewallName'))), not(empty(parameters('vnetResourceId')))), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('vnetResourceId'), '/')[2], split(parameters('vnetResourceId'), '/')[4]), 'Microsoft.Network/azureFirewalls', parameters('azureFirewallName')), '2020-11-01').ipConfigurations[0].properties.privateIPAddress, 'N/A')]" + }, + "firewallPolicyId": { + "type": "string", + "value": "[if(not(empty(parameters('azureFirewallName'))), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('vnetResourceId'), '/')[2], split(parameters('vnetResourceId'), '/')[4]), 'Microsoft.Network/azureFirewalls', parameters('azureFirewallName')), '2020-11-01').firewallPolicy.id, 'N/A')]" + }, + "subnetAddressPrefix": { + "type": "string", + "value": "[if(and(not(empty(parameters('subnetName'))), not(empty(parameters('vnetResourceId')))), reference(resourceId('Microsoft.Network/virtualNetworks/subnets', last(split(parameters('vnetResourceId'), '/')), parameters('subnetName')), '2020-11-01').addressPrefix, 'N/A')]" + }, + "vnetAddressSpace": { + "type": "array", + "value": "[if(not(empty(parameters('vnetResourceId'))), reference(resourceId('Microsoft.Network/virtualNetworks', last(split(parameters('vnetResourceId'), '/'))), '2020-11-01').addressSpace.addressPrefixes, createArray())]" + }, + "peeringsData": { + "type": "object", + "value": "[if(not(empty(parameters('vnetResourceId'))), createObject('vnetResourceId', parameters('vnetResourceId'), 'peeringsList', reference(resourceId('Microsoft.Network/virtualNetworks', last(split(parameters('vnetResourceId'), '/'))), '2020-11-01').virtualNetworkPeerings), createObject('vnetResourceId', 'N/A', 'peeringsList', createArray()))]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('hubResourceGroupName')), 'Microsoft.Resources/deployments', format('vpnConnection-{0}', parameters('deploymentNameSuffix')))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('retrieveHubVnetPeerings-{0}', parameters('deploymentNameSuffix'))]", + "subscriptionId": "[split(parameters('hubVirtualNetworkResourceId'), '/')[2]]", + "resourceGroup": "[split(parameters('hubVirtualNetworkResourceId'), '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "vnetResourceId": { + "value": "[parameters('hubVirtualNetworkResourceId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.34.60546", + "templateHash": "8229271046057649788" + } + }, + "parameters": { + "azureFirewallName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Name of the Azure Firewall (optional)" + } + }, + "subnetName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Name of the subnet (optional)" + } + }, + "vnetResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The resource ID of the existing spoke virtual network (optional)" + } + }, + "routeTableName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the route table associated with the hub virtual network (optional)" + } + } + }, + "resources": [], + "outputs": { + "routeTableId": { + "type": "string", + "value": "[if(not(empty(parameters('routeTableName'))), resourceId('Microsoft.Network/routeTables', parameters('routeTableName')), 'N/A')]" + }, + "firewallPrivateIp": { + "type": "string", + "value": "[if(and(not(empty(parameters('azureFirewallName'))), not(empty(parameters('vnetResourceId')))), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('vnetResourceId'), '/')[2], split(parameters('vnetResourceId'), '/')[4]), 'Microsoft.Network/azureFirewalls', parameters('azureFirewallName')), '2020-11-01').ipConfigurations[0].properties.privateIPAddress, 'N/A')]" + }, + "firewallPolicyId": { + "type": "string", + "value": "[if(not(empty(parameters('azureFirewallName'))), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('vnetResourceId'), '/')[2], split(parameters('vnetResourceId'), '/')[4]), 'Microsoft.Network/azureFirewalls', parameters('azureFirewallName')), '2020-11-01').firewallPolicy.id, 'N/A')]" + }, + "subnetAddressPrefix": { + "type": "string", + "value": "[if(and(not(empty(parameters('subnetName'))), not(empty(parameters('vnetResourceId')))), reference(resourceId('Microsoft.Network/virtualNetworks/subnets', last(split(parameters('vnetResourceId'), '/')), parameters('subnetName')), '2020-11-01').addressPrefix, 'N/A')]" + }, + "vnetAddressSpace": { + "type": "array", + "value": "[if(not(empty(parameters('vnetResourceId'))), reference(resourceId('Microsoft.Network/virtualNetworks', last(split(parameters('vnetResourceId'), '/'))), '2020-11-01').addressSpace.addressPrefixes, createArray())]" + }, + "peeringsData": { + "type": "object", + "value": "[if(not(empty(parameters('vnetResourceId'))), createObject('vnetResourceId', parameters('vnetResourceId'), 'peeringsList', reference(resourceId('Microsoft.Network/virtualNetworks', last(split(parameters('vnetResourceId'), '/'))), '2020-11-01').virtualNetworkPeerings), createObject('vnetResourceId', 'N/A', 'peeringsList', createArray()))]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, variables('hubResourceGroupName')), 'Microsoft.Resources/deployments', format('vpnConnection-{0}', parameters('deploymentNameSuffix')))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('retrieveRouteTableInfo-{0}', parameters('deploymentNameSuffix'))]", + "subscriptionId": "[split(parameters('hubVirtualNetworkResourceId'), '/')[2]]", + "resourceGroup": "[split(parameters('hubVirtualNetworkResourceId'), '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "vnetResourceId": { + "value": "[parameters('hubVirtualNetworkResourceId')]" + }, + "azureFirewallName": { + "value": "[variables('azureFirewallName')]" + }, + "subnetName": { + "value": "[parameters('gatewaySubnetName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.34.60546", + "templateHash": "8229271046057649788" + } + }, + "parameters": { + "azureFirewallName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Name of the Azure Firewall (optional)" + } + }, + "subnetName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "Name of the subnet (optional)" + } + }, + "vnetResourceId": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The resource ID of the existing spoke virtual network (optional)" + } + }, + "routeTableName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The name of the route table associated with the hub virtual network (optional)" + } + } + }, + "resources": [], + "outputs": { + "routeTableId": { + "type": "string", + "value": "[if(not(empty(parameters('routeTableName'))), resourceId('Microsoft.Network/routeTables', parameters('routeTableName')), 'N/A')]" + }, + "firewallPrivateIp": { + "type": "string", + "value": "[if(and(not(empty(parameters('azureFirewallName'))), not(empty(parameters('vnetResourceId')))), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('vnetResourceId'), '/')[2], split(parameters('vnetResourceId'), '/')[4]), 'Microsoft.Network/azureFirewalls', parameters('azureFirewallName')), '2020-11-01').ipConfigurations[0].properties.privateIPAddress, 'N/A')]" + }, + "firewallPolicyId": { + "type": "string", + "value": "[if(not(empty(parameters('azureFirewallName'))), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('vnetResourceId'), '/')[2], split(parameters('vnetResourceId'), '/')[4]), 'Microsoft.Network/azureFirewalls', parameters('azureFirewallName')), '2020-11-01').firewallPolicy.id, 'N/A')]" + }, + "subnetAddressPrefix": { + "type": "string", + "value": "[if(and(not(empty(parameters('subnetName'))), not(empty(parameters('vnetResourceId')))), reference(resourceId('Microsoft.Network/virtualNetworks/subnets', last(split(parameters('vnetResourceId'), '/')), parameters('subnetName')), '2020-11-01').addressPrefix, 'N/A')]" + }, + "vnetAddressSpace": { + "type": "array", + "value": "[if(not(empty(parameters('vnetResourceId'))), reference(resourceId('Microsoft.Network/virtualNetworks', last(split(parameters('vnetResourceId'), '/'))), '2020-11-01').addressSpace.addressPrefixes, createArray())]" + }, + "peeringsData": { + "type": "object", + "value": "[if(not(empty(parameters('vnetResourceId'))), createObject('vnetResourceId', parameters('vnetResourceId'), 'peeringsList', reference(resourceId('Microsoft.Network/virtualNetworks', last(split(parameters('vnetResourceId'), '/'))), '2020-11-01').virtualNetworkPeerings), createObject('vnetResourceId', 'N/A', 'peeringsList', createArray()))]" + } + } + } + }, + "dependsOn": [ + "updatePeerings" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('updateHubPeerings-{0}', parameters('deploymentNameSuffix'))]", + "subscriptionId": "[split(parameters('hubVirtualNetworkResourceId'), '/')[2]]", + "resourceGroup": "[split(parameters('hubVirtualNetworkResourceId'), '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "vnetResourceId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Resources/deployments', format('retrieveHubVnetPeerings-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.peeringsData.value.vnetResourceId]" + }, + "peeringsList": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Resources/deployments', format('retrieveHubVnetPeerings-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.peeringsData.value.peeringsList]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.34.60546", + "templateHash": "10372783504892384694" + } + }, + "parameters": { + "peeringsList": { + "type": "array", + "metadata": { + "description": "The list of peerings to update" + } + }, + "vnetResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the existing virtual network" + } + } + }, + "variables": { + "copy": [ + { + "name": "updatedPeerings", + "count": "[length(parameters('peeringsList'))]", + "input": { + "name": "[last(split(parameters('peeringsList')[copyIndex('updatedPeerings')].id, '/'))]", + "properties": { + "allowGatewayTransit": "[if(contains(variables('vnetName'), '-hub-'), true(), parameters('peeringsList')[copyIndex('updatedPeerings')].properties.allowGatewayTransit)]", + "useRemoteGateways": "[if(not(contains(variables('vnetName'), '-hub-')), true(), parameters('peeringsList')[copyIndex('updatedPeerings')].properties.useRemoteGateways)]", + "allowForwardedTraffic": "[if(equals(parameters('peeringsList')[copyIndex('updatedPeerings')].properties.allowForwardedTraffic, null()), true(), parameters('peeringsList')[copyIndex('updatedPeerings')].properties.allowForwardedTraffic)]", + "remoteVirtualNetwork": "[parameters('peeringsList')[copyIndex('updatedPeerings')].properties.remoteVirtualNetwork]" + } + } + } + ], + "vnetName": "[last(split(parameters('vnetResourceId'), '/'))]" + }, + "resources": [ + { + "copy": { + "name": "peeringUpdates", + "count": "[length(variables('updatedPeerings'))]" + }, + "type": "Microsoft.Network/virtualNetworks/virtualNetworkPeerings", + "apiVersion": "2021-02-01", + "name": "[format('{0}/{1}', variables('vnetName'), variables('updatedPeerings')[copyIndex()].name)]", + "properties": "[variables('updatedPeerings')[copyIndex()].properties]" + } + ] + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Resources/deployments', format('retrieveHubVnetPeerings-{0}', parameters('deploymentNameSuffix')))]", + "retrieveVnetInfo" + ] + }, + { + "copy": { + "name": "updatePeerings", + "count": "[length(parameters('vnetResourceIdList'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('updatePeerings-{0}-{1}', parameters('deploymentNameSuffix'), copyIndex())]", + "subscriptionId": "[split(parameters('vnetResourceIdList')[copyIndex()], '/')[2]]", + "resourceGroup": "[split(parameters('vnetResourceIdList')[copyIndex()], '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "vnetResourceId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('vnetResourceIdList')[copyIndex()], '/')[2], split(parameters('vnetResourceIdList')[copyIndex()], '/')[4]), 'Microsoft.Resources/deployments', format('retrieveVnetPeerings-{0}-{1}', parameters('deploymentNameSuffix'), copyIndex())), '2022-09-01').outputs.peeringsData.value.vnetResourceId]" + }, + "peeringsList": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('vnetResourceIdList')[copyIndex()], '/')[2], split(parameters('vnetResourceIdList')[copyIndex()], '/')[4]), 'Microsoft.Resources/deployments', format('retrieveVnetPeerings-{0}-{1}', parameters('deploymentNameSuffix'), copyIndex())), '2022-09-01').outputs.peeringsData.value.peeringsList]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.34.60546", + "templateHash": "10372783504892384694" + } + }, + "parameters": { + "peeringsList": { + "type": "array", + "metadata": { + "description": "The list of peerings to update" + } + }, + "vnetResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the existing virtual network" + } + } + }, + "variables": { + "copy": [ + { + "name": "updatedPeerings", + "count": "[length(parameters('peeringsList'))]", + "input": { + "name": "[last(split(parameters('peeringsList')[copyIndex('updatedPeerings')].id, '/'))]", + "properties": { + "allowGatewayTransit": "[if(contains(variables('vnetName'), '-hub-'), true(), parameters('peeringsList')[copyIndex('updatedPeerings')].properties.allowGatewayTransit)]", + "useRemoteGateways": "[if(not(contains(variables('vnetName'), '-hub-')), true(), parameters('peeringsList')[copyIndex('updatedPeerings')].properties.useRemoteGateways)]", + "allowForwardedTraffic": "[if(equals(parameters('peeringsList')[copyIndex('updatedPeerings')].properties.allowForwardedTraffic, null()), true(), parameters('peeringsList')[copyIndex('updatedPeerings')].properties.allowForwardedTraffic)]", + "remoteVirtualNetwork": "[parameters('peeringsList')[copyIndex('updatedPeerings')].properties.remoteVirtualNetwork]" + } + } + } + ], + "vnetName": "[last(split(parameters('vnetResourceId'), '/'))]" + }, + "resources": [ + { + "copy": { + "name": "peeringUpdates", + "count": "[length(variables('updatedPeerings'))]" + }, + "type": "Microsoft.Network/virtualNetworks/virtualNetworkPeerings", + "apiVersion": "2021-02-01", + "name": "[format('{0}/{1}', variables('vnetName'), variables('updatedPeerings')[copyIndex()].name)]", + "properties": "[variables('updatedPeerings')[copyIndex()].properties]" + } + ] + } + }, + "dependsOn": [ + "retrieveVnetInfo", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Resources/deployments', format('updateHubPeerings-{0}', parameters('deploymentNameSuffix')))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('createVgwRouteTable-{0}', parameters('deploymentNameSuffix'))]", + "subscriptionId": "[split(parameters('hubVirtualNetworkResourceId'), '/')[2]]", + "resourceGroup": "[split(parameters('hubVirtualNetworkResourceId'), '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "routeTableName": { + "value": "[parameters('vgwRouteTableName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.34.60546", + "templateHash": "13855829966458352" + } + }, + "parameters": { + "routeTableName": { + "type": "string", + "metadata": { + "description": "Name of the route table to create" + } + } + }, + "resources": [ + { + "type": "Microsoft.Network/routeTables", + "apiVersion": "2021-02-01", + "name": "[parameters('routeTableName')]", + "location": "[resourceGroup().location]", + "properties": { + "disableBgpRoutePropagation": true + } + } + ], + "outputs": { + "routeTableId": { + "type": "string", + "value": "[resourceId('Microsoft.Network/routeTables', parameters('routeTableName'))]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Resources/deployments', format('retrieveRouteTableInfo-{0}', parameters('deploymentNameSuffix')))]", + "retrieveVnetInfo", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Resources/deployments', format('updateHubPeerings-{0}', parameters('deploymentNameSuffix')))]", + "updatePeerings" + ] + }, + { + "copy": { + "name": "createRoutes", + "count": "[length(parameters('vnetResourceIdList'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('createRoute-{0}-{1}', copyIndex(), parameters('deploymentNameSuffix'))]", + "subscriptionId": "[split(parameters('hubVirtualNetworkResourceId'), '/')[2]]", + "resourceGroup": "[split(parameters('hubVirtualNetworkResourceId'), '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "routeTableName": { + "value": "[parameters('vgwRouteTableName')]" + }, + "addressSpace": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('vnetResourceIdList')[copyIndex()], '/')[2], split(parameters('vnetResourceIdList')[copyIndex()], '/')[4]), 'Microsoft.Resources/deployments', format('retrieveVnetPeerings-{0}-{1}', parameters('deploymentNameSuffix'), copyIndex())), '2022-09-01').outputs.vnetAddressSpace.value]" + }, + "routeName": { + "value": "[format('route-{0}', copyIndex())]" + }, + "nextHopType": { + "value": "VirtualAppliance" + }, + "nextHopIpAddress": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Resources/deployments', format('retrieveRouteTableInfo-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.firewallPrivateIp.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.34.60546", + "templateHash": "280047033181845191" + } + }, + "parameters": { + "routeTableName": { + "type": "string", + "metadata": { + "description": "Name of the route table to create" + } + }, + "routeName": { + "type": "string", + "metadata": { + "description": "Name of the route" + } + }, + "addressSpace": { + "type": "array", + "metadata": { + "description": "CIDR prefixes for the route" + } + }, + "nextHopType": { + "type": "string", + "metadata": { + "description": "The next hop type for the route" + } + }, + "nextHopIpAddress": { + "type": "string", + "metadata": { + "description": "The next hop IP address for the route" + } + } + }, + "resources": [ + { + "copy": { + "name": "routes", + "count": "[length(parameters('addressSpace'))]" + }, + "type": "Microsoft.Network/routeTables/routes", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('routeTableName'), format('{0}-{1}', parameters('routeName'), copyIndex()))]", + "properties": { + "addressPrefix": "[parameters('addressSpace')[copyIndex()]]", + "nextHopType": "[parameters('nextHopType')]", + "nextHopIpAddress": "[if(not(equals(parameters('nextHopIpAddress'), '')), parameters('nextHopIpAddress'), null())]" + } + } + ] + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Resources/deployments', format('createVgwRouteTable-{0}', parameters('deploymentNameSuffix')))]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Resources/deployments', format('retrieveRouteTableInfo-{0}', parameters('deploymentNameSuffix')))]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('vnetResourceIdList')[copyIndex()], '/')[2], split(parameters('vnetResourceIdList')[copyIndex()], '/')[4]), 'Microsoft.Resources/deployments', format('retrieveVnetPeerings-{0}-{1}', parameters('deploymentNameSuffix'), copyIndex()))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('createOverrideRoutes-{0}', parameters('deploymentNameSuffix'))]", + "subscriptionId": "[split(parameters('hubVirtualNetworkResourceId'), '/')[2]]", + "resourceGroup": "[split(parameters('hubVirtualNetworkResourceId'), '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "routeTableName": { + "value": "[variables('hubVnetRouteTableName')]" + }, + "addressSpace": { + "value": "[parameters('localAddressPrefixes')]" + }, + "routeName": { + "value": "route-onprem-override" + }, + "nextHopType": { + "value": "VirtualAppliance" + }, + "nextHopIpAddress": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Resources/deployments', format('retrieveRouteTableInfo-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.firewallPrivateIp.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.34.60546", + "templateHash": "280047033181845191" + } + }, + "parameters": { + "routeTableName": { + "type": "string", + "metadata": { + "description": "Name of the route table to create" + } + }, + "routeName": { + "type": "string", + "metadata": { + "description": "Name of the route" + } + }, + "addressSpace": { + "type": "array", + "metadata": { + "description": "CIDR prefixes for the route" + } + }, + "nextHopType": { + "type": "string", + "metadata": { + "description": "The next hop type for the route" + } + }, + "nextHopIpAddress": { + "type": "string", + "metadata": { + "description": "The next hop IP address for the route" + } + } + }, + "resources": [ + { + "copy": { + "name": "routes", + "count": "[length(parameters('addressSpace'))]" + }, + "type": "Microsoft.Network/routeTables/routes", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('routeTableName'), format('{0}-{1}', parameters('routeName'), copyIndex()))]", + "properties": { + "addressPrefix": "[parameters('addressSpace')[copyIndex()]]", + "nextHopType": "[parameters('nextHopType')]", + "nextHopIpAddress": "[if(not(equals(parameters('nextHopIpAddress'), '')), parameters('nextHopIpAddress'), null())]" + } + } + ] + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Resources/deployments', format('createVgwRouteTable-{0}', parameters('deploymentNameSuffix')))]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Resources/deployments', format('retrieveRouteTableInfo-{0}', parameters('deploymentNameSuffix')))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('associateRouteTable-{0}', parameters('deploymentNameSuffix'))]", + "subscriptionId": "[split(parameters('hubVirtualNetworkResourceId'), '/')[2]]", + "resourceGroup": "[split(parameters('hubVirtualNetworkResourceId'), '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "vnetResourceId": { + "value": "[parameters('hubVirtualNetworkResourceId')]" + }, + "routeTableResourceId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Resources/deployments', format('createVgwRouteTable-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.routeTableId.value]" + }, + "subnetName": { + "value": "[parameters('gatewaySubnetName')]" + }, + "subnetAddressPrefix": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Resources/deployments', format('retrieveRouteTableInfo-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.subnetAddressPrefix.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.34.60546", + "templateHash": "17439321293785150273" + } + }, + "parameters": { + "vnetResourceId": { + "type": "string", + "metadata": { + "description": "virtual network resource ID that holds the subnet" + } + }, + "routeTableResourceId": { + "type": "string", + "metadata": { + "description": "route table resource ID to associate with the subnet" + } + }, + "subnetName": { + "type": "string", + "metadata": { + "description": "name of the subnet to associate with the route table" + } + }, + "subnetAddressPrefix": { + "type": "string", + "metadata": { + "description": "address prefix of the gateway subnet" + } + } + }, + "resources": [ + { + "type": "Microsoft.Network/virtualNetworks/subnets", + "apiVersion": "2023-11-01", + "name": "[format('{0}/{1}', last(split(parameters('vnetResourceId'), '/')), parameters('subnetName'))]", + "properties": { + "addressPrefix": "[parameters('subnetAddressPrefix')]", + "routeTable": { + "id": "[resourceId('Microsoft.Network/routeTables', last(split(parameters('routeTableResourceId'), '/')))]" + } + } + } + ] + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Resources/deployments', format('createVgwRouteTable-{0}', parameters('deploymentNameSuffix')))]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Resources/deployments', format('retrieveRouteTableInfo-{0}', parameters('deploymentNameSuffix')))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('createHubRoute-{0}', parameters('deploymentNameSuffix'))]", + "subscriptionId": "[split(parameters('hubVirtualNetworkResourceId'), '/')[2]]", + "resourceGroup": "[split(parameters('hubVirtualNetworkResourceId'), '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "routeTableName": { + "value": "[variables('hubVnetRouteTableName')]" + }, + "addressSpace": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Resources/deployments', format('retrieveRouteTableInfo-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.vnetAddressSpace.value]" + }, + "routeName": { + "value": "route-hubvnet" + }, + "nextHopType": { + "value": "VirtualAppliance" + }, + "nextHopIpAddress": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Resources/deployments', format('retrieveRouteTableInfo-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.firewallPrivateIp.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.34.60546", + "templateHash": "280047033181845191" + } + }, + "parameters": { + "routeTableName": { + "type": "string", + "metadata": { + "description": "Name of the route table to create" + } + }, + "routeName": { + "type": "string", + "metadata": { + "description": "Name of the route" + } + }, + "addressSpace": { + "type": "array", + "metadata": { + "description": "CIDR prefixes for the route" + } + }, + "nextHopType": { + "type": "string", + "metadata": { + "description": "The next hop type for the route" + } + }, + "nextHopIpAddress": { + "type": "string", + "metadata": { + "description": "The next hop IP address for the route" + } + } + }, + "resources": [ + { + "copy": { + "name": "routes", + "count": "[length(parameters('addressSpace'))]" + }, + "type": "Microsoft.Network/routeTables/routes", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', parameters('routeTableName'), format('{0}-{1}', parameters('routeName'), copyIndex()))]", + "properties": { + "addressPrefix": "[parameters('addressSpace')[copyIndex()]]", + "nextHopType": "[parameters('nextHopType')]", + "nextHopIpAddress": "[if(not(equals(parameters('nextHopIpAddress'), '')), parameters('nextHopIpAddress'), null())]" + } + } + ] + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Resources/deployments', format('createVgwRouteTable-{0}', parameters('deploymentNameSuffix')))]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Resources/deployments', format('retrieveRouteTableInfo-{0}', parameters('deploymentNameSuffix')))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('firewallRules-{0}', parameters('deploymentNameSuffix'))]", + "subscriptionId": "[split(parameters('hubVirtualNetworkResourceId'), '/')[2]]", + "resourceGroup": "[split(parameters('hubVirtualNetworkResourceId'), '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "allowVnetAddressSpaces": { + "value": "[parameters('allowedAzureAddressPrefixes')]" + }, + "onPremAddressSpaces": { + "value": "[parameters('localAddressPrefixes')]" + }, + "firewallPolicyId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Resources/deployments', format('retrieveRouteTableInfo-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.firewallPolicyId.value]" + }, + "priorityValue": { + "value": 300 + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.34.60546", + "templateHash": "17390602903085657580" + } + }, + "parameters": { + "allowVnetAddressSpaces": { + "type": "array", + "metadata": { + "description": "The list of virtual network resource IDs to be used as the source IP groups" + } + }, + "onPremAddressSpaces": { + "type": "array", + "metadata": { + "description": "Address prefixes of the on-premises network" + } + }, + "firewallPolicyId": { + "type": "string", + "metadata": { + "description": "Name of the firewall policy" + } + }, + "priorityValue": { + "type": "int", + "minValue": 300, + "maxValue": 65000, + "metadata": { + "description": "The priority value for the rule collection" + } + } + }, + "variables": { + "outboundRules": { + "name": "AllowAzureToOnPremRule", + "ruleType": "NetworkRule", + "sourceAddresses": "[parameters('allowVnetAddressSpaces')]", + "destinationAddresses": "[parameters('onPremAddressSpaces')]", + "destinationPorts": [ + "*" + ], + "ipProtocols": [ + "Any" + ] + }, + "inboundRules": { + "name": "AllowOnPremToAzureRule", + "ruleType": "NetworkRule", + "sourceAddresses": "[parameters('onPremAddressSpaces')]", + "destinationAddresses": "[parameters('allowVnetAddressSpaces')]", + "destinationPorts": [ + "*" + ], + "ipProtocols": [ + "Any" + ] + } + }, + "resources": [ + { + "type": "Microsoft.Network/firewallPolicies/ruleCollectionGroups", + "apiVersion": "2023-04-01", + "name": "[format('{0}/{1}', last(split(parameters('firewallPolicyId'), '/')), 'VgwNetworkRuleCollectionGroup')]", + "properties": { + "priority": "[parameters('priorityValue')]", + "ruleCollections": [ + { + "name": "AllowVgw", + "priority": "[parameters('priorityValue')]", + "ruleCollectionType": "FirewallPolicyFilterRuleCollection", + "action": { + "type": "Allow" + }, + "rules": [ + "[variables('outboundRules')]", + "[variables('inboundRules')]" + ] + } + ] + } + } + ] + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Resources/deployments', format('associateRouteTable-{0}', parameters('deploymentNameSuffix')))]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Resources/deployments', format('retrieveRouteTableInfo-{0}', parameters('deploymentNameSuffix')))]" + ] + } + ] +} \ No newline at end of file diff --git a/src/bicep/mlz.json b/src/bicep/mlz.json index 0dc43b61c..105425e42 100644 --- a/src/bicep/mlz.json +++ b/src/bicep/mlz.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "15343776706780459746" + "version": "0.31.92.45157", + "templateHash": "7670256562437793548" } }, "parameters": { @@ -887,8 +887,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "10924801470287352076" + "version": "0.31.92.45157", + "templateHash": "4114664614478275863" } }, "parameters": { @@ -967,8 +967,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "11992572431768878515" + "version": "0.31.92.45157", + "templateHash": "6411408920489493501" } }, "parameters": { @@ -1561,8 +1561,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "2502930168536032010" + "version": "0.31.92.45157", + "templateHash": "17619606927129129347" } }, "parameters": { @@ -1706,8 +1706,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "12782100486021815060" + "version": "0.31.92.45157", + "templateHash": "5311912111839888762" } }, "parameters": { @@ -1766,8 +1766,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "17445726037807437290" + "version": "0.31.92.45157", + "templateHash": "9371138343009436350" } }, "parameters": { @@ -1907,8 +1907,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "11630019985682391796" + "version": "0.31.92.45157", + "templateHash": "6531207997218247037" } }, "parameters": { @@ -2084,8 +2084,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "12984958392421507149" + "version": "0.31.92.45157", + "templateHash": "17606032368841347839" } }, "parameters": { @@ -2352,8 +2352,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "2196017082128829477" + "version": "0.31.92.45157", + "templateHash": "3681943409502537301" } }, "parameters": { @@ -2431,8 +2431,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "2196017082128829477" + "version": "0.31.92.45157", + "templateHash": "3681943409502537301" } }, "parameters": { @@ -2512,8 +2512,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "8417416771003518918" + "version": "0.31.92.45157", + "templateHash": "18425321023142226965" } }, "parameters": { @@ -2613,8 +2613,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "4071285799602638747" + "version": "0.31.92.45157", + "templateHash": "15002544166394392504" } }, "parameters": { @@ -2682,8 +2682,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "3465065949976146403" + "version": "0.31.92.45157", + "templateHash": "16817025486402215719" } }, "parameters": { @@ -2796,8 +2796,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "1730170030487163899" + "version": "0.31.92.45157", + "templateHash": "782319697243698272" } }, "parameters": { @@ -2886,8 +2886,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "1730170030487163899" + "version": "0.31.92.45157", + "templateHash": "782319697243698272" } }, "parameters": { @@ -3000,8 +3000,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "5808375390237466838" + "version": "0.31.92.45157", + "templateHash": "4750034338253477937" } }, "parameters": { @@ -3401,8 +3401,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "11567108053836979012" + "version": "0.31.92.45157", + "templateHash": "11818136489056939588" } }, "parameters": { @@ -3519,8 +3519,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "2196017082128829477" + "version": "0.31.92.45157", + "templateHash": "3681943409502537301" } }, "parameters": { @@ -3602,8 +3602,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "8417416771003518918" + "version": "0.31.92.45157", + "templateHash": "18425321023142226965" } }, "parameters": { @@ -3705,8 +3705,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "4071285799602638747" + "version": "0.31.92.45157", + "templateHash": "15002544166394392504" } }, "parameters": { @@ -3782,8 +3782,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "3465065949976146403" + "version": "0.31.92.45157", + "templateHash": "16817025486402215719" } }, "parameters": { @@ -3930,8 +3930,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "6489616383757058493" + "version": "0.31.92.45157", + "templateHash": "11553909803736438916" } }, "parameters": { @@ -3983,8 +3983,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "5031620623183573702" + "version": "0.31.92.45157", + "templateHash": "5574526676512163672" } }, "parameters": { @@ -4062,8 +4062,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "15799890372401066181" + "version": "0.31.92.45157", + "templateHash": "10267893616110384815" } }, "parameters": { @@ -4115,8 +4115,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "5031620623183573702" + "version": "0.31.92.45157", + "templateHash": "5574526676512163672" } }, "parameters": { @@ -4192,8 +4192,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "16841222955467860758" + "version": "0.31.92.45157", + "templateHash": "17171440191267536743" } }, "parameters": { @@ -4269,8 +4269,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "12405780209119797551" + "version": "0.31.92.45157", + "templateHash": "16077950968688123011" } }, "parameters": { @@ -4435,8 +4435,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "6386888682917235118" + "version": "0.31.92.45157", + "templateHash": "12787329163785242553" } }, "parameters": { @@ -4524,8 +4524,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "419730844167715947" + "version": "0.31.92.45157", + "templateHash": "11761568940379970751" } }, "parameters": { @@ -4782,8 +4782,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "7946295394028911325" + "version": "0.31.92.45157", + "templateHash": "4207798980384159491" } }, "parameters": { @@ -4862,8 +4862,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "967013811257719495" + "version": "0.31.92.45157", + "templateHash": "1966035938992047983" } }, "parameters": { @@ -4957,8 +4957,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "9115514582672423063" + "version": "0.31.92.45157", + "templateHash": "7930493629995578222" } }, "parameters": { @@ -5102,8 +5102,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "366255573709691198" + "version": "0.31.92.45157", + "templateHash": "17900321188332105834" } }, "parameters": { @@ -5188,8 +5188,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "11337913680813675353" + "version": "0.31.92.45157", + "templateHash": "17298378299072098272" } }, "parameters": { @@ -5356,8 +5356,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "6648745810362038848" + "version": "0.31.92.45157", + "templateHash": "12087616562036012055" } }, "parameters": { @@ -5484,8 +5484,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "18318483669505051648" + "version": "0.31.92.45157", + "templateHash": "2081045465267717136" } }, "parameters": { @@ -5712,8 +5712,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "12373038860746323248" + "version": "0.31.92.45157", + "templateHash": "15042212833055960396" } }, "parameters": { @@ -5889,8 +5889,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "10895428597231985214" + "version": "0.31.92.45157", + "templateHash": "2760766953842709390" } }, "parameters": { @@ -6049,8 +6049,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "13001914044795164689" + "version": "0.31.92.45157", + "templateHash": "10850741356290813493" } }, "parameters": { @@ -6371,8 +6371,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "8788916623054227962" + "version": "0.31.92.45157", + "templateHash": "2785700700806650182" } }, "parameters": { @@ -6538,8 +6538,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "16352081091904770478" + "version": "0.31.92.45157", + "templateHash": "10838080267928735788" } }, "parameters": { @@ -6846,8 +6846,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "8788916623054227962" + "version": "0.31.92.45157", + "templateHash": "2785700700806650182" } }, "parameters": { @@ -6991,8 +6991,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "15180554823445581333" + "version": "0.31.92.45157", + "templateHash": "15042903713059976655" } }, "parameters": { @@ -7108,8 +7108,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "10245709494871874056" + "version": "0.31.92.45157", + "templateHash": "1625826941635729014" } }, "parameters": { @@ -7392,8 +7392,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "11013785870428129557" + "version": "0.31.92.45157", + "templateHash": "11341470403202647858" } }, "parameters": { @@ -7478,8 +7478,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "5964906331561259426" + "version": "0.31.92.45157", + "templateHash": "4687229436121899773" } }, "parameters": { @@ -7565,8 +7565,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "3200577753830159376" + "version": "0.31.92.45157", + "templateHash": "6315472047633861096" } }, "parameters": { @@ -7652,8 +7652,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "5533529544253509904" + "version": "0.31.92.45157", + "templateHash": "2073766618455932098" } }, "parameters": { @@ -7734,8 +7734,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "169088610601729285" + "version": "0.31.92.45157", + "templateHash": "9546260853018527046" } }, "parameters": { @@ -7816,8 +7816,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "5104130163491279218" + "version": "0.31.92.45157", + "templateHash": "16372121177996394493" } }, "parameters": { @@ -7894,8 +7894,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "8958046244399156747" + "version": "0.31.92.45157", + "templateHash": "3821176451773778831" } }, "parameters": { @@ -7975,8 +7975,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "13721817451936402949" + "version": "0.31.92.45157", + "templateHash": "1721966359516622278" } }, "parameters": { @@ -8047,8 +8047,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "16505356842938617427" + "version": "0.31.92.45157", + "templateHash": "8510408576984746573" } }, "parameters": { @@ -8133,8 +8133,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "7145459344230861166" + "version": "0.31.92.45157", + "templateHash": "3495057416767671634" } }, "parameters": { @@ -8190,8 +8190,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "4206571908443996627" + "version": "0.31.92.45157", + "templateHash": "14528983897386416653" } }, "parameters": { @@ -8366,8 +8366,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "967013811257719495" + "version": "0.31.92.45157", + "templateHash": "1966035938992047983" } }, "parameters": { @@ -8464,8 +8464,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "4104174025531546645" + "version": "0.31.92.45157", + "templateHash": "17309660590425732791" } }, "parameters": { @@ -8528,8 +8528,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "2575529544484247921" + "version": "0.31.92.45157", + "templateHash": "17047820191891552534" } }, "parameters": { From 57cce4d4c45d09385862013596c16cfcbdc4483a Mon Sep 17 00:00:00 2001 From: Jason Masten Date: Wed, 27 Nov 2024 16:14:37 -0500 Subject: [PATCH 9/9] AVD Add-On: Added marketplace terms & other fixes (#1134) * Fixed API versions for MAG * Added condition for role assignment * Fixed API version for MAG * Fixed API version for MAG * Compiled bicep changes * Moved role definitions, Fixed tags & workspace names * Fixed tags * Fixed workspace names * Moved role definitions, Fixed tags & comment * Moved role definitions; Fixed scopes, tags, & comments * Moved role definitions * Moved role definitions; Fixed tags & comments * Compiled bicep changes * Sorted params, Added marketplace terms deployment & dependency * Added missing params * Added param for session hosts deployment * Compiled bicep changes * Added new element for security groups * Added example of security principal, Updated property name for security principal * Compiled bicep changes * Fixed scopes * Fix tags, Simplified subnet resource ID * Added resources for tagging * Fixed names for app group & workspaces * Fixed tags * Fixed name for app group * Removed param * Compiled bicep changes * Fixed app group name * Added VM plan * Fixed JSON conversion * Updated role assignment for scaling plan mgmt * Moved control plane to mgmt * Moved control plane to management * Added policy & resource group; Updated deployment names; Fixed tags; Moved role assignment & function app; Updated comments * Moved mgmt step * Fixed tags * Fixed tags * Updated SKU & PS version * Moved function resource * Added function resource * Moved function app & function * Fixed resource group scope * Added condition to role assignment * Fixed VM size constraints * Updated naming changes * GitHub Action: Build Bicep to JSON --------- Co-authored-by: github-actions --- .../artifacts/Disable-Autoscale.ps1 | 6 +- .../artifacts/Set-AzureMarketplaceTerms.ps1 | 4 +- .../Set-HostPoolRegistrationToken.ps1 | 57 - .../modules/common/function.bicep | 27 - .../modules/controlPlane/controlPlane.bicep | 103 - .../fslogix/azureFiles/azureFiles.bicep | 38 +- .../modules/fslogix/fslogix.bicep | 55 +- .../azure-virtual-desktop/modules/logic.bicep | 100 - .../applicationGroup.bicep | 42 +- .../modules/management/diskAccess.bicep | 12 +- .../modules/management/functionApp.bicep | 99 +- .../hostPool.bicep | 19 +- .../modules/management/management.bicep | 268 +- .../modules/management/monitoring.bicep | 16 +- .../modules/management/policy.bicep | 3 + .../management/recoveryServicesVault.bicep | 19 +- .../modules/management/scalingPlan.bicep | 4 +- .../modules/management/virtualMachine.bicep | 17 +- .../modules/resourceGroup.bicep | 11 - .../modules/sessionHosts/sessionHosts.bicep | 114 +- .../sessionHosts/virtualMachines.bicep | 11 +- .../azure-virtual-desktop/solution.bicep | 253 +- .../azure-virtual-desktop/solution.json | 15478 ++++++++-------- .../azure-virtual-desktop/uiDefinition.json | 547 +- src/bicep/add-ons/imaging/solution.json | 257 +- src/bicep/add-ons/imaging/uiDefinition.json | 4 - src/bicep/add-ons/tier3/solution.json | 153 +- src/bicep/mlz.json | 24 +- src/bicep/modules/naming-convention.bicep | 18 +- 29 files changed, 8321 insertions(+), 9438 deletions(-) delete mode 100644 src/bicep/add-ons/azure-virtual-desktop/artifacts/Set-HostPoolRegistrationToken.ps1 delete mode 100644 src/bicep/add-ons/azure-virtual-desktop/modules/common/function.bicep delete mode 100644 src/bicep/add-ons/azure-virtual-desktop/modules/controlPlane/controlPlane.bicep delete mode 100644 src/bicep/add-ons/azure-virtual-desktop/modules/logic.bicep rename src/bicep/add-ons/azure-virtual-desktop/modules/{controlPlane => management}/applicationGroup.bicep (51%) rename src/bicep/add-ons/azure-virtual-desktop/modules/{controlPlane => management}/hostPool.bicep (75%) delete mode 100644 src/bicep/add-ons/azure-virtual-desktop/modules/resourceGroup.bicep diff --git a/src/bicep/add-ons/azure-virtual-desktop/artifacts/Disable-Autoscale.ps1 b/src/bicep/add-ons/azure-virtual-desktop/artifacts/Disable-Autoscale.ps1 index 15d9eb3e6..2b57637bf 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/artifacts/Disable-Autoscale.ps1 +++ b/src/bicep/add-ons/azure-virtual-desktop/artifacts/Disable-Autoscale.ps1 @@ -28,14 +28,14 @@ $AzureManagementHeader = @{ $ScalingPlanExists = (Invoke-RestMethod ` -Headers $AzureManagementHeader ` -Method 'GET' ` - -Uri $($ResourceManagerUriFixed + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $ResourceGroupName + '/providers/Microsoft.DesktopVirtualization/scalingPlans?api-version=2024-04-03')).value | Where-Object {$_.name -eq $ScalingPlanName} + -Uri $($ResourceManagerUriFixed + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $ResourceGroupName + '/providers/Microsoft.DesktopVirtualization/scalingPlans?api-version=2023-09-05')).value | Where-Object {$_.name -eq $ScalingPlanName} # Disable autoscale for the host pool: https://learn.microsoft.com/rest/api/desktopvirtualization/scaling-plans/update if ($ScalingPlanExists) { Invoke-RestMethod ` - -Body (@{properties = @{hostPoolReferences = @(@{hostPoolArmPath = $HostPoolResourceId; scalingPlanEnabled = $false})}} | ConvertTo-Json) ` + -Body (@{properties = @{hostPoolReferences = @(@{hostPoolArmPath = $HostPoolResourceId; scalingPlanEnabled = $false})}} | ConvertTo-Json -Depth 3) ` -Headers $AzureManagementHeader ` -Method 'PATCH' ` - -Uri $($ResourceManagerUriFixed + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $ResourceGroupName + '/providers/Microsoft.DesktopVirtualization/scalingPlans/' + $ScalingPlanName + '?api-version=2024-04-03') | Out-Null + -Uri $($ResourceManagerUriFixed + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $ResourceGroupName + '/providers/Microsoft.DesktopVirtualization/scalingPlans/' + $ScalingPlanName + '?api-version=2023-09-05') | Out-Null } \ No newline at end of file diff --git a/src/bicep/add-ons/azure-virtual-desktop/artifacts/Set-AzureMarketplaceTerms.ps1 b/src/bicep/add-ons/azure-virtual-desktop/artifacts/Set-AzureMarketplaceTerms.ps1 index 0c3873cbd..dbdf82f11 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/artifacts/Set-AzureMarketplaceTerms.ps1 +++ b/src/bicep/add-ons/azure-virtual-desktop/artifacts/Set-AzureMarketplaceTerms.ps1 @@ -2,7 +2,9 @@ param ( [string]$ImageOffer, [string]$ImagePublisher, [string]$ImageSku, - [string]$ResourceManagerUri + [string]$ResourceManagerUri, + [string]$SubscriptionId, + [string]$UserAssignedIdentityClientId ) # Fix the resource manager URI since only AzureCloud contains a trailing slash diff --git a/src/bicep/add-ons/azure-virtual-desktop/artifacts/Set-HostPoolRegistrationToken.ps1 b/src/bicep/add-ons/azure-virtual-desktop/artifacts/Set-HostPoolRegistrationToken.ps1 deleted file mode 100644 index 87e183781..000000000 --- a/src/bicep/add-ons/azure-virtual-desktop/artifacts/Set-HostPoolRegistrationToken.ps1 +++ /dev/null @@ -1,57 +0,0 @@ -Param( - [string]$HostPoolName, - [string]$HostPoolResourceGroupName, - [string]$KeyVaultUri, - [string]$ResourceManagerUri, - [string]$SubscriptionId, - [string]$UserAssignedIdentityClientId -) - -$ErrorActionPreference = 'Stop' -$WarningPreference = 'SilentlyContinue' - -# Fix the resource manager URI since only AzureCloud contains a trailing slash -$ResourceManagerUriFixed = if($ResourceManagerUri[-1] -eq '/'){$ResourceManagerUri} else {$ResourceManagerUri + '/'} - -# Get an access token for Azure resources -$AzureManagementAccessToken = (Invoke-RestMethod ` - -Headers @{Metadata="true"} ` - -Uri $('http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=' + $ResourceManagerUriFixed + '&client_id=' + $UserAssignedIdentityClientId)).access_token - -# Set header for Azure Management API -$AzureManagementHeader = @{ - 'Content-Type'='application/json' - 'Authorization'='Bearer ' + $AzureManagementAccessToken -} - -# Use the access token to update the host pool registration token -Invoke-RestMethod ` - -Body (@{properties = @{registrationInfo = @{expirationTime = $(Get-Date).AddMinutes(90); registrationTokenOperation = "Update" }}} | ConvertTo-Json) ` - -Headers $AzureManagementHeader ` - -Method 'PATCH' ` - -Uri $($ResourceManagerUriFixed + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $HostPoolResourceGroupName + '/providers/Microsoft.DesktopVirtualization/hostPools/' + $HostPoolName + '?api-version=2022-02-10-preview') | Out-Null - -# Use the access token to get the host pool registration token -$HostPoolRegistrationToken = (Invoke-RestMethod ` - -Headers $AzureManagementHeader ` - -Method 'POST' ` - -Uri $($ResourceManagerUriFixed + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $HostPoolResourceGroupName + '/providers/Microsoft.DesktopVirtualization/hostPools/' + $HostPoolName + '/retrieveRegistrationToken?api-version=2022-02-10-preview')).token - -# Get an access token for the Azure key vault -$KeyVaultAccessToken = (Invoke-RestMethod ` - -Headers @{Metadata="true"} ` - -Method 'GET' ` - -Uri $('http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=' + $KeyVaultUri + '&client_id=' + $UserAssignedIdentityClientId)).access_token - -# Set header for Azure Key Vault API -$KeyVaultHeader = @{ - 'Content-Type'='application/json' - 'Authorization'='Bearer ' + $KeyVaultAccessToken -} - -# Create a key vault secret with the host pool registration token -Invoke-RestMethod ` - -Body (@{value = $HostPoolRegistrationToken} | ConvertTo-Json) ` - -Headers $KeyVaultHeader ` - -Method 'PUT' ` - -Uri $($KeyVaultUri + '/secrets/avdHostPoolRegistrationToken?api-version=7.4') | Out-Null \ No newline at end of file diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/common/function.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/common/function.bicep deleted file mode 100644 index 347a13908..000000000 --- a/src/bicep/add-ons/azure-virtual-desktop/modules/common/function.bicep +++ /dev/null @@ -1,27 +0,0 @@ -param files object -param functionAppName string -param functionName string -param schedule string - -resource functionApp 'Microsoft.Web/sites@2020-12-01' existing = { - name: functionAppName -} - -resource function 'Microsoft.Web/sites/functions@2020-12-01' = { - parent: functionApp - name: functionName - properties: { - config: { - disabled: false - bindings: [ - { - name: 'Timer' - type: 'timerTrigger' - direction: 'in' - schedule: schedule - } - ] - } - files: files - } -} diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/controlPlane/controlPlane.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/controlPlane/controlPlane.bicep deleted file mode 100644 index 441ac9f53..000000000 --- a/src/bicep/add-ons/azure-virtual-desktop/modules/controlPlane/controlPlane.bicep +++ /dev/null @@ -1,103 +0,0 @@ -targetScope = 'subscription' - -param activeDirectorySolution string -param avdPrivateDnsZoneResourceId string -param customImageId string -param customRdpProperty string -param deploymentNameSuffix string -param deploymentUserAssignedIdentityClientId string -param deploymentUserAssignedIdentityPrincipalId string -param desktopFriendlyName string -param diskSku string -param domainName string -param enableAvdInsights bool -param hostPoolPublicNetworkAccess string -param hostPoolType string -param imageOffer string -param imagePublisher string -param imageSku string -param imageVersionResourceId string -param locationControlPlane string -param locationVirtualMachines string -param logAnalyticsWorkspaceResourceId string -param managementVirtualMachineName string -param maxSessionLimit int -param mlzTags object -param namingConvention object -param resourceGroupControlPlane string -param resourceGroupManagement string -param roleDefinitions object -param securityPrincipalObjectIds array -param serviceToken string -param sessionHostNamePrefix string -param subnetResourceId string -param tags object -param validationEnvironment bool -param virtualMachineSize string - -var galleryImageOffer = empty(imageVersionResourceId) ? '"${imageOffer}"' : 'null' -var galleryImagePublisher = empty(imageVersionResourceId) ? '"${imagePublisher}"' : 'null' -var galleryImageSku = empty(imageVersionResourceId) ? '"${imageSku}"' : 'null' -var galleryItemId = empty(imageVersionResourceId) ? '"${imagePublisher}.${imageOffer}${imageSku}"' : 'null' -var hostPoolName = namingConvention.hostPool -var imageType = empty(imageVersionResourceId) ? '"Gallery"' : '"CustomImage"' - -module hostPool 'hostPool.bicep' = { - name: 'deploy-vdpool-${deploymentNameSuffix}' - scope: resourceGroup(resourceGroupControlPlane) - params: { - activeDirectorySolution: activeDirectorySolution - avdPrivateDnsZoneResourceId: avdPrivateDnsZoneResourceId - customImageId: customImageId - customRdpProperty: customRdpProperty - deploymentUserAssignedIdentityPrincipalId: deploymentUserAssignedIdentityPrincipalId - diskSku: diskSku - domainName: domainName - enableAvdInsights: enableAvdInsights - galleryImageOffer: galleryImageOffer - galleryImagePublisher: galleryImagePublisher - galleryImageSku: galleryImageSku - galleryItemId: galleryItemId - hostPoolDiagnosticSettingName: namingConvention.hostPoolDiagnosticSetting - hostPoolName: hostPoolName - hostPoolNetworkInterfaceName: namingConvention.hostPoolNetworkInterface - hostPoolPrivateEndpointName: namingConvention.hostPoolPrivateEndpoint - hostPoolPublicNetworkAccess: hostPoolPublicNetworkAccess - hostPoolType: hostPoolType - imageType: imageType - location: locationControlPlane - logAnalyticsWorkspaceResourceId: logAnalyticsWorkspaceResourceId - maxSessionLimit: maxSessionLimit - mlzTags: mlzTags - sessionHostNamePrefix: sessionHostNamePrefix - subnetResourceId: subnetResourceId - tags: tags - validationEnvironment: validationEnvironment - virtualMachineSize: virtualMachineSize - } -} - -module applicationGroup 'applicationGroup.bicep' = { - name: 'deploy-vdag-${deploymentNameSuffix}' - scope: resourceGroup(resourceGroupControlPlane) - params: { - deploymentNameSuffix: deploymentNameSuffix - deploymentUserAssignedIdentityClientId: deploymentUserAssignedIdentityClientId - deploymentUserAssignedIdentityPrincipalId: deploymentUserAssignedIdentityPrincipalId - desktopApplicationGroupName: replace(namingConvention.applicationGroup, serviceToken, 'desktop') - hostPoolResourceId: hostPool.outputs.resourceId - locationControlPlane: locationControlPlane - locationVirtualMachines: locationVirtualMachines - mlzTags: mlzTags - resourceGroupManagement: resourceGroupManagement - roleDefinitions: roleDefinitions - securityPrincipalObjectIds: securityPrincipalObjectIds - desktopFriendlyName: desktopFriendlyName - tags: tags - virtualMachineName: managementVirtualMachineName - } -} - -output applicationGroupResourceId string = applicationGroup.outputs.resourceId -output hostPoolName string = hostPool.outputs.name -output hostPoolResourceId string = hostPool.outputs.resourceId diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/fslogix/azureFiles/azureFiles.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/fslogix/azureFiles/azureFiles.bicep index 4d2cc6277..ff4f3f0b6 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/modules/fslogix/azureFiles/azureFiles.bicep +++ b/src/bicep/add-ons/azure-virtual-desktop/modules/fslogix/azureFiles/azureFiles.bicep @@ -12,9 +12,7 @@ param encryptionUserAssignedIdentityResourceId string param fileShares array param fslogixShareSizeInGB int param fslogixContainerType string -param fslogixStorageService string -param functionAppName string -param hostPoolName string +param hostPoolResourceId string param keyVaultUri string param location string param managementVirtualMachineName string @@ -23,9 +21,7 @@ param namingConvention object param netbios string param organizationalUnitPath string param recoveryServicesVaultName string -param resourceGroupControlPlane string param resourceGroupManagement string -param resourceGroupStorage string param securityPrincipalObjectIds array param securityPrincipalNames array param serviceToken string @@ -51,10 +47,10 @@ var smbSettings = { } var storageAccountNamePrefix = uniqueString(replace(namingConvention.storageAccount, serviceToken, 'file-fslogix'), resourceGroup().id) var storageRedundancy = availability == 'availabilityZones' ? '_ZRS' : '_LRS' -var tagsPrivateEndpoints = union({'cm-resource-parent': '${subscription().id}}/resourceGroups/${resourceGroupControlPlane}/providers/Microsoft.DesktopVirtualization/hostpools/${hostPoolName}'}, contains(tags, 'Microsoft.Network/privateEndpoints') ? tags['Microsoft.Network/privateEndpoints'] : {}, mlzTags) -var tagsStorageAccounts = union({'cm-resource-parent': '${subscription().id}}/resourceGroups/${resourceGroupControlPlane}/providers/Microsoft.DesktopVirtualization/hostpools/${hostPoolName}'}, contains(tags, 'Microsoft.Storage/storageAccounts') ? tags['Microsoft.Storage/storageAccounts'] : {}, mlzTags) -var tagsRecoveryServicesVault = union({'cm-resource-parent': '${subscription().id}}/resourceGroups/${resourceGroupControlPlane}/providers/Microsoft.DesktopVirtualization/hostpools/${hostPoolName}'}, contains(tags, 'Microsoft.recoveryServices/vaults') ? tags['Microsoft.recoveryServices/vaults'] : {}, mlzTags) -var tagsVirtualMachines = union({'cm-resource-parent': '${subscription().id}}/resourceGroups/${resourceGroupControlPlane}/providers/Microsoft.DesktopVirtualization/hostpools/${hostPoolName}'}, contains(tags, 'Microsoft.Compute/virtualMachines') ? tags['Microsoft.Compute/virtualMachines'] : {}, mlzTags) +var tagsPrivateEndpoints = union({'cm-resource-parent': hostPoolResourceId}, tags[?'Microsoft.Network/privateEndpoints'] ?? {}, mlzTags) +var tagsStorageAccounts = union({'cm-resource-parent': hostPoolResourceId}, tags[?'Microsoft.Storage/storageAccounts'] ?? {}, mlzTags) +var tagsRecoveryServicesVault = union({'cm-resource-parent': hostPoolResourceId}, tags[?'Microsoft.recoveryServices/vaults'] ?? {}, mlzTags) +var tagsVirtualMachines = union({'cm-resource-parent': hostPoolResourceId}, tags[?'Microsoft.Compute/virtualMachines'] ?? {}, mlzTags) resource storageAccounts 'Microsoft.Storage/storageAccounts@2022-09-01' = [for i in range(0, storageCount): { name: take('${storageAccountNamePrefix}${padLeft(i + storageIndex, 2, '0')}', 15) @@ -206,6 +202,7 @@ resource privateDnsZoneGroups 'Microsoft.Network/privateEndpoints/privateDnsZone ] }] +// Sets NTFS permissions on the file shares module ntfsPermissions '../runCommand.bicep' = { name: 'set-ntfs-permissions-${deploymentNameSuffix}' scope: resourceGroup(resourceGroupManagement) @@ -245,7 +242,7 @@ module ntfsPermissions '../runCommand.bicep' = { } { name: 'StorageAccountResourceGroupName' - value: resourceGroupStorage + value: resourceGroup().name } { name: 'StorageCount' @@ -283,6 +280,7 @@ module ntfsPermissions '../runCommand.bicep' = { ] } +// Deploys backup items for Azure Files module recoveryServices 'recoveryServices.bicep' = if (enableRecoveryServices) { name: 'deploy-backup-${deploymentNameSuffix}' scope: resourceGroup(resourceGroupManagement) @@ -291,7 +289,7 @@ module recoveryServices 'recoveryServices.bicep' = if (enableRecoveryServices) { fileShares: fileShares location: location recoveryServicesVaultName: recoveryServicesVaultName - resourceGroupStorage: resourceGroupStorage + resourceGroupStorage: resourceGroup().name storageAccountNamePrefix: storageAccountNamePrefix storageCount: storageCount storageIndex: storageIndex @@ -302,22 +300,4 @@ module recoveryServices 'recoveryServices.bicep' = if (enableRecoveryServices) { ] } -module autoIncreaseStandardFileShareQuota '../../common/function.bicep' = if (fslogixStorageService == 'AzureFiles Premium' && storageCount > 0) { - name: 'deploy-file-share-scaling-${deploymentNameSuffix}' - scope: resourceGroup(resourceGroupManagement) - params: { - files: { - 'requirements.psd1': loadTextContent('../../../artifacts/auto-increase-file-share/requirements.psd1') - 'run.ps1': loadTextContent('../../../artifacts/auto-increase-file-share/run.ps1') - '../profile.ps1': loadTextContent('../../../artifacts/auto-increase-file-share/profile.ps1') - } - functionAppName: functionAppName - functionName: 'auto-increase-file-share-quota' - schedule: '0 */15 * * * *' - } - dependsOn: [ - recoveryServices - ] -} - output storageAccountNamePrefix string = storageAccountNamePrefix diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/fslogix/fslogix.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/fslogix/fslogix.bicep index d4555ce84..3a7fb800d 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/modules/fslogix/fslogix.bicep +++ b/src/bicep/add-ons/azure-virtual-desktop/modules/fslogix/fslogix.bicep @@ -1,12 +1,12 @@ targetScope = 'subscription' -param existingSharedActiveDirectoryConnection bool param activeDirectorySolution string param availability string param azureFilesPrivateDnsZoneResourceId string param subnets array param deploymentNameSuffix string param deploymentUserAssignedIdentityClientId string +param deploymentUserAssignedIdentityPrincipalId string param dnsServers string @secure() param domainJoinPassword string @@ -14,11 +14,13 @@ param domainJoinPassword string param domainJoinUserPrincipalName string param domainName string param encryptionUserAssignedIdentityResourceId string +param existingSharedActiveDirectoryConnection bool param fileShares array -param fslogixShareSizeInGB int param fslogixContainerType string +param fslogixShareSizeInGB int param fslogixStorageService string -param functionAppName string +param functionAppPrincipalId string +param hostPoolResourceId string param keyVaultUri string param location string param managementVirtualMachineName string @@ -27,9 +29,8 @@ param namingConvention object param netbios string param organizationalUnitPath string param recoveryServices bool -param resourceGroupControlPlane string param resourceGroupManagement string -param resourceGroupStorage string +param resourceGroupName string param securityPrincipalObjectIds array param securityPrincipalNames array param serviceToken string @@ -42,15 +43,43 @@ param storageService string param subnetResourceId string param tags object -var hostPoolName = namingConvention.hostPool +var tagsNetAppAccount = union({'cm-resource-parent': hostPoolResourceId}, tags[?'Microsoft.NetApp/netAppAccounts'] ?? {}, mlzTags) +var tagsVirtualMachines = union({'cm-resource-parent': hostPoolResourceId}, tags[?'Microsoft.Compute/virtualMachines'] ?? {}, mlzTags) -var tagsNetAppAccount = union({'cm-resource-parent': '${subscription().id}}/resourceGroups/${resourceGroupControlPlane}/providers/Microsoft.DesktopVirtualization/hostpools/${hostPoolName}'}, contains(tags, 'Microsoft.NetApp/netAppAccounts') ? tags['Microsoft.NetApp/netAppAccounts'] : {}, mlzTags) -var tagsVirtualMachines = union({'cm-resource-parent': '${subscription().id}}/resourceGroups/${resourceGroupControlPlane}/providers/Microsoft.DesktopVirtualization/hostpools/${hostPoolName}'}, contains(tags, 'Microsoft.Compute/virtualMachines') ? tags['Microsoft.Compute/virtualMachines'] : {}, mlzTags) +resource resourceGroup 'Microsoft.Resources/resourceGroups@2023-07-01' = { + name: resourceGroupName + location: location + tags: union({'cm-resource-parent': hostPoolResourceId}, tags[?'Microsoft.Resources/resourceGroups'] ?? {}, mlzTags) +} + +// Role Assignment for FSLogix +// Purpose: assigns the Storage Account Contributor role to the managed identity on the +// management virtual machine storage resource group to domain join storage account(s) & set NTFS permissions on the file share(s) +module roleAssignment_Storage '../common/roleAssignments/resourceGroup.bicep' = { + name: 'assign-role-storage-${deploymentNameSuffix}' + scope: resourceGroup + params: { + principalId: deploymentUserAssignedIdentityPrincipalId + principalType: 'ServicePrincipal' + roleDefinitionId: '17d1049b-9a84-46fb-8f53-869881c3d3ab' + } +} + +// Required role assignment for the funciton to manage the quota on Azure Files Premium +module roleAssignments_resourceGroup '../common/roleAssignments/resourceGroup.bicep' = if (fslogixStorageService == 'AzureFiles Premium') { + name: 'set-role-assignment-${deploymentNameSuffix}' + scope: resourceGroup + params: { + principalId: functionAppPrincipalId + principalType: 'ServicePrincipal' + roleDefinitionId: '17d1049b-9a84-46fb-8f53-869881c3d3ab' // Storage Account Contributor + } +} // Azure NetApp Files for Fslogix module azureNetAppFiles 'azureNetAppFiles.bicep' = if (storageService == 'AzureNetAppFiles') { name: 'deploy-anf-${deploymentNameSuffix}' - scope: resourceGroup(resourceGroupStorage) + scope: resourceGroup params: { existingSharedActiveDirectoryConnection: existingSharedActiveDirectoryConnection delegatedSubnetResourceId: filter(subnets, subnet => contains(subnet.name, 'AzureNetAppFiles'))[0].id @@ -79,7 +108,7 @@ module azureNetAppFiles 'azureNetAppFiles.bicep' = if (storageService == 'AzureN // Azure Files for FSLogix module azureFiles 'azureFiles/azureFiles.bicep' = if (storageService == 'AzureFiles') { name: 'deploy-azure-files-${deploymentNameSuffix}' - scope: resourceGroup(resourceGroupStorage) + scope: resourceGroup params: { activeDirectorySolution: activeDirectorySolution availability: availability @@ -93,8 +122,7 @@ module azureFiles 'azureFiles/azureFiles.bicep' = if (storageService == 'AzureFi fileShares: fileShares fslogixContainerType: fslogixContainerType fslogixShareSizeInGB: fslogixShareSizeInGB - fslogixStorageService: fslogixStorageService - functionAppName: functionAppName + hostPoolResourceId: hostPoolResourceId keyVaultUri: keyVaultUri location: location managementVirtualMachineName: managementVirtualMachineName @@ -103,7 +131,6 @@ module azureFiles 'azureFiles/azureFiles.bicep' = if (storageService == 'AzureFi organizationalUnitPath: organizationalUnitPath recoveryServicesVaultName: namingConvention.recoveryServicesVault resourceGroupManagement: resourceGroupManagement - resourceGroupStorage: resourceGroupStorage securityPrincipalNames: securityPrincipalNames securityPrincipalObjectIds: securityPrincipalObjectIds serviceToken: serviceToken @@ -114,9 +141,7 @@ module azureFiles 'azureFiles/azureFiles.bicep' = if (storageService == 'AzureFi storageSku: storageSku subnetResourceId: subnetResourceId tags: tags - hostPoolName: hostPoolName mlzTags: mlzTags - resourceGroupControlPlane: resourceGroupControlPlane } } diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/logic.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/logic.bicep deleted file mode 100644 index 1ef2d017e..000000000 --- a/src/bicep/add-ons/azure-virtual-desktop/modules/logic.bicep +++ /dev/null @@ -1,100 +0,0 @@ -targetScope = 'subscription' - -param activeDirectorySolution string -param deploymentLocations array -param diskSku string -param domainName string -param fileShareNames object -param fslogixContainerType string -param fslogixStorageService string -param imageOffer string -param imagePublisher string -param imageSku string -param imageVersionResourceId string -param locations object -param locationVirtualMachines string -param networkName string -param resourceGroupControlPlane string -param resourceGroupFeedWorkspace string -param resourceGroupHosts string -param resourceGroupManagement string -param resourceGroupsNetwork array -param resourceGroupStorage string -param securityPrincipals array -param serviceName string -param sessionHostCount int -param sessionHostIndex int -param virtualMachineNamePrefix string -param virtualMachineSize string - -// BATCH SESSION HOSTS -// The following variables are used to determine the batches to deploy any number of AVD session hosts. -var maxResourcesPerTemplateDeployment = 88 // This is the max number of session hosts that can be deployed from the sessionHosts.bicep file in each batch / for loop. Math: (800 - ) / -var divisionValue = sessionHostCount / maxResourcesPerTemplateDeployment // This determines if any full batches are required. -var divisionRemainderValue = sessionHostCount % maxResourcesPerTemplateDeployment // This determines if any partial batches are required. -var sessionHostBatchCount = divisionRemainderValue > 0 ? divisionValue + 1 : divisionValue // This determines the total number of batches needed, whether full and / or partial. - -// BATCH AVAILABILITY SETS -// The following variables are used to determine the number of availability sets. -var maxAvSetMembers = 200 // This is the max number of session hosts that can be deployed in an availability set. -var beginAvSetRange = sessionHostIndex / maxAvSetMembers // This determines the availability set to start with. -var endAvSetRange = (sessionHostCount + sessionHostIndex) / maxAvSetMembers // This determines the availability set to end with. -var availabilitySetsCount = length(range(beginAvSetRange, (endAvSetRange - beginAvSetRange) + 1)) - -// OTHER LOGIC & COMPUTED VALUES -var customImageId = empty(imageVersionResourceId) ? 'null' : '"${imageVersionResourceId}"' -var fileShares = fileShareNames[fslogixContainerType] -var fslogix = fslogixStorageService == 'None' || !contains(activeDirectorySolution, 'DomainServices') ? false : true -var galleryImageOffer = empty(imageVersionResourceId) ? '"${imageOffer}"' : 'null' -var galleryImagePublisher = empty(imageVersionResourceId) ? '"${imagePublisher}"' : 'null' -var galleryImageSku = empty(imageVersionResourceId) ? '"${imageSku}"' : 'null' -var galleryItemId = empty(imageVersionResourceId) ? '"${imagePublisher}.${imageOffer}${imageSku}"' : 'null' -var imageType = empty(imageVersionResourceId) ? '"Gallery"' : '"CustomImage"' -var netbios = split(domainName, '.')[0] -var resourceGroups = union(resourceGroupsCommon, resourceGroupsNetworking, resourceGroupsStorage) -var resourceGroupsCommon = [ - resourceGroupControlPlane - resourceGroupFeedWorkspace - resourceGroupHosts - resourceGroupManagement -] -var resourceGroupsNetworking = length(deploymentLocations) == 2 ? resourceGroupsNetwork : [ - resourceGroupsNetwork[0] -] -var resourceGroupsStorage = fslogix ? [ - resourceGroupStorage -] : [] -var roleDefinitions = { - DesktopVirtualizationPowerOnContributor: '489581de-a3bd-480d-9518-53dea7416b33' - DesktopVirtualizationUser: '1d18fff3-a72a-46b5-b4a9-0b38a3cd7e63' - Reader: 'acdd72a7-3385-48ef-bd42-f606fba81ae7' - VirtualMachineUserLogin: 'fb879df8-f326-4884-b1cf-06f3ad86be52' -} -var securityPrincipalsCount = length(securityPrincipals) -var sessionHostNamePrefix = replace(virtualMachineNamePrefix, '${serviceName}${networkName}', '') -var smbServerLocation = locations[locationVirtualMachines].abbreviation -var storageSku = fslogixStorageService == 'None' ? 'None' : split(fslogixStorageService, ' ')[1] -var storageService = split(fslogixStorageService, ' ')[0] -var storageSuffix = environment().suffixes.storage -var timeDifference = locations[locationVirtualMachines].timeDifference -var timeZone = locations[locationVirtualMachines].timeZone -var vmTemplate = '{"domain":"${domainName}","galleryImageOffer":${galleryImageOffer},"galleryImagePublisher":${galleryImagePublisher},"galleryImageSKU":${galleryImageSku},"imageType":${imageType},"customImageId":${customImageId},"namePrefix":"${sessionHostNamePrefix}","osDiskType":"${diskSku}","vmSize":{"id":"${virtualMachineSize}","cores":null,"ram":null,"rdmaEnabled": false,"supportsMemoryPreservingMaintenance": true},"galleryItemId":${galleryItemId},"hibernate":false,"diskSizeGB":0,"securityType":"TrustedLaunch","secureBoot":true,"vTPM":true,"vmInfrastructureType":"Cloud","virtualProcessorCount":null,"memoryGB":null,"maximumMemoryGB":null,"minimumMemoryGB":null,"dynamicMemoryConfig":false}' - -output availabilitySetsCount int = availabilitySetsCount -output beginAvSetRange int = beginAvSetRange -output divisionRemainderValue int = divisionRemainderValue -output fileShares array = fileShares -output fslogix bool = fslogix -output maxResourcesPerTemplateDeployment int = maxResourcesPerTemplateDeployment -output netbios string = netbios -output resourceGroups array = resourceGroups -output roleDefinitions object = roleDefinitions -output sessionHostBatchCount int = sessionHostBatchCount -output securityPrincipalsCount int = securityPrincipalsCount -output smbServerLocation string = smbServerLocation -output storageSku string = storageSku -output storageService string = storageService -output storageSuffix string = storageSuffix -output timeDifference string = timeDifference -output timeZone string = timeZone -output vmTemplate string = vmTemplate diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/controlPlane/applicationGroup.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/management/applicationGroup.bicep similarity index 51% rename from src/bicep/add-ons/azure-virtual-desktop/modules/controlPlane/applicationGroup.bicep rename to src/bicep/add-ons/azure-virtual-desktop/modules/management/applicationGroup.bicep index 9093c1d72..149f3e530 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/modules/controlPlane/applicationGroup.bicep +++ b/src/bicep/add-ons/azure-virtual-desktop/modules/management/applicationGroup.bicep @@ -1,43 +1,28 @@ param deploymentNameSuffix string param deploymentUserAssignedIdentityClientId string -param deploymentUserAssignedIdentityPrincipalId string param desktopApplicationGroupName string param desktopFriendlyName string param hostPoolResourceId string param locationControlPlane string param locationVirtualMachines string param mlzTags object -param resourceGroupManagement string -param roleDefinitions object param securityPrincipalObjectIds array param tags object param virtualMachineName string -resource applicationGroup 'Microsoft.DesktopVirtualization/applicationGroups@2024-04-03' = { +resource applicationGroup 'Microsoft.DesktopVirtualization/applicationGroups@2023-09-05' = { name: desktopApplicationGroupName location: locationControlPlane - tags: union({ - 'cm-resource-parent': hostPoolResourceId - }, contains(tags, 'Microsoft.DesktopVirtualization/applicationGroups') ? tags['Microsoft.DesktopVirtualization/applicationGroups'] : {}, mlzTags) + tags: union({'cm-resource-parent': hostPoolResourceId}, tags[?'Microsoft.DesktopVirtualization/applicationGroups'] ?? {}, mlzTags) properties: { hostPoolArmPath: hostPoolResourceId applicationGroupType: 'Desktop' } } -resource roleAssignment_ManagedIdentity 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - name: guid(deploymentUserAssignedIdentityPrincipalId, '86240b0e-9422-4c43-887b-b61143f32ba8', applicationGroup.id) - scope: applicationGroup - properties: { - roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', '86240b0e-9422-4c43-887b-b61143f32ba8') // Desktop Virtualization Application Group Contributor (Purpose: updates the friendly name for the desktop) - principalId: deploymentUserAssignedIdentityPrincipalId - principalType: 'ServicePrincipal' - } -} - -// Adds a friendly name to the SessionDesktop application for the desktop application group +// Run Command to update the Application +// Purpose: executes a script to update the friendly name on the application module applicationFriendlyName '../common/runCommand.bicep' = if (!empty(desktopFriendlyName)) { - scope: resourceGroup(resourceGroupManagement) name: 'deploy-vdapp-friendly-name-${deploymentNameSuffix}' params: { location: locationVirtualMachines @@ -69,25 +54,18 @@ module applicationFriendlyName '../common/runCommand.bicep' = if (!empty(desktop } ] script: loadTextContent('../../artifacts/Update-AvdDesktop.ps1') - tags: union( - { - 'cm-resource-parent': hostPoolResourceId - }, - contains(tags, 'Microsoft.Compute/virtualMachines') ? tags['Microsoft.Compute/virtualMachines'] : {}, - mlzTags - ) + tags: union({'cm-resource-parent': hostPoolResourceId}, tags[?'Microsoft.Compute/virtualMachines'] ?? {}, mlzTags) virtualMachineName: virtualMachineName } - dependsOn: [ - roleAssignment_ManagedIdentity - ] } -resource roleAssignment_Users 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for i in range(0, length(securityPrincipalObjectIds)): { +// Role Assignment for AVD access +// Purpose: assigns the Desktop Virtualization User role to the application group for the specified security principals +resource roleAssignment_ApplicationGroup 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for i in range(0, length(securityPrincipalObjectIds)): { + name: guid(securityPrincipalObjectIds[i], '1d18fff3-a72a-46b5-b4a9-0b38a3cd7e63', applicationGroup.id) scope: applicationGroup - name: guid(securityPrincipalObjectIds[i], roleDefinitions.DesktopVirtualizationUser, desktopApplicationGroupName) properties: { - roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', roleDefinitions.DesktopVirtualizationUser) + roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', '1d18fff3-a72a-46b5-b4a9-0b38a3cd7e63') principalId: securityPrincipalObjectIds[i] } }] diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/management/diskAccess.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/management/diskAccess.bicep index db57de786..4eb8fc219 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/modules/management/diskAccess.bicep +++ b/src/bicep/add-ons/azure-virtual-desktop/modules/management/diskAccess.bicep @@ -1,26 +1,21 @@ -param hostPoolName string +param hostPoolResourceId string param location string param mlzTags object param namingConvention object -param resourceGroupControlPlane string param subnetResourceId string param tags object resource diskAccess 'Microsoft.Compute/diskAccesses@2021-04-01' = { name: namingConvention.diskAccess location: location - tags: union({ - 'cm-resource-parent': '${subscription().id}}/resourceGroups/${resourceGroupControlPlane}/providers/Microsoft.DesktopVirtualization/hostpools/${hostPoolName}' - }, contains(tags, 'Microsoft.Compute/diskAccesses') ? tags['Microsoft.Compute/diskAccesses'] : {}, mlzTags) + tags: union({'cm-resource-parent': hostPoolResourceId}, tags[?'Microsoft.Compute/diskAccesses'] ?? {}, mlzTags) properties: {} } resource privateEndpoint 'Microsoft.Network/privateEndpoints@2023-04-01' = { name: namingConvention.diskAccessPrivateEndpoint location: location - tags: union({ - 'cm-resource-parent': '${subscription().id}}/resourceGroups/${resourceGroupControlPlane}/providers/Microsoft.DesktopVirtualization/hostpools/${hostPoolName}' - }, contains(tags, 'Microsoft.Network/privateEndpoints') ? tags['Microsoft.Network/privateEndpoints'] : {}, mlzTags) + tags: union({'cm-resource-parent': hostPoolResourceId}, tags[?'Microsoft.Network/privateEndpoints'] ?? {}, mlzTags) properties: { customNetworkInterfaceName: namingConvention.diskAccessNetworkInterface privateLinkServiceConnections: [ @@ -40,5 +35,4 @@ resource privateEndpoint 'Microsoft.Network/privateEndpoints@2023-04-01' = { } } - output resourceId string = diskAccess.id diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/management/functionApp.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/management/functionApp.bicep index f4af029e1..9d4ccc520 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/modules/management/functionApp.bicep +++ b/src/bicep/add-ons/azure-virtual-desktop/modules/management/functionApp.bicep @@ -2,21 +2,20 @@ param delegatedSubnetResourceId string param deploymentNameSuffix string param enableApplicationInsights bool param environmentAbbreviation string -param hostPoolName string +param hostPoolResourceId string param keyExpirationInDays int = 30 param location string = resourceGroup().location param logAnalyticsWorkspaceResourceId string +param mlzTags object param namingConvention object param privateDnsZoneResourceIdPrefix string param privateDnsZones array param privateLinkScopeResourceId string param resourceAbbreviations object -param resourceGroupControlPlane string -param resourceGroupStorage string +param resourceGroupProfiles string param serviceToken string param subnetResourceId string param tags object -param timeDifference string var cloudSuffix = replace(replace(environment().resourceManager, 'https://management.', ''), '/', '') var functionAppKeyword = environment().name == 'AzureCloud' || environment().name == 'AzureUSGovernment' ? 'azurewebsites' : 'appservice' @@ -52,13 +51,13 @@ var storageSubResources = [ resource userAssignedIdentity 'Microsoft.ManagedIdentity/userAssignedIdentities@2023-01-31' = { name: replace(namingConvention.userAssignedIdentity, serviceToken, service) location: location - tags: tags[?'Microsoft.ManagedIdentity/userAssignedIdentities'] ?? {} + tags: union({'cm-resource-parent': hostPoolResourceId}, tags[?'Microsoft.ManagedIdentity/userAssignedIdentities'] ?? {}, mlzTags) } resource vault 'Microsoft.KeyVault/vaults@2022-07-01' = { name: '${resourceAbbreviations.keyVaults}${uniqueString(replace(namingConvention.keyVault, serviceToken, service), resourceGroup().id)}' location: location - tags: tags[?'Microsoft.KeyVault/vaults'] ?? {} + tags: union({'cm-resource-parent': hostPoolResourceId}, tags[?'Microsoft.KeyVault/vaults'] ?? {}, mlzTags) properties: { enabledForDeployment: false enabledForDiskEncryption: false @@ -95,7 +94,7 @@ resource roleAssignment_Encryption 'Microsoft.Authorization/roleAssignments@2020 resource privateEndpoint_vault 'Microsoft.Network/privateEndpoints@2023-04-01' = { name: replace(namingConvention.keyVaultPrivateEndpoint, serviceToken, service) location: location - tags: tags[?'Microsoft.Network/privateEndpoints'] ?? {} + tags: union({'cm-resource-parent': hostPoolResourceId}, tags[?'Microsoft.Network/privateEndpoints'] ?? {}, mlzTags) properties: { customNetworkInterfaceName: replace(namingConvention.keyVaultNetworkInterface, serviceToken, service) privateLinkServiceConnections: [ @@ -169,7 +168,7 @@ resource key_storageAccount 'Microsoft.KeyVault/vaults/keys@2022-07-01' = { resource storageAccount 'Microsoft.Storage/storageAccounts@2022-09-01' = { name: uniqueString(replace(namingConvention.storageAccount, serviceToken, service), resourceGroup().id) location: location - tags: tags[?'Microsoft.Storage/storageAccounts'] ?? {} + tags: union({'cm-resource-parent': hostPoolResourceId}, tags[?'Microsoft.Storage/storageAccounts'] ?? {}, mlzTags) sku: { name: 'Standard_LRS' } @@ -247,7 +246,7 @@ resource privateEndpoints_storage 'Microsoft.Network/privateEndpoints@2023-04-01 for resource in storageSubResources: { name: replace(resource.pe, serviceToken, service) location: location - tags: tags[?'Microsoft.Network/privateEndpoints'] ?? {} + tags: union({'cm-resource-parent': hostPoolResourceId}, tags[?'Microsoft.Network/privateEndpoints'] ?? {}, mlzTags) properties: { customNetworkInterfaceName: replace(resource.nic, serviceToken, service) privateLinkServiceConnections: [ @@ -313,7 +312,7 @@ resource diagnosticSetting_storage_blob 'Microsoft.Insights/diagnosticsettings@2 resource applicationInsights 'Microsoft.Insights/components@2020-02-02' = if (enableApplicationInsights) { name: replace(namingConvention.applicationInsights, serviceToken, service) location: location - tags: tags[?'Microsoft.Insights/components'] ?? {} + tags: union({'cm-resource-parent': hostPoolResourceId}, tags[?'Microsoft.Insights/components'] ?? {}, mlzTags) properties: { Application_Type: 'web' publicNetworkAccessForIngestion: 'Disabled' @@ -334,11 +333,11 @@ module privateLinkScope 'privateLinkScope.bicep' = if (enableApplicationInsights resource appServicePlan 'Microsoft.Web/serverfarms@2023-01-01' = { name: replace(namingConvention.appServicePlan, serviceToken, service) location: location - tags: tags[?'Microsoft.Web/serverfarms'] ?? {} + tags: union({'cm-resource-parent': hostPoolResourceId}, tags[?'Microsoft.Web/serverfarms'] ?? {}, mlzTags) sku: { - name: 'P0v3' + name: 'P1v3' tier: 'PremiumV3' - size: 'P0v3' + size: 'P1v3' family: 'Pv3' capacity: 1 } @@ -348,7 +347,7 @@ resource appServicePlan 'Microsoft.Web/serverfarms@2023-01-01' = { resource functionApp 'Microsoft.Web/sites@2023-01-01' = { name: uniqueString(replace(namingConvention.functionApp, serviceToken, service), resourceGroup().id) location: location - tags: tags[?'Microsoft.Web/sites'] ?? {} + tags: union({'cm-resource-parent': hostPoolResourceId}, tags[?'Microsoft.Web/sites'] ?? {}, mlzTags) kind: 'functionapp' identity: { type: 'SystemAssigned' @@ -389,37 +388,13 @@ resource functionApp 'Microsoft.Web/sites@2023-01-01' = { name: 'WEBSITE_LOAD_USER_PROFILE' value: '1' } - { - name: 'EnvironmentName' - value: environment().name - } { name: 'FileShareName' value: 'profile-containers' } - { - name: 'HostPoolName' - value: hostPoolName - } - { - name: 'HostPoolResourceGroupName' - value: resourceGroupControlPlane - } - { - name: 'LogOffMessageBody' - value: 'This session is about to be logged off. Please save your work.' - } - { - name: 'LogOffMessageTitle' - value: 'Session Log Off' - } - { - name: 'MaintenanceTagName' - value: 'Maintenance' - } { name: 'ResourceGroupName' - value: resourceGroupStorage + value: resourceGroupProfiles } { name: 'ResourceManagerUrl' @@ -436,14 +411,6 @@ resource functionApp 'Microsoft.Web/sites@2023-01-01' = { name: 'SubscriptionId' value: subscription().subscriptionId } - { - name: 'TenantId' - value: subscription().tenantId - } - { - name: 'TimeDifference' - value: timeDifference - } ], enableApplicationInsights ? [ { name: 'APPLICATIONINSIGHTS_CONNECTION_STRING' @@ -460,7 +427,7 @@ resource functionApp 'Microsoft.Web/sites@2023-01-01' = { } ftpsState: 'Disabled' netFrameworkVersion: 'v6.0' - powerShellVersion: '7.2' + powerShellVersion: '7.4' publicNetworkAccess: 'Disabled' use32BitWorkerProcess: false } @@ -473,6 +440,7 @@ resource functionApp 'Microsoft.Web/sites@2023-01-01' = { resource privateEndpoint_functionApp 'Microsoft.Network/privateEndpoints@2023-04-01' = { name: replace(namingConvention.functionAppPrivateEndpoint, serviceToken, service) location: location + tags: union({'cm-resource-parent': hostPoolResourceId}, tags[?'Microsoft.Network/privateEndpoints'] ?? {}, mlzTags) properties: { customNetworkInterfaceName: replace(namingConvention.functionAppNetworkInterface, serviceToken, service) privateLinkServiceConnections: [ @@ -508,17 +476,6 @@ resource privateDnsZoneGroup_functionApp 'Microsoft.Network/privateEndpoints/pri } } -// Required role assignment for the funciton to manage the quota on Azure Files Premium -module roleAssignments_resourceGroups '../common/roleAssignments/resourceGroup.bicep' = { - name: 'set-role-assignment-${deploymentNameSuffix}' - scope: resourceGroup(resourceGroupStorage) - params: { - principalId: functionApp.identity.principalId - principalType: 'ServicePrincipal' - roleDefinitionId: '17d1049b-9a84-46fb-8f53-869881c3d3ab' // Storage Account Contributor - } -} - // Required role assignment to support the zero trust deployment of a function app module roleAssignment_storageAccount '../common/roleAssignments/storageAccount.bicep' = { name: 'set-role-assignment-storage-${deploymentNameSuffix}' @@ -541,4 +498,28 @@ module scmARecord 'aRecord.bicep' = { } } +resource function 'Microsoft.Web/sites/functions@2020-12-01' = { + parent: functionApp + name: 'auto-increase-file-share-quota' + properties: { + config: { + disabled: false + bindings: [ + { + name: 'Timer' + type: 'timerTrigger' + direction: 'in' + schedule: '0 */15 * * * *' + } + ] + } + files: { + 'requirements.psd1': loadTextContent('../../artifacts/auto-increase-file-share/requirements.psd1') + 'run.ps1': loadTextContent('../../artifacts/auto-increase-file-share/run.ps1') + '../profile.ps1': loadTextContent('../../artifacts/auto-increase-file-share/profile.ps1') + } + } +} + output functionAppName string = functionApp.name +output functionAppPrincipalId string = functionApp.identity.principalId diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/controlPlane/hostPool.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/management/hostPool.bicep similarity index 75% rename from src/bicep/add-ons/azure-virtual-desktop/modules/controlPlane/hostPool.bicep rename to src/bicep/add-ons/azure-virtual-desktop/modules/management/hostPool.bicep index e98bf306c..40ba676bc 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/modules/controlPlane/hostPool.bicep +++ b/src/bicep/add-ons/azure-virtual-desktop/modules/management/hostPool.bicep @@ -1,6 +1,5 @@ param activeDirectorySolution string param avdPrivateDnsZoneResourceId string -param deploymentUserAssignedIdentityPrincipalId string param customImageId string param customRdpProperty string param diskSku string @@ -33,9 +32,7 @@ var customRdpProperty_Complete = contains(activeDirectorySolution, 'MicrosoftEnt resource hostPool 'Microsoft.DesktopVirtualization/hostPools@2023-09-05' = { name: hostPoolName location: location - tags: union({ - 'cm-resource-parent': '${subscription().id}}/resourceGroups/${resourceGroup().name}/providers/Microsoft.DesktopVirtualization/hostpools/${hostPoolName}' - }, contains(tags, 'Microsoft.DesktopVirtualization/hostPools') ? tags['Microsoft.DesktopVirtualization/hostPools'] : {}, mlzTags) + tags: union({'cm-resource-parent': '${subscription().id}}/resourceGroups/${resourceGroup().name}/providers/Microsoft.DesktopVirtualization/hostpools/${hostPoolName}'}, tags[?'Microsoft.DesktopVirtualization/hostPools'] ?? {}, mlzTags) properties: { customRdpProperty: customRdpProperty_Complete hostPoolType: hostPoolType @@ -57,9 +54,7 @@ resource hostPool 'Microsoft.DesktopVirtualization/hostPools@2023-09-05' = { resource privateEndpoint 'Microsoft.Network/privateEndpoints@2023-04-01' = { name: hostPoolPrivateEndpointName location: location - tags: union({ - 'cm-resource-parent': '${subscription().id}}/resourceGroups/${resourceGroup().name}/providers/Microsoft.DesktopVirtualization/hostpools/${hostPoolName}' - }, contains(tags, 'Microsoft.Network/privateEndpoints') ? tags['Microsoft.Network/privateEndpoints'] : {}, mlzTags) + tags: union({'cm-resource-parent': hostPool.id}, tags[?'Microsoft.Network/privateEndpoints'] ?? {}, mlzTags) properties: { customNetworkInterfaceName: hostPoolNetworkInterfaceName privateLinkServiceConnections: [ @@ -94,16 +89,6 @@ resource privateDnsZoneGroup 'Microsoft.Network/privateEndpoints/privateDnsZoneG } } -resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - name: guid(deploymentUserAssignedIdentityPrincipalId, '2ad6aaab-ead9-4eaa-8ac5-da422f562408', hostPool.id) - scope: hostPool - properties: { - roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', '2ad6aaab-ead9-4eaa-8ac5-da422f562408') // Desktop Virtualization Session Host Operator (Purpose: sets drain mode on the AVD session hosts) - principalId: deploymentUserAssignedIdentityPrincipalId - principalType: 'ServicePrincipal' - } -} - resource diagnosticSetting 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (enableAvdInsights) { name: hostPoolDiagnosticSettingName scope: hostPool diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/management/management.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/management/management.bicep index 6102b23f9..874468670 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/modules/management/management.bicep +++ b/src/bicep/add-ons/azure-virtual-desktop/modules/management/management.bicep @@ -1,8 +1,13 @@ targetScope = 'subscription' +param activeDirectorySolution string param avdObjectId string +param avdPrivateDnsZoneResourceId string +param customImageId string +param customRdpProperty string param deployFslogix bool param deploymentNameSuffix string +param desktopFriendlyName string param diskEncryptionSetResourceId string param diskSku string @secure() @@ -13,9 +18,17 @@ param enableApplicationInsights bool param enableAvdInsights bool param environmentAbbreviation string param fslogixStorageService string +param hostPoolPublicNetworkAccess string +param hostPoolType string +param imageOffer string +param imagePublisher string +param imageSku string +param imageVersionResourceId string +param locationControlPlane string param locationVirtualMachines string param logAnalyticsWorkspaceRetention int param logAnalyticsWorkspaceSku string +param maxSessionLimit int param mlzTags object param namingConvention object param organizationalUnitPath string @@ -25,111 +38,152 @@ param privateLinkScopeResourceId string param recoveryServices bool param recoveryServicesGeo string param resourceAbbreviations object -param resourceGroupControlPlane string -param resourceGroupHosts string -param resourceGroupManagement string -param resourceGroupStorage string -param roleDefinitions object +param resourceGroupName string +param resourceGroupProfiles string +param securityPrincipalObjectIds array param serviceToken string +param sessionHostNamePrefix string param storageService string param subnetResourceId string param subnets array param tags object -param timeDifference string param timeZone string +param validationEnvironment bool @secure() param virtualMachinePassword string +param virtualMachineSize string param virtualMachineUsername string +var galleryImageOffer = empty(imageVersionResourceId) ? '"${imageOffer}"' : 'null' +var galleryImagePublisher = empty(imageVersionResourceId) ? '"${imagePublisher}"' : 'null' +var galleryImageSku = empty(imageVersionResourceId) ? '"${imageSku}"' : 'null' +var galleryItemId = empty(imageVersionResourceId) ? '"${imagePublisher}.${imageOffer}${imageSku}"' : 'null' var hostPoolName = namingConvention.hostPool -var resourceGroups = union( - [ - resourceGroupControlPlane - resourceGroupHosts - resourceGroupManagement - ], - deployFslogix ? [ - resourceGroupStorage - ] : [] -) +var imageType = empty(imageVersionResourceId) ? '"Gallery"' : '"CustomImage"' var userAssignedIdentityNamePrefix = namingConvention.userAssignedIdentity -var virtualNetworkName = split(subnetResourceId, '/')[8] -var virtualNetworkResourceGroupName = split(subnetResourceId, '/')[4] + +resource resourceGroup 'Microsoft.Resources/resourceGroups@2023-07-01' = { + name: resourceGroupName + location: locationControlPlane + tags: union({'cm-resource-parent': '${subscription().id}}/resourceGroups/${resourceGroupName}/providers/Microsoft.DesktopVirtualization/hostpools/${hostPoolName}'}, tags[?'Microsoft.Resources/resourceGroups'] ?? {}, mlzTags) +} + +// Role Assignment for Autoscale +// Purpose: assigns the Desktop Virtualization Power On Off Contributor role to the +// Azure Virtual Desktop service to scale the host pool +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { + name: guid(avdObjectId, '40c5ff49-9181-41f8-ae61-143b0e78555e', subscription().id) + properties: { + roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', '40c5ff49-9181-41f8-ae61-143b0e78555e') + principalId: avdObjectId + } +} + +// Monitoring Resources for AVD Insights +// This module deploys a Log Analytics Workspace with a Data Collection Rule +module monitoring 'monitoring.bicep' = if (enableApplicationInsights || enableAvdInsights) { + name: 'deploy-monitoring-${deploymentNameSuffix}' + scope: resourceGroup + params: { + deploymentNameSuffix: deploymentNameSuffix + enableAvdInsights: enableAvdInsights + hostPoolResourceId: '${subscription().id}}/resourceGroups/${resourceGroup.name}/providers/Microsoft.DesktopVirtualization/hostpools/${hostPoolName}' + location: locationVirtualMachines + logAnalyticsWorkspaceRetention: logAnalyticsWorkspaceRetention + logAnalyticsWorkspaceSku: logAnalyticsWorkspaceSku + mlzTags: mlzTags + namingConvention: namingConvention + privateLinkScopeResourceId: privateLinkScopeResourceId + serviceToken: serviceToken + tags: tags + } +} + +module hostPool 'hostPool.bicep' = { + name: 'deploy-vdpool-${deploymentNameSuffix}' + scope: resourceGroup + params: { + activeDirectorySolution: activeDirectorySolution + avdPrivateDnsZoneResourceId: avdPrivateDnsZoneResourceId + customImageId: customImageId + customRdpProperty: customRdpProperty + diskSku: diskSku + domainName: domainName + enableAvdInsights: enableAvdInsights + galleryImageOffer: galleryImageOffer + galleryImagePublisher: galleryImagePublisher + galleryImageSku: galleryImageSku + galleryItemId: galleryItemId + hostPoolDiagnosticSettingName: namingConvention.hostPoolDiagnosticSetting + hostPoolName: hostPoolName + hostPoolNetworkInterfaceName: namingConvention.hostPoolNetworkInterface + hostPoolPrivateEndpointName: namingConvention.hostPoolPrivateEndpoint + hostPoolPublicNetworkAccess: hostPoolPublicNetworkAccess + hostPoolType: hostPoolType + imageType: imageType + location: locationControlPlane + logAnalyticsWorkspaceResourceId: monitoring.outputs.logAnalyticsWorkspaceResourceId + maxSessionLimit: maxSessionLimit + mlzTags: mlzTags + sessionHostNamePrefix: sessionHostNamePrefix + subnetResourceId: subnetResourceId + tags: tags + validationEnvironment: validationEnvironment + virtualMachineSize: virtualMachineSize + } +} module diskAccess 'diskAccess.bicep' = { - scope: resourceGroup(resourceGroupManagement) + scope: resourceGroup name: 'deploy-disk-access-${deploymentNameSuffix}' params: { - hostPoolName: hostPoolName + hostPoolResourceId: hostPool.outputs.resourceId location: locationVirtualMachines mlzTags: mlzTags namingConvention: namingConvention - resourceGroupControlPlane: resourceGroupControlPlane subnetResourceId: subnetResourceId tags: tags } } // Sets an Azure policy to disable public network access to managed disks -module policy 'policy.bicep' = { +module policy '../management/policy.bicep' = { name: 'deploy-policy-disks-${deploymentNameSuffix}' params: { diskAccessResourceId: diskAccess.outputs.resourceId - location: locationVirtualMachines - resourceGroupName: resourceGroupHosts + location: locationControlPlane + resourceGroupName: resourceGroup.name } } module deploymentUserAssignedIdentity 'userAssignedIdentity.bicep' = { - scope: resourceGroup(resourceGroupManagement) + scope: resourceGroup name: 'deploy-id-deployment-${deploymentNameSuffix}' params: { location: locationVirtualMachines name: replace(userAssignedIdentityNamePrefix, serviceToken, 'deployment') - tags: union( - { - 'cm-resource-parent': '${subscription().id}}/resourceGroups/${resourceGroupControlPlane}/providers/Microsoft.DesktopVirtualization/hostpools/${hostPoolName}' - }, - contains(tags, 'Microsoft.ManagedIdentity/userAssignedIdentities') - ? tags['Microsoft.ManagedIdentity/userAssignedIdentities'] - : {}, - mlzTags - ) + tags: union({'cm-resource-parent': hostPool.outputs.resourceId}, tags[?'Microsoft.ManagedIdentity/userAssignedIdentities'] ?? {}, mlzTags) } } - -// Role Assignments: Reader on the resource groups -//Purpose: domain join storage account(s) & set NTFS permissions on the file share(s) -module roleAssignments_deployment '../common/roleAssignments/resourceGroup.bicep' = [ - for i in range(0, length(resourceGroups)): { - scope: resourceGroup(resourceGroups[i]) - name: 'deploy-role-assignment-${i}-${deploymentNameSuffix}' - params: { - principalId: deploymentUserAssignedIdentity.outputs.principalId - principalType: 'ServicePrincipal' - roleDefinitionId: 'acdd72a7-3385-48ef-bd42-f606fba81ae7' // Reader - } - } -] - -// Role Assignment: Storage Account Contributor on the storage resource group -// Purpose: domain join storage account(s) & set NTFS permissions on the file share(s) -module roleAssignment_StorageAccountContributor '../common/roleAssignments/resourceGroup.bicep' = { - scope: resourceGroup(resourceGroupStorage) - name: 'deploy-role-assignment-${deploymentNameSuffix}' +// Role Assignment for the AVD host pool +// Purpose: assigns the Desktop Virtualization Contributor role to the managed identity on the +// management virtual machine to set the drain mode on the AVD session hosts and manage the scaling plan +module roleAssignment_Management '../common/roleAssignments/resourceGroup.bicep' = { + name: 'assign-role-mgmt-${deploymentNameSuffix}' + scope: resourceGroup params: { principalId: deploymentUserAssignedIdentity.outputs.principalId principalType: 'ServicePrincipal' - roleDefinitionId: '17d1049b-9a84-46fb-8f53-869881c3d3ab' // Storage Account Contributor + roleDefinitionId: '082f0a83-3be5-4ba1-904c-961cca79b387' } } -// Management VM -// The management VM is required to execute PowerShell scripts. +// Management Virtual Machine +// Purpose: deploys the management VM is required to execute PowerShell scripts. module virtualMachine 'virtualMachine.bicep' = { name: 'deploy-mgmt-vm-${deploymentNameSuffix}' - scope: resourceGroup(resourceGroupManagement) + scope: resourceGroup params: { deploymentUserAssignedIdentityPrincipalId: deploymentUserAssignedIdentity.outputs.principalId deploymentUserAssignedIdentityResourceId: deploymentUserAssignedIdentity.outputs.resourceId @@ -139,106 +193,96 @@ module virtualMachine 'virtualMachine.bicep' = { domainJoinPassword: domainJoinPassword domainJoinUserPrincipalName: domainJoinUserPrincipalName domainName: domainName - hostPoolName: hostPoolName + hostPoolResourceId: hostPool.outputs.resourceId location: locationVirtualMachines mlzTags: mlzTags networkInterfaceName: replace(namingConvention.virtualMachineNetworkInterface, serviceToken, 'mgt') organizationalUnitPath: organizationalUnitPath - resourceGroupControlPlane: resourceGroupControlPlane - subnet: split(subnetResourceId, '/')[10] + subnetResourceId: subnetResourceId tags: tags virtualMachineName: replace(namingConvention.virtualMachine, serviceToken, 'mgt') virtualMachinePassword: virtualMachinePassword virtualMachineUsername: virtualMachineUsername - virtualNetwork: virtualNetworkName - virtualNetworkResourceGroup: virtualNetworkResourceGroupName } } -// Role Assignment required for Scaling Plan -resource roleAssignment 'Microsoft.Authorization/roleAssignments@2022-04-01' = { - name: guid(avdObjectId, roleDefinitions.DesktopVirtualizationPowerOnOffContributor, subscription().id) - properties: { - roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions',roleDefinitions.DesktopVirtualizationPowerOnOffContributor) - principalId: avdObjectId +module applicationGroup 'applicationGroup.bicep' = { + name: 'deploy-vdag-${deploymentNameSuffix}' + scope: resourceGroup + params: { + deploymentNameSuffix: deploymentNameSuffix + deploymentUserAssignedIdentityClientId: deploymentUserAssignedIdentity.outputs.clientId + desktopApplicationGroupName: namingConvention.applicationGroup + hostPoolResourceId: hostPool.outputs.resourceId + locationControlPlane: locationControlPlane + locationVirtualMachines: locationVirtualMachines + mlzTags: mlzTags + securityPrincipalObjectIds: securityPrincipalObjectIds + desktopFriendlyName: desktopFriendlyName + tags: tags + virtualMachineName: virtualMachine.outputs.name } } -// Monitoring Resources for AVD Insights -// This module deploys a Log Analytics Workspace with a Data Collection Rule -module monitoring 'monitoring.bicep' = if (enableApplicationInsights || enableAvdInsights) { - name: 'deploy-monitoring-${deploymentNameSuffix}' - scope: resourceGroup(resourceGroupManagement) +module recoveryServicesVault 'recoveryServicesVault.bicep' = if (recoveryServices) { + name: 'deploy-rsv-${deploymentNameSuffix}' + scope: resourceGroup params: { - deploymentNameSuffix: deploymentNameSuffix - enableAvdInsights: enableAvdInsights - hostPoolName: hostPoolName + azureBlobsPrivateDnsZoneResourceId: '${privateDnsZoneResourceIdPrefix}${filter(privateDnsZones, name => contains(name, 'blob'))[0]}' + azureQueueStoragePrivateDnsZoneResourceId: '${privateDnsZoneResourceIdPrefix}${filter(privateDnsZones, name => contains(name, 'queue'))[0]}' + deployFslogix: deployFslogix + hostPoolResourceId: hostPool.outputs.resourceId location: locationVirtualMachines - logAnalyticsWorkspaceRetention: logAnalyticsWorkspaceRetention - logAnalyticsWorkspaceSku: logAnalyticsWorkspaceSku mlzTags: mlzTags - namingConvention: namingConvention - privateLinkScopeResourceId: privateLinkScopeResourceId - resourceGroupControlPlane: resourceGroupControlPlane - serviceToken: serviceToken + recoveryServicesPrivateDnsZoneResourceId: '${privateDnsZoneResourceIdPrefix}${filter(privateDnsZones, name => startsWith(name, 'privatelink.${recoveryServicesGeo}.backup.windowsazure'))[0]}' + recoveryServicesVaultName: namingConvention.recoveryServicesVault + recoveryServicesVaultNetworkInterfaceName: namingConvention.recoveryServicesVaultNetworkInterface + recoveryServicesVaultPrivateEndpointName: namingConvention.recoveryServicesVaultPrivateEndpoint + storageService: storageService + subnetId: subnetResourceId tags: tags + timeZone: timeZone } } // Deploys the Auto Increase Premium File Share Quota solution on an Azure Function App -module functionApp 'functionApp.bicep' = if (deployFslogix && fslogixStorageService == 'AzureFiles Premium') { +module functionApp '../management/functionApp.bicep' = if (fslogixStorageService == 'AzureFiles Premium') { name: 'deploy-function-app-${deploymentNameSuffix}' - scope: resourceGroup(resourceGroupManagement) + scope: resourceGroup params: { delegatedSubnetResourceId: filter(subnets, subnet => contains(subnet.name, 'FunctionAppOutbound'))[0].id deploymentNameSuffix: deploymentNameSuffix enableApplicationInsights: enableApplicationInsights environmentAbbreviation: environmentAbbreviation - hostPoolName: hostPoolName + hostPoolResourceId: hostPool.outputs.resourceId logAnalyticsWorkspaceResourceId: monitoring.outputs.logAnalyticsWorkspaceResourceId + mlzTags: mlzTags namingConvention: namingConvention privateDnsZoneResourceIdPrefix: privateDnsZoneResourceIdPrefix privateDnsZones: privateDnsZones privateLinkScopeResourceId: privateLinkScopeResourceId resourceAbbreviations: resourceAbbreviations - resourceGroupControlPlane: resourceGroupControlPlane - resourceGroupStorage: resourceGroupStorage + resourceGroupProfiles: resourceGroupProfiles serviceToken: serviceToken subnetResourceId: subnetResourceId tags: tags - timeDifference: timeDifference - } -} - -module recoveryServicesVault 'recoveryServicesVault.bicep' = if (recoveryServices) { - name: 'deploy-rsv-${deploymentNameSuffix}' - scope: resourceGroup(resourceGroupManagement) - params: { - azureBlobsPrivateDnsZoneResourceId: '${privateDnsZoneResourceIdPrefix}${filter(privateDnsZones, name => contains(name, 'blob'))[0]}' - azureQueueStoragePrivateDnsZoneResourceId: '${privateDnsZoneResourceIdPrefix}${filter(privateDnsZones, name => contains(name, 'queue'))[0]}' - deployFslogix: deployFslogix - hostPoolName: hostPoolName - location: locationVirtualMachines - mlzTags: mlzTags - recoveryServicesPrivateDnsZoneResourceId: '${privateDnsZoneResourceIdPrefix}${filter(privateDnsZones, name => startsWith(name, 'privatelink.${recoveryServicesGeo}.backup.windowsazure'))[0]}' - recoveryServicesVaultName: namingConvention.recoveryServicesVault - recoveryServicesVaultNetworkInterfaceName: namingConvention.recoveryServicesVaultNetworkInterface - recoveryServicesVaultPrivateEndpointName: namingConvention.recoveryServicesVaultPrivateEndpoint - resourceGroupControlPlane: resourceGroupControlPlane - storageService: storageService - subnetId: subnetResourceId - tags: tags - timeZone: timeZone } } +output applicationGroupResourceId string = applicationGroup.outputs.resourceId output dataCollectionRuleResourceId string = enableAvdInsights ? monitoring.outputs.dataCollectionRuleResourceId : '' output deploymentUserAssignedIdentityClientId string = deploymentUserAssignedIdentity.outputs.clientId output deploymentUserAssignedIdentityPrincipalId string = deploymentUserAssignedIdentity.outputs.principalId output deploymentUserAssignedIdentityResourceId string = deploymentUserAssignedIdentity.outputs.resourceId -output functionAppName string = fslogixStorageService == 'AzureFiles Premium' ? functionApp.outputs.functionAppName : '' +output diskAccessPolicyDefinitionId string = policy.outputs.policyDefinitionId +output diskAccessPolicyDisplayName string = policy.outputs.policyDisplayName +output diskAccessResourceId string = diskAccess.outputs.resourceId +output functionAppPrincipalId string = fslogixStorageService == 'AzureFiles Premium' ? functionApp.outputs.functionAppPrincipalId : '' +output hostPoolName string = hostPool.outputs.name +output hostPoolResourceId string = hostPool.outputs.resourceId output logAnalyticsWorkspaceName string = enableApplicationInsights || enableAvdInsights ? monitoring.outputs.logAnalyticsWorkspaceName : '' output logAnalyticsWorkspaceResourceId string = enableApplicationInsights || enableAvdInsights ? monitoring.outputs.logAnalyticsWorkspaceResourceId : '' output recoveryServicesVaultName string = recoveryServices ? recoveryServicesVault.outputs.name : '' +output resourceGroupName string = resourceGroup.name output virtualMachineName string = virtualMachine.outputs.name output virtualMachineResourceId string = virtualMachine.outputs.resourceId diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/management/monitoring.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/management/monitoring.bicep index 61c77a780..8490569b7 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/modules/management/monitoring.bicep +++ b/src/bicep/add-ons/azure-virtual-desktop/modules/management/monitoring.bicep @@ -1,13 +1,12 @@ param deploymentNameSuffix string param enableAvdInsights bool -param hostPoolName string +param hostPoolResourceId string param location string param logAnalyticsWorkspaceRetention int param logAnalyticsWorkspaceSku string param mlzTags object param namingConvention object param privateLinkScopeResourceId string -param resourceGroupControlPlane string param service string = 'mgmt' param serviceToken string param tags object @@ -15,9 +14,7 @@ param tags object resource logAnalyticsWorkspace 'Microsoft.OperationalInsights/workspaces@2021-06-01' = { name: replace(namingConvention.logAnalyticsWorkspace, serviceToken, service) location: location - tags: union({ - 'cm-resource-parent': '${subscription().id}}/resourceGroups/${resourceGroupControlPlane}/providers/Microsoft.DesktopVirtualization/hostpools/${hostPoolName}' - }, contains(tags, 'Microsoft.OperationalInsights/workspaces') ? tags['Microsoft.OperationalInsights/workspaces'] : {}, mlzTags) + tags: union({'cm-resource-parent': hostPoolResourceId}, tags[?'Microsoft.OperationalInsights/workspaces'] ?? {}, mlzTags) properties: { sku: { name: logAnalyticsWorkspaceSku @@ -43,9 +40,7 @@ module privateLinkScope_logAnalyticsWorkspace 'privateLinkScope.bicep' = { resource dataCollectionRule 'Microsoft.Insights/dataCollectionRules@2022-06-01' = if (enableAvdInsights) { name: 'microsoft-avdi-${replace(namingConvention.dataCollectionRule, serviceToken, service)}' // The name must start with 'microsoft-avdi-' for proper integration with AVD Insights location: location - tags: union({ - 'cm-resource-parent': '${subscription().id}}/resourceGroups/${resourceGroupControlPlane}/providers/Microsoft.DesktopVirtualization/hostpools/${hostPoolName}' - }, contains(tags, 'Microsoft.Insights/dataCollectionRules') ? tags['Microsoft.Insights/dataCollectionRules'] : {}, mlzTags) + tags: union({'cm-resource-parent': hostPoolResourceId}, tags[?'Microsoft.Insights/dataCollectionRules'] ?? {}, mlzTags) kind: 'Windows' properties: { dataSources: { @@ -131,9 +126,7 @@ resource dataCollectionRule 'Microsoft.Insights/dataCollectionRules@2022-06-01' resource dataCollectionEndpoint 'Microsoft.Insights/dataCollectionEndpoints@2021-04-01' = if (enableAvdInsights) { name: replace(namingConvention.dataCollectionEndpoint, serviceToken, service) location: location - tags: union({ - 'cm-resource-parent': '${subscription().id}}/resourceGroups/${resourceGroupControlPlane}/providers/Microsoft.DesktopVirtualization/hostpools/${hostPoolName}' - }, contains(tags, 'Microsoft.Insights/dataCollectionEndpoints') ? tags['Microsoft.Insights/dataCollectionEndpoints'] : {}, mlzTags) + tags: union({'cm-resource-parent': hostPoolResourceId}, tags[?'Microsoft.Insights/dataCollectionEndpoints'] ?? {}, mlzTags) kind: 'Windows' properties: { networkAcls: { @@ -154,4 +147,3 @@ module privateLinkScope_dataCollectionEndpoint 'privateLinkScope.bicep' = if (en output logAnalyticsWorkspaceName string = logAnalyticsWorkspace.name output logAnalyticsWorkspaceResourceId string = logAnalyticsWorkspace.id output dataCollectionRuleResourceId string = enableAvdInsights ? dataCollectionRule.id : '' - diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/management/policy.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/management/policy.bicep index 6d0268c61..fc565c4a9 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/modules/management/policy.bicep +++ b/src/bicep/add-ons/azure-virtual-desktop/modules/management/policy.bicep @@ -83,3 +83,6 @@ module policyAssignment 'policyAssignment.bicep' = { policyName: policyDefinition.properties.displayName } } + +output policyDefinitionId string = policyDefinition.id +output policyDisplayName string = policyDefinition.properties.displayName diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/management/recoveryServicesVault.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/management/recoveryServicesVault.bicep index 07545a3fd..5e3d85ec8 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/modules/management/recoveryServicesVault.bicep +++ b/src/bicep/add-ons/azure-virtual-desktop/modules/management/recoveryServicesVault.bicep @@ -1,14 +1,13 @@ param azureBlobsPrivateDnsZoneResourceId string param azureQueueStoragePrivateDnsZoneResourceId string param deployFslogix bool -param hostPoolName string +param hostPoolResourceId string param location string param mlzTags object param recoveryServicesPrivateDnsZoneResourceId string param recoveryServicesVaultName string param recoveryServicesVaultNetworkInterfaceName string param recoveryServicesVaultPrivateEndpointName string -param resourceGroupControlPlane string param storageService string param subnetId string param tags object @@ -17,9 +16,7 @@ param timeZone string resource vault 'Microsoft.RecoveryServices/vaults@2022-03-01' = { name: recoveryServicesVaultName location: location - tags: union({ - 'cm-resource-parent': '${subscription().id}}/resourceGroups/${resourceGroupControlPlane}/providers/Microsoft.DesktopVirtualization/hostpools/${hostPoolName}' - }, contains(tags, 'Microsoft.RecoveryServices/vaults') ? tags['Microsoft.RecoveryServices/vaults'] : {}, mlzTags) + tags: union({'cm-resource-parent': hostPoolResourceId}, tags[?'Microsoft.RecoveryServices/vaults'] ?? {}, mlzTags) sku: { name: 'RS0' tier: 'Standard' @@ -31,9 +28,7 @@ resource backupPolicy_Storage 'Microsoft.RecoveryServices/vaults/backupPolicies@ parent: vault name: 'AvdPolicyStorage' location: location - tags: union({ - 'cm-resource-parent': '${subscription().id}}/resourceGroups/${resourceGroupControlPlane}/providers/Microsoft.DesktopVirtualization/hostpools/${hostPoolName}' - }, contains(tags, 'Microsoft.RecoveryServices/vaults') ? tags['Microsoft.RecoveryServices/vaults'] : {}, mlzTags) + tags: union({'cm-resource-parent': hostPoolResourceId}, tags[?'Microsoft.RecoveryServices/vaults'] ?? {}, mlzTags) properties: { backupManagementType: 'AzureStorage' schedulePolicy: { @@ -64,9 +59,7 @@ resource backupPolicy_Vm 'Microsoft.RecoveryServices/vaults/backupPolicies@2022- parent: vault name: 'AvdPolicyVm' location: location - tags: union({ - 'cm-resource-parent': '${subscription().id}}/resourceGroups/${resourceGroupControlPlane}/providers/Microsoft.DesktopVirtualization/hostpools/${hostPoolName}' - }, contains(tags, 'Microsoft.RecoveryServices/vaults') ? tags['Microsoft.RecoveryServices/vaults'] : {}, mlzTags) + tags: union({'cm-resource-parent': hostPoolResourceId}, tags[?'Microsoft.RecoveryServices/vaults'] ?? {}, mlzTags) properties: { backupManagementType: 'AzureIaasVM' instantRpRetentionRangeInDays: 2 @@ -99,9 +92,7 @@ resource backupPolicy_Vm 'Microsoft.RecoveryServices/vaults/backupPolicies@2022- resource privateEndpoint 'Microsoft.Network/privateEndpoints@2023-04-01' = { name: recoveryServicesVaultPrivateEndpointName location: location - tags: union({ - 'cm-resource-parent': '${subscription().id}}/resourceGroups/${resourceGroupControlPlane}/providers/Microsoft.DesktopVirtualization/hostpools/${hostPoolName}' - }, contains(tags, 'Microsoft.Network/privateEndpoints') ? tags['Microsoft.Network/privateEndpoints'] : {}, mlzTags) + tags: union({'cm-resource-parent': hostPoolResourceId}, tags[?'Microsoft.Network/privateEndpoints'] ?? {}, mlzTags) properties: { customNetworkInterfaceName: recoveryServicesVaultNetworkInterfaceName privateLinkServiceConnections: [ diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/management/scalingPlan.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/management/scalingPlan.bicep index 44b851d2f..b9f38bdbd 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/modules/management/scalingPlan.bicep +++ b/src/bicep/add-ons/azure-virtual-desktop/modules/management/scalingPlan.bicep @@ -232,13 +232,13 @@ resource scalingPlan 'Microsoft.DesktopVirtualization/scalingPlans@2023-09-05' = } } -resource schedules_Pooled 'Microsoft.DesktopVirtualization/scalingPlans/pooledSchedules@2024-04-03' = [for i in range(0, length(schedules)): if (hostPoolType == 'Pooled') { +resource schedules_Pooled 'Microsoft.DesktopVirtualization/scalingPlans/pooledSchedules@2023-09-05' = [for i in range(0, length(schedules)): if (hostPoolType == 'Pooled') { name: i == 0 ? 'Weekdays' : 'Weekends' parent: scalingPlan properties: schedules[i] }] -resource schedule_Personal 'Microsoft.DesktopVirtualization/scalingPlans/personalSchedules@2024-04-03' = [for i in range(0, length(schedules)): if (hostPoolType == 'Personal') { +resource schedule_Personal 'Microsoft.DesktopVirtualization/scalingPlans/personalSchedules@2023-09-05' = [for i in range(0, length(schedules)): if (hostPoolType == 'Personal') { name: i == 0 ? 'Weekdays' : 'Weekends' parent: scalingPlan properties: schedules[i] diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/management/virtualMachine.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/management/virtualMachine.bicep index ff432a965..3f6c64318 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/modules/management/virtualMachine.bicep +++ b/src/bicep/add-ons/azure-virtual-desktop/modules/management/virtualMachine.bicep @@ -7,32 +7,27 @@ param diskSku string param domainJoinPassword string param domainJoinUserPrincipalName string param domainName string -param hostPoolName string +param hostPoolResourceId string param location string param mlzTags object param networkInterfaceName string param organizationalUnitPath string -param resourceGroupControlPlane string -param subnet string +param subnetResourceId string param tags object param timestamp string = utcNow('yyyyMMddhhmmss') -param virtualNetwork string -param virtualNetworkResourceGroup string param virtualMachineName string @secure() param virtualMachinePassword string param virtualMachineUsername string -var tagsVirtualMachines = union({ - 'cm-resource-parent': '${subscription().id}}/resourceGroups/${resourceGroupControlPlane}/providers/Microsoft.DesktopVirtualization/hostpools/${hostPoolName}' -}, contains(tags, 'Microsoft.Compute/virtualMachines') ? tags['Microsoft.Compute/virtualMachines'] : {}, mlzTags) +var tagsVirtualMachines = union({'cm-resource-parent': hostPoolResourceId}, tags[?'Microsoft.Compute/virtualMachines'] ?? {}, mlzTags) resource networkInterface 'Microsoft.Network/networkInterfaces@2020-05-01' = { name: networkInterfaceName location: location tags: union({ - 'cm-resource-parent': '${subscription().id}}/resourceGroups/${resourceGroupControlPlane}/providers/Microsoft.DesktopVirtualization/hostpools/${hostPoolName}' - }, contains(tags, 'Microsoft.Network/networkInterfaces') ? tags['Microsoft.Network/networkInterfaces'] : {}, mlzTags) + 'cm-resource-parent': hostPoolResourceId + }, tags[?'Microsoft.Network/networkInterfaces'] ?? {}, mlzTags) properties: { ipConfigurations: [ { @@ -40,7 +35,7 @@ resource networkInterface 'Microsoft.Network/networkInterfaces@2020-05-01' = { properties: { privateIPAllocationMethod: 'Dynamic' subnet: { - id: resourceId(virtualNetworkResourceGroup, 'Microsoft.Network/virtualNetworks/subnets', virtualNetwork, subnet) + id: subnetResourceId } primary: true privateIPAddressVersion: 'IPv4' diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/resourceGroup.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/resourceGroup.bicep deleted file mode 100644 index e689bc4f0..000000000 --- a/src/bicep/add-ons/azure-virtual-desktop/modules/resourceGroup.bicep +++ /dev/null @@ -1,11 +0,0 @@ -targetScope = 'subscription' - -param location string -param resourceGroupName string -param tags object - -resource resourceGroup 'Microsoft.Resources/resourceGroups@2020-10-01' = { - name: resourceGroupName - location: location - tags: contains(tags, 'Microsoft.Resources/resourceGroups') ? tags['Microsoft.Resources/resourceGroups'] : {} -} diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/sessionHosts/sessionHosts.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/sessionHosts/sessionHosts.bicep index 05662d833..1161fe40b 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/modules/sessionHosts/sessionHosts.bicep +++ b/src/bicep/add-ons/azure-virtual-desktop/modules/sessionHosts/sessionHosts.bicep @@ -11,6 +11,9 @@ param deployFslogix bool param deploymentNameSuffix string param deploymentUserAssignedIdentityClientId string param deploymentUserAssignedIdentityPrincipalId string +param diskAccessPolicyDefinitionId string +param diskAccessPolicyDisplayName string +param diskAccessResourceId string param diskEncryptionSetResourceId string param diskSku string param divisionRemainderValue int @@ -21,6 +24,7 @@ param domainName string param drainMode bool param enableAcceleratedNetworking bool param enableAvdInsights bool +param enableRecoveryServices bool param environmentAbbreviation string param fslogixContainerType string param hostPoolName string @@ -39,12 +43,10 @@ param mlzTags object param namingConvention object param netAppFileShares array param organizationalUnitPath string -param enableRecoveryServices bool +param profile string param recoveryServicesVaultName string -param resourceGroupControlPlane string -param resourceGroupHosts string param resourceGroupManagement string -param roleDefinitions object +param resourceGroupName string param scalingWeekdaysOffPeakStartTime string param scalingWeekdaysPeakStartTime string param scalingWeekendsOffPeakStartTime string @@ -67,34 +69,51 @@ param virtualMachineSize string param virtualMachineUsername string var availabilitySetNamePrefix = namingConvention.availabilitySet -var tagsAvailabilitySets = union({'cm-resource-parent': '${subscription().id}/resourceGroups/${resourceGroupManagement}/providers/Microsoft.DesktopVirtualization/hostpools/${hostPoolName}'}, contains(tags, 'Microsoft.Compute/availabilitySets') ? tags['Microsoft.Compute/availabilitySets'] : {}, mlzTags) -var tagsNetworkInterfaces = union({'cm-resource-parent': '${subscription().id}/resourceGroups/${resourceGroupManagement}/providers/Microsoft.DesktopVirtualization/hostpools/${hostPoolName}'}, contains(tags, 'Microsoft.Network/networkInterfaces') ? tags['Microsoft.Network/networkInterfaces'] : {}, mlzTags) -var tagsRecoveryServicesVault = union({'cm-resource-parent': '${subscription().id}/resourceGroups/${resourceGroupManagement}/providers/Microsoft.DesktopVirtualization/hostpools/${hostPoolName}'}, contains(tags, 'Microsoft.recoveryServices/vaults') ? tags['Microsoft.recoveryServices/vaults'] : {}, mlzTags) -var tagsVirtualMachines = union({'cm-resource-parent': '${subscription().id}/resourceGroups/${resourceGroupManagement}/providers/Microsoft.DesktopVirtualization/hostpools/${hostPoolName}'}, contains(tags, 'Microsoft.Compute/virtualMachines') ? tags['Microsoft.Compute/virtualMachines'] : {}, mlzTags) +var tagsVirtualMachines = union({'cm-resource-parent': hostPoolResourceId}, tags[?'Microsoft.Compute/virtualMachines'] ?? {}, mlzTags) var uniqueToken = uniqueString(identifier, environmentAbbreviation, subscription().subscriptionId) var virtualMachineNamePrefix = replace(namingConvention.virtualMachine, serviceToken, '') +resource rg 'Microsoft.Resources/resourceGroups@2023-07-01' = { + name: resourceGroupName + location: location + tags: union({'cm-resource-parent': hostPoolResourceId}, tags[?'Microsoft.Resources/resourceGroups'] ?? {}, mlzTags) +} + +// Sets an Azure policy to disable public network access to managed disks +module policyAssignment '../management/policyAssignment.bicep' = { + name: 'assign-policy-diskAccess-${deploymentNameSuffix}' + scope: rg + params: { + diskAccessResourceId: diskAccessResourceId + location: location + policyDefinitionId: diskAccessPolicyDefinitionId + policyDisplayName: diskAccessPolicyDisplayName + policyName: diskAccessPolicyDisplayName + } +} + module availabilitySets 'availabilitySets.bicep' = if (hostPoolType == 'Pooled' && availability == 'AvailabilitySets') { - name: 'deploy-avail-${deploymentNameSuffix}' - scope: resourceGroup(resourceGroupHosts) + name: 'deploy-avSets-${deploymentNameSuffix}' + scope: rg params: { availabilitySetNamePrefix: availabilitySetNamePrefix availabilitySetsCount: availabilitySetsCount availabilitySetsIndex: availabilitySetsIndex location: location - tagsAvailabilitySets: tagsAvailabilitySets + tagsAvailabilitySets: union({'cm-resource-parent': hostPoolResourceId}, tags[?'Microsoft.Compute/availabilitySets'] ?? {}, mlzTags) } } -// Role Assignment for Virtual Machine Login User -// This module deploys the role assignments to login to Azure AD joined session hosts -module roleAssignments '../common/roleAssignments/resourceGroup.bicep' = [for i in range(0, length(securityPrincipalObjectIds)): if (!contains(activeDirectorySolution, 'DomainServices')) { - name: 'deploy-role-assignments-${i}-${deploymentNameSuffix}' - scope: resourceGroup(resourceGroupHosts) +// Role Assignment for Entra Joined Virtual Machines +// Purpose: assigns the Virtual Machine Login User role on the hosts resource group +// to enable the login to Entra joined virtual machines +module roleAssignments '../common/roleAssignments/resourceGroup.bicep' = [for i in range(0, length(securityPrincipalObjectIds)): if (contains(activeDirectorySolution, 'EntraId')) { + name: 'assign-role-${i}-${deploymentNameSuffix}' + scope: rg params: { principalId: securityPrincipalObjectIds[i] principalType: 'Group' - roleDefinitionId: roleDefinitions.VirtualMachineUserLogin + roleDefinitionId: 'fb879df8-f326-4884-b1cf-06f3ad86be52' } }] @@ -110,7 +129,7 @@ resource image 'Microsoft.Compute/galleries/images@2023-07-03' existing = if (em // Disable Autoscale if adding new session hosts to an existing host pool module disableAutoscale '../common/runCommand.bicep' = { - name: 'deploy-disable-autoscale-${deploymentNameSuffix}' + name: 'deploy-disableAutoscale-${deploymentNameSuffix}' scope: resourceGroup(resourceGroupManagement) params: { location: location @@ -147,10 +166,52 @@ module disableAutoscale '../common/runCommand.bicep' = { } } +// Set MarketPlace Terms for ESRI's ArcGIS Pro image +module setMarketplaceTerms '../common/runCommand.bicep' = if (profile == 'ArcGISPro') { + name: 'set-marketplaceTerms-${deploymentNameSuffix}' + scope: resourceGroup(resourceGroupManagement) + params: { + location: location + name: 'Set-AzureMarketplaceTerms' + parameters: [ + { + name: 'ImageOffer' + value: imageOffer + } + { + name: 'ImagePublisher' + value: imagePublisher + } + { + name: 'ImageSku' + value: imageSku + } + { + name: 'ResourceManagerUri' + value: environment().resourceManager + } + { + name: 'SubscriptionId' + value: subscription().subscriptionId + } + { + name: 'UserAssignedidentityClientId' + value: deploymentUserAssignedIdentityClientId + } + ] + script: loadTextContent('../../artifacts/Set-AzureMarketplaceTerms.ps1') + tags: tagsVirtualMachines + virtualMachineName: managementVirtualMachineName + } + dependsOn: [ + disableAutoscale + ] +} + @batchSize(1) module virtualMachines 'virtualMachines.bicep' = [for i in range(1, sessionHostBatchCount): { name: 'deploy-vms-${i - 1}-${deploymentNameSuffix}' - scope: resourceGroup(resourceGroupHosts) + scope: rg params: { activeDirectorySolution: activeDirectorySolution availability: availability @@ -183,7 +244,7 @@ module virtualMachines 'virtualMachines.bicep' = [for i in range(1, sessionHostB netAppFileShares: netAppFileShares networkInterfaceNamePrefix: namingConvention.virtualMachineNetworkInterface organizationalUnitPath: organizationalUnitPath - resourceGroupControlPlane: resourceGroupControlPlane + profile: profile resourceGroupManagement: resourceGroupManagement serviceToken: serviceToken sessionHostCount: i == sessionHostBatchCount && divisionRemainderValue > 0 ? divisionRemainderValue : maxResourcesPerTemplateDeployment @@ -194,7 +255,7 @@ module virtualMachines 'virtualMachines.bicep' = [for i in range(1, sessionHostB storageService: storageService storageSuffix: storageSuffix subnetResourceId: subnetResourceId - tagsNetworkInterfaces: tagsNetworkInterfaces + tagsNetworkInterfaces: union({'cm-resource-parent': hostPoolResourceId}, tags[?'Microsoft.Network/networkInterfaces'] ?? {}, mlzTags) tagsVirtualMachines: tagsVirtualMachines uniqueToken: uniqueToken virtualMachineNamePrefix: virtualMachineNamePrefix @@ -205,11 +266,12 @@ module virtualMachines 'virtualMachines.bicep' = [for i in range(1, sessionHostB dependsOn: [ availabilitySets disableAutoscale + setMarketplaceTerms ] }] module recoveryServices 'recoveryServices.bicep' = if (enableRecoveryServices && hostPoolType == 'Personal') { - name: 'deploy-recovery-services-${deploymentNameSuffix}' + name: 'deploy-recoveryServices-${deploymentNameSuffix}' scope: resourceGroup(resourceGroupManagement) params: { deployFslogix: deployFslogix @@ -218,11 +280,11 @@ module recoveryServices 'recoveryServices.bicep' = if (enableRecoveryServices && location: location maxResourcesPerTemplateDeployment: maxResourcesPerTemplateDeployment recoveryServicesVaultName: recoveryServicesVaultName - resourceGroupHosts: resourceGroupHosts + resourceGroupHosts: rg.name resourceGroupManagement: resourceGroupManagement sessionHostBatchCount: sessionHostBatchCount sessionHostIndex: sessionHostIndex - tagsRecoveryServicesVault: tagsRecoveryServicesVault + tagsRecoveryServicesVault: union({'cm-resource-parent': hostPoolResourceId}, tags[?'Microsoft.recoveryServices/vaults'] ?? {}, mlzTags) virtualMachineNamePrefix: virtualMachineNamePrefix } dependsOn: [ @@ -231,7 +293,7 @@ module recoveryServices 'recoveryServices.bicep' = if (enableRecoveryServices && } module scalingPlan '../management/scalingPlan.bicep' = { - name: 'deploy-scaling-plan-${deploymentNameSuffix}' + name: 'deploy-scalingPlan-${deploymentNameSuffix}' scope: resourceGroup(resourceGroupManagement) params: { deploymentUserAssignedIdentityPrincipalId: deploymentUserAssignedIdentityPrincipalId @@ -242,7 +304,7 @@ module scalingPlan '../management/scalingPlan.bicep' = { logAnalyticsWorkspaceResourceId: logAnalyticsWorkspaceResourceId scalingPlanDiagnosticSettingName: namingConvention.scalingPlanDiagnosticSetting scalingPlanName: namingConvention.scalingPlan - tags: tags + tags: union({'cm-resource-parent': hostPoolResourceId}, tags[?'Microsoft.DesktopVirtualization/scalingPlans'] ?? {}, mlzTags) timeZone: timeZone weekdaysOffPeakStartTime: scalingWeekdaysOffPeakStartTime weekdaysPeakStartTime: scalingWeekdaysPeakStartTime diff --git a/src/bicep/add-ons/azure-virtual-desktop/modules/sessionHosts/virtualMachines.bicep b/src/bicep/add-ons/azure-virtual-desktop/modules/sessionHosts/virtualMachines.bicep index 6cba20cb0..5584da0a7 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/modules/sessionHosts/virtualMachines.bicep +++ b/src/bicep/add-ons/azure-virtual-desktop/modules/sessionHosts/virtualMachines.bicep @@ -30,7 +30,7 @@ param managementVirtualMachineName string param netAppFileShares array param networkInterfaceNamePrefix string param organizationalUnitPath string -param resourceGroupControlPlane string +param profile string param resourceGroupManagement string param serviceToken string param sessionHostCount int @@ -97,7 +97,7 @@ var storageAccountToken = '${storageAccountPrefix}??' // The token is used for A resource hostPool 'Microsoft.DesktopVirtualization/hostPools@2023-09-05' existing = { name: hostPoolName - scope: resourceGroup(subscription().subscriptionId, resourceGroupControlPlane) + scope: resourceGroup(subscription().subscriptionId, resourceGroupManagement) } resource networkInterface 'Microsoft.Network/networkInterfaces@2020-05-01' = [for i in range(0, sessionHostCount): { @@ -130,6 +130,11 @@ resource virtualMachine 'Microsoft.Compute/virtualMachines@2021-03-01' = [for i identity: { type: 'SystemAssigned' // Required for Entra join } + plan: profile == 'ArcGISPro' ? { + name: imageSku + publisher: imagePublisher + product: imageOffer + } : null zones: availability == 'AvailabilityZones' ? [ availabilityZones[i % length(availabilityZones)] ] : null @@ -397,7 +402,7 @@ module drainMode '../common/runCommand.bicep' = if (enableDrainMode) { } { name: 'HostPoolResourceGroupName' - value: resourceGroupControlPlane + value: resourceGroupManagement } { name: 'ResourceManagerUri' diff --git a/src/bicep/add-ons/azure-virtual-desktop/solution.bicep b/src/bicep/add-ons/azure-virtual-desktop/solution.bicep index 8ec64a176..d01785a2d 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/solution.bicep +++ b/src/bicep/add-ons/azure-virtual-desktop/solution.bicep @@ -220,6 +220,15 @@ param scalingWeekendsPeakStartTime string = '09:00' @description('The array of Security Principals with their object IDs and display names to assign to the AVD Application Group and FSLogix Storage.') param securityPrincipals array +/* Example of a security principal +[ + { + displayName: 'AVD' + objectId: '00000000-0000-0000-0000-000000000000' + } +] +*/ + @maxValue(5000) @minValue(0) @description('The number of session hosts to deploy in the host pool. Ensure you have the approved quota to deploy the desired count.') @@ -333,24 +342,6 @@ var fileShareNames = { var fileShares = fileShareNames[fslogixContainerType] var netbios = split(domainName, '.')[0] var privateDnsZoneResourceIdPrefix = '/subscriptions/${split(hubVirtualNetworkResourceId, '/')[2]}/resourceGroups/${split(hubVirtualNetworkResourceId, '/')[4]}/providers/Microsoft.Network/privateDnsZones/' -var resourceGroupServices = union( - [ - 'controlPlane' - 'hosts' - 'management' - ], - deployFslogix - ? [ - 'storage' - ] - : [] -) -var roleDefinitions = { - DesktopVirtualizationPowerOnOffContributor: '40c5ff49-9181-41f8-ae61-143b0e78555e' - DesktopVirtualizationUser: '1d18fff3-a72a-46b5-b4a9-0b38a3cd7e63' - Reader: 'acdd72a7-3385-48ef-bd42-f606fba81ae7' - VirtualMachineUserLogin: 'fb879df8-f326-4884-b1cf-06f3ad86be52' -} var storageSku = fslogixStorageService == 'None' ? 'None' : split(fslogixStorageService, ' ')[1] var storageService = split(fslogixStorageService, ' ')[0] var storageSuffix = environment().suffixes.storage @@ -385,19 +376,19 @@ var subnets = { : [] } -// Gets the hub virtual network for its location and tags +// Gets the MLZ hub virtual network for its location and tags resource virtualNetwork 'Microsoft.Network/virtualNetworks@2023-11-01' existing = { name: split(hubVirtualNetworkResourceId, '/')[8] scope: resourceGroup(split(hubVirtualNetworkResourceId, '/')[2], split(hubVirtualNetworkResourceId, '/')[4]) } -// Gets the application group references if the feed workspace already exists +// Gets the application group references if the AVD feed workspace already exists resource workspace 'Microsoft.DesktopVirtualization/workspaces@2023-09-05' existing = if (!empty(existingFeedWorkspaceResourceId)) { scope: resourceGroup(split(existingFeedWorkspaceResourceId, '/')[2], split(existingFeedWorkspaceResourceId, '/')[4]) name: split(existingFeedWorkspaceResourceId, '/')[8] } -// This module deploys telemetry for ArcGIS Pro deployments +// Optionally deploys telemetry for ArcGIS Pro deployments #disable-next-line no-deployments-resources resource partnerTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (enableTelemetry && profile == 'ArcGISPro') { name: 'pid-4e82be1d-7fcb-4913-a90c-aa84d7ea3a1c' @@ -412,22 +403,9 @@ resource partnerTelemetry 'Microsoft.Resources/deployments@2021-04-01' = if (ena } } -// This deployment is used to get the naming convention and tokens for the resource groups and resources. -module naming_controlPlane '../../modules/naming-convention.bicep' = { - name: 'get-naming-cp-${deploymentNameSuffix}' - params: { - environmentAbbreviation: environmentAbbreviation - location: virtualNetwork.location - networkName: 'avd' - networkShortName: 'avd' - resourcePrefix: identifier - stampIndex: string(stampIndex) - } -} - -// This deployment is used to get the naming convention and tokens for the resource groups and resources. -module naming_hub '../../modules/naming-convention.bicep' = { - name: 'get-naming-hub-${deploymentNameSuffix}' +// Gets the naming convention and tokens for the resource groups and resources +module naming_management '../../modules/naming-convention.bicep' = { + name: 'get-naming-mgmt-${deploymentNameSuffix}' params: { environmentAbbreviation: environmentAbbreviation location: virtualNetwork.location @@ -465,28 +443,18 @@ module tier3_hosts '../tier3/solution.bicep' = { } } -// Resource Groups -module rgs '../../modules/resource-group.bicep' = [ - for service in resourceGroupServices: { - name: 'deploy-rg-${service}-${deploymentNameSuffix}' - params: { - location: service == 'controlPlane' ? virtualNetwork.location : locationVirtualMachines - mlzTags: tier3_hosts.outputs.mlzTags - name: service == 'controlPlane' - ? replace(naming_controlPlane.outputs.names.resourceGroup, naming_controlPlane.outputs.tokens.service, service) - : replace(tier3_hosts.outputs.namingConvention.resourceGroup, tier3_hosts.outputs.tokens.service, service) - tags: tags - } - } -] - -// Management Services: AVD Insights, File Share Scaling, Scaling Tool +// Management: host pool, app group, AVD Insights, File Share Scaling module management 'modules/management/management.bicep' = { name: 'deploy-management-${deploymentNameSuffix}' params: { + activeDirectorySolution: activeDirectorySolution avdObjectId: avdObjectId + avdPrivateDnsZoneResourceId: '${privateDnsZoneResourceIdPrefix}${filter(tier3_hosts.outputs.privateDnsZones, name => startsWith(name, 'privatelink.wvd'))[0]}' + customImageId: customImageId + customRdpProperty: customRdpProperty deployFslogix: deployFslogix deploymentNameSuffix: deploymentNameSuffix + desktopFriendlyName: empty(desktopFriendlyName) ? string(stampIndex) : desktopFriendlyName diskEncryptionSetResourceId: tier3_hosts.outputs.diskEncryptionSetResourceId diskSku: diskSku domainJoinPassword: domainJoinPassword @@ -496,9 +464,17 @@ module management 'modules/management/management.bicep' = { enableAvdInsights: enableAvdInsights environmentAbbreviation: environmentAbbreviation fslogixStorageService: fslogixStorageService + hostPoolPublicNetworkAccess: hostPoolPublicNetworkAccess + hostPoolType: hostPoolType + imageOffer: imageOffer + imagePublisher: imagePublisher + imageSku: imageSku + imageVersionResourceId: imageVersionResourceId + locationControlPlane: virtualNetwork.location locationVirtualMachines: locationVirtualMachines logAnalyticsWorkspaceRetention: logAnalyticsWorkspaceRetention logAnalyticsWorkspaceSku: logAnalyticsWorkspaceSku + maxSessionLimit: usersPerCore * virtualMachineVirtualCpuCount mlzTags: tier3_hosts.outputs.mlzTags namingConvention: tier3_hosts.outputs.namingConvention organizationalUnitPath: organizationalUnitPath @@ -508,72 +484,25 @@ module management 'modules/management/management.bicep' = { recoveryServices: recoveryServices recoveryServicesGeo: tier3_hosts.outputs.locationProperties.recoveryServicesGeo resourceAbbreviations: tier3_hosts.outputs.resourceAbbreviations - resourceGroupControlPlane: rgs[0].outputs.name - resourceGroupHosts: rgs[1].outputs.name - resourceGroupManagement: rgs[2].outputs.name - resourceGroupStorage: deployFslogix - ? replace(tier3_hosts.outputs.namingConvention.resourceGroup, tier3_hosts.outputs.tokens.service, 'storage') - : '' - roleDefinitions: roleDefinitions - serviceToken: tier3_hosts.outputs.tokens.service - storageService: storageService - subnetResourceId: tier3_hosts.outputs.subnets[0].id - subnets: tier3_hosts.outputs.subnets - tags: tags - timeDifference: tier3_hosts.outputs.locationProperties.timeDifference - timeZone: tier3_hosts.outputs.locationProperties.timeZone - virtualMachinePassword: virtualMachinePassword - virtualMachineUsername: virtualMachineUsername - } -} - -// AVD Control Plane -// This module deploys the host pool and desktop application group -module controlPlane 'modules/controlPlane/controlPlane.bicep' = { - name: 'deploy-control-plane-${deploymentNameSuffix}' - params: { - activeDirectorySolution: activeDirectorySolution - avdPrivateDnsZoneResourceId: '${privateDnsZoneResourceIdPrefix}${filter(tier3_hosts.outputs.privateDnsZones, name => startsWith(name, 'privatelink.wvd'))[0]}' - customImageId: customImageId - customRdpProperty: customRdpProperty - deploymentNameSuffix: deploymentNameSuffix - deploymentUserAssignedIdentityClientId: management.outputs.deploymentUserAssignedIdentityClientId - deploymentUserAssignedIdentityPrincipalId: management.outputs.deploymentUserAssignedIdentityPrincipalId - desktopFriendlyName: empty(desktopFriendlyName) ? string(stampIndex) : desktopFriendlyName - diskSku: diskSku - domainName: domainName - enableAvdInsights: enableAvdInsights - hostPoolPublicNetworkAccess: hostPoolPublicNetworkAccess - hostPoolType: hostPoolType - imageOffer: imageOffer - imagePublisher: imagePublisher - imageSku: imageSku - imageVersionResourceId: imageVersionResourceId - locationControlPlane: virtualNetwork.location - locationVirtualMachines: locationVirtualMachines - logAnalyticsWorkspaceResourceId: enableAvdInsights ? management.outputs.logAnalyticsWorkspaceResourceId : '' - managementVirtualMachineName: management.outputs.virtualMachineName - maxSessionLimit: usersPerCore * virtualMachineVirtualCpuCount - mlzTags: tier3_hosts.outputs.mlzTags - namingConvention: naming_controlPlane.outputs.names - resourceGroupControlPlane: rgs[0].outputs.name - resourceGroupManagement: rgs[2].outputs.name - roleDefinitions: roleDefinitions + resourceGroupName: replace(naming_management.outputs.names.resourceGroup, naming_management.outputs.tokens.service, 'management') + resourceGroupProfiles: replace(tier3_hosts.outputs.namingConvention.resourceGroup, tier3_hosts.outputs.tokens.service, 'profiles') securityPrincipalObjectIds: map(securityPrincipals, item => item.objectId) - serviceToken: naming_controlPlane.outputs.tokens.service + serviceToken: tier3_hosts.outputs.tokens.service sessionHostNamePrefix: replace( tier3_hosts.outputs.namingConvention.virtualMachine, tier3_hosts.outputs.tokens.service, '' ) - subnetResourceId: tier3_hosts.outputs.subnets[1].id + storageService: storageService + subnetResourceId: tier3_hosts.outputs.subnets[0].id + subnets: tier3_hosts.outputs.subnets tags: tags + timeZone: tier3_hosts.outputs.locationProperties.timeZone validationEnvironment: validationEnvironment + virtualMachinePassword: virtualMachinePassword virtualMachineSize: virtualMachineSize + virtualMachineUsername: virtualMachineUsername } - dependsOn: [ - rgs - ] } // AVD Workspaces @@ -582,7 +511,7 @@ module workspaces 'modules/sharedServices/sharedServices.bicep' = { name: 'deploy-workspaces-${deploymentNameSuffix}' scope: subscription(split(sharedServicesSubnetResourceId, '/')[2]) params: { - applicationGroupResourceId: controlPlane.outputs.applicationGroupResourceId + applicationGroupResourceId: management.outputs.applicationGroupResourceId avdPrivateDnsZoneResourceId: '${privateDnsZoneResourceIdPrefix}${filter(tier3_hosts.outputs.privateDnsZones, name => startsWith(name, 'privatelink.wvd'))[0]}' deploymentNameSuffix: deploymentNameSuffix deploymentUserAssignedIdentityClientId: management.outputs.deploymentUserAssignedIdentityClientId @@ -593,84 +522,40 @@ module workspaces 'modules/sharedServices/sharedServices.bicep' = { : workspace.properties.applicationGroupReferences existingFeedWorkspaceResourceId: existingFeedWorkspaceResourceId existingWorkspace: !empty(existingFeedWorkspaceResourceId) - hostPoolName: controlPlane.outputs.hostPoolName + hostPoolName: management.outputs.hostPoolName locationControlPlane: virtualNetwork.location locationVirtualMachines: locationVirtualMachines logAnalyticsWorkspaceResourceId: management.outputs.logAnalyticsWorkspaceResourceId managementVirtualMachineName: management.outputs.virtualMachineName mlzTags: tier3_hosts.outputs.mlzTags - resourceGroupManagement: rgs[2].outputs.name + resourceGroupManagement: management.outputs.resourceGroupName sharedServicesSubnetResourceId: sharedServicesSubnetResourceId tags: tags - workspaceFeedDiagnoticSettingName: replace( - replace(naming_hub.outputs.names.workspaceFeedDiagnosticSetting, naming_hub.outputs.tokens.service, 'feed'), - '-${stampIndex}', - '' - ) - workspaceFeedName: replace( - replace(naming_controlPlane.outputs.names.workspaceFeed, naming_controlPlane.outputs.tokens.service, 'feed'), - '-${stampIndex}', - '' - ) - workspaceFeedNetworkInterfaceName: replace( - replace(naming_hub.outputs.names.workspaceFeedNetworkInterface, naming_hub.outputs.tokens.service, 'feed'), - '-${stampIndex}', - '' - ) - workspaceFeedPrivateEndpointName: replace( - replace(naming_hub.outputs.names.workspaceFeedPrivateEndpoint, naming_hub.outputs.tokens.service, 'feed'), - '-${stampIndex}', - '' - ) + workspaceFeedDiagnoticSettingName: naming_management.outputs.names.workspaceFeedDiagnosticSetting + workspaceFeedName: naming_management.outputs.names.workspaceFeed + workspaceFeedNetworkInterfaceName: naming_management.outputs.names.workspaceFeedNetworkInterface + workspaceFeedPrivateEndpointName: naming_management.outputs.names.workspaceFeedPrivateEndpoint workspaceFeedResourceGroupName: replace( replace( - naming_controlPlane.outputs.names.resourceGroup, - naming_controlPlane.outputs.tokens.service, + naming_management.outputs.names.resourceGroup, + naming_management.outputs.tokens.service, 'feedWorkspace' ), '-${stampIndex}', '' ) workspaceFriendlyName: empty(workspaceFriendlyName) - ? replace( - replace(naming_controlPlane.outputs.names.workspaceFeed, '-${naming_controlPlane.outputs.tokens.service}', ''), - '-${stampIndex}', - '' - ) + ? replace(naming_management.outputs.names.workspaceFeed, '-${naming_management.outputs.tokens.service}', '') : '${workspaceFriendlyName} (${virtualNetwork.location})' - workspaceGlobalName: replace( - replace( - replace(naming_controlPlane.outputs.names.workspaceGlobal, naming_controlPlane.outputs.tokens.service, 'global'), - '-${stampIndex}', - '' - ), - identifier, - virtualNetwork.tags.resourcePrefix - ) - workspaceGlobalNetworkInterfaceName: replace( - replace( - replace(naming_hub.outputs.names.workspaceGlobalNetworkInterface, naming_hub.outputs.tokens.service, 'global'), - '-${stampIndex}', - '' - ), - identifier, - virtualNetwork.tags.resourcePrefix - ) + workspaceGlobalName: replace(naming_management.outputs.names.workspaceGlobal, identifier, virtualNetwork.tags.resourcePrefix) + workspaceGlobalNetworkInterfaceName: replace(naming_management.outputs.names.workspaceGlobalNetworkInterface, identifier, virtualNetwork.tags.resourcePrefix) workspaceGlobalPrivateDnsZoneResourceId: '${privateDnsZoneResourceIdPrefix}${filter(tier3_hosts.outputs.privateDnsZones, name => startsWith(name, 'privatelink-global.wvd'))[0]}' - workspaceGlobalPrivateEndpointName: replace( - replace( - replace(naming_hub.outputs.names.workspaceGlobalPrivateEndpoint, naming_hub.outputs.tokens.service, 'global'), - '-${stampIndex}', - '' - ), - identifier, - virtualNetwork.tags.resourcePrefix - ) + workspaceGlobalPrivateEndpointName: replace(naming_management.outputs.names.workspaceGlobalPrivateEndpoint, identifier, virtualNetwork.tags.resourcePrefix) workspaceGlobalResourceGroupName: replace( replace( replace( - naming_controlPlane.outputs.names.resourceGroup, - naming_controlPlane.outputs.tokens.service, + naming_management.outputs.names.resourceGroup, + naming_management.outputs.tokens.service, 'globalWorkspace' ), '-${stampIndex}', @@ -691,6 +576,7 @@ module fslogix 'modules/fslogix/fslogix.bicep' = if (deployFslogix) { azureFilesPrivateDnsZoneResourceId: '${privateDnsZoneResourceIdPrefix}${filter(tier3_hosts.outputs.privateDnsZones, name => contains(name, 'file'))[0]}' deploymentNameSuffix: deploymentNameSuffix deploymentUserAssignedIdentityClientId: management.outputs.deploymentUserAssignedIdentityClientId + deploymentUserAssignedIdentityPrincipalId: management.outputs.deploymentUserAssignedIdentityPrincipalId dnsServers: string(tier3_hosts.outputs.dnsServers) domainJoinPassword: domainJoinPassword domainJoinUserPrincipalName: domainJoinUserPrincipalName @@ -701,7 +587,8 @@ module fslogix 'modules/fslogix/fslogix.bicep' = if (deployFslogix) { fslogixContainerType: fslogixContainerType fslogixShareSizeInGB: fslogixShareSizeInGB fslogixStorageService: fslogixStorageService - functionAppName: management.outputs.functionAppName + functionAppPrincipalId: management.outputs.functionAppPrincipalId + hostPoolResourceId: management.outputs.hostPoolResourceId keyVaultUri: tier3_hosts.outputs.keyVaultUri location: locationVirtualMachines managementVirtualMachineName: management.outputs.virtualMachineName @@ -710,10 +597,9 @@ module fslogix 'modules/fslogix/fslogix.bicep' = if (deployFslogix) { netbios: netbios organizationalUnitPath: organizationalUnitPath recoveryServices: recoveryServices - resourceGroupControlPlane: rgs[0].outputs.name - resourceGroupManagement: rgs[2].outputs.name - resourceGroupStorage: deployFslogix ? rgs[3].outputs.name : '' - securityPrincipalNames: map(securityPrincipals, item => item.name) + resourceGroupManagement: management.outputs.resourceGroupName + resourceGroupName: replace(tier3_hosts.outputs.namingConvention.resourceGroup, tier3_hosts.outputs.tokens.service, 'profiles') + securityPrincipalNames: map(securityPrincipals, item => item.displayName) securityPrincipalObjectIds: map(securityPrincipals, item => item.objectId) serviceToken: tier3_hosts.outputs.tokens.service smbServerLocation: tier3_hosts.outputs.locationProperties.timeZone @@ -726,10 +612,6 @@ module fslogix 'modules/fslogix/fslogix.bicep' = if (deployFslogix) { subnets: tier3_hosts.outputs.subnets tags: tags } - dependsOn: [ - controlPlane - rgs - ] } module sessionHosts 'modules/sessionHosts/sessionHosts.bicep' = { @@ -746,6 +628,9 @@ module sessionHosts 'modules/sessionHosts/sessionHosts.bicep' = { deploymentNameSuffix: deploymentNameSuffix deploymentUserAssignedIdentityClientId: management.outputs.deploymentUserAssignedIdentityClientId deploymentUserAssignedIdentityPrincipalId: management.outputs.deploymentUserAssignedIdentityPrincipalId + diskAccessPolicyDefinitionId: management.outputs.diskAccessPolicyDefinitionId + diskAccessPolicyDisplayName: management.outputs.diskAccessPolicyDisplayName + diskAccessResourceId: management.outputs.diskAccessResourceId diskEncryptionSetResourceId: tier3_hosts.outputs.diskEncryptionSetResourceId diskSku: diskSku divisionRemainderValue: divisionRemainderValue @@ -758,8 +643,8 @@ module sessionHosts 'modules/sessionHosts/sessionHosts.bicep' = { enableRecoveryServices: recoveryServices environmentAbbreviation: environmentAbbreviation fslogixContainerType: fslogixContainerType - hostPoolName: controlPlane.outputs.hostPoolName - hostPoolResourceId: controlPlane.outputs.hostPoolResourceId + hostPoolName: management.outputs.hostPoolName + hostPoolResourceId: management.outputs.hostPoolResourceId hostPoolType: hostPoolType identifier: identifier imageOffer: imageOffer @@ -776,11 +661,10 @@ module sessionHosts 'modules/sessionHosts/sessionHosts.bicep' = { 'None' ] organizationalUnitPath: organizationalUnitPath + profile: profile recoveryServicesVaultName: management.outputs.recoveryServicesVaultName - resourceGroupControlPlane: rgs[0].outputs.name - resourceGroupHosts: rgs[1].outputs.name - resourceGroupManagement: rgs[2].outputs.name - roleDefinitions: roleDefinitions + resourceGroupManagement: management.outputs.resourceGroupName + resourceGroupName: replace(tier3_hosts.outputs.namingConvention.resourceGroup, tier3_hosts.outputs.tokens.service, 'hosts') scalingWeekdaysOffPeakStartTime: scalingWeekdaysOffPeakStartTime scalingWeekdaysPeakStartTime: scalingWeekdaysPeakStartTime scalingWeekendsOffPeakStartTime: scalingWeekendsOffPeakStartTime @@ -803,7 +687,6 @@ module sessionHosts 'modules/sessionHosts/sessionHosts.bicep' = { } dependsOn: [ fslogix - rgs ] } @@ -812,7 +695,7 @@ module cleanUp 'modules/cleanUp/cleanUp.bicep' = { params: { deploymentNameSuffix: deploymentNameSuffix location: locationVirtualMachines - resourceGroupManagement: rgs[2].outputs.name + resourceGroupManagement: management.outputs.resourceGroupName userAssignedIdentityClientId: management.outputs.deploymentUserAssignedIdentityClientId virtualMachineResourceId: management.outputs.virtualMachineResourceId } diff --git a/src/bicep/add-ons/azure-virtual-desktop/solution.json b/src/bicep/add-ons/azure-virtual-desktop/solution.json index 10c417380..d9109271c 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/solution.json +++ b/src/bicep/add-ons/azure-virtual-desktop/solution.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "495118660330348734" + "version": "0.31.92.45157", + "templateHash": "8358069094561879230" } }, "parameters": { @@ -593,13 +593,6 @@ "fileShares": "[variables('fileShareNames')[parameters('fslogixContainerType')]]", "netbios": "[split(parameters('domainName'), '.')[0]]", "privateDnsZoneResourceIdPrefix": "[format('/subscriptions/{0}/resourceGroups/{1}/providers/Microsoft.Network/privateDnsZones/', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4])]", - "resourceGroupServices": "[union(createArray('controlPlane', 'hosts', 'management'), if(variables('deployFslogix'), createArray('storage'), createArray()))]", - "roleDefinitions": { - "DesktopVirtualizationPowerOnOffContributor": "40c5ff49-9181-41f8-ae61-143b0e78555e", - "DesktopVirtualizationUser": "1d18fff3-a72a-46b5-b4a9-0b38a3cd7e63", - "Reader": "acdd72a7-3385-48ef-bd42-f606fba81ae7", - "VirtualMachineUserLogin": "fb879df8-f326-4884-b1cf-06f3ad86be52" - }, "storageSku": "[if(equals(parameters('fslogixStorageService'), 'None'), 'None', split(parameters('fslogixStorageService'), ' ')[1])]", "storageService": "[split(parameters('fslogixStorageService'), ' ')[0]]", "storageSuffix": "[environment().suffixes.storage]", @@ -635,7 +628,7 @@ { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('get-naming-cp-{0}', parameters('deploymentNameSuffix'))]", + "name": "[format('get-naming-mgmt-{0}', parameters('deploymentNameSuffix'))]", "location": "[deployment().location]", "properties": { "expressionEvaluationOptions": { @@ -668,8 +661,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "11992572431768878515" + "version": "0.31.92.45157", + "templateHash": "11125044402666498605" } }, "parameters": { @@ -1136,7 +1129,7 @@ "namingConvention_Service": "[format('{0}-{1}{2}-{3}-{4}-{5}-{6}', toLower(parameters('resourcePrefix')), if(empty(parameters('stampIndex')), '', format('{0}-', parameters('stampIndex'))), parameters('tokens').resource, parameters('networkName'), parameters('tokens').service, variables('locationAbbreviation'), parameters('environmentAbbreviation'))]", "names": { "actionGroup": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').actionGroups)]", - "applicationGroup": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').applicationGroups)]", + "applicationGroup": "[replace(variables('namingConvention'), parameters('tokens').resource, format('{0}-desktop', variables('resourceAbbreviations').applicationGroups))]", "applicationInsights": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').applicationInsights)]", "appServicePlan": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').appServicePlans)]", "automationAccount": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').automationAccounts)]", @@ -1209,14 +1202,14 @@ "virtualMachineNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').virtualMachines))]", "virtualNetwork": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').virtualNetworks)]", "virtualNetworkDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, variables('resourceAbbreviations').virtualNetworks)]", - "workspaceFeed": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').workspaces), format('-{0}', parameters('stampIndex')), '')]", - "workspaceFeedDiagnosticSetting": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", - "workspaceFeedNetworkInterface": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", - "workspaceFeedPrivateEndpoint": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", - "workspaceGlobal": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').workspaces), format('-{0}', parameters('stampIndex')), '')]", - "workspaceGlobalDiagnosticSetting": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", - "workspaceGlobalNetworkInterface": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", - "workspaceGlobalPrivateEndpoint": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]" + "workspaceFeed": "[replace(replace(variables('namingConvention'), parameters('tokens').resource, format('{0}-feed', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", + "workspaceFeedDiagnosticSetting": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-feed', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", + "workspaceFeedNetworkInterface": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-feed', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", + "workspaceFeedPrivateEndpoint": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-feed', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", + "workspaceGlobal": "[replace(replace(variables('namingConvention'), parameters('tokens').resource, format('{0}-global', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", + "workspaceGlobalDiagnosticSetting": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-global', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", + "workspaceGlobalNetworkInterface": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-global', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", + "workspaceGlobalPrivateEndpoint": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-global', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]" } }, "resources": [], @@ -1244,7 +1237,7 @@ { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('get-naming-hub-{0}', parameters('deploymentNameSuffix'))]", + "name": "[format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))]", "location": "[deployment().location]", "properties": { "expressionEvaluationOptions": { @@ -1252,23 +1245,68 @@ }, "mode": "Incremental", "parameters": { + "additionalSubnets": { + "value": "[union(variables('subnets').avdControlPlane, variables('subnets').azureNetAppFiles, variables('subnets').functionApp)]" + }, + "deployActivityLogDiagnosticSetting": { + "value": "[parameters('deployActivityLogDiagnosticSetting')]" + }, + "deployDefender": { + "value": "[parameters('deployDefender')]" + }, + "deploymentNameSuffix": { + "value": "[parameters('deploymentNameSuffix')]" + }, + "deployNetworkWatcher": { + "value": "[parameters('deployNetworkWatcher')]" + }, + "deployPolicy": { + "value": "[parameters('deployPolicy')]" + }, + "emailSecurityContact": { + "value": "[parameters('emailSecurityContact')]" + }, "environmentAbbreviation": { "value": "[parameters('environmentAbbreviation')]" }, - "location": { - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Network/virtualNetworks', split(parameters('hubVirtualNetworkResourceId'), '/')[8]), '2023-11-01', 'full').location]" - }, - "networkName": { - "value": "avd" + "firewallResourceId": { + "value": "[parameters('hubAzureFirewallResourceId')]" }, - "networkShortName": { - "value": "avd" + "hubVirtualNetworkResourceId": { + "value": "[parameters('hubVirtualNetworkResourceId')]" }, - "resourcePrefix": { + "identifier": { "value": "[parameters('identifier')]" }, + "location": { + "value": "[parameters('locationVirtualMachines')]" + }, + "logAnalyticsWorkspaceResourceId": { + "value": "[parameters('operationsLogAnalyticsWorkspaceResourceId')]" + }, + "policy": { + "value": "[parameters('policy')]" + }, "stampIndex": { "value": "[string(parameters('stampIndex'))]" + }, + "subnetName": { + "value": "AvdSessionHosts" + }, + "subnetAddressPrefix": { + "value": "[parameters('subnetAddressPrefixes')[0]]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "virtualNetworkAddressPrefix": { + "value": "[parameters('virtualNetworkAddressPrefixes')[0]]" + }, + "workloadName": { + "value": "avd" + }, + "workloadShortName": { + "value": "avd" } }, "template": { @@ -1277,5473 +1315,2337 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "11992572431768878515" + "version": "0.31.92.45157", + "templateHash": "14266827690996546891" } }, "parameters": { + "additionalSubnets": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "An array of additional subnets to support the tier3 workload." + } + }, + "deployActivityLogDiagnosticSetting": { + "type": "bool", + "metadata": { + "description": "Choose whether to deploy a diagnostic setting for the Activity Log." + } + }, + "deployDefender": { + "type": "bool", + "metadata": { + "description": "Choose whether to deploy Defender for Cloud." + } + }, + "deploymentNameSuffix": { + "type": "string", + "defaultValue": "[utcNow()]", + "metadata": { + "description": "The suffix to append to the deployment name. It defaults to the current UTC date and time." + } + }, + "deployNetworkWatcher": { + "type": "bool", + "metadata": { + "description": "Choose whether to deploy Network Watcher for the deployment location." + } + }, + "deployPolicy": { + "type": "bool", + "metadata": { + "description": "Choose whether to deploy a policy assignment." + } + }, + "emailSecurityContact": { + "type": "string", + "metadata": { + "description": "The email address to use for Defender for Cloud notifications." + } + }, "environmentAbbreviation": { - "type": "string" + "type": "string", + "defaultValue": "dev", + "allowedValues": [ + "dev", + "prod", + "test" + ], + "metadata": { + "description": "The abbreviation for the environment." + } + }, + "firewallResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Azure Firewall in the HUB." + } + }, + "hubVirtualNetworkResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the HUB Virtual Network." + } + }, + "identifier": { + "type": "string", + "maxLength": 3, + "metadata": { + "description": "The identifier for the resource names. This value should represent the workload, project, or business unit." + } + }, + "keyVaultDiagnosticsLogs": { + "type": "array", + "defaultValue": [ + { + "category": "AuditEvent", + "enabled": true + }, + { + "category": "AzurePolicyEvaluationDetails", + "enabled": true + } + ], + "metadata": { + "description": "An array of Key Vault Diagnostic Logs categories to collect. See \"https://learn.microsoft.com/en-us/azure/key-vault/general/logging?tabs=Vault\" for valid values." + } }, "location": { - "type": "string" + "type": "string", + "defaultValue": "[deployment().location]", + "metadata": { + "description": "The location for the deployment. It defaults to the location of the deployment." + } }, - "networkName": { - "type": "string" + "logAnalyticsWorkspaceResourceId": { + "type": "string", + "metadata": { + "description": "The resource ID of the Log Analytics Workspace to use for log storage." + } }, - "networkShortName": { - "type": "string" + "logStorageSkuName": { + "type": "string", + "defaultValue": "Standard_GRS", + "metadata": { + "description": "The Storage Account SKU to use for log storage. It defaults to \"Standard_GRS\". See https://docs.microsoft.com/en-us/rest/api/storagerp/srp_sku_types for valid settings." + } }, - "resourcePrefix": { - "type": "string" + "networkSecurityGroupDiagnosticsLogs": { + "type": "array", + "defaultValue": [ + { + "category": "NetworkSecurityGroupEvent", + "enabled": true + }, + { + "category": "NetworkSecurityGroupRuleCounter", + "enabled": true + } + ], + "metadata": { + "description": "An array of Network Security Group diagnostic logs to apply to the workload Virtual Network. See https://docs.microsoft.com/en-us/azure/virtual-network/virtual-network-nsg-manage-log#log-categories for valid settings." + } + }, + "networkSecurityGroupDiagnosticsMetrics": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The metrics to monitor for the Network Security Group." + } + }, + "networkSecurityGroupRules": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The rules to apply to the Network Security Group." + } + }, + "policy": { + "type": "string", + "defaultValue": "NISTRev4", + "metadata": { + "description": "The policy to assign to the workload." + } }, "stampIndex": { "type": "string", - "defaultValue": "" + "defaultValue": "", + "metadata": { + "description": "The stamp index allows for multiple AVD stamps with the same business unit or project to support different use cases." + } }, - "tokens": { + "subnetAddressPrefix": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The address prefix for the workload subnet." + } + }, + "subnetName": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The custom name for the workload subnet if the naming convention is not desired. Subnets are child resources and do not require a unique name between virtual networks, only within the same virtual network." + } + }, + "tags": { "type": "object", - "defaultValue": { - "purpose": "purpose_token", - "resource": "resource_token", - "service": "service_token" + "defaultValue": {}, + "metadata": { + "description": "The tags to apply to the resources." } - } - }, - "variables": { - "$fxv#0": { - "AzureChina": { - "chinaeast": { - "abbreviation": "cne", - "recoveryServicesGeo": "sha", - "timeDifference": "+8:00", - "timeZone": "China Standard Time" - }, - "chinaeast2": { - "abbreviation": "cne2", - "recoveryServicesGeo": "sha2", - "timeDifference": "+8:00", - "timeZone": "China Standard Time" - }, - "chinanorth": { - "abbreviation": "cnn", - "recoveryServicesGeo": "bjb", - "timeDifference": "+8:00", - "timeZone": "China Standard Time" - }, - "chinanorth2": { - "abbreviation": "cnn2", - "recoveryServicesGeo": "bjb2", - "timeDifference": "+8:00", - "timeZone": "China Standard Time" - } - }, - "AzureCloud": { - "australiacentral": { - "abbreviation": "auc", - "recoveryServicesGeo": "acl", - "timeDifference": "+10:00", - "timeZone": "AUS Eastern Standard Time" - }, - "australiacentral2": { - "abbreviation": "auc2", - "recoveryServicesGeo": "acl2", - "timeDifference": "+10:00", - "timeZone": "AUS Eastern Standard Time" - }, - "australiaeast": { - "abbreviation": "aue", - "recoveryServicesGeo": "ae", - "timeDifference": "+10:00", - "timeZone": "AUS Eastern Standard Time" - }, - "australiasoutheast": { - "abbreviation": "ause", - "recoveryServicesGeo": "ase", - "timeDifference": "+10:00", - "timeZone": "AUS Eastern Standard Time" - }, - "brazilsouth": { - "abbreviation": "brs", - "recoveryServicesGeo": "brs", - "timeDifference": "-3:00", - "timeZone": "E. South America Standard Time" - }, - "brazilsoutheast": { - "abbreviation": "brse", - "recoveryServicesGeo": "bse", - "timeDifference": "-3:00", - "timeZone": "E. South America Standard Time" - }, - "canadacentral": { - "abbreviation": "cac", - "recoveryServicesGeo": "cnc", - "timeDifference": "-5:00", - "timeZone": "Eastern Standard Time" - }, - "canadaeast": { - "abbreviation": "cae", - "recoveryServicesGeo": "cne", - "timeDifference": "-5:00", - "timeZone": "Eastern Standard Time" - }, - "centralindia": { - "abbreviation": "inc", - "recoveryServicesGeo": "inc", - "timeDifference": "+5:30", - "timeZone": "India Standard Time" - }, - "centralus": { - "abbreviation": "usc", - "recoveryServicesGeo": "cus", - "timeDifference": "-6:00", - "timeZone": "Central Standard Time" - }, - "eastasia": { - "abbreviation": "ase", - "recoveryServicesGeo": "ea", - "timeDifference": "+8:00", - "timeZone": "China Standard Time" - }, - "eastus": { - "abbreviation": "use", - "recoveryServicesGeo": "eus", - "timeDifference": "-5:00", - "timeZone": "Eastern Standard Time" - }, - "eastus2": { - "abbreviation": "use2", - "recoveryServicesGeo": "eus2", - "timeDifference": "-5:00", - "timeZone": "Eastern Standard Time" - }, - "francecentral": { - "abbreviation": "frc", - "recoveryServicesGeo": "frc", - "timeDifference": "+1:00", - "timeZone": "Central Europe Standard Time" - }, - "francesouth": { - "abbreviation": "frs", - "recoveryServicesGeo": "frs", - "timeDifference": "+1:00", - "timeZone": "Central Europe Standard Time" - }, - "germanynorth": { - "abbreviation": "den", - "recoveryServicesGeo": "gn", - "timeDifference": "+1:00", - "timeZone": "Central Europe Standard Time" - }, - "germanywestcentral": { - "abbreviation": "dewc", - "recoveryServicesGeo": "gwc", - "timeDifference": "+1:00", - "timeZone": "Central Europe Standard Time" - }, - "israelcentral": { - "abbreviation": "ilc", - "recoveryServicesGeo": "ilc", - "timeDifference": "+2:00", - "timeZone": "Israel Standard Time" - }, - "italynorth": { - "abbreviation": "itn", - "recoveryServicesGeo": "itn", - "timeDifference": "+1:00", - "timeZone": "Central Europe Standard Time" - }, - "japaneast": { - "abbreviation": "jpe", - "recoveryServicesGeo": "jpe", - "timeDifference": "+9:00", - "timeZone": "Tokyo Standard Time" - }, - "japanwest": { - "abbreviation": "jpw", - "recoveryServicesGeo": "jpw", - "timeDifference": "+9:00", - "timeZone": "Tokyo Standard Time" - }, - "jioindiacentral": { - "abbreviation": "injc", - "recoveryServicesGeo": "jic", - "timeDifference": "+5:30", - "timeZone": "India Standard Time" - }, - "jioindiawest": { - "abbreviation": "injw", - "recoveryServicesGeo": "jiw", - "timeDifference": "+5:30", - "timeZone": "India Standard Time" - }, - "koreacentral": { - "abbreviation": "krc", - "recoveryServicesGeo": "krc", - "timeDifference": "+9:00", - "timeZone": "Korea Standard Time" + }, + "virtualNetworkAddressPrefix": { + "type": "string", + "defaultValue": "", + "metadata": { + "description": "The address prefix for the workload Virtual Network." + } + }, + "virtualNetworkDiagnosticsLogs": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The diagnostic logs to apply to the workload Virtual Network." + } + }, + "virtualNetworkDiagnosticsMetrics": { + "type": "array", + "defaultValue": [], + "metadata": { + "description": "The metrics to monitor for the workload Virtual Network." + } + }, + "workloadName": { + "type": "string", + "defaultValue": "tier3", + "minLength": 1, + "maxLength": 10, + "metadata": { + "description": "The name for the workload." + } + }, + "workloadShortName": { + "type": "string", + "defaultValue": "t3", + "minLength": 1, + "maxLength": 3, + "metadata": { + "description": "The short name for the workload." + } + } + }, + "variables": { + "hubResourceGroupName": "[split(parameters('hubVirtualNetworkResourceId'), '/')[4]]", + "hubSubscriptionId": "[split(parameters('hubVirtualNetworkResourceId'), '/')[2]]", + "subscriptionId": "[subscription().subscriptionId]" + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))]", + "location": "[deployment().location]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" }, - "koreasouth": { - "abbreviation": "krs", - "recoveryServicesGeo": "krs", - "timeDifference": "+9:00", - "timeZone": "Korea Standard Time" + "mode": "Incremental", + "parameters": { + "deploymentNameSuffix": { + "value": "[parameters('deploymentNameSuffix')]" + }, + "environmentAbbreviation": { + "value": "[parameters('environmentAbbreviation')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "networks": { + "value": [ + { + "name": "[parameters('workloadName')]", + "shortName": "[parameters('workloadShortName')]", + "deployUniqueResources": false, + "subscriptionId": "[variables('subscriptionId')]", + "nsgDiagLogs": "[parameters('networkSecurityGroupDiagnosticsLogs')]", + "nsgDiagMetrics": "[parameters('networkSecurityGroupDiagnosticsMetrics')]", + "nsgRules": "[parameters('networkSecurityGroupRules')]", + "vnetAddressPrefix": "[parameters('virtualNetworkAddressPrefix')]", + "vnetDiagLogs": "[parameters('virtualNetworkDiagnosticsLogs')]", + "vnetDiagMetrics": "[parameters('virtualNetworkDiagnosticsMetrics')]", + "subnetAddressPrefix": "[parameters('subnetAddressPrefix')]" + } + ] + }, + "resourcePrefix": { + "value": "[parameters('identifier')]" + }, + "stampIndex": { + "value": "[parameters('stampIndex')]" + } }, - "northcentralus": { - "abbreviation": "usnc", - "recoveryServicesGeo": "ncus", - "timeDifference": "-6:00", - "timeZone": "Central Standard Time" - }, - "northeurope": { - "abbreviation": "eun", - "recoveryServicesGeo": "ne", - "timeDifference": "0:00", - "timeZone": "GMT Standard Time" - }, - "norwayeast": { - "abbreviation": "noe", - "recoveryServicesGeo": "nwe", - "timeDifference": "+1:00", - "timeZone": "Central Europe Standard Time" - }, - "norwaywest": { - "abbreviation": "now", - "recoveryServicesGeo": "nww", - "timeDifference": "+1:00", - "timeZone": "Central Europe Standard Time" - }, - "polandcentral": { - "abbreviation": "plc", - "recoveryServicesGeo": "plc", - "timeDifference": "+1:00", - "timeZone": "Central Europe Standard Time" - }, - "qatarcentral": { - "abbreviation": "qac", - "recoveryServicesGeo": "qac", - "timeDifference": "+3:00", - "timeZone": "Arabian Standard Time" - }, - "southafricanorth": { - "abbreviation": "zan", - "recoveryServicesGeo": "san", - "timeDifference": "+2:00", - "timeZone": "South Africa Standard Time" - }, - "southafricawest": { - "abbreviation": "zaw", - "recoveryServicesGeo": "saw", - "timeDifference": "+2:00", - "timeZone": "South Africa Standard Time" - }, - "southcentralus": { - "abbreviation": "ussc", - "recoveryServicesGeo": "scus", - "timeDifference": "-6:00", - "timeZone": "Central Standard Time" - }, - "southeastasia": { - "abbreviation": "asse", - "recoveryServicesGeo": "sea", - "timeDifference": "+8:00", - "timeZone": "Singapore Standard Time" - }, - "southindia": { - "abbreviation": "ins", - "recoveryServicesGeo": "ins", - "timeDifference": "+5:30", - "timeZone": "India Standard Time" - }, - "swedencentral": { - "abbreviation": "sec", - "recoveryServicesGeo": "sdc", - "timeDifference": "+1:00", - "timeZone": "Central Europe Standard Time" - }, - "switzerlandnorth": { - "abbreviation": "chn", - "recoveryServicesGeo": "szn", - "timeDifference": "+1:00", - "timeZone": "Central Europe Standard Time" - }, - "switzerlandwest": { - "abbreviation": "chw", - "recoveryServicesGeo": "szw", - "timeDifference": "+1:00", - "timeZone": "Central Europe Standard Time" - }, - "uaecentral": { - "abbreviation": "aec", - "recoveryServicesGeo": "uac", - "timeDifference": "+3:00", - "timeZone": "Arabian Standard Time" - }, - "uaenorth": { - "abbreviation": "aen", - "recoveryServicesGeo": "uan", - "timeDifference": "+3:00", - "timeZone": "Arabian Standard Time" - }, - "uksouth": { - "abbreviation": "uks", - "recoveryServicesGeo": "uks", - "timeDifference": "0:00", - "timeZone": "GMT Standard Time" - }, - "ukwest": { - "abbreviation": "ukw", - "recoveryServicesGeo": "ukw", - "timeDifference": "0:00", - "timeZone": "GMT Standard Time" - }, - "westcentralus": { - "abbreviation": "uswc", - "recoveryServicesGeo": "wcus", - "timeDifference": "-7:00", - "timeZone": "Mountain Standard Time" - }, - "westeurope": { - "abbreviation": "euw", - "recoveryServicesGeo": "we", - "timeDifference": "+1:00", - "timeZone": "Central Europe Standard Time" - }, - "westindia": { - "abbreviation": "inw", - "recoveryServicesGeo": "inw", - "timeDifference": "+5:30", - "timeZone": "India Standard Time" - }, - "westus": { - "abbreviation": "usw", - "recoveryServicesGeo": "wus", - "timeDifference": "-8:00", - "timeZone": "Pacific Standard Time" - }, - "westus2": { - "abbreviation": "usw2", - "recoveryServicesGeo": "wus2", - "timeDifference": "-8:00", - "timeZone": "Pacific Standard Time" - }, - "westus3": { - "abbreviation": "usw3", - "recoveryServicesGeo": "wus3", - "timeDifference": "-7:00", - "timeZone": "Mountain Standard Time" - } - }, - "AzureUSGovernment": { - "usdodcentral": { - "abbreviation": "dodc", - "recoveryServicesGeo": "udc", - "timeDifference": "-6:00", - "timeZone": "Central Standard Time" - }, - "usdodeast": { - "abbreviation": "dode", - "recoveryServicesGeo": "ude", - "timeDifference": "-5:00", - "timeZone": "Eastern Standard Time" - }, - "usgovarizona": { - "abbreviation": "az", - "recoveryServicesGeo": "uga", - "timeDifference": "-7:00", - "timeZone": "Mountain Standard Time" - }, - "usgovtexas": { - "abbreviation": "tx", - "recoveryServicesGeo": "ugt", - "timeDifference": "-6:00", - "timeZone": "Central Standard Time" - }, - "usgovvirginia": { - "abbreviation": "va", - "recoveryServicesGeo": "ugv", - "timeDifference": "-5:00", - "timeZone": "Eastern Standard Time" - } - }, - "USNat": { - "usnateast": { - "abbreviation": "east", - "recoveryServicesGeo": "exe", - "timeDifference": "-5:00", - "timeZone": "Eastern Standard Time" - }, - "usnatwest": { - "abbreviation": "west", - "recoveryServicesGeo": "exw", - "timeDifference": "-8:00", - "timeZone": "Pacific Standard Time" - } - }, - "USSec": { - "usseceast": { - "abbreviation": "east", - "recoveryServicesGeo": "rxe", - "timeDifference": "-5:00", - "timeZone": "Eastern Standard Time" - }, - "ussecwest": { - "abbreviation": "west", - "recoveryServicesGeo": "rxw", - "timeDifference": "-8:00", - "timeZone": "Pacific Standard Time" - } - } - }, - "$fxv#1": { - "actionGroups": "ag", - "applicationGroups": "vdag", - "applicationInsights": "appi", - "appServicePlans": "asp", - "automationAccounts": "aa", - "availabilitySets": "avail", - "azureFirewalls": "afw", - "bastionHosts": "bas", - "computeGallieries": "cg", - "dataCollectionEndpoints": "dce", - "dataCollectionRuleAssociations": "dcra", - "dataCollectionRules": "dcr", - "diagnosticSettings": "diag", - "diskAccesses": "da", - "diskEncryptionSets": "des", - "disks": "disk", - "firewallPolicies": "afwp", - "functionApps": "func", - "hostPools": "vdpool", - "ipConfigurations": "ipconf", - "keyVaults": "kv", - "logAnalyticsWorkspaces": "log", - "netAppAccounts": "naa", - "netAppCapacityPools": "nacp", - "networkInterfaces": "nic", - "networkSecurityGroups": "nsg", - "networkWatchers": "nw", - "privateEndpoints": "pe", - "privateLinkScopes": "pls", - "publicIPAddresses": "pip", - "recoveryServicesVaults": "rsv", - "remoteApplicationGroups": "vdag", - "resourceGroups": "rg", - "routeTables": "rt", - "scalingPlans": "vdscaling", - "storageAccounts": "st", - "subnets": "snet", - "userAssignedIdentities": "id", - "virtualMachines": "vm", - "virtualNetworks": "vnet", - "workspaces": "vdws" - }, - "locations": "[variables('$fxv#0')[environment().name]]", - "locationAbbreviation": "[variables('locations')[parameters('location')].abbreviation]", - "resourceAbbreviations": "[variables('$fxv#1')]", - "namingConvention": "[format('{0}-{1}{2}-{3}-{4}-{5}', toLower(parameters('resourcePrefix')), if(empty(parameters('stampIndex')), '', format('{0}-', parameters('stampIndex'))), parameters('tokens').resource, parameters('networkName'), variables('locationAbbreviation'), parameters('environmentAbbreviation'))]", - "namingConvention_Service": "[format('{0}-{1}{2}-{3}-{4}-{5}-{6}', toLower(parameters('resourcePrefix')), if(empty(parameters('stampIndex')), '', format('{0}-', parameters('stampIndex'))), parameters('tokens').resource, parameters('networkName'), parameters('tokens').service, variables('locationAbbreviation'), parameters('environmentAbbreviation'))]", - "names": { - "actionGroup": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').actionGroups)]", - "applicationGroup": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').applicationGroups)]", - "applicationInsights": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').applicationInsights)]", - "appServicePlan": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').appServicePlans)]", - "automationAccount": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').automationAccounts)]", - "automationAccountDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, variables('resourceAbbreviations').automationAccounts)]", - "automationAccountNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, variables('resourceAbbreviations').automationAccounts)]", - "automationAccountPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, variables('resourceAbbreviations').automationAccounts)]", - "availabilitySet": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').availabilitySets)]", - "azureFirewall": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').azureFirewalls)]", - "azureFirewallClientPublicIPAddress": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').publicIPAddresses), parameters('tokens').service, format('client-{0}', variables('resourceAbbreviations').azureFirewalls))]", - "azureFirewallClientPublicIPAddressDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-client-{1}', variables('resourceAbbreviations').publicIPAddresses, variables('resourceAbbreviations').azureFirewalls))]", - "azureFirewallDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, variables('resourceAbbreviations').azureFirewalls)]", - "azureFirewallManagementPublicIPAddress": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').publicIPAddresses), parameters('tokens').service, format('mgmt-{0}', variables('resourceAbbreviations').azureFirewalls))]", - "azureFirewallManagementPublicIPAddressDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-mgmt-{1}', variables('resourceAbbreviations').publicIPAddresses, variables('resourceAbbreviations').azureFirewalls))]", - "azureFirewallPolicy": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').firewallPolicies)]", - "bastionHost": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').bastionHosts)]", - "bastionHostNetworkSecurityGroup": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkSecurityGroups), parameters('tokens').service, variables('resourceAbbreviations').bastionHosts)]", - "bastionHostDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, variables('resourceAbbreviations').bastionHosts)]", - "bastionHostPublicIPAddress": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').publicIPAddresses), parameters('tokens').service, variables('resourceAbbreviations').bastionHosts)]", - "bastionHostPublicIPAddressDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-{1}', variables('resourceAbbreviations').publicIPAddresses, variables('resourceAbbreviations').bastionHosts))]", - "computeGallery": "[replace(replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').computeGallieries), '-', '_')]", - "dataCollectionEndpoint": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').dataCollectionEndpoints)]", - "dataCollectionRuleAssociation": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').dataCollectionRuleAssociations)]", - "dataCollectionRule": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').dataCollectionRules)]", - "diskAccess": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').diskAccesses)]", - "diskAccessNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, variables('resourceAbbreviations').diskAccesses)]", - "diskAccessPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, variables('resourceAbbreviations').diskAccesses)]", - "diskEncryptionSet": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').diskEncryptionSets)]", - "functionApp": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').functionApps)]", - "functionAppNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-{1}', variables('resourceAbbreviations').functionApps, parameters('tokens').service))]", - "functionAppPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-{1}', variables('resourceAbbreviations').functionApps, parameters('tokens').service))]", - "hostPool": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').hostPools)]", - "hostPoolDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, variables('resourceAbbreviations').hostPools)]", - "hostPoolNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, variables('resourceAbbreviations').hostPools)]", - "hostPoolPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, variables('resourceAbbreviations').hostPools)]", - "keyVault": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').keyVaults), '-', ''), parameters('networkName'), parameters('networkShortName'))]", - "keyVaultDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-{1}', variables('resourceAbbreviations').keyVaults, parameters('tokens').service))]", - "keyVaultNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-{1}', variables('resourceAbbreviations').keyVaults, parameters('tokens').service))]", - "keyVaultPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-{1}', variables('resourceAbbreviations').keyVaults, parameters('tokens').service))]", - "logAnalyticsWorkspace": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').logAnalyticsWorkspaces)]", - "logAnalyticsWorkspaceDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, variables('resourceAbbreviations').logAnalyticsWorkspaces)]", - "netAppAccountCapacityPool": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').netAppCapacityPools)]", - "netAppAccount": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').netAppAccounts)]", - "networkSecurityGroup": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').networkSecurityGroups)]", - "networkSecurityGroupDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, variables('resourceAbbreviations').networkSecurityGroups)]", - "networkWatcher": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').networkWatchers)]", - "privateLinkScope": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').privateLinkScopes)]", - "privateLinkScopeNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, variables('resourceAbbreviations').privateLinkScopes)]", - "privateLinkScopePrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, variables('resourceAbbreviations').privateLinkScopes)]", - "recoveryServicesVault": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').recoveryServicesVaults)]", - "recoveryServicesVaultNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, variables('resourceAbbreviations').recoveryServicesVaults)]", - "recoveryServicesVaultPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, variables('resourceAbbreviations').recoveryServicesVaults)]", - "resourceGroup": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').resourceGroups)]", - "routeTable": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').routeTables)]", - "scalingPlan": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').scalingPlans)]", - "scalingPlanDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, variables('resourceAbbreviations').scalingPlans)]", - "storageAccount": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').storageAccounts), parameters('networkName'), parameters('networkShortName'))]", - "storageAccountDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').storageAccounts))]", - "storageAccountBlobNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-blob', variables('resourceAbbreviations').storageAccounts))]", - "storageAccountFileNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-file', variables('resourceAbbreviations').storageAccounts))]", - "storageAccountQueueNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-queue', variables('resourceAbbreviations').storageAccounts))]", - "storageAccountTableNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-table', variables('resourceAbbreviations').storageAccounts))]", - "storageAccountBlobPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-blob', variables('resourceAbbreviations').storageAccounts))]", - "storageAccountFilePrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-file', variables('resourceAbbreviations').storageAccounts))]", - "storageAccountQueuePrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-queue', variables('resourceAbbreviations').storageAccounts))]", - "storageAccountTablePrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-table', variables('resourceAbbreviations').storageAccounts))]", - "subnet": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').subnets)]", - "userAssignedIdentity": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').userAssignedIdentities)]", - "virtualMachine": "[replace(replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').virtualMachines), parameters('environmentAbbreviation'), first(parameters('environmentAbbreviation'))), parameters('networkName'), ''), '-', '')]", - "virtualMachineDisk": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').disks), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').virtualMachines))]", - "virtualMachineNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').virtualMachines))]", - "virtualNetwork": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').virtualNetworks)]", - "virtualNetworkDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, variables('resourceAbbreviations').virtualNetworks)]", - "workspaceFeed": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').workspaces), format('-{0}', parameters('stampIndex')), '')]", - "workspaceFeedDiagnosticSetting": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", - "workspaceFeedNetworkInterface": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", - "workspaceFeedPrivateEndpoint": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", - "workspaceGlobal": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').workspaces), format('-{0}', parameters('stampIndex')), '')]", - "workspaceGlobalDiagnosticSetting": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", - "workspaceGlobalNetworkInterface": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", - "workspaceGlobalPrivateEndpoint": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]" - } - }, - "resources": [], - "outputs": { - "locations": { - "type": "object", - "value": "[variables('locations')]" - }, - "names": { - "type": "object", - "value": "[variables('names')]" - }, - "resourceAbbreviations": { - "type": "object", - "value": "[variables('resourceAbbreviations')]" - }, - "tokens": { - "type": "object", - "value": "[parameters('tokens')]" - } - } - } - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))]", - "location": "[deployment().location]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "additionalSubnets": { - "value": "[union(variables('subnets').avdControlPlane, variables('subnets').azureNetAppFiles, variables('subnets').functionApp)]" - }, - "deployActivityLogDiagnosticSetting": { - "value": "[parameters('deployActivityLogDiagnosticSetting')]" - }, - "deployDefender": { - "value": "[parameters('deployDefender')]" - }, - "deploymentNameSuffix": { - "value": "[parameters('deploymentNameSuffix')]" - }, - "deployNetworkWatcher": { - "value": "[parameters('deployNetworkWatcher')]" - }, - "deployPolicy": { - "value": "[parameters('deployPolicy')]" - }, - "emailSecurityContact": { - "value": "[parameters('emailSecurityContact')]" - }, - "environmentAbbreviation": { - "value": "[parameters('environmentAbbreviation')]" - }, - "firewallResourceId": { - "value": "[parameters('hubAzureFirewallResourceId')]" - }, - "hubVirtualNetworkResourceId": { - "value": "[parameters('hubVirtualNetworkResourceId')]" - }, - "identifier": { - "value": "[parameters('identifier')]" - }, - "location": { - "value": "[parameters('locationVirtualMachines')]" - }, - "logAnalyticsWorkspaceResourceId": { - "value": "[parameters('operationsLogAnalyticsWorkspaceResourceId')]" - }, - "policy": { - "value": "[parameters('policy')]" - }, - "stampIndex": { - "value": "[string(parameters('stampIndex'))]" - }, - "subnetName": { - "value": "AvdSessionHosts" - }, - "subnetAddressPrefix": { - "value": "[parameters('subnetAddressPrefixes')[0]]" - }, - "tags": { - "value": "[parameters('tags')]" - }, - "virtualNetworkAddressPrefix": { - "value": "[parameters('virtualNetworkAddressPrefixes')[0]]" - }, - "workloadName": { - "value": "avd" - }, - "workloadShortName": { - "value": "avd" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "9467495710159860030" - } - }, - "parameters": { - "additionalSubnets": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "An array of additional subnets to support the tier3 workload." - } - }, - "deployActivityLogDiagnosticSetting": { - "type": "bool", - "metadata": { - "description": "Choose whether to deploy a diagnostic setting for the Activity Log." - } - }, - "deployDefender": { - "type": "bool", - "metadata": { - "description": "Choose whether to deploy Defender for Cloud." - } - }, - "deploymentNameSuffix": { - "type": "string", - "defaultValue": "[utcNow()]", - "metadata": { - "description": "The suffix to append to the deployment name. It defaults to the current UTC date and time." - } - }, - "deployNetworkWatcher": { - "type": "bool", - "metadata": { - "description": "Choose whether to deploy Network Watcher for the deployment location." - } - }, - "deployPolicy": { - "type": "bool", - "metadata": { - "description": "Choose whether to deploy a policy assignment." - } - }, - "emailSecurityContact": { - "type": "string", - "metadata": { - "description": "The email address to use for Defender for Cloud notifications." - } - }, - "environmentAbbreviation": { - "type": "string", - "defaultValue": "dev", - "allowedValues": [ - "dev", - "prod", - "test" - ], - "metadata": { - "description": "The abbreviation for the environment." - } - }, - "firewallResourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the Azure Firewall in the HUB." - } - }, - "hubVirtualNetworkResourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the HUB Virtual Network." - } - }, - "identifier": { - "type": "string", - "maxLength": 3, - "metadata": { - "description": "The identifier for the resource names. This value should represent the workload, project, or business unit." - } - }, - "keyVaultDiagnosticsLogs": { - "type": "array", - "defaultValue": [ - { - "category": "AuditEvent", - "enabled": true - }, - { - "category": "AzurePolicyEvaluationDetails", - "enabled": true - } - ], - "metadata": { - "description": "An array of Key Vault Diagnostic Logs categories to collect. See \"https://learn.microsoft.com/en-us/azure/key-vault/general/logging?tabs=Vault\" for valid values." - } - }, - "location": { - "type": "string", - "defaultValue": "[deployment().location]", - "metadata": { - "description": "The location for the deployment. It defaults to the location of the deployment." - } - }, - "logAnalyticsWorkspaceResourceId": { - "type": "string", - "metadata": { - "description": "The resource ID of the Log Analytics Workspace to use for log storage." - } - }, - "logStorageSkuName": { - "type": "string", - "defaultValue": "Standard_GRS", - "metadata": { - "description": "The Storage Account SKU to use for log storage. It defaults to \"Standard_GRS\". See https://docs.microsoft.com/en-us/rest/api/storagerp/srp_sku_types for valid settings." - } - }, - "networkSecurityGroupDiagnosticsLogs": { - "type": "array", - "defaultValue": [ - { - "category": "NetworkSecurityGroupEvent", - "enabled": true - }, - { - "category": "NetworkSecurityGroupRuleCounter", - "enabled": true - } - ], - "metadata": { - "description": "An array of Network Security Group diagnostic logs to apply to the workload Virtual Network. See https://docs.microsoft.com/en-us/azure/virtual-network/virtual-network-nsg-manage-log#log-categories for valid settings." - } - }, - "networkSecurityGroupDiagnosticsMetrics": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "The metrics to monitor for the Network Security Group." - } - }, - "networkSecurityGroupRules": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "The rules to apply to the Network Security Group." - } - }, - "policy": { - "type": "string", - "defaultValue": "NISTRev4", - "metadata": { - "description": "The policy to assign to the workload." - } - }, - "stampIndex": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The stamp index allows for multiple AVD stamps with the same business unit or project to support different use cases." - } - }, - "subnetAddressPrefix": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The address prefix for the workload subnet." - } - }, - "subnetName": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The custom name for the workload subnet if the naming convention is not desired. Subnets are child resources and do not require a unique name between virtual networks, only within the same virtual network." - } - }, - "tags": { - "type": "object", - "defaultValue": {}, - "metadata": { - "description": "The tags to apply to the resources." - } - }, - "virtualNetworkAddressPrefix": { - "type": "string", - "defaultValue": "", - "metadata": { - "description": "The address prefix for the workload Virtual Network." - } - }, - "virtualNetworkDiagnosticsLogs": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "The diagnostic logs to apply to the workload Virtual Network." - } - }, - "virtualNetworkDiagnosticsMetrics": { - "type": "array", - "defaultValue": [], - "metadata": { - "description": "The metrics to monitor for the workload Virtual Network." - } - }, - "workloadName": { - "type": "string", - "defaultValue": "tier3", - "minLength": 1, - "maxLength": 10, - "metadata": { - "description": "The name for the workload." - } - }, - "workloadShortName": { - "type": "string", - "defaultValue": "t3", - "minLength": 1, - "maxLength": 3, - "metadata": { - "description": "The short name for the workload." - } - } - }, - "variables": { - "hubResourceGroupName": "[split(parameters('hubVirtualNetworkResourceId'), '/')[4]]", - "hubSubscriptionId": "[split(parameters('hubVirtualNetworkResourceId'), '/')[2]]", - "subscriptionId": "[subscription().subscriptionId]" - }, - "resources": [ - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))]", - "location": "[deployment().location]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "deploymentNameSuffix": { - "value": "[parameters('deploymentNameSuffix')]" - }, - "environmentAbbreviation": { - "value": "[parameters('environmentAbbreviation')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "networks": { - "value": [ - { - "name": "[parameters('workloadName')]", - "shortName": "[parameters('workloadShortName')]", - "deployUniqueResources": false, - "subscriptionId": "[variables('subscriptionId')]", - "nsgDiagLogs": "[parameters('networkSecurityGroupDiagnosticsLogs')]", - "nsgDiagMetrics": "[parameters('networkSecurityGroupDiagnosticsMetrics')]", - "nsgRules": "[parameters('networkSecurityGroupRules')]", - "vnetAddressPrefix": "[parameters('virtualNetworkAddressPrefix')]", - "vnetDiagLogs": "[parameters('virtualNetworkDiagnosticsLogs')]", - "vnetDiagMetrics": "[parameters('virtualNetworkDiagnosticsMetrics')]", - "subnetAddressPrefix": "[parameters('subnetAddressPrefix')]" - } - ] - }, - "resourcePrefix": { - "value": "[parameters('identifier')]" - }, - "stampIndex": { - "value": "[parameters('stampIndex')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "10924801470287352076" - } - }, - "parameters": { - "deploymentNameSuffix": { - "type": "string" - }, - "environmentAbbreviation": { - "type": "string" - }, - "location": { - "type": "string" - }, - "networks": { - "type": "array" - }, - "resourcePrefix": { - "type": "string" - }, - "stampIndex": { - "type": "string", - "defaultValue": "" - } - }, - "variables": { - "$fxv#0": "1.0.0", - "environmentName": { - "dev": "Development", - "prod": "Production", - "test": "Test" - }, - "mlzTags": { - "environment": "[variables('environmentName')[parameters('environmentAbbreviation')]]", - "landingZoneName": "MissionLandingZone", - "landingZoneVersion": "[variables('$fxv#0')]", - "resourcePrefix": "[parameters('resourcePrefix')]" - } - }, - "resources": [ - { - "copy": { - "name": "namingConventions", - "count": "[length(parameters('networks'))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('naming-convention-{0}-{1}', parameters('networks')[copyIndex()].shortName, parameters('deploymentNameSuffix'))]", - "location": "[deployment().location]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "environmentAbbreviation": { - "value": "[parameters('environmentAbbreviation')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "networkName": { - "value": "[parameters('networks')[copyIndex()].name]" - }, - "networkShortName": { - "value": "[parameters('networks')[copyIndex()].shortName]" - }, - "resourcePrefix": { - "value": "[parameters('resourcePrefix')]" - }, - "stampIndex": { - "value": "[parameters('stampIndex')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "11992572431768878515" - } - }, - "parameters": { - "environmentAbbreviation": { - "type": "string" - }, - "location": { - "type": "string" - }, - "networkName": { - "type": "string" - }, - "networkShortName": { - "type": "string" - }, - "resourcePrefix": { - "type": "string" - }, - "stampIndex": { - "type": "string", - "defaultValue": "" - }, - "tokens": { - "type": "object", - "defaultValue": { - "purpose": "purpose_token", - "resource": "resource_token", - "service": "service_token" - } - } - }, - "variables": { - "$fxv#0": { - "AzureChina": { - "chinaeast": { - "abbreviation": "cne", - "recoveryServicesGeo": "sha", - "timeDifference": "+8:00", - "timeZone": "China Standard Time" - }, - "chinaeast2": { - "abbreviation": "cne2", - "recoveryServicesGeo": "sha2", - "timeDifference": "+8:00", - "timeZone": "China Standard Time" - }, - "chinanorth": { - "abbreviation": "cnn", - "recoveryServicesGeo": "bjb", - "timeDifference": "+8:00", - "timeZone": "China Standard Time" - }, - "chinanorth2": { - "abbreviation": "cnn2", - "recoveryServicesGeo": "bjb2", - "timeDifference": "+8:00", - "timeZone": "China Standard Time" - } - }, - "AzureCloud": { - "australiacentral": { - "abbreviation": "auc", - "recoveryServicesGeo": "acl", - "timeDifference": "+10:00", - "timeZone": "AUS Eastern Standard Time" - }, - "australiacentral2": { - "abbreviation": "auc2", - "recoveryServicesGeo": "acl2", - "timeDifference": "+10:00", - "timeZone": "AUS Eastern Standard Time" - }, - "australiaeast": { - "abbreviation": "aue", - "recoveryServicesGeo": "ae", - "timeDifference": "+10:00", - "timeZone": "AUS Eastern Standard Time" - }, - "australiasoutheast": { - "abbreviation": "ause", - "recoveryServicesGeo": "ase", - "timeDifference": "+10:00", - "timeZone": "AUS Eastern Standard Time" - }, - "brazilsouth": { - "abbreviation": "brs", - "recoveryServicesGeo": "brs", - "timeDifference": "-3:00", - "timeZone": "E. South America Standard Time" - }, - "brazilsoutheast": { - "abbreviation": "brse", - "recoveryServicesGeo": "bse", - "timeDifference": "-3:00", - "timeZone": "E. South America Standard Time" - }, - "canadacentral": { - "abbreviation": "cac", - "recoveryServicesGeo": "cnc", - "timeDifference": "-5:00", - "timeZone": "Eastern Standard Time" - }, - "canadaeast": { - "abbreviation": "cae", - "recoveryServicesGeo": "cne", - "timeDifference": "-5:00", - "timeZone": "Eastern Standard Time" - }, - "centralindia": { - "abbreviation": "inc", - "recoveryServicesGeo": "inc", - "timeDifference": "+5:30", - "timeZone": "India Standard Time" - }, - "centralus": { - "abbreviation": "usc", - "recoveryServicesGeo": "cus", - "timeDifference": "-6:00", - "timeZone": "Central Standard Time" - }, - "eastasia": { - "abbreviation": "ase", - "recoveryServicesGeo": "ea", - "timeDifference": "+8:00", - "timeZone": "China Standard Time" - }, - "eastus": { - "abbreviation": "use", - "recoveryServicesGeo": "eus", - "timeDifference": "-5:00", - "timeZone": "Eastern Standard Time" - }, - "eastus2": { - "abbreviation": "use2", - "recoveryServicesGeo": "eus2", - "timeDifference": "-5:00", - "timeZone": "Eastern Standard Time" - }, - "francecentral": { - "abbreviation": "frc", - "recoveryServicesGeo": "frc", - "timeDifference": "+1:00", - "timeZone": "Central Europe Standard Time" - }, - "francesouth": { - "abbreviation": "frs", - "recoveryServicesGeo": "frs", - "timeDifference": "+1:00", - "timeZone": "Central Europe Standard Time" - }, - "germanynorth": { - "abbreviation": "den", - "recoveryServicesGeo": "gn", - "timeDifference": "+1:00", - "timeZone": "Central Europe Standard Time" - }, - "germanywestcentral": { - "abbreviation": "dewc", - "recoveryServicesGeo": "gwc", - "timeDifference": "+1:00", - "timeZone": "Central Europe Standard Time" - }, - "israelcentral": { - "abbreviation": "ilc", - "recoveryServicesGeo": "ilc", - "timeDifference": "+2:00", - "timeZone": "Israel Standard Time" - }, - "italynorth": { - "abbreviation": "itn", - "recoveryServicesGeo": "itn", - "timeDifference": "+1:00", - "timeZone": "Central Europe Standard Time" - }, - "japaneast": { - "abbreviation": "jpe", - "recoveryServicesGeo": "jpe", - "timeDifference": "+9:00", - "timeZone": "Tokyo Standard Time" - }, - "japanwest": { - "abbreviation": "jpw", - "recoveryServicesGeo": "jpw", - "timeDifference": "+9:00", - "timeZone": "Tokyo Standard Time" - }, - "jioindiacentral": { - "abbreviation": "injc", - "recoveryServicesGeo": "jic", - "timeDifference": "+5:30", - "timeZone": "India Standard Time" - }, - "jioindiawest": { - "abbreviation": "injw", - "recoveryServicesGeo": "jiw", - "timeDifference": "+5:30", - "timeZone": "India Standard Time" - }, - "koreacentral": { - "abbreviation": "krc", - "recoveryServicesGeo": "krc", - "timeDifference": "+9:00", - "timeZone": "Korea Standard Time" - }, - "koreasouth": { - "abbreviation": "krs", - "recoveryServicesGeo": "krs", - "timeDifference": "+9:00", - "timeZone": "Korea Standard Time" - }, - "northcentralus": { - "abbreviation": "usnc", - "recoveryServicesGeo": "ncus", - "timeDifference": "-6:00", - "timeZone": "Central Standard Time" - }, - "northeurope": { - "abbreviation": "eun", - "recoveryServicesGeo": "ne", - "timeDifference": "0:00", - "timeZone": "GMT Standard Time" - }, - "norwayeast": { - "abbreviation": "noe", - "recoveryServicesGeo": "nwe", - "timeDifference": "+1:00", - "timeZone": "Central Europe Standard Time" - }, - "norwaywest": { - "abbreviation": "now", - "recoveryServicesGeo": "nww", - "timeDifference": "+1:00", - "timeZone": "Central Europe Standard Time" - }, - "polandcentral": { - "abbreviation": "plc", - "recoveryServicesGeo": "plc", - "timeDifference": "+1:00", - "timeZone": "Central Europe Standard Time" - }, - "qatarcentral": { - "abbreviation": "qac", - "recoveryServicesGeo": "qac", - "timeDifference": "+3:00", - "timeZone": "Arabian Standard Time" - }, - "southafricanorth": { - "abbreviation": "zan", - "recoveryServicesGeo": "san", - "timeDifference": "+2:00", - "timeZone": "South Africa Standard Time" - }, - "southafricawest": { - "abbreviation": "zaw", - "recoveryServicesGeo": "saw", - "timeDifference": "+2:00", - "timeZone": "South Africa Standard Time" - }, - "southcentralus": { - "abbreviation": "ussc", - "recoveryServicesGeo": "scus", - "timeDifference": "-6:00", - "timeZone": "Central Standard Time" - }, - "southeastasia": { - "abbreviation": "asse", - "recoveryServicesGeo": "sea", - "timeDifference": "+8:00", - "timeZone": "Singapore Standard Time" - }, - "southindia": { - "abbreviation": "ins", - "recoveryServicesGeo": "ins", - "timeDifference": "+5:30", - "timeZone": "India Standard Time" - }, - "swedencentral": { - "abbreviation": "sec", - "recoveryServicesGeo": "sdc", - "timeDifference": "+1:00", - "timeZone": "Central Europe Standard Time" - }, - "switzerlandnorth": { - "abbreviation": "chn", - "recoveryServicesGeo": "szn", - "timeDifference": "+1:00", - "timeZone": "Central Europe Standard Time" - }, - "switzerlandwest": { - "abbreviation": "chw", - "recoveryServicesGeo": "szw", - "timeDifference": "+1:00", - "timeZone": "Central Europe Standard Time" - }, - "uaecentral": { - "abbreviation": "aec", - "recoveryServicesGeo": "uac", - "timeDifference": "+3:00", - "timeZone": "Arabian Standard Time" - }, - "uaenorth": { - "abbreviation": "aen", - "recoveryServicesGeo": "uan", - "timeDifference": "+3:00", - "timeZone": "Arabian Standard Time" - }, - "uksouth": { - "abbreviation": "uks", - "recoveryServicesGeo": "uks", - "timeDifference": "0:00", - "timeZone": "GMT Standard Time" - }, - "ukwest": { - "abbreviation": "ukw", - "recoveryServicesGeo": "ukw", - "timeDifference": "0:00", - "timeZone": "GMT Standard Time" - }, - "westcentralus": { - "abbreviation": "uswc", - "recoveryServicesGeo": "wcus", - "timeDifference": "-7:00", - "timeZone": "Mountain Standard Time" - }, - "westeurope": { - "abbreviation": "euw", - "recoveryServicesGeo": "we", - "timeDifference": "+1:00", - "timeZone": "Central Europe Standard Time" - }, - "westindia": { - "abbreviation": "inw", - "recoveryServicesGeo": "inw", - "timeDifference": "+5:30", - "timeZone": "India Standard Time" - }, - "westus": { - "abbreviation": "usw", - "recoveryServicesGeo": "wus", - "timeDifference": "-8:00", - "timeZone": "Pacific Standard Time" - }, - "westus2": { - "abbreviation": "usw2", - "recoveryServicesGeo": "wus2", - "timeDifference": "-8:00", - "timeZone": "Pacific Standard Time" - }, - "westus3": { - "abbreviation": "usw3", - "recoveryServicesGeo": "wus3", - "timeDifference": "-7:00", - "timeZone": "Mountain Standard Time" - } - }, - "AzureUSGovernment": { - "usdodcentral": { - "abbreviation": "dodc", - "recoveryServicesGeo": "udc", - "timeDifference": "-6:00", - "timeZone": "Central Standard Time" - }, - "usdodeast": { - "abbreviation": "dode", - "recoveryServicesGeo": "ude", - "timeDifference": "-5:00", - "timeZone": "Eastern Standard Time" - }, - "usgovarizona": { - "abbreviation": "az", - "recoveryServicesGeo": "uga", - "timeDifference": "-7:00", - "timeZone": "Mountain Standard Time" - }, - "usgovtexas": { - "abbreviation": "tx", - "recoveryServicesGeo": "ugt", - "timeDifference": "-6:00", - "timeZone": "Central Standard Time" - }, - "usgovvirginia": { - "abbreviation": "va", - "recoveryServicesGeo": "ugv", - "timeDifference": "-5:00", - "timeZone": "Eastern Standard Time" - } - }, - "USNat": { - "usnateast": { - "abbreviation": "east", - "recoveryServicesGeo": "exe", - "timeDifference": "-5:00", - "timeZone": "Eastern Standard Time" - }, - "usnatwest": { - "abbreviation": "west", - "recoveryServicesGeo": "exw", - "timeDifference": "-8:00", - "timeZone": "Pacific Standard Time" - } - }, - "USSec": { - "usseceast": { - "abbreviation": "east", - "recoveryServicesGeo": "rxe", - "timeDifference": "-5:00", - "timeZone": "Eastern Standard Time" - }, - "ussecwest": { - "abbreviation": "west", - "recoveryServicesGeo": "rxw", - "timeDifference": "-8:00", - "timeZone": "Pacific Standard Time" - } - } - }, - "$fxv#1": { - "actionGroups": "ag", - "applicationGroups": "vdag", - "applicationInsights": "appi", - "appServicePlans": "asp", - "automationAccounts": "aa", - "availabilitySets": "avail", - "azureFirewalls": "afw", - "bastionHosts": "bas", - "computeGallieries": "cg", - "dataCollectionEndpoints": "dce", - "dataCollectionRuleAssociations": "dcra", - "dataCollectionRules": "dcr", - "diagnosticSettings": "diag", - "diskAccesses": "da", - "diskEncryptionSets": "des", - "disks": "disk", - "firewallPolicies": "afwp", - "functionApps": "func", - "hostPools": "vdpool", - "ipConfigurations": "ipconf", - "keyVaults": "kv", - "logAnalyticsWorkspaces": "log", - "netAppAccounts": "naa", - "netAppCapacityPools": "nacp", - "networkInterfaces": "nic", - "networkSecurityGroups": "nsg", - "networkWatchers": "nw", - "privateEndpoints": "pe", - "privateLinkScopes": "pls", - "publicIPAddresses": "pip", - "recoveryServicesVaults": "rsv", - "remoteApplicationGroups": "vdag", - "resourceGroups": "rg", - "routeTables": "rt", - "scalingPlans": "vdscaling", - "storageAccounts": "st", - "subnets": "snet", - "userAssignedIdentities": "id", - "virtualMachines": "vm", - "virtualNetworks": "vnet", - "workspaces": "vdws" - }, - "locations": "[variables('$fxv#0')[environment().name]]", - "locationAbbreviation": "[variables('locations')[parameters('location')].abbreviation]", - "resourceAbbreviations": "[variables('$fxv#1')]", - "namingConvention": "[format('{0}-{1}{2}-{3}-{4}-{5}', toLower(parameters('resourcePrefix')), if(empty(parameters('stampIndex')), '', format('{0}-', parameters('stampIndex'))), parameters('tokens').resource, parameters('networkName'), variables('locationAbbreviation'), parameters('environmentAbbreviation'))]", - "namingConvention_Service": "[format('{0}-{1}{2}-{3}-{4}-{5}-{6}', toLower(parameters('resourcePrefix')), if(empty(parameters('stampIndex')), '', format('{0}-', parameters('stampIndex'))), parameters('tokens').resource, parameters('networkName'), parameters('tokens').service, variables('locationAbbreviation'), parameters('environmentAbbreviation'))]", - "names": { - "actionGroup": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').actionGroups)]", - "applicationGroup": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').applicationGroups)]", - "applicationInsights": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').applicationInsights)]", - "appServicePlan": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').appServicePlans)]", - "automationAccount": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').automationAccounts)]", - "automationAccountDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, variables('resourceAbbreviations').automationAccounts)]", - "automationAccountNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, variables('resourceAbbreviations').automationAccounts)]", - "automationAccountPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, variables('resourceAbbreviations').automationAccounts)]", - "availabilitySet": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').availabilitySets)]", - "azureFirewall": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').azureFirewalls)]", - "azureFirewallClientPublicIPAddress": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').publicIPAddresses), parameters('tokens').service, format('client-{0}', variables('resourceAbbreviations').azureFirewalls))]", - "azureFirewallClientPublicIPAddressDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-client-{1}', variables('resourceAbbreviations').publicIPAddresses, variables('resourceAbbreviations').azureFirewalls))]", - "azureFirewallDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, variables('resourceAbbreviations').azureFirewalls)]", - "azureFirewallManagementPublicIPAddress": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').publicIPAddresses), parameters('tokens').service, format('mgmt-{0}', variables('resourceAbbreviations').azureFirewalls))]", - "azureFirewallManagementPublicIPAddressDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-mgmt-{1}', variables('resourceAbbreviations').publicIPAddresses, variables('resourceAbbreviations').azureFirewalls))]", - "azureFirewallPolicy": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').firewallPolicies)]", - "bastionHost": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').bastionHosts)]", - "bastionHostNetworkSecurityGroup": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkSecurityGroups), parameters('tokens').service, variables('resourceAbbreviations').bastionHosts)]", - "bastionHostDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, variables('resourceAbbreviations').bastionHosts)]", - "bastionHostPublicIPAddress": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').publicIPAddresses), parameters('tokens').service, variables('resourceAbbreviations').bastionHosts)]", - "bastionHostPublicIPAddressDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-{1}', variables('resourceAbbreviations').publicIPAddresses, variables('resourceAbbreviations').bastionHosts))]", - "computeGallery": "[replace(replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').computeGallieries), '-', '_')]", - "dataCollectionEndpoint": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').dataCollectionEndpoints)]", - "dataCollectionRuleAssociation": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').dataCollectionRuleAssociations)]", - "dataCollectionRule": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').dataCollectionRules)]", - "diskAccess": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').diskAccesses)]", - "diskAccessNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, variables('resourceAbbreviations').diskAccesses)]", - "diskAccessPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, variables('resourceAbbreviations').diskAccesses)]", - "diskEncryptionSet": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').diskEncryptionSets)]", - "functionApp": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').functionApps)]", - "functionAppNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-{1}', variables('resourceAbbreviations').functionApps, parameters('tokens').service))]", - "functionAppPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-{1}', variables('resourceAbbreviations').functionApps, parameters('tokens').service))]", - "hostPool": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').hostPools)]", - "hostPoolDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, variables('resourceAbbreviations').hostPools)]", - "hostPoolNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, variables('resourceAbbreviations').hostPools)]", - "hostPoolPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, variables('resourceAbbreviations').hostPools)]", - "keyVault": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').keyVaults), '-', ''), parameters('networkName'), parameters('networkShortName'))]", - "keyVaultDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-{1}', variables('resourceAbbreviations').keyVaults, parameters('tokens').service))]", - "keyVaultNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-{1}', variables('resourceAbbreviations').keyVaults, parameters('tokens').service))]", - "keyVaultPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-{1}', variables('resourceAbbreviations').keyVaults, parameters('tokens').service))]", - "logAnalyticsWorkspace": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').logAnalyticsWorkspaces)]", - "logAnalyticsWorkspaceDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, variables('resourceAbbreviations').logAnalyticsWorkspaces)]", - "netAppAccountCapacityPool": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').netAppCapacityPools)]", - "netAppAccount": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').netAppAccounts)]", - "networkSecurityGroup": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').networkSecurityGroups)]", - "networkSecurityGroupDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, variables('resourceAbbreviations').networkSecurityGroups)]", - "networkWatcher": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').networkWatchers)]", - "privateLinkScope": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').privateLinkScopes)]", - "privateLinkScopeNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, variables('resourceAbbreviations').privateLinkScopes)]", - "privateLinkScopePrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, variables('resourceAbbreviations').privateLinkScopes)]", - "recoveryServicesVault": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').recoveryServicesVaults)]", - "recoveryServicesVaultNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, variables('resourceAbbreviations').recoveryServicesVaults)]", - "recoveryServicesVaultPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, variables('resourceAbbreviations').recoveryServicesVaults)]", - "resourceGroup": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').resourceGroups)]", - "routeTable": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').routeTables)]", - "scalingPlan": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').scalingPlans)]", - "scalingPlanDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, variables('resourceAbbreviations').scalingPlans)]", - "storageAccount": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').storageAccounts), parameters('networkName'), parameters('networkShortName'))]", - "storageAccountDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').storageAccounts))]", - "storageAccountBlobNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-blob', variables('resourceAbbreviations').storageAccounts))]", - "storageAccountFileNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-file', variables('resourceAbbreviations').storageAccounts))]", - "storageAccountQueueNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-queue', variables('resourceAbbreviations').storageAccounts))]", - "storageAccountTableNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-table', variables('resourceAbbreviations').storageAccounts))]", - "storageAccountBlobPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-blob', variables('resourceAbbreviations').storageAccounts))]", - "storageAccountFilePrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-file', variables('resourceAbbreviations').storageAccounts))]", - "storageAccountQueuePrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-queue', variables('resourceAbbreviations').storageAccounts))]", - "storageAccountTablePrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-table', variables('resourceAbbreviations').storageAccounts))]", - "subnet": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').subnets)]", - "userAssignedIdentity": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').userAssignedIdentities)]", - "virtualMachine": "[replace(replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').virtualMachines), parameters('environmentAbbreviation'), first(parameters('environmentAbbreviation'))), parameters('networkName'), ''), '-', '')]", - "virtualMachineDisk": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').disks), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').virtualMachines))]", - "virtualMachineNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').virtualMachines))]", - "virtualNetwork": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').virtualNetworks)]", - "virtualNetworkDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, variables('resourceAbbreviations').virtualNetworks)]", - "workspaceFeed": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').workspaces), format('-{0}', parameters('stampIndex')), '')]", - "workspaceFeedDiagnosticSetting": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", - "workspaceFeedNetworkInterface": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", - "workspaceFeedPrivateEndpoint": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", - "workspaceGlobal": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').workspaces), format('-{0}', parameters('stampIndex')), '')]", - "workspaceGlobalDiagnosticSetting": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", - "workspaceGlobalNetworkInterface": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", - "workspaceGlobalPrivateEndpoint": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]" - } - }, - "resources": [], - "outputs": { - "locations": { - "type": "object", - "value": "[variables('locations')]" - }, - "names": { - "type": "object", - "value": "[variables('names')]" - }, - "resourceAbbreviations": { - "type": "object", - "value": "[variables('resourceAbbreviations')]" - }, - "tokens": { - "type": "object", - "value": "[parameters('tokens')]" - } - } - } - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('private-dns-zones-{0}', parameters('deploymentNameSuffix'))]", - "location": "[deployment().location]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "locations": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('naming-convention-{0}-{1}', parameters('networks')[0].shortName, parameters('deploymentNameSuffix'))), '2022-09-01').outputs.locations.value]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "2502930168536032010" - } - }, - "parameters": { - "locations": { - "type": "object" - } - }, - "variables": { - "copy": [ - { - "name": "privateDnsZoneNames_Backup", - "count": "[length(items(parameters('locations')))]", - "input": "[format('privatelink.{0}.backup.windowsazure.{1}', items(parameters('locations'))[copyIndex('privateDnsZoneNames_Backup')].value.recoveryServicesGeo, coalesce(variables('privateDnsZoneSuffixes_Backup')[environment().name], variables('cloudSuffix')))]" - } - ], - "cloudSuffix": "[replace(replace(environment().resourceManager, 'https://management.', ''), '/', '')]", - "privateDnsZoneNames": "[union(createArray(format('privatelink.agentsvc.azure-automation.{0}', coalesce(variables('privateDnsZoneSuffixes_AzureAutomation')[environment().name], variables('cloudSuffix'))), format('privatelink.azure-automation.{0}', coalesce(variables('privateDnsZoneSuffixes_AzureAutomation')[environment().name], variables('cloudSuffix'))), format('privatelink.{0}', coalesce(variables('privateDnsZoneSuffixes_AzureWebSites')[environment().name], format('appservice.{0}', variables('cloudSuffix')))), format('scm.privatelink.{0}', coalesce(variables('privateDnsZoneSuffixes_AzureWebSites')[environment().name], format('appservice.{0}', variables('cloudSuffix')))), format('privatelink.wvd.{0}', coalesce(variables('privateDnsZoneSuffixes_AzureVirtualDesktop')[environment().name], variables('cloudSuffix'))), format('privatelink-global.wvd.{0}', coalesce(variables('privateDnsZoneSuffixes_AzureVirtualDesktop')[environment().name], variables('cloudSuffix'))), format('privatelink.file.{0}', environment().suffixes.storage), format('privatelink.queue.{0}', environment().suffixes.storage), format('privatelink.table.{0}', environment().suffixes.storage), format('privatelink.blob.{0}', environment().suffixes.storage), format('privatelink{0}', replace(environment().suffixes.keyvaultDns, 'vault', 'vaultcore')), format('privatelink.monitor.{0}', coalesce(variables('privateDnsZoneSuffixes_Monitor')[environment().name], variables('cloudSuffix'))), format('privatelink.ods.opinsights.{0}', coalesce(variables('privateDnsZoneSuffixes_Monitor')[environment().name], variables('cloudSuffix'))), format('privatelink.oms.opinsights.{0}', coalesce(variables('privateDnsZoneSuffixes_Monitor')[environment().name], variables('cloudSuffix')))), variables('privateDnsZoneNames_Backup'))]", - "privateDnsZoneSuffixes_AzureAutomation": { - "AzureCloud": "net", - "AzureUSGovernment": "us", - "USNat": null, - "USSec": null - }, - "privateDnsZoneSuffixes_AzureVirtualDesktop": { - "AzureCloud": "microsoft.com", - "AzureUSGovernment": "azure.us", - "USNat": null, - "USSec": null - }, - "privateDnsZoneSuffixes_AzureWebSites": { - "AzureCloud": "azurewebsites.net", - "AzureUSGovernment": "azurewebsites.us", - "USNat": null, - "USSec": null - }, - "privateDnsZoneSuffixes_Backup": { - "AzureCloud": "com", - "AzureUSGovernment": "us", - "USNat": null, - "USSec": null - }, - "privateDnsZoneSuffixes_Monitor": { - "AzureCloud": "azure.com", - "AzureUSGovernment": "azure.us", - "USNat": null, - "USSec": null - } - }, - "resources": [], - "outputs": { - "names": { - "type": "array", - "value": "[variables('privateDnsZoneNames')]" - } - } - } - }, - "dependsOn": [ - "[subscriptionResourceId('Microsoft.Resources/deployments', format('naming-convention-{0}-{1}', parameters('networks')[0].shortName, parameters('deploymentNameSuffix')))]" - ] - } - ], - "outputs": { - "locationProperties": { - "type": "object", - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('naming-convention-{0}-{1}', parameters('networks')[0].shortName, parameters('deploymentNameSuffix'))), '2022-09-01').outputs.locations.value[parameters('location')]]" - }, - "mlzTags": { - "type": "object", - "value": "[variables('mlzTags')]" - }, - "privateDnsZones": { - "type": "array", - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('private-dns-zones-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.names.value]" - }, - "resourceAbbreviations": { - "type": "object", - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('naming-convention-{0}-{1}', parameters('networks')[0].shortName, parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceAbbreviations.value]" - }, - "tiers": { - "type": "array", - "copy": { - "count": "[length(parameters('networks'))]", - "input": { - "name": "[parameters('networks')[copyIndex()].name]", - "shortName": "[parameters('networks')[copyIndex()].shortName]", - "deployUniqueResources": "[parameters('networks')[copyIndex()].deployUniqueResources]", - "subscriptionId": "[parameters('networks')[copyIndex()].subscriptionId]", - "nsgDiagLogs": "[coalesce(tryGet(parameters('networks')[copyIndex()], 'nsgDiagLogs'), createArray())]", - "nsgDiagMetrics": "[coalesce(tryGet(parameters('networks')[copyIndex()], 'nsgDiagMetrics'), createArray())]", - "nsgRules": "[coalesce(tryGet(parameters('networks')[copyIndex()], 'nsgRules'), createArray())]", - "vnetAddressPrefix": "[coalesce(tryGet(parameters('networks')[copyIndex()], 'vnetAddressPrefix'), '')]", - "vnetDiagLogs": "[coalesce(tryGet(parameters('networks')[copyIndex()], 'vnetDiagLogs'), createArray())]", - "vnetDiagMetrics": "[coalesce(tryGet(parameters('networks')[copyIndex()], 'vnetDiagMetrics'), createArray())]", - "subnetAddressPrefix": "[coalesce(tryGet(parameters('networks')[copyIndex()], 'subnetAddressPrefix'), '')]", - "namingConvention": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('naming-convention-{0}-{1}', parameters('networks')[copyIndex()].shortName, parameters('deploymentNameSuffix'))), '2022-09-01').outputs.names.value]" - } - } - }, - "tokens": { - "type": "object", - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('naming-convention-{0}-{1}', parameters('networks')[0].shortName, parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value]" - } - } - } - } - }, - { - "condition": "[not(empty(parameters('virtualNetworkAddressPrefix')))]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('deploy-rg-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))]", - "location": "[deployment().location]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "location": { - "value": "[parameters('location')]" - }, - "mlzTags": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.mlzTags.value]" - }, - "name": { - "value": "[replace(reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tiers.value[0].namingConvention.resourceGroup, reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value.service, 'network')]" - }, - "tags": { - "value": "[parameters('tags')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "17445726037807437290" - } - }, - "parameters": { - "mlzTags": { - "type": "object" - }, - "name": { - "type": "string" - }, - "location": { - "type": "string" - }, - "tags": { - "type": "object", - "defaultValue": {} - } - }, - "resources": [ - { - "type": "Microsoft.Resources/resourceGroups", - "apiVersion": "2019-05-01", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[union(if(contains(parameters('tags'), 'Microsoft.Resources/resourceGroups'), parameters('tags')['Microsoft.Resources/resourceGroups'], createObject()), parameters('mlzTags'))]" - } - ], - "outputs": { - "id": { - "type": "string", - "value": "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('name'))]" - }, - "name": { - "type": "string", - "value": "[parameters('name')]" - }, - "location": { - "type": "string", - "value": "[reference(subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('name')), '2019-05-01', 'full').location]" - }, - "tags": { - "type": "object", - "value": "[reference(subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('name')), '2019-05-01', 'full').tags]" - } - } - } - }, - "dependsOn": [ - "[subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]" - ] - }, - { - "condition": "[not(empty(parameters('virtualNetworkAddressPrefix')))]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('deploy-network-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))]", - "location": "[deployment().location]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "additionalSubnets": { - "value": "[parameters('additionalSubnets')]" - }, - "deploymentNameSuffix": { - "value": "[parameters('deploymentNameSuffix')]" - }, - "deployNetworkWatcher": { - "value": "[parameters('deployNetworkWatcher')]" - }, - "hubVirtualNetworkResourceId": { - "value": "[parameters('hubVirtualNetworkResourceId')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "mlzTags": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.mlzTags.value]" - }, - "networkSecurityGroupName": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tiers.value[0].namingConvention.networkSecurityGroup]" - }, - "networkSecurityGroupRules": { - "value": "[parameters('networkSecurityGroupRules')]" - }, - "networkWatcherName": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tiers.value[0].namingConvention.networkWatcher]" - }, - "resourceGroupName": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.name.value]" - }, - "routeTableName": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tiers.value[0].namingConvention.routeTable]" - }, - "routeTableRouteNextHopIpAddress": { - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('firewallResourceId'), '/')[2], split(parameters('firewallResourceId'), '/')[4]), 'Microsoft.Network/azureFirewalls', split(parameters('firewallResourceId'), '/')[8]), '2020-11-01').ipConfigurations[0].properties.privateIPAddress]" - }, - "subnetAddressPrefix": { - "value": "[parameters('subnetAddressPrefix')]" - }, - "subnetName": "[if(empty(parameters('subnetName')), createObject('value', reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tiers.value[0].namingConvention.subnet), createObject('value', parameters('subnetName')))]", - "subscriptionId": { - "value": "[variables('subscriptionId')]" - }, - "tags": { - "value": "[parameters('tags')]" - }, - "virtualNetworkAddressPrefix": { - "value": "[parameters('virtualNetworkAddressPrefix')]" - }, - "virtualNetworkName": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tiers.value[0].namingConvention.virtualNetwork]" - }, - "vNetDnsServers": { - "value": "[coalesce(tryGet(reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Network/virtualNetworks', split(parameters('hubVirtualNetworkResourceId'), '/')[8]), '2023-11-01'), 'dhcpOptions', 'dnsServers'), createArray())]" - }, - "workloadShortName": { - "value": "[parameters('workloadShortName')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "1068007055123133889" - } - }, - "parameters": { - "additionalSubnets": { - "type": "array" - }, - "deploymentNameSuffix": { - "type": "string" - }, - "deployNetworkWatcher": { - "type": "bool" - }, - "hubVirtualNetworkResourceId": { - "type": "string" - }, - "location": { - "type": "string" - }, - "mlzTags": { - "type": "object" - }, - "networkSecurityGroupName": { - "type": "string" - }, - "networkSecurityGroupRules": { - "type": "array" - }, - "networkWatcherName": { - "type": "string" - }, - "resourceGroupName": { - "type": "string" - }, - "routeTableName": { - "type": "string" - }, - "routeTableRouteNextHopIpAddress": { - "type": "string" - }, - "subnetAddressPrefix": { - "type": "string" - }, - "subnetName": { - "type": "string" - }, - "subscriptionId": { - "type": "string" - }, - "tags": { - "type": "object" - }, - "vNetDnsServers": { - "type": "array" - }, - "virtualNetworkAddressPrefix": { - "type": "string" - }, - "virtualNetworkName": { - "type": "string" - }, - "workloadShortName": { - "type": "string" - } - }, - "resources": [ - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('deploy-spoke-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))]", - "location": "[deployment().location]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "additionalSubnets": { - "value": "[parameters('additionalSubnets')]" - }, - "deployNetworkWatcher": { - "value": "[parameters('deployNetworkWatcher')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "mlzTags": { - "value": "[parameters('mlzTags')]" - }, - "networkSecurityGroupName": { - "value": "[parameters('networkSecurityGroupName')]" - }, - "networkSecurityGroupRules": { - "value": "[parameters('networkSecurityGroupRules')]" - }, - "networkWatcherName": { - "value": "[parameters('networkWatcherName')]" - }, - "resourceGroupName": { - "value": "[parameters('resourceGroupName')]" - }, - "routeTableName": { - "value": "[parameters('routeTableName')]" - }, - "routeTableRouteNextHopIpAddress": { - "value": "[parameters('routeTableRouteNextHopIpAddress')]" - }, - "subnetAddressPrefix": { - "value": "[parameters('subnetAddressPrefix')]" - }, - "subnetName": { - "value": "[parameters('subnetName')]" - }, - "subscriptionId": { - "value": "[parameters('subscriptionId')]" - }, - "tags": { - "value": "[parameters('tags')]" - }, - "virtualNetworkAddressPrefix": { - "value": "[parameters('virtualNetworkAddressPrefix')]" - }, - "virtualNetworkName": { - "value": "[parameters('virtualNetworkName')]" - }, - "vNetDnsServers": { - "value": "[parameters('vNetDnsServers')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "11567108053836979012" - } - }, - "parameters": { - "additionalSubnets": { - "type": "array", - "defaultValue": [] - }, - "deployNetworkWatcher": { - "type": "bool" - }, - "location": { - "type": "string" - }, - "mlzTags": { - "type": "object" - }, - "networkSecurityGroupName": { - "type": "string" - }, - "networkSecurityGroupRules": { - "type": "array" - }, - "networkWatcherName": { - "type": "string" - }, - "resourceGroupName": { - "type": "string" - }, - "routeTableName": { - "type": "string" - }, - "routeTableRouteNextHopIpAddress": { - "type": "string" - }, - "subnetAddressPrefix": { - "type": "string" - }, - "subnetName": { - "type": "string" - }, - "subscriptionId": { - "type": "string" - }, - "tags": { - "type": "object" - }, - "virtualNetworkAddressPrefix": { - "type": "string" - }, - "virtualNetworkName": { - "type": "string" - }, - "vNetDnsServers": { - "type": "array" - } - }, - "variables": { - "delegations": { - "AzureNetAppFiles": [ - { - "name": "Microsoft.Netapp.volumes", - "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets/delegations', parameters('virtualNetworkName'), 'AzureNetAppFiles', 'delegation')]", - "properties": { - "serviceName": "Microsoft.Netapp/volumes" - }, - "type": "Microsoft.Network/virtualNetworks/subnets/delegations" - } - ], - "FunctionAppOutbound": [ - { - "name": "Microsoft.Web/sites", - "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets/delegations', parameters('virtualNetworkName'), 'FunctionAppOutbound', 'delegation')]", - "properties": { - "serviceName": "Microsoft.Web/serverfarms" - }, - "type": "Microsoft.Network/virtualNetworks/subnets/delegations" - } - ] - }, - "subnets": "[union(createArray(createObject('name', parameters('subnetName'), 'properties', createObject('addressPrefix', parameters('subnetAddressPrefix')))), parameters('additionalSubnets'))]" - }, - "resources": [ - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "networkSecurityGroup", - "subscriptionId": "[parameters('subscriptionId')]", - "resourceGroup": "[parameters('resourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "location": { - "value": "[parameters('location')]" - }, - "mlzTags": { - "value": "[parameters('mlzTags')]" - }, - "name": { - "value": "[parameters('networkSecurityGroupName')]" - }, - "securityRules": { - "value": "[parameters('networkSecurityGroupRules')]" - }, - "tags": { - "value": "[parameters('tags')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "2196017082128829477" - } - }, - "parameters": { - "location": { - "type": "string" - }, - "mlzTags": { - "type": "object" - }, - "name": { - "type": "string" - }, - "securityRules": { - "type": "array" - }, - "tags": { - "type": "object" - } - }, - "resources": [ - { - "type": "Microsoft.Network/networkSecurityGroups", - "apiVersion": "2021-02-01", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[union(if(contains(parameters('tags'), 'Microsoft.Network/networkSecurityGroups'), parameters('tags')['Microsoft.Network/networkSecurityGroups'], createObject()), parameters('mlzTags'))]", - "properties": { - "securityRules": "[parameters('securityRules')]" - } - } - ], - "outputs": { - "id": { - "type": "string", - "value": "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('name'))]" - }, - "name": { - "type": "string", - "value": "[parameters('name')]" - } - } - } - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "routeTable", - "subscriptionId": "[parameters('subscriptionId')]", - "resourceGroup": "[parameters('resourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "disableBgpRoutePropagation": { - "value": true - }, - "location": { - "value": "[parameters('location')]" - }, - "mlzTags": { - "value": "[parameters('mlzTags')]" - }, - "name": { - "value": "[parameters('routeTableName')]" - }, - "routeNextHopIpAddress": { - "value": "[parameters('routeTableRouteNextHopIpAddress')]" - }, - "tags": { - "value": "[parameters('tags')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "8417416771003518918" - } - }, - "parameters": { - "disableBgpRoutePropagation": { - "type": "bool" - }, - "location": { - "type": "string" - }, - "mlzTags": { - "type": "object" - }, - "name": { - "type": "string" - }, - "routeAddressPrefix": { - "type": "string", - "defaultValue": "0.0.0.0/0" - }, - "routeName": { - "type": "string", - "defaultValue": "default_route" - }, - "routeNextHopIpAddress": { - "type": "string" - }, - "routeNextHopType": { - "type": "string", - "defaultValue": "VirtualAppliance" - }, - "tags": { - "type": "object" - } - }, - "resources": [ - { - "type": "Microsoft.Network/routeTables", - "apiVersion": "2021-02-01", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[union(if(contains(parameters('tags'), 'Microsoft.Network/routeTables'), parameters('tags')['Microsoft.Network/routeTables'], createObject()), parameters('mlzTags'))]", - "properties": { - "disableBgpRoutePropagation": "[parameters('disableBgpRoutePropagation')]", - "routes": [ - { - "name": "[parameters('routeName')]", - "properties": { - "addressPrefix": "[parameters('routeAddressPrefix')]", - "nextHopIpAddress": "[parameters('routeNextHopIpAddress')]", - "nextHopType": "[parameters('routeNextHopType')]" - } - } - ] - } - } - ], - "outputs": { - "id": { - "type": "string", - "value": "[resourceId('Microsoft.Network/routeTables', parameters('name'))]" - }, - "name": { - "type": "string", - "value": "[parameters('name')]" - } - } - } - } - }, - { - "condition": "[parameters('deployNetworkWatcher')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "networkWatcher", - "subscriptionId": "[parameters('subscriptionId')]", - "resourceGroup": "[parameters('resourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "location": { - "value": "[parameters('location')]" - }, - "mlzTags": { - "value": "[parameters('mlzTags')]" - }, - "name": { - "value": "[parameters('networkWatcherName')]" - }, - "tags": { - "value": "[parameters('tags')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "4071285799602638747" - } - }, - "parameters": { - "location": { - "type": "string" - }, - "mlzTags": { - "type": "object" - }, - "name": { - "type": "string" - }, - "tags": { - "type": "object" - } - }, - "resources": [ - { - "type": "Microsoft.Network/networkWatchers", - "apiVersion": "2021-02-01", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[union(if(contains(parameters('tags'), 'Microsoft.Network/networkWatchers'), parameters('tags')['Microsoft.Network/networkWatchers'], createObject()), parameters('mlzTags'))]", - "properties": {} - } - ] - } - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "virtualNetwork", - "subscriptionId": "[parameters('subscriptionId')]", - "resourceGroup": "[parameters('resourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "addressPrefix": { - "value": "[parameters('virtualNetworkAddressPrefix')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "mlzTags": { - "value": "[parameters('mlzTags')]" - }, - "name": { - "value": "[parameters('virtualNetworkName')]" - }, - "subnets": { - "copy": [ - { - "name": "value", - "count": "[length(variables('subnets'))]", - "input": "[createObject('name', variables('subnets')[copyIndex('value')].name, 'properties', createObject('addressPrefix', variables('subnets')[copyIndex('value')].properties.addressPrefix, 'delegations', coalesce(tryGet(variables('delegations'), variables('subnets')[copyIndex('value')].name), createArray()), 'networkSecurityGroup', createObject('id', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'networkSecurityGroup'), '2022-09-01').outputs.id.value), 'routeTable', createObject('id', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'routeTable'), '2022-09-01').outputs.id.value), 'privateEndpointNetworkPolicies', 'Disabled', 'privateLinkServiceNetworkPolicies', 'Disabled'))]" - } - ] - }, - "tags": { - "value": "[parameters('tags')]" - }, - "vNetDnsServers": { - "value": "[parameters('vNetDnsServers')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "3465065949976146403" - } - }, - "parameters": { - "addressPrefix": { - "type": "string" - }, - "location": { - "type": "string" - }, - "mlzTags": { - "type": "object" - }, - "name": { - "type": "string" - }, - "subnets": { - "type": "array" - }, - "tags": { - "type": "object" - }, - "vNetDnsServers": { - "type": "array" - } - }, - "resources": [ - { - "type": "Microsoft.Network/virtualNetworks", - "apiVersion": "2021-02-01", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[union(if(contains(parameters('tags'), 'Microsoft.Network/virtualNetworks'), parameters('tags')['Microsoft.Network/virtualNetworks'], createObject()), parameters('mlzTags'))]", - "properties": { - "addressSpace": { - "addressPrefixes": [ - "[parameters('addressPrefix')]" - ] - }, - "subnets": "[parameters('subnets')]", - "dhcpOptions": "[if(empty(parameters('vNetDnsServers')), null(), createObject('dnsServers', parameters('vNetDnsServers')))]" - } - } - ], - "outputs": { - "addressPrefix": { - "type": "string", - "value": "[reference(resourceId('Microsoft.Network/virtualNetworks', parameters('name')), '2021-02-01').addressSpace.addressPrefixes[0]]" - }, - "dnsServers": { - "type": "array", - "value": "[parameters('vNetDnsServers')]" - }, - "id": { - "type": "string", - "value": "[resourceId('Microsoft.Network/virtualNetworks', parameters('name'))]" - }, - "name": { - "type": "string", - "value": "[parameters('name')]" - }, - "subnets": { - "type": "array", - "value": "[reference(resourceId('Microsoft.Network/virtualNetworks', parameters('name')), '2021-02-01').subnets]" - } - } - } - }, - "dependsOn": [ - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'networkSecurityGroup')]", - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'networkWatcher')]", - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'routeTable')]" - ] - } - ], - "outputs": { - "networkSecurityGroupName": { - "type": "string", - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'networkSecurityGroup'), '2022-09-01').outputs.name.value]" - }, - "networkSecurityGroupResourceId": { - "type": "string", - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'networkSecurityGroup'), '2022-09-01').outputs.id.value]" - }, - "subnets": { - "type": "array", - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'virtualNetwork'), '2022-09-01').outputs.subnets.value]" - }, - "virtualNetworkAddressPrefix": { - "type": "string", - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'virtualNetwork'), '2022-09-01').outputs.addressPrefix.value]" - }, - "virtualNetworkName": { - "type": "string", - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'virtualNetwork'), '2022-09-01').outputs.name.value]" - }, - "virtualNetworkResourceId": { - "type": "string", - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'virtualNetwork'), '2022-09-01').outputs.id.value]" - } - } - } - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('deploy-spoke-peering-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))]", - "location": "[deployment().location]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "deploymentNameSuffix": { - "value": "[parameters('deploymentNameSuffix')]" - }, - "hubVirtualNetworkResourceId": { - "value": "[parameters('hubVirtualNetworkResourceId')]" - }, - "resourceGroupName": { - "value": "[parameters('resourceGroupName')]" - }, - "spokeShortName": { - "value": "[parameters('workloadShortName')]" - }, - "spokeVirtualNetworkName": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-spoke-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.virtualNetworkName.value]" - }, - "subscriptionId": { - "value": "[parameters('subscriptionId')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "15799890372401066181" - } - }, - "parameters": { - "deploymentNameSuffix": { - "type": "string" - }, - "hubVirtualNetworkResourceId": { - "type": "string" - }, - "resourceGroupName": { - "type": "string" - }, - "spokeShortName": { - "type": "string" - }, - "spokeVirtualNetworkName": { - "type": "string" - }, - "subscriptionId": { - "type": "string" - } - }, - "resources": [ - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('peer-{0}-to-hub-{1}', parameters('spokeShortName'), parameters('deploymentNameSuffix'))]", - "subscriptionId": "[parameters('subscriptionId')]", - "resourceGroup": "[parameters('resourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "remoteVirtualNetworkResourceId": { - "value": "[parameters('hubVirtualNetworkResourceId')]" - }, - "virtualNetworkName": { - "value": "[parameters('spokeVirtualNetworkName')]" - }, - "virtualNetworkPeerName": { - "value": "[format('to-{0}', split(parameters('hubVirtualNetworkResourceId'), '/')[8])]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "5031620623183573702" - } - }, - "parameters": { - "remoteVirtualNetworkResourceId": { - "type": "string" - }, - "virtualNetworkName": { - "type": "string" - }, - "virtualNetworkPeerName": { - "type": "string" - } - }, - "resources": [ - { - "type": "Microsoft.Network/virtualNetworks/virtualNetworkPeerings", - "apiVersion": "2021-02-01", - "name": "[format('{0}/{1}', parameters('virtualNetworkName'), parameters('virtualNetworkPeerName'))]", - "properties": { - "allowForwardedTraffic": true, - "remoteVirtualNetwork": { - "id": "[parameters('remoteVirtualNetworkResourceId')]" - } - } - } - ] - } - } - } - ] - } - }, - "dependsOn": [ - "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-spoke-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('deploy-hub-peering-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))]", - "location": "[deployment().location]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "deploymentNameSuffix": { - "value": "[parameters('deploymentNameSuffix')]" - }, - "hubVirtualNetworkName": { - "value": "[split(parameters('hubVirtualNetworkResourceId'), '/')[8]]" - }, - "resourceGroupName": { - "value": "[split(parameters('hubVirtualNetworkResourceId'), '/')[4]]" - }, - "spokeShortName": { - "value": "[parameters('workloadShortName')]" - }, - "spokeVirtualNetworkResourceId": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-spoke-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.virtualNetworkResourceId.value]" - }, - "subscriptionId": { - "value": "[split(parameters('hubVirtualNetworkResourceId'), '/')[2]]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "6489616383757058493" - } - }, - "parameters": { - "deploymentNameSuffix": { - "type": "string" - }, - "hubVirtualNetworkName": { - "type": "string" - }, - "resourceGroupName": { - "type": "string" - }, - "spokeShortName": { - "type": "string" - }, - "spokeVirtualNetworkResourceId": { - "type": "string" - }, - "subscriptionId": { - "type": "string" - } - }, - "resources": [ - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('peer-hub-to-{0}-{1}', parameters('spokeShortName'), parameters('deploymentNameSuffix'))]", - "subscriptionId": "[parameters('subscriptionId')]", - "resourceGroup": "[parameters('resourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "remoteVirtualNetworkResourceId": { - "value": "[parameters('spokeVirtualNetworkResourceId')]" - }, - "virtualNetworkName": { - "value": "[parameters('hubVirtualNetworkName')]" - }, - "virtualNetworkPeerName": { - "value": "[format('to-{0}', split(parameters('spokeVirtualNetworkResourceId'), '/')[8])]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "5031620623183573702" - } - }, - "parameters": { - "remoteVirtualNetworkResourceId": { - "type": "string" - }, - "virtualNetworkName": { - "type": "string" - }, - "virtualNetworkPeerName": { - "type": "string" - } - }, - "resources": [ - { - "type": "Microsoft.Network/virtualNetworks/virtualNetworkPeerings", - "apiVersion": "2021-02-01", - "name": "[format('{0}/{1}', parameters('virtualNetworkName'), parameters('virtualNetworkPeerName'))]", - "properties": { - "allowForwardedTraffic": true, - "remoteVirtualNetwork": { - "id": "[parameters('remoteVirtualNetworkResourceId')]" - } - } - } - ] - } - } - } - ] - } - }, - "dependsOn": [ - "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-spoke-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]" - ] - } - ], - "outputs": { - "networkSecurityGroupName": { - "type": "string", - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-spoke-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.networkSecurityGroupName.value]" - }, - "subnets": { - "type": "array", - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-spoke-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.subnets.value]" - }, - "virtualNetworkName": { - "type": "string", - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-spoke-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.virtualNetworkName.value]" - } - } - } - }, - "dependsOn": [ - "[subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]", - "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]" - ] - }, - { - "condition": "[not(empty(parameters('virtualNetworkAddressPrefix')))]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('deploy-vnet-links-{0}-sub-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))]", - "resourceGroup": "[variables('hubResourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "azureFirewallSku": { - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('firewallResourceId'), '/')[2], split(parameters('firewallResourceId'), '/')[4]), 'Microsoft.Network/azureFirewalls', split(parameters('firewallResourceId'), '/')[8]), '2020-11-01').sku.tier]" - }, - "deploymentNameSuffix": { - "value": "[parameters('deploymentNameSuffix')]" - }, - "privateDnsZoneNames": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.privateDnsZones.value]" - }, - "virtualNetworkName": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-network-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.virtualNetworkName.value]" - }, - "virtualNetworkResourceGroupName": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.name.value]" - }, - "virtualNetworkSubscriptionId": { - "value": "[variables('subscriptionId')]" - }, - "workloadShortName": { - "value": "[parameters('workloadShortName')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "7410232214960945771" - } - }, - "parameters": { - "azureFirewallSku": { - "type": "string" - }, - "deploymentNameSuffix": { - "type": "string" - }, - "privateDnsZoneNames": { - "type": "array" - }, - "virtualNetworkName": { - "type": "string" - }, - "virtualNetworkResourceGroupName": { - "type": "string" - }, - "virtualNetworkSubscriptionId": { - "type": "string" - }, - "workloadShortName": { - "type": "string" - } - }, - "resources": [ - { - "condition": "[equals(parameters('azureFirewallSku'), 'Basic')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('deploy-vnet-links-{0}-rg-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "privateDnsZoneNames": { - "value": "[parameters('privateDnsZoneNames')]" - }, - "virtualNetworkName": { - "value": "[parameters('virtualNetworkName')]" - }, - "virtualNetworkResourceGroupName": { - "value": "[parameters('virtualNetworkResourceGroupName')]" - }, - "virtualNetworkSubscriptionId": { - "value": "[parameters('virtualNetworkSubscriptionId')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "12405780209119797551" - } - }, - "parameters": { - "privateDnsZoneNames": { - "type": "array" - }, - "virtualNetworkName": { - "type": "string" - }, - "virtualNetworkResourceGroupName": { - "type": "string" - }, - "virtualNetworkSubscriptionId": { - "type": "string" - } - }, - "resources": [ - { - "copy": { - "name": "virtualNetworkLinks", - "count": "[length(parameters('privateDnsZoneNames'))]" - }, - "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", - "apiVersion": "2018-09-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneNames')[copyIndex()], parameters('virtualNetworkName'))]", - "location": "global", - "properties": { - "registrationEnabled": false, - "virtualNetwork": { - "id": "[resourceId(parameters('virtualNetworkSubscriptionId'), parameters('virtualNetworkResourceGroupName'), 'Microsoft.Network/virtualNetworks', parameters('virtualNetworkName'))]" - } - } - } - ] - } - } - } - ] - } - }, - "dependsOn": [ - "[subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]", - "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-network-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]", - "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]" - ] - }, - { - "condition": "[not(empty(parameters('virtualNetworkAddressPrefix')))]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('deploy-cmk-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))]", - "location": "[deployment().location]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "deploymentNameSuffix": { - "value": "[parameters('deploymentNameSuffix')]" - }, - "environmentAbbreviation": { - "value": "[parameters('environmentAbbreviation')]" - }, - "keyVaultPrivateDnsZoneResourceId": { - "value": "[resourceId(variables('hubSubscriptionId'), variables('hubResourceGroupName'), 'Microsoft.Network/privateDnsZones', replace(format('privatelink{0}', environment().suffixes.keyvaultDns), 'vault', 'vaultcore'))]" - }, - "location": { - "value": "[parameters('location')]" - }, - "mlzTags": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.mlzTags.value]" - }, - "resourceAbbreviations": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceAbbreviations.value]" - }, - "resourceGroupName": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.name.value]" - }, - "subnetResourceId": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-network-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.subnets.value[0].id]" - }, - "tags": { - "value": "[parameters('tags')]" - }, - "tier": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tiers.value[0]]" - }, - "tokens": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value]" - }, - "workloadShortName": { - "value": "[parameters('workloadShortName')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "6386888682917235118" - } - }, - "parameters": { - "deploymentNameSuffix": { - "type": "string" - }, - "environmentAbbreviation": { - "type": "string" - }, - "keyVaultPrivateDnsZoneResourceId": { - "type": "string" - }, - "location": { - "type": "string" - }, - "mlzTags": { - "type": "object" - }, - "resourceAbbreviations": { - "type": "object" - }, - "resourceGroupName": { - "type": "string" - }, - "subnetResourceId": { - "type": "string" - }, - "tags": { - "type": "object" - }, - "tier": { - "type": "object" - }, - "tokens": { - "type": "object" - }, - "workloadShortName": { - "type": "string" - } - }, - "resources": [ - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('deploy-kv-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))]", - "subscriptionId": "[parameters('tier').subscriptionId]", - "resourceGroup": "[parameters('resourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "environmentAbbreviation": { - "value": "[parameters('environmentAbbreviation')]" - }, - "keyVaultPrivateDnsZoneResourceId": { - "value": "[parameters('keyVaultPrivateDnsZoneResourceId')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "mlzTags": { - "value": "[parameters('mlzTags')]" - }, - "resourceAbbreviations": { - "value": "[parameters('resourceAbbreviations')]" - }, - "subnetResourceId": { - "value": "[parameters('subnetResourceId')]" - }, - "tags": { - "value": "[parameters('tags')]" - }, - "tier": { - "value": "[parameters('tier')]" - }, - "tokens": { - "value": "[parameters('tokens')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "419730844167715947" - } - }, - "parameters": { - "diskEncryptionKeyExpirationInDays": { - "type": "int", - "defaultValue": 30 - }, - "environmentAbbreviation": { - "type": "string" - }, - "keyVaultPrivateDnsZoneResourceId": { - "type": "string" - }, - "location": { - "type": "string" - }, - "mlzTags": { - "type": "object" - }, - "resourceAbbreviations": { - "type": "object" - }, - "subnetResourceId": { - "type": "string" - }, - "tags": { - "type": "object" - }, - "tier": { - "type": "object" - }, - "tokens": { - "type": "object" - } - }, - "variables": { - "keyVaultPrivateEndpointName": "[replace(parameters('tier').namingConvention.keyVaultPrivateEndpoint, parameters('tokens').service, 'cmk')]" - }, - "resources": [ - { - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2022-07-01", - "name": "[format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('tier').namingConvention.keyVault, parameters('tokens').service, 'cmk'), resourceGroup().id))]", - "location": "[parameters('location')]", - "tags": "[union(if(contains(parameters('tags'), 'Microsoft.KeyVault/vaults'), parameters('tags')['Microsoft.KeyVault/vaults'], createObject()), parameters('mlzTags'))]", - "properties": { - "enabledForDeployment": false, - "enabledForDiskEncryption": false, - "enabledForTemplateDeployment": false, - "enablePurgeProtection": true, - "enableRbacAuthorization": true, - "enableSoftDelete": true, - "networkAcls": { - "bypass": "AzureServices", - "defaultAction": "Deny", - "ipRules": [], - "virtualNetworkRules": [] - }, - "publicNetworkAccess": "Disabled", - "sku": { - "family": "A", - "name": "premium" - }, - "softDeleteRetentionInDays": "[if(or(equals(parameters('environmentAbbreviation'), 'dev'), equals(parameters('environmentAbbreviation'), 'test')), 7, 90)]", - "tenantId": "[subscription().tenantId]" - } - }, - { - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", - "name": "[variables('keyVaultPrivateEndpointName')]", - "location": "[parameters('location')]", - "tags": "[union(if(contains(parameters('tags'), 'Microsoft.Network/privateEndpoints'), parameters('tags')['Microsoft.Network/privateEndpoints'], createObject()), parameters('mlzTags'))]", - "properties": { - "customNetworkInterfaceName": "[replace(parameters('tier').namingConvention.keyVaultNetworkInterface, parameters('tokens').service, 'cmk')]", - "privateLinkServiceConnections": [ - { - "name": "[variables('keyVaultPrivateEndpointName')]", - "properties": { - "privateLinkServiceId": "[resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('tier').namingConvention.keyVault, parameters('tokens').service, 'cmk'), resourceGroup().id)))]", - "groupIds": [ - "vault" - ] - } - } - ], - "subnet": { - "id": "[parameters('subnetResourceId')]" - } - }, - "dependsOn": [ - "[resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('tier').namingConvention.keyVault, parameters('tokens').service, 'cmk'), resourceGroup().id)))]" - ] - }, - { - "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2021-08-01", - "name": "[format('{0}/{1}', variables('keyVaultPrivateEndpointName'), format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('tier').namingConvention.keyVault, parameters('tokens').service, 'cmk'), resourceGroup().id)))]", - "properties": { - "privateDnsZoneConfigs": [ - { - "name": "ipconfig1", - "properties": { - "privateDnsZoneId": "[parameters('keyVaultPrivateDnsZoneResourceId')]" - } - } - ] - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/privateEndpoints', variables('keyVaultPrivateEndpointName'))]", - "[resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('tier').namingConvention.keyVault, parameters('tokens').service, 'cmk'), resourceGroup().id)))]" - ] - }, - { - "type": "Microsoft.KeyVault/vaults/keys", - "apiVersion": "2022-07-01", - "name": "[format('{0}/{1}', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('tier').namingConvention.keyVault, parameters('tokens').service, 'cmk'), resourceGroup().id)), 'DiskEncryptionKey')]", - "properties": { - "attributes": { - "enabled": true - }, - "keySize": 4096, - "kty": "RSA", - "rotationPolicy": { - "attributes": { - "expiryTime": "[format('P{0}D', string(parameters('diskEncryptionKeyExpirationInDays')))]" - }, - "lifetimeActions": [ - { - "action": { - "type": "Notify" - }, - "trigger": { - "timeBeforeExpiry": "P10D" - } - }, - { - "action": { - "type": "Rotate" - }, - "trigger": { - "timeAfterCreate": "[format('P{0}D', string(sub(parameters('diskEncryptionKeyExpirationInDays'), 7)))]" - } - } - ] - } - }, - "dependsOn": [ - "[resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('tier').namingConvention.keyVault, parameters('tokens').service, 'cmk'), resourceGroup().id)))]" - ] - }, - { - "type": "Microsoft.KeyVault/vaults/keys", - "apiVersion": "2022-07-01", - "name": "[format('{0}/{1}', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('tier').namingConvention.keyVault, parameters('tokens').service, 'cmk'), resourceGroup().id)), 'StorageEncryptionKey')]", - "properties": { - "attributes": { - "enabled": true - }, - "keySize": 4096, - "kty": "RSA", - "rotationPolicy": { - "attributes": { - "expiryTime": "[format('P{0}D', string(parameters('diskEncryptionKeyExpirationInDays')))]" - }, - "lifetimeActions": [ - { - "action": { - "type": "Notify" - }, - "trigger": { - "timeBeforeExpiry": "P10D" - } - }, - { - "action": { - "type": "Rotate" - }, - "trigger": { - "timeAfterCreate": "[format('P{0}D', string(sub(parameters('diskEncryptionKeyExpirationInDays'), 7)))]" - } - } - ] - } - }, - "dependsOn": [ - "[resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('tier').namingConvention.keyVault, parameters('tokens').service, 'cmk'), resourceGroup().id)))]" - ] - } - ], - "outputs": { - "keyUriWithVersion": { - "type": "string", - "value": "[reference(resourceId('Microsoft.KeyVault/vaults/keys', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('tier').namingConvention.keyVault, parameters('tokens').service, 'cmk'), resourceGroup().id)), 'DiskEncryptionKey'), '2022-07-01').keyUriWithVersion]" - }, - "keyVaultResourceId": { - "type": "string", - "value": "[resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('tier').namingConvention.keyVault, parameters('tokens').service, 'cmk'), resourceGroup().id)))]" - }, - "keyVaultName": { - "type": "string", - "value": "[format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('tier').namingConvention.keyVault, parameters('tokens').service, 'cmk'), resourceGroup().id))]" - }, - "keyVaultUri": { - "type": "string", - "value": "[reference(resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('tier').namingConvention.keyVault, parameters('tokens').service, 'cmk'), resourceGroup().id))), '2022-07-01').vaultUri]" - }, - "storageKeyName": { - "type": "string", - "value": "StorageEncryptionKey" - } - } - } - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('deploy-des-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))]", - "subscriptionId": "[parameters('tier').subscriptionId]", - "resourceGroup": "[parameters('resourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "deploymentNameSuffix": { - "value": "[parameters('deploymentNameSuffix')]" - }, - "diskEncryptionSetName": { - "value": "[parameters('tier').namingConvention.diskEncryptionSet]" - }, - "keyUrl": { - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('tier').subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-kv-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.keyUriWithVersion.value]" - }, - "keyVaultResourceId": { - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('tier').subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-kv-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.keyVaultResourceId.value]" - }, - "location": { - "value": "[parameters('location')]" - }, - "mlzTags": { - "value": "[parameters('mlzTags')]" - }, - "tags": "[if(contains(parameters('tags'), 'Microsoft.Compute/diskEncryptionSets'), createObject('value', parameters('tags')['Microsoft.Compute/diskEncryptionSets']), createObject('value', createObject()))]", - "workloadShortName": { - "value": "[parameters('workloadShortName')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "7946295394028911325" - } - }, - "parameters": { - "deploymentNameSuffix": { - "type": "string" - }, - "diskEncryptionSetName": { - "type": "string" - }, - "keyUrl": { - "type": "string" - }, - "keyVaultResourceId": { - "type": "string" - }, - "location": { - "type": "string" - }, - "mlzTags": { - "type": "object" - }, - "tags": { - "type": "object" - }, - "workloadShortName": { - "type": "string" - } - }, - "resources": [ - { - "type": "Microsoft.Compute/diskEncryptionSets", - "apiVersion": "2023-04-02", - "name": "[parameters('diskEncryptionSetName')]", - "location": "[parameters('location')]", - "tags": "[union(if(contains(parameters('tags'), 'Microsoft.Compute/diskEncryptionSets'), parameters('tags')['Microsoft.Compute/diskEncryptionSets'], createObject()), parameters('mlzTags'))]", - "identity": { - "type": "SystemAssigned" - }, - "properties": { - "activeKey": { - "sourceVault": { - "id": "[parameters('keyVaultResourceId')]" - }, - "keyUrl": "[parameters('keyUrl')]" - }, - "encryptionType": "EncryptionAtRestWithPlatformAndCustomerKeys", - "rotationToLatestKeyVersionEnabled": true - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('assign-role-des-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "principalId": { - "value": "[reference(resourceId('Microsoft.Compute/diskEncryptionSets', parameters('diskEncryptionSetName')), '2023-04-02', 'full').identity.principalId]" - }, - "principalType": { - "value": "ServicePrincipal" - }, - "roleDefinitionId": { - "value": "[resourceId('Microsoft.Authorization/roleDefinitions', 'e147488a-f6f5-4113-8e2d-b22465e65bf6')]" - }, - "targetResourceId": { - "value": "[resourceGroup().id]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "967013811257719495" - } - }, - "parameters": { - "targetResourceId": { - "type": "string" - }, - "roleDefinitionId": { - "type": "string" - }, - "principalId": { - "type": "string" - }, - "principalType": { - "type": "string", - "defaultValue": "ServicePrincipal", - "allowedValues": [ - "ForeignGroup", - "Group", - "ServicePrincipal", - "User" - ] - }, - "description": { - "type": "string", - "defaultValue": "" - } - }, - "resources": [ - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2020-04-01-preview", - "name": "[guid(parameters('targetResourceId'), parameters('roleDefinitionId'), parameters('principalId'))]", - "properties": { - "principalId": "[parameters('principalId')]", - "principalType": "[parameters('principalType')]", - "roleDefinitionId": "[parameters('roleDefinitionId')]", - "description": "[parameters('description')]" - } - } - ] - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Compute/diskEncryptionSets', parameters('diskEncryptionSetName'))]" - ] - } - ], - "outputs": { - "resourceId": { - "type": "string", - "value": "[resourceId('Microsoft.Compute/diskEncryptionSets', parameters('diskEncryptionSetName'))]" - } - } - } - }, - "dependsOn": [ - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('tier').subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-kv-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('deploy-id-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))]", - "subscriptionId": "[parameters('tier').subscriptionId]", - "resourceGroup": "[parameters('resourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "keyVaultName": { - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('tier').subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-kv-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.keyVaultName.value]" - }, - "location": { - "value": "[parameters('location')]" - }, - "mlzTags": { - "value": "[parameters('mlzTags')]" - }, - "tags": { - "value": "[parameters('tags')]" - }, - "userAssignedIdentityName": { - "value": "[replace(parameters('tier').namingConvention.userAssignedIdentity, format('-{0}', parameters('tokens').service), '')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "9115514582672423063" - } - }, - "parameters": { - "keyVaultName": { - "type": "string" - }, - "location": { - "type": "string" - }, - "mlzTags": { - "type": "object" - }, - "tags": { - "type": "object" - }, - "userAssignedIdentityName": { - "type": "string" - } - }, - "resources": [ - { - "type": "Microsoft.ManagedIdentity/userAssignedIdentities", - "apiVersion": "2018-11-30", - "name": "[parameters('userAssignedIdentityName')]", - "location": "[parameters('location')]", - "tags": "[union(if(contains(parameters('tags'), 'Microsoft.ManagedIdentity/userAssignedIdentities'), parameters('tags')['Microsoft.ManagedIdentity/userAssignedIdentities'], createObject()), parameters('mlzTags'))]" - }, - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2020-04-01-preview", - "scope": "[format('Microsoft.KeyVault/vaults/{0}', parameters('keyVaultName'))]", - "name": "[guid(parameters('userAssignedIdentityName'), 'e147488a-f6f5-4113-8e2d-b22465e65bf6', parameters('keyVaultName'))]", - "properties": { - "principalId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('userAssignedIdentityName')), '2018-11-30').principalId]", - "principalType": "ServicePrincipal", - "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', 'e147488a-f6f5-4113-8e2d-b22465e65bf6')]" - }, - "dependsOn": [ - "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('userAssignedIdentityName'))]" - ] - } - ], - "outputs": { - "resourceId": { - "type": "string", - "value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('userAssignedIdentityName'))]" - } - } - } - }, - "dependsOn": [ - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('tier').subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-kv-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]" - ] - } - ], - "outputs": { - "diskEncryptionSetResourceId": { - "type": "string", - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('tier').subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-des-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceId.value]" - }, - "keyVaultName": { - "type": "string", - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('tier').subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-kv-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.keyVaultName.value]" - }, - "keyVaultUri": { - "type": "string", - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('tier').subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-kv-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.keyVaultUri.value]" - }, - "keyVaultResourceId": { - "type": "string", - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('tier').subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-kv-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.keyVaultResourceId.value]" - }, - "storageKeyName": { - "type": "string", - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('tier').subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-kv-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.storageKeyName.value]" - }, - "userAssignedIdentityResourceId": { - "type": "string", - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('tier').subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-id-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceId.value]" - } - } - } - }, - "dependsOn": [ - "[subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]", - "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-network-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]", - "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]" - ] - }, - { - "condition": "[not(empty(parameters('virtualNetworkAddressPrefix')))]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('deploy-storage-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))]", - "location": "[deployment().location]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "blobsPrivateDnsZoneResourceId": { - "value": "[resourceId(variables('hubSubscriptionId'), variables('hubResourceGroupName'), 'Microsoft.Network/privateDnsZones', format('privatelink.blob.{0}', environment().suffixes.storage))]" - }, - "filesPrivateDnsZoneResourceId": { - "value": "[resourceId(variables('hubSubscriptionId'), variables('hubResourceGroupName'), 'Microsoft.Network/privateDnsZones', format('privatelink.file.{0}', environment().suffixes.storage))]" - }, - "keyVaultUri": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-cmk-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.keyVaultUri.value]" - }, - "location": { - "value": "[parameters('location')]" - }, - "logStorageSkuName": { - "value": "[parameters('logStorageSkuName')]" - }, - "mlzTags": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.mlzTags.value]" - }, - "network": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tiers.value[0]]" - }, - "queuesPrivateDnsZoneResourceId": { - "value": "[resourceId(variables('hubSubscriptionId'), variables('hubResourceGroupName'), 'Microsoft.Network/privateDnsZones', format('privatelink.queue.{0}', environment().suffixes.storage))]" - }, - "resourceGroupName": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.name.value]" - }, - "serviceToken": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value.service]" - }, - "storageEncryptionKeyName": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-cmk-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.storageKeyName.value]" - }, - "subnetResourceId": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-network-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.subnets.value[0].id]" - }, - "tablesPrivateDnsZoneResourceId": { - "value": "[resourceId(variables('hubSubscriptionId'), variables('hubResourceGroupName'), 'Microsoft.Network/privateDnsZones', format('privatelink.table.{0}', environment().suffixes.storage))]" - }, - "tags": { - "value": "[parameters('tags')]" - }, - "tier": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tiers.value[0]]" - }, - "userAssignedIdentityResourceId": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-cmk-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.userAssignedIdentityResourceId.value]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "11229301381209629588" - } - }, - "parameters": { - "blobsPrivateDnsZoneResourceId": { - "type": "string" - }, - "filesPrivateDnsZoneResourceId": { - "type": "string" - }, - "keyVaultUri": { - "type": "string" - }, - "logStorageSkuName": { - "type": "string" - }, - "location": { - "type": "string" - }, - "mlzTags": { - "type": "object" - }, - "network": { - "type": "object" - }, - "queuesPrivateDnsZoneResourceId": { - "type": "string" - }, - "resourceGroupName": { - "type": "string" - }, - "serviceToken": { - "type": "string" - }, - "storageEncryptionKeyName": { - "type": "string" - }, - "subnetResourceId": { - "type": "string" - }, - "tablesPrivateDnsZoneResourceId": { - "type": "string" - }, - "tags": { - "type": "object" - }, - "tier": { - "type": "object" - }, - "userAssignedIdentityResourceId": { - "type": "string" - } - }, - "resources": [ - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "storage", - "subscriptionId": "[parameters('network').subscriptionId]", - "resourceGroup": "[parameters('resourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "blobsPrivateDnsZoneResourceId": { - "value": "[parameters('blobsPrivateDnsZoneResourceId')]" - }, - "filesPrivateDnsZoneResourceId": { - "value": "[parameters('filesPrivateDnsZoneResourceId')]" - }, - "keyVaultUri": { - "value": "[parameters('keyVaultUri')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "mlzTags": { - "value": "[parameters('mlzTags')]" - }, - "queuesPrivateDnsZoneResourceId": { - "value": "[parameters('queuesPrivateDnsZoneResourceId')]" - }, - "serviceToken": { - "value": "[parameters('serviceToken')]" - }, - "skuName": { - "value": "[parameters('logStorageSkuName')]" - }, - "storageEncryptionKeyName": { - "value": "[parameters('storageEncryptionKeyName')]" - }, - "subnetResourceId": { - "value": "[parameters('subnetResourceId')]" - }, - "tablesPrivateDnsZoneResourceId": { - "value": "[parameters('tablesPrivateDnsZoneResourceId')]" - }, - "tags": { - "value": "[parameters('tags')]" - }, - "tier": { - "value": "[parameters('tier')]" - }, - "userAssignedIdentityResourceId": { - "value": "[parameters('userAssignedIdentityResourceId')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "10245709494871874056" - } - }, - "parameters": { - "blobsPrivateDnsZoneResourceId": { - "type": "string" - }, - "filesPrivateDnsZoneResourceId": { - "type": "string" - }, - "keyVaultUri": { - "type": "string" - }, - "location": { - "type": "string" - }, - "mlzTags": { - "type": "object" - }, - "queuesPrivateDnsZoneResourceId": { - "type": "string" - }, - "serviceToken": { - "type": "string" - }, - "skuName": { - "type": "string" - }, - "storageEncryptionKeyName": { - "type": "string" - }, - "subnetResourceId": { - "type": "string" - }, - "tablesPrivateDnsZoneResourceId": { - "type": "string" - }, - "tags": { - "type": "object" - }, - "tier": { - "type": "object" - }, - "userAssignedIdentityResourceId": { - "type": "string" - } - }, - "variables": { - "subResources": [ - { - "id": "[parameters('blobsPrivateDnsZoneResourceId')]", - "nic": "[parameters('tier').namingConvention.storageAccountBlobNetworkInterface]", - "pe": "[parameters('tier').namingConvention.storageAccountBlobPrivateEndpoint]" - }, - { - "id": "[parameters('filesPrivateDnsZoneResourceId')]", - "nic": "[parameters('tier').namingConvention.storageAccountFileNetworkInterface]", - "pe": "[parameters('tier').namingConvention.storageAccountFilePrivateEndpoint]" - }, - { - "id": "[parameters('queuesPrivateDnsZoneResourceId')]", - "nic": "[parameters('tier').namingConvention.storageAccountQueueNetworkInterface]", - "pe": "[parameters('tier').namingConvention.storageAccountQueuePrivateEndpoint]" - }, - { - "id": "[parameters('tablesPrivateDnsZoneResourceId')]", - "nic": "[parameters('tier').namingConvention.storageAccountTableNetworkInterface]", - "pe": "[parameters('tier').namingConvention.storageAccountTablePrivateEndpoint]" - } - ] - }, - "resources": [ - { - "type": "Microsoft.Storage/storageAccounts", - "apiVersion": "2023-01-01", - "name": "[uniqueString(replace(parameters('tier').namingConvention.storageAccount, parameters('serviceToken'), 'log'), resourceGroup().id)]", - "location": "[parameters('location')]", - "tags": "[union(if(contains(parameters('tags'), 'Microsoft.Storage/storageAccounts'), parameters('tags')['Microsoft.Storage/storageAccounts'], createObject()), parameters('mlzTags'))]", - "identity": { - "type": "UserAssigned", - "userAssignedIdentities": { - "[format('{0}', parameters('userAssignedIdentityResourceId'))]": {} - } - }, - "kind": "StorageV2", - "sku": { - "name": "[parameters('skuName')]" - }, - "properties": { - "accessTier": "Hot", - "allowBlobPublicAccess": false, - "allowCrossTenantReplication": false, - "allowedCopyScope": "PrivateLink", - "allowSharedKeyAccess": false, - "defaultToOAuthAuthentication": false, - "dnsEndpointType": "Standard", - "encryption": { - "identity": { - "userAssignedIdentity": "[parameters('userAssignedIdentityResourceId')]" - }, - "keySource": "Microsoft.KeyVault", - "keyvaultproperties": { - "keyvaulturi": "[parameters('keyVaultUri')]", - "keyname": "[parameters('storageEncryptionKeyName')]" - }, - "requireInfrastructureEncryption": true, - "services": { - "blob": { - "keyType": "Account", - "enabled": true - }, - "file": { - "keyType": "Account", - "enabled": true - }, - "queue": { - "keyType": "Account", - "enabled": true - }, - "table": { - "keyType": "Account", - "enabled": true - } - } - }, - "minimumTlsVersion": "TLS1_2", - "networkAcls": { - "bypass": "AzureServices", - "virtualNetworkRules": [], - "ipRules": [], - "defaultAction": "Deny" - }, - "publicNetworkAccess": "Disabled", - "supportsHttpsTrafficOnly": true - } - }, - { - "copy": { - "name": "privateEndpoints", - "count": "[length(variables('subResources'))]" - }, - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", - "name": "[variables('subResources')[copyIndex()].pe]", - "location": "[parameters('location')]", - "tags": "[union(if(contains(parameters('tags'), 'Microsoft.Network/privateEndpoints'), parameters('tags')['Microsoft.Network/privateEndpoints'], createObject()), parameters('mlzTags'))]", - "properties": { - "customNetworkInterfaceName": "[variables('subResources')[copyIndex()].nic]", - "privateLinkServiceConnections": [ - { - "name": "[variables('subResources')[copyIndex()].pe]", - "properties": { - "privateLinkServiceId": "[resourceId('Microsoft.Storage/storageAccounts', uniqueString(replace(parameters('tier').namingConvention.storageAccount, parameters('serviceToken'), 'log'), resourceGroup().id))]", - "groupIds": [ - "[split(split(variables('subResources')[copyIndex()].id, '/')[8], '.')[1]]" - ] - } - } - ], - "subnet": { - "id": "[parameters('subnetResourceId')]" - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Storage/storageAccounts', uniqueString(replace(parameters('tier').namingConvention.storageAccount, parameters('serviceToken'), 'log'), resourceGroup().id))]" - ] - }, - { - "copy": { - "name": "privateDnsZoneGroups", - "count": "[length(variables('subResources'))]" - }, - "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2021-08-01", - "name": "[format('{0}/{1}', variables('subResources')[copyIndex()].pe, uniqueString(replace(parameters('tier').namingConvention.storageAccount, parameters('serviceToken'), 'log'), resourceGroup().id))]", - "properties": { - "privateDnsZoneConfigs": [ - { - "name": "ipconfig1", - "properties": { - "privateDnsZoneId": "[variables('subResources')[copyIndex()].id]" - } - } - ] - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/privateEndpoints', variables('subResources')[copyIndex()].pe)]", - "[resourceId('Microsoft.Storage/storageAccounts', uniqueString(replace(parameters('tier').namingConvention.storageAccount, parameters('serviceToken'), 'log'), resourceGroup().id))]" - ] - } - ], - "outputs": { - "id": { - "type": "string", - "value": "[resourceId('Microsoft.Storage/storageAccounts', uniqueString(replace(parameters('tier').namingConvention.storageAccount, parameters('serviceToken'), 'log'), resourceGroup().id))]" - } - } - } - } - } - ], - "outputs": { - "storageAccountResourceId": { - "type": "string", - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('network').subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'storage'), '2022-09-01').outputs.id.value]" - } - } - } - }, - "dependsOn": [ - "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-cmk-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]", - "[subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]", - "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-network-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]", - "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]" - ] - }, - { - "condition": "[not(empty(parameters('virtualNetworkAddressPrefix')))]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('deploy-diag-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))]", - "location": "[deployment().location]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "deployActivityLogDiagnosticSetting": { - "value": "[parameters('deployActivityLogDiagnosticSetting')]" - }, - "deploymentNameSuffix": { - "value": "[parameters('deploymentNameSuffix')]" - }, - "keyVaultDiagnosticLogs": { - "value": "[parameters('keyVaultDiagnosticsLogs')]" - }, - "keyVaultName": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-cmk-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.keyVaultName.value]" - }, - "logAnalyticsWorkspaceResourceId": { - "value": "[parameters('logAnalyticsWorkspaceResourceId')]" - }, - "networkSecurityGroupDiagnosticsLogs": { - "value": "[parameters('networkSecurityGroupDiagnosticsLogs')]" - }, - "networkSecurityGroupDiagnosticsMetrics": { - "value": "[parameters('networkSecurityGroupDiagnosticsMetrics')]" - }, - "networkSecurityGroupName": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-network-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.networkSecurityGroupName.value]" - }, - "resourceGroupName": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.name.value]" - }, - "serviceToken": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value.service]" - }, - "storageAccountResourceId": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-storage-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.storageAccountResourceId.value]" - }, - "tier": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tiers.value[0]]" - }, - "virtualNetworkDiagnosticsLogs": { - "value": "[parameters('virtualNetworkDiagnosticsLogs')]" - }, - "virtualNetworkDiagnosticsMetrics": { - "value": "[parameters('virtualNetworkDiagnosticsMetrics')]" - }, - "virtualNetworkName": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-network-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.virtualNetworkName.value]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "13756267312445220554" - } - }, - "parameters": { - "deployActivityLogDiagnosticSetting": { - "type": "bool" - }, - "deploymentNameSuffix": { - "type": "string" - }, - "keyVaultDiagnosticLogs": { - "type": "array" - }, - "keyVaultName": { - "type": "string" - }, - "logAnalyticsWorkspaceResourceId": { - "type": "string" - }, - "networkSecurityGroupDiagnosticsLogs": { - "type": "array" - }, - "networkSecurityGroupDiagnosticsMetrics": { - "type": "array" - }, - "networkSecurityGroupName": { - "type": "string" - }, - "resourceGroupName": { - "type": "string" - }, - "serviceToken": { - "type": "string" - }, - "storageAccountResourceId": { - "type": "string" - }, - "tier": { - "type": "object" - }, - "virtualNetworkDiagnosticsLogs": { - "type": "array" - }, - "virtualNetworkDiagnosticsMetrics": { - "type": "array" - }, - "virtualNetworkName": { - "type": "string" - } - }, - "resources": [ - { - "condition": "[parameters('deployActivityLogDiagnosticSetting')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('deploy-activity-diags-{0}-{1}', parameters('tier').shortName, parameters('deploymentNameSuffix'))]", - "subscriptionId": "[parameters('tier').subscriptionId]", - "location": "[deployment().location]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "logAnalyticsWorkspaceId": { - "value": "[parameters('logAnalyticsWorkspaceResourceId')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "5964906331561259426" - } - }, - "parameters": { - "logAnalyticsWorkspaceId": { - "type": "string" - } - }, - "resources": [ - { - "type": "Microsoft.Insights/diagnosticSettings", - "apiVersion": "2017-05-01-preview", - "name": "[format('diag-activity-log-{0}', subscription().subscriptionId)]", - "properties": { - "workspaceId": "[parameters('logAnalyticsWorkspaceId')]", - "logs": [ - { - "category": "Administrative", - "enabled": true - }, - { - "category": "Security", - "enabled": true - }, - { - "category": "ServiceHealth", - "enabled": true - }, - { - "category": "Alert", - "enabled": true - }, - { - "category": "Recommendation", - "enabled": true - }, - { - "category": "Policy", - "enabled": true - }, - { - "category": "Autoscale", - "enabled": true - }, - { - "category": "ResourceHealth", - "enabled": true - } - ] - } - } - ] - } - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('deploy-kv-diags-{0}-{1}', parameters('tier').shortName, parameters('deploymentNameSuffix'))]", - "subscriptionId": "[parameters('tier').subscriptionId]", - "resourceGroup": "[parameters('resourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "keyVaultDiagnosticSettingName": { - "value": "[replace(parameters('tier').namingConvention.keyVaultDiagnosticSetting, format('{0}-', parameters('serviceToken')), '')]" - }, - "keyVaultName": { - "value": "[parameters('keyVaultName')]" - }, - "keyVaultStorageAccountId": { - "value": "[parameters('storageAccountResourceId')]" - }, - "logAnalyticsWorkspaceResourceId": { - "value": "[parameters('logAnalyticsWorkspaceResourceId')]" - }, - "logs": { - "value": "[parameters('keyVaultDiagnosticLogs')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "13721817451936402949" - } - }, - "parameters": { - "keyVaultDiagnosticSettingName": { - "type": "string" - }, - "keyVaultName": { - "type": "string" - }, - "keyVaultStorageAccountId": { - "type": "string" - }, - "logAnalyticsWorkspaceResourceId": { - "type": "string" - }, - "logs": { - "type": "array" - } - }, - "resources": [ - { - "type": "Microsoft.Insights/diagnosticSettings", - "apiVersion": "2017-05-01-preview", - "scope": "[format('Microsoft.KeyVault/vaults/{0}', parameters('keyVaultName'))]", - "name": "[parameters('keyVaultDiagnosticSettingName')]", - "properties": { - "storageAccountId": "[parameters('keyVaultStorageAccountId')]", - "workspaceId": "[parameters('logAnalyticsWorkspaceResourceId')]", - "logs": "[parameters('logs')]" - } - } - ] - } - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('deploy-nsg-diags-{0}-{1}', parameters('tier').shortName, parameters('deploymentNameSuffix'))]", - "subscriptionId": "[parameters('tier').subscriptionId]", - "resourceGroup": "[parameters('resourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "logAnalyticsWorkspaceResourceId": { - "value": "[parameters('logAnalyticsWorkspaceResourceId')]" - }, - "logs": { - "value": "[parameters('networkSecurityGroupDiagnosticsLogs')]" - }, - "logStorageAccountResourceId": { - "value": "[parameters('storageAccountResourceId')]" - }, - "metrics": { - "value": "[parameters('networkSecurityGroupDiagnosticsMetrics')]" - }, - "networkSecurityGroupDiagnosticSettingName": { - "value": "[parameters('tier').namingConvention.networkSecurityGroupDiagnosticSetting]" - }, - "networkSecurityGroupName": { - "value": "[parameters('networkSecurityGroupName')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "5533529544253509904" - } - }, - "parameters": { - "logAnalyticsWorkspaceResourceId": { - "type": "string" - }, - "logs": { - "type": "array" - }, - "logStorageAccountResourceId": { - "type": "string" - }, - "metrics": { - "type": "array" - }, - "networkSecurityGroupDiagnosticSettingName": { - "type": "string" - }, - "networkSecurityGroupName": { - "type": "string" - } - }, - "resources": [ - { - "type": "Microsoft.Insights/diagnosticSettings", - "apiVersion": "2017-05-01-preview", - "scope": "[format('Microsoft.Network/networkSecurityGroups/{0}', parameters('networkSecurityGroupName'))]", - "name": "[parameters('networkSecurityGroupDiagnosticSettingName')]", - "properties": { - "storageAccountId": "[parameters('logStorageAccountResourceId')]", - "workspaceId": "[parameters('logAnalyticsWorkspaceResourceId')]", - "logs": "[parameters('logs')]", - "metrics": "[parameters('metrics')]" - } - } - ] - } - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('deploy-vnet-diags-{0}-{1}', parameters('tier').shortName, parameters('deploymentNameSuffix'))]", - "subscriptionId": "[parameters('tier').subscriptionId]", - "resourceGroup": "[parameters('resourceGroupName')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "logAnalyticsWorkspaceResourceId": { - "value": "[parameters('logAnalyticsWorkspaceResourceId')]" - }, - "logs": { - "value": "[parameters('virtualNetworkDiagnosticsLogs')]" - }, - "logStorageAccountResourceId": { - "value": "[parameters('storageAccountResourceId')]" - }, - "metrics": { - "value": "[parameters('virtualNetworkDiagnosticsMetrics')]" - }, - "virtualNetworkDiagnosticSettingName": { - "value": "[parameters('tier').namingConvention.virtualNetworkDiagnosticSetting]" - }, - "virtualNetworkName": { - "value": "[parameters('virtualNetworkName')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "169088610601729285" - } - }, - "parameters": { - "logAnalyticsWorkspaceResourceId": { - "type": "string" - }, - "logs": { - "type": "array" - }, - "logStorageAccountResourceId": { - "type": "string" - }, - "metrics": { - "type": "array" - }, - "virtualNetworkDiagnosticSettingName": { - "type": "string" - }, - "virtualNetworkName": { - "type": "string" - } - }, - "resources": [ - { - "type": "Microsoft.Insights/diagnosticSettings", - "apiVersion": "2017-05-01-preview", - "scope": "[format('Microsoft.Network/virtualNetworks/{0}', parameters('virtualNetworkName'))]", - "name": "[parameters('virtualNetworkDiagnosticSettingName')]", - "properties": { - "storageAccountId": "[parameters('logStorageAccountResourceId')]", - "workspaceId": "[parameters('logAnalyticsWorkspaceResourceId')]", - "logs": "[parameters('logs')]", - "metrics": "[parameters('metrics')]" - } - } - ] - } - } - } - ] - } - }, - "dependsOn": [ - "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-cmk-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]", - "[subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]", - "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-network-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]", - "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]", - "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-storage-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]" - ] - }, - { - "condition": "[and(parameters('deployPolicy'), not(empty(parameters('virtualNetworkAddressPrefix'))))]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('assign-policy-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))]", - "location": "[deployment().location]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "deploymentNameSuffix": { - "value": "[parameters('deploymentNameSuffix')]" - }, - "location": { - "value": "[parameters('location')]" - }, - "logAnalyticsWorkspaceResourceId": { - "value": "[parameters('logAnalyticsWorkspaceResourceId')]" - }, - "tiers": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tiers.value]" - }, - "policy": { - "value": "[parameters('policy')]" - }, - "resourceGroupNames": { - "value": [ - "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.name.value]" - ] - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "2239324965165269347" - } - }, - "parameters": { - "deploymentNameSuffix": { - "type": "string" - }, - "location": { - "type": "string" - }, - "logAnalyticsWorkspaceResourceId": { - "type": "string" - }, - "policy": { - "type": "string" - }, - "resourceGroupNames": { - "type": "array" - }, - "tiers": { - "type": "array" - } - }, - "resources": [ - { - "copy": { - "name": "policyAssignment", - "count": "[length(parameters('tiers'))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('assign-policy-{0}-{1}', parameters('tiers')[copyIndex()].name, parameters('deploymentNameSuffix'))]", - "subscriptionId": "[parameters('tiers')[copyIndex()].subscriptionId]", - "resourceGroup": "[parameters('resourceGroupNames')[copyIndex()]]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "builtInAssignment": { - "value": "[parameters('policy')]" - }, - "logAnalyticsWorkspaceResourceId": { - "value": "[parameters('logAnalyticsWorkspaceResourceId')]" - }, - "location": { - "value": "[parameters('location')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "16715470716191237776" - } - }, - "parameters": { - "builtInAssignment": { - "type": "string", - "defaultValue": "NISTRev4", - "allowedValues": [ - "NISTRev4", - "NISTRev5", - "IL5", - "CMMC" - ], - "metadata": { - "description": "[NISTRev4/NISTRev5/IL5/CMMC] Built-in policy assignments to assign, default is NISTRev4. IL5 is only available for AzureUsGovernment and will switch to NISTRev4 if tried in AzureCloud." - } - }, - "logAnalyticsWorkspaceResourceId": { - "type": "string" - }, - "deployRemediation": { - "type": "bool", - "defaultValue": false, - "metadata": { - "description": "Starts a policy remediation for the VM Agent policies in hub RG. Set to false by default since this is time consuming in deployment." - } - }, - "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]", - "metadata": { - "description": "The location of this resource" - } - } - }, - "variables": { - "$fxv#0": " {\r\n \"listOfMembersToExcludeFromWindowsVMAdministratorsGroup\": \r\n {\r\n \"value\": \"admin\"\r\n },\r\n \"listOfMembersToIncludeInWindowsVMAdministratorsGroup\": \r\n {\r\n \"value\": \"azureuser\"\r\n },\r\n \"logAnalyticsWorkspaceIdforVMReporting\": \r\n {\r\n \"value\": \"\"\r\n },\r\n \"IncludeArcMachines\": \r\n {\r\n \"value\": \"true\"\r\n },\r\n \"MinimumTLSVersion-5752e6d6-1206-46d8-8ab1-ecc2f71a8112\": \r\n {\r\n \"value\": \"1.2\"\r\n },\r\n \"NotAvailableMachineState-bed48b13-6647-468e-aa2f-1af1d3f4dd40\": \r\n {\r\n \"value\": \"Compliant\"\r\n },\r\n \"requiredRetentionDays\": \r\n {\r\n \"value\": \"365\"\r\n },\r\n \"resourceGroupName-b6e2945c-0b7b-40f5-9233-7a5323b5cdc6\": \r\n {\r\n \"value\": \"NetworkWatcherRG\"\r\n }\r\n }", - "$fxv#1": " {\r\n \"IncludeArcMachines\": \r\n {\r\n \"value\": \"true\"\r\n },\r\n \"MinimumTLSVersion-5752e6d6-1206-46d8-8ab1-ecc2f71a8112\": \r\n {\r\n \"value\": \"1.2\"\r\n },\r\n \"NotAvailableMachineState-bed48b13-6647-468e-aa2f-1af1d3f4dd40\": \r\n {\r\n \"value\": \"Compliant\"\r\n },\r\n \"requiredRetentionDays\": \r\n {\r\n \"value\": \"365\"\r\n },\r\n \"resourceGroupName-b6e2945c-0b7b-40f5-9233-7a5323b5cdc6\": \r\n {\r\n \"value\": \"NetworkWatcherRG\"\r\n }\r\n }", - "$fxv#2": "{\r\n \"IncludeArcMachines\" : { \r\n \"value\" : \"false\"\r\n },\r\n \"NotAvailableMachineState-bed48b13-6647-468e-aa2f-1af1d3f4dd40\" : { \r\n \"value\" : \"Compliant\"\r\n },\r\n \"MinimumTLSVersionForWindowsServers\" : { \r\n \"value\" : \"1.2\"\r\n },\r\n \"requiredRetentionDays\" : { \r\n \"value\" : \"365\"\r\n },\r\n \"effect-febd0533-8e55-448f-b837-bd0e06f16469\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"allowedContainerImagesRegex-febd0533-8e55-448f-b837-bd0e06f16469\" : { \r\n \"value\" : \"^(.+){0}$\"\r\n },\r\n \"effect-95edb821-ddaf-4404-9732-666045e056b4\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"effect-440b515e-a580-421e-abeb-b159a61ddcbc\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"effect-233a2a17-77ca-4fb1-9b6b-69223d272a44\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"effect-e345eecc-fa47-480f-9e88-67dcc122b164\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"cpuLimit-e345eecc-fa47-480f-9e88-67dcc122b164\" : { \r\n \"value\" : \"0\"\r\n },\r\n \"memoryLimit-e345eecc-fa47-480f-9e88-67dcc122b164\" : { \r\n \"value\" : \"0\"\r\n },\r\n \"effect-f06ddb64-5fa3-4b77-b166-acb36f7f6042\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"runAsUserRule-f06ddb64-5fa3-4b77-b166-acb36f7f6042\" : { \r\n \"value\" : \"MustRunAsNonRoot\"\r\n },\r\n \"runAsGroupRule-f06ddb64-5fa3-4b77-b166-acb36f7f6042\" : { \r\n \"value\" : \"RunAsAny\"\r\n },\r\n \"supplementalGroupsRule-f06ddb64-5fa3-4b77-b166-acb36f7f6042\" : { \r\n \"value\" : \"RunAsAny\"\r\n },\r\n \"fsGroupRule-f06ddb64-5fa3-4b77-b166-acb36f7f6042\" : { \r\n \"value\" : \"RunAsAny\"\r\n },\r\n \"effect-1c6e92c9-99f0-4e55-9cf2-0c234dc48f99\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"effect-47a1ee2f-2a2a-4576-bf2a-e0e36709c2b8\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"effect-df49d893-a74c-421d-bc95-c663042e5b80\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"effect-1a5b4dca-0b6f-4cf5-907c-56316bc1bf3d\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"effect-c26596ff-4d70-4e6a-9a30-c2506bd2f80c\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"effect-511f5417-5d12-434d-ab2e-816901e72a5e\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"effect-82985f06-dc18-4a48-bc1c-b9f4f0098cfe\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"effect-098fc59e-46c7-4d99-9b16-64990e543d75\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"NetworkWatcherResourceGroupName\" : { \r\n \"value\" : \"NetworkWatcherRG\"\r\n },\r\n \"setting-a6fb4358-5bf4-4ad7-ba82-2cd2f41ce5e9\" : { \r\n \"value\" : \"enabled\"\r\n },\r\n \"aadAuthenticationInServiceFabricMonitoringEffect\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-71ef260a-8f18-47b7-abcb-62d0673d94dc\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-055aa869-bc98-4af8-bafc-23f1ab6ffe2c\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-564feb30-bf6a-4854-b4bb-0d2d2d1e6c66\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-862e97cf-49fc-4a5c-9de4-40d4e2e7c8eb\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-d9da03a1-f3c3-412a-9709-947156872263\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-617c02be-7f02-4efd-8836-3180d47b6c68\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-0b60c0b2-2dc2-4e1c-b5c9-abbed971de53\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-1e66c121-a66a-4b1f-9b83-0fd99bf0fc2d\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-ec068d99-e9c7-401f-8cef-5bdde4e6ccf1\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-c349d81b-9985-44ae-a8da-ff98d108ede8\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-3657f5a0-770e-44a3-b44e-9431ba1e9735\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-b4ac1030-89c5-4697-8e00-28b5ba6a8811\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"effect-ea0dfaed-95fb-448c-934e-d6e713ce393d\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"effect-4733ea7b-a883-42fe-8cac-97454c2a9e4a\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-f4b53539-8df9-40e4-86c6-6b607703bd4e\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-41425d9f-d1a5-499a-9932-f8ed8453932c\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-fc4d8e41-e223-45ea-9bf5-eada37891d87\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-86efb160-8de7-451d-bc08-5d475b0aadae\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-4ec52d6d-beb7-40c4-9a9e-fe753254690e\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-64d314f6-6062-4780-a861-c23e8951bee5\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-1fd32ebd-e4c3-4e13-a54a-d7422d4d95f6\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-fa298e57-9444-42ba-bf04-86e8470e32c7\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"effect-67121cc7-ff39-4ab8-b7e3-95b84dab487d\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-1f905d99-2ab7-462c-a6b0-f709acca6c8f\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"effect-5b9159ae-1701-4a6f-9a7a-aa9c8ddd0580\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-ba769a63-b8cc-4b2d-abf6-ac33c7204be8\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-81e74cea-30fd-40d5-802f-d72103c2aaaa\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-0aa61e00-0a01-4a3c-9945-e93cffedf0e6\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-47031206-ce96-41f8-861b-6a915f3de284\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-87ba29ef-1ab3-4d82-b763-87fcd4f531f7\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"effect-51522a96-0869-4791-82f3-981000c2c67f\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"effect-b5ec538c-daa0-4006-8596-35468b9148e8\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-56a5ee18-2ae6-4810-86f7-18e39ce5629b\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-2e94d99a-8a36-4563-bc77-810d8893b671\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-1fafeaf6-7927-4059-a50a-8eb2a7a6f2b5\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-99e9ccd8-3db9-4592-b0d1-14b1715a4d8a\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-1f68a601-6e6d-4e42-babf-3f643a047ea2\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"effect-f7d52b2d-e161-4dfa-a82b-55e564167385\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-7d7be79c-23ba-4033-84dd-45e2a5ccdd67\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-ca91455f-eace-4f96-be59-e6e2c35b4816\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-702dd420-7fcc-42c5-afe8-4026edd20fe0\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"diagnosticsLogsInRedisCacheMonitoringEffect\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"secureTransferToStorageAccountMonitoringEffect\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-d0793b48-0edc-4296-a390-4c75d1bdfd71\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-7d092e0a-7acd-40d2-a975-dca21cae48c4\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-2a1a9cdf-e04d-429a-8416-3bfb72a1b26f\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"disableUnrestrictedNetworkToStorageAccountMonitoringEffect\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-55615ac9-af46-4a59-874e-391cc3dfb490\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-1b8ca024-1d5c-4dec-8995-b1a932b41780\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-037eea7a-bd0a-46c5-9a66-03aea78705d3\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-53503636-bcc9-4748-9663-5348217f160f\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-40cec1dd-a100-4920-b15b-3024fe8901ab\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-0725b4dd-7e76-479c-a735-68e7ee23d5ca\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-a049bf77-880b-470f-ba6d-9f21c530cf83\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-ee980b6d-0eca-4501-8d54-f6290fd512c3\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-1d84d5fb-01f6-4d12-ba4f-4a26081d403d\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-37e0d2fe-28a5-43d6-a273-67d37d1f5606\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"identityDesignateMoreThanOneOwnerMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"diskEncryptionMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"emailNotificationToSubscriptionOwnerHighSeverityAlertsEnabledEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"functionAppDisableRemoteDebuggingMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"sqlDbEncryptionMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"vulnerabilityAssessmentOnManagedInstanceMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"ensurePHPVersionLatestForAPIAppEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"aadAuthenticationInSqlServerMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"vmssEndpointProtectionMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"vmssOsVulnerabilitiesMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"adaptiveApplicationControlsMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"geoRedundantBackupShouldBeEnabledForAzureDatabaseForPostgreSQLEffect\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"ensureJavaVersionLatestForWebAppEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"identityDesignateLessThanOwnersMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"securityContactEmailAddressForSubscriptionEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"webAppRestrictCORSAccessMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"identityRemoveExternalAccountWithWritePermissionsMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"identityRemoveExternalAccountWithReadPermissionsMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"identityRemoveDeprecatedAccountMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"functionAppEnforceHttpsMonitoringEffect\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"ensurePythonVersionLatestForWebAppEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"ensurePythonVersionLatestForFunctionAppEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"ensurePHPVersionLatestForWebAppEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"ensurePythonVersionLatestForAPIAppEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"geoRedundantBackupShouldBeEnabledForAzureDatabaseForMySQLEffect\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"systemUpdatesMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"ensureJavaVersionLatestForAPIAppEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"ensureHTTPVersionLatestForWebAppEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"apiAppRequireLatestTlsMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"identityEnableMFAForWritePermissionsMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"ensureHTTPVersionLatestForAPIAppEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"ensureJavaVersionLatestForFunctionAppEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"nextGenerationFirewallMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"useRbacRulesMonitoringEffect\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"webAppEnforceHttpsMonitoringEffect\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"sqlServerAuditingMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"vnetEnableDDoSProtectionMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"identityEnableMFAForOwnerPermissionsMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"sqlServerAdvancedDataSecurityMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"sqlManagedInstanceAdvancedDataSecurityMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"endpointProtectionMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"jitNetworkAccessMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"apiAppEnforceHttpsMonitoringEffect\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"geoRedundantStorageShouldBeEnabledForStorageAccountsEffect\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"vmssSystemUpdatesMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"webAppDisableRemoteDebuggingMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"longtermGeoRedundantBackupEnabledAzureSQLDatabasesEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"systemConfigurationsMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"ensureHTTPVersionLatestForFunctionAppEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"identityEnableMFAForReadPermissionsMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"containerBenchmarkMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"apiAppDisableRemoteDebuggingMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"identityRemoveDeprecatedAccountWithOwnerPermissionsMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"vulnerabilityAssessmentOnServerMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"webAppRequireLatestTlsMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"identityRemoveExternalAccountWithOwnerPermissionsMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"functionAppRequireLatestTlsMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"kubernetesServiceVersionUpToDateMonitoringEffect\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"sqlDbVulnerabilityAssesmentMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"membersToIncludeInLocalAdministratorsGroup\" : { \r\n \"value\" : \"\"\r\n },\r\n \"membersToExcludeInLocalAdministratorsGroup\" : { \r\n \"value\" : \"\"\r\n },\r\n \"logAnalyticsWorkspaceIDForVMAgents\" : { \r\n \"value\" : \"\"\r\n },\r\n \"PHPLatestVersionForAppServices\" : { \r\n \"value\" : \"7.4\"\r\n },\r\n \"JavaLatestVersionForAppServices\" : { \r\n \"value\" : \"11\"\r\n },\r\n \"WindowsPythonLatestVersionForAppServices\" : { \r\n \"value\" : \"3.6\"\r\n },\r\n \"LinuxPythonLatestVersionForAppServices\" : { \r\n \"value\" : \"3.9\"\r\n },\r\n \"ensureDotNetFrameworkLatestForFunctionAppEffect\" : { \r\n \"value\" : \"Disabled\"\r\n },\r\n \"sqlManagedInstanceAdvancedDataSecurityEmailsMonitoringEffect\" : { \r\n \"value\" : \"Disabled\"\r\n },\r\n \"vulnerabilityAssessmentMonitoringEffect\" : { \r\n \"value\" : \"Disabled\"\r\n },\r\n \"ensureDotNetFrameworkLatestForWebAppEffect\" : { \r\n \"value\" : \"Disabled\"\r\n },\r\n \"sqlServerAdvancedDataSecurityEmailsMonitoringEffect\" : { \r\n \"value\" : \"Disabled\"\r\n },\r\n \"microsoftIaaSAntimalwareExtensionShouldBeDeployedOnWindowsServersEffect\" : { \r\n \"value\" : \"Disabled\"\r\n },\r\n \"securityCenterStandardPricingTierShouldBeSelectedEffect\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"theLogAnalyticsAgentShouldBeInstalledOnVirtualMachinesEffect\" : { \r\n \"value\" : \"Disabled\"\r\n },\r\n \"ensurePHPVersionLatestForFunctionAppEffect\" : { \r\n \"value\" : \"Disabled\"\r\n },\r\n \"sqlManagedInstanceAdvancedDataSecurityEmailAdminsMonitoringEffect\" : { \r\n \"value\" : \"Disabled\"\r\n },\r\n \"securityContactPhoneNumberShouldBeProvidedForSubscriptionEffect\" : { \r\n \"value\" : \"Disabled\"\r\n },\r\n \"threatDetectionTypesOnManagedInstanceMonitoringEffect\" : { \r\n \"value\" : \"Disabled\"\r\n },\r\n \"ensureDotNetFrameworkLatestForAPIAppEffect\" : { \r\n \"value\" : \"Disabled\"\r\n },\r\n \"sqlServerAdvancedDataSecurityEmailAdminsMonitoringEffect\" : { \r\n \"value\" : \"Disabled\"\r\n },\r\n \"threatDetectionTypesOnServerMonitoringEffect\" : { \r\n \"value\" : \"Disabled\"\r\n },\r\n \"theLogAnalyticsAgentShouldBeInstalledOnVirtualMachineScaleSetsEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n }\r\n}", - "$fxv#3": "{\r\n \"logAnalyticsWorkspaceId-f47b5582-33ec-4c5c-87c0-b010a6b2e917\" : { \r\n \"value\" : \"\"\r\n },\r\n \"effect-09024ccc-0c5f-475e-9457-b7c0d9ed487b\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"MembersToExclude-69bf4abd-ca1e-4cf6-8b5a-762d42e61d4f\" :{\r\n \"value\": \"\"\r\n },\r\n \"MembersToInclude-30f71ea1-ac77-4f26-9fc5-2d926bbd4ba7\": {\r\n \"value\": \"\"\r\n },\r\n \"effect-0961003e-5a0a-4549-abde-af6a37f2724d\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-0b15565f-aa9e-48ba-8619-45960f2c314d\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-0e60b895-3786-45da-8377-9c6b4b6ac5f9\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-17k78e20-9358-41c9-923c-fb736d382a12\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-1bc1795e-d44a-4d48-9b3b-6fff0fd5f9ba\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"PHPLatestVersion\" : { \r\n \"value\" : \"7.3\"\r\n },\r\n \"effect-22bee202-a82f-4305-9a2a-6d7f44d4dedb\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-26a828e1-e88f-464e-bbb3-c134a282b9de\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-34c877ad-507e-4c82-993e-3452a6e0ad3c\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-3c735d8a-a4ba-4a3a-b7cf-db7754cf57f4\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-404c3081-a854-4457-ae30-26a93ef643f9\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-47a6b606-51aa-4496-8bb7-64b11cf66adc\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-496223c3-ad65-4ecd-878a-bae78737e9ed\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"JavaLatestVersion\" : { \r\n \"value\" : \"11\"\r\n },\r\n \"effect-4f11b553-d42e-4e3a-89be-32ca364cad4c\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-4f4f78b8-e367-4b10-a341-d9a4ad5cf1c7\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-5c607a2e-c700-4744-8254-d77e7c9eb5e4\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-5f76cf89-fbf2-47fd-a3f4-b891fa780b60\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-6b1cbf55-e8b6-442f-ba4c-7246b6381474\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-6d555dd1-86f2-4f1c-8ed7-5abae7c6cbab\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-7008174a-fd10-4ef0-817e-fc820a951d73\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"LinuxPythonLatestVersion\" : { \r\n \"value\" : \"3.8\"\r\n },\r\n \"effect-7238174a-fd10-4ef0-817e-fc820a951d73\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-7261b898-8a84-4db8-9e04-18527132abb3\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-74c3584d-afae-46f7-a20a-6f8adba71a16\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-86b3d65f-7626-441e-b690-81a8b71cff60\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-88999f4c-376a-45c8-bcb3-4058f713cf39\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-8c122334-9d20-4eb8-89ea-ac9a705b74ae\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-8cb6aa8b-9e41-4f4e-aa25-089a7ac2581e\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-9297c21d-2ed6-4474-b48f-163f75654ce3\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-991310cd-e9f3-47bc-b7b6-f57b557d07db\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-9b597639-28e4-48eb-b506-56b05d366257\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-9d0b6ea4-93e2-4578-bf2f-6bb17d22b4bc\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-9daedab3-fb2d-461e-b861-71790eead4f6\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-a4af4a39-4135-47fb-b175-47fbdf85311d\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-a6fb4358-5bf4-4ad7-ba82-2cd2f41ce5e9\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"setting-a6fb4358-5bf4-4ad7-ba82-2cd2f41ce5e9\" : { \r\n \"value\" : \"enabled\"\r\n },\r\n \"effect-a70ca396-0a34-413a-88e1-b956c1e683be\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-aa633080-8b72-40c4-a2d7-d00c03e80bed\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-abfb4388-5bf4-4ad7-ba82-2cd2f41ceae9\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-abfb7388-5bf4-4ad7-ba99-2cd2f41cebb9\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-af6cd1bd-1635-48cb-bde7-5b15693900b9\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"resourceGroupName-b6e2945c-0b7b-40f5-9233-7a5323b5cdc6\" : { \r\n \"value\" : \"NetworkWatcherRG\"\r\n },\r\n \"effect-b7ddfbdc-1260-477d-91fd-98bd9be789a6\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-c3f317a7-a95c-4547-b7e7-11017ebdf2fe\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-cb510bfd-1cba-4d9f-a230-cb0976f4bb71\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-e1e5fd5d-3e4c-4ce1-8661-7d1873ae6b15\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-e2c1c086-2d84-4019-bff3-c44ccd95113c\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-e3576e28-8b17-4677-84c3-db2990658d64\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-e8cbc669-f12d-49eb-93e7-9273119e9933\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-e9c8d085-d9cc-4b17-9cdc-059f1f01f19e\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-ebb62a0c-3560-49e1-89ed-27e074e9f8ad\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-efbde977-ba53-4479-b8e9-10b957924fbf\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-f0e6e85b-9b9f-4a4b-b67b-f730d42f1b0b\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-f6de0be7-9a8a-4b8a-b349-43cf02d22f7c\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-f8456c1c-aa66-4dfb-861a-25d127b775c9\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-f9d614c5-c173-4d56-95a7-b4437057d193\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-fb893a29-21bb-418c-a157-e99480ec364c\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-feedbf84-6b99-488c-acc2-71c829aa5ffc\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-3b980d31-7904-4bb7-8575-5665739a8052\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-6e2593d9-add6-4083-9c9b-4b7d2188c899\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-b607c5de-e7d9-4eee-9e5c-83f1bcee4fa0\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-12430be1-6cc8-4527-a9a8-e3d38f250096\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"modeRequirement-12430be1-6cc8-4527-a9a8-e3d38f250096\" : { \r\n \"value\" : \"Detection\"\r\n },\r\n \"effect-425bea59-a659-4cbb-8d31-34499bd030b8\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"modeRequirement-425bea59-a659-4cbb-8d31-34499bd030b8\" : { \r\n \"value\" : \"Detection\"\r\n },\r\n \"effect-564feb30-bf6a-4854-b4bb-0d2d2d1e6c66\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-055aa869-bc98-4af8-bafc-23f1ab6ffe2c\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-013e242c-8828-4970-87b3-ab247555486d\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-d38fc420-0735-4ef3-ac11-c806f651a570\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-a1181c5f-672a-477a-979a-7d58aa086233\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-308fbb08-4ab8-4e67-9b29-592e93fb94fa\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-4da35fc9-c9e7-4960-aec9-797fe7d9051d\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-523b5cd1-3e23-492f-a539-13118b6d1e3a\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-7fe3b40f-802b-4cdd-8bd4-fd799c948cc2\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-c25d9a16-bc35-4e15-a7e5-9db606bf9ed4\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-b0f33259-77d7-4c9e-aac6-3aabcfae693c\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-037eea7a-bd0a-46c5-9a66-03aea78705d3\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-0725b4dd-7e76-479c-a735-68e7ee23d5ca\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-0820b7b9-23aa-4725-a1ce-ae4558f718e5\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-2c89a2e5-7285-40fe-afe0-ae8654b92fab\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-358c20a6-3f9e-4f0e-97ff-c6ce485e2aac\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-5744710e-cc2f-4ee8-8809-3b11e89f4bc9\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-ac4a19c2-fa67-49b4-8ae5-0b2e78c49457\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-c9d007d0-c057-4772-b18c-01e546713bcd\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-d0793b48-0edc-4296-a390-4c75d1bdfd71\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-e372f825-a257-4fb8-9175-797a8a8627d6\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-d158790f-bfb0-486c-8631-2dc6b4e8e6af\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-e802a67a-daf5-4436-9ea6-f6d821dd0c5d\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-a451c1ef-c6ca-483d-87ed-f49761e3ffb5\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-b954148f-4c11-4c38-8221-be76711e194a-MicrosoftSql-servers-firewallRules-delete\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-b954148f-4c11-4c38-8221-be76711e194a-MicrosoftNetwork-networkSecurityGroups-delete\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-b954148f-4c11-4c38-8221-be76711e194a-MicrosoftClassicNetwork-networkSecurityGroups-delete\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-b954148f-4c11-4c38-8221-be76711e194a-MicrosoftNetwork-networkSecurityGroups-securityRules-delete\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-b954148f-4c11-4c38-8221-be76711e194a-MicrosoftClassicNetwork-networkSecurityGroups-securityRules-delete\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-ae89ebca-1c92-4898-ac2c-9f63decb045c\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-d26f7642-7545-4e18-9b75-8c9bbdee3a9a\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-1a4e592a-6a6e-44a5-9814-e36264ca96e7\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-7796937f-307b-4598-941c-67d3a05ebfe7\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-c5447c04-a4d7-4ba8-a263-c9ee321a6858\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-41388f1c-2db0-4c25-95b2-35d7f5ccbfa9\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-b02aacc0-b073-424e-8298-42b22829ee0a\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-057d6cfe-9c4f-4a6d-bc60-14420ea1f1a9\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-0ec47710-77ff-4a3d-9181-6aa50af424d0\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-48af4db5-9b8b-401c-8e74-076be876a430\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-82339799-d096-41ae-8538-b108becf0970\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-1b7aa243-30e4-4c9e-bca8-d0d3022b634a\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-ef2a8f2a-b3d9-49cd-a8a8-9a3aaaf647d9\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-bb91dfba-c30d-4263-9add-9c2384e659a6\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-e71308d3-144b-4262-b144-efdc3cc90517\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-2bdd0062-9d75-436e-89df-487dd8e4b3c7\" : { \r\n \"value\" : \"Disabled\"\r\n },\r\n \"effect-4733ea7b-a883-42fe-8cac-97454c2a9e4a\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-67121cc7-ff39-4ab8-b7e3-95b84dab487d\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-6fac406b-40ca-413b-bf8e-0bf964659c25\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-81e74cea-30fd-40d5-802f-d72103c2aaaa\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-c349d81b-9985-44ae-a8da-ff98d108ede8\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-f4b53539-8df9-40e4-86c6-6b607703bd4e\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-ec068d99-e9c7-401f-8cef-5bdde4e6ccf1\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-048248b0-55cd-46da-b1ff-39efd52db260\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-0d134df8-db83-46fb-ad72-fe0c9428c8dd\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-2c89a2e5-7285-40fe-afe0-ae8654b92fb2\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-3657f5a0-770e-44a3-b44e-9431ba1e9735\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-5b9159ae-1701-4a6f-9a7a-aa9c8ddd0580\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-617c02be-7f02-4efd-8836-3180d47b6c68\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-7d7be79c-23ba-4033-84dd-45e2a5ccdd67\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-87ba29ef-1ab3-4d82-b763-87fcd4f531f7\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"effect-f7d52b2d-e161-4dfa-a82b-55e564167385\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-c43e4a30-77cb-48ab-a4dd-93f175c63b57\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-0b60c0b2-2dc2-4e1c-b5c9-abbed971de53\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-1e66c121-a66a-4b1f-9b83-0fd99bf0fc2d\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-1f314764-cb73-4fc9-b863-8eca98ac36e9\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-123a3936-f020-408a-ba0c-47873faf1534\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n }\r\n}\r\n", - "modifiedAssignment": "[if(and(equals(toLower(environment().name), toLower('AzureCloud')), equals(toLower(parameters('builtInAssignment')), toLower('IL5'))), 'NISTRev4', parameters('builtInAssignment'))]", - "assignmentName": "[format('{0} {1}', variables('modifiedAssignment'), resourceGroup().name)]", - "agentVmssAssignmentName": "[format('Deploy VMSS Agents {0}', resourceGroup().name)]", - "agentVmAssignmentName": "[format('Deploy VM Agents {0}', resourceGroup().name)]", - "contributorRoleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", - "lawsReaderRoleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293')]" - }, - "resources": [ - { - "type": "Microsoft.Authorization/policyAssignments", - "apiVersion": "2020-09-01", - "name": "[variables('assignmentName')]", - "location": "[parameters('location')]", - "properties": { - "policyDefinitionId": "[createObject('NISTRev4', createObject('id', '/providers/Microsoft.Authorization/policySetDefinitions/cf25b9c1-bd23-4eb6-bd2c-f4f3ac644a5f', 'parameters', json(replace(variables('$fxv#0'), '', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[2], split(parameters('logAnalyticsWorkspaceResourceId'), '/')[4]), 'Microsoft.OperationalInsights/workspaces', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[8])))), 'NISTRev5', createObject('id', '/providers/Microsoft.Authorization/policySetDefinitions/179d1daa-458f-4e47-8086-2a68d0d6c38f', 'parameters', json(variables('$fxv#1'))), 'IL5', createObject('id', '/providers/Microsoft.Authorization/policySetDefinitions/f9a961fa-3241-4b20-adc4-bbf8ad9d7197', 'parameters', json(replace(variables('$fxv#2'), '', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[2], split(parameters('logAnalyticsWorkspaceResourceId'), '/')[4]), 'Microsoft.OperationalInsights/workspaces', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[8])))), 'CMMC', createObject('id', '/providers/Microsoft.Authorization/policySetDefinitions/b5629c75-5c77-4422-87b9-2509e680f8de', 'parameters', json(replace(variables('$fxv#3'), '', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[2], split(parameters('logAnalyticsWorkspaceResourceId'), '/')[4]), 'Microsoft.OperationalInsights/workspaces', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[8]), '2021-06-01').customerId))))[variables('modifiedAssignment')].id]", - "parameters": "[createObject('NISTRev4', createObject('id', '/providers/Microsoft.Authorization/policySetDefinitions/cf25b9c1-bd23-4eb6-bd2c-f4f3ac644a5f', 'parameters', json(replace(variables('$fxv#0'), '', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[2], split(parameters('logAnalyticsWorkspaceResourceId'), '/')[4]), 'Microsoft.OperationalInsights/workspaces', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[8])))), 'NISTRev5', createObject('id', '/providers/Microsoft.Authorization/policySetDefinitions/179d1daa-458f-4e47-8086-2a68d0d6c38f', 'parameters', json(variables('$fxv#1'))), 'IL5', createObject('id', '/providers/Microsoft.Authorization/policySetDefinitions/f9a961fa-3241-4b20-adc4-bbf8ad9d7197', 'parameters', json(replace(variables('$fxv#2'), '', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[2], split(parameters('logAnalyticsWorkspaceResourceId'), '/')[4]), 'Microsoft.OperationalInsights/workspaces', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[8])))), 'CMMC', createObject('id', '/providers/Microsoft.Authorization/policySetDefinitions/b5629c75-5c77-4422-87b9-2509e680f8de', 'parameters', json(replace(variables('$fxv#3'), '', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[2], split(parameters('logAnalyticsWorkspaceResourceId'), '/')[4]), 'Microsoft.OperationalInsights/workspaces', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[8]), '2021-06-01').customerId))))[variables('modifiedAssignment')].parameters]" - }, - "identity": { - "type": "SystemAssigned" - } - }, - { - "type": "Microsoft.Authorization/policyAssignments", - "apiVersion": "2020-09-01", - "name": "[variables('agentVmssAssignmentName')]", - "location": "[parameters('location')]", - "properties": { - "policyDefinitionId": "[tenantResourceId('Microsoft.Authorization/policySetDefinitions', '75714362-cae7-409e-9b99-a8e5075b7fad')]", - "parameters": { - "logAnalytics_1": { - "value": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[2], split(parameters('logAnalyticsWorkspaceResourceId'), '/')[4]), 'Microsoft.OperationalInsights/workspaces', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[8])]" - } - } - }, - "identity": { - "type": "SystemAssigned" - } - }, - { - "type": "Microsoft.Authorization/policyAssignments", - "apiVersion": "2020-09-01", - "name": "[variables('agentVmAssignmentName')]", - "location": "[parameters('location')]", - "properties": { - "policyDefinitionId": "[tenantResourceId('Microsoft.Authorization/policySetDefinitions', '55f3eceb-5573-4f18-9695-226972c6d74a')]", - "parameters": { - "logAnalytics_1": { - "value": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[2], split(parameters('logAnalyticsWorkspaceResourceId'), '/')[4]), 'Microsoft.OperationalInsights/workspaces', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[8])]" - } - } - }, - "identity": { - "type": "SystemAssigned" - } - }, - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2020-04-01-preview", - "name": "[guid(variables('contributorRoleDefinitionId'), variables('assignmentName'))]", - "properties": { - "roleDefinitionId": "[variables('contributorRoleDefinitionId')]", - "principalId": "[if(empty(variables('modifiedAssignment')), '', reference(resourceId('Microsoft.Authorization/policyAssignments', variables('assignmentName')), '2020-09-01', 'full').identity.principalId)]", - "principalType": "ServicePrincipal" - }, - "dependsOn": [ - "[resourceId('Microsoft.Authorization/policyAssignments', variables('assignmentName'))]" - ] - }, - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2020-04-01-preview", - "name": "[guid(variables('contributorRoleDefinitionId'), variables('agentVmssAssignmentName'))]", - "properties": { - "roleDefinitionId": "[variables('contributorRoleDefinitionId')]", - "principalId": "[reference(resourceId('Microsoft.Authorization/policyAssignments', variables('agentVmssAssignmentName')), '2020-09-01', 'full').identity.principalId]", - "principalType": "ServicePrincipal" - }, - "dependsOn": [ - "[resourceId('Microsoft.Authorization/policyAssignments', variables('agentVmssAssignmentName'))]" - ] - }, - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2020-04-01-preview", - "name": "[guid(variables('contributorRoleDefinitionId'), variables('agentVmAssignmentName'))]", - "properties": { - "roleDefinitionId": "[variables('contributorRoleDefinitionId')]", - "principalId": "[reference(resourceId('Microsoft.Authorization/policyAssignments', variables('agentVmAssignmentName')), '2020-09-01', 'full').identity.principalId]", - "principalType": "ServicePrincipal" - }, - "dependsOn": [ - "[resourceId('Microsoft.Authorization/policyAssignments', variables('agentVmAssignmentName'))]" - ] - }, - { - "condition": "[parameters('deployRemediation')]", - "type": "Microsoft.PolicyInsights/remediations", - "apiVersion": "2019-07-01", - "name": "VM-Agent-Policy-Remediation", - "properties": { - "policyAssignmentId": "[resourceId('Microsoft.Authorization/policyAssignments', variables('agentVmAssignmentName'))]", - "resourceDiscoveryMode": "ReEvaluateCompliance" - }, - "dependsOn": [ - "[resourceId('Microsoft.Authorization/policyAssignments', variables('agentVmAssignmentName'))]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('Assign-Laws-Role-Policy-{0}', resourceGroup().name)]", - "subscriptionId": "[split(parameters('logAnalyticsWorkspaceResourceId'), '/')[2]]", - "resourceGroup": "[split(parameters('logAnalyticsWorkspaceResourceId'), '/')[4]]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "targetResourceId": { - "value": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[2], split(parameters('logAnalyticsWorkspaceResourceId'), '/')[4]), 'Microsoft.OperationalInsights/workspaces', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[8])]" - }, - "roleDefinitionId": { - "value": "[variables('lawsReaderRoleDefinitionId')]" - }, - "principalId": { - "value": "[reference(resourceId('Microsoft.Authorization/policyAssignments', variables('agentVmAssignmentName')), '2020-09-01', 'full').identity.principalId]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "967013811257719495" - } - }, - "parameters": { - "targetResourceId": { - "type": "string" - }, - "roleDefinitionId": { - "type": "string" - }, - "principalId": { - "type": "string" - }, - "principalType": { - "type": "string", - "defaultValue": "ServicePrincipal", - "allowedValues": [ - "ForeignGroup", - "Group", - "ServicePrincipal", - "User" - ] - }, - "description": { - "type": "string", - "defaultValue": "" - } - }, - "resources": [ - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2020-04-01-preview", - "name": "[guid(parameters('targetResourceId'), parameters('roleDefinitionId'), parameters('principalId'))]", - "properties": { - "principalId": "[parameters('principalId')]", - "principalType": "[parameters('principalType')]", - "roleDefinitionId": "[parameters('roleDefinitionId')]", - "description": "[parameters('description')]" - } - } - ] - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Authorization/policyAssignments', variables('agentVmAssignmentName'))]" - ] - } - ] - } - } - } - ] - } - }, - "dependsOn": [ - "[subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]", - "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]" - ] - }, - { - "condition": "[and(parameters('deployDefender'), not(empty(parameters('virtualNetworkAddressPrefix'))))]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('set-defender-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))]", - "location": "[deployment().location]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "emailSecurityContact": { - "value": "[parameters('emailSecurityContact')]" - }, - "logAnalyticsWorkspaceId": { - "value": "[parameters('logAnalyticsWorkspaceResourceId')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "2575529544484247921" - } - }, - "parameters": { - "defenderPlans": { - "type": "array", - "defaultValue": [ - "VirtualMachines" - ], - "metadata": { - "description": "Defender Paid protection Plans. Even if a customer selects the free sku, at least 1 paid protection plan must be specified." - } - }, - "enableAutoProvisioning": { - "type": "bool", - "defaultValue": true, - "metadata": { - "description": "Turn automatic deployment by Defender of the MMA (OMS VM extension) on or off" - } - }, - "logAnalyticsWorkspaceId": { - "type": "string", - "metadata": { - "description": "Specify the ID of your custom Log Analytics workspace to collect Defender data." - } - }, - "emailSecurityContact": { - "type": "string", - "metadata": { - "description": "Email address of the contact, in the form of john@doe.com" - } - }, - "policySetDescription": { - "type": "string", - "defaultValue": "The Microsoft Cloud Security Benchmark initiative represents the policies and controls implementing security recommendations defined in Microsoft Cloud Security Benchmark v2, see https://aka.ms/azsecbm. This also serves as the Microsoft Defender for Cloud default policy initiative. You can directly assign this initiative, or manage its policies and compliance results within Microsoft Defender.", - "metadata": { - "description": "Policy Initiative description field" - } - }, - "defenderSkuTier": { - "type": "string", - "defaultValue": "Free", - "metadata": { - "description": "[Standard/Free] The SKU for Defender. It defaults to \"Free\"." - } - } - }, - "variables": { - "autoProvisioning": "[if(parameters('enableAutoProvisioning'), 'On', 'Off')]", - "defenderPaidPlanConfig": { - "AzureCloud": { - "Api": { - "subPlan": "P1" - }, - "appServices": {}, - "KeyVaults": { - "subPlan": "PerKeyVault" - }, - "Arm": { - "subPlan": "PerSubscription" - }, - "CloudPosture": { - "extensions": [ - { - "name": "SensitiveDataDiscovery", - "isEnabled": "True" - }, - { - "name": "ContainerRegistriesVulnerabilityAssessments", - "isEnabled": "True" - }, - { - "name": "AgentlessDiscoveryForKubernetes", - "isEnabled": "True" - }, - { - "name": "AgentlessVmScanning", - "isEnabled": "True" - }, - { - "name": "EntraPermissionsManagement", - "isEnabled": "True" - } - ] - }, - "Containers": { - "extensions": [ - { - "name": "ContainerRegistriesVulnerabilityAssessments", - "isEnabled": "True" - }, - { - "name": "AgentlessDiscoveryForKubernetes", - "isEnabled": "True" - } - ] - }, - "CosmosDbs": {}, - "StorageAccounts": { - "subPlan": "DefenderForStorageV2", - "extensions": [ - { - "name": "OnUploadMalwareScanning", - "isEnabled": "True", - "additionalExtensionProperties": { - "CapGBPerMonthPerStorageAccount": "5000" - } - }, - { - "name": "SensitiveDataDiscovery", - "isEnabled": "True" - } - ] - }, - "VirtualMachines": { - "subPlan": "P1" - }, - "SqlServerVirtualMachines": {}, - "SqlServers": {}, - "OpenSourceRelationalDatabases": {} - } - } - }, - "resources": [ - { - "copy": { - "name": "defenderFreeAllClouds", - "count": "[length(parameters('defenderPlans'))]", - "mode": "serial", - "batchSize": 1 - }, - "condition": "[equals(parameters('defenderSkuTier'), 'Free')]", - "type": "Microsoft.Security/pricings", - "apiVersion": "2023-01-01", - "name": "[parameters('defenderPlans')[copyIndex()]]", - "properties": { - "pricingTier": "[parameters('defenderSkuTier')]" - } - }, - { - "copy": { - "name": "defenderStandardNoSubplanNoExtensions", - "count": "[length(parameters('defenderPlans'))]", - "mode": "serial", - "batchSize": 1 - }, - "condition": "[and(equals(parameters('defenderSkuTier'), 'Standard'), not(equals(environment().name, 'AzureCloud')))]", - "type": "Microsoft.Security/pricings", - "apiVersion": "2023-01-01", - "name": "[parameters('defenderPlans')[copyIndex()]]", - "properties": { - "pricingTier": "[parameters('defenderSkuTier')]" - } - }, - { - "copy": { - "name": "defenderStandardSubplanExtensionsAzureCloud", - "count": "[length(parameters('defenderPlans'))]", - "mode": "serial", - "batchSize": 1 - }, - "condition": "[and(equals(parameters('defenderSkuTier'), 'Standard'), equals(environment().name, 'AzureCloud'))]", - "type": "Microsoft.Security/pricings", - "apiVersion": "2023-01-01", - "name": "[parameters('defenderPlans')[copyIndex()]]", - "properties": { - "pricingTier": "[parameters('defenderSkuTier')]", - "subPlan": "[if(contains(variables('defenderPaidPlanConfig')[environment().name][parameters('defenderPlans')[copyIndex()]], 'subPlan'), variables('defenderPaidPlanConfig')[environment().name][parameters('defenderPlans')[copyIndex()]].subPlan, json('null'))]", - "extensions": "[if(contains(variables('defenderPaidPlanConfig')[environment().name][parameters('defenderPlans')[copyIndex()]], 'extensions'), variables('defenderPaidPlanConfig')[environment().name][parameters('defenderPlans')[copyIndex()]].extensions, json('null'))]" - } - }, - { - "type": "Microsoft.Security/autoProvisioningSettings", - "apiVersion": "2019-01-01", - "name": "default", - "properties": { - "autoProvision": "[variables('autoProvisioning')]" - } - }, - { - "type": "Microsoft.Security/workspaceSettings", - "apiVersion": "2019-01-01", - "name": "default", - "properties": { - "workspaceId": "[parameters('logAnalyticsWorkspaceId')]", - "scope": "[subscription().id]" - } - }, - { - "condition": "[not(empty(parameters('emailSecurityContact')))]", - "type": "Microsoft.Security/securityContacts", - "apiVersion": "2020-01-01-preview", - "name": "default", - "properties": { - "notificationsByRole": { - "roles": [ - "AccountAdmin", - "Contributor", - "Owner", - "ServiceAdmin" - ], - "state": "On" - }, - "alertNotifications": { - "state": "On" - }, - "emails": "[parameters('emailSecurityContact')]" - } - }, - { - "type": "Microsoft.Authorization/policyAssignments", - "apiVersion": "2022-06-01", - "name": "Microsoft Cloud Security Benchmark", - "properties": { - "displayName": "Defender Default", - "description": "[parameters('policySetDescription')]", - "enforcementMode": "DoNotEnforce", - "parameters": {}, - "policyDefinitionId": "[tenantResourceId('Microsoft.Authorization/policySetDefinitions', '1f3afdf9-d0c9-4c3d-847f-89da613e70a8')]" - } - } - ] - } - } - } - ], - "outputs": { - "diskEncryptionSetResourceId": { - "type": "string", - "value": "[if(not(empty(parameters('virtualNetworkAddressPrefix'))), reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-cmk-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.diskEncryptionSetResourceId.value, '')]" - }, - "dnsServers": { - "type": "array", - "value": "[if(not(empty(parameters('virtualNetworkAddressPrefix'))), coalesce(tryGet(reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Network/virtualNetworks', split(parameters('hubVirtualNetworkResourceId'), '/')[8]), '2023-11-01'), 'dhcpOptions', 'dnsServers'), createArray()), createArray())]" - }, - "keyVaultUri": { - "type": "string", - "value": "[if(not(empty(parameters('virtualNetworkAddressPrefix'))), reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-cmk-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.keyVaultUri.value, '')]" - }, - "locationProperties": { - "type": "object", - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.locationProperties.value]" - }, - "logAnalyticsWorkspaceResourceId": { - "type": "string", - "value": "[parameters('logAnalyticsWorkspaceResourceId')]" - }, - "mlzTags": { - "type": "object", - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.mlzTags.value]" - }, - "namingConvention": { - "type": "object", - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tiers.value[0].namingConvention]" - }, - "privateDnsZones": { - "type": "array", - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.privateDnsZones.value]" - }, - "resourceAbbreviations": { - "type": "object", - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceAbbreviations.value]" - }, - "resourcePrefix": { - "type": "string", - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('firewallResourceId'), '/')[2], split(parameters('firewallResourceId'), '/')[4]), 'Microsoft.Network/azureFirewalls', split(parameters('firewallResourceId'), '/')[8]), '2020-11-01', 'full').tags.resourcePrefix]" - }, - "storageAccountResourceId": { - "type": "string", - "value": "[if(not(empty(parameters('virtualNetworkAddressPrefix'))), reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-storage-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.storageAccountResourceId.value, '')]" - }, - "storageEncryptionKeyName": { - "type": "string", - "value": "[if(not(empty(parameters('virtualNetworkAddressPrefix'))), reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-cmk-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.storageKeyName.value, '')]" - }, - "subnets": { - "type": "array", - "value": "[if(not(empty(parameters('virtualNetworkAddressPrefix'))), reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-network-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.subnets.value, createArray())]" - }, - "tier": { - "type": "object", - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tiers.value[0]]" - }, - "tokens": { - "type": "object", - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value]" - }, - "userAssignedIdentityResourceId": { - "type": "string", - "value": "[if(not(empty(parameters('virtualNetworkAddressPrefix'))), reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-cmk-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.userAssignedIdentityResourceId.value, '')]" - } - } - } - } - }, - { - "copy": { - "name": "rgs", - "count": "[length(variables('resourceGroupServices'))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('deploy-rg-{0}-{1}', variables('resourceGroupServices')[copyIndex()], parameters('deploymentNameSuffix'))]", - "location": "[deployment().location]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "location": "[if(equals(variables('resourceGroupServices')[copyIndex()], 'controlPlane'), createObject('value', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Network/virtualNetworks', split(parameters('hubVirtualNetworkResourceId'), '/')[8]), '2023-11-01', 'full').location), createObject('value', parameters('locationVirtualMachines')))]", - "mlzTags": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.mlzTags.value]" - }, - "name": "[if(equals(variables('resourceGroupServices')[copyIndex()], 'controlPlane'), createObject('value', replace(reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-cp-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.names.value.resourceGroup, reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-cp-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value.service, variables('resourceGroupServices')[copyIndex()])), createObject('value', replace(reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.namingConvention.value.resourceGroup, reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value.service, variables('resourceGroupServices')[copyIndex()])))]", - "tags": { - "value": "[parameters('tags')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "17445726037807437290" - } - }, - "parameters": { - "mlzTags": { - "type": "object" - }, - "name": { - "type": "string" - }, - "location": { - "type": "string" - }, - "tags": { - "type": "object", - "defaultValue": {} - } - }, - "resources": [ - { - "type": "Microsoft.Resources/resourceGroups", - "apiVersion": "2019-05-01", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[union(if(contains(parameters('tags'), 'Microsoft.Resources/resourceGroups'), parameters('tags')['Microsoft.Resources/resourceGroups'], createObject()), parameters('mlzTags'))]" - } - ], - "outputs": { - "id": { - "type": "string", - "value": "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('name'))]" - }, - "name": { - "type": "string", - "value": "[parameters('name')]" - }, - "location": { - "type": "string", - "value": "[reference(subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('name')), '2019-05-01', 'full').location]" - }, - "tags": { - "type": "object", - "value": "[reference(subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('name')), '2019-05-01', 'full').tags]" - } - } - } - }, - "dependsOn": [ - "[subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-cp-{0}', parameters('deploymentNameSuffix')))]", - "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix')))]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('deploy-management-{0}', parameters('deploymentNameSuffix'))]", - "location": "[deployment().location]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "avdObjectId": { - "value": "[parameters('avdObjectId')]" - }, - "deployFslogix": { - "value": "[variables('deployFslogix')]" - }, - "deploymentNameSuffix": { - "value": "[parameters('deploymentNameSuffix')]" - }, - "diskEncryptionSetResourceId": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.diskEncryptionSetResourceId.value]" - }, - "diskSku": { - "value": "[parameters('diskSku')]" - }, - "domainJoinPassword": { - "value": "[parameters('domainJoinPassword')]" - }, - "domainJoinUserPrincipalName": { - "value": "[parameters('domainJoinUserPrincipalName')]" - }, - "domainName": { - "value": "[parameters('domainName')]" - }, - "enableApplicationInsights": { - "value": "[parameters('enableApplicationInsights')]" - }, - "enableAvdInsights": { - "value": "[parameters('enableAvdInsights')]" - }, - "environmentAbbreviation": { - "value": "[parameters('environmentAbbreviation')]" - }, - "fslogixStorageService": { - "value": "[parameters('fslogixStorageService')]" - }, - "locationVirtualMachines": { - "value": "[parameters('locationVirtualMachines')]" - }, - "logAnalyticsWorkspaceRetention": { - "value": "[parameters('logAnalyticsWorkspaceRetention')]" - }, - "logAnalyticsWorkspaceSku": { - "value": "[parameters('logAnalyticsWorkspaceSku')]" - }, - "mlzTags": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.mlzTags.value]" - }, - "namingConvention": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.namingConvention.value]" - }, - "organizationalUnitPath": { - "value": "[parameters('organizationalUnitPath')]" - }, - "privateDnsZoneResourceIdPrefix": { - "value": "[variables('privateDnsZoneResourceIdPrefix')]" - }, - "privateDnsZones": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.privateDnsZones.value]" - }, - "privateLinkScopeResourceId": { - "value": "[parameters('privateLinkScopeResourceId')]" - }, - "recoveryServices": { - "value": "[parameters('recoveryServices')]" - }, - "recoveryServicesGeo": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.locationProperties.value.recoveryServicesGeo]" - }, - "resourceAbbreviations": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceAbbreviations.value]" - }, - "resourceGroupControlPlane": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', variables('resourceGroupServices')[0], parameters('deploymentNameSuffix'))), '2022-09-01').outputs.name.value]" - }, - "resourceGroupHosts": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', variables('resourceGroupServices')[1], parameters('deploymentNameSuffix'))), '2022-09-01').outputs.name.value]" - }, - "resourceGroupManagement": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', variables('resourceGroupServices')[2], parameters('deploymentNameSuffix'))), '2022-09-01').outputs.name.value]" - }, - "resourceGroupStorage": "[if(variables('deployFslogix'), createObject('value', replace(reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.namingConvention.value.resourceGroup, reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value.service, 'storage')), createObject('value', ''))]", - "roleDefinitions": { - "value": "[variables('roleDefinitions')]" - }, - "serviceToken": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value.service]" - }, - "storageService": { - "value": "[variables('storageService')]" - }, - "subnetResourceId": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.subnets.value[0].id]" - }, - "subnets": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.subnets.value]" - }, - "tags": { - "value": "[parameters('tags')]" - }, - "timeDifference": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.locationProperties.value.timeDifference]" - }, - "timeZone": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.locationProperties.value.timeZone]" - }, - "virtualMachinePassword": { - "value": "[parameters('virtualMachinePassword')]" - }, - "virtualMachineUsername": { - "value": "[parameters('virtualMachineUsername')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "12558774705370628238" - } - }, - "parameters": { - "avdObjectId": { - "type": "string" - }, - "deployFslogix": { - "type": "bool" - }, - "deploymentNameSuffix": { - "type": "string" - }, - "diskEncryptionSetResourceId": { - "type": "string" - }, - "diskSku": { - "type": "string" - }, - "domainJoinPassword": { - "type": "securestring" - }, - "domainJoinUserPrincipalName": { - "type": "string" - }, - "domainName": { - "type": "string" - }, - "enableApplicationInsights": { - "type": "bool" - }, - "enableAvdInsights": { - "type": "bool" - }, - "environmentAbbreviation": { - "type": "string" - }, - "fslogixStorageService": { - "type": "string" - }, - "locationVirtualMachines": { - "type": "string" - }, - "logAnalyticsWorkspaceRetention": { - "type": "int" - }, - "logAnalyticsWorkspaceSku": { - "type": "string" - }, - "mlzTags": { - "type": "object" - }, - "namingConvention": { - "type": "object" - }, - "organizationalUnitPath": { - "type": "string" - }, - "privateDnsZoneResourceIdPrefix": { - "type": "string" - }, - "privateDnsZones": { - "type": "array" - }, - "privateLinkScopeResourceId": { - "type": "string" - }, - "recoveryServices": { - "type": "bool" - }, - "recoveryServicesGeo": { - "type": "string" - }, - "resourceAbbreviations": { - "type": "object" - }, - "resourceGroupControlPlane": { - "type": "string" - }, - "resourceGroupHosts": { - "type": "string" - }, - "resourceGroupManagement": { - "type": "string" - }, - "resourceGroupStorage": { - "type": "string" - }, - "roleDefinitions": { - "type": "object" - }, - "serviceToken": { - "type": "string" - }, - "storageService": { - "type": "string" - }, - "subnetResourceId": { - "type": "string" - }, - "subnets": { - "type": "array" - }, - "tags": { - "type": "object" - }, - "timeDifference": { - "type": "string" + "template": { + "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "15810721730485220824" + } + }, + "parameters": { + "deploymentNameSuffix": { + "type": "string" + }, + "environmentAbbreviation": { + "type": "string" + }, + "location": { + "type": "string" + }, + "networks": { + "type": "array" + }, + "resourcePrefix": { + "type": "string" + }, + "stampIndex": { + "type": "string", + "defaultValue": "" + } + }, + "variables": { + "$fxv#0": "1.0.0", + "environmentName": { + "dev": "Development", + "prod": "Production", + "test": "Test" + }, + "mlzTags": { + "environment": "[variables('environmentName')[parameters('environmentAbbreviation')]]", + "landingZoneName": "MissionLandingZone", + "landingZoneVersion": "[variables('$fxv#0')]", + "resourcePrefix": "[parameters('resourcePrefix')]" + } + }, + "resources": [ + { + "copy": { + "name": "namingConventions", + "count": "[length(parameters('networks'))]" + }, + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('naming-convention-{0}-{1}', parameters('networks')[copyIndex()].shortName, parameters('deploymentNameSuffix'))]", + "location": "[deployment().location]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "environmentAbbreviation": { + "value": "[parameters('environmentAbbreviation')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "networkName": { + "value": "[parameters('networks')[copyIndex()].name]" + }, + "networkShortName": { + "value": "[parameters('networks')[copyIndex()].shortName]" + }, + "resourcePrefix": { + "value": "[parameters('resourcePrefix')]" + }, + "stampIndex": { + "value": "[parameters('stampIndex')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "11125044402666498605" + } + }, + "parameters": { + "environmentAbbreviation": { + "type": "string" + }, + "location": { + "type": "string" + }, + "networkName": { + "type": "string" + }, + "networkShortName": { + "type": "string" + }, + "resourcePrefix": { + "type": "string" + }, + "stampIndex": { + "type": "string", + "defaultValue": "" + }, + "tokens": { + "type": "object", + "defaultValue": { + "purpose": "purpose_token", + "resource": "resource_token", + "service": "service_token" + } + } + }, + "variables": { + "$fxv#0": { + "AzureChina": { + "chinaeast": { + "abbreviation": "cne", + "recoveryServicesGeo": "sha", + "timeDifference": "+8:00", + "timeZone": "China Standard Time" + }, + "chinaeast2": { + "abbreviation": "cne2", + "recoveryServicesGeo": "sha2", + "timeDifference": "+8:00", + "timeZone": "China Standard Time" + }, + "chinanorth": { + "abbreviation": "cnn", + "recoveryServicesGeo": "bjb", + "timeDifference": "+8:00", + "timeZone": "China Standard Time" + }, + "chinanorth2": { + "abbreviation": "cnn2", + "recoveryServicesGeo": "bjb2", + "timeDifference": "+8:00", + "timeZone": "China Standard Time" + } + }, + "AzureCloud": { + "australiacentral": { + "abbreviation": "auc", + "recoveryServicesGeo": "acl", + "timeDifference": "+10:00", + "timeZone": "AUS Eastern Standard Time" + }, + "australiacentral2": { + "abbreviation": "auc2", + "recoveryServicesGeo": "acl2", + "timeDifference": "+10:00", + "timeZone": "AUS Eastern Standard Time" + }, + "australiaeast": { + "abbreviation": "aue", + "recoveryServicesGeo": "ae", + "timeDifference": "+10:00", + "timeZone": "AUS Eastern Standard Time" + }, + "australiasoutheast": { + "abbreviation": "ause", + "recoveryServicesGeo": "ase", + "timeDifference": "+10:00", + "timeZone": "AUS Eastern Standard Time" + }, + "brazilsouth": { + "abbreviation": "brs", + "recoveryServicesGeo": "brs", + "timeDifference": "-3:00", + "timeZone": "E. South America Standard Time" + }, + "brazilsoutheast": { + "abbreviation": "brse", + "recoveryServicesGeo": "bse", + "timeDifference": "-3:00", + "timeZone": "E. South America Standard Time" + }, + "canadacentral": { + "abbreviation": "cac", + "recoveryServicesGeo": "cnc", + "timeDifference": "-5:00", + "timeZone": "Eastern Standard Time" + }, + "canadaeast": { + "abbreviation": "cae", + "recoveryServicesGeo": "cne", + "timeDifference": "-5:00", + "timeZone": "Eastern Standard Time" + }, + "centralindia": { + "abbreviation": "inc", + "recoveryServicesGeo": "inc", + "timeDifference": "+5:30", + "timeZone": "India Standard Time" + }, + "centralus": { + "abbreviation": "usc", + "recoveryServicesGeo": "cus", + "timeDifference": "-6:00", + "timeZone": "Central Standard Time" + }, + "eastasia": { + "abbreviation": "ase", + "recoveryServicesGeo": "ea", + "timeDifference": "+8:00", + "timeZone": "China Standard Time" + }, + "eastus": { + "abbreviation": "use", + "recoveryServicesGeo": "eus", + "timeDifference": "-5:00", + "timeZone": "Eastern Standard Time" + }, + "eastus2": { + "abbreviation": "use2", + "recoveryServicesGeo": "eus2", + "timeDifference": "-5:00", + "timeZone": "Eastern Standard Time" + }, + "francecentral": { + "abbreviation": "frc", + "recoveryServicesGeo": "frc", + "timeDifference": "+1:00", + "timeZone": "Central Europe Standard Time" + }, + "francesouth": { + "abbreviation": "frs", + "recoveryServicesGeo": "frs", + "timeDifference": "+1:00", + "timeZone": "Central Europe Standard Time" + }, + "germanynorth": { + "abbreviation": "den", + "recoveryServicesGeo": "gn", + "timeDifference": "+1:00", + "timeZone": "Central Europe Standard Time" + }, + "germanywestcentral": { + "abbreviation": "dewc", + "recoveryServicesGeo": "gwc", + "timeDifference": "+1:00", + "timeZone": "Central Europe Standard Time" + }, + "israelcentral": { + "abbreviation": "ilc", + "recoveryServicesGeo": "ilc", + "timeDifference": "+2:00", + "timeZone": "Israel Standard Time" + }, + "italynorth": { + "abbreviation": "itn", + "recoveryServicesGeo": "itn", + "timeDifference": "+1:00", + "timeZone": "Central Europe Standard Time" + }, + "japaneast": { + "abbreviation": "jpe", + "recoveryServicesGeo": "jpe", + "timeDifference": "+9:00", + "timeZone": "Tokyo Standard Time" + }, + "japanwest": { + "abbreviation": "jpw", + "recoveryServicesGeo": "jpw", + "timeDifference": "+9:00", + "timeZone": "Tokyo Standard Time" + }, + "jioindiacentral": { + "abbreviation": "injc", + "recoveryServicesGeo": "jic", + "timeDifference": "+5:30", + "timeZone": "India Standard Time" + }, + "jioindiawest": { + "abbreviation": "injw", + "recoveryServicesGeo": "jiw", + "timeDifference": "+5:30", + "timeZone": "India Standard Time" + }, + "koreacentral": { + "abbreviation": "krc", + "recoveryServicesGeo": "krc", + "timeDifference": "+9:00", + "timeZone": "Korea Standard Time" + }, + "koreasouth": { + "abbreviation": "krs", + "recoveryServicesGeo": "krs", + "timeDifference": "+9:00", + "timeZone": "Korea Standard Time" + }, + "northcentralus": { + "abbreviation": "usnc", + "recoveryServicesGeo": "ncus", + "timeDifference": "-6:00", + "timeZone": "Central Standard Time" + }, + "northeurope": { + "abbreviation": "eun", + "recoveryServicesGeo": "ne", + "timeDifference": "0:00", + "timeZone": "GMT Standard Time" + }, + "norwayeast": { + "abbreviation": "noe", + "recoveryServicesGeo": "nwe", + "timeDifference": "+1:00", + "timeZone": "Central Europe Standard Time" + }, + "norwaywest": { + "abbreviation": "now", + "recoveryServicesGeo": "nww", + "timeDifference": "+1:00", + "timeZone": "Central Europe Standard Time" + }, + "polandcentral": { + "abbreviation": "plc", + "recoveryServicesGeo": "plc", + "timeDifference": "+1:00", + "timeZone": "Central Europe Standard Time" + }, + "qatarcentral": { + "abbreviation": "qac", + "recoveryServicesGeo": "qac", + "timeDifference": "+3:00", + "timeZone": "Arabian Standard Time" + }, + "southafricanorth": { + "abbreviation": "zan", + "recoveryServicesGeo": "san", + "timeDifference": "+2:00", + "timeZone": "South Africa Standard Time" + }, + "southafricawest": { + "abbreviation": "zaw", + "recoveryServicesGeo": "saw", + "timeDifference": "+2:00", + "timeZone": "South Africa Standard Time" + }, + "southcentralus": { + "abbreviation": "ussc", + "recoveryServicesGeo": "scus", + "timeDifference": "-6:00", + "timeZone": "Central Standard Time" + }, + "southeastasia": { + "abbreviation": "asse", + "recoveryServicesGeo": "sea", + "timeDifference": "+8:00", + "timeZone": "Singapore Standard Time" + }, + "southindia": { + "abbreviation": "ins", + "recoveryServicesGeo": "ins", + "timeDifference": "+5:30", + "timeZone": "India Standard Time" + }, + "swedencentral": { + "abbreviation": "sec", + "recoveryServicesGeo": "sdc", + "timeDifference": "+1:00", + "timeZone": "Central Europe Standard Time" + }, + "switzerlandnorth": { + "abbreviation": "chn", + "recoveryServicesGeo": "szn", + "timeDifference": "+1:00", + "timeZone": "Central Europe Standard Time" + }, + "switzerlandwest": { + "abbreviation": "chw", + "recoveryServicesGeo": "szw", + "timeDifference": "+1:00", + "timeZone": "Central Europe Standard Time" + }, + "uaecentral": { + "abbreviation": "aec", + "recoveryServicesGeo": "uac", + "timeDifference": "+3:00", + "timeZone": "Arabian Standard Time" + }, + "uaenorth": { + "abbreviation": "aen", + "recoveryServicesGeo": "uan", + "timeDifference": "+3:00", + "timeZone": "Arabian Standard Time" + }, + "uksouth": { + "abbreviation": "uks", + "recoveryServicesGeo": "uks", + "timeDifference": "0:00", + "timeZone": "GMT Standard Time" + }, + "ukwest": { + "abbreviation": "ukw", + "recoveryServicesGeo": "ukw", + "timeDifference": "0:00", + "timeZone": "GMT Standard Time" + }, + "westcentralus": { + "abbreviation": "uswc", + "recoveryServicesGeo": "wcus", + "timeDifference": "-7:00", + "timeZone": "Mountain Standard Time" + }, + "westeurope": { + "abbreviation": "euw", + "recoveryServicesGeo": "we", + "timeDifference": "+1:00", + "timeZone": "Central Europe Standard Time" + }, + "westindia": { + "abbreviation": "inw", + "recoveryServicesGeo": "inw", + "timeDifference": "+5:30", + "timeZone": "India Standard Time" + }, + "westus": { + "abbreviation": "usw", + "recoveryServicesGeo": "wus", + "timeDifference": "-8:00", + "timeZone": "Pacific Standard Time" + }, + "westus2": { + "abbreviation": "usw2", + "recoveryServicesGeo": "wus2", + "timeDifference": "-8:00", + "timeZone": "Pacific Standard Time" + }, + "westus3": { + "abbreviation": "usw3", + "recoveryServicesGeo": "wus3", + "timeDifference": "-7:00", + "timeZone": "Mountain Standard Time" + } + }, + "AzureUSGovernment": { + "usdodcentral": { + "abbreviation": "dodc", + "recoveryServicesGeo": "udc", + "timeDifference": "-6:00", + "timeZone": "Central Standard Time" + }, + "usdodeast": { + "abbreviation": "dode", + "recoveryServicesGeo": "ude", + "timeDifference": "-5:00", + "timeZone": "Eastern Standard Time" + }, + "usgovarizona": { + "abbreviation": "az", + "recoveryServicesGeo": "uga", + "timeDifference": "-7:00", + "timeZone": "Mountain Standard Time" + }, + "usgovtexas": { + "abbreviation": "tx", + "recoveryServicesGeo": "ugt", + "timeDifference": "-6:00", + "timeZone": "Central Standard Time" + }, + "usgovvirginia": { + "abbreviation": "va", + "recoveryServicesGeo": "ugv", + "timeDifference": "-5:00", + "timeZone": "Eastern Standard Time" + } + }, + "USNat": { + "usnateast": { + "abbreviation": "east", + "recoveryServicesGeo": "exe", + "timeDifference": "-5:00", + "timeZone": "Eastern Standard Time" + }, + "usnatwest": { + "abbreviation": "west", + "recoveryServicesGeo": "exw", + "timeDifference": "-8:00", + "timeZone": "Pacific Standard Time" + } + }, + "USSec": { + "usseceast": { + "abbreviation": "east", + "recoveryServicesGeo": "rxe", + "timeDifference": "-5:00", + "timeZone": "Eastern Standard Time" + }, + "ussecwest": { + "abbreviation": "west", + "recoveryServicesGeo": "rxw", + "timeDifference": "-8:00", + "timeZone": "Pacific Standard Time" + } + } + }, + "$fxv#1": { + "actionGroups": "ag", + "applicationGroups": "vdag", + "applicationInsights": "appi", + "appServicePlans": "asp", + "automationAccounts": "aa", + "availabilitySets": "avail", + "azureFirewalls": "afw", + "bastionHosts": "bas", + "computeGallieries": "cg", + "dataCollectionEndpoints": "dce", + "dataCollectionRuleAssociations": "dcra", + "dataCollectionRules": "dcr", + "diagnosticSettings": "diag", + "diskAccesses": "da", + "diskEncryptionSets": "des", + "disks": "disk", + "firewallPolicies": "afwp", + "functionApps": "func", + "hostPools": "vdpool", + "ipConfigurations": "ipconf", + "keyVaults": "kv", + "logAnalyticsWorkspaces": "log", + "netAppAccounts": "naa", + "netAppCapacityPools": "nacp", + "networkInterfaces": "nic", + "networkSecurityGroups": "nsg", + "networkWatchers": "nw", + "privateEndpoints": "pe", + "privateLinkScopes": "pls", + "publicIPAddresses": "pip", + "recoveryServicesVaults": "rsv", + "remoteApplicationGroups": "vdag", + "resourceGroups": "rg", + "routeTables": "rt", + "scalingPlans": "vdscaling", + "storageAccounts": "st", + "subnets": "snet", + "userAssignedIdentities": "id", + "virtualMachines": "vm", + "virtualNetworks": "vnet", + "workspaces": "vdws" + }, + "locations": "[variables('$fxv#0')[environment().name]]", + "locationAbbreviation": "[variables('locations')[parameters('location')].abbreviation]", + "resourceAbbreviations": "[variables('$fxv#1')]", + "namingConvention": "[format('{0}-{1}{2}-{3}-{4}-{5}', toLower(parameters('resourcePrefix')), if(empty(parameters('stampIndex')), '', format('{0}-', parameters('stampIndex'))), parameters('tokens').resource, parameters('networkName'), variables('locationAbbreviation'), parameters('environmentAbbreviation'))]", + "namingConvention_Service": "[format('{0}-{1}{2}-{3}-{4}-{5}-{6}', toLower(parameters('resourcePrefix')), if(empty(parameters('stampIndex')), '', format('{0}-', parameters('stampIndex'))), parameters('tokens').resource, parameters('networkName'), parameters('tokens').service, variables('locationAbbreviation'), parameters('environmentAbbreviation'))]", + "names": { + "actionGroup": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').actionGroups)]", + "applicationGroup": "[replace(variables('namingConvention'), parameters('tokens').resource, format('{0}-desktop', variables('resourceAbbreviations').applicationGroups))]", + "applicationInsights": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').applicationInsights)]", + "appServicePlan": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').appServicePlans)]", + "automationAccount": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').automationAccounts)]", + "automationAccountDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, variables('resourceAbbreviations').automationAccounts)]", + "automationAccountNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, variables('resourceAbbreviations').automationAccounts)]", + "automationAccountPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, variables('resourceAbbreviations').automationAccounts)]", + "availabilitySet": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').availabilitySets)]", + "azureFirewall": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').azureFirewalls)]", + "azureFirewallClientPublicIPAddress": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').publicIPAddresses), parameters('tokens').service, format('client-{0}', variables('resourceAbbreviations').azureFirewalls))]", + "azureFirewallClientPublicIPAddressDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-client-{1}', variables('resourceAbbreviations').publicIPAddresses, variables('resourceAbbreviations').azureFirewalls))]", + "azureFirewallDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, variables('resourceAbbreviations').azureFirewalls)]", + "azureFirewallManagementPublicIPAddress": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').publicIPAddresses), parameters('tokens').service, format('mgmt-{0}', variables('resourceAbbreviations').azureFirewalls))]", + "azureFirewallManagementPublicIPAddressDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-mgmt-{1}', variables('resourceAbbreviations').publicIPAddresses, variables('resourceAbbreviations').azureFirewalls))]", + "azureFirewallPolicy": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').firewallPolicies)]", + "bastionHost": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').bastionHosts)]", + "bastionHostNetworkSecurityGroup": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkSecurityGroups), parameters('tokens').service, variables('resourceAbbreviations').bastionHosts)]", + "bastionHostDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, variables('resourceAbbreviations').bastionHosts)]", + "bastionHostPublicIPAddress": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').publicIPAddresses), parameters('tokens').service, variables('resourceAbbreviations').bastionHosts)]", + "bastionHostPublicIPAddressDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-{1}', variables('resourceAbbreviations').publicIPAddresses, variables('resourceAbbreviations').bastionHosts))]", + "computeGallery": "[replace(replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').computeGallieries), '-', '_')]", + "dataCollectionEndpoint": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').dataCollectionEndpoints)]", + "dataCollectionRuleAssociation": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').dataCollectionRuleAssociations)]", + "dataCollectionRule": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').dataCollectionRules)]", + "diskAccess": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').diskAccesses)]", + "diskAccessNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, variables('resourceAbbreviations').diskAccesses)]", + "diskAccessPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, variables('resourceAbbreviations').diskAccesses)]", + "diskEncryptionSet": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').diskEncryptionSets)]", + "functionApp": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').functionApps)]", + "functionAppNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-{1}', variables('resourceAbbreviations').functionApps, parameters('tokens').service))]", + "functionAppPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-{1}', variables('resourceAbbreviations').functionApps, parameters('tokens').service))]", + "hostPool": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').hostPools)]", + "hostPoolDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, variables('resourceAbbreviations').hostPools)]", + "hostPoolNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, variables('resourceAbbreviations').hostPools)]", + "hostPoolPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, variables('resourceAbbreviations').hostPools)]", + "keyVault": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').keyVaults), '-', ''), parameters('networkName'), parameters('networkShortName'))]", + "keyVaultDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-{1}', variables('resourceAbbreviations').keyVaults, parameters('tokens').service))]", + "keyVaultNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-{1}', variables('resourceAbbreviations').keyVaults, parameters('tokens').service))]", + "keyVaultPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-{1}', variables('resourceAbbreviations').keyVaults, parameters('tokens').service))]", + "logAnalyticsWorkspace": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').logAnalyticsWorkspaces)]", + "logAnalyticsWorkspaceDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, variables('resourceAbbreviations').logAnalyticsWorkspaces)]", + "netAppAccountCapacityPool": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').netAppCapacityPools)]", + "netAppAccount": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').netAppAccounts)]", + "networkSecurityGroup": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').networkSecurityGroups)]", + "networkSecurityGroupDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, variables('resourceAbbreviations').networkSecurityGroups)]", + "networkWatcher": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').networkWatchers)]", + "privateLinkScope": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').privateLinkScopes)]", + "privateLinkScopeNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, variables('resourceAbbreviations').privateLinkScopes)]", + "privateLinkScopePrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, variables('resourceAbbreviations').privateLinkScopes)]", + "recoveryServicesVault": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').recoveryServicesVaults)]", + "recoveryServicesVaultNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, variables('resourceAbbreviations').recoveryServicesVaults)]", + "recoveryServicesVaultPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, variables('resourceAbbreviations').recoveryServicesVaults)]", + "resourceGroup": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').resourceGroups)]", + "routeTable": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').routeTables)]", + "scalingPlan": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').scalingPlans)]", + "scalingPlanDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, variables('resourceAbbreviations').scalingPlans)]", + "storageAccount": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').storageAccounts), parameters('networkName'), parameters('networkShortName'))]", + "storageAccountDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').storageAccounts))]", + "storageAccountBlobNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-blob', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountFileNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-file', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountQueueNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-queue', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountTableNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-table', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountBlobPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-blob', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountFilePrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-file', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountQueuePrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-queue', variables('resourceAbbreviations').storageAccounts))]", + "storageAccountTablePrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-table', variables('resourceAbbreviations').storageAccounts))]", + "subnet": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').subnets)]", + "userAssignedIdentity": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').userAssignedIdentities)]", + "virtualMachine": "[replace(replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').virtualMachines), parameters('environmentAbbreviation'), first(parameters('environmentAbbreviation'))), parameters('networkName'), ''), '-', '')]", + "virtualMachineDisk": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').disks), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').virtualMachines))]", + "virtualMachineNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').virtualMachines))]", + "virtualNetwork": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').virtualNetworks)]", + "virtualNetworkDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, variables('resourceAbbreviations').virtualNetworks)]", + "workspaceFeed": "[replace(replace(variables('namingConvention'), parameters('tokens').resource, format('{0}-feed', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", + "workspaceFeedDiagnosticSetting": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-feed', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", + "workspaceFeedNetworkInterface": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-feed', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", + "workspaceFeedPrivateEndpoint": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-feed', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", + "workspaceGlobal": "[replace(replace(variables('namingConvention'), parameters('tokens').resource, format('{0}-global', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", + "workspaceGlobalDiagnosticSetting": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-global', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", + "workspaceGlobalNetworkInterface": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-global', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", + "workspaceGlobalPrivateEndpoint": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-global', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]" + } + }, + "resources": [], + "outputs": { + "locations": { + "type": "object", + "value": "[variables('locations')]" + }, + "names": { + "type": "object", + "value": "[variables('names')]" + }, + "resourceAbbreviations": { + "type": "object", + "value": "[variables('resourceAbbreviations')]" + }, + "tokens": { + "type": "object", + "value": "[parameters('tokens')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('private-dns-zones-{0}', parameters('deploymentNameSuffix'))]", + "location": "[deployment().location]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "locations": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('naming-convention-{0}-{1}', parameters('networks')[0].shortName, parameters('deploymentNameSuffix'))), '2022-09-01').outputs.locations.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "17619606927129129347" + } + }, + "parameters": { + "locations": { + "type": "object" + } + }, + "variables": { + "copy": [ + { + "name": "privateDnsZoneNames_Backup", + "count": "[length(items(parameters('locations')))]", + "input": "[format('privatelink.{0}.backup.windowsazure.{1}', items(parameters('locations'))[copyIndex('privateDnsZoneNames_Backup')].value.recoveryServicesGeo, coalesce(variables('privateDnsZoneSuffixes_Backup')[environment().name], variables('cloudSuffix')))]" + } + ], + "cloudSuffix": "[replace(replace(environment().resourceManager, 'https://management.', ''), '/', '')]", + "privateDnsZoneNames": "[union(createArray(format('privatelink.agentsvc.azure-automation.{0}', coalesce(variables('privateDnsZoneSuffixes_AzureAutomation')[environment().name], variables('cloudSuffix'))), format('privatelink.azure-automation.{0}', coalesce(variables('privateDnsZoneSuffixes_AzureAutomation')[environment().name], variables('cloudSuffix'))), format('privatelink.{0}', coalesce(variables('privateDnsZoneSuffixes_AzureWebSites')[environment().name], format('appservice.{0}', variables('cloudSuffix')))), format('scm.privatelink.{0}', coalesce(variables('privateDnsZoneSuffixes_AzureWebSites')[environment().name], format('appservice.{0}', variables('cloudSuffix')))), format('privatelink.wvd.{0}', coalesce(variables('privateDnsZoneSuffixes_AzureVirtualDesktop')[environment().name], variables('cloudSuffix'))), format('privatelink-global.wvd.{0}', coalesce(variables('privateDnsZoneSuffixes_AzureVirtualDesktop')[environment().name], variables('cloudSuffix'))), format('privatelink.file.{0}', environment().suffixes.storage), format('privatelink.queue.{0}', environment().suffixes.storage), format('privatelink.table.{0}', environment().suffixes.storage), format('privatelink.blob.{0}', environment().suffixes.storage), format('privatelink{0}', replace(environment().suffixes.keyvaultDns, 'vault', 'vaultcore')), format('privatelink.monitor.{0}', coalesce(variables('privateDnsZoneSuffixes_Monitor')[environment().name], variables('cloudSuffix'))), format('privatelink.ods.opinsights.{0}', coalesce(variables('privateDnsZoneSuffixes_Monitor')[environment().name], variables('cloudSuffix'))), format('privatelink.oms.opinsights.{0}', coalesce(variables('privateDnsZoneSuffixes_Monitor')[environment().name], variables('cloudSuffix')))), variables('privateDnsZoneNames_Backup'))]", + "privateDnsZoneSuffixes_AzureAutomation": { + "AzureCloud": "net", + "AzureUSGovernment": "us", + "USNat": null, + "USSec": null + }, + "privateDnsZoneSuffixes_AzureVirtualDesktop": { + "AzureCloud": "microsoft.com", + "AzureUSGovernment": "azure.us", + "USNat": null, + "USSec": null + }, + "privateDnsZoneSuffixes_AzureWebSites": { + "AzureCloud": "azurewebsites.net", + "AzureUSGovernment": "azurewebsites.us", + "USNat": null, + "USSec": null + }, + "privateDnsZoneSuffixes_Backup": { + "AzureCloud": "com", + "AzureUSGovernment": "us", + "USNat": null, + "USSec": null + }, + "privateDnsZoneSuffixes_Monitor": { + "AzureCloud": "azure.com", + "AzureUSGovernment": "azure.us", + "USNat": null, + "USSec": null + } + }, + "resources": [], + "outputs": { + "names": { + "type": "array", + "value": "[variables('privateDnsZoneNames')]" + } + } + } + }, + "dependsOn": [ + "[subscriptionResourceId('Microsoft.Resources/deployments', format('naming-convention-{0}-{1}', parameters('networks')[0].shortName, parameters('deploymentNameSuffix')))]" + ] + } + ], + "outputs": { + "locationProperties": { + "type": "object", + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('naming-convention-{0}-{1}', parameters('networks')[0].shortName, parameters('deploymentNameSuffix'))), '2022-09-01').outputs.locations.value[parameters('location')]]" + }, + "mlzTags": { + "type": "object", + "value": "[variables('mlzTags')]" + }, + "privateDnsZones": { + "type": "array", + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('private-dns-zones-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.names.value]" + }, + "resourceAbbreviations": { + "type": "object", + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('naming-convention-{0}-{1}', parameters('networks')[0].shortName, parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceAbbreviations.value]" + }, + "tiers": { + "type": "array", + "copy": { + "count": "[length(parameters('networks'))]", + "input": { + "name": "[parameters('networks')[copyIndex()].name]", + "shortName": "[parameters('networks')[copyIndex()].shortName]", + "deployUniqueResources": "[parameters('networks')[copyIndex()].deployUniqueResources]", + "subscriptionId": "[parameters('networks')[copyIndex()].subscriptionId]", + "nsgDiagLogs": "[coalesce(tryGet(parameters('networks')[copyIndex()], 'nsgDiagLogs'), createArray())]", + "nsgDiagMetrics": "[coalesce(tryGet(parameters('networks')[copyIndex()], 'nsgDiagMetrics'), createArray())]", + "nsgRules": "[coalesce(tryGet(parameters('networks')[copyIndex()], 'nsgRules'), createArray())]", + "vnetAddressPrefix": "[coalesce(tryGet(parameters('networks')[copyIndex()], 'vnetAddressPrefix'), '')]", + "vnetDiagLogs": "[coalesce(tryGet(parameters('networks')[copyIndex()], 'vnetDiagLogs'), createArray())]", + "vnetDiagMetrics": "[coalesce(tryGet(parameters('networks')[copyIndex()], 'vnetDiagMetrics'), createArray())]", + "subnetAddressPrefix": "[coalesce(tryGet(parameters('networks')[copyIndex()], 'subnetAddressPrefix'), '')]", + "namingConvention": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('naming-convention-{0}-{1}', parameters('networks')[copyIndex()].shortName, parameters('deploymentNameSuffix'))), '2022-09-01').outputs.names.value]" + } + } + }, + "tokens": { + "type": "object", + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('naming-convention-{0}-{1}', parameters('networks')[0].shortName, parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value]" + } + } + } + } }, - "timeZone": { - "type": "string" + { + "condition": "[not(empty(parameters('virtualNetworkAddressPrefix')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('deploy-rg-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))]", + "location": "[deployment().location]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "mlzTags": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.mlzTags.value]" + }, + "name": { + "value": "[replace(reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tiers.value[0].namingConvention.resourceGroup, reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value.service, 'network')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "9371138343009436350" + } + }, + "parameters": { + "mlzTags": { + "type": "object" + }, + "name": { + "type": "string" + }, + "location": { + "type": "string" + }, + "tags": { + "type": "object", + "defaultValue": {} + } + }, + "resources": [ + { + "type": "Microsoft.Resources/resourceGroups", + "apiVersion": "2019-05-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[union(if(contains(parameters('tags'), 'Microsoft.Resources/resourceGroups'), parameters('tags')['Microsoft.Resources/resourceGroups'], createObject()), parameters('mlzTags'))]" + } + ], + "outputs": { + "id": { + "type": "string", + "value": "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + }, + "location": { + "type": "string", + "value": "[reference(subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('name')), '2019-05-01', 'full').location]" + }, + "tags": { + "type": "object", + "value": "[reference(subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('name')), '2019-05-01', 'full').tags]" + } + } + } + }, + "dependsOn": [ + "[subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]" + ] }, - "virtualMachinePassword": { - "type": "securestring" + { + "condition": "[not(empty(parameters('virtualNetworkAddressPrefix')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('deploy-network-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))]", + "location": "[deployment().location]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "additionalSubnets": { + "value": "[parameters('additionalSubnets')]" + }, + "deploymentNameSuffix": { + "value": "[parameters('deploymentNameSuffix')]" + }, + "deployNetworkWatcher": { + "value": "[parameters('deployNetworkWatcher')]" + }, + "hubVirtualNetworkResourceId": { + "value": "[parameters('hubVirtualNetworkResourceId')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "mlzTags": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.mlzTags.value]" + }, + "networkSecurityGroupName": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tiers.value[0].namingConvention.networkSecurityGroup]" + }, + "networkSecurityGroupRules": { + "value": "[parameters('networkSecurityGroupRules')]" + }, + "networkWatcherName": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tiers.value[0].namingConvention.networkWatcher]" + }, + "resourceGroupName": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.name.value]" + }, + "routeTableName": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tiers.value[0].namingConvention.routeTable]" + }, + "routeTableRouteNextHopIpAddress": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('firewallResourceId'), '/')[2], split(parameters('firewallResourceId'), '/')[4]), 'Microsoft.Network/azureFirewalls', split(parameters('firewallResourceId'), '/')[8]), '2020-11-01').ipConfigurations[0].properties.privateIPAddress]" + }, + "subnetAddressPrefix": { + "value": "[parameters('subnetAddressPrefix')]" + }, + "subnetName": "[if(empty(parameters('subnetName')), createObject('value', reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tiers.value[0].namingConvention.subnet), createObject('value', parameters('subnetName')))]", + "subscriptionId": { + "value": "[variables('subscriptionId')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "virtualNetworkAddressPrefix": { + "value": "[parameters('virtualNetworkAddressPrefix')]" + }, + "virtualNetworkName": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tiers.value[0].namingConvention.virtualNetwork]" + }, + "vNetDnsServers": { + "value": "[coalesce(tryGet(reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Network/virtualNetworks', split(parameters('hubVirtualNetworkResourceId'), '/')[8]), '2023-11-01'), 'dhcpOptions', 'dnsServers'), createArray())]" + }, + "workloadShortName": { + "value": "[parameters('workloadShortName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "12095518102496352105" + } + }, + "parameters": { + "additionalSubnets": { + "type": "array" + }, + "deploymentNameSuffix": { + "type": "string" + }, + "deployNetworkWatcher": { + "type": "bool" + }, + "hubVirtualNetworkResourceId": { + "type": "string" + }, + "location": { + "type": "string" + }, + "mlzTags": { + "type": "object" + }, + "networkSecurityGroupName": { + "type": "string" + }, + "networkSecurityGroupRules": { + "type": "array" + }, + "networkWatcherName": { + "type": "string" + }, + "resourceGroupName": { + "type": "string" + }, + "routeTableName": { + "type": "string" + }, + "routeTableRouteNextHopIpAddress": { + "type": "string" + }, + "subnetAddressPrefix": { + "type": "string" + }, + "subnetName": { + "type": "string" + }, + "subscriptionId": { + "type": "string" + }, + "tags": { + "type": "object" + }, + "vNetDnsServers": { + "type": "array" + }, + "virtualNetworkAddressPrefix": { + "type": "string" + }, + "virtualNetworkName": { + "type": "string" + }, + "workloadShortName": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('deploy-spoke-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))]", + "location": "[deployment().location]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "additionalSubnets": { + "value": "[parameters('additionalSubnets')]" + }, + "deployNetworkWatcher": { + "value": "[parameters('deployNetworkWatcher')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "mlzTags": { + "value": "[parameters('mlzTags')]" + }, + "networkSecurityGroupName": { + "value": "[parameters('networkSecurityGroupName')]" + }, + "networkSecurityGroupRules": { + "value": "[parameters('networkSecurityGroupRules')]" + }, + "networkWatcherName": { + "value": "[parameters('networkWatcherName')]" + }, + "resourceGroupName": { + "value": "[parameters('resourceGroupName')]" + }, + "routeTableName": { + "value": "[parameters('routeTableName')]" + }, + "routeTableRouteNextHopIpAddress": { + "value": "[parameters('routeTableRouteNextHopIpAddress')]" + }, + "subnetAddressPrefix": { + "value": "[parameters('subnetAddressPrefix')]" + }, + "subnetName": { + "value": "[parameters('subnetName')]" + }, + "subscriptionId": { + "value": "[parameters('subscriptionId')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "virtualNetworkAddressPrefix": { + "value": "[parameters('virtualNetworkAddressPrefix')]" + }, + "virtualNetworkName": { + "value": "[parameters('virtualNetworkName')]" + }, + "vNetDnsServers": { + "value": "[parameters('vNetDnsServers')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "11818136489056939588" + } + }, + "parameters": { + "additionalSubnets": { + "type": "array", + "defaultValue": [] + }, + "deployNetworkWatcher": { + "type": "bool" + }, + "location": { + "type": "string" + }, + "mlzTags": { + "type": "object" + }, + "networkSecurityGroupName": { + "type": "string" + }, + "networkSecurityGroupRules": { + "type": "array" + }, + "networkWatcherName": { + "type": "string" + }, + "resourceGroupName": { + "type": "string" + }, + "routeTableName": { + "type": "string" + }, + "routeTableRouteNextHopIpAddress": { + "type": "string" + }, + "subnetAddressPrefix": { + "type": "string" + }, + "subnetName": { + "type": "string" + }, + "subscriptionId": { + "type": "string" + }, + "tags": { + "type": "object" + }, + "virtualNetworkAddressPrefix": { + "type": "string" + }, + "virtualNetworkName": { + "type": "string" + }, + "vNetDnsServers": { + "type": "array" + } + }, + "variables": { + "delegations": { + "AzureNetAppFiles": [ + { + "name": "Microsoft.Netapp.volumes", + "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets/delegations', parameters('virtualNetworkName'), 'AzureNetAppFiles', 'delegation')]", + "properties": { + "serviceName": "Microsoft.Netapp/volumes" + }, + "type": "Microsoft.Network/virtualNetworks/subnets/delegations" + } + ], + "FunctionAppOutbound": [ + { + "name": "Microsoft.Web/sites", + "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets/delegations', parameters('virtualNetworkName'), 'FunctionAppOutbound', 'delegation')]", + "properties": { + "serviceName": "Microsoft.Web/serverfarms" + }, + "type": "Microsoft.Network/virtualNetworks/subnets/delegations" + } + ] + }, + "subnets": "[union(createArray(createObject('name', parameters('subnetName'), 'properties', createObject('addressPrefix', parameters('subnetAddressPrefix')))), parameters('additionalSubnets'))]" + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "networkSecurityGroup", + "subscriptionId": "[parameters('subscriptionId')]", + "resourceGroup": "[parameters('resourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "mlzTags": { + "value": "[parameters('mlzTags')]" + }, + "name": { + "value": "[parameters('networkSecurityGroupName')]" + }, + "securityRules": { + "value": "[parameters('networkSecurityGroupRules')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "3681943409502537301" + } + }, + "parameters": { + "location": { + "type": "string" + }, + "mlzTags": { + "type": "object" + }, + "name": { + "type": "string" + }, + "securityRules": { + "type": "array" + }, + "tags": { + "type": "object" + } + }, + "resources": [ + { + "type": "Microsoft.Network/networkSecurityGroups", + "apiVersion": "2021-02-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[union(if(contains(parameters('tags'), 'Microsoft.Network/networkSecurityGroups'), parameters('tags')['Microsoft.Network/networkSecurityGroups'], createObject()), parameters('mlzTags'))]", + "properties": { + "securityRules": "[parameters('securityRules')]" + } + } + ], + "outputs": { + "id": { + "type": "string", + "value": "[resourceId('Microsoft.Network/networkSecurityGroups', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "routeTable", + "subscriptionId": "[parameters('subscriptionId')]", + "resourceGroup": "[parameters('resourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "disableBgpRoutePropagation": { + "value": true + }, + "location": { + "value": "[parameters('location')]" + }, + "mlzTags": { + "value": "[parameters('mlzTags')]" + }, + "name": { + "value": "[parameters('routeTableName')]" + }, + "routeNextHopIpAddress": { + "value": "[parameters('routeTableRouteNextHopIpAddress')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "18425321023142226965" + } + }, + "parameters": { + "disableBgpRoutePropagation": { + "type": "bool" + }, + "location": { + "type": "string" + }, + "mlzTags": { + "type": "object" + }, + "name": { + "type": "string" + }, + "routeAddressPrefix": { + "type": "string", + "defaultValue": "0.0.0.0/0" + }, + "routeName": { + "type": "string", + "defaultValue": "default_route" + }, + "routeNextHopIpAddress": { + "type": "string" + }, + "routeNextHopType": { + "type": "string", + "defaultValue": "VirtualAppliance" + }, + "tags": { + "type": "object" + } + }, + "resources": [ + { + "type": "Microsoft.Network/routeTables", + "apiVersion": "2021-02-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[union(if(contains(parameters('tags'), 'Microsoft.Network/routeTables'), parameters('tags')['Microsoft.Network/routeTables'], createObject()), parameters('mlzTags'))]", + "properties": { + "disableBgpRoutePropagation": "[parameters('disableBgpRoutePropagation')]", + "routes": [ + { + "name": "[parameters('routeName')]", + "properties": { + "addressPrefix": "[parameters('routeAddressPrefix')]", + "nextHopIpAddress": "[parameters('routeNextHopIpAddress')]", + "nextHopType": "[parameters('routeNextHopType')]" + } + } + ] + } + } + ], + "outputs": { + "id": { + "type": "string", + "value": "[resourceId('Microsoft.Network/routeTables', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + } + } + } + } + }, + { + "condition": "[parameters('deployNetworkWatcher')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "networkWatcher", + "subscriptionId": "[parameters('subscriptionId')]", + "resourceGroup": "[parameters('resourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "mlzTags": { + "value": "[parameters('mlzTags')]" + }, + "name": { + "value": "[parameters('networkWatcherName')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "15002544166394392504" + } + }, + "parameters": { + "location": { + "type": "string" + }, + "mlzTags": { + "type": "object" + }, + "name": { + "type": "string" + }, + "tags": { + "type": "object" + } + }, + "resources": [ + { + "type": "Microsoft.Network/networkWatchers", + "apiVersion": "2021-02-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[union(if(contains(parameters('tags'), 'Microsoft.Network/networkWatchers'), parameters('tags')['Microsoft.Network/networkWatchers'], createObject()), parameters('mlzTags'))]", + "properties": {} + } + ] + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "virtualNetwork", + "subscriptionId": "[parameters('subscriptionId')]", + "resourceGroup": "[parameters('resourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "addressPrefix": { + "value": "[parameters('virtualNetworkAddressPrefix')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "mlzTags": { + "value": "[parameters('mlzTags')]" + }, + "name": { + "value": "[parameters('virtualNetworkName')]" + }, + "subnets": { + "copy": [ + { + "name": "value", + "count": "[length(variables('subnets'))]", + "input": "[createObject('name', variables('subnets')[copyIndex('value')].name, 'properties', createObject('addressPrefix', variables('subnets')[copyIndex('value')].properties.addressPrefix, 'delegations', coalesce(tryGet(variables('delegations'), variables('subnets')[copyIndex('value')].name), createArray()), 'networkSecurityGroup', createObject('id', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'networkSecurityGroup'), '2022-09-01').outputs.id.value), 'routeTable', createObject('id', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'routeTable'), '2022-09-01').outputs.id.value), 'privateEndpointNetworkPolicies', 'Disabled', 'privateLinkServiceNetworkPolicies', 'Disabled'))]" + } + ] + }, + "tags": { + "value": "[parameters('tags')]" + }, + "vNetDnsServers": { + "value": "[parameters('vNetDnsServers')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "16817025486402215719" + } + }, + "parameters": { + "addressPrefix": { + "type": "string" + }, + "location": { + "type": "string" + }, + "mlzTags": { + "type": "object" + }, + "name": { + "type": "string" + }, + "subnets": { + "type": "array" + }, + "tags": { + "type": "object" + }, + "vNetDnsServers": { + "type": "array" + } + }, + "resources": [ + { + "type": "Microsoft.Network/virtualNetworks", + "apiVersion": "2021-02-01", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[union(if(contains(parameters('tags'), 'Microsoft.Network/virtualNetworks'), parameters('tags')['Microsoft.Network/virtualNetworks'], createObject()), parameters('mlzTags'))]", + "properties": { + "addressSpace": { + "addressPrefixes": [ + "[parameters('addressPrefix')]" + ] + }, + "subnets": "[parameters('subnets')]", + "dhcpOptions": "[if(empty(parameters('vNetDnsServers')), null(), createObject('dnsServers', parameters('vNetDnsServers')))]" + } + } + ], + "outputs": { + "addressPrefix": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Network/virtualNetworks', parameters('name')), '2021-02-01').addressSpace.addressPrefixes[0]]" + }, + "dnsServers": { + "type": "array", + "value": "[parameters('vNetDnsServers')]" + }, + "id": { + "type": "string", + "value": "[resourceId('Microsoft.Network/virtualNetworks', parameters('name'))]" + }, + "name": { + "type": "string", + "value": "[parameters('name')]" + }, + "subnets": { + "type": "array", + "value": "[reference(resourceId('Microsoft.Network/virtualNetworks', parameters('name')), '2021-02-01').subnets]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'networkSecurityGroup')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'networkWatcher')]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'routeTable')]" + ] + } + ], + "outputs": { + "networkSecurityGroupName": { + "type": "string", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'networkSecurityGroup'), '2022-09-01').outputs.name.value]" + }, + "networkSecurityGroupResourceId": { + "type": "string", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'networkSecurityGroup'), '2022-09-01').outputs.id.value]" + }, + "subnets": { + "type": "array", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'virtualNetwork'), '2022-09-01').outputs.subnets.value]" + }, + "virtualNetworkAddressPrefix": { + "type": "string", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'virtualNetwork'), '2022-09-01').outputs.addressPrefix.value]" + }, + "virtualNetworkName": { + "type": "string", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'virtualNetwork'), '2022-09-01').outputs.name.value]" + }, + "virtualNetworkResourceId": { + "type": "string", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('subscriptionId'), parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'virtualNetwork'), '2022-09-01').outputs.id.value]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('deploy-spoke-peering-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))]", + "location": "[deployment().location]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "deploymentNameSuffix": { + "value": "[parameters('deploymentNameSuffix')]" + }, + "hubVirtualNetworkResourceId": { + "value": "[parameters('hubVirtualNetworkResourceId')]" + }, + "resourceGroupName": { + "value": "[parameters('resourceGroupName')]" + }, + "spokeShortName": { + "value": "[parameters('workloadShortName')]" + }, + "spokeVirtualNetworkName": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-spoke-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.virtualNetworkName.value]" + }, + "subscriptionId": { + "value": "[parameters('subscriptionId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "10267893616110384815" + } + }, + "parameters": { + "deploymentNameSuffix": { + "type": "string" + }, + "hubVirtualNetworkResourceId": { + "type": "string" + }, + "resourceGroupName": { + "type": "string" + }, + "spokeShortName": { + "type": "string" + }, + "spokeVirtualNetworkName": { + "type": "string" + }, + "subscriptionId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('peer-{0}-to-hub-{1}', parameters('spokeShortName'), parameters('deploymentNameSuffix'))]", + "subscriptionId": "[parameters('subscriptionId')]", + "resourceGroup": "[parameters('resourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "remoteVirtualNetworkResourceId": { + "value": "[parameters('hubVirtualNetworkResourceId')]" + }, + "virtualNetworkName": { + "value": "[parameters('spokeVirtualNetworkName')]" + }, + "virtualNetworkPeerName": { + "value": "[format('to-{0}', split(parameters('hubVirtualNetworkResourceId'), '/')[8])]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "5574526676512163672" + } + }, + "parameters": { + "remoteVirtualNetworkResourceId": { + "type": "string" + }, + "virtualNetworkName": { + "type": "string" + }, + "virtualNetworkPeerName": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Network/virtualNetworks/virtualNetworkPeerings", + "apiVersion": "2021-02-01", + "name": "[format('{0}/{1}', parameters('virtualNetworkName'), parameters('virtualNetworkPeerName'))]", + "properties": { + "allowForwardedTraffic": true, + "remoteVirtualNetwork": { + "id": "[parameters('remoteVirtualNetworkResourceId')]" + } + } + } + ] + } + } + } + ] + } + }, + "dependsOn": [ + "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-spoke-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('deploy-hub-peering-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))]", + "location": "[deployment().location]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "deploymentNameSuffix": { + "value": "[parameters('deploymentNameSuffix')]" + }, + "hubVirtualNetworkName": { + "value": "[split(parameters('hubVirtualNetworkResourceId'), '/')[8]]" + }, + "resourceGroupName": { + "value": "[split(parameters('hubVirtualNetworkResourceId'), '/')[4]]" + }, + "spokeShortName": { + "value": "[parameters('workloadShortName')]" + }, + "spokeVirtualNetworkResourceId": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-spoke-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.virtualNetworkResourceId.value]" + }, + "subscriptionId": { + "value": "[split(parameters('hubVirtualNetworkResourceId'), '/')[2]]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "11553909803736438916" + } + }, + "parameters": { + "deploymentNameSuffix": { + "type": "string" + }, + "hubVirtualNetworkName": { + "type": "string" + }, + "resourceGroupName": { + "type": "string" + }, + "spokeShortName": { + "type": "string" + }, + "spokeVirtualNetworkResourceId": { + "type": "string" + }, + "subscriptionId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('peer-hub-to-{0}-{1}', parameters('spokeShortName'), parameters('deploymentNameSuffix'))]", + "subscriptionId": "[parameters('subscriptionId')]", + "resourceGroup": "[parameters('resourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "remoteVirtualNetworkResourceId": { + "value": "[parameters('spokeVirtualNetworkResourceId')]" + }, + "virtualNetworkName": { + "value": "[parameters('hubVirtualNetworkName')]" + }, + "virtualNetworkPeerName": { + "value": "[format('to-{0}', split(parameters('spokeVirtualNetworkResourceId'), '/')[8])]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "5574526676512163672" + } + }, + "parameters": { + "remoteVirtualNetworkResourceId": { + "type": "string" + }, + "virtualNetworkName": { + "type": "string" + }, + "virtualNetworkPeerName": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Network/virtualNetworks/virtualNetworkPeerings", + "apiVersion": "2021-02-01", + "name": "[format('{0}/{1}', parameters('virtualNetworkName'), parameters('virtualNetworkPeerName'))]", + "properties": { + "allowForwardedTraffic": true, + "remoteVirtualNetwork": { + "id": "[parameters('remoteVirtualNetworkResourceId')]" + } + } + } + ] + } + } + } + ] + } + }, + "dependsOn": [ + "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-spoke-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]" + ] + } + ], + "outputs": { + "networkSecurityGroupName": { + "type": "string", + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-spoke-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.networkSecurityGroupName.value]" + }, + "subnets": { + "type": "array", + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-spoke-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.subnets.value]" + }, + "virtualNetworkName": { + "type": "string", + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-spoke-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.virtualNetworkName.value]" + } + } + } + }, + "dependsOn": [ + "[subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]", + "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]" + ] }, - "virtualMachineUsername": { - "type": "string" - } - }, - "variables": { - "hostPoolName": "[parameters('namingConvention').hostPool]", - "resourceGroups": "[union(createArray(parameters('resourceGroupControlPlane'), parameters('resourceGroupHosts'), parameters('resourceGroupManagement')), if(parameters('deployFslogix'), createArray(parameters('resourceGroupStorage')), createArray()))]", - "userAssignedIdentityNamePrefix": "[parameters('namingConvention').userAssignedIdentity]", - "virtualNetworkName": "[split(parameters('subnetResourceId'), '/')[8]]", - "virtualNetworkResourceGroupName": "[split(parameters('subnetResourceId'), '/')[4]]" - }, - "resources": [ { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "name": "[guid(parameters('avdObjectId'), parameters('roleDefinitions').DesktopVirtualizationPowerOnOffContributor, subscription().id)]", + "condition": "[not(empty(parameters('virtualNetworkAddressPrefix')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('deploy-vnet-links-{0}-sub-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))]", + "resourceGroup": "[variables('hubResourceGroupName')]", "properties": { - "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitions').DesktopVirtualizationPowerOnOffContributor)]", - "principalId": "[parameters('avdObjectId')]" - } + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "azureFirewallSku": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('firewallResourceId'), '/')[2], split(parameters('firewallResourceId'), '/')[4]), 'Microsoft.Network/azureFirewalls', split(parameters('firewallResourceId'), '/')[8]), '2020-11-01').sku.tier]" + }, + "deploymentNameSuffix": { + "value": "[parameters('deploymentNameSuffix')]" + }, + "privateDnsZoneNames": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.privateDnsZones.value]" + }, + "virtualNetworkName": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-network-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.virtualNetworkName.value]" + }, + "virtualNetworkResourceGroupName": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.name.value]" + }, + "virtualNetworkSubscriptionId": { + "value": "[variables('subscriptionId')]" + }, + "workloadShortName": { + "value": "[parameters('workloadShortName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "4304275711041823961" + } + }, + "parameters": { + "azureFirewallSku": { + "type": "string" + }, + "deploymentNameSuffix": { + "type": "string" + }, + "privateDnsZoneNames": { + "type": "array" + }, + "virtualNetworkName": { + "type": "string" + }, + "virtualNetworkResourceGroupName": { + "type": "string" + }, + "virtualNetworkSubscriptionId": { + "type": "string" + }, + "workloadShortName": { + "type": "string" + } + }, + "resources": [ + { + "condition": "[equals(parameters('azureFirewallSku'), 'Basic')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('deploy-vnet-links-{0}-rg-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "privateDnsZoneNames": { + "value": "[parameters('privateDnsZoneNames')]" + }, + "virtualNetworkName": { + "value": "[parameters('virtualNetworkName')]" + }, + "virtualNetworkResourceGroupName": { + "value": "[parameters('virtualNetworkResourceGroupName')]" + }, + "virtualNetworkSubscriptionId": { + "value": "[parameters('virtualNetworkSubscriptionId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "16077950968688123011" + } + }, + "parameters": { + "privateDnsZoneNames": { + "type": "array" + }, + "virtualNetworkName": { + "type": "string" + }, + "virtualNetworkResourceGroupName": { + "type": "string" + }, + "virtualNetworkSubscriptionId": { + "type": "string" + } + }, + "resources": [ + { + "copy": { + "name": "virtualNetworkLinks", + "count": "[length(parameters('privateDnsZoneNames'))]" + }, + "type": "Microsoft.Network/privateDnsZones/virtualNetworkLinks", + "apiVersion": "2018-09-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneNames')[copyIndex()], parameters('virtualNetworkName'))]", + "location": "global", + "properties": { + "registrationEnabled": false, + "virtualNetwork": { + "id": "[resourceId(parameters('virtualNetworkSubscriptionId'), parameters('virtualNetworkResourceGroupName'), 'Microsoft.Network/virtualNetworks', parameters('virtualNetworkName'))]" + } + } + } + ] + } + } + } + ] + } + }, + "dependsOn": [ + "[subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]", + "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-network-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]", + "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]" + ] }, { + "condition": "[not(empty(parameters('virtualNetworkAddressPrefix')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('deploy-disk-access-{0}', parameters('deploymentNameSuffix'))]", - "resourceGroup": "[parameters('resourceGroupManagement')]", + "name": "[format('deploy-cmk-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))]", + "location": "[deployment().location]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "hostPoolName": { - "value": "[variables('hostPoolName')]" + "deploymentNameSuffix": { + "value": "[parameters('deploymentNameSuffix')]" + }, + "environmentAbbreviation": { + "value": "[parameters('environmentAbbreviation')]" + }, + "keyVaultPrivateDnsZoneResourceId": { + "value": "[resourceId(variables('hubSubscriptionId'), variables('hubResourceGroupName'), 'Microsoft.Network/privateDnsZones', replace(format('privatelink{0}', environment().suffixes.keyvaultDns), 'vault', 'vaultcore'))]" }, "location": { - "value": "[parameters('locationVirtualMachines')]" + "value": "[parameters('location')]" }, "mlzTags": { - "value": "[parameters('mlzTags')]" + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.mlzTags.value]" }, - "namingConvention": { - "value": "[parameters('namingConvention')]" + "resourceAbbreviations": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceAbbreviations.value]" }, - "resourceGroupControlPlane": { - "value": "[parameters('resourceGroupControlPlane')]" + "resourceGroupName": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.name.value]" }, "subnetResourceId": { - "value": "[parameters('subnetResourceId')]" + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-network-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.subnets.value[0].id]" }, "tags": { "value": "[parameters('tags')]" + }, + "tier": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tiers.value[0]]" + }, + "tokens": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value]" + }, + "workloadShortName": { + "value": "[parameters('workloadShortName')]" } }, "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", "contentVersion": "1.0.0.0", "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "5378203314803052603" + "version": "0.31.92.45157", + "templateHash": "12787329163785242553" } }, "parameters": { - "hostPoolName": { + "deploymentNameSuffix": { + "type": "string" + }, + "environmentAbbreviation": { + "type": "string" + }, + "keyVaultPrivateDnsZoneResourceId": { "type": "string" }, "location": { @@ -6752,10 +3654,10 @@ "mlzTags": { "type": "object" }, - "namingConvention": { + "resourceAbbreviations": { "type": "object" }, - "resourceGroupControlPlane": { + "resourceGroupName": { "type": "string" }, "subnetResourceId": { @@ -6763,133 +3665,468 @@ }, "tags": { "type": "object" - } - }, - "resources": [ - { - "type": "Microsoft.Compute/diskAccesses", - "apiVersion": "2021-04-01", - "name": "[parameters('namingConvention').diskAccess]", - "location": "[parameters('location')]", - "tags": "[union(createObject('cm-resource-parent', format('{0}}}/resourceGroups/{1}/providers/Microsoft.DesktopVirtualization/hostpools/{2}', subscription().id, parameters('resourceGroupControlPlane'), parameters('hostPoolName'))), if(contains(parameters('tags'), 'Microsoft.Compute/diskAccesses'), parameters('tags')['Microsoft.Compute/diskAccesses'], createObject()), parameters('mlzTags'))]", - "properties": {} }, + "tier": { + "type": "object" + }, + "tokens": { + "type": "object" + }, + "workloadShortName": { + "type": "string" + } + }, + "resources": [ { - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", - "name": "[parameters('namingConvention').diskAccessPrivateEndpoint]", - "location": "[parameters('location')]", - "tags": "[union(createObject('cm-resource-parent', format('{0}}}/resourceGroups/{1}/providers/Microsoft.DesktopVirtualization/hostpools/{2}', subscription().id, parameters('resourceGroupControlPlane'), parameters('hostPoolName'))), if(contains(parameters('tags'), 'Microsoft.Network/privateEndpoints'), parameters('tags')['Microsoft.Network/privateEndpoints'], createObject()), parameters('mlzTags'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('deploy-kv-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))]", + "subscriptionId": "[parameters('tier').subscriptionId]", + "resourceGroup": "[parameters('resourceGroupName')]", "properties": { - "customNetworkInterfaceName": "[parameters('namingConvention').diskAccessNetworkInterface]", - "privateLinkServiceConnections": [ - { - "name": "[parameters('namingConvention').diskAccessPrivateEndpoint]", - "properties": { - "privateLinkServiceId": "[resourceId('Microsoft.Compute/diskAccesses', parameters('namingConvention').diskAccess)]", - "groupIds": [ - "disks" + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "environmentAbbreviation": { + "value": "[parameters('environmentAbbreviation')]" + }, + "keyVaultPrivateDnsZoneResourceId": { + "value": "[parameters('keyVaultPrivateDnsZoneResourceId')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "mlzTags": { + "value": "[parameters('mlzTags')]" + }, + "resourceAbbreviations": { + "value": "[parameters('resourceAbbreviations')]" + }, + "subnetResourceId": { + "value": "[parameters('subnetResourceId')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "tier": { + "value": "[parameters('tier')]" + }, + "tokens": { + "value": "[parameters('tokens')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "11761568940379970751" + } + }, + "parameters": { + "diskEncryptionKeyExpirationInDays": { + "type": "int", + "defaultValue": 30 + }, + "environmentAbbreviation": { + "type": "string" + }, + "keyVaultPrivateDnsZoneResourceId": { + "type": "string" + }, + "location": { + "type": "string" + }, + "mlzTags": { + "type": "object" + }, + "resourceAbbreviations": { + "type": "object" + }, + "subnetResourceId": { + "type": "string" + }, + "tags": { + "type": "object" + }, + "tier": { + "type": "object" + }, + "tokens": { + "type": "object" + } + }, + "variables": { + "keyVaultPrivateEndpointName": "[replace(parameters('tier').namingConvention.keyVaultPrivateEndpoint, parameters('tokens').service, 'cmk')]" + }, + "resources": [ + { + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('tier').namingConvention.keyVault, parameters('tokens').service, 'cmk'), resourceGroup().id))]", + "location": "[parameters('location')]", + "tags": "[union(if(contains(parameters('tags'), 'Microsoft.KeyVault/vaults'), parameters('tags')['Microsoft.KeyVault/vaults'], createObject()), parameters('mlzTags'))]", + "properties": { + "enabledForDeployment": false, + "enabledForDiskEncryption": false, + "enabledForTemplateDeployment": false, + "enablePurgeProtection": true, + "enableRbacAuthorization": true, + "enableSoftDelete": true, + "networkAcls": { + "bypass": "AzureServices", + "defaultAction": "Deny", + "ipRules": [], + "virtualNetworkRules": [] + }, + "publicNetworkAccess": "Disabled", + "sku": { + "family": "A", + "name": "premium" + }, + "softDeleteRetentionInDays": "[if(or(equals(parameters('environmentAbbreviation'), 'dev'), equals(parameters('environmentAbbreviation'), 'test')), 7, 90)]", + "tenantId": "[subscription().tenantId]" + } + }, + { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-04-01", + "name": "[variables('keyVaultPrivateEndpointName')]", + "location": "[parameters('location')]", + "tags": "[union(if(contains(parameters('tags'), 'Microsoft.Network/privateEndpoints'), parameters('tags')['Microsoft.Network/privateEndpoints'], createObject()), parameters('mlzTags'))]", + "properties": { + "customNetworkInterfaceName": "[replace(parameters('tier').namingConvention.keyVaultNetworkInterface, parameters('tokens').service, 'cmk')]", + "privateLinkServiceConnections": [ + { + "name": "[variables('keyVaultPrivateEndpointName')]", + "properties": { + "privateLinkServiceId": "[resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('tier').namingConvention.keyVault, parameters('tokens').service, 'cmk'), resourceGroup().id)))]", + "groupIds": [ + "vault" + ] + } + } + ], + "subnet": { + "id": "[parameters('subnetResourceId')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('tier').namingConvention.keyVault, parameters('tokens').service, 'cmk'), resourceGroup().id)))]" + ] + }, + { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2021-08-01", + "name": "[format('{0}/{1}', variables('keyVaultPrivateEndpointName'), format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('tier').namingConvention.keyVault, parameters('tokens').service, 'cmk'), resourceGroup().id)))]", + "properties": { + "privateDnsZoneConfigs": [ + { + "name": "ipconfig1", + "properties": { + "privateDnsZoneId": "[parameters('keyVaultPrivateDnsZoneResourceId')]" + } + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateEndpoints', variables('keyVaultPrivateEndpointName'))]", + "[resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('tier').namingConvention.keyVault, parameters('tokens').service, 'cmk'), resourceGroup().id)))]" + ] + }, + { + "type": "Microsoft.KeyVault/vaults/keys", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('tier').namingConvention.keyVault, parameters('tokens').service, 'cmk'), resourceGroup().id)), 'DiskEncryptionKey')]", + "properties": { + "attributes": { + "enabled": true + }, + "keySize": 4096, + "kty": "RSA", + "rotationPolicy": { + "attributes": { + "expiryTime": "[format('P{0}D', string(parameters('diskEncryptionKeyExpirationInDays')))]" + }, + "lifetimeActions": [ + { + "action": { + "type": "Notify" + }, + "trigger": { + "timeBeforeExpiry": "P10D" + } + }, + { + "action": { + "type": "Rotate" + }, + "trigger": { + "timeAfterCreate": "[format('P{0}D', string(sub(parameters('diskEncryptionKeyExpirationInDays'), 7)))]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('tier').namingConvention.keyVault, parameters('tokens').service, 'cmk'), resourceGroup().id)))]" + ] + }, + { + "type": "Microsoft.KeyVault/vaults/keys", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('tier').namingConvention.keyVault, parameters('tokens').service, 'cmk'), resourceGroup().id)), 'StorageEncryptionKey')]", + "properties": { + "attributes": { + "enabled": true + }, + "keySize": 4096, + "kty": "RSA", + "rotationPolicy": { + "attributes": { + "expiryTime": "[format('P{0}D', string(parameters('diskEncryptionKeyExpirationInDays')))]" + }, + "lifetimeActions": [ + { + "action": { + "type": "Notify" + }, + "trigger": { + "timeBeforeExpiry": "P10D" + } + }, + { + "action": { + "type": "Rotate" + }, + "trigger": { + "timeAfterCreate": "[format('P{0}D', string(sub(parameters('diskEncryptionKeyExpirationInDays'), 7)))]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('tier').namingConvention.keyVault, parameters('tokens').service, 'cmk'), resourceGroup().id)))]" ] } + ], + "outputs": { + "keyUriWithVersion": { + "type": "string", + "value": "[reference(resourceId('Microsoft.KeyVault/vaults/keys', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('tier').namingConvention.keyVault, parameters('tokens').service, 'cmk'), resourceGroup().id)), 'DiskEncryptionKey'), '2022-07-01').keyUriWithVersion]" + }, + "keyVaultResourceId": { + "type": "string", + "value": "[resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('tier').namingConvention.keyVault, parameters('tokens').service, 'cmk'), resourceGroup().id)))]" + }, + "keyVaultName": { + "type": "string", + "value": "[format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('tier').namingConvention.keyVault, parameters('tokens').service, 'cmk'), resourceGroup().id))]" + }, + "keyVaultUri": { + "type": "string", + "value": "[reference(resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('tier').namingConvention.keyVault, parameters('tokens').service, 'cmk'), resourceGroup().id))), '2022-07-01').vaultUri]" + }, + "storageKeyName": { + "type": "string", + "value": "StorageEncryptionKey" + } } - ], - "subnet": { - "id": "[parameters('subnetResourceId')]" } - }, - "dependsOn": [ - "[resourceId('Microsoft.Compute/diskAccesses', parameters('namingConvention').diskAccess)]" - ] - } - ], - "outputs": { - "resourceId": { - "type": "string", - "value": "[resourceId('Microsoft.Compute/diskAccesses', parameters('namingConvention').diskAccess)]" - } - } - } - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('deploy-policy-disks-{0}', parameters('deploymentNameSuffix'))]", - "location": "[deployment().location]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "diskAccessResourceId": { - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-disk-access-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceId.value]" - }, - "location": { - "value": "[parameters('locationVirtualMachines')]" - }, - "resourceGroupName": { - "value": "[parameters('resourceGroupHosts')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "702699492047562635" - } - }, - "parameters": { - "diskAccessResourceId": { - "type": "string", - "defaultValue": "" - }, - "location": { - "type": "string" + } }, - "resourceGroupName": { - "type": "string" - } - }, - "variables": { - "parameters": "[if(not(empty(parameters('diskAccessResourceId'))), createObject('diskAccessId', createObject('type', 'String', 'metadata', createObject('displayName', 'Disk Access Resource Id', 'description', 'The resource Id of the Disk Access to associate to the managed disks.'))), createObject())]", - "operations": "[if(not(empty(parameters('diskAccessResourceId'))), createArray(createObject('operation', 'addOrReplace', 'field', 'Microsoft.Compute/disks/networkAccessPolicy', 'value', 'AllowPrivate'), createObject('operation', 'addOrReplace', 'field', 'Microsoft.Compute/disks/publicNetworkAccess', 'value', 'Disabled'), createObject('operation', 'addOrReplace', 'field', 'Microsoft.Compute/disks/diskAccessId', 'value', '[parameters(''diskAccessId'')]')), createArray(createObject('operation', 'addOrReplace', 'field', 'Microsoft.Compute/disks/networkAccessPolicy', 'value', 'DenyAll'), createObject('operation', 'addOrReplace', 'field', 'Microsoft.Compute/disks/publicNetworkAccess', 'value', 'Disabled')))]" - }, - "resources": [ { - "type": "Microsoft.Authorization/policyDefinitions", - "apiVersion": "2021-06-01", - "name": "DiskNetworkAccess", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('deploy-des-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))]", + "subscriptionId": "[parameters('tier').subscriptionId]", + "resourceGroup": "[parameters('resourceGroupName')]", "properties": { - "description": "Disable network access to managed disks.", - "displayName": "Disable Disk Access", - "mode": "All", - "parameters": "[variables('parameters')]", - "policyRule": { - "if": { - "field": "type", - "equals": "Microsoft.Compute/disks" + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "deploymentNameSuffix": { + "value": "[parameters('deploymentNameSuffix')]" }, - "then": { - "effect": "modify", - "details": { - "roleDefinitionIds": [ - "/providers/Microsoft.Authorization/roleDefinitions/60fc6e62-5479-42d4-8bf4-67625fcc2840" - ], - "operations": "[variables('operations')]" + "diskEncryptionSetName": { + "value": "[parameters('tier').namingConvention.diskEncryptionSet]" + }, + "keyUrl": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('tier').subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-kv-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.keyUriWithVersion.value]" + }, + "keyVaultResourceId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('tier').subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-kv-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.keyVaultResourceId.value]" + }, + "location": { + "value": "[parameters('location')]" + }, + "mlzTags": { + "value": "[parameters('mlzTags')]" + }, + "tags": "[if(contains(parameters('tags'), 'Microsoft.Compute/diskEncryptionSets'), createObject('value', parameters('tags')['Microsoft.Compute/diskEncryptionSets']), createObject('value', createObject()))]", + "workloadShortName": { + "value": "[parameters('workloadShortName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "4207798980384159491" + } + }, + "parameters": { + "deploymentNameSuffix": { + "type": "string" + }, + "diskEncryptionSetName": { + "type": "string" + }, + "keyUrl": { + "type": "string" + }, + "keyVaultResourceId": { + "type": "string" + }, + "location": { + "type": "string" + }, + "mlzTags": { + "type": "object" + }, + "tags": { + "type": "object" + }, + "workloadShortName": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Compute/diskEncryptionSets", + "apiVersion": "2023-04-02", + "name": "[parameters('diskEncryptionSetName')]", + "location": "[parameters('location')]", + "tags": "[union(if(contains(parameters('tags'), 'Microsoft.Compute/diskEncryptionSets'), parameters('tags')['Microsoft.Compute/diskEncryptionSets'], createObject()), parameters('mlzTags'))]", + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "activeKey": { + "sourceVault": { + "id": "[parameters('keyVaultResourceId')]" + }, + "keyUrl": "[parameters('keyUrl')]" + }, + "encryptionType": "EncryptionAtRestWithPlatformAndCustomerKeys", + "rotationToLatestKeyVersionEnabled": true + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('assign-role-des-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "principalId": { + "value": "[reference(resourceId('Microsoft.Compute/diskEncryptionSets', parameters('diskEncryptionSetName')), '2023-04-02', 'full').identity.principalId]" + }, + "principalType": { + "value": "ServicePrincipal" + }, + "roleDefinitionId": { + "value": "[resourceId('Microsoft.Authorization/roleDefinitions', 'e147488a-f6f5-4113-8e2d-b22465e65bf6')]" + }, + "targetResourceId": { + "value": "[resourceGroup().id]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "1966035938992047983" + } + }, + "parameters": { + "targetResourceId": { + "type": "string" + }, + "roleDefinitionId": { + "type": "string" + }, + "principalId": { + "type": "string" + }, + "principalType": { + "type": "string", + "defaultValue": "ServicePrincipal", + "allowedValues": [ + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ] + }, + "description": { + "type": "string", + "defaultValue": "" + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2020-04-01-preview", + "name": "[guid(parameters('targetResourceId'), parameters('roleDefinitionId'), parameters('principalId'))]", + "properties": { + "principalId": "[parameters('principalId')]", + "principalType": "[parameters('principalType')]", + "roleDefinitionId": "[parameters('roleDefinitionId')]", + "description": "[parameters('description')]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Compute/diskEncryptionSets', parameters('diskEncryptionSetName'))]" + ] + } + ], + "outputs": { + "resourceId": { + "type": "string", + "value": "[resourceId('Microsoft.Compute/diskEncryptionSets', parameters('diskEncryptionSetName'))]" } } - }, - "policyType": "Custom" - } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('tier').subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-kv-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]" + ] }, { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "assign-policy-disk-network-access", + "name": "[format('deploy-id-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))]", + "subscriptionId": "[parameters('tier').subscriptionId]", "resourceGroup": "[parameters('resourceGroupName')]", "properties": { "expressionEvaluationOptions": { @@ -6897,20 +4134,20 @@ }, "mode": "Incremental", "parameters": { - "diskAccessResourceId": { - "value": "[parameters('diskAccessResourceId')]" + "keyVaultName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('tier').subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-kv-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.keyVaultName.value]" }, "location": { "value": "[parameters('location')]" }, - "policyDefinitionId": { - "value": "[subscriptionResourceId('Microsoft.Authorization/policyDefinitions', 'DiskNetworkAccess')]" + "mlzTags": { + "value": "[parameters('mlzTags')]" }, - "policyDisplayName": { - "value": "[reference(subscriptionResourceId('Microsoft.Authorization/policyDefinitions', 'DiskNetworkAccess'), '2021-06-01').displayName]" + "tags": { + "value": "[parameters('tags')]" }, - "policyName": { - "value": "[reference(subscriptionResourceId('Microsoft.Authorization/policyDefinitions', 'DiskNetworkAccess'), '2021-06-01').displayName]" + "userAssignedIdentityName": { + "value": "[replace(parameters('tier').namingConvention.userAssignedIdentity, format('-{0}', parameters('tokens').service), '')]" } }, "template": { @@ -6919,359 +4156,179 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "6289202887333913438" + "version": "0.31.92.45157", + "templateHash": "7930493629995578222" } }, "parameters": { - "diskAccessResourceId": { + "keyVaultName": { "type": "string" }, "location": { "type": "string" }, - "policyDefinitionId": { - "type": "string" + "mlzTags": { + "type": "object" }, - "policyDisplayName": { - "type": "string" + "tags": { + "type": "object" }, - "policyName": { + "userAssignedIdentityName": { "type": "string" } }, "resources": [ { - "type": "Microsoft.Authorization/policyAssignments", - "apiVersion": "2022-06-01", - "name": "[parameters('policyName')]", + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2018-11-30", + "name": "[parameters('userAssignedIdentityName')]", "location": "[parameters('location')]", - "identity": { - "type": "SystemAssigned" - }, + "tags": "[union(if(contains(parameters('tags'), 'Microsoft.ManagedIdentity/userAssignedIdentities'), parameters('tags')['Microsoft.ManagedIdentity/userAssignedIdentities'], createObject()), parameters('mlzTags'))]" + }, + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2020-04-01-preview", + "scope": "[format('Microsoft.KeyVault/vaults/{0}', parameters('keyVaultName'))]", + "name": "[guid(parameters('userAssignedIdentityName'), 'e147488a-f6f5-4113-8e2d-b22465e65bf6', parameters('keyVaultName'))]", "properties": { - "displayName": "[parameters('policyDisplayName')]", - "policyDefinitionId": "[parameters('policyDefinitionId')]", - "parameters": "[if(not(empty(parameters('diskAccessResourceId'))), createObject('diskAccessId', createObject('value', parameters('diskAccessResourceId'))), createObject())]" - } + "principalId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('userAssignedIdentityName')), '2018-11-30').principalId]", + "principalType": "ServicePrincipal", + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', 'e147488a-f6f5-4113-8e2d-b22465e65bf6')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('userAssignedIdentityName'))]" + ] } - ] + ], + "outputs": { + "resourceId": { + "type": "string", + "value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('userAssignedIdentityName'))]" + } + } } }, "dependsOn": [ - "[subscriptionResourceId('Microsoft.Authorization/policyDefinitions', 'DiskNetworkAccess')]" + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('tier').subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-kv-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]" ] } - ] - } - }, - "dependsOn": [ - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-disk-access-{0}', parameters('deploymentNameSuffix')))]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('deploy-id-deployment-{0}', parameters('deploymentNameSuffix'))]", - "resourceGroup": "[parameters('resourceGroupManagement')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "location": { - "value": "[parameters('locationVirtualMachines')]" - }, - "name": { - "value": "[replace(variables('userAssignedIdentityNamePrefix'), parameters('serviceToken'), 'deployment')]" - }, - "tags": { - "value": "[union(createObject('cm-resource-parent', format('{0}}}/resourceGroups/{1}/providers/Microsoft.DesktopVirtualization/hostpools/{2}', subscription().id, parameters('resourceGroupControlPlane'), variables('hostPoolName'))), if(contains(parameters('tags'), 'Microsoft.ManagedIdentity/userAssignedIdentities'), parameters('tags')['Microsoft.ManagedIdentity/userAssignedIdentities'], createObject()), parameters('mlzTags'))]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "12664463677760204409" - } - }, - "parameters": { - "location": { - "type": "string" - }, - "name": { - "type": "string" - }, - "tags": { - "type": "object" - } - }, - "resources": [ - { - "type": "Microsoft.ManagedIdentity/userAssignedIdentities", - "apiVersion": "2018-11-30", - "name": "[parameters('name')]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]" - } ], "outputs": { - "clientId": { + "diskEncryptionSetResourceId": { "type": "string", - "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('name')), '2018-11-30').clientId]" + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('tier').subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-des-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceId.value]" }, - "resourceId": { + "keyVaultName": { "type": "string", - "value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('name'))]" + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('tier').subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-kv-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.keyVaultName.value]" }, - "principalId": { + "keyVaultUri": { "type": "string", - "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('name')), '2018-11-30').principalId]" - } - } - } - } - }, - { - "copy": { - "name": "roleAssignments_deployment", - "count": "[length(range(0, length(variables('resourceGroups'))))]" - }, - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('deploy-role-assignment-{0}-{1}', range(0, length(variables('resourceGroups')))[copyIndex()], parameters('deploymentNameSuffix'))]", - "resourceGroup": "[variables('resourceGroups')[range(0, length(variables('resourceGroups')))[copyIndex()]]]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "principalId": { - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-id-deployment-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.principalId.value]" - }, - "principalType": { - "value": "ServicePrincipal" - }, - "roleDefinitionId": { - "value": "acdd72a7-3385-48ef-bd42-f606fba81ae7" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "1315528727979495191" - } - }, - "parameters": { - "principalId": { - "type": "string" - }, - "principalType": { - "type": "string" + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('tier').subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-kv-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.keyVaultUri.value]" }, - "roleDefinitionId": { - "type": "string" - } - }, - "resources": [ - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "name": "[guid(parameters('principalId'), parameters('roleDefinitionId'), resourceGroup().id)]", - "properties": { - "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitionId'))]", - "principalId": "[parameters('principalId')]", - "principalType": "[parameters('principalType')]" - } - } - ] - } - }, - "dependsOn": [ - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-id-deployment-{0}', parameters('deploymentNameSuffix')))]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('deploy-role-assignment-{0}', parameters('deploymentNameSuffix'))]", - "resourceGroup": "[parameters('resourceGroupStorage')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "principalId": { - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-id-deployment-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.principalId.value]" - }, - "principalType": { - "value": "ServicePrincipal" - }, - "roleDefinitionId": { - "value": "17d1049b-9a84-46fb-8f53-869881c3d3ab" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "1315528727979495191" - } - }, - "parameters": { - "principalId": { - "type": "string" + "keyVaultResourceId": { + "type": "string", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('tier').subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-kv-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.keyVaultResourceId.value]" }, - "principalType": { - "type": "string" + "storageKeyName": { + "type": "string", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('tier').subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-kv-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.storageKeyName.value]" }, - "roleDefinitionId": { - "type": "string" - } - }, - "resources": [ - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "name": "[guid(parameters('principalId'), parameters('roleDefinitionId'), resourceGroup().id)]", - "properties": { - "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitionId'))]", - "principalId": "[parameters('principalId')]", - "principalType": "[parameters('principalType')]" - } + "userAssignedIdentityResourceId": { + "type": "string", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('tier').subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-id-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceId.value]" } - ] + } } }, "dependsOn": [ - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-id-deployment-{0}', parameters('deploymentNameSuffix')))]" + "[subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]", + "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-network-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]", + "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]" ] }, { + "condition": "[not(empty(parameters('virtualNetworkAddressPrefix')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('deploy-mgmt-vm-{0}', parameters('deploymentNameSuffix'))]", - "resourceGroup": "[parameters('resourceGroupManagement')]", + "name": "[format('deploy-storage-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))]", + "location": "[deployment().location]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "deploymentUserAssignedIdentityPrincipalId": { - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-id-deployment-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.principalId.value]" - }, - "deploymentUserAssignedIdentityResourceId": { - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-id-deployment-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceId.value]" - }, - "diskEncryptionSetResourceId": { - "value": "[parameters('diskEncryptionSetResourceId')]" + "blobsPrivateDnsZoneResourceId": { + "value": "[resourceId(variables('hubSubscriptionId'), variables('hubResourceGroupName'), 'Microsoft.Network/privateDnsZones', format('privatelink.blob.{0}', environment().suffixes.storage))]" }, - "diskName": { - "value": "[replace(parameters('namingConvention').virtualMachineDisk, parameters('serviceToken'), 'mgt')]" + "filesPrivateDnsZoneResourceId": { + "value": "[resourceId(variables('hubSubscriptionId'), variables('hubResourceGroupName'), 'Microsoft.Network/privateDnsZones', format('privatelink.file.{0}', environment().suffixes.storage))]" }, - "diskSku": { - "value": "[parameters('diskSku')]" + "keyVaultUri": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-cmk-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.keyVaultUri.value]" }, - "domainJoinPassword": { - "value": "[parameters('domainJoinPassword')]" + "location": { + "value": "[parameters('location')]" }, - "domainJoinUserPrincipalName": { - "value": "[parameters('domainJoinUserPrincipalName')]" + "logStorageSkuName": { + "value": "[parameters('logStorageSkuName')]" }, - "domainName": { - "value": "[parameters('domainName')]" + "mlzTags": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.mlzTags.value]" }, - "hostPoolName": { - "value": "[variables('hostPoolName')]" + "network": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tiers.value[0]]" }, - "location": { - "value": "[parameters('locationVirtualMachines')]" + "queuesPrivateDnsZoneResourceId": { + "value": "[resourceId(variables('hubSubscriptionId'), variables('hubResourceGroupName'), 'Microsoft.Network/privateDnsZones', format('privatelink.queue.{0}', environment().suffixes.storage))]" }, - "mlzTags": { - "value": "[parameters('mlzTags')]" + "resourceGroupName": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.name.value]" }, - "networkInterfaceName": { - "value": "[replace(parameters('namingConvention').virtualMachineNetworkInterface, parameters('serviceToken'), 'mgt')]" + "serviceToken": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value.service]" }, - "organizationalUnitPath": { - "value": "[parameters('organizationalUnitPath')]" + "storageEncryptionKeyName": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-cmk-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.storageKeyName.value]" }, - "resourceGroupControlPlane": { - "value": "[parameters('resourceGroupControlPlane')]" + "subnetResourceId": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-network-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.subnets.value[0].id]" }, - "subnet": { - "value": "[split(parameters('subnetResourceId'), '/')[10]]" + "tablesPrivateDnsZoneResourceId": { + "value": "[resourceId(variables('hubSubscriptionId'), variables('hubResourceGroupName'), 'Microsoft.Network/privateDnsZones', format('privatelink.table.{0}', environment().suffixes.storage))]" }, "tags": { "value": "[parameters('tags')]" }, - "virtualMachineName": { - "value": "[replace(parameters('namingConvention').virtualMachine, parameters('serviceToken'), 'mgt')]" - }, - "virtualMachinePassword": { - "value": "[parameters('virtualMachinePassword')]" - }, - "virtualMachineUsername": { - "value": "[parameters('virtualMachineUsername')]" - }, - "virtualNetwork": { - "value": "[variables('virtualNetworkName')]" + "tier": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tiers.value[0]]" }, - "virtualNetworkResourceGroup": { - "value": "[variables('virtualNetworkResourceGroupName')]" + "userAssignedIdentityResourceId": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-cmk-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.userAssignedIdentityResourceId.value]" } }, "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", "contentVersion": "1.0.0.0", "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "16276650963509010370" + "version": "0.31.92.45157", + "templateHash": "17358288344184718166" } }, "parameters": { - "deploymentUserAssignedIdentityPrincipalId": { - "type": "string" - }, - "deploymentUserAssignedIdentityResourceId": { - "type": "string" - }, - "diskEncryptionSetResourceId": { - "type": "string" - }, - "diskName": { - "type": "string" - }, - "diskSku": { + "blobsPrivateDnsZoneResourceId": { "type": "string" }, - "domainJoinPassword": { - "type": "securestring" - }, - "domainJoinUserPrincipalName": { + "filesPrivateDnsZoneResourceId": { "type": "string" }, - "domainName": { + "keyVaultUri": { "type": "string" }, - "hostPoolName": { + "logStorageSkuName": { "type": "string" }, "location": { @@ -7280,504 +4337,837 @@ "mlzTags": { "type": "object" }, - "networkInterfaceName": { - "type": "string" + "network": { + "type": "object" }, - "organizationalUnitPath": { + "queuesPrivateDnsZoneResourceId": { "type": "string" }, - "resourceGroupControlPlane": { + "resourceGroupName": { "type": "string" }, - "subnet": { + "serviceToken": { "type": "string" }, - "tags": { - "type": "object" - }, - "timestamp": { - "type": "string", - "defaultValue": "[utcNow('yyyyMMddhhmmss')]" - }, - "virtualNetwork": { + "storageEncryptionKeyName": { "type": "string" }, - "virtualNetworkResourceGroup": { + "subnetResourceId": { "type": "string" }, - "virtualMachineName": { + "tablesPrivateDnsZoneResourceId": { "type": "string" }, - "virtualMachinePassword": { - "type": "securestring" + "tags": { + "type": "object" }, - "virtualMachineUsername": { + "tier": { + "type": "object" + }, + "userAssignedIdentityResourceId": { "type": "string" } }, - "variables": { - "tagsVirtualMachines": "[union(createObject('cm-resource-parent', format('{0}}}/resourceGroups/{1}/providers/Microsoft.DesktopVirtualization/hostpools/{2}', subscription().id, parameters('resourceGroupControlPlane'), parameters('hostPoolName'))), if(contains(parameters('tags'), 'Microsoft.Compute/virtualMachines'), parameters('tags')['Microsoft.Compute/virtualMachines'], createObject()), parameters('mlzTags'))]" - }, "resources": [ { - "type": "Microsoft.Network/networkInterfaces", - "apiVersion": "2020-05-01", - "name": "[parameters('networkInterfaceName')]", - "location": "[parameters('location')]", - "tags": "[union(createObject('cm-resource-parent', format('{0}}}/resourceGroups/{1}/providers/Microsoft.DesktopVirtualization/hostpools/{2}', subscription().id, parameters('resourceGroupControlPlane'), parameters('hostPoolName'))), if(contains(parameters('tags'), 'Microsoft.Network/networkInterfaces'), parameters('tags')['Microsoft.Network/networkInterfaces'], createObject()), parameters('mlzTags'))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "storage", + "subscriptionId": "[parameters('network').subscriptionId]", + "resourceGroup": "[parameters('resourceGroupName')]", "properties": { - "ipConfigurations": [ - { - "name": "ipconfig", - "properties": { - "privateIPAllocationMethod": "Dynamic", - "subnet": { - "id": "[resourceId(parameters('virtualNetworkResourceGroup'), 'Microsoft.Network/virtualNetworks/subnets', parameters('virtualNetwork'), parameters('subnet'))]" - }, - "primary": true, - "privateIPAddressVersion": "IPv4" - } + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "blobsPrivateDnsZoneResourceId": { + "value": "[parameters('blobsPrivateDnsZoneResourceId')]" + }, + "filesPrivateDnsZoneResourceId": { + "value": "[parameters('filesPrivateDnsZoneResourceId')]" + }, + "keyVaultUri": { + "value": "[parameters('keyVaultUri')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "mlzTags": { + "value": "[parameters('mlzTags')]" + }, + "queuesPrivateDnsZoneResourceId": { + "value": "[parameters('queuesPrivateDnsZoneResourceId')]" + }, + "serviceToken": { + "value": "[parameters('serviceToken')]" + }, + "skuName": { + "value": "[parameters('logStorageSkuName')]" + }, + "storageEncryptionKeyName": { + "value": "[parameters('storageEncryptionKeyName')]" + }, + "subnetResourceId": { + "value": "[parameters('subnetResourceId')]" + }, + "tablesPrivateDnsZoneResourceId": { + "value": "[parameters('tablesPrivateDnsZoneResourceId')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "tier": { + "value": "[parameters('tier')]" + }, + "userAssignedIdentityResourceId": { + "value": "[parameters('userAssignedIdentityResourceId')]" } - ], - "enableAcceleratedNetworking": false, - "enableIPForwarding": false - } - }, - { - "type": "Microsoft.Compute/virtualMachines", - "apiVersion": "2021-11-01", - "name": "[parameters('virtualMachineName')]", - "location": "[parameters('location')]", - "tags": "[variables('tagsVirtualMachines')]", - "properties": { - "hardwareProfile": { - "vmSize": "Standard_B2s" }, - "storageProfile": { - "imageReference": { - "publisher": "MicrosoftWindowsServer", - "offer": "WindowsServer", - "sku": "2019-datacenter-core-g2", - "version": "latest" + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "1625826941635729014" + } }, - "osDisk": { - "deleteOption": "Delete", - "osType": "Windows", - "createOption": "FromImage", - "caching": "None", - "managedDisk": { - "diskEncryptionSet": { - "id": "[parameters('diskEncryptionSetResourceId')]" - }, - "storageAccountType": "[parameters('diskSku')]" + "parameters": { + "blobsPrivateDnsZoneResourceId": { + "type": "string" + }, + "filesPrivateDnsZoneResourceId": { + "type": "string" + }, + "keyVaultUri": { + "type": "string" + }, + "location": { + "type": "string" + }, + "mlzTags": { + "type": "object" + }, + "queuesPrivateDnsZoneResourceId": { + "type": "string" + }, + "serviceToken": { + "type": "string" }, - "name": "[parameters('diskName')]" + "skuName": { + "type": "string" + }, + "storageEncryptionKeyName": { + "type": "string" + }, + "subnetResourceId": { + "type": "string" + }, + "tablesPrivateDnsZoneResourceId": { + "type": "string" + }, + "tags": { + "type": "object" + }, + "tier": { + "type": "object" + }, + "userAssignedIdentityResourceId": { + "type": "string" + } }, - "dataDisks": [] - }, - "osProfile": { - "computerName": "[parameters('virtualMachineName')]", - "adminUsername": "[parameters('virtualMachineUsername')]", - "adminPassword": "[parameters('virtualMachinePassword')]", - "windowsConfiguration": { - "provisionVMAgent": true, - "enableAutomaticUpdates": false + "variables": { + "subResources": [ + { + "id": "[parameters('blobsPrivateDnsZoneResourceId')]", + "nic": "[parameters('tier').namingConvention.storageAccountBlobNetworkInterface]", + "pe": "[parameters('tier').namingConvention.storageAccountBlobPrivateEndpoint]" + }, + { + "id": "[parameters('filesPrivateDnsZoneResourceId')]", + "nic": "[parameters('tier').namingConvention.storageAccountFileNetworkInterface]", + "pe": "[parameters('tier').namingConvention.storageAccountFilePrivateEndpoint]" + }, + { + "id": "[parameters('queuesPrivateDnsZoneResourceId')]", + "nic": "[parameters('tier').namingConvention.storageAccountQueueNetworkInterface]", + "pe": "[parameters('tier').namingConvention.storageAccountQueuePrivateEndpoint]" + }, + { + "id": "[parameters('tablesPrivateDnsZoneResourceId')]", + "nic": "[parameters('tier').namingConvention.storageAccountTableNetworkInterface]", + "pe": "[parameters('tier').namingConvention.storageAccountTablePrivateEndpoint]" + } + ] }, - "secrets": [], - "allowExtensionOperations": true - }, - "networkProfile": { - "networkInterfaces": [ + "resources": [ { - "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('networkInterfaceName'))]", + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2023-01-01", + "name": "[uniqueString(replace(parameters('tier').namingConvention.storageAccount, parameters('serviceToken'), 'log'), resourceGroup().id)]", + "location": "[parameters('location')]", + "tags": "[union(if(contains(parameters('tags'), 'Microsoft.Storage/storageAccounts'), parameters('tags')['Microsoft.Storage/storageAccounts'], createObject()), parameters('mlzTags'))]", + "identity": { + "type": "UserAssigned", + "userAssignedIdentities": { + "[format('{0}', parameters('userAssignedIdentityResourceId'))]": {} + } + }, + "kind": "StorageV2", + "sku": { + "name": "[parameters('skuName')]" + }, "properties": { - "deleteOption": "Delete" + "accessTier": "Hot", + "allowBlobPublicAccess": false, + "allowCrossTenantReplication": false, + "allowedCopyScope": "PrivateLink", + "allowSharedKeyAccess": false, + "defaultToOAuthAuthentication": false, + "dnsEndpointType": "Standard", + "encryption": { + "identity": { + "userAssignedIdentity": "[parameters('userAssignedIdentityResourceId')]" + }, + "keySource": "Microsoft.KeyVault", + "keyvaultproperties": { + "keyvaulturi": "[parameters('keyVaultUri')]", + "keyname": "[parameters('storageEncryptionKeyName')]" + }, + "requireInfrastructureEncryption": true, + "services": { + "blob": { + "keyType": "Account", + "enabled": true + }, + "file": { + "keyType": "Account", + "enabled": true + }, + "queue": { + "keyType": "Account", + "enabled": true + }, + "table": { + "keyType": "Account", + "enabled": true + } + } + }, + "minimumTlsVersion": "TLS1_2", + "networkAcls": { + "bypass": "AzureServices", + "virtualNetworkRules": [], + "ipRules": [], + "defaultAction": "Deny" + }, + "publicNetworkAccess": "Disabled", + "supportsHttpsTrafficOnly": true } - } - ] - }, - "securityProfile": { - "uefiSettings": { - "secureBootEnabled": true, - "vTpmEnabled": true - }, - "securityType": "TrustedLaunch", - "encryptionAtHost": true - }, - "diagnosticsProfile": { - "bootDiagnostics": { - "enabled": false - } - }, - "licenseType": "Windows_Server" - }, - "identity": { - "type": "SystemAssigned, UserAssigned", - "userAssignedIdentities": { - "[format('{0}', parameters('deploymentUserAssignedIdentityResourceId'))]": {} - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/networkInterfaces', parameters('networkInterfaceName'))]" - ] - }, - { - "type": "Microsoft.Compute/virtualMachines/extensions", - "apiVersion": "2021-03-01", - "name": "[format('{0}/{1}', parameters('virtualMachineName'), 'IaaSAntimalware')]", - "location": "[parameters('location')]", - "tags": "[variables('tagsVirtualMachines')]", - "properties": { - "publisher": "Microsoft.Azure.Security", - "type": "IaaSAntimalware", - "typeHandlerVersion": "1.3", - "autoUpgradeMinorVersion": true, - "enableAutomaticUpgrade": false, - "settings": { - "AntimalwareEnabled": true, - "RealtimeProtectionEnabled": "true", - "ScheduledScanSettings": { - "isEnabled": "true", - "day": "7", - "time": "120", - "scanType": "Quick" - }, - "Exclusions": {} - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Compute/virtualMachines', parameters('virtualMachineName'))]" - ] - }, - { - "type": "Microsoft.Compute/virtualMachines/extensions", - "apiVersion": "2021-03-01", - "name": "[format('{0}/{1}', parameters('virtualMachineName'), 'GuestAttestation')]", - "location": "[parameters('location')]", - "properties": { - "publisher": "Microsoft.Azure.Security.WindowsAttestation", - "type": "GuestAttestation", - "typeHandlerVersion": "1.0", - "autoUpgradeMinorVersion": true, - "settings": { - "AttestationConfig": { - "MaaSettings": { - "maaEndpoint": "", - "maaTenantName": "GuestAttestation" }, - "AscSettings": { - "ascReportingEndpoint": "", - "ascReportingFrequency": "" + { + "copy": { + "name": "privateEndpoints", + "count": "[length(variables('subResources'))]" + }, + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-04-01", + "name": "[variables('subResources')[copyIndex()].pe]", + "location": "[parameters('location')]", + "tags": "[union(if(contains(parameters('tags'), 'Microsoft.Network/privateEndpoints'), parameters('tags')['Microsoft.Network/privateEndpoints'], createObject()), parameters('mlzTags'))]", + "properties": { + "customNetworkInterfaceName": "[variables('subResources')[copyIndex()].nic]", + "privateLinkServiceConnections": [ + { + "name": "[variables('subResources')[copyIndex()].pe]", + "properties": { + "privateLinkServiceId": "[resourceId('Microsoft.Storage/storageAccounts', uniqueString(replace(parameters('tier').namingConvention.storageAccount, parameters('serviceToken'), 'log'), resourceGroup().id))]", + "groupIds": [ + "[split(split(variables('subResources')[copyIndex()].id, '/')[8], '.')[1]]" + ] + } + } + ], + "subnet": { + "id": "[parameters('subnetResourceId')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', uniqueString(replace(parameters('tier').namingConvention.storageAccount, parameters('serviceToken'), 'log'), resourceGroup().id))]" + ] }, - "useCustomToken": "false", - "disableAlerts": "false" + { + "copy": { + "name": "privateDnsZoneGroups", + "count": "[length(variables('subResources'))]" + }, + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2021-08-01", + "name": "[format('{0}/{1}', variables('subResources')[copyIndex()].pe, uniqueString(replace(parameters('tier').namingConvention.storageAccount, parameters('serviceToken'), 'log'), resourceGroup().id))]", + "properties": { + "privateDnsZoneConfigs": [ + { + "name": "ipconfig1", + "properties": { + "privateDnsZoneId": "[variables('subResources')[copyIndex()].id]" + } + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateEndpoints', variables('subResources')[copyIndex()].pe)]", + "[resourceId('Microsoft.Storage/storageAccounts', uniqueString(replace(parameters('tier').namingConvention.storageAccount, parameters('serviceToken'), 'log'), resourceGroup().id))]" + ] + } + ], + "outputs": { + "id": { + "type": "string", + "value": "[resourceId('Microsoft.Storage/storageAccounts', uniqueString(replace(parameters('tier').namingConvention.storageAccount, parameters('serviceToken'), 'log'), resourceGroup().id))]" + } } } - }, - "dependsOn": [ - "[resourceId('Microsoft.Compute/virtualMachines', parameters('virtualMachineName'))]" - ] - }, - { - "type": "Microsoft.Compute/virtualMachines/extensions", - "apiVersion": "2019-07-01", - "name": "[format('{0}/{1}', parameters('virtualMachineName'), 'JsonADDomainExtension')]", - "location": "[parameters('location')]", - "tags": "[variables('tagsVirtualMachines')]", - "properties": { - "forceUpdateTag": "[parameters('timestamp')]", - "publisher": "Microsoft.Compute", - "type": "JsonADDomainExtension", - "typeHandlerVersion": "1.3", - "autoUpgradeMinorVersion": true, - "settings": { - "Name": "[parameters('domainName')]", - "Options": "3", - "OUPath": "[parameters('organizationalUnitPath')]", - "Restart": "true", - "User": "[parameters('domainJoinUserPrincipalName')]" - }, - "protectedSettings": { - "Password": "[parameters('domainJoinPassword')]" - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Compute/virtualMachines', parameters('virtualMachineName'))]" - ] - }, - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Compute/virtualMachines/{0}', parameters('virtualMachineName'))]", - "name": "[guid(parameters('deploymentUserAssignedIdentityPrincipalId'), 'a959dbd1-f747-45e3-8ba6-dd80f235f97c', resourceId('Microsoft.Compute/virtualMachines', parameters('virtualMachineName')))]", - "properties": { - "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c')]", - "principalId": "[parameters('deploymentUserAssignedIdentityPrincipalId')]", - "principalType": "ServicePrincipal" - }, - "dependsOn": [ - "[resourceId('Microsoft.Compute/virtualMachines', parameters('virtualMachineName'))]" - ] + } } ], "outputs": { - "name": { - "type": "string", - "value": "[parameters('virtualMachineName')]" - }, - "resourceId": { + "storageAccountResourceId": { "type": "string", - "value": "[resourceId('Microsoft.Compute/virtualMachines', parameters('virtualMachineName'))]" + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', parameters('network').subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', 'storage'), '2022-09-01').outputs.id.value]" } } } }, "dependsOn": [ - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-id-deployment-{0}', parameters('deploymentNameSuffix')))]" + "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-cmk-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]", + "[subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]", + "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-network-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]", + "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]" ] }, { - "condition": "[or(parameters('enableApplicationInsights'), parameters('enableAvdInsights'))]", + "condition": "[not(empty(parameters('virtualNetworkAddressPrefix')))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('deploy-monitoring-{0}', parameters('deploymentNameSuffix'))]", - "resourceGroup": "[parameters('resourceGroupManagement')]", + "name": "[format('deploy-diag-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))]", + "location": "[deployment().location]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { + "deployActivityLogDiagnosticSetting": { + "value": "[parameters('deployActivityLogDiagnosticSetting')]" + }, "deploymentNameSuffix": { "value": "[parameters('deploymentNameSuffix')]" }, - "enableAvdInsights": { - "value": "[parameters('enableAvdInsights')]" + "keyVaultDiagnosticLogs": { + "value": "[parameters('keyVaultDiagnosticsLogs')]" }, - "hostPoolName": { - "value": "[variables('hostPoolName')]" + "keyVaultName": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-cmk-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.keyVaultName.value]" }, - "location": { - "value": "[parameters('locationVirtualMachines')]" + "logAnalyticsWorkspaceResourceId": { + "value": "[parameters('logAnalyticsWorkspaceResourceId')]" }, - "logAnalyticsWorkspaceRetention": { - "value": "[parameters('logAnalyticsWorkspaceRetention')]" + "networkSecurityGroupDiagnosticsLogs": { + "value": "[parameters('networkSecurityGroupDiagnosticsLogs')]" }, - "logAnalyticsWorkspaceSku": { - "value": "[parameters('logAnalyticsWorkspaceSku')]" + "networkSecurityGroupDiagnosticsMetrics": { + "value": "[parameters('networkSecurityGroupDiagnosticsMetrics')]" }, - "mlzTags": { - "value": "[parameters('mlzTags')]" + "networkSecurityGroupName": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-network-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.networkSecurityGroupName.value]" }, - "namingConvention": { - "value": "[parameters('namingConvention')]" + "resourceGroupName": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.name.value]" }, - "privateLinkScopeResourceId": { - "value": "[parameters('privateLinkScopeResourceId')]" + "serviceToken": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value.service]" }, - "resourceGroupControlPlane": { - "value": "[parameters('resourceGroupControlPlane')]" + "storageAccountResourceId": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-storage-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.storageAccountResourceId.value]" }, - "serviceToken": { - "value": "[parameters('serviceToken')]" + "tier": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tiers.value[0]]" }, - "tags": { - "value": "[parameters('tags')]" + "virtualNetworkDiagnosticsLogs": { + "value": "[parameters('virtualNetworkDiagnosticsLogs')]" + }, + "virtualNetworkDiagnosticsMetrics": { + "value": "[parameters('virtualNetworkDiagnosticsMetrics')]" + }, + "virtualNetworkName": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-network-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.virtualNetworkName.value]" } }, "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", "contentVersion": "1.0.0.0", "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "12055090228824682687" + "version": "0.31.92.45157", + "templateHash": "4345251511078445463" } }, "parameters": { - "deploymentNameSuffix": { - "type": "string" - }, - "enableAvdInsights": { + "deployActivityLogDiagnosticSetting": { "type": "bool" }, - "hostPoolName": { + "deploymentNameSuffix": { "type": "string" }, - "location": { - "type": "string" + "keyVaultDiagnosticLogs": { + "type": "array" }, - "logAnalyticsWorkspaceRetention": { - "type": "int" + "keyVaultName": { + "type": "string" }, - "logAnalyticsWorkspaceSku": { + "logAnalyticsWorkspaceResourceId": { "type": "string" }, - "mlzTags": { - "type": "object" + "networkSecurityGroupDiagnosticsLogs": { + "type": "array" }, - "namingConvention": { - "type": "object" + "networkSecurityGroupDiagnosticsMetrics": { + "type": "array" }, - "privateLinkScopeResourceId": { + "networkSecurityGroupName": { "type": "string" }, - "resourceGroupControlPlane": { + "resourceGroupName": { "type": "string" }, - "service": { - "type": "string", - "defaultValue": "mgmt" - }, "serviceToken": { "type": "string" }, - "tags": { + "storageAccountResourceId": { + "type": "string" + }, + "tier": { "type": "object" + }, + "virtualNetworkDiagnosticsLogs": { + "type": "array" + }, + "virtualNetworkDiagnosticsMetrics": { + "type": "array" + }, + "virtualNetworkName": { + "type": "string" } }, "resources": [ { - "type": "Microsoft.OperationalInsights/workspaces", - "apiVersion": "2021-06-01", - "name": "[replace(parameters('namingConvention').logAnalyticsWorkspace, parameters('serviceToken'), parameters('service'))]", - "location": "[parameters('location')]", - "tags": "[union(createObject('cm-resource-parent', format('{0}}}/resourceGroups/{1}/providers/Microsoft.DesktopVirtualization/hostpools/{2}', subscription().id, parameters('resourceGroupControlPlane'), parameters('hostPoolName'))), if(contains(parameters('tags'), 'Microsoft.OperationalInsights/workspaces'), parameters('tags')['Microsoft.OperationalInsights/workspaces'], createObject()), parameters('mlzTags'))]", + "condition": "[parameters('deployActivityLogDiagnosticSetting')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('deploy-activity-diags-{0}-{1}', parameters('tier').shortName, parameters('deploymentNameSuffix'))]", + "subscriptionId": "[parameters('tier').subscriptionId]", + "location": "[deployment().location]", "properties": { - "sku": { - "name": "[parameters('logAnalyticsWorkspaceSku')]" + "expressionEvaluationOptions": { + "scope": "inner" }, - "retentionInDays": "[parameters('logAnalyticsWorkspaceRetention')]", - "workspaceCapping": { - "dailyQuotaGb": -1 + "mode": "Incremental", + "parameters": { + "logAnalyticsWorkspaceId": { + "value": "[parameters('logAnalyticsWorkspaceResourceId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "4687229436121899773" + } + }, + "parameters": { + "logAnalyticsWorkspaceId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2017-05-01-preview", + "name": "[format('diag-activity-log-{0}', subscription().subscriptionId)]", + "properties": { + "workspaceId": "[parameters('logAnalyticsWorkspaceId')]", + "logs": [ + { + "category": "Administrative", + "enabled": true + }, + { + "category": "Security", + "enabled": true + }, + { + "category": "ServiceHealth", + "enabled": true + }, + { + "category": "Alert", + "enabled": true + }, + { + "category": "Recommendation", + "enabled": true + }, + { + "category": "Policy", + "enabled": true + }, + { + "category": "Autoscale", + "enabled": true + }, + { + "category": "ResourceHealth", + "enabled": true + } + ] + } + } + ] + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('deploy-kv-diags-{0}-{1}', parameters('tier').shortName, parameters('deploymentNameSuffix'))]", + "subscriptionId": "[parameters('tier').subscriptionId]", + "resourceGroup": "[parameters('resourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" }, - "publicNetworkAccessForIngestion": "Disabled", - "publicNetworkAccessForQuery": "Enabled" + "mode": "Incremental", + "parameters": { + "keyVaultDiagnosticSettingName": { + "value": "[replace(parameters('tier').namingConvention.keyVaultDiagnosticSetting, format('{0}-', parameters('serviceToken')), '')]" + }, + "keyVaultName": { + "value": "[parameters('keyVaultName')]" + }, + "keyVaultStorageAccountId": { + "value": "[parameters('storageAccountResourceId')]" + }, + "logAnalyticsWorkspaceResourceId": { + "value": "[parameters('logAnalyticsWorkspaceResourceId')]" + }, + "logs": { + "value": "[parameters('keyVaultDiagnosticLogs')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "1721966359516622278" + } + }, + "parameters": { + "keyVaultDiagnosticSettingName": { + "type": "string" + }, + "keyVaultName": { + "type": "string" + }, + "keyVaultStorageAccountId": { + "type": "string" + }, + "logAnalyticsWorkspaceResourceId": { + "type": "string" + }, + "logs": { + "type": "array" + } + }, + "resources": [ + { + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2017-05-01-preview", + "scope": "[format('Microsoft.KeyVault/vaults/{0}', parameters('keyVaultName'))]", + "name": "[parameters('keyVaultDiagnosticSettingName')]", + "properties": { + "storageAccountId": "[parameters('keyVaultStorageAccountId')]", + "workspaceId": "[parameters('logAnalyticsWorkspaceResourceId')]", + "logs": "[parameters('logs')]" + } + } + ] + } } }, { - "condition": "[parameters('enableAvdInsights')]", - "type": "Microsoft.Insights/dataCollectionRules", - "apiVersion": "2022-06-01", - "name": "[format('microsoft-avdi-{0}', replace(parameters('namingConvention').dataCollectionRule, parameters('serviceToken'), parameters('service')))]", - "location": "[parameters('location')]", - "tags": "[union(createObject('cm-resource-parent', format('{0}}}/resourceGroups/{1}/providers/Microsoft.DesktopVirtualization/hostpools/{2}', subscription().id, parameters('resourceGroupControlPlane'), parameters('hostPoolName'))), if(contains(parameters('tags'), 'Microsoft.Insights/dataCollectionRules'), parameters('tags')['Microsoft.Insights/dataCollectionRules'], createObject()), parameters('mlzTags'))]", - "kind": "Windows", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('deploy-nsg-diags-{0}-{1}', parameters('tier').shortName, parameters('deploymentNameSuffix'))]", + "subscriptionId": "[parameters('tier').subscriptionId]", + "resourceGroup": "[parameters('resourceGroupName')]", "properties": { - "dataSources": { - "performanceCounters": [ - { - "streams": [ - "Microsoft-Perf" - ], - "samplingFrequencyInSeconds": 30, - "counterSpecifiers": [ - "\\LogicalDisk(C:)\\Avg. Disk Queue Length", - "\\LogicalDisk(C:)\\Current Disk Queue Length", - "\\Memory\\Available Mbytes", - "\\Memory\\Page Faults/sec", - "\\Memory\\Pages/sec", - "\\Memory\\% Committed Bytes In Use", - "\\PhysicalDisk(*)\\Avg. Disk Queue Length", - "\\PhysicalDisk(*)\\Avg. Disk sec/Read", - "\\PhysicalDisk(*)\\Avg. Disk sec/Transfer", - "\\PhysicalDisk(*)\\Avg. Disk sec/Write", - "\\Processor Information(_Total)\\% Processor Time", - "\\User Input Delay per Process(*)\\Max Input Delay", - "\\User Input Delay per Session(*)\\Max Input Delay", - "\\RemoteFX Network(*)\\Current TCP RTT", - "\\RemoteFX Network(*)\\Current UDP Bandwidth" - ], - "name": "perfCounterDataSource10" + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "logAnalyticsWorkspaceResourceId": { + "value": "[parameters('logAnalyticsWorkspaceResourceId')]" + }, + "logs": { + "value": "[parameters('networkSecurityGroupDiagnosticsLogs')]" + }, + "logStorageAccountResourceId": { + "value": "[parameters('storageAccountResourceId')]" + }, + "metrics": { + "value": "[parameters('networkSecurityGroupDiagnosticsMetrics')]" + }, + "networkSecurityGroupDiagnosticSettingName": { + "value": "[parameters('tier').namingConvention.networkSecurityGroupDiagnosticSetting]" + }, + "networkSecurityGroupName": { + "value": "[parameters('networkSecurityGroupName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "2073766618455932098" + } + }, + "parameters": { + "logAnalyticsWorkspaceResourceId": { + "type": "string" }, - { - "streams": [ - "Microsoft-Perf" - ], - "samplingFrequencyInSeconds": 60, - "counterSpecifiers": [ - "\\LogicalDisk(C:)\\% Free Space", - "\\LogicalDisk(C:)\\Avg. Disk sec/Transfer", - "\\Terminal Services(*)\\Active Sessions", - "\\Terminal Services(*)\\Inactive Sessions", - "\\Terminal Services(*)\\Total Sessions" - ], - "name": "perfCounterDataSource30" + "logs": { + "type": "array" + }, + "logStorageAccountResourceId": { + "type": "string" + }, + "metrics": { + "type": "array" + }, + "networkSecurityGroupDiagnosticSettingName": { + "type": "string" + }, + "networkSecurityGroupName": { + "type": "string" } - ], - "windowsEventLogs": [ + }, + "resources": [ { - "streams": [ - "Microsoft-Event" - ], - "xPathQueries": [ - "Microsoft-Windows-TerminalServices-RemoteConnectionManager/Admin!*[System[(Level=2 or Level=3 or Level=4 or Level=0)]]", - "Microsoft-Windows-TerminalServices-LocalSessionManager/Operational!*[System[(Level=2 or Level=3 or Level=4 or Level=0)]]", - "System!*", - "Microsoft-FSLogix-Apps/Operational!*[System[(Level=2 or Level=3 or Level=4 or Level=0)]]", - "Application!*[System[(Level=2 or Level=3)]]", - "Microsoft-FSLogix-Apps/Admin!*[System[(Level=2 or Level=3 or Level=4 or Level=0)]]" - ], - "name": "eventLogsDataSource" + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2017-05-01-preview", + "scope": "[format('Microsoft.Network/networkSecurityGroups/{0}', parameters('networkSecurityGroupName'))]", + "name": "[parameters('networkSecurityGroupDiagnosticSettingName')]", + "properties": { + "storageAccountId": "[parameters('logStorageAccountResourceId')]", + "workspaceId": "[parameters('logAnalyticsWorkspaceResourceId')]", + "logs": "[parameters('logs')]", + "metrics": "[parameters('metrics')]" + } } ] + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('deploy-vnet-diags-{0}-{1}', parameters('tier').shortName, parameters('deploymentNameSuffix'))]", + "subscriptionId": "[parameters('tier').subscriptionId]", + "resourceGroup": "[parameters('resourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" }, - "destinations": { - "logAnalytics": [ + "mode": "Incremental", + "parameters": { + "logAnalyticsWorkspaceResourceId": { + "value": "[parameters('logAnalyticsWorkspaceResourceId')]" + }, + "logs": { + "value": "[parameters('virtualNetworkDiagnosticsLogs')]" + }, + "logStorageAccountResourceId": { + "value": "[parameters('storageAccountResourceId')]" + }, + "metrics": { + "value": "[parameters('virtualNetworkDiagnosticsMetrics')]" + }, + "virtualNetworkDiagnosticSettingName": { + "value": "[parameters('tier').namingConvention.virtualNetworkDiagnosticSetting]" + }, + "virtualNetworkName": { + "value": "[parameters('virtualNetworkName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "9546260853018527046" + } + }, + "parameters": { + "logAnalyticsWorkspaceResourceId": { + "type": "string" + }, + "logs": { + "type": "array" + }, + "logStorageAccountResourceId": { + "type": "string" + }, + "metrics": { + "type": "array" + }, + "virtualNetworkDiagnosticSettingName": { + "type": "string" + }, + "virtualNetworkName": { + "type": "string" + } + }, + "resources": [ { - "workspaceResourceId": "[resourceId('Microsoft.OperationalInsights/workspaces', replace(parameters('namingConvention').logAnalyticsWorkspace, parameters('serviceToken'), parameters('service')))]", - "name": "la-workspace" + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2017-05-01-preview", + "scope": "[format('Microsoft.Network/virtualNetworks/{0}', parameters('virtualNetworkName'))]", + "name": "[parameters('virtualNetworkDiagnosticSettingName')]", + "properties": { + "storageAccountId": "[parameters('logStorageAccountResourceId')]", + "workspaceId": "[parameters('logAnalyticsWorkspaceResourceId')]", + "logs": "[parameters('logs')]", + "metrics": "[parameters('metrics')]" + } } ] - }, - "dataFlows": [ - { - "streams": [ - "Microsoft-Perf", - "Microsoft-Event" - ], - "destinations": [ - "la-workspace" - ] - } - ] - }, - "dependsOn": [ - "[resourceId('Microsoft.OperationalInsights/workspaces', replace(parameters('namingConvention').logAnalyticsWorkspace, parameters('serviceToken'), parameters('service')))]" - ] - }, - { - "condition": "[parameters('enableAvdInsights')]", - "type": "Microsoft.Insights/dataCollectionEndpoints", - "apiVersion": "2021-04-01", - "name": "[replace(parameters('namingConvention').dataCollectionEndpoint, parameters('serviceToken'), parameters('service'))]", - "location": "[parameters('location')]", - "tags": "[union(createObject('cm-resource-parent', format('{0}}}/resourceGroups/{1}/providers/Microsoft.DesktopVirtualization/hostpools/{2}', subscription().id, parameters('resourceGroupControlPlane'), parameters('hostPoolName'))), if(contains(parameters('tags'), 'Microsoft.Insights/dataCollectionEndpoints'), parameters('tags')['Microsoft.Insights/dataCollectionEndpoints'], createObject()), parameters('mlzTags'))]", - "kind": "Windows", - "properties": { - "networkAcls": { - "publicNetworkAccess": "Disabled" } } + } + ] + } + }, + "dependsOn": [ + "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-cmk-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]", + "[subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]", + "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-network-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]", + "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]", + "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-storage-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]" + ] + }, + { + "condition": "[and(parameters('deployPolicy'), not(empty(parameters('virtualNetworkAddressPrefix'))))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('assign-policy-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))]", + "location": "[deployment().location]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "deploymentNameSuffix": { + "value": "[parameters('deploymentNameSuffix')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "logAnalyticsWorkspaceResourceId": { + "value": "[parameters('logAnalyticsWorkspaceResourceId')]" + }, + "tiers": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tiers.value]" + }, + "policy": { + "value": "[parameters('policy')]" + }, + "resourceGroupNames": { + "value": [ + "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.name.value]" + ] + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "15014526386353172066" + } + }, + "parameters": { + "deploymentNameSuffix": { + "type": "string" + }, + "location": { + "type": "string" + }, + "logAnalyticsWorkspaceResourceId": { + "type": "string" + }, + "policy": { + "type": "string" + }, + "resourceGroupNames": { + "type": "array" }, + "tiers": { + "type": "array" + } + }, + "resources": [ { + "copy": { + "name": "policyAssignment", + "count": "[length(parameters('tiers'))]" + }, "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('deploy-private-link-scope-law-{0}', parameters('deploymentNameSuffix'))]", - "subscriptionId": "[split(parameters('privateLinkScopeResourceId'), '/')[2]]", - "resourceGroup": "[split(parameters('privateLinkScopeResourceId'), '/')[4]]", + "name": "[format('assign-policy-{0}-{1}', parameters('tiers')[copyIndex()].name, parameters('deploymentNameSuffix'))]", + "subscriptionId": "[parameters('tiers')[copyIndex()].subscriptionId]", + "resourceGroup": "[parameters('resourceGroupNames')[copyIndex()]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { + "builtInAssignment": { + "value": "[parameters('policy')]" + }, "logAnalyticsWorkspaceResourceId": { - "value": "[resourceId('Microsoft.OperationalInsights/workspaces', replace(parameters('namingConvention').logAnalyticsWorkspace, parameters('serviceToken'), parameters('service')))]" + "value": "[parameters('logAnalyticsWorkspaceResourceId')]" }, - "privateLinkScopeResourceId": { - "value": "[parameters('privateLinkScopeResourceId')]" + "location": { + "value": "[parameters('location')]" } }, "template": { @@ -7786,224 +5176,961 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "8149225552603313421" + "version": "0.31.92.45157", + "templateHash": "8545903679924437739" } }, "parameters": { - "applicationInsightsResourceId": { - "type": "string", - "defaultValue": "" - }, - "dataCollectionEndpointResourceId": { + "builtInAssignment": { "type": "string", - "defaultValue": "" + "defaultValue": "NISTRev4", + "allowedValues": [ + "NISTRev4", + "NISTRev5", + "IL5", + "CMMC" + ], + "metadata": { + "description": "[NISTRev4/NISTRev5/IL5/CMMC] Built-in policy assignments to assign, default is NISTRev4. IL5 is only available for AzureUsGovernment and will switch to NISTRev4 if tried in AzureCloud." + } }, "logAnalyticsWorkspaceResourceId": { - "type": "string", - "defaultValue": "" - }, - "privateLinkScopeResourceId": { "type": "string" + }, + "deployRemediation": { + "type": "bool", + "defaultValue": false, + "metadata": { + "description": "Starts a policy remediation for the VM Agent policies in hub RG. Set to false by default since this is time consuming in deployment." + } + }, + "location": { + "type": "string", + "defaultValue": "[resourceGroup().location]", + "metadata": { + "description": "The location of this resource" + } } }, + "variables": { + "$fxv#0": " {\r\n \"listOfMembersToExcludeFromWindowsVMAdministratorsGroup\": \r\n {\r\n \"value\": \"admin\"\r\n },\r\n \"listOfMembersToIncludeInWindowsVMAdministratorsGroup\": \r\n {\r\n \"value\": \"azureuser\"\r\n },\r\n \"logAnalyticsWorkspaceIdforVMReporting\": \r\n {\r\n \"value\": \"\"\r\n },\r\n \"IncludeArcMachines\": \r\n {\r\n \"value\": \"true\"\r\n },\r\n \"MinimumTLSVersion-5752e6d6-1206-46d8-8ab1-ecc2f71a8112\": \r\n {\r\n \"value\": \"1.2\"\r\n },\r\n \"NotAvailableMachineState-bed48b13-6647-468e-aa2f-1af1d3f4dd40\": \r\n {\r\n \"value\": \"Compliant\"\r\n },\r\n \"requiredRetentionDays\": \r\n {\r\n \"value\": \"365\"\r\n },\r\n \"resourceGroupName-b6e2945c-0b7b-40f5-9233-7a5323b5cdc6\": \r\n {\r\n \"value\": \"NetworkWatcherRG\"\r\n }\r\n }", + "$fxv#1": " {\r\n \"IncludeArcMachines\": \r\n {\r\n \"value\": \"true\"\r\n },\r\n \"MinimumTLSVersion-5752e6d6-1206-46d8-8ab1-ecc2f71a8112\": \r\n {\r\n \"value\": \"1.2\"\r\n },\r\n \"NotAvailableMachineState-bed48b13-6647-468e-aa2f-1af1d3f4dd40\": \r\n {\r\n \"value\": \"Compliant\"\r\n },\r\n \"requiredRetentionDays\": \r\n {\r\n \"value\": \"365\"\r\n },\r\n \"resourceGroupName-b6e2945c-0b7b-40f5-9233-7a5323b5cdc6\": \r\n {\r\n \"value\": \"NetworkWatcherRG\"\r\n }\r\n }", + "$fxv#2": "{\r\n \"IncludeArcMachines\" : { \r\n \"value\" : \"false\"\r\n },\r\n \"NotAvailableMachineState-bed48b13-6647-468e-aa2f-1af1d3f4dd40\" : { \r\n \"value\" : \"Compliant\"\r\n },\r\n \"MinimumTLSVersionForWindowsServers\" : { \r\n \"value\" : \"1.2\"\r\n },\r\n \"requiredRetentionDays\" : { \r\n \"value\" : \"365\"\r\n },\r\n \"effect-febd0533-8e55-448f-b837-bd0e06f16469\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"allowedContainerImagesRegex-febd0533-8e55-448f-b837-bd0e06f16469\" : { \r\n \"value\" : \"^(.+){0}$\"\r\n },\r\n \"effect-95edb821-ddaf-4404-9732-666045e056b4\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"effect-440b515e-a580-421e-abeb-b159a61ddcbc\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"effect-233a2a17-77ca-4fb1-9b6b-69223d272a44\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"effect-e345eecc-fa47-480f-9e88-67dcc122b164\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"cpuLimit-e345eecc-fa47-480f-9e88-67dcc122b164\" : { \r\n \"value\" : \"0\"\r\n },\r\n \"memoryLimit-e345eecc-fa47-480f-9e88-67dcc122b164\" : { \r\n \"value\" : \"0\"\r\n },\r\n \"effect-f06ddb64-5fa3-4b77-b166-acb36f7f6042\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"runAsUserRule-f06ddb64-5fa3-4b77-b166-acb36f7f6042\" : { \r\n \"value\" : \"MustRunAsNonRoot\"\r\n },\r\n \"runAsGroupRule-f06ddb64-5fa3-4b77-b166-acb36f7f6042\" : { \r\n \"value\" : \"RunAsAny\"\r\n },\r\n \"supplementalGroupsRule-f06ddb64-5fa3-4b77-b166-acb36f7f6042\" : { \r\n \"value\" : \"RunAsAny\"\r\n },\r\n \"fsGroupRule-f06ddb64-5fa3-4b77-b166-acb36f7f6042\" : { \r\n \"value\" : \"RunAsAny\"\r\n },\r\n \"effect-1c6e92c9-99f0-4e55-9cf2-0c234dc48f99\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"effect-47a1ee2f-2a2a-4576-bf2a-e0e36709c2b8\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"effect-df49d893-a74c-421d-bc95-c663042e5b80\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"effect-1a5b4dca-0b6f-4cf5-907c-56316bc1bf3d\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"effect-c26596ff-4d70-4e6a-9a30-c2506bd2f80c\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"effect-511f5417-5d12-434d-ab2e-816901e72a5e\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"effect-82985f06-dc18-4a48-bc1c-b9f4f0098cfe\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"effect-098fc59e-46c7-4d99-9b16-64990e543d75\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"NetworkWatcherResourceGroupName\" : { \r\n \"value\" : \"NetworkWatcherRG\"\r\n },\r\n \"setting-a6fb4358-5bf4-4ad7-ba82-2cd2f41ce5e9\" : { \r\n \"value\" : \"enabled\"\r\n },\r\n \"aadAuthenticationInServiceFabricMonitoringEffect\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-71ef260a-8f18-47b7-abcb-62d0673d94dc\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-055aa869-bc98-4af8-bafc-23f1ab6ffe2c\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-564feb30-bf6a-4854-b4bb-0d2d2d1e6c66\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-862e97cf-49fc-4a5c-9de4-40d4e2e7c8eb\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-d9da03a1-f3c3-412a-9709-947156872263\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-617c02be-7f02-4efd-8836-3180d47b6c68\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-0b60c0b2-2dc2-4e1c-b5c9-abbed971de53\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-1e66c121-a66a-4b1f-9b83-0fd99bf0fc2d\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-ec068d99-e9c7-401f-8cef-5bdde4e6ccf1\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-c349d81b-9985-44ae-a8da-ff98d108ede8\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-3657f5a0-770e-44a3-b44e-9431ba1e9735\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-b4ac1030-89c5-4697-8e00-28b5ba6a8811\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"effect-ea0dfaed-95fb-448c-934e-d6e713ce393d\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"effect-4733ea7b-a883-42fe-8cac-97454c2a9e4a\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-f4b53539-8df9-40e4-86c6-6b607703bd4e\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-41425d9f-d1a5-499a-9932-f8ed8453932c\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-fc4d8e41-e223-45ea-9bf5-eada37891d87\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-86efb160-8de7-451d-bc08-5d475b0aadae\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-4ec52d6d-beb7-40c4-9a9e-fe753254690e\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-64d314f6-6062-4780-a861-c23e8951bee5\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-1fd32ebd-e4c3-4e13-a54a-d7422d4d95f6\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-fa298e57-9444-42ba-bf04-86e8470e32c7\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"effect-67121cc7-ff39-4ab8-b7e3-95b84dab487d\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-1f905d99-2ab7-462c-a6b0-f709acca6c8f\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"effect-5b9159ae-1701-4a6f-9a7a-aa9c8ddd0580\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-ba769a63-b8cc-4b2d-abf6-ac33c7204be8\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-81e74cea-30fd-40d5-802f-d72103c2aaaa\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-0aa61e00-0a01-4a3c-9945-e93cffedf0e6\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-47031206-ce96-41f8-861b-6a915f3de284\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-87ba29ef-1ab3-4d82-b763-87fcd4f531f7\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"effect-51522a96-0869-4791-82f3-981000c2c67f\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"effect-b5ec538c-daa0-4006-8596-35468b9148e8\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-56a5ee18-2ae6-4810-86f7-18e39ce5629b\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-2e94d99a-8a36-4563-bc77-810d8893b671\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-1fafeaf6-7927-4059-a50a-8eb2a7a6f2b5\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-99e9ccd8-3db9-4592-b0d1-14b1715a4d8a\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-1f68a601-6e6d-4e42-babf-3f643a047ea2\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"effect-f7d52b2d-e161-4dfa-a82b-55e564167385\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-7d7be79c-23ba-4033-84dd-45e2a5ccdd67\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-ca91455f-eace-4f96-be59-e6e2c35b4816\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-702dd420-7fcc-42c5-afe8-4026edd20fe0\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"diagnosticsLogsInRedisCacheMonitoringEffect\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"secureTransferToStorageAccountMonitoringEffect\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-d0793b48-0edc-4296-a390-4c75d1bdfd71\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-7d092e0a-7acd-40d2-a975-dca21cae48c4\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-2a1a9cdf-e04d-429a-8416-3bfb72a1b26f\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"disableUnrestrictedNetworkToStorageAccountMonitoringEffect\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-55615ac9-af46-4a59-874e-391cc3dfb490\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-1b8ca024-1d5c-4dec-8995-b1a932b41780\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-037eea7a-bd0a-46c5-9a66-03aea78705d3\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-53503636-bcc9-4748-9663-5348217f160f\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-40cec1dd-a100-4920-b15b-3024fe8901ab\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-0725b4dd-7e76-479c-a735-68e7ee23d5ca\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-a049bf77-880b-470f-ba6d-9f21c530cf83\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-ee980b6d-0eca-4501-8d54-f6290fd512c3\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-1d84d5fb-01f6-4d12-ba4f-4a26081d403d\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-37e0d2fe-28a5-43d6-a273-67d37d1f5606\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"identityDesignateMoreThanOneOwnerMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"diskEncryptionMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"emailNotificationToSubscriptionOwnerHighSeverityAlertsEnabledEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"functionAppDisableRemoteDebuggingMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"sqlDbEncryptionMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"vulnerabilityAssessmentOnManagedInstanceMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"ensurePHPVersionLatestForAPIAppEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"aadAuthenticationInSqlServerMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"vmssEndpointProtectionMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"vmssOsVulnerabilitiesMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"adaptiveApplicationControlsMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"geoRedundantBackupShouldBeEnabledForAzureDatabaseForPostgreSQLEffect\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"ensureJavaVersionLatestForWebAppEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"identityDesignateLessThanOwnersMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"securityContactEmailAddressForSubscriptionEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"webAppRestrictCORSAccessMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"identityRemoveExternalAccountWithWritePermissionsMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"identityRemoveExternalAccountWithReadPermissionsMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"identityRemoveDeprecatedAccountMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"functionAppEnforceHttpsMonitoringEffect\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"ensurePythonVersionLatestForWebAppEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"ensurePythonVersionLatestForFunctionAppEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"ensurePHPVersionLatestForWebAppEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"ensurePythonVersionLatestForAPIAppEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"geoRedundantBackupShouldBeEnabledForAzureDatabaseForMySQLEffect\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"systemUpdatesMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"ensureJavaVersionLatestForAPIAppEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"ensureHTTPVersionLatestForWebAppEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"apiAppRequireLatestTlsMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"identityEnableMFAForWritePermissionsMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"ensureHTTPVersionLatestForAPIAppEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"ensureJavaVersionLatestForFunctionAppEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"nextGenerationFirewallMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"useRbacRulesMonitoringEffect\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"webAppEnforceHttpsMonitoringEffect\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"sqlServerAuditingMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"vnetEnableDDoSProtectionMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"identityEnableMFAForOwnerPermissionsMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"sqlServerAdvancedDataSecurityMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"sqlManagedInstanceAdvancedDataSecurityMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"endpointProtectionMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"jitNetworkAccessMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"apiAppEnforceHttpsMonitoringEffect\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"geoRedundantStorageShouldBeEnabledForStorageAccountsEffect\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"vmssSystemUpdatesMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"webAppDisableRemoteDebuggingMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"longtermGeoRedundantBackupEnabledAzureSQLDatabasesEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"systemConfigurationsMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"ensureHTTPVersionLatestForFunctionAppEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"identityEnableMFAForReadPermissionsMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"containerBenchmarkMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"apiAppDisableRemoteDebuggingMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"identityRemoveDeprecatedAccountWithOwnerPermissionsMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"vulnerabilityAssessmentOnServerMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"webAppRequireLatestTlsMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"identityRemoveExternalAccountWithOwnerPermissionsMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"functionAppRequireLatestTlsMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"kubernetesServiceVersionUpToDateMonitoringEffect\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"sqlDbVulnerabilityAssesmentMonitoringEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"membersToIncludeInLocalAdministratorsGroup\" : { \r\n \"value\" : \"\"\r\n },\r\n \"membersToExcludeInLocalAdministratorsGroup\" : { \r\n \"value\" : \"\"\r\n },\r\n \"logAnalyticsWorkspaceIDForVMAgents\" : { \r\n \"value\" : \"\"\r\n },\r\n \"PHPLatestVersionForAppServices\" : { \r\n \"value\" : \"7.4\"\r\n },\r\n \"JavaLatestVersionForAppServices\" : { \r\n \"value\" : \"11\"\r\n },\r\n \"WindowsPythonLatestVersionForAppServices\" : { \r\n \"value\" : \"3.6\"\r\n },\r\n \"LinuxPythonLatestVersionForAppServices\" : { \r\n \"value\" : \"3.9\"\r\n },\r\n \"ensureDotNetFrameworkLatestForFunctionAppEffect\" : { \r\n \"value\" : \"Disabled\"\r\n },\r\n \"sqlManagedInstanceAdvancedDataSecurityEmailsMonitoringEffect\" : { \r\n \"value\" : \"Disabled\"\r\n },\r\n \"vulnerabilityAssessmentMonitoringEffect\" : { \r\n \"value\" : \"Disabled\"\r\n },\r\n \"ensureDotNetFrameworkLatestForWebAppEffect\" : { \r\n \"value\" : \"Disabled\"\r\n },\r\n \"sqlServerAdvancedDataSecurityEmailsMonitoringEffect\" : { \r\n \"value\" : \"Disabled\"\r\n },\r\n \"microsoftIaaSAntimalwareExtensionShouldBeDeployedOnWindowsServersEffect\" : { \r\n \"value\" : \"Disabled\"\r\n },\r\n \"securityCenterStandardPricingTierShouldBeSelectedEffect\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"theLogAnalyticsAgentShouldBeInstalledOnVirtualMachinesEffect\" : { \r\n \"value\" : \"Disabled\"\r\n },\r\n \"ensurePHPVersionLatestForFunctionAppEffect\" : { \r\n \"value\" : \"Disabled\"\r\n },\r\n \"sqlManagedInstanceAdvancedDataSecurityEmailAdminsMonitoringEffect\" : { \r\n \"value\" : \"Disabled\"\r\n },\r\n \"securityContactPhoneNumberShouldBeProvidedForSubscriptionEffect\" : { \r\n \"value\" : \"Disabled\"\r\n },\r\n \"threatDetectionTypesOnManagedInstanceMonitoringEffect\" : { \r\n \"value\" : \"Disabled\"\r\n },\r\n \"ensureDotNetFrameworkLatestForAPIAppEffect\" : { \r\n \"value\" : \"Disabled\"\r\n },\r\n \"sqlServerAdvancedDataSecurityEmailAdminsMonitoringEffect\" : { \r\n \"value\" : \"Disabled\"\r\n },\r\n \"threatDetectionTypesOnServerMonitoringEffect\" : { \r\n \"value\" : \"Disabled\"\r\n },\r\n \"theLogAnalyticsAgentShouldBeInstalledOnVirtualMachineScaleSetsEffect\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n }\r\n}", + "$fxv#3": "{\r\n \"logAnalyticsWorkspaceId-f47b5582-33ec-4c5c-87c0-b010a6b2e917\" : { \r\n \"value\" : \"\"\r\n },\r\n \"effect-09024ccc-0c5f-475e-9457-b7c0d9ed487b\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"MembersToExclude-69bf4abd-ca1e-4cf6-8b5a-762d42e61d4f\" :{\r\n \"value\": \"\"\r\n },\r\n \"MembersToInclude-30f71ea1-ac77-4f26-9fc5-2d926bbd4ba7\": {\r\n \"value\": \"\"\r\n },\r\n \"effect-0961003e-5a0a-4549-abde-af6a37f2724d\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-0b15565f-aa9e-48ba-8619-45960f2c314d\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-0e60b895-3786-45da-8377-9c6b4b6ac5f9\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-17k78e20-9358-41c9-923c-fb736d382a12\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-1bc1795e-d44a-4d48-9b3b-6fff0fd5f9ba\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"PHPLatestVersion\" : { \r\n \"value\" : \"7.3\"\r\n },\r\n \"effect-22bee202-a82f-4305-9a2a-6d7f44d4dedb\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-26a828e1-e88f-464e-bbb3-c134a282b9de\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-34c877ad-507e-4c82-993e-3452a6e0ad3c\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-3c735d8a-a4ba-4a3a-b7cf-db7754cf57f4\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-404c3081-a854-4457-ae30-26a93ef643f9\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-47a6b606-51aa-4496-8bb7-64b11cf66adc\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-496223c3-ad65-4ecd-878a-bae78737e9ed\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"JavaLatestVersion\" : { \r\n \"value\" : \"11\"\r\n },\r\n \"effect-4f11b553-d42e-4e3a-89be-32ca364cad4c\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-4f4f78b8-e367-4b10-a341-d9a4ad5cf1c7\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-5c607a2e-c700-4744-8254-d77e7c9eb5e4\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-5f76cf89-fbf2-47fd-a3f4-b891fa780b60\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-6b1cbf55-e8b6-442f-ba4c-7246b6381474\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-6d555dd1-86f2-4f1c-8ed7-5abae7c6cbab\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-7008174a-fd10-4ef0-817e-fc820a951d73\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"LinuxPythonLatestVersion\" : { \r\n \"value\" : \"3.8\"\r\n },\r\n \"effect-7238174a-fd10-4ef0-817e-fc820a951d73\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-7261b898-8a84-4db8-9e04-18527132abb3\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-74c3584d-afae-46f7-a20a-6f8adba71a16\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-86b3d65f-7626-441e-b690-81a8b71cff60\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-88999f4c-376a-45c8-bcb3-4058f713cf39\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-8c122334-9d20-4eb8-89ea-ac9a705b74ae\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-8cb6aa8b-9e41-4f4e-aa25-089a7ac2581e\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-9297c21d-2ed6-4474-b48f-163f75654ce3\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-991310cd-e9f3-47bc-b7b6-f57b557d07db\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-9b597639-28e4-48eb-b506-56b05d366257\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-9d0b6ea4-93e2-4578-bf2f-6bb17d22b4bc\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-9daedab3-fb2d-461e-b861-71790eead4f6\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-a4af4a39-4135-47fb-b175-47fbdf85311d\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-a6fb4358-5bf4-4ad7-ba82-2cd2f41ce5e9\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"setting-a6fb4358-5bf4-4ad7-ba82-2cd2f41ce5e9\" : { \r\n \"value\" : \"enabled\"\r\n },\r\n \"effect-a70ca396-0a34-413a-88e1-b956c1e683be\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-aa633080-8b72-40c4-a2d7-d00c03e80bed\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-abfb4388-5bf4-4ad7-ba82-2cd2f41ceae9\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-abfb7388-5bf4-4ad7-ba99-2cd2f41cebb9\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-af6cd1bd-1635-48cb-bde7-5b15693900b9\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"resourceGroupName-b6e2945c-0b7b-40f5-9233-7a5323b5cdc6\" : { \r\n \"value\" : \"NetworkWatcherRG\"\r\n },\r\n \"effect-b7ddfbdc-1260-477d-91fd-98bd9be789a6\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-c3f317a7-a95c-4547-b7e7-11017ebdf2fe\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-cb510bfd-1cba-4d9f-a230-cb0976f4bb71\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-e1e5fd5d-3e4c-4ce1-8661-7d1873ae6b15\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-e2c1c086-2d84-4019-bff3-c44ccd95113c\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-e3576e28-8b17-4677-84c3-db2990658d64\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-e8cbc669-f12d-49eb-93e7-9273119e9933\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-e9c8d085-d9cc-4b17-9cdc-059f1f01f19e\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-ebb62a0c-3560-49e1-89ed-27e074e9f8ad\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-efbde977-ba53-4479-b8e9-10b957924fbf\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-f0e6e85b-9b9f-4a4b-b67b-f730d42f1b0b\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-f6de0be7-9a8a-4b8a-b349-43cf02d22f7c\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-f8456c1c-aa66-4dfb-861a-25d127b775c9\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-f9d614c5-c173-4d56-95a7-b4437057d193\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-fb893a29-21bb-418c-a157-e99480ec364c\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-feedbf84-6b99-488c-acc2-71c829aa5ffc\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-3b980d31-7904-4bb7-8575-5665739a8052\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-6e2593d9-add6-4083-9c9b-4b7d2188c899\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-b607c5de-e7d9-4eee-9e5c-83f1bcee4fa0\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-12430be1-6cc8-4527-a9a8-e3d38f250096\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"modeRequirement-12430be1-6cc8-4527-a9a8-e3d38f250096\" : { \r\n \"value\" : \"Detection\"\r\n },\r\n \"effect-425bea59-a659-4cbb-8d31-34499bd030b8\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"modeRequirement-425bea59-a659-4cbb-8d31-34499bd030b8\" : { \r\n \"value\" : \"Detection\"\r\n },\r\n \"effect-564feb30-bf6a-4854-b4bb-0d2d2d1e6c66\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-055aa869-bc98-4af8-bafc-23f1ab6ffe2c\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-013e242c-8828-4970-87b3-ab247555486d\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-d38fc420-0735-4ef3-ac11-c806f651a570\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-a1181c5f-672a-477a-979a-7d58aa086233\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-308fbb08-4ab8-4e67-9b29-592e93fb94fa\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-4da35fc9-c9e7-4960-aec9-797fe7d9051d\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-523b5cd1-3e23-492f-a539-13118b6d1e3a\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-7fe3b40f-802b-4cdd-8bd4-fd799c948cc2\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-c25d9a16-bc35-4e15-a7e5-9db606bf9ed4\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-b0f33259-77d7-4c9e-aac6-3aabcfae693c\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-037eea7a-bd0a-46c5-9a66-03aea78705d3\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-0725b4dd-7e76-479c-a735-68e7ee23d5ca\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-0820b7b9-23aa-4725-a1ce-ae4558f718e5\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-2c89a2e5-7285-40fe-afe0-ae8654b92fab\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-358c20a6-3f9e-4f0e-97ff-c6ce485e2aac\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-5744710e-cc2f-4ee8-8809-3b11e89f4bc9\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-ac4a19c2-fa67-49b4-8ae5-0b2e78c49457\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-c9d007d0-c057-4772-b18c-01e546713bcd\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-d0793b48-0edc-4296-a390-4c75d1bdfd71\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-e372f825-a257-4fb8-9175-797a8a8627d6\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-d158790f-bfb0-486c-8631-2dc6b4e8e6af\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-e802a67a-daf5-4436-9ea6-f6d821dd0c5d\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-a451c1ef-c6ca-483d-87ed-f49761e3ffb5\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-b954148f-4c11-4c38-8221-be76711e194a-MicrosoftSql-servers-firewallRules-delete\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-b954148f-4c11-4c38-8221-be76711e194a-MicrosoftNetwork-networkSecurityGroups-delete\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-b954148f-4c11-4c38-8221-be76711e194a-MicrosoftClassicNetwork-networkSecurityGroups-delete\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-b954148f-4c11-4c38-8221-be76711e194a-MicrosoftNetwork-networkSecurityGroups-securityRules-delete\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-b954148f-4c11-4c38-8221-be76711e194a-MicrosoftClassicNetwork-networkSecurityGroups-securityRules-delete\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-ae89ebca-1c92-4898-ac2c-9f63decb045c\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-d26f7642-7545-4e18-9b75-8c9bbdee3a9a\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-1a4e592a-6a6e-44a5-9814-e36264ca96e7\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-7796937f-307b-4598-941c-67d3a05ebfe7\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-c5447c04-a4d7-4ba8-a263-c9ee321a6858\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-41388f1c-2db0-4c25-95b2-35d7f5ccbfa9\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-b02aacc0-b073-424e-8298-42b22829ee0a\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-057d6cfe-9c4f-4a6d-bc60-14420ea1f1a9\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-0ec47710-77ff-4a3d-9181-6aa50af424d0\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-48af4db5-9b8b-401c-8e74-076be876a430\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-82339799-d096-41ae-8538-b108becf0970\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-1b7aa243-30e4-4c9e-bca8-d0d3022b634a\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-ef2a8f2a-b3d9-49cd-a8a8-9a3aaaf647d9\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-bb91dfba-c30d-4263-9add-9c2384e659a6\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-e71308d3-144b-4262-b144-efdc3cc90517\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-2bdd0062-9d75-436e-89df-487dd8e4b3c7\" : { \r\n \"value\" : \"Disabled\"\r\n },\r\n \"effect-4733ea7b-a883-42fe-8cac-97454c2a9e4a\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-67121cc7-ff39-4ab8-b7e3-95b84dab487d\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-6fac406b-40ca-413b-bf8e-0bf964659c25\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-81e74cea-30fd-40d5-802f-d72103c2aaaa\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-c349d81b-9985-44ae-a8da-ff98d108ede8\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-f4b53539-8df9-40e4-86c6-6b607703bd4e\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-ec068d99-e9c7-401f-8cef-5bdde4e6ccf1\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-048248b0-55cd-46da-b1ff-39efd52db260\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-0d134df8-db83-46fb-ad72-fe0c9428c8dd\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-2c89a2e5-7285-40fe-afe0-ae8654b92fb2\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-3657f5a0-770e-44a3-b44e-9431ba1e9735\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-5b9159ae-1701-4a6f-9a7a-aa9c8ddd0580\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-617c02be-7f02-4efd-8836-3180d47b6c68\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-7d7be79c-23ba-4033-84dd-45e2a5ccdd67\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-87ba29ef-1ab3-4d82-b763-87fcd4f531f7\" : { \r\n \"value\" : \"audit\"\r\n },\r\n \"effect-f7d52b2d-e161-4dfa-a82b-55e564167385\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-c43e4a30-77cb-48ab-a4dd-93f175c63b57\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-0b60c0b2-2dc2-4e1c-b5c9-abbed971de53\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-1e66c121-a66a-4b1f-9b83-0fd99bf0fc2d\" : { \r\n \"value\" : \"Audit\"\r\n },\r\n \"effect-1f314764-cb73-4fc9-b863-8eca98ac36e9\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n },\r\n \"effect-123a3936-f020-408a-ba0c-47873faf1534\" : { \r\n \"value\" : \"AuditIfNotExists\"\r\n }\r\n}\r\n", + "modifiedAssignment": "[if(and(equals(toLower(environment().name), toLower('AzureCloud')), equals(toLower(parameters('builtInAssignment')), toLower('IL5'))), 'NISTRev4', parameters('builtInAssignment'))]", + "assignmentName": "[format('{0} {1}', variables('modifiedAssignment'), resourceGroup().name)]", + "agentVmssAssignmentName": "[format('Deploy VMSS Agents {0}', resourceGroup().name)]", + "agentVmAssignmentName": "[format('Deploy VM Agents {0}', resourceGroup().name)]", + "contributorRoleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', 'b24988ac-6180-42a0-ab88-20f7382dd24c')]", + "lawsReaderRoleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', '92aaf0da-9dab-42b6-94a3-d43ce8d16293')]" + }, "resources": [ { - "condition": "[not(empty(parameters('applicationInsightsResourceId')))]", - "type": "Microsoft.Insights/privateLinkScopes/scopedResources", - "apiVersion": "2021-09-01", - "name": "[format('{0}/{1}', split(parameters('privateLinkScopeResourceId'), '/')[8], if(empty(parameters('applicationInsightsResourceId')), 'applicationInsights', split(parameters('applicationInsightsResourceId'), '/')[8]))]", + "type": "Microsoft.Authorization/policyAssignments", + "apiVersion": "2020-09-01", + "name": "[variables('assignmentName')]", + "location": "[parameters('location')]", "properties": { - "linkedResourceId": "[parameters('applicationInsightsResourceId')]" + "policyDefinitionId": "[createObject('NISTRev4', createObject('id', '/providers/Microsoft.Authorization/policySetDefinitions/cf25b9c1-bd23-4eb6-bd2c-f4f3ac644a5f', 'parameters', json(replace(variables('$fxv#0'), '', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[2], split(parameters('logAnalyticsWorkspaceResourceId'), '/')[4]), 'Microsoft.OperationalInsights/workspaces', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[8])))), 'NISTRev5', createObject('id', '/providers/Microsoft.Authorization/policySetDefinitions/179d1daa-458f-4e47-8086-2a68d0d6c38f', 'parameters', json(variables('$fxv#1'))), 'IL5', createObject('id', '/providers/Microsoft.Authorization/policySetDefinitions/f9a961fa-3241-4b20-adc4-bbf8ad9d7197', 'parameters', json(replace(variables('$fxv#2'), '', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[2], split(parameters('logAnalyticsWorkspaceResourceId'), '/')[4]), 'Microsoft.OperationalInsights/workspaces', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[8])))), 'CMMC', createObject('id', '/providers/Microsoft.Authorization/policySetDefinitions/b5629c75-5c77-4422-87b9-2509e680f8de', 'parameters', json(replace(variables('$fxv#3'), '', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[2], split(parameters('logAnalyticsWorkspaceResourceId'), '/')[4]), 'Microsoft.OperationalInsights/workspaces', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[8]), '2021-06-01').customerId))))[variables('modifiedAssignment')].id]", + "parameters": "[createObject('NISTRev4', createObject('id', '/providers/Microsoft.Authorization/policySetDefinitions/cf25b9c1-bd23-4eb6-bd2c-f4f3ac644a5f', 'parameters', json(replace(variables('$fxv#0'), '', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[2], split(parameters('logAnalyticsWorkspaceResourceId'), '/')[4]), 'Microsoft.OperationalInsights/workspaces', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[8])))), 'NISTRev5', createObject('id', '/providers/Microsoft.Authorization/policySetDefinitions/179d1daa-458f-4e47-8086-2a68d0d6c38f', 'parameters', json(variables('$fxv#1'))), 'IL5', createObject('id', '/providers/Microsoft.Authorization/policySetDefinitions/f9a961fa-3241-4b20-adc4-bbf8ad9d7197', 'parameters', json(replace(variables('$fxv#2'), '', extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[2], split(parameters('logAnalyticsWorkspaceResourceId'), '/')[4]), 'Microsoft.OperationalInsights/workspaces', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[8])))), 'CMMC', createObject('id', '/providers/Microsoft.Authorization/policySetDefinitions/b5629c75-5c77-4422-87b9-2509e680f8de', 'parameters', json(replace(variables('$fxv#3'), '', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[2], split(parameters('logAnalyticsWorkspaceResourceId'), '/')[4]), 'Microsoft.OperationalInsights/workspaces', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[8]), '2021-06-01').customerId))))[variables('modifiedAssignment')].parameters]" + }, + "identity": { + "type": "SystemAssigned" + } + }, + { + "type": "Microsoft.Authorization/policyAssignments", + "apiVersion": "2020-09-01", + "name": "[variables('agentVmssAssignmentName')]", + "location": "[parameters('location')]", + "properties": { + "policyDefinitionId": "[tenantResourceId('Microsoft.Authorization/policySetDefinitions', '75714362-cae7-409e-9b99-a8e5075b7fad')]", + "parameters": { + "logAnalytics_1": { + "value": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[2], split(parameters('logAnalyticsWorkspaceResourceId'), '/')[4]), 'Microsoft.OperationalInsights/workspaces', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[8])]" + } + } + }, + "identity": { + "type": "SystemAssigned" + } + }, + { + "type": "Microsoft.Authorization/policyAssignments", + "apiVersion": "2020-09-01", + "name": "[variables('agentVmAssignmentName')]", + "location": "[parameters('location')]", + "properties": { + "policyDefinitionId": "[tenantResourceId('Microsoft.Authorization/policySetDefinitions', '55f3eceb-5573-4f18-9695-226972c6d74a')]", + "parameters": { + "logAnalytics_1": { + "value": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[2], split(parameters('logAnalyticsWorkspaceResourceId'), '/')[4]), 'Microsoft.OperationalInsights/workspaces', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[8])]" + } + } + }, + "identity": { + "type": "SystemAssigned" } }, { - "condition": "[not(empty(parameters('dataCollectionEndpointResourceId')))]", - "type": "Microsoft.Insights/privateLinkScopes/scopedResources", - "apiVersion": "2021-09-01", - "name": "[format('{0}/{1}', split(parameters('privateLinkScopeResourceId'), '/')[8], if(empty(parameters('dataCollectionEndpointResourceId')), 'dataCollectionEndpoint', split(parameters('dataCollectionEndpointResourceId'), '/')[8]))]", + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2020-04-01-preview", + "name": "[guid(variables('contributorRoleDefinitionId'), variables('assignmentName'))]", + "properties": { + "roleDefinitionId": "[variables('contributorRoleDefinitionId')]", + "principalId": "[if(empty(variables('modifiedAssignment')), '', reference(resourceId('Microsoft.Authorization/policyAssignments', variables('assignmentName')), '2020-09-01', 'full').identity.principalId)]", + "principalType": "ServicePrincipal" + }, + "dependsOn": [ + "[resourceId('Microsoft.Authorization/policyAssignments', variables('assignmentName'))]" + ] + }, + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2020-04-01-preview", + "name": "[guid(variables('contributorRoleDefinitionId'), variables('agentVmssAssignmentName'))]", + "properties": { + "roleDefinitionId": "[variables('contributorRoleDefinitionId')]", + "principalId": "[reference(resourceId('Microsoft.Authorization/policyAssignments', variables('agentVmssAssignmentName')), '2020-09-01', 'full').identity.principalId]", + "principalType": "ServicePrincipal" + }, + "dependsOn": [ + "[resourceId('Microsoft.Authorization/policyAssignments', variables('agentVmssAssignmentName'))]" + ] + }, + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2020-04-01-preview", + "name": "[guid(variables('contributorRoleDefinitionId'), variables('agentVmAssignmentName'))]", + "properties": { + "roleDefinitionId": "[variables('contributorRoleDefinitionId')]", + "principalId": "[reference(resourceId('Microsoft.Authorization/policyAssignments', variables('agentVmAssignmentName')), '2020-09-01', 'full').identity.principalId]", + "principalType": "ServicePrincipal" + }, + "dependsOn": [ + "[resourceId('Microsoft.Authorization/policyAssignments', variables('agentVmAssignmentName'))]" + ] + }, + { + "condition": "[parameters('deployRemediation')]", + "type": "Microsoft.PolicyInsights/remediations", + "apiVersion": "2019-07-01", + "name": "VM-Agent-Policy-Remediation", "properties": { - "linkedResourceId": "[parameters('dataCollectionEndpointResourceId')]" - } + "policyAssignmentId": "[resourceId('Microsoft.Authorization/policyAssignments', variables('agentVmAssignmentName'))]", + "resourceDiscoveryMode": "ReEvaluateCompliance" + }, + "dependsOn": [ + "[resourceId('Microsoft.Authorization/policyAssignments', variables('agentVmAssignmentName'))]" + ] }, { - "condition": "[not(empty(parameters('logAnalyticsWorkspaceResourceId')))]", - "type": "Microsoft.Insights/privateLinkScopes/scopedResources", - "apiVersion": "2021-09-01", - "name": "[format('{0}/{1}', split(parameters('privateLinkScopeResourceId'), '/')[8], if(empty(parameters('logAnalyticsWorkspaceResourceId')), 'logAnalyticsWorkspace', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[8]))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('Assign-Laws-Role-Policy-{0}', resourceGroup().name)]", + "subscriptionId": "[split(parameters('logAnalyticsWorkspaceResourceId'), '/')[2]]", + "resourceGroup": "[split(parameters('logAnalyticsWorkspaceResourceId'), '/')[4]]", "properties": { - "linkedResourceId": "[parameters('logAnalyticsWorkspaceResourceId')]" - } + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "targetResourceId": { + "value": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[2], split(parameters('logAnalyticsWorkspaceResourceId'), '/')[4]), 'Microsoft.OperationalInsights/workspaces', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[8])]" + }, + "roleDefinitionId": { + "value": "[variables('lawsReaderRoleDefinitionId')]" + }, + "principalId": { + "value": "[reference(resourceId('Microsoft.Authorization/policyAssignments', variables('agentVmAssignmentName')), '2020-09-01', 'full').identity.principalId]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "1966035938992047983" + } + }, + "parameters": { + "targetResourceId": { + "type": "string" + }, + "roleDefinitionId": { + "type": "string" + }, + "principalId": { + "type": "string" + }, + "principalType": { + "type": "string", + "defaultValue": "ServicePrincipal", + "allowedValues": [ + "ForeignGroup", + "Group", + "ServicePrincipal", + "User" + ] + }, + "description": { + "type": "string", + "defaultValue": "" + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2020-04-01-preview", + "name": "[guid(parameters('targetResourceId'), parameters('roleDefinitionId'), parameters('principalId'))]", + "properties": { + "principalId": "[parameters('principalId')]", + "principalType": "[parameters('principalType')]", + "roleDefinitionId": "[parameters('roleDefinitionId')]", + "description": "[parameters('description')]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Authorization/policyAssignments', variables('agentVmAssignmentName'))]" + ] } ] } - }, - "dependsOn": [ - "[resourceId('Microsoft.OperationalInsights/workspaces', replace(parameters('namingConvention').logAnalyticsWorkspace, parameters('serviceToken'), parameters('service')))]" - ] + } + } + ] + } + }, + "dependsOn": [ + "[subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]", + "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix')))]" + ] + }, + { + "condition": "[and(parameters('deployDefender'), not(empty(parameters('virtualNetworkAddressPrefix'))))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('set-defender-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))]", + "location": "[deployment().location]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "emailSecurityContact": { + "value": "[parameters('emailSecurityContact')]" + }, + "logAnalyticsWorkspaceId": { + "value": "[parameters('logAnalyticsWorkspaceResourceId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "17047820191891552534" + } + }, + "parameters": { + "defenderPlans": { + "type": "array", + "defaultValue": [ + "VirtualMachines" + ], + "metadata": { + "description": "Defender Paid protection Plans. Even if a customer selects the free sku, at least 1 paid protection plan must be specified." + } }, - { - "condition": "[parameters('enableAvdInsights')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('deploy-private-link-scope-dce-{0}', parameters('deploymentNameSuffix'))]", - "subscriptionId": "[split(parameters('privateLinkScopeResourceId'), '/')[2]]", - "resourceGroup": "[split(parameters('privateLinkScopeResourceId'), '/')[4]]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" + "enableAutoProvisioning": { + "type": "bool", + "defaultValue": true, + "metadata": { + "description": "Turn automatic deployment by Defender of the MMA (OMS VM extension) on or off" + } + }, + "logAnalyticsWorkspaceId": { + "type": "string", + "metadata": { + "description": "Specify the ID of your custom Log Analytics workspace to collect Defender data." + } + }, + "emailSecurityContact": { + "type": "string", + "metadata": { + "description": "Email address of the contact, in the form of john@doe.com" + } + }, + "policySetDescription": { + "type": "string", + "defaultValue": "The Microsoft Cloud Security Benchmark initiative represents the policies and controls implementing security recommendations defined in Microsoft Cloud Security Benchmark v2, see https://aka.ms/azsecbm. This also serves as the Microsoft Defender for Cloud default policy initiative. You can directly assign this initiative, or manage its policies and compliance results within Microsoft Defender.", + "metadata": { + "description": "Policy Initiative description field" + } + }, + "defenderSkuTier": { + "type": "string", + "defaultValue": "Free", + "metadata": { + "description": "[Standard/Free] The SKU for Defender. It defaults to \"Free\"." + } + } + }, + "variables": { + "autoProvisioning": "[if(parameters('enableAutoProvisioning'), 'On', 'Off')]", + "defenderPaidPlanConfig": { + "AzureCloud": { + "Api": { + "subPlan": "P1" }, - "mode": "Incremental", - "parameters": { - "dataCollectionEndpointResourceId": { - "value": "[resourceId('Microsoft.Insights/dataCollectionEndpoints', replace(parameters('namingConvention').dataCollectionEndpoint, parameters('serviceToken'), parameters('service')))]" - }, - "privateLinkScopeResourceId": { - "value": "[parameters('privateLinkScopeResourceId')]" - } + "appServices": {}, + "KeyVaults": { + "subPlan": "PerKeyVault" }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "8149225552603313421" - } - }, - "parameters": { - "applicationInsightsResourceId": { - "type": "string", - "defaultValue": "" + "Arm": { + "subPlan": "PerSubscription" + }, + "CloudPosture": { + "extensions": [ + { + "name": "SensitiveDataDiscovery", + "isEnabled": "True" }, - "dataCollectionEndpointResourceId": { - "type": "string", - "defaultValue": "" + { + "name": "ContainerRegistriesVulnerabilityAssessments", + "isEnabled": "True" }, - "logAnalyticsWorkspaceResourceId": { - "type": "string", - "defaultValue": "" + { + "name": "AgentlessDiscoveryForKubernetes", + "isEnabled": "True" }, - "privateLinkScopeResourceId": { - "type": "string" - } - }, - "resources": [ { - "condition": "[not(empty(parameters('applicationInsightsResourceId')))]", - "type": "Microsoft.Insights/privateLinkScopes/scopedResources", - "apiVersion": "2021-09-01", - "name": "[format('{0}/{1}', split(parameters('privateLinkScopeResourceId'), '/')[8], if(empty(parameters('applicationInsightsResourceId')), 'applicationInsights', split(parameters('applicationInsightsResourceId'), '/')[8]))]", - "properties": { - "linkedResourceId": "[parameters('applicationInsightsResourceId')]" - } + "name": "AgentlessVmScanning", + "isEnabled": "True" + }, + { + "name": "EntraPermissionsManagement", + "isEnabled": "True" + } + ] + }, + "Containers": { + "extensions": [ + { + "name": "ContainerRegistriesVulnerabilityAssessments", + "isEnabled": "True" }, { - "condition": "[not(empty(parameters('dataCollectionEndpointResourceId')))]", - "type": "Microsoft.Insights/privateLinkScopes/scopedResources", - "apiVersion": "2021-09-01", - "name": "[format('{0}/{1}', split(parameters('privateLinkScopeResourceId'), '/')[8], if(empty(parameters('dataCollectionEndpointResourceId')), 'dataCollectionEndpoint', split(parameters('dataCollectionEndpointResourceId'), '/')[8]))]", - "properties": { - "linkedResourceId": "[parameters('dataCollectionEndpointResourceId')]" + "name": "AgentlessDiscoveryForKubernetes", + "isEnabled": "True" + } + ] + }, + "CosmosDbs": {}, + "StorageAccounts": { + "subPlan": "DefenderForStorageV2", + "extensions": [ + { + "name": "OnUploadMalwareScanning", + "isEnabled": "True", + "additionalExtensionProperties": { + "CapGBPerMonthPerStorageAccount": "5000" } }, { - "condition": "[not(empty(parameters('logAnalyticsWorkspaceResourceId')))]", - "type": "Microsoft.Insights/privateLinkScopes/scopedResources", - "apiVersion": "2021-09-01", - "name": "[format('{0}/{1}', split(parameters('privateLinkScopeResourceId'), '/')[8], if(empty(parameters('logAnalyticsWorkspaceResourceId')), 'logAnalyticsWorkspace', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[8]))]", - "properties": { - "linkedResourceId": "[parameters('logAnalyticsWorkspaceResourceId')]" - } + "name": "SensitiveDataDiscovery", + "isEnabled": "True" } ] - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Insights/dataCollectionEndpoints', replace(parameters('namingConvention').dataCollectionEndpoint, parameters('serviceToken'), parameters('service')))]" - ] + }, + "VirtualMachines": { + "subPlan": "P1" + }, + "SqlServerVirtualMachines": {}, + "SqlServers": {}, + "OpenSourceRelationalDatabases": {} + } } - ], - "outputs": { - "logAnalyticsWorkspaceName": { - "type": "string", - "value": "[replace(parameters('namingConvention').logAnalyticsWorkspace, parameters('serviceToken'), parameters('service'))]" + }, + "resources": [ + { + "copy": { + "name": "defenderFreeAllClouds", + "count": "[length(parameters('defenderPlans'))]", + "mode": "serial", + "batchSize": 1 + }, + "condition": "[equals(parameters('defenderSkuTier'), 'Free')]", + "type": "Microsoft.Security/pricings", + "apiVersion": "2023-01-01", + "name": "[parameters('defenderPlans')[copyIndex()]]", + "properties": { + "pricingTier": "[parameters('defenderSkuTier')]" + } }, - "logAnalyticsWorkspaceResourceId": { - "type": "string", - "value": "[resourceId('Microsoft.OperationalInsights/workspaces', replace(parameters('namingConvention').logAnalyticsWorkspace, parameters('serviceToken'), parameters('service')))]" + { + "copy": { + "name": "defenderStandardNoSubplanNoExtensions", + "count": "[length(parameters('defenderPlans'))]", + "mode": "serial", + "batchSize": 1 + }, + "condition": "[and(equals(parameters('defenderSkuTier'), 'Standard'), not(equals(environment().name, 'AzureCloud')))]", + "type": "Microsoft.Security/pricings", + "apiVersion": "2023-01-01", + "name": "[parameters('defenderPlans')[copyIndex()]]", + "properties": { + "pricingTier": "[parameters('defenderSkuTier')]" + } }, - "dataCollectionRuleResourceId": { - "type": "string", - "value": "[if(parameters('enableAvdInsights'), resourceId('Microsoft.Insights/dataCollectionRules', format('microsoft-avdi-{0}', replace(parameters('namingConvention').dataCollectionRule, parameters('serviceToken'), parameters('service')))), '')]" + { + "copy": { + "name": "defenderStandardSubplanExtensionsAzureCloud", + "count": "[length(parameters('defenderPlans'))]", + "mode": "serial", + "batchSize": 1 + }, + "condition": "[and(equals(parameters('defenderSkuTier'), 'Standard'), equals(environment().name, 'AzureCloud'))]", + "type": "Microsoft.Security/pricings", + "apiVersion": "2023-01-01", + "name": "[parameters('defenderPlans')[copyIndex()]]", + "properties": { + "pricingTier": "[parameters('defenderSkuTier')]", + "subPlan": "[if(contains(variables('defenderPaidPlanConfig')[environment().name][parameters('defenderPlans')[copyIndex()]], 'subPlan'), variables('defenderPaidPlanConfig')[environment().name][parameters('defenderPlans')[copyIndex()]].subPlan, json('null'))]", + "extensions": "[if(contains(variables('defenderPaidPlanConfig')[environment().name][parameters('defenderPlans')[copyIndex()]], 'extensions'), variables('defenderPaidPlanConfig')[environment().name][parameters('defenderPlans')[copyIndex()]].extensions, json('null'))]" + } + }, + { + "type": "Microsoft.Security/autoProvisioningSettings", + "apiVersion": "2019-01-01", + "name": "default", + "properties": { + "autoProvision": "[variables('autoProvisioning')]" + } + }, + { + "type": "Microsoft.Security/workspaceSettings", + "apiVersion": "2019-01-01", + "name": "default", + "properties": { + "workspaceId": "[parameters('logAnalyticsWorkspaceId')]", + "scope": "[subscription().id]" + } + }, + { + "condition": "[not(empty(parameters('emailSecurityContact')))]", + "type": "Microsoft.Security/securityContacts", + "apiVersion": "2020-01-01-preview", + "name": "default", + "properties": { + "notificationsByRole": { + "roles": [ + "AccountAdmin", + "Contributor", + "Owner", + "ServiceAdmin" + ], + "state": "On" + }, + "alertNotifications": { + "state": "On" + }, + "emails": "[parameters('emailSecurityContact')]" + } + }, + { + "type": "Microsoft.Authorization/policyAssignments", + "apiVersion": "2022-06-01", + "name": "Microsoft Cloud Security Benchmark", + "properties": { + "displayName": "Defender Default", + "description": "[parameters('policySetDescription')]", + "enforcementMode": "DoNotEnforce", + "parameters": {}, + "policyDefinitionId": "[tenantResourceId('Microsoft.Authorization/policySetDefinitions', '1f3afdf9-d0c9-4c3d-847f-89da613e70a8')]" + } } - } + ] } } + } + ], + "outputs": { + "diskEncryptionSetResourceId": { + "type": "string", + "value": "[if(not(empty(parameters('virtualNetworkAddressPrefix'))), reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-cmk-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.diskEncryptionSetResourceId.value, '')]" + }, + "dnsServers": { + "type": "array", + "value": "[if(not(empty(parameters('virtualNetworkAddressPrefix'))), coalesce(tryGet(reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Network/virtualNetworks', split(parameters('hubVirtualNetworkResourceId'), '/')[8]), '2023-11-01'), 'dhcpOptions', 'dnsServers'), createArray()), createArray())]" + }, + "keyVaultUri": { + "type": "string", + "value": "[if(not(empty(parameters('virtualNetworkAddressPrefix'))), reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-cmk-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.keyVaultUri.value, '')]" + }, + "locationProperties": { + "type": "object", + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.locationProperties.value]" + }, + "logAnalyticsWorkspaceResourceId": { + "type": "string", + "value": "[parameters('logAnalyticsWorkspaceResourceId')]" + }, + "mlzTags": { + "type": "object", + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.mlzTags.value]" + }, + "namingConvention": { + "type": "object", + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tiers.value[0].namingConvention]" + }, + "privateDnsZones": { + "type": "array", + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.privateDnsZones.value]" + }, + "resourceAbbreviations": { + "type": "object", + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceAbbreviations.value]" + }, + "resourcePrefix": { + "type": "string", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('firewallResourceId'), '/')[2], split(parameters('firewallResourceId'), '/')[4]), 'Microsoft.Network/azureFirewalls', split(parameters('firewallResourceId'), '/')[8]), '2020-11-01', 'full').tags.resourcePrefix]" + }, + "storageAccountResourceId": { + "type": "string", + "value": "[if(not(empty(parameters('virtualNetworkAddressPrefix'))), reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-storage-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.storageAccountResourceId.value, '')]" + }, + "storageEncryptionKeyName": { + "type": "string", + "value": "[if(not(empty(parameters('virtualNetworkAddressPrefix'))), reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-cmk-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.storageKeyName.value, '')]" + }, + "subnets": { + "type": "array", + "value": "[if(not(empty(parameters('virtualNetworkAddressPrefix'))), reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-network-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.subnets.value, createArray())]" + }, + "tier": { + "type": "object", + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tiers.value[0]]" + }, + "tokens": { + "type": "object", + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-logic-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value]" + }, + "userAssignedIdentityResourceId": { + "type": "string", + "value": "[if(not(empty(parameters('virtualNetworkAddressPrefix'))), reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-cmk-{0}-{1}', parameters('workloadShortName'), parameters('deploymentNameSuffix'))), '2022-09-01').outputs.userAssignedIdentityResourceId.value, '')]" + } + } + } + } + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('deploy-management-{0}', parameters('deploymentNameSuffix'))]", + "location": "[deployment().location]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "activeDirectorySolution": { + "value": "[parameters('activeDirectorySolution')]" + }, + "avdObjectId": { + "value": "[parameters('avdObjectId')]" + }, + "avdPrivateDnsZoneResourceId": { + "value": "[format('{0}{1}', variables('privateDnsZoneResourceIdPrefix'), filter(reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.privateDnsZones.value, lambda('name', startsWith(lambdaVariables('name'), 'privatelink.wvd')))[0])]" + }, + "customImageId": { + "value": "[variables('customImageId')]" + }, + "customRdpProperty": { + "value": "[parameters('customRdpProperty')]" + }, + "deployFslogix": { + "value": "[variables('deployFslogix')]" + }, + "deploymentNameSuffix": { + "value": "[parameters('deploymentNameSuffix')]" + }, + "desktopFriendlyName": "[if(empty(parameters('desktopFriendlyName')), createObject('value', string(parameters('stampIndex'))), createObject('value', parameters('desktopFriendlyName')))]", + "diskEncryptionSetResourceId": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.diskEncryptionSetResourceId.value]" + }, + "diskSku": { + "value": "[parameters('diskSku')]" + }, + "domainJoinPassword": { + "value": "[parameters('domainJoinPassword')]" + }, + "domainJoinUserPrincipalName": { + "value": "[parameters('domainJoinUserPrincipalName')]" + }, + "domainName": { + "value": "[parameters('domainName')]" + }, + "enableApplicationInsights": { + "value": "[parameters('enableApplicationInsights')]" + }, + "enableAvdInsights": { + "value": "[parameters('enableAvdInsights')]" + }, + "environmentAbbreviation": { + "value": "[parameters('environmentAbbreviation')]" + }, + "fslogixStorageService": { + "value": "[parameters('fslogixStorageService')]" + }, + "hostPoolPublicNetworkAccess": { + "value": "[parameters('hostPoolPublicNetworkAccess')]" + }, + "hostPoolType": { + "value": "[parameters('hostPoolType')]" + }, + "imageOffer": { + "value": "[parameters('imageOffer')]" + }, + "imagePublisher": { + "value": "[parameters('imagePublisher')]" + }, + "imageSku": { + "value": "[parameters('imageSku')]" + }, + "imageVersionResourceId": { + "value": "[parameters('imageVersionResourceId')]" + }, + "locationControlPlane": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Network/virtualNetworks', split(parameters('hubVirtualNetworkResourceId'), '/')[8]), '2023-11-01', 'full').location]" + }, + "locationVirtualMachines": { + "value": "[parameters('locationVirtualMachines')]" + }, + "logAnalyticsWorkspaceRetention": { + "value": "[parameters('logAnalyticsWorkspaceRetention')]" + }, + "logAnalyticsWorkspaceSku": { + "value": "[parameters('logAnalyticsWorkspaceSku')]" + }, + "maxSessionLimit": { + "value": "[mul(parameters('usersPerCore'), parameters('virtualMachineVirtualCpuCount'))]" + }, + "mlzTags": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.mlzTags.value]" + }, + "namingConvention": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.namingConvention.value]" + }, + "organizationalUnitPath": { + "value": "[parameters('organizationalUnitPath')]" + }, + "privateDnsZoneResourceIdPrefix": { + "value": "[variables('privateDnsZoneResourceIdPrefix')]" + }, + "privateDnsZones": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.privateDnsZones.value]" + }, + "privateLinkScopeResourceId": { + "value": "[parameters('privateLinkScopeResourceId')]" + }, + "recoveryServices": { + "value": "[parameters('recoveryServices')]" + }, + "recoveryServicesGeo": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.locationProperties.value.recoveryServicesGeo]" + }, + "resourceAbbreviations": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceAbbreviations.value]" + }, + "resourceGroupName": { + "value": "[replace(reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-mgmt-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.names.value.resourceGroup, reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-mgmt-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value.service, 'management')]" + }, + "resourceGroupProfiles": { + "value": "[replace(reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.namingConvention.value.resourceGroup, reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value.service, 'profiles')]" + }, + "securityPrincipalObjectIds": { + "value": "[map(parameters('securityPrincipals'), lambda('item', lambdaVariables('item').objectId))]" + }, + "serviceToken": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value.service]" + }, + "sessionHostNamePrefix": { + "value": "[replace(reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.namingConvention.value.virtualMachine, reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value.service, '')]" + }, + "storageService": { + "value": "[variables('storageService')]" + }, + "subnetResourceId": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.subnets.value[0].id]" + }, + "subnets": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.subnets.value]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "timeZone": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.locationProperties.value.timeZone]" + }, + "validationEnvironment": { + "value": "[parameters('validationEnvironment')]" + }, + "virtualMachinePassword": { + "value": "[parameters('virtualMachinePassword')]" + }, + "virtualMachineSize": { + "value": "[parameters('virtualMachineSize')]" + }, + "virtualMachineUsername": { + "value": "[parameters('virtualMachineUsername')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "10495259955667653232" + } + }, + "parameters": { + "activeDirectorySolution": { + "type": "string" + }, + "avdObjectId": { + "type": "string" + }, + "avdPrivateDnsZoneResourceId": { + "type": "string" + }, + "customImageId": { + "type": "string" + }, + "customRdpProperty": { + "type": "string" + }, + "deployFslogix": { + "type": "bool" + }, + "deploymentNameSuffix": { + "type": "string" + }, + "desktopFriendlyName": { + "type": "string" + }, + "diskEncryptionSetResourceId": { + "type": "string" + }, + "diskSku": { + "type": "string" + }, + "domainJoinPassword": { + "type": "securestring" + }, + "domainJoinUserPrincipalName": { + "type": "string" + }, + "domainName": { + "type": "string" + }, + "enableApplicationInsights": { + "type": "bool" + }, + "enableAvdInsights": { + "type": "bool" + }, + "environmentAbbreviation": { + "type": "string" + }, + "fslogixStorageService": { + "type": "string" + }, + "hostPoolPublicNetworkAccess": { + "type": "string" + }, + "hostPoolType": { + "type": "string" + }, + "imageOffer": { + "type": "string" + }, + "imagePublisher": { + "type": "string" + }, + "imageSku": { + "type": "string" + }, + "imageVersionResourceId": { + "type": "string" + }, + "locationControlPlane": { + "type": "string" + }, + "locationVirtualMachines": { + "type": "string" + }, + "logAnalyticsWorkspaceRetention": { + "type": "int" + }, + "logAnalyticsWorkspaceSku": { + "type": "string" + }, + "maxSessionLimit": { + "type": "int" + }, + "mlzTags": { + "type": "object" + }, + "namingConvention": { + "type": "object" + }, + "organizationalUnitPath": { + "type": "string" + }, + "privateDnsZoneResourceIdPrefix": { + "type": "string" + }, + "privateDnsZones": { + "type": "array" + }, + "privateLinkScopeResourceId": { + "type": "string" + }, + "recoveryServices": { + "type": "bool" + }, + "recoveryServicesGeo": { + "type": "string" + }, + "resourceAbbreviations": { + "type": "object" + }, + "resourceGroupName": { + "type": "string" + }, + "resourceGroupProfiles": { + "type": "string" + }, + "securityPrincipalObjectIds": { + "type": "array" + }, + "serviceToken": { + "type": "string" + }, + "sessionHostNamePrefix": { + "type": "string" + }, + "storageService": { + "type": "string" + }, + "subnetResourceId": { + "type": "string" + }, + "subnets": { + "type": "array" + }, + "tags": { + "type": "object" + }, + "timeZone": { + "type": "string" + }, + "validationEnvironment": { + "type": "bool" + }, + "virtualMachinePassword": { + "type": "securestring" + }, + "virtualMachineSize": { + "type": "string" + }, + "virtualMachineUsername": { + "type": "string" + } + }, + "variables": { + "galleryImageOffer": "[if(empty(parameters('imageVersionResourceId')), format('\"{0}\"', parameters('imageOffer')), 'null')]", + "galleryImagePublisher": "[if(empty(parameters('imageVersionResourceId')), format('\"{0}\"', parameters('imagePublisher')), 'null')]", + "galleryImageSku": "[if(empty(parameters('imageVersionResourceId')), format('\"{0}\"', parameters('imageSku')), 'null')]", + "galleryItemId": "[if(empty(parameters('imageVersionResourceId')), format('\"{0}.{1}{2}\"', parameters('imagePublisher'), parameters('imageOffer'), parameters('imageSku')), 'null')]", + "hostPoolName": "[parameters('namingConvention').hostPool]", + "imageType": "[if(empty(parameters('imageVersionResourceId')), '\"Gallery\"', '\"CustomImage\"')]", + "userAssignedIdentityNamePrefix": "[parameters('namingConvention').userAssignedIdentity]" + }, + "resources": [ + { + "type": "Microsoft.Resources/resourceGroups", + "apiVersion": "2023-07-01", + "name": "[parameters('resourceGroupName')]", + "location": "[parameters('locationControlPlane')]", + "tags": "[union(createObject('cm-resource-parent', format('{0}}}/resourceGroups/{1}/providers/Microsoft.DesktopVirtualization/hostpools/{2}', subscription().id, parameters('resourceGroupName'), variables('hostPoolName'))), coalesce(tryGet(parameters('tags'), 'Microsoft.Resources/resourceGroups'), createObject()), parameters('mlzTags'))]" + }, + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(parameters('avdObjectId'), '40c5ff49-9181-41f8-ae61-143b0e78555e', subscription().id)]", + "properties": { + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', '40c5ff49-9181-41f8-ae61-143b0e78555e')]", + "principalId": "[parameters('avdObjectId')]" + } }, { - "condition": "[and(parameters('deployFslogix'), equals(parameters('fslogixStorageService'), 'AzureFiles Premium'))]", + "condition": "[or(parameters('enableApplicationInsights'), parameters('enableAvdInsights'))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('deploy-function-app-{0}', parameters('deploymentNameSuffix'))]", - "resourceGroup": "[parameters('resourceGroupManagement')]", + "name": "[format('deploy-monitoring-{0}', parameters('deploymentNameSuffix'))]", + "resourceGroup": "[parameters('resourceGroupName')]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "delegatedSubnetResourceId": { - "value": "[filter(parameters('subnets'), lambda('subnet', contains(lambdaVariables('subnet').name, 'FunctionAppOutbound')))[0].id]" - }, "deploymentNameSuffix": { "value": "[parameters('deploymentNameSuffix')]" }, - "enableApplicationInsights": { - "value": "[parameters('enableApplicationInsights')]" + "enableAvdInsights": { + "value": "[parameters('enableAvdInsights')]" }, - "environmentAbbreviation": { - "value": "[parameters('environmentAbbreviation')]" + "hostPoolResourceId": { + "value": "[format('{0}}}/resourceGroups/{1}/providers/Microsoft.DesktopVirtualization/hostpools/{2}', subscription().id, parameters('resourceGroupName'), variables('hostPoolName'))]" }, - "hostPoolName": { - "value": "[variables('hostPoolName')]" + "location": { + "value": "[parameters('locationVirtualMachines')]" }, - "logAnalyticsWorkspaceResourceId": { - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-monitoring-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.logAnalyticsWorkspaceResourceId.value]" + "logAnalyticsWorkspaceRetention": { + "value": "[parameters('logAnalyticsWorkspaceRetention')]" }, - "namingConvention": { - "value": "[parameters('namingConvention')]" + "logAnalyticsWorkspaceSku": { + "value": "[parameters('logAnalyticsWorkspaceSku')]" }, - "privateDnsZoneResourceIdPrefix": { - "value": "[parameters('privateDnsZoneResourceIdPrefix')]" + "mlzTags": { + "value": "[parameters('mlzTags')]" }, - "privateDnsZones": { - "value": "[parameters('privateDnsZones')]" + "namingConvention": { + "value": "[parameters('namingConvention')]" }, "privateLinkScopeResourceId": { "value": "[parameters('privateLinkScopeResourceId')]" }, - "resourceAbbreviations": { - "value": "[parameters('resourceAbbreviations')]" - }, - "resourceGroupControlPlane": { - "value": "[parameters('resourceGroupControlPlane')]" - }, - "resourceGroupStorage": { - "value": "[parameters('resourceGroupStorage')]" - }, "serviceToken": { "value": "[parameters('serviceToken')]" }, - "subnetResourceId": { - "value": "[parameters('subnetResourceId')]" - }, "tags": { "value": "[parameters('tags')]" - }, - "timeDifference": { - "value": "[parameters('timeDifference')]" } }, "template": { @@ -8012,486 +6139,589 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "13334192291900135747" + "version": "0.31.92.45157", + "templateHash": "14902820815719387395" } }, "parameters": { - "delegatedSubnetResourceId": { - "type": "string" - }, "deploymentNameSuffix": { "type": "string" }, - "enableApplicationInsights": { + "enableAvdInsights": { "type": "bool" }, - "environmentAbbreviation": { - "type": "string" - }, - "hostPoolName": { + "hostPoolResourceId": { "type": "string" }, - "keyExpirationInDays": { - "type": "int", - "defaultValue": 30 - }, "location": { - "type": "string", - "defaultValue": "[resourceGroup().location]" - }, - "logAnalyticsWorkspaceResourceId": { "type": "string" }, - "namingConvention": { - "type": "object" + "logAnalyticsWorkspaceRetention": { + "type": "int" }, - "privateDnsZoneResourceIdPrefix": { + "logAnalyticsWorkspaceSku": { "type": "string" }, - "privateDnsZones": { - "type": "array" - }, - "privateLinkScopeResourceId": { - "type": "string" + "mlzTags": { + "type": "object" }, - "resourceAbbreviations": { + "namingConvention": { "type": "object" }, - "resourceGroupControlPlane": { + "privateLinkScopeResourceId": { "type": "string" }, - "resourceGroupStorage": { - "type": "string" + "service": { + "type": "string", + "defaultValue": "mgmt" }, "serviceToken": { "type": "string" }, - "subnetResourceId": { - "type": "string" - }, "tags": { "type": "object" - }, - "timeDifference": { - "type": "string" } }, - "variables": { - "cloudSuffix": "[replace(replace(environment().resourceManager, 'https://management.', ''), '/', '')]", - "functionAppKeyword": "[if(or(equals(environment().name, 'AzureCloud'), equals(environment().name, 'AzureUSGovernment')), 'azurewebsites', 'appservice')]", - "functionAppScmPrivateDnsZoneResourceId": "[format('{0}scm.{1}', parameters('privateDnsZoneResourceIdPrefix'), filter(parameters('privateDnsZones'), lambda('name', contains(lambdaVariables('name'), variables('functionAppKeyword'))))[0])]", - "service": "aipfsq", - "storageSubResources": [ - { - "name": "blob", - "id": "[format('{0}{1}', parameters('privateDnsZoneResourceIdPrefix'), filter(parameters('privateDnsZones'), lambda('name', contains(lambdaVariables('name'), 'blob')))[0])]", - "nic": "[parameters('namingConvention').storageAccountBlobNetworkInterface]", - "pe": "[parameters('namingConvention').storageAccountBlobPrivateEndpoint]" - }, - { - "name": "file", - "id": "[format('{0}{1}', parameters('privateDnsZoneResourceIdPrefix'), filter(parameters('privateDnsZones'), lambda('name', contains(lambdaVariables('name'), 'file')))[0])]", - "nic": "[parameters('namingConvention').storageAccountFileNetworkInterface]", - "pe": "[parameters('namingConvention').storageAccountFilePrivateEndpoint]" - }, - { - "name": "queue", - "id": "[format('{0}{1}', parameters('privateDnsZoneResourceIdPrefix'), filter(parameters('privateDnsZones'), lambda('name', contains(lambdaVariables('name'), 'queue')))[0])]", - "nic": "[parameters('namingConvention').storageAccountQueueNetworkInterface]", - "pe": "[parameters('namingConvention').storageAccountQueuePrivateEndpoint]" - }, - { - "name": "table", - "id": "[format('{0}{1}', parameters('privateDnsZoneResourceIdPrefix'), filter(parameters('privateDnsZones'), lambda('name', contains(lambdaVariables('name'), 'table')))[0])]", - "nic": "[parameters('namingConvention').storageAccountTableNetworkInterface]", - "pe": "[parameters('namingConvention').storageAccountTablePrivateEndpoint]" - } - ] - }, "resources": [ { - "type": "Microsoft.ManagedIdentity/userAssignedIdentities", - "apiVersion": "2023-01-31", - "name": "[replace(parameters('namingConvention').userAssignedIdentity, parameters('serviceToken'), variables('service'))]", + "type": "Microsoft.OperationalInsights/workspaces", + "apiVersion": "2021-06-01", + "name": "[replace(parameters('namingConvention').logAnalyticsWorkspace, parameters('serviceToken'), parameters('service'))]", "location": "[parameters('location')]", - "tags": "[coalesce(tryGet(parameters('tags'), 'Microsoft.ManagedIdentity/userAssignedIdentities'), createObject())]" + "tags": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), coalesce(tryGet(parameters('tags'), 'Microsoft.OperationalInsights/workspaces'), createObject()), parameters('mlzTags'))]", + "properties": { + "sku": { + "name": "[parameters('logAnalyticsWorkspaceSku')]" + }, + "retentionInDays": "[parameters('logAnalyticsWorkspaceRetention')]", + "workspaceCapping": { + "dailyQuotaGb": -1 + }, + "publicNetworkAccessForIngestion": "Disabled", + "publicNetworkAccessForQuery": "Enabled" + } }, { - "type": "Microsoft.KeyVault/vaults", - "apiVersion": "2022-07-01", - "name": "[format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id))]", + "condition": "[parameters('enableAvdInsights')]", + "type": "Microsoft.Insights/dataCollectionRules", + "apiVersion": "2022-06-01", + "name": "[format('microsoft-avdi-{0}', replace(parameters('namingConvention').dataCollectionRule, parameters('serviceToken'), parameters('service')))]", "location": "[parameters('location')]", - "tags": "[coalesce(tryGet(parameters('tags'), 'Microsoft.KeyVault/vaults'), createObject())]", + "tags": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), coalesce(tryGet(parameters('tags'), 'Microsoft.Insights/dataCollectionRules'), createObject()), parameters('mlzTags'))]", + "kind": "Windows", "properties": { - "enabledForDeployment": false, - "enabledForDiskEncryption": false, - "enabledForTemplateDeployment": false, - "enablePurgeProtection": true, - "enableRbacAuthorization": true, - "enableSoftDelete": true, - "networkAcls": { - "bypass": "AzureServices", - "defaultAction": "Deny", - "ipRules": [], - "virtualNetworkRules": [] + "dataSources": { + "performanceCounters": [ + { + "streams": [ + "Microsoft-Perf" + ], + "samplingFrequencyInSeconds": 30, + "counterSpecifiers": [ + "\\LogicalDisk(C:)\\Avg. Disk Queue Length", + "\\LogicalDisk(C:)\\Current Disk Queue Length", + "\\Memory\\Available Mbytes", + "\\Memory\\Page Faults/sec", + "\\Memory\\Pages/sec", + "\\Memory\\% Committed Bytes In Use", + "\\PhysicalDisk(*)\\Avg. Disk Queue Length", + "\\PhysicalDisk(*)\\Avg. Disk sec/Read", + "\\PhysicalDisk(*)\\Avg. Disk sec/Transfer", + "\\PhysicalDisk(*)\\Avg. Disk sec/Write", + "\\Processor Information(_Total)\\% Processor Time", + "\\User Input Delay per Process(*)\\Max Input Delay", + "\\User Input Delay per Session(*)\\Max Input Delay", + "\\RemoteFX Network(*)\\Current TCP RTT", + "\\RemoteFX Network(*)\\Current UDP Bandwidth" + ], + "name": "perfCounterDataSource10" + }, + { + "streams": [ + "Microsoft-Perf" + ], + "samplingFrequencyInSeconds": 60, + "counterSpecifiers": [ + "\\LogicalDisk(C:)\\% Free Space", + "\\LogicalDisk(C:)\\Avg. Disk sec/Transfer", + "\\Terminal Services(*)\\Active Sessions", + "\\Terminal Services(*)\\Inactive Sessions", + "\\Terminal Services(*)\\Total Sessions" + ], + "name": "perfCounterDataSource30" + } + ], + "windowsEventLogs": [ + { + "streams": [ + "Microsoft-Event" + ], + "xPathQueries": [ + "Microsoft-Windows-TerminalServices-RemoteConnectionManager/Admin!*[System[(Level=2 or Level=3 or Level=4 or Level=0)]]", + "Microsoft-Windows-TerminalServices-LocalSessionManager/Operational!*[System[(Level=2 or Level=3 or Level=4 or Level=0)]]", + "System!*", + "Microsoft-FSLogix-Apps/Operational!*[System[(Level=2 or Level=3 or Level=4 or Level=0)]]", + "Application!*[System[(Level=2 or Level=3)]]", + "Microsoft-FSLogix-Apps/Admin!*[System[(Level=2 or Level=3 or Level=4 or Level=0)]]" + ], + "name": "eventLogsDataSource" + } + ] }, - "publicNetworkAccess": "Disabled", - "sku": { - "family": "A", - "name": "premium" + "destinations": { + "logAnalytics": [ + { + "workspaceResourceId": "[resourceId('Microsoft.OperationalInsights/workspaces', replace(parameters('namingConvention').logAnalyticsWorkspace, parameters('serviceToken'), parameters('service')))]", + "name": "la-workspace" + } + ] }, - "softDeleteRetentionInDays": "[if(or(equals(parameters('environmentAbbreviation'), 'dev'), equals(parameters('environmentAbbreviation'), 'test')), 7, 90)]", - "tenantId": "[subscription().tenantId]" - } - }, - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2020-04-01-preview", - "scope": "[format('Microsoft.KeyVault/vaults/{0}', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id)))]", - "name": "[guid(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', replace(parameters('namingConvention').userAssignedIdentity, parameters('serviceToken'), variables('service'))), 'e147488a-f6f5-4113-8e2d-b22465e65bf6', resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id))))]", - "properties": { - "principalId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', replace(parameters('namingConvention').userAssignedIdentity, parameters('serviceToken'), variables('service'))), '2023-01-31').principalId]", - "principalType": "ServicePrincipal", - "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', 'e147488a-f6f5-4113-8e2d-b22465e65bf6')]" + "dataFlows": [ + { + "streams": [ + "Microsoft-Perf", + "Microsoft-Event" + ], + "destinations": [ + "la-workspace" + ] + } + ] }, "dependsOn": [ - "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', replace(parameters('namingConvention').userAssignedIdentity, parameters('serviceToken'), variables('service')))]", - "[resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id)))]" + "[resourceId('Microsoft.OperationalInsights/workspaces', replace(parameters('namingConvention').logAnalyticsWorkspace, parameters('serviceToken'), parameters('service')))]" ] }, { - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", - "name": "[replace(parameters('namingConvention').keyVaultPrivateEndpoint, parameters('serviceToken'), variables('service'))]", + "condition": "[parameters('enableAvdInsights')]", + "type": "Microsoft.Insights/dataCollectionEndpoints", + "apiVersion": "2021-04-01", + "name": "[replace(parameters('namingConvention').dataCollectionEndpoint, parameters('serviceToken'), parameters('service'))]", "location": "[parameters('location')]", - "tags": "[coalesce(tryGet(parameters('tags'), 'Microsoft.Network/privateEndpoints'), createObject())]", + "tags": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), coalesce(tryGet(parameters('tags'), 'Microsoft.Insights/dataCollectionEndpoints'), createObject()), parameters('mlzTags'))]", + "kind": "Windows", "properties": { - "customNetworkInterfaceName": "[replace(parameters('namingConvention').keyVaultNetworkInterface, parameters('serviceToken'), variables('service'))]", - "privateLinkServiceConnections": [ - { - "name": "[replace(parameters('namingConvention').keyVaultPrivateEndpoint, parameters('serviceToken'), variables('service'))]", - "properties": { - "privateLinkServiceId": "[resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id)))]", - "groupIds": [ - "vault" - ] - } - } - ], - "subnet": { - "id": "[parameters('subnetResourceId')]" + "networkAcls": { + "publicNetworkAccess": "Disabled" } - }, - "dependsOn": [ - "[resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id)))]" - ] + } }, { - "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2021-08-01", - "name": "[format('{0}/{1}', replace(parameters('namingConvention').keyVaultPrivateEndpoint, parameters('serviceToken'), variables('service')), format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id)))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('deploy-private-link-scope-law-{0}', parameters('deploymentNameSuffix'))]", + "subscriptionId": "[split(parameters('privateLinkScopeResourceId'), '/')[2]]", + "resourceGroup": "[split(parameters('privateLinkScopeResourceId'), '/')[4]]", "properties": { - "privateDnsZoneConfigs": [ - { - "name": "ipconfig1", - "properties": { - "privateDnsZoneId": "[format('{0}{1}', parameters('privateDnsZoneResourceIdPrefix'), filter(parameters('privateDnsZones'), lambda('name', contains(lambdaVariables('name'), 'vaultcore')))[0])]" - } + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "logAnalyticsWorkspaceResourceId": { + "value": "[resourceId('Microsoft.OperationalInsights/workspaces', replace(parameters('namingConvention').logAnalyticsWorkspace, parameters('serviceToken'), parameters('service')))]" + }, + "privateLinkScopeResourceId": { + "value": "[parameters('privateLinkScopeResourceId')]" } - ] - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/privateEndpoints', replace(parameters('namingConvention').keyVaultPrivateEndpoint, parameters('serviceToken'), variables('service')))]", - "[resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id)))]" - ] - }, - { - "type": "Microsoft.KeyVault/vaults/keys", - "apiVersion": "2022-07-01", - "name": "[format('{0}/{1}', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id)), 'StorageEncryptionKey')]", - "properties": { - "attributes": { - "enabled": true }, - "keySize": 4096, - "kty": "RSA", - "rotationPolicy": { - "attributes": { - "expiryTime": "[format('P{0}D', string(parameters('keyExpirationInDays')))]" + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "6666075809401495349" + } }, - "lifetimeActions": [ + "parameters": { + "applicationInsightsResourceId": { + "type": "string", + "defaultValue": "" + }, + "dataCollectionEndpointResourceId": { + "type": "string", + "defaultValue": "" + }, + "logAnalyticsWorkspaceResourceId": { + "type": "string", + "defaultValue": "" + }, + "privateLinkScopeResourceId": { + "type": "string" + } + }, + "resources": [ { - "action": { - "type": "Notify" - }, - "trigger": { - "timeBeforeExpiry": "P10D" + "condition": "[not(empty(parameters('applicationInsightsResourceId')))]", + "type": "Microsoft.Insights/privateLinkScopes/scopedResources", + "apiVersion": "2021-09-01", + "name": "[format('{0}/{1}', split(parameters('privateLinkScopeResourceId'), '/')[8], if(empty(parameters('applicationInsightsResourceId')), 'applicationInsights', split(parameters('applicationInsightsResourceId'), '/')[8]))]", + "properties": { + "linkedResourceId": "[parameters('applicationInsightsResourceId')]" } }, { - "action": { - "type": "Rotate" - }, - "trigger": { - "timeAfterCreate": "[format('P{0}D', string(sub(parameters('keyExpirationInDays'), 7)))]" + "condition": "[not(empty(parameters('dataCollectionEndpointResourceId')))]", + "type": "Microsoft.Insights/privateLinkScopes/scopedResources", + "apiVersion": "2021-09-01", + "name": "[format('{0}/{1}', split(parameters('privateLinkScopeResourceId'), '/')[8], if(empty(parameters('dataCollectionEndpointResourceId')), 'dataCollectionEndpoint', split(parameters('dataCollectionEndpointResourceId'), '/')[8]))]", + "properties": { + "linkedResourceId": "[parameters('dataCollectionEndpointResourceId')]" + } + }, + { + "condition": "[not(empty(parameters('logAnalyticsWorkspaceResourceId')))]", + "type": "Microsoft.Insights/privateLinkScopes/scopedResources", + "apiVersion": "2021-09-01", + "name": "[format('{0}/{1}', split(parameters('privateLinkScopeResourceId'), '/')[8], if(empty(parameters('logAnalyticsWorkspaceResourceId')), 'logAnalyticsWorkspace', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[8]))]", + "properties": { + "linkedResourceId": "[parameters('logAnalyticsWorkspaceResourceId')]" } } ] } }, "dependsOn": [ - "[resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id)))]" + "[resourceId('Microsoft.OperationalInsights/workspaces', replace(parameters('namingConvention').logAnalyticsWorkspace, parameters('serviceToken'), parameters('service')))]" ] }, { - "type": "Microsoft.Storage/storageAccounts", + "condition": "[parameters('enableAvdInsights')]", + "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id)]", - "location": "[parameters('location')]", - "tags": "[coalesce(tryGet(parameters('tags'), 'Microsoft.Storage/storageAccounts'), createObject())]", - "sku": { - "name": "Standard_LRS" - }, - "kind": "StorageV2", - "identity": { - "type": "UserAssigned", - "userAssignedIdentities": { - "[format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', replace(parameters('namingConvention').userAssignedIdentity, parameters('serviceToken'), variables('service'))))]": {} - } - }, + "name": "[format('deploy-private-link-scope-dce-{0}', parameters('deploymentNameSuffix'))]", + "subscriptionId": "[split(parameters('privateLinkScopeResourceId'), '/')[2]]", + "resourceGroup": "[split(parameters('privateLinkScopeResourceId'), '/')[4]]", "properties": { - "accessTier": "Hot", - "allowBlobPublicAccess": false, - "allowCrossTenantReplication": false, - "allowedCopyScope": "PrivateLink", - "allowSharedKeyAccess": false, - "azureFilesIdentityBasedAuthentication": { - "directoryServiceOptions": "None" + "expressionEvaluationOptions": { + "scope": "inner" }, - "defaultToOAuthAuthentication": false, - "dnsEndpointType": "Standard", - "encryption": { - "identity": { - "userAssignedIdentity": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', replace(parameters('namingConvention').userAssignedIdentity, parameters('serviceToken'), variables('service')))]" + "mode": "Incremental", + "parameters": { + "dataCollectionEndpointResourceId": { + "value": "[resourceId('Microsoft.Insights/dataCollectionEndpoints', replace(parameters('namingConvention').dataCollectionEndpoint, parameters('serviceToken'), parameters('service')))]" }, - "requireInfrastructureEncryption": true, - "keyvaultproperties": { - "keyvaulturi": "[reference(resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id))), '2022-07-01').vaultUri]", - "keyname": "StorageEncryptionKey" + "privateLinkScopeResourceId": { + "value": "[parameters('privateLinkScopeResourceId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "6666075809401495349" + } }, - "services": { - "file": { - "keyType": "Account", - "enabled": true + "parameters": { + "applicationInsightsResourceId": { + "type": "string", + "defaultValue": "" }, - "table": { - "keyType": "Account", - "enabled": true + "dataCollectionEndpointResourceId": { + "type": "string", + "defaultValue": "" + }, + "logAnalyticsWorkspaceResourceId": { + "type": "string", + "defaultValue": "" + }, + "privateLinkScopeResourceId": { + "type": "string" + } + }, + "resources": [ + { + "condition": "[not(empty(parameters('applicationInsightsResourceId')))]", + "type": "Microsoft.Insights/privateLinkScopes/scopedResources", + "apiVersion": "2021-09-01", + "name": "[format('{0}/{1}', split(parameters('privateLinkScopeResourceId'), '/')[8], if(empty(parameters('applicationInsightsResourceId')), 'applicationInsights', split(parameters('applicationInsightsResourceId'), '/')[8]))]", + "properties": { + "linkedResourceId": "[parameters('applicationInsightsResourceId')]" + } }, - "queue": { - "keyType": "Account", - "enabled": true + { + "condition": "[not(empty(parameters('dataCollectionEndpointResourceId')))]", + "type": "Microsoft.Insights/privateLinkScopes/scopedResources", + "apiVersion": "2021-09-01", + "name": "[format('{0}/{1}', split(parameters('privateLinkScopeResourceId'), '/')[8], if(empty(parameters('dataCollectionEndpointResourceId')), 'dataCollectionEndpoint', split(parameters('dataCollectionEndpointResourceId'), '/')[8]))]", + "properties": { + "linkedResourceId": "[parameters('dataCollectionEndpointResourceId')]" + } }, - "blob": { - "keyType": "Account", - "enabled": true + { + "condition": "[not(empty(parameters('logAnalyticsWorkspaceResourceId')))]", + "type": "Microsoft.Insights/privateLinkScopes/scopedResources", + "apiVersion": "2021-09-01", + "name": "[format('{0}/{1}', split(parameters('privateLinkScopeResourceId'), '/')[8], if(empty(parameters('logAnalyticsWorkspaceResourceId')), 'logAnalyticsWorkspace', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[8]))]", + "properties": { + "linkedResourceId": "[parameters('logAnalyticsWorkspaceResourceId')]" + } } - }, - "keySource": "Microsoft.KeyVault" - }, - "largeFileSharesState": "Disabled", - "minimumTlsVersion": "TLS1_2", - "networkAcls": { - "bypass": "AzureServices", - "virtualNetworkRules": [], - "ipRules": [], - "defaultAction": "Deny" - }, - "publicNetworkAccess": "Disabled", - "supportsHttpsTrafficOnly": true + ] + } }, "dependsOn": [ - "[resourceId('Microsoft.KeyVault/vaults/keys', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id)), 'StorageEncryptionKey')]", - "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', replace(parameters('namingConvention').keyVaultPrivateEndpoint, parameters('serviceToken'), variables('service')), format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id)))]", - "[resourceId('Microsoft.Network/privateEndpoints', replace(parameters('namingConvention').keyVaultPrivateEndpoint, parameters('serviceToken'), variables('service')))]", - "[extensionResourceId(resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id))), 'Microsoft.Authorization/roleAssignments', guid(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', replace(parameters('namingConvention').userAssignedIdentity, parameters('serviceToken'), variables('service'))), 'e147488a-f6f5-4113-8e2d-b22465e65bf6', resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id)))))]", - "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', replace(parameters('namingConvention').userAssignedIdentity, parameters('serviceToken'), variables('service')))]", - "[resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id)))]" + "[resourceId('Microsoft.Insights/dataCollectionEndpoints', replace(parameters('namingConvention').dataCollectionEndpoint, parameters('serviceToken'), parameters('service')))]" ] + } + ], + "outputs": { + "logAnalyticsWorkspaceName": { + "type": "string", + "value": "[replace(parameters('namingConvention').logAnalyticsWorkspace, parameters('serviceToken'), parameters('service'))]" }, - { - "type": "Microsoft.Storage/storageAccounts/blobServices", - "apiVersion": "2021-09-01", - "name": "[format('{0}/{1}', uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id), 'default')]", - "dependsOn": [ - "[resourceId('Microsoft.Storage/storageAccounts', uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id))]" - ] + "logAnalyticsWorkspaceResourceId": { + "type": "string", + "value": "[resourceId('Microsoft.OperationalInsights/workspaces', replace(parameters('namingConvention').logAnalyticsWorkspace, parameters('serviceToken'), parameters('service')))]" }, - { - "copy": { - "name": "privateEndpoints_storage", - "count": "[length(variables('storageSubResources'))]" - }, - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", - "name": "[replace(variables('storageSubResources')[copyIndex()].pe, parameters('serviceToken'), variables('service'))]", - "location": "[parameters('location')]", - "tags": "[coalesce(tryGet(parameters('tags'), 'Microsoft.Network/privateEndpoints'), createObject())]", - "properties": { - "customNetworkInterfaceName": "[replace(variables('storageSubResources')[copyIndex()].nic, parameters('serviceToken'), variables('service'))]", - "privateLinkServiceConnections": [ - { - "name": "[replace(variables('storageSubResources')[copyIndex()].pe, parameters('serviceToken'), variables('service'))]", - "properties": { - "privateLinkServiceId": "[resourceId('Microsoft.Storage/storageAccounts', uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id))]", - "groupIds": [ - "[variables('storageSubResources')[copyIndex()].name]" - ] - } - } - ], - "subnet": { - "id": "[parameters('subnetResourceId')]" - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Storage/storageAccounts', uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id))]" - ] + "dataCollectionRuleResourceId": { + "type": "string", + "value": "[if(parameters('enableAvdInsights'), resourceId('Microsoft.Insights/dataCollectionRules', format('microsoft-avdi-{0}', replace(parameters('namingConvention').dataCollectionRule, parameters('serviceToken'), parameters('service')))), '')]" + } + } + } + }, + "dependsOn": [ + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('resourceGroupName'))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('deploy-vdpool-{0}', parameters('deploymentNameSuffix'))]", + "resourceGroup": "[parameters('resourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "activeDirectorySolution": { + "value": "[parameters('activeDirectorySolution')]" + }, + "avdPrivateDnsZoneResourceId": { + "value": "[parameters('avdPrivateDnsZoneResourceId')]" + }, + "customImageId": { + "value": "[parameters('customImageId')]" + }, + "customRdpProperty": { + "value": "[parameters('customRdpProperty')]" + }, + "diskSku": { + "value": "[parameters('diskSku')]" + }, + "domainName": { + "value": "[parameters('domainName')]" + }, + "enableAvdInsights": { + "value": "[parameters('enableAvdInsights')]" + }, + "galleryImageOffer": { + "value": "[variables('galleryImageOffer')]" + }, + "galleryImagePublisher": { + "value": "[variables('galleryImagePublisher')]" + }, + "galleryImageSku": { + "value": "[variables('galleryImageSku')]" + }, + "galleryItemId": { + "value": "[variables('galleryItemId')]" + }, + "hostPoolDiagnosticSettingName": { + "value": "[parameters('namingConvention').hostPoolDiagnosticSetting]" + }, + "hostPoolName": { + "value": "[variables('hostPoolName')]" + }, + "hostPoolNetworkInterfaceName": { + "value": "[parameters('namingConvention').hostPoolNetworkInterface]" + }, + "hostPoolPrivateEndpointName": { + "value": "[parameters('namingConvention').hostPoolPrivateEndpoint]" + }, + "hostPoolPublicNetworkAccess": { + "value": "[parameters('hostPoolPublicNetworkAccess')]" + }, + "hostPoolType": { + "value": "[parameters('hostPoolType')]" + }, + "imageType": { + "value": "[variables('imageType')]" + }, + "location": { + "value": "[parameters('locationControlPlane')]" + }, + "logAnalyticsWorkspaceResourceId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-monitoring-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.logAnalyticsWorkspaceResourceId.value]" + }, + "maxSessionLimit": { + "value": "[parameters('maxSessionLimit')]" + }, + "mlzTags": { + "value": "[parameters('mlzTags')]" + }, + "sessionHostNamePrefix": { + "value": "[parameters('sessionHostNamePrefix')]" + }, + "subnetResourceId": { + "value": "[parameters('subnetResourceId')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "validationEnvironment": { + "value": "[parameters('validationEnvironment')]" + }, + "virtualMachineSize": { + "value": "[parameters('virtualMachineSize')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "17657921580737492350" + } + }, + "parameters": { + "activeDirectorySolution": { + "type": "string" + }, + "avdPrivateDnsZoneResourceId": { + "type": "string" + }, + "customImageId": { + "type": "string" + }, + "customRdpProperty": { + "type": "string" + }, + "diskSku": { + "type": "string" + }, + "domainName": { + "type": "string" + }, + "enableAvdInsights": { + "type": "bool" + }, + "galleryImageOffer": { + "type": "string" + }, + "galleryImagePublisher": { + "type": "string" + }, + "galleryImageSku": { + "type": "string" + }, + "galleryItemId": { + "type": "string" + }, + "hostPoolDiagnosticSettingName": { + "type": "string" + }, + "hostPoolName": { + "type": "string" + }, + "hostPoolNetworkInterfaceName": { + "type": "string" + }, + "hostPoolPrivateEndpointName": { + "type": "string" + }, + "hostPoolPublicNetworkAccess": { + "type": "string" + }, + "hostPoolType": { + "type": "string" + }, + "imageType": { + "type": "string" + }, + "location": { + "type": "string" }, - { - "copy": { - "name": "privateDnsZoneGroups_storage", - "count": "[length(variables('storageSubResources'))]" - }, - "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2021-08-01", - "name": "[format('{0}/{1}', replace(variables('storageSubResources')[copyIndex()].pe, parameters('serviceToken'), variables('service')), uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id))]", - "properties": { - "privateDnsZoneConfigs": [ - { - "name": "ipconfig1", - "properties": { - "privateDnsZoneId": "[variables('storageSubResources')[copyIndex()].id]" - } - } - ] - }, - "dependsOn": [ - "[resourceId('Microsoft.Network/privateEndpoints', replace(variables('storageSubResources')[copyIndex()].pe, parameters('serviceToken'), variables('service')))]", - "[resourceId('Microsoft.Storage/storageAccounts', uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id))]" - ] + "logAnalyticsWorkspaceResourceId": { + "type": "string" }, - { - "condition": "[parameters('enableApplicationInsights')]", - "type": "Microsoft.Insights/diagnosticSettings", - "apiVersion": "2017-05-01-preview", - "scope": "[format('Microsoft.Storage/storageAccounts/{0}/blobServices/{1}', uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id), 'default')]", - "name": "[replace(parameters('namingConvention').storageAccountDiagnosticSetting, format('{0}-{1}', parameters('serviceToken'), parameters('resourceAbbreviations').storageAccounts), format('blob-{0}-scale', parameters('resourceAbbreviations').storageAccounts))]", - "properties": { - "logs": [ - { - "category": "StorageWrite", - "enabled": true - } - ], - "metrics": [ - { - "category": "Transaction", - "enabled": true - } - ], - "workspaceId": "[parameters('logAnalyticsWorkspaceResourceId')]" - }, - "dependsOn": [ - "[resourceId('Microsoft.Storage/storageAccounts/blobServices', uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id), 'default')]" - ] + "maxSessionLimit": { + "type": "int" }, - { - "condition": "[parameters('enableApplicationInsights')]", - "type": "Microsoft.Insights/components", - "apiVersion": "2020-02-02", - "name": "[replace(parameters('namingConvention').applicationInsights, parameters('serviceToken'), variables('service'))]", - "location": "[parameters('location')]", - "tags": "[coalesce(tryGet(parameters('tags'), 'Microsoft.Insights/components'), createObject())]", - "properties": { - "Application_Type": "web", - "publicNetworkAccessForIngestion": "Disabled", - "publicNetworkAccessForQuery": "Disabled" - }, - "kind": "web" + "mlzTags": { + "type": "object" }, - { - "type": "Microsoft.Web/serverfarms", - "apiVersion": "2023-01-01", - "name": "[replace(parameters('namingConvention').appServicePlan, parameters('serviceToken'), variables('service'))]", - "location": "[parameters('location')]", - "tags": "[coalesce(tryGet(parameters('tags'), 'Microsoft.Web/serverfarms'), createObject())]", - "sku": { - "name": "P0v3", - "tier": "PremiumV3", - "size": "P0v3", - "family": "Pv3", - "capacity": 1 - }, - "kind": "functionapp" + "sessionHostNamePrefix": { + "type": "string" + }, + "subnetResourceId": { + "type": "string" + }, + "tags": { + "type": "object" + }, + "time": { + "type": "string", + "defaultValue": "[utcNow('u')]" + }, + "validationEnvironment": { + "type": "bool" }, + "virtualMachineSize": { + "type": "string" + } + }, + "variables": { + "customRdpProperty_Complete": "[if(contains(parameters('activeDirectorySolution'), 'MicrosoftEntraId'), format('{0}enablerdsaadauth:i:1;', parameters('customRdpProperty')), parameters('customRdpProperty'))]" + }, + "resources": [ { - "type": "Microsoft.Web/sites", - "apiVersion": "2023-01-01", - "name": "[uniqueString(replace(parameters('namingConvention').functionApp, parameters('serviceToken'), variables('service')), resourceGroup().id)]", + "type": "Microsoft.DesktopVirtualization/hostPools", + "apiVersion": "2023-09-05", + "name": "[parameters('hostPoolName')]", "location": "[parameters('location')]", - "tags": "[coalesce(tryGet(parameters('tags'), 'Microsoft.Web/sites'), createObject())]", - "kind": "functionapp", - "identity": { - "type": "SystemAssigned" - }, + "tags": "[union(createObject('cm-resource-parent', format('{0}}}/resourceGroups/{1}/providers/Microsoft.DesktopVirtualization/hostpools/{2}', subscription().id, resourceGroup().name, parameters('hostPoolName'))), coalesce(tryGet(parameters('tags'), 'Microsoft.DesktopVirtualization/hostPools'), createObject()), parameters('mlzTags'))]", "properties": { - "clientAffinityEnabled": false, - "httpsOnly": true, - "publicNetworkAccess": "Disabled", - "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', replace(parameters('namingConvention').appServicePlan, parameters('serviceToken'), variables('service')))]", - "siteConfig": { - "alwaysOn": true, - "appSettings": "[union(createArray(createObject('name', 'AzureWebJobsStorage__blobServiceUri', 'value', format('https://{0}.blob.{1}', uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id), environment().suffixes.storage)), createObject('name', 'AzureWebJobsStorage__credential', 'value', 'managedidentity'), createObject('name', 'AzureWebJobsStorage__queueServiceUri', 'value', format('https://{0}.queue.{1}', uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id), environment().suffixes.storage)), createObject('name', 'AzureWebJobsStorage__tableServiceUri', 'value', format('https://{0}.table.{1}', uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id), environment().suffixes.storage)), createObject('name', 'FUNCTIONS_EXTENSION_VERSION', 'value', '~4'), createObject('name', 'FUNCTIONS_WORKER_RUNTIME', 'value', 'powershell'), createObject('name', 'WEBSITE_LOAD_USER_PROFILE', 'value', '1'), createObject('name', 'EnvironmentName', 'value', environment().name), createObject('name', 'FileShareName', 'value', 'profile-containers'), createObject('name', 'HostPoolName', 'value', parameters('hostPoolName')), createObject('name', 'HostPoolResourceGroupName', 'value', parameters('resourceGroupControlPlane')), createObject('name', 'LogOffMessageBody', 'value', 'This session is about to be logged off. Please save your work.'), createObject('name', 'LogOffMessageTitle', 'value', 'Session Log Off'), createObject('name', 'MaintenanceTagName', 'value', 'Maintenance'), createObject('name', 'ResourceGroupName', 'value', parameters('resourceGroupStorage')), createObject('name', 'ResourceManagerUrl', 'value', if(endsWith(environment().resourceManager, '/'), environment().resourceManager, format('{0}/', environment().resourceManager))), createObject('name', 'StorageSuffix', 'value', environment().suffixes.storage), createObject('name', 'SubscriptionId', 'value', subscription().subscriptionId), createObject('name', 'TenantId', 'value', subscription().tenantId), createObject('name', 'TimeDifference', 'value', parameters('timeDifference'))), if(parameters('enableApplicationInsights'), createArray(createObject('name', 'APPLICATIONINSIGHTS_CONNECTION_STRING', 'value', reference(resourceId('Microsoft.Insights/components', replace(parameters('namingConvention').applicationInsights, parameters('serviceToken'), variables('service'))), '2020-02-02').ConnectionString)), createArray()))]", - "cors": { - "allowedOrigins": [ - "[format('{0}', environment().portal)]", - "[format('https://functions-next.{0}', variables('cloudSuffix'))]", - "[format('https://functions-staging.{0}', variables('cloudSuffix'))]", - "[format('https://functions.{0}', variables('cloudSuffix'))]" - ] - }, - "ftpsState": "Disabled", - "netFrameworkVersion": "v6.0", - "powerShellVersion": "7.2", - "publicNetworkAccess": "Disabled", - "use32BitWorkerProcess": false + "customRdpProperty": "[variables('customRdpProperty_Complete')]", + "hostPoolType": "[parameters('hostPoolType')]", + "loadBalancerType": "[if(equals(parameters('hostPoolType'), 'Pooled'), 'DepthFirst', 'Persistent')]", + "maxSessionLimit": "[parameters('maxSessionLimit')]", + "personalDesktopAssignmentType": "[if(equals(parameters('hostPoolType'), 'Personal'), 'Automatic', null())]", + "preferredAppGroupType": "Desktop", + "publicNetworkAccess": "[parameters('hostPoolPublicNetworkAccess')]", + "registrationInfo": { + "expirationTime": "[dateTimeAdd(parameters('time'), 'PT2H')]", + "registrationTokenOperation": "Update" }, - "virtualNetworkSubnetId": "[parameters('delegatedSubnetResourceId')]", - "vnetContentShareEnabled": false, - "vnetRouteAllEnabled": true - }, - "dependsOn": [ - "[resourceId('Microsoft.Insights/components', replace(parameters('namingConvention').applicationInsights, parameters('serviceToken'), variables('service')))]", - "[resourceId('Microsoft.Web/serverfarms', replace(parameters('namingConvention').appServicePlan, parameters('serviceToken'), variables('service')))]", - "[resourceId('Microsoft.Storage/storageAccounts', uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id))]" - ] + "startVMOnConnect": true, + "validationEnvironment": "[parameters('validationEnvironment')]", + "vmTemplate": "[format('{{\"domain\":\"{0}\",\"galleryImageOffer\":{1},\"galleryImagePublisher\":{2},\"galleryImageSKU\":{3},\"imageType\":{4},\"customImageId\":{5},\"namePrefix\":\"{6}\",\"osDiskType\":\"{7}\",\"vmSize\":{{\"id\":\"{8}\",\"cores\":null,\"ram\":null,\"rdmaEnabled\": false,\"supportsMemoryPreservingMaintenance\": true}},\"galleryItemId\":{9},\"hibernate\":false,\"diskSizeGB\":0,\"securityType\":\"TrustedLaunch\",\"secureBoot\":true,\"vTPM\":true,\"vmInfrastructureType\":\"Cloud\",\"virtualProcessorCount\":null,\"memoryGB\":null,\"maximumMemoryGB\":null,\"minimumMemoryGB\":null,\"dynamicMemoryConfig\":false}}', parameters('domainName'), parameters('galleryImageOffer'), parameters('galleryImagePublisher'), parameters('galleryImageSku'), parameters('imageType'), parameters('customImageId'), parameters('sessionHostNamePrefix'), parameters('diskSku'), parameters('virtualMachineSize'), parameters('galleryItemId'))]" + } }, { "type": "Microsoft.Network/privateEndpoints", "apiVersion": "2023-04-01", - "name": "[replace(parameters('namingConvention').functionAppPrivateEndpoint, parameters('serviceToken'), variables('service'))]", + "name": "[parameters('hostPoolPrivateEndpointName')]", "location": "[parameters('location')]", + "tags": "[union(createObject('cm-resource-parent', resourceId('Microsoft.DesktopVirtualization/hostPools', parameters('hostPoolName'))), coalesce(tryGet(parameters('tags'), 'Microsoft.Network/privateEndpoints'), createObject()), parameters('mlzTags'))]", "properties": { - "customNetworkInterfaceName": "[replace(parameters('namingConvention').functionAppNetworkInterface, parameters('serviceToken'), variables('service'))]", + "customNetworkInterfaceName": "[parameters('hostPoolNetworkInterfaceName')]", "privateLinkServiceConnections": [ { - "name": "[replace(parameters('namingConvention').functionAppPrivateEndpoint, parameters('serviceToken'), variables('service'))]", + "name": "[parameters('hostPoolPrivateEndpointName')]", "properties": { - "privateLinkServiceId": "[resourceId('Microsoft.Web/sites', uniqueString(replace(parameters('namingConvention').functionApp, parameters('serviceToken'), variables('service')), resourceGroup().id))]", + "privateLinkServiceId": "[resourceId('Microsoft.DesktopVirtualization/hostPools', parameters('hostPoolName'))]", "groupIds": [ - "sites" + "connection" ] } } @@ -8501,256 +6731,274 @@ } }, "dependsOn": [ - "[resourceId('Microsoft.Web/sites', uniqueString(replace(parameters('namingConvention').functionApp, parameters('serviceToken'), variables('service')), resourceGroup().id))]" + "[resourceId('Microsoft.DesktopVirtualization/hostPools', parameters('hostPoolName'))]" ] }, { "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2021-08-01", - "name": "[format('{0}/{1}', replace(parameters('namingConvention').functionAppPrivateEndpoint, parameters('serviceToken'), variables('service')), 'default')]", + "apiVersion": "2023-05-01", + "name": "[format('{0}/{1}', parameters('hostPoolPrivateEndpointName'), 'default')]", "properties": { "privateDnsZoneConfigs": [ { - "name": "ipconfig1", + "name": "[replace(split(parameters('avdPrivateDnsZoneResourceId'), '/')[8], '.', '-')]", "properties": { - "privateDnsZoneId": "[format('{0}{1}', parameters('privateDnsZoneResourceIdPrefix'), filter(parameters('privateDnsZones'), lambda('name', contains(lambdaVariables('name'), variables('functionAppKeyword'))))[0])]" + "privateDnsZoneId": "[parameters('avdPrivateDnsZoneResourceId')]" } } ] }, "dependsOn": [ - "[resourceId('Microsoft.Network/privateEndpoints', replace(parameters('namingConvention').functionAppPrivateEndpoint, parameters('serviceToken'), variables('service')))]" + "[resourceId('Microsoft.Network/privateEndpoints', parameters('hostPoolPrivateEndpointName'))]" ] }, { - "condition": "[parameters('enableApplicationInsights')]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('deploy-private-link-scope-appi-{0}', parameters('deploymentNameSuffix'))]", - "subscriptionId": "[split(parameters('privateLinkScopeResourceId'), '/')[2]]", - "resourceGroup": "[split(parameters('privateLinkScopeResourceId'), '/')[4]]", + "condition": "[parameters('enableAvdInsights')]", + "type": "Microsoft.Insights/diagnosticSettings", + "apiVersion": "2021-05-01-preview", + "scope": "[format('Microsoft.DesktopVirtualization/hostPools/{0}', parameters('hostPoolName'))]", + "name": "[parameters('hostPoolDiagnosticSettingName')]", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "applicationInsightsResourceId": { - "value": "[resourceId('Microsoft.Insights/components', replace(parameters('namingConvention').applicationInsights, parameters('serviceToken'), variables('service')))]" - }, - "privateLinkScopeResourceId": { - "value": "[parameters('privateLinkScopeResourceId')]" + "logs": [ + { + "categoryGroup": "allLogs", + "enabled": true } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "8149225552603313421" - } - }, - "parameters": { - "applicationInsightsResourceId": { - "type": "string", - "defaultValue": "" - }, - "dataCollectionEndpointResourceId": { - "type": "string", - "defaultValue": "" - }, - "logAnalyticsWorkspaceResourceId": { - "type": "string", - "defaultValue": "" - }, - "privateLinkScopeResourceId": { - "type": "string" - } - }, - "resources": [ - { - "condition": "[not(empty(parameters('applicationInsightsResourceId')))]", - "type": "Microsoft.Insights/privateLinkScopes/scopedResources", - "apiVersion": "2021-09-01", - "name": "[format('{0}/{1}', split(parameters('privateLinkScopeResourceId'), '/')[8], if(empty(parameters('applicationInsightsResourceId')), 'applicationInsights', split(parameters('applicationInsightsResourceId'), '/')[8]))]", - "properties": { - "linkedResourceId": "[parameters('applicationInsightsResourceId')]" - } - }, - { - "condition": "[not(empty(parameters('dataCollectionEndpointResourceId')))]", - "type": "Microsoft.Insights/privateLinkScopes/scopedResources", - "apiVersion": "2021-09-01", - "name": "[format('{0}/{1}', split(parameters('privateLinkScopeResourceId'), '/')[8], if(empty(parameters('dataCollectionEndpointResourceId')), 'dataCollectionEndpoint', split(parameters('dataCollectionEndpointResourceId'), '/')[8]))]", - "properties": { - "linkedResourceId": "[parameters('dataCollectionEndpointResourceId')]" - } - }, - { - "condition": "[not(empty(parameters('logAnalyticsWorkspaceResourceId')))]", - "type": "Microsoft.Insights/privateLinkScopes/scopedResources", - "apiVersion": "2021-09-01", - "name": "[format('{0}/{1}', split(parameters('privateLinkScopeResourceId'), '/')[8], if(empty(parameters('logAnalyticsWorkspaceResourceId')), 'logAnalyticsWorkspace', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[8]))]", - "properties": { - "linkedResourceId": "[parameters('logAnalyticsWorkspaceResourceId')]" - } - } - ] - } + ], + "workspaceId": "[parameters('logAnalyticsWorkspaceResourceId')]" }, "dependsOn": [ - "[resourceId('Microsoft.Insights/components', replace(parameters('namingConvention').applicationInsights, parameters('serviceToken'), variables('service')))]" + "[resourceId('Microsoft.DesktopVirtualization/hostPools', parameters('hostPoolName'))]" ] + } + ], + "outputs": { + "name": { + "type": "string", + "value": "[parameters('hostPoolName')]" + }, + "resourceId": { + "type": "string", + "value": "[resourceId('Microsoft.DesktopVirtualization/hostPools', parameters('hostPoolName'))]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-monitoring-{0}', parameters('deploymentNameSuffix')))]", + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('resourceGroupName'))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('deploy-disk-access-{0}', parameters('deploymentNameSuffix'))]", + "resourceGroup": "[parameters('resourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "hostPoolResourceId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-vdpool-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceId.value]" + }, + "location": { + "value": "[parameters('locationVirtualMachines')]" + }, + "mlzTags": { + "value": "[parameters('mlzTags')]" + }, + "namingConvention": { + "value": "[parameters('namingConvention')]" + }, + "subnetResourceId": { + "value": "[parameters('subnetResourceId')]" + }, + "tags": { + "value": "[parameters('tags')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "3145769776971011079" + } + }, + "parameters": { + "hostPoolResourceId": { + "type": "string" + }, + "location": { + "type": "string" + }, + "mlzTags": { + "type": "object" }, + "namingConvention": { + "type": "object" + }, + "subnetResourceId": { + "type": "string" + }, + "tags": { + "type": "object" + } + }, + "resources": [ { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('set-role-assignment-{0}', parameters('deploymentNameSuffix'))]", - "resourceGroup": "[parameters('resourceGroupStorage')]", + "type": "Microsoft.Compute/diskAccesses", + "apiVersion": "2021-04-01", + "name": "[parameters('namingConvention').diskAccess]", + "location": "[parameters('location')]", + "tags": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), coalesce(tryGet(parameters('tags'), 'Microsoft.Compute/diskAccesses'), createObject()), parameters('mlzTags'))]", + "properties": {} + }, + { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-04-01", + "name": "[parameters('namingConvention').diskAccessPrivateEndpoint]", + "location": "[parameters('location')]", + "tags": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), coalesce(tryGet(parameters('tags'), 'Microsoft.Network/privateEndpoints'), createObject()), parameters('mlzTags'))]", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "principalId": { - "value": "[reference(resourceId('Microsoft.Web/sites', uniqueString(replace(parameters('namingConvention').functionApp, parameters('serviceToken'), variables('service')), resourceGroup().id)), '2023-01-01', 'full').identity.principalId]" - }, - "principalType": { - "value": "ServicePrincipal" - }, - "roleDefinitionId": { - "value": "17d1049b-9a84-46fb-8f53-869881c3d3ab" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "1315528727979495191" - } - }, - "parameters": { - "principalId": { - "type": "string" - }, - "principalType": { - "type": "string" - }, - "roleDefinitionId": { - "type": "string" - } - }, - "resources": [ - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "name": "[guid(parameters('principalId'), parameters('roleDefinitionId'), resourceGroup().id)]", - "properties": { - "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitionId'))]", - "principalId": "[parameters('principalId')]", - "principalType": "[parameters('principalType')]" - } + "customNetworkInterfaceName": "[parameters('namingConvention').diskAccessNetworkInterface]", + "privateLinkServiceConnections": [ + { + "name": "[parameters('namingConvention').diskAccessPrivateEndpoint]", + "properties": { + "privateLinkServiceId": "[resourceId('Microsoft.Compute/diskAccesses', parameters('namingConvention').diskAccess)]", + "groupIds": [ + "disks" + ] } - ] + } + ], + "subnet": { + "id": "[parameters('subnetResourceId')]" } }, "dependsOn": [ - "[resourceId('Microsoft.Web/sites', uniqueString(replace(parameters('namingConvention').functionApp, parameters('serviceToken'), variables('service')), resourceGroup().id))]" + "[resourceId('Microsoft.Compute/diskAccesses', parameters('namingConvention').diskAccess)]" ] + } + ], + "outputs": { + "resourceId": { + "type": "string", + "value": "[resourceId('Microsoft.Compute/diskAccesses', parameters('namingConvention').diskAccess)]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-vdpool-{0}', parameters('deploymentNameSuffix')))]", + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('resourceGroupName'))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('deploy-policy-disks-{0}', parameters('deploymentNameSuffix'))]", + "location": "[deployment().location]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "diskAccessResourceId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-disk-access-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceId.value]" + }, + "location": { + "value": "[parameters('locationControlPlane')]" + }, + "resourceGroupName": { + "value": "[parameters('resourceGroupName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "6736401548874707618" + } + }, + "parameters": { + "diskAccessResourceId": { + "type": "string", + "defaultValue": "" + }, + "location": { + "type": "string" }, + "resourceGroupName": { + "type": "string" + } + }, + "variables": { + "parameters": "[if(not(empty(parameters('diskAccessResourceId'))), createObject('diskAccessId', createObject('type', 'String', 'metadata', createObject('displayName', 'Disk Access Resource Id', 'description', 'The resource Id of the Disk Access to associate to the managed disks.'))), createObject())]", + "operations": "[if(not(empty(parameters('diskAccessResourceId'))), createArray(createObject('operation', 'addOrReplace', 'field', 'Microsoft.Compute/disks/networkAccessPolicy', 'value', 'AllowPrivate'), createObject('operation', 'addOrReplace', 'field', 'Microsoft.Compute/disks/publicNetworkAccess', 'value', 'Disabled'), createObject('operation', 'addOrReplace', 'field', 'Microsoft.Compute/disks/diskAccessId', 'value', '[parameters(''diskAccessId'')]')), createArray(createObject('operation', 'addOrReplace', 'field', 'Microsoft.Compute/disks/networkAccessPolicy', 'value', 'DenyAll'), createObject('operation', 'addOrReplace', 'field', 'Microsoft.Compute/disks/publicNetworkAccess', 'value', 'Disabled')))]" + }, + "resources": [ { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('set-role-assignment-storage-{0}', parameters('deploymentNameSuffix'))]", + "type": "Microsoft.Authorization/policyDefinitions", + "apiVersion": "2021-06-01", + "name": "DiskNetworkAccess", "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "principalId": { - "value": "[reference(resourceId('Microsoft.Web/sites', uniqueString(replace(parameters('namingConvention').functionApp, parameters('serviceToken'), variables('service')), resourceGroup().id)), '2023-01-01', 'full').identity.principalId]" - }, - "principalType": { - "value": "ServicePrincipal" - }, - "roleDefinitionId": { - "value": "b7e6dc6d-f1e8-4753-8033-0f276bb0955b" - }, - "storageAccountName": { - "value": "[uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id)]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "6271853608623957112" - } - }, - "parameters": { - "principalId": { - "type": "string" - }, - "principalType": { - "type": "string" - }, - "roleDefinitionId": { - "type": "string" - }, - "storageAccountName": { - "type": "string" - } + "description": "Disable network access to managed disks.", + "displayName": "Disable Disk Access", + "mode": "All", + "parameters": "[variables('parameters')]", + "policyRule": { + "if": { + "field": "type", + "equals": "Microsoft.Compute/disks" }, - "resources": [ - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('storageAccountName'))]", - "name": "[guid(parameters('principalId'), parameters('roleDefinitionId'), resourceGroup().id)]", - "properties": { - "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitionId'))]", - "principalId": "[parameters('principalId')]", - "principalType": "[parameters('principalType')]" - } + "then": { + "effect": "modify", + "details": { + "roleDefinitionIds": [ + "/providers/Microsoft.Authorization/roleDefinitions/60fc6e62-5479-42d4-8bf4-67625fcc2840" + ], + "operations": "[variables('operations')]" } - ] - } - }, - "dependsOn": [ - "[resourceId('Microsoft.Web/sites', uniqueString(replace(parameters('namingConvention').functionApp, parameters('serviceToken'), variables('service')), resourceGroup().id))]", - "[resourceId('Microsoft.Storage/storageAccounts', uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id))]" - ] + } + }, + "policyType": "Custom" + } }, { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('deploy-scm-a-record-{0}', parameters('deploymentNameSuffix'))]", - "subscriptionId": "[split(variables('functionAppScmPrivateDnsZoneResourceId'), '/')[2]]", - "resourceGroup": "[split(variables('functionAppScmPrivateDnsZoneResourceId'), '/')[4]]", + "name": "assign-policy-disk-network-access", + "resourceGroup": "[parameters('resourceGroupName')]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "functionAppName": { - "value": "[uniqueString(replace(parameters('namingConvention').functionApp, parameters('serviceToken'), variables('service')), resourceGroup().id)]" + "diskAccessResourceId": { + "value": "[parameters('diskAccessResourceId')]" }, - "ipv4Address": { - "value": "[filter(reference(resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', replace(parameters('namingConvention').functionAppPrivateEndpoint, parameters('serviceToken'), variables('service')), 'default'), '2021-08-01').privateDnsZoneConfigs[0].properties.recordSets, lambda('record', equals(lambdaVariables('record').recordSetName, uniqueString(replace(parameters('namingConvention').functionApp, parameters('serviceToken'), variables('service')), resourceGroup().id))))[0].ipAddresses[0]]" + "location": { + "value": "[parameters('location')]" }, - "privateDnsZoneName": { - "value": "[split(variables('functionAppScmPrivateDnsZoneResourceId'), '/')[8]]" + "policyDefinitionId": { + "value": "[subscriptionResourceId('Microsoft.Authorization/policyDefinitions', 'DiskNetworkAccess')]" + }, + "policyDisplayName": { + "value": "[reference(subscriptionResourceId('Microsoft.Authorization/policyDefinitions', 'DiskNetworkAccess'), '2021-06-01').displayName]" + }, + "policyName": { + "value": "[reference(subscriptionResourceId('Microsoft.Authorization/policyDefinitions', 'DiskNetworkAccess'), '2021-06-01').displayName]" } }, "template": { @@ -8759,112 +7007,264 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "9646048614586675609" + "version": "0.31.92.45157", + "templateHash": "1275953978872597894" } }, "parameters": { - "functionAppName": { + "diskAccessResourceId": { "type": "string" }, - "ipv4Address": { + "location": { "type": "string" }, - "privateDnsZoneName": { + "policyDefinitionId": { + "type": "string" + }, + "policyDisplayName": { + "type": "string" + }, + "policyName": { "type": "string" } }, "resources": [ { - "type": "Microsoft.Network/privateDnsZones/A", - "apiVersion": "2020-06-01", - "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('functionAppName'))]", + "type": "Microsoft.Authorization/policyAssignments", + "apiVersion": "2022-06-01", + "name": "[parameters('policyName')]", + "location": "[parameters('location')]", + "identity": { + "type": "SystemAssigned" + }, "properties": { - "aRecords": [ - { - "ipv4Address": "[parameters('ipv4Address')]" - } - ], - "ttl": 3600 + "displayName": "[parameters('policyDisplayName')]", + "policyDefinitionId": "[parameters('policyDefinitionId')]", + "parameters": "[if(not(empty(parameters('diskAccessResourceId'))), createObject('diskAccessId', createObject('value', parameters('diskAccessResourceId'))), createObject())]" } } ] } }, "dependsOn": [ - "[resourceId('Microsoft.Web/sites', uniqueString(replace(parameters('namingConvention').functionApp, parameters('serviceToken'), variables('service')), resourceGroup().id))]", - "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', replace(parameters('namingConvention').functionAppPrivateEndpoint, parameters('serviceToken'), variables('service')), 'default')]" + "[subscriptionResourceId('Microsoft.Authorization/policyDefinitions', 'DiskNetworkAccess')]" ] } ], "outputs": { - "functionAppName": { + "policyDefinitionId": { "type": "string", - "value": "[uniqueString(replace(parameters('namingConvention').functionApp, parameters('serviceToken'), variables('service')), resourceGroup().id)]" + "value": "[subscriptionResourceId('Microsoft.Authorization/policyDefinitions', 'DiskNetworkAccess')]" + }, + "policyDisplayName": { + "type": "string", + "value": "[reference(subscriptionResourceId('Microsoft.Authorization/policyDefinitions', 'DiskNetworkAccess'), '2021-06-01').displayName]" } } } }, "dependsOn": [ - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-monitoring-{0}', parameters('deploymentNameSuffix')))]" + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-disk-access-{0}', parameters('deploymentNameSuffix')))]", + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('resourceGroupName'))]" ] }, { - "condition": "[parameters('recoveryServices')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('deploy-rsv-{0}', parameters('deploymentNameSuffix'))]", - "resourceGroup": "[parameters('resourceGroupManagement')]", + "name": "[format('deploy-id-deployment-{0}', parameters('deploymentNameSuffix'))]", + "resourceGroup": "[parameters('resourceGroupName')]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "azureBlobsPrivateDnsZoneResourceId": { - "value": "[format('{0}{1}', parameters('privateDnsZoneResourceIdPrefix'), filter(parameters('privateDnsZones'), lambda('name', contains(lambdaVariables('name'), 'blob')))[0])]" + "location": { + "value": "[parameters('locationVirtualMachines')]" }, - "azureQueueStoragePrivateDnsZoneResourceId": { - "value": "[format('{0}{1}', parameters('privateDnsZoneResourceIdPrefix'), filter(parameters('privateDnsZones'), lambda('name', contains(lambdaVariables('name'), 'queue')))[0])]" + "name": { + "value": "[replace(variables('userAssignedIdentityNamePrefix'), parameters('serviceToken'), 'deployment')]" }, - "deployFslogix": { - "value": "[parameters('deployFslogix')]" + "tags": { + "value": "[union(createObject('cm-resource-parent', reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-vdpool-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceId.value), coalesce(tryGet(parameters('tags'), 'Microsoft.ManagedIdentity/userAssignedIdentities'), createObject()), parameters('mlzTags'))]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "10766099399924390355" + } }, - "hostPoolName": { - "value": "[variables('hostPoolName')]" + "parameters": { + "location": { + "type": "string" + }, + "name": { + "type": "string" + }, + "tags": { + "type": "object" + } }, - "location": { - "value": "[parameters('locationVirtualMachines')]" + "resources": [ + { + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2018-11-30", + "name": "[parameters('name')]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]" + } + ], + "outputs": { + "clientId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('name')), '2018-11-30').clientId]" + }, + "resourceId": { + "type": "string", + "value": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('name'))]" + }, + "principalId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', parameters('name')), '2018-11-30').principalId]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-vdpool-{0}', parameters('deploymentNameSuffix')))]", + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('resourceGroupName'))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('assign-role-mgmt-{0}', parameters('deploymentNameSuffix'))]", + "resourceGroup": "[parameters('resourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "principalId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-id-deployment-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.principalId.value]" }, - "mlzTags": { - "value": "[parameters('mlzTags')]" + "principalType": { + "value": "ServicePrincipal" }, - "recoveryServicesPrivateDnsZoneResourceId": { - "value": "[format('{0}{1}', parameters('privateDnsZoneResourceIdPrefix'), filter(parameters('privateDnsZones'), lambda('name', startsWith(lambdaVariables('name'), format('privatelink.{0}.backup.windowsazure', parameters('recoveryServicesGeo')))))[0])]" + "roleDefinitionId": { + "value": "082f0a83-3be5-4ba1-904c-961cca79b387" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "10174155731090308034" + } }, - "recoveryServicesVaultName": { - "value": "[parameters('namingConvention').recoveryServicesVault]" + "parameters": { + "principalId": { + "type": "string" + }, + "principalType": { + "type": "string" + }, + "roleDefinitionId": { + "type": "string" + } }, - "recoveryServicesVaultNetworkInterfaceName": { - "value": "[parameters('namingConvention').recoveryServicesVaultNetworkInterface]" + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(parameters('principalId'), parameters('roleDefinitionId'), resourceGroup().id)]", + "properties": { + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitionId'))]", + "principalId": "[parameters('principalId')]", + "principalType": "[parameters('principalType')]" + } + } + ] + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-id-deployment-{0}', parameters('deploymentNameSuffix')))]", + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('resourceGroupName'))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('deploy-mgmt-vm-{0}', parameters('deploymentNameSuffix'))]", + "resourceGroup": "[parameters('resourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "deploymentUserAssignedIdentityPrincipalId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-id-deployment-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.principalId.value]" }, - "recoveryServicesVaultPrivateEndpointName": { - "value": "[parameters('namingConvention').recoveryServicesVaultPrivateEndpoint]" + "deploymentUserAssignedIdentityResourceId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-id-deployment-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceId.value]" }, - "resourceGroupControlPlane": { - "value": "[parameters('resourceGroupControlPlane')]" + "diskEncryptionSetResourceId": { + "value": "[parameters('diskEncryptionSetResourceId')]" }, - "storageService": { - "value": "[parameters('storageService')]" + "diskName": { + "value": "[replace(parameters('namingConvention').virtualMachineDisk, parameters('serviceToken'), 'mgt')]" + }, + "diskSku": { + "value": "[parameters('diskSku')]" + }, + "domainJoinPassword": { + "value": "[parameters('domainJoinPassword')]" + }, + "domainJoinUserPrincipalName": { + "value": "[parameters('domainJoinUserPrincipalName')]" + }, + "domainName": { + "value": "[parameters('domainName')]" + }, + "hostPoolResourceId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-vdpool-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceId.value]" + }, + "location": { + "value": "[parameters('locationVirtualMachines')]" + }, + "mlzTags": { + "value": "[parameters('mlzTags')]" + }, + "networkInterfaceName": { + "value": "[replace(parameters('namingConvention').virtualMachineNetworkInterface, parameters('serviceToken'), 'mgt')]" }, - "subnetId": { + "organizationalUnitPath": { + "value": "[parameters('organizationalUnitPath')]" + }, + "subnetResourceId": { "value": "[parameters('subnetResourceId')]" }, "tags": { "value": "[parameters('tags')]" }, - "timeZone": { - "value": "[parameters('timeZone')]" + "virtualMachineName": { + "value": "[replace(parameters('namingConvention').virtualMachine, parameters('serviceToken'), 'mgt')]" + }, + "virtualMachinePassword": { + "value": "[parameters('virtualMachinePassword')]" + }, + "virtualMachineUsername": { + "value": "[parameters('virtualMachineUsername')]" } }, "template": { @@ -8873,590 +7273,874 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "12830230504350465325" + "version": "0.31.92.45157", + "templateHash": "3320903441430575822" } }, "parameters": { - "azureBlobsPrivateDnsZoneResourceId": { + "deploymentUserAssignedIdentityPrincipalId": { "type": "string" }, - "azureQueueStoragePrivateDnsZoneResourceId": { + "deploymentUserAssignedIdentityResourceId": { "type": "string" }, - "deployFslogix": { - "type": "bool" + "diskEncryptionSetResourceId": { + "type": "string" }, - "hostPoolName": { + "diskName": { "type": "string" }, - "location": { + "diskSku": { "type": "string" }, - "mlzTags": { - "type": "object" + "domainJoinPassword": { + "type": "securestring" }, - "recoveryServicesPrivateDnsZoneResourceId": { + "domainJoinUserPrincipalName": { "type": "string" }, - "recoveryServicesVaultName": { + "domainName": { "type": "string" }, - "recoveryServicesVaultNetworkInterfaceName": { + "hostPoolResourceId": { "type": "string" }, - "recoveryServicesVaultPrivateEndpointName": { + "location": { "type": "string" }, - "resourceGroupControlPlane": { + "mlzTags": { + "type": "object" + }, + "networkInterfaceName": { "type": "string" }, - "storageService": { + "organizationalUnitPath": { "type": "string" }, - "subnetId": { + "subnetResourceId": { "type": "string" }, "tags": { "type": "object" }, - "timeZone": { + "timestamp": { + "type": "string", + "defaultValue": "[utcNow('yyyyMMddhhmmss')]" + }, + "virtualMachineName": { + "type": "string" + }, + "virtualMachinePassword": { + "type": "securestring" + }, + "virtualMachineUsername": { "type": "string" } }, + "variables": { + "tagsVirtualMachines": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), coalesce(tryGet(parameters('tags'), 'Microsoft.Compute/virtualMachines'), createObject()), parameters('mlzTags'))]" + }, "resources": [ { - "type": "Microsoft.RecoveryServices/vaults", - "apiVersion": "2022-03-01", - "name": "[parameters('recoveryServicesVaultName')]", - "location": "[parameters('location')]", - "tags": "[union(createObject('cm-resource-parent', format('{0}}}/resourceGroups/{1}/providers/Microsoft.DesktopVirtualization/hostpools/{2}', subscription().id, parameters('resourceGroupControlPlane'), parameters('hostPoolName'))), if(contains(parameters('tags'), 'Microsoft.RecoveryServices/vaults'), parameters('tags')['Microsoft.RecoveryServices/vaults'], createObject()), parameters('mlzTags'))]", - "sku": { - "name": "RS0", - "tier": "Standard" - }, - "properties": {} - }, - { - "condition": "[and(parameters('deployFslogix'), equals(parameters('storageService'), 'AzureFiles'))]", - "type": "Microsoft.RecoveryServices/vaults/backupPolicies", - "apiVersion": "2022-03-01", - "name": "[format('{0}/{1}', parameters('recoveryServicesVaultName'), 'AvdPolicyStorage')]", - "location": "[parameters('location')]", - "tags": "[union(createObject('cm-resource-parent', format('{0}}}/resourceGroups/{1}/providers/Microsoft.DesktopVirtualization/hostpools/{2}', subscription().id, parameters('resourceGroupControlPlane'), parameters('hostPoolName'))), if(contains(parameters('tags'), 'Microsoft.RecoveryServices/vaults'), parameters('tags')['Microsoft.RecoveryServices/vaults'], createObject()), parameters('mlzTags'))]", - "properties": { - "backupManagementType": "AzureStorage", - "schedulePolicy": { - "scheduleRunFrequency": "Daily", - "scheduleRunTimes": [ - "23:00" - ], - "schedulePolicyType": "SimpleSchedulePolicy" - }, - "retentionPolicy": { - "retentionPolicyType": "LongTermRetentionPolicy", - "dailySchedule": { - "retentionTimes": [ - "23:00" - ], - "retentionDuration": { - "count": 30, - "durationType": "Days" - } - } - }, - "timeZone": "[parameters('timeZone')]", - "workLoadType": "AzureFileShare" - }, - "dependsOn": [ - "[resourceId('Microsoft.RecoveryServices/vaults', parameters('recoveryServicesVaultName'))]" - ] - }, - { - "condition": "[not(parameters('deployFslogix'))]", - "type": "Microsoft.RecoveryServices/vaults/backupPolicies", - "apiVersion": "2022-03-01", - "name": "[format('{0}/{1}', parameters('recoveryServicesVaultName'), 'AvdPolicyVm')]", - "location": "[parameters('location')]", - "tags": "[union(createObject('cm-resource-parent', format('{0}}}/resourceGroups/{1}/providers/Microsoft.DesktopVirtualization/hostpools/{2}', subscription().id, parameters('resourceGroupControlPlane'), parameters('hostPoolName'))), if(contains(parameters('tags'), 'Microsoft.RecoveryServices/vaults'), parameters('tags')['Microsoft.RecoveryServices/vaults'], createObject()), parameters('mlzTags'))]", - "properties": { - "backupManagementType": "AzureIaasVM", - "instantRpRetentionRangeInDays": 2, - "policyType": "V2", - "retentionPolicy": { - "retentionPolicyType": "LongTermRetentionPolicy", - "dailySchedule": { - "retentionTimes": [ - "23:00" - ], - "retentionDuration": { - "count": 30, - "durationType": "Days" - } - } - }, - "schedulePolicy": { - "schedulePolicyType": "SimpleSchedulePolicyV2", - "scheduleRunFrequency": "Daily", - "dailySchedule": { - "scheduleRunTimes": [ - "23:00" - ] - } - }, - "timeZone": "[parameters('timeZone')]" - }, - "dependsOn": [ - "[resourceId('Microsoft.RecoveryServices/vaults', parameters('recoveryServicesVaultName'))]" - ] - }, - { - "type": "Microsoft.Network/privateEndpoints", - "apiVersion": "2023-04-01", - "name": "[parameters('recoveryServicesVaultPrivateEndpointName')]", + "type": "Microsoft.Network/networkInterfaces", + "apiVersion": "2020-05-01", + "name": "[parameters('networkInterfaceName')]", "location": "[parameters('location')]", - "tags": "[union(createObject('cm-resource-parent', format('{0}}}/resourceGroups/{1}/providers/Microsoft.DesktopVirtualization/hostpools/{2}', subscription().id, parameters('resourceGroupControlPlane'), parameters('hostPoolName'))), if(contains(parameters('tags'), 'Microsoft.Network/privateEndpoints'), parameters('tags')['Microsoft.Network/privateEndpoints'], createObject()), parameters('mlzTags'))]", + "tags": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), coalesce(tryGet(parameters('tags'), 'Microsoft.Network/networkInterfaces'), createObject()), parameters('mlzTags'))]", "properties": { - "customNetworkInterfaceName": "[parameters('recoveryServicesVaultNetworkInterfaceName')]", - "privateLinkServiceConnections": [ + "ipConfigurations": [ { - "name": "[parameters('recoveryServicesVaultPrivateEndpointName')]", + "name": "ipconfig", "properties": { - "privateLinkServiceId": "[resourceId('Microsoft.RecoveryServices/vaults', parameters('recoveryServicesVaultName'))]", - "groupIds": [ - "AzureBackup" - ] + "privateIPAllocationMethod": "Dynamic", + "subnet": { + "id": "[parameters('subnetResourceId')]" + }, + "primary": true, + "privateIPAddressVersion": "IPv4" } } ], - "subnet": { - "id": "[parameters('subnetId')]" - } - }, - "dependsOn": [ - "[resourceId('Microsoft.RecoveryServices/vaults', parameters('recoveryServicesVaultName'))]" - ] + "enableAcceleratedNetworking": false, + "enableIPForwarding": false + } }, { - "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2021-08-01", - "name": "[format('{0}/{1}', parameters('recoveryServicesVaultPrivateEndpointName'), parameters('recoveryServicesVaultName'))]", + "type": "Microsoft.Compute/virtualMachines", + "apiVersion": "2021-11-01", + "name": "[parameters('virtualMachineName')]", + "location": "[parameters('location')]", + "tags": "[variables('tagsVirtualMachines')]", "properties": { - "privateDnsZoneConfigs": [ - { - "name": "[replace(parameters('recoveryServicesPrivateDnsZoneResourceId'), '.', '-')]", - "properties": { - "privateDnsZoneId": "[parameters('recoveryServicesPrivateDnsZoneResourceId')]" - } + "hardwareProfile": { + "vmSize": "Standard_B2s" + }, + "storageProfile": { + "imageReference": { + "publisher": "MicrosoftWindowsServer", + "offer": "WindowsServer", + "sku": "2019-datacenter-core-g2", + "version": "latest" }, - { - "name": "[replace(parameters('azureQueueStoragePrivateDnsZoneResourceId'), '.', '-')]", - "properties": { - "privateDnsZoneId": "[parameters('azureQueueStoragePrivateDnsZoneResourceId')]" - } + "osDisk": { + "deleteOption": "Delete", + "osType": "Windows", + "createOption": "FromImage", + "caching": "None", + "managedDisk": { + "diskEncryptionSet": { + "id": "[parameters('diskEncryptionSetResourceId')]" + }, + "storageAccountType": "[parameters('diskSku')]" + }, + "name": "[parameters('diskName')]" }, - { - "name": "[replace(parameters('azureBlobsPrivateDnsZoneResourceId'), '.', '-')]", - "properties": { - "privateDnsZoneId": "[parameters('azureBlobsPrivateDnsZoneResourceId')]" + "dataDisks": [] + }, + "osProfile": { + "computerName": "[parameters('virtualMachineName')]", + "adminUsername": "[parameters('virtualMachineUsername')]", + "adminPassword": "[parameters('virtualMachinePassword')]", + "windowsConfiguration": { + "provisionVMAgent": true, + "enableAutomaticUpdates": false + }, + "secrets": [], + "allowExtensionOperations": true + }, + "networkProfile": { + "networkInterfaces": [ + { + "id": "[resourceId('Microsoft.Network/networkInterfaces', parameters('networkInterfaceName'))]", + "properties": { + "deleteOption": "Delete" + } } + ] + }, + "securityProfile": { + "uefiSettings": { + "secureBootEnabled": true, + "vTpmEnabled": true + }, + "securityType": "TrustedLaunch", + "encryptionAtHost": true + }, + "diagnosticsProfile": { + "bootDiagnostics": { + "enabled": false } - ] + }, + "licenseType": "Windows_Server" + }, + "identity": { + "type": "SystemAssigned, UserAssigned", + "userAssignedIdentities": { + "[format('{0}', parameters('deploymentUserAssignedIdentityResourceId'))]": {} + } }, "dependsOn": [ - "[resourceId('Microsoft.Network/privateEndpoints', parameters('recoveryServicesVaultPrivateEndpointName'))]", - "[resourceId('Microsoft.RecoveryServices/vaults', parameters('recoveryServicesVaultName'))]" - ] - } - ], - "outputs": { - "name": { - "type": "string", - "value": "[parameters('recoveryServicesVaultName')]" - } - } - } - } - } - ], - "outputs": { - "dataCollectionRuleResourceId": { - "type": "string", - "value": "[if(parameters('enableAvdInsights'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-monitoring-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.dataCollectionRuleResourceId.value, '')]" - }, - "deploymentUserAssignedIdentityClientId": { - "type": "string", - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-id-deployment-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.clientId.value]" - }, - "deploymentUserAssignedIdentityPrincipalId": { - "type": "string", - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-id-deployment-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.principalId.value]" - }, - "deploymentUserAssignedIdentityResourceId": { - "type": "string", - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-id-deployment-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceId.value]" - }, - "functionAppName": { - "type": "string", - "value": "[if(equals(parameters('fslogixStorageService'), 'AzureFiles Premium'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-function-app-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.functionAppName.value, '')]" - }, - "logAnalyticsWorkspaceName": { - "type": "string", - "value": "[if(or(parameters('enableApplicationInsights'), parameters('enableAvdInsights')), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-monitoring-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.logAnalyticsWorkspaceName.value, '')]" - }, - "logAnalyticsWorkspaceResourceId": { - "type": "string", - "value": "[if(or(parameters('enableApplicationInsights'), parameters('enableAvdInsights')), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-monitoring-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.logAnalyticsWorkspaceResourceId.value, '')]" - }, - "recoveryServicesVaultName": { - "type": "string", - "value": "[if(parameters('recoveryServices'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-rsv-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.name.value, '')]" - }, - "virtualMachineName": { - "type": "string", - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-mgmt-vm-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.name.value]" - }, - "virtualMachineResourceId": { - "type": "string", - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-mgmt-vm-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceId.value]" - } - } - } - }, - "dependsOn": [ - "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', variables('resourceGroupServices')[0], parameters('deploymentNameSuffix')))]", - "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', variables('resourceGroupServices')[1], parameters('deploymentNameSuffix')))]", - "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', variables('resourceGroupServices')[2], parameters('deploymentNameSuffix')))]", - "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix')))]" - ] - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('deploy-control-plane-{0}', parameters('deploymentNameSuffix'))]", - "location": "[deployment().location]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "activeDirectorySolution": { - "value": "[parameters('activeDirectorySolution')]" - }, - "avdPrivateDnsZoneResourceId": { - "value": "[format('{0}{1}', variables('privateDnsZoneResourceIdPrefix'), filter(reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.privateDnsZones.value, lambda('name', startsWith(lambdaVariables('name'), 'privatelink.wvd')))[0])]" - }, - "customImageId": { - "value": "[variables('customImageId')]" - }, - "customRdpProperty": { - "value": "[parameters('customRdpProperty')]" - }, - "deploymentNameSuffix": { - "value": "[parameters('deploymentNameSuffix')]" - }, - "deploymentUserAssignedIdentityClientId": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.deploymentUserAssignedIdentityClientId.value]" - }, - "deploymentUserAssignedIdentityPrincipalId": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.deploymentUserAssignedIdentityPrincipalId.value]" - }, - "desktopFriendlyName": "[if(empty(parameters('desktopFriendlyName')), createObject('value', string(parameters('stampIndex'))), createObject('value', parameters('desktopFriendlyName')))]", - "diskSku": { - "value": "[parameters('diskSku')]" - }, - "domainName": { - "value": "[parameters('domainName')]" - }, - "enableAvdInsights": { - "value": "[parameters('enableAvdInsights')]" - }, - "hostPoolPublicNetworkAccess": { - "value": "[parameters('hostPoolPublicNetworkAccess')]" - }, - "hostPoolType": { - "value": "[parameters('hostPoolType')]" - }, - "imageOffer": { - "value": "[parameters('imageOffer')]" - }, - "imagePublisher": { - "value": "[parameters('imagePublisher')]" - }, - "imageSku": { - "value": "[parameters('imageSku')]" - }, - "imageVersionResourceId": { - "value": "[parameters('imageVersionResourceId')]" - }, - "locationControlPlane": { - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Network/virtualNetworks', split(parameters('hubVirtualNetworkResourceId'), '/')[8]), '2023-11-01', 'full').location]" - }, - "locationVirtualMachines": { - "value": "[parameters('locationVirtualMachines')]" - }, - "logAnalyticsWorkspaceResourceId": "[if(parameters('enableAvdInsights'), createObject('value', reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.logAnalyticsWorkspaceResourceId.value), createObject('value', ''))]", - "managementVirtualMachineName": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.virtualMachineName.value]" - }, - "maxSessionLimit": { - "value": "[mul(parameters('usersPerCore'), parameters('virtualMachineVirtualCpuCount'))]" - }, - "mlzTags": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.mlzTags.value]" - }, - "namingConvention": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-cp-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.names.value]" - }, - "resourceGroupControlPlane": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', variables('resourceGroupServices')[0], parameters('deploymentNameSuffix'))), '2022-09-01').outputs.name.value]" - }, - "resourceGroupManagement": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', variables('resourceGroupServices')[2], parameters('deploymentNameSuffix'))), '2022-09-01').outputs.name.value]" - }, - "roleDefinitions": { - "value": "[variables('roleDefinitions')]" - }, - "securityPrincipalObjectIds": { - "value": "[map(parameters('securityPrincipals'), lambda('item', lambdaVariables('item').objectId))]" - }, - "serviceToken": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-cp-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value.service]" - }, - "sessionHostNamePrefix": { - "value": "[replace(reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.namingConvention.value.virtualMachine, reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value.service, '')]" - }, - "subnetResourceId": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.subnets.value[1].id]" - }, - "tags": { - "value": "[parameters('tags')]" - }, - "validationEnvironment": { - "value": "[parameters('validationEnvironment')]" - }, - "virtualMachineSize": { - "value": "[parameters('virtualMachineSize')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2018-05-01/subscriptionDeploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "16583821302504592892" - } - }, - "parameters": { - "activeDirectorySolution": { - "type": "string" - }, - "avdPrivateDnsZoneResourceId": { - "type": "string" - }, - "customImageId": { - "type": "string" - }, - "customRdpProperty": { - "type": "string" - }, - "deploymentNameSuffix": { - "type": "string" - }, - "deploymentUserAssignedIdentityClientId": { - "type": "string" - }, - "deploymentUserAssignedIdentityPrincipalId": { - "type": "string" - }, - "desktopFriendlyName": { - "type": "string" - }, - "diskSku": { - "type": "string" - }, - "domainName": { - "type": "string" - }, - "enableAvdInsights": { - "type": "bool" - }, - "hostPoolPublicNetworkAccess": { - "type": "string" - }, - "hostPoolType": { - "type": "string" - }, - "imageOffer": { - "type": "string" - }, - "imagePublisher": { - "type": "string" - }, - "imageSku": { - "type": "string" - }, - "imageVersionResourceId": { - "type": "string" - }, - "locationControlPlane": { - "type": "string" - }, - "locationVirtualMachines": { - "type": "string" - }, - "logAnalyticsWorkspaceResourceId": { - "type": "string" - }, - "managementVirtualMachineName": { - "type": "string" - }, - "maxSessionLimit": { - "type": "int" - }, - "mlzTags": { - "type": "object" - }, - "namingConvention": { - "type": "object" - }, - "resourceGroupControlPlane": { - "type": "string" - }, - "resourceGroupManagement": { - "type": "string" - }, - "roleDefinitions": { - "type": "object" - }, - "securityPrincipalObjectIds": { - "type": "array" - }, - "serviceToken": { - "type": "string" - }, - "sessionHostNamePrefix": { - "type": "string" - }, - "subnetResourceId": { - "type": "string" + "[resourceId('Microsoft.Network/networkInterfaces', parameters('networkInterfaceName'))]" + ] + }, + { + "type": "Microsoft.Compute/virtualMachines/extensions", + "apiVersion": "2021-03-01", + "name": "[format('{0}/{1}', parameters('virtualMachineName'), 'IaaSAntimalware')]", + "location": "[parameters('location')]", + "tags": "[variables('tagsVirtualMachines')]", + "properties": { + "publisher": "Microsoft.Azure.Security", + "type": "IaaSAntimalware", + "typeHandlerVersion": "1.3", + "autoUpgradeMinorVersion": true, + "enableAutomaticUpgrade": false, + "settings": { + "AntimalwareEnabled": true, + "RealtimeProtectionEnabled": "true", + "ScheduledScanSettings": { + "isEnabled": "true", + "day": "7", + "time": "120", + "scanType": "Quick" + }, + "Exclusions": {} + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Compute/virtualMachines', parameters('virtualMachineName'))]" + ] + }, + { + "type": "Microsoft.Compute/virtualMachines/extensions", + "apiVersion": "2021-03-01", + "name": "[format('{0}/{1}', parameters('virtualMachineName'), 'GuestAttestation')]", + "location": "[parameters('location')]", + "properties": { + "publisher": "Microsoft.Azure.Security.WindowsAttestation", + "type": "GuestAttestation", + "typeHandlerVersion": "1.0", + "autoUpgradeMinorVersion": true, + "settings": { + "AttestationConfig": { + "MaaSettings": { + "maaEndpoint": "", + "maaTenantName": "GuestAttestation" + }, + "AscSettings": { + "ascReportingEndpoint": "", + "ascReportingFrequency": "" + }, + "useCustomToken": "false", + "disableAlerts": "false" + } + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Compute/virtualMachines', parameters('virtualMachineName'))]" + ] + }, + { + "type": "Microsoft.Compute/virtualMachines/extensions", + "apiVersion": "2019-07-01", + "name": "[format('{0}/{1}', parameters('virtualMachineName'), 'JsonADDomainExtension')]", + "location": "[parameters('location')]", + "tags": "[variables('tagsVirtualMachines')]", + "properties": { + "forceUpdateTag": "[parameters('timestamp')]", + "publisher": "Microsoft.Compute", + "type": "JsonADDomainExtension", + "typeHandlerVersion": "1.3", + "autoUpgradeMinorVersion": true, + "settings": { + "Name": "[parameters('domainName')]", + "Options": "3", + "OUPath": "[parameters('organizationalUnitPath')]", + "Restart": "true", + "User": "[parameters('domainJoinUserPrincipalName')]" + }, + "protectedSettings": { + "Password": "[parameters('domainJoinPassword')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Compute/virtualMachines', parameters('virtualMachineName'))]" + ] + }, + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Compute/virtualMachines/{0}', parameters('virtualMachineName'))]", + "name": "[guid(parameters('deploymentUserAssignedIdentityPrincipalId'), 'a959dbd1-f747-45e3-8ba6-dd80f235f97c', resourceId('Microsoft.Compute/virtualMachines', parameters('virtualMachineName')))]", + "properties": { + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', 'a959dbd1-f747-45e3-8ba6-dd80f235f97c')]", + "principalId": "[parameters('deploymentUserAssignedIdentityPrincipalId')]", + "principalType": "ServicePrincipal" + }, + "dependsOn": [ + "[resourceId('Microsoft.Compute/virtualMachines', parameters('virtualMachineName'))]" + ] + } + ], + "outputs": { + "name": { + "type": "string", + "value": "[parameters('virtualMachineName')]" + }, + "resourceId": { + "type": "string", + "value": "[resourceId('Microsoft.Compute/virtualMachines', parameters('virtualMachineName'))]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-id-deployment-{0}', parameters('deploymentNameSuffix')))]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-vdpool-{0}', parameters('deploymentNameSuffix')))]", + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('resourceGroupName'))]" + ] }, - "tags": { - "type": "object" + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('deploy-vdag-{0}', parameters('deploymentNameSuffix'))]", + "resourceGroup": "[parameters('resourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "deploymentNameSuffix": { + "value": "[parameters('deploymentNameSuffix')]" + }, + "deploymentUserAssignedIdentityClientId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-id-deployment-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.clientId.value]" + }, + "desktopApplicationGroupName": { + "value": "[parameters('namingConvention').applicationGroup]" + }, + "hostPoolResourceId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-vdpool-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceId.value]" + }, + "locationControlPlane": { + "value": "[parameters('locationControlPlane')]" + }, + "locationVirtualMachines": { + "value": "[parameters('locationVirtualMachines')]" + }, + "mlzTags": { + "value": "[parameters('mlzTags')]" + }, + "securityPrincipalObjectIds": { + "value": "[parameters('securityPrincipalObjectIds')]" + }, + "desktopFriendlyName": { + "value": "[parameters('desktopFriendlyName')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "virtualMachineName": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-mgmt-vm-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.name.value]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "15585962471976115155" + } + }, + "parameters": { + "deploymentNameSuffix": { + "type": "string" + }, + "deploymentUserAssignedIdentityClientId": { + "type": "string" + }, + "desktopApplicationGroupName": { + "type": "string" + }, + "desktopFriendlyName": { + "type": "string" + }, + "hostPoolResourceId": { + "type": "string" + }, + "locationControlPlane": { + "type": "string" + }, + "locationVirtualMachines": { + "type": "string" + }, + "mlzTags": { + "type": "object" + }, + "securityPrincipalObjectIds": { + "type": "array" + }, + "tags": { + "type": "object" + }, + "virtualMachineName": { + "type": "string" + } + }, + "variables": { + "$fxv#0": "Param(\r\n [string]$ApplicationGroupName,\r\n [string]$FriendlyName,\r\n [string]$ResourceGroupName,\r\n [string]$ResourceManagerUri,\r\n [string]$SubscriptionId,\r\n [string]$UserAssignedIdentityClientId\r\n)\r\n\r\n$ErrorActionPreference = 'Stop'\r\n$WarningPreference = 'SilentlyContinue'\r\n\r\n# Wait for role assignment propagation\r\nStart-Sleep -Seconds 30\r\n\r\n# Fix the resource manager URI since only AzureCloud contains a trailing slash\r\n$ResourceManagerUriFixed = if ($ResourceManagerUri[-1] -eq '/') {$ResourceManagerUri} else {$ResourceManagerUri + '/'}\r\n\r\n# Get an access token for Azure resources\r\n$AzureManagementAccessToken = (Invoke-RestMethod `\r\n -Headers @{Metadata=\"true\"} `\r\n -Uri $('http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=' + $ResourceManagerUriFixed + '&client_id=' + $UserAssignedIdentityClientId)).access_token\r\n\r\n# Set header for Azure Management API\r\n$AzureManagementHeader = @{\r\n 'Content-Type'='application/json'\r\n 'Authorization'='Bearer ' + $AzureManagementAccessToken\r\n}\r\n\r\n# Update the friendly name on the session desktop\r\nInvoke-RestMethod `\r\n -Body (@{properties = @{friendlyName = $FriendlyName.Replace('\"', '')}} | ConvertTo-Json) `\r\n -Headers $AzureManagementHeader `\r\n -Method 'PATCH' `\r\n -Uri $($ResourceManagerUriFixed + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $ResourceGroupName + '/providers/Microsoft.DesktopVirtualization/applicationGroups/' + $ApplicationGroupName + '/desktops/SessionDesktop?api-version=2022-02-10-preview') | Out-Null" + }, + "resources": [ + { + "type": "Microsoft.DesktopVirtualization/applicationGroups", + "apiVersion": "2023-09-05", + "name": "[parameters('desktopApplicationGroupName')]", + "location": "[parameters('locationControlPlane')]", + "tags": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), coalesce(tryGet(parameters('tags'), 'Microsoft.DesktopVirtualization/applicationGroups'), createObject()), parameters('mlzTags'))]", + "properties": { + "hostPoolArmPath": "[parameters('hostPoolResourceId')]", + "applicationGroupType": "Desktop" + } + }, + { + "copy": { + "name": "roleAssignment_ApplicationGroup", + "count": "[length(range(0, length(parameters('securityPrincipalObjectIds'))))]" + }, + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.DesktopVirtualization/applicationGroups/{0}', parameters('desktopApplicationGroupName'))]", + "name": "[guid(parameters('securityPrincipalObjectIds')[range(0, length(parameters('securityPrincipalObjectIds')))[copyIndex()]], '1d18fff3-a72a-46b5-b4a9-0b38a3cd7e63', resourceId('Microsoft.DesktopVirtualization/applicationGroups', parameters('desktopApplicationGroupName')))]", + "properties": { + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', '1d18fff3-a72a-46b5-b4a9-0b38a3cd7e63')]", + "principalId": "[parameters('securityPrincipalObjectIds')[range(0, length(parameters('securityPrincipalObjectIds')))[copyIndex()]]]" + }, + "dependsOn": [ + "[resourceId('Microsoft.DesktopVirtualization/applicationGroups', parameters('desktopApplicationGroupName'))]" + ] + }, + { + "condition": "[not(empty(parameters('desktopFriendlyName')))]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('deploy-vdapp-friendly-name-{0}', parameters('deploymentNameSuffix'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('locationVirtualMachines')]" + }, + "name": { + "value": "Update-AvdDesktop" + }, + "parameters": { + "value": [ + { + "name": "ApplicationGroupName", + "value": "[parameters('desktopApplicationGroupName')]" + }, + { + "name": "FriendlyName", + "value": "[parameters('desktopFriendlyName')]" + }, + { + "name": "ResourceGroupName", + "value": "[resourceGroup().name]" + }, + { + "name": "ResourceManagerUri", + "value": "[environment().resourceManager]" + }, + { + "name": "SubscriptionId", + "value": "[subscription().subscriptionId]" + }, + { + "name": "UserAssignedIdentityClientId", + "value": "[parameters('deploymentUserAssignedIdentityClientId')]" + } + ] + }, + "script": { + "value": "[variables('$fxv#0')]" + }, + "tags": { + "value": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), coalesce(tryGet(parameters('tags'), 'Microsoft.Compute/virtualMachines'), createObject()), parameters('mlzTags'))]" + }, + "virtualMachineName": { + "value": "[parameters('virtualMachineName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "6896852651895359166" + } + }, + "parameters": { + "asyncExecution": { + "type": "bool", + "defaultValue": false + }, + "location": { + "type": "string" + }, + "name": { + "type": "string" + }, + "parameters": { + "type": "array", + "defaultValue": [] + }, + "script": { + "type": "string" + }, + "tags": { + "type": "object" + }, + "treatFailureAsDeploymentFailure": { + "type": "bool", + "defaultValue": true + }, + "virtualMachineName": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Compute/virtualMachines/runCommands", + "apiVersion": "2023-09-01", + "name": "[format('{0}/{1}', parameters('virtualMachineName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "asyncExecution": "[parameters('asyncExecution')]", + "parameters": "[parameters('parameters')]", + "source": { + "script": "[parameters('script')]" + }, + "treatFailureAsDeploymentFailure": "[parameters('treatFailureAsDeploymentFailure')]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.DesktopVirtualization/applicationGroups', parameters('desktopApplicationGroupName'))]" + ] + } + ], + "outputs": { + "resourceId": { + "type": "string", + "value": "[resourceId('Microsoft.DesktopVirtualization/applicationGroups', parameters('desktopApplicationGroupName'))]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-id-deployment-{0}', parameters('deploymentNameSuffix')))]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-vdpool-{0}', parameters('deploymentNameSuffix')))]", + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('resourceGroupName'))]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-mgmt-vm-{0}', parameters('deploymentNameSuffix')))]" + ] }, - "validationEnvironment": { - "type": "bool" + { + "condition": "[parameters('recoveryServices')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('deploy-rsv-{0}', parameters('deploymentNameSuffix'))]", + "resourceGroup": "[parameters('resourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "azureBlobsPrivateDnsZoneResourceId": { + "value": "[format('{0}{1}', parameters('privateDnsZoneResourceIdPrefix'), filter(parameters('privateDnsZones'), lambda('name', contains(lambdaVariables('name'), 'blob')))[0])]" + }, + "azureQueueStoragePrivateDnsZoneResourceId": { + "value": "[format('{0}{1}', parameters('privateDnsZoneResourceIdPrefix'), filter(parameters('privateDnsZones'), lambda('name', contains(lambdaVariables('name'), 'queue')))[0])]" + }, + "deployFslogix": { + "value": "[parameters('deployFslogix')]" + }, + "hostPoolResourceId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-vdpool-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceId.value]" + }, + "location": { + "value": "[parameters('locationVirtualMachines')]" + }, + "mlzTags": { + "value": "[parameters('mlzTags')]" + }, + "recoveryServicesPrivateDnsZoneResourceId": { + "value": "[format('{0}{1}', parameters('privateDnsZoneResourceIdPrefix'), filter(parameters('privateDnsZones'), lambda('name', startsWith(lambdaVariables('name'), format('privatelink.{0}.backup.windowsazure', parameters('recoveryServicesGeo')))))[0])]" + }, + "recoveryServicesVaultName": { + "value": "[parameters('namingConvention').recoveryServicesVault]" + }, + "recoveryServicesVaultNetworkInterfaceName": { + "value": "[parameters('namingConvention').recoveryServicesVaultNetworkInterface]" + }, + "recoveryServicesVaultPrivateEndpointName": { + "value": "[parameters('namingConvention').recoveryServicesVaultPrivateEndpoint]" + }, + "storageService": { + "value": "[parameters('storageService')]" + }, + "subnetId": { + "value": "[parameters('subnetResourceId')]" + }, + "tags": { + "value": "[parameters('tags')]" + }, + "timeZone": { + "value": "[parameters('timeZone')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "15719775133926232352" + } + }, + "parameters": { + "azureBlobsPrivateDnsZoneResourceId": { + "type": "string" + }, + "azureQueueStoragePrivateDnsZoneResourceId": { + "type": "string" + }, + "deployFslogix": { + "type": "bool" + }, + "hostPoolResourceId": { + "type": "string" + }, + "location": { + "type": "string" + }, + "mlzTags": { + "type": "object" + }, + "recoveryServicesPrivateDnsZoneResourceId": { + "type": "string" + }, + "recoveryServicesVaultName": { + "type": "string" + }, + "recoveryServicesVaultNetworkInterfaceName": { + "type": "string" + }, + "recoveryServicesVaultPrivateEndpointName": { + "type": "string" + }, + "storageService": { + "type": "string" + }, + "subnetId": { + "type": "string" + }, + "tags": { + "type": "object" + }, + "timeZone": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.RecoveryServices/vaults", + "apiVersion": "2022-03-01", + "name": "[parameters('recoveryServicesVaultName')]", + "location": "[parameters('location')]", + "tags": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), coalesce(tryGet(parameters('tags'), 'Microsoft.RecoveryServices/vaults'), createObject()), parameters('mlzTags'))]", + "sku": { + "name": "RS0", + "tier": "Standard" + }, + "properties": {} + }, + { + "condition": "[and(parameters('deployFslogix'), equals(parameters('storageService'), 'AzureFiles'))]", + "type": "Microsoft.RecoveryServices/vaults/backupPolicies", + "apiVersion": "2022-03-01", + "name": "[format('{0}/{1}', parameters('recoveryServicesVaultName'), 'AvdPolicyStorage')]", + "location": "[parameters('location')]", + "tags": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), coalesce(tryGet(parameters('tags'), 'Microsoft.RecoveryServices/vaults'), createObject()), parameters('mlzTags'))]", + "properties": { + "backupManagementType": "AzureStorage", + "schedulePolicy": { + "scheduleRunFrequency": "Daily", + "scheduleRunTimes": [ + "23:00" + ], + "schedulePolicyType": "SimpleSchedulePolicy" + }, + "retentionPolicy": { + "retentionPolicyType": "LongTermRetentionPolicy", + "dailySchedule": { + "retentionTimes": [ + "23:00" + ], + "retentionDuration": { + "count": 30, + "durationType": "Days" + } + } + }, + "timeZone": "[parameters('timeZone')]", + "workLoadType": "AzureFileShare" + }, + "dependsOn": [ + "[resourceId('Microsoft.RecoveryServices/vaults', parameters('recoveryServicesVaultName'))]" + ] + }, + { + "condition": "[not(parameters('deployFslogix'))]", + "type": "Microsoft.RecoveryServices/vaults/backupPolicies", + "apiVersion": "2022-03-01", + "name": "[format('{0}/{1}', parameters('recoveryServicesVaultName'), 'AvdPolicyVm')]", + "location": "[parameters('location')]", + "tags": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), coalesce(tryGet(parameters('tags'), 'Microsoft.RecoveryServices/vaults'), createObject()), parameters('mlzTags'))]", + "properties": { + "backupManagementType": "AzureIaasVM", + "instantRpRetentionRangeInDays": 2, + "policyType": "V2", + "retentionPolicy": { + "retentionPolicyType": "LongTermRetentionPolicy", + "dailySchedule": { + "retentionTimes": [ + "23:00" + ], + "retentionDuration": { + "count": 30, + "durationType": "Days" + } + } + }, + "schedulePolicy": { + "schedulePolicyType": "SimpleSchedulePolicyV2", + "scheduleRunFrequency": "Daily", + "dailySchedule": { + "scheduleRunTimes": [ + "23:00" + ] + } + }, + "timeZone": "[parameters('timeZone')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.RecoveryServices/vaults', parameters('recoveryServicesVaultName'))]" + ] + }, + { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-04-01", + "name": "[parameters('recoveryServicesVaultPrivateEndpointName')]", + "location": "[parameters('location')]", + "tags": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), coalesce(tryGet(parameters('tags'), 'Microsoft.Network/privateEndpoints'), createObject()), parameters('mlzTags'))]", + "properties": { + "customNetworkInterfaceName": "[parameters('recoveryServicesVaultNetworkInterfaceName')]", + "privateLinkServiceConnections": [ + { + "name": "[parameters('recoveryServicesVaultPrivateEndpointName')]", + "properties": { + "privateLinkServiceId": "[resourceId('Microsoft.RecoveryServices/vaults', parameters('recoveryServicesVaultName'))]", + "groupIds": [ + "AzureBackup" + ] + } + } + ], + "subnet": { + "id": "[parameters('subnetId')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.RecoveryServices/vaults', parameters('recoveryServicesVaultName'))]" + ] + }, + { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2021-08-01", + "name": "[format('{0}/{1}', parameters('recoveryServicesVaultPrivateEndpointName'), parameters('recoveryServicesVaultName'))]", + "properties": { + "privateDnsZoneConfigs": [ + { + "name": "[replace(parameters('recoveryServicesPrivateDnsZoneResourceId'), '.', '-')]", + "properties": { + "privateDnsZoneId": "[parameters('recoveryServicesPrivateDnsZoneResourceId')]" + } + }, + { + "name": "[replace(parameters('azureQueueStoragePrivateDnsZoneResourceId'), '.', '-')]", + "properties": { + "privateDnsZoneId": "[parameters('azureQueueStoragePrivateDnsZoneResourceId')]" + } + }, + { + "name": "[replace(parameters('azureBlobsPrivateDnsZoneResourceId'), '.', '-')]", + "properties": { + "privateDnsZoneId": "[parameters('azureBlobsPrivateDnsZoneResourceId')]" + } + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateEndpoints', parameters('recoveryServicesVaultPrivateEndpointName'))]", + "[resourceId('Microsoft.RecoveryServices/vaults', parameters('recoveryServicesVaultName'))]" + ] + } + ], + "outputs": { + "name": { + "type": "string", + "value": "[parameters('recoveryServicesVaultName')]" + } + } + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-vdpool-{0}', parameters('deploymentNameSuffix')))]", + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('resourceGroupName'))]" + ] }, - "virtualMachineSize": { - "type": "string" - } - }, - "variables": { - "galleryImageOffer": "[if(empty(parameters('imageVersionResourceId')), format('\"{0}\"', parameters('imageOffer')), 'null')]", - "galleryImagePublisher": "[if(empty(parameters('imageVersionResourceId')), format('\"{0}\"', parameters('imagePublisher')), 'null')]", - "galleryImageSku": "[if(empty(parameters('imageVersionResourceId')), format('\"{0}\"', parameters('imageSku')), 'null')]", - "galleryItemId": "[if(empty(parameters('imageVersionResourceId')), format('\"{0}.{1}{2}\"', parameters('imagePublisher'), parameters('imageOffer'), parameters('imageSku')), 'null')]", - "hostPoolName": "[parameters('namingConvention').hostPool]", - "imageType": "[if(empty(parameters('imageVersionResourceId')), '\"Gallery\"', '\"CustomImage\"')]" - }, - "resources": [ { + "condition": "[equals(parameters('fslogixStorageService'), 'AzureFiles Premium')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('deploy-vdpool-{0}', parameters('deploymentNameSuffix'))]", - "resourceGroup": "[parameters('resourceGroupControlPlane')]", + "name": "[format('deploy-function-app-{0}', parameters('deploymentNameSuffix'))]", + "resourceGroup": "[parameters('resourceGroupName')]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "activeDirectorySolution": { - "value": "[parameters('activeDirectorySolution')]" - }, - "avdPrivateDnsZoneResourceId": { - "value": "[parameters('avdPrivateDnsZoneResourceId')]" - }, - "customImageId": { - "value": "[parameters('customImageId')]" - }, - "customRdpProperty": { - "value": "[parameters('customRdpProperty')]" - }, - "deploymentUserAssignedIdentityPrincipalId": { - "value": "[parameters('deploymentUserAssignedIdentityPrincipalId')]" - }, - "diskSku": { - "value": "[parameters('diskSku')]" - }, - "domainName": { - "value": "[parameters('domainName')]" - }, - "enableAvdInsights": { - "value": "[parameters('enableAvdInsights')]" - }, - "galleryImageOffer": { - "value": "[variables('galleryImageOffer')]" - }, - "galleryImagePublisher": { - "value": "[variables('galleryImagePublisher')]" - }, - "galleryImageSku": { - "value": "[variables('galleryImageSku')]" + "delegatedSubnetResourceId": { + "value": "[filter(parameters('subnets'), lambda('subnet', contains(lambdaVariables('subnet').name, 'FunctionAppOutbound')))[0].id]" }, - "galleryItemId": { - "value": "[variables('galleryItemId')]" + "deploymentNameSuffix": { + "value": "[parameters('deploymentNameSuffix')]" }, - "hostPoolDiagnosticSettingName": { - "value": "[parameters('namingConvention').hostPoolDiagnosticSetting]" + "enableApplicationInsights": { + "value": "[parameters('enableApplicationInsights')]" }, - "hostPoolName": { - "value": "[variables('hostPoolName')]" + "environmentAbbreviation": { + "value": "[parameters('environmentAbbreviation')]" }, - "hostPoolNetworkInterfaceName": { - "value": "[parameters('namingConvention').hostPoolNetworkInterface]" + "hostPoolResourceId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-vdpool-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceId.value]" }, - "hostPoolPrivateEndpointName": { - "value": "[parameters('namingConvention').hostPoolPrivateEndpoint]" + "logAnalyticsWorkspaceResourceId": { + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-monitoring-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.logAnalyticsWorkspaceResourceId.value]" }, - "hostPoolPublicNetworkAccess": { - "value": "[parameters('hostPoolPublicNetworkAccess')]" + "mlzTags": { + "value": "[parameters('mlzTags')]" }, - "hostPoolType": { - "value": "[parameters('hostPoolType')]" + "namingConvention": { + "value": "[parameters('namingConvention')]" }, - "imageType": { - "value": "[variables('imageType')]" + "privateDnsZoneResourceIdPrefix": { + "value": "[parameters('privateDnsZoneResourceIdPrefix')]" }, - "location": { - "value": "[parameters('locationControlPlane')]" + "privateDnsZones": { + "value": "[parameters('privateDnsZones')]" }, - "logAnalyticsWorkspaceResourceId": { - "value": "[parameters('logAnalyticsWorkspaceResourceId')]" + "privateLinkScopeResourceId": { + "value": "[parameters('privateLinkScopeResourceId')]" }, - "maxSessionLimit": { - "value": "[parameters('maxSessionLimit')]" + "resourceAbbreviations": { + "value": "[parameters('resourceAbbreviations')]" }, - "mlzTags": { - "value": "[parameters('mlzTags')]" + "resourceGroupProfiles": { + "value": "[parameters('resourceGroupProfiles')]" }, - "sessionHostNamePrefix": { - "value": "[parameters('sessionHostNamePrefix')]" + "serviceToken": { + "value": "[parameters('serviceToken')]" }, "subnetResourceId": { "value": "[parameters('subnetResourceId')]" }, "tags": { "value": "[parameters('tags')]" - }, - "validationEnvironment": { - "value": "[parameters('validationEnvironment')]" - }, - "virtualMachineSize": { - "value": "[parameters('virtualMachineSize')]" } }, "template": { @@ -9465,142 +8149,341 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "9719336724197777662" + "version": "0.31.92.45157", + "templateHash": "1960226860650229376" } }, "parameters": { - "activeDirectorySolution": { - "type": "string" - }, - "avdPrivateDnsZoneResourceId": { - "type": "string" - }, - "deploymentUserAssignedIdentityPrincipalId": { - "type": "string" - }, - "customImageId": { - "type": "string" - }, - "customRdpProperty": { - "type": "string" - }, - "diskSku": { + "delegatedSubnetResourceId": { "type": "string" }, - "domainName": { + "deploymentNameSuffix": { "type": "string" }, - "enableAvdInsights": { + "enableApplicationInsights": { "type": "bool" }, - "galleryImageOffer": { - "type": "string" - }, - "galleryImagePublisher": { - "type": "string" - }, - "galleryImageSku": { - "type": "string" - }, - "galleryItemId": { - "type": "string" - }, - "hostPoolDiagnosticSettingName": { - "type": "string" - }, - "hostPoolName": { - "type": "string" - }, - "hostPoolNetworkInterfaceName": { - "type": "string" - }, - "hostPoolPrivateEndpointName": { - "type": "string" - }, - "hostPoolPublicNetworkAccess": { + "environmentAbbreviation": { "type": "string" }, - "hostPoolType": { + "hostPoolResourceId": { "type": "string" }, - "imageType": { - "type": "string" + "keyExpirationInDays": { + "type": "int", + "defaultValue": 30 }, "location": { - "type": "string" + "type": "string", + "defaultValue": "[resourceGroup().location]" }, "logAnalyticsWorkspaceResourceId": { "type": "string" }, - "maxSessionLimit": { - "type": "int" - }, "mlzTags": { "type": "object" }, - "sessionHostNamePrefix": { + "namingConvention": { + "type": "object" + }, + "privateDnsZoneResourceIdPrefix": { "type": "string" }, - "subnetResourceId": { + "privateDnsZones": { + "type": "array" + }, + "privateLinkScopeResourceId": { "type": "string" }, - "tags": { + "resourceAbbreviations": { "type": "object" }, - "time": { - "type": "string", - "defaultValue": "[utcNow('u')]" + "resourceGroupProfiles": { + "type": "string" }, - "validationEnvironment": { - "type": "bool" + "serviceToken": { + "type": "string" }, - "virtualMachineSize": { + "subnetResourceId": { "type": "string" + }, + "tags": { + "type": "object" } }, "variables": { - "customRdpProperty_Complete": "[if(contains(parameters('activeDirectorySolution'), 'MicrosoftEntraId'), format('{0}enablerdsaadauth:i:1;', parameters('customRdpProperty')), parameters('customRdpProperty'))]" + "$fxv#0": "# This file enables modules to be automatically managed by the Functions service.\r\n# See https://aka.ms/functionsmanageddependency for additional information.\r\n#\r\n@{\r\n # For latest supported version, go to 'https://www.powershellgallery.com/packages/Az'. \r\n # To use the Az module in your function app, please uncomment the line below.\r\n # 'Az' = '7.*'\r\n}", + "$fxv#1": "param($Timer)\r\n\r\ntry\r\n{\r\n\t[string]$FileShareName = $env:FileShareName\r\n\t[string]$ResourceGroupName = $env:ResourceGroupName\r\n\t[string]$ResourceManagerUrl = $env:ResourceManagerUrl\r\n\t[string]$StorageSuffix = $env:StorageSuffix\r\n\t[string]$SubscriptionId = $env:SubscriptionId\r\n\r\n\t$ErrorActionPreference = 'Stop'\r\n\t$WarningPreference = 'SilentlyContinue'\r\n\r\n\t#region Functions\r\n\tfunction Write-Log \r\n {\r\n\t\t[CmdletBinding()]\r\n\t\tparam (\r\n\t\t\t[Parameter(Mandatory = $false)]\r\n\t\t\t[switch]$Err,\r\n\r\n\t\t\t[Parameter(Mandatory = $true)]\r\n\t\t\t[string]$Message,\r\n\r\n\t\t\t[Parameter(Mandatory = $true)]\r\n\t\t\t[string]$ResourceName,\r\n\r\n\t\t\t[Parameter(Mandatory = $false)]\r\n\t\t\t[switch]$Warn\r\n\t\t)\r\n\r\n\t\t[string]$MessageTimeStamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'\r\n\t\t$Message = \"[$($MyInvocation.ScriptLineNumber)] [$($ResourceName)] $Message\"\r\n\t\t[string]$WriteMessage = \"[$($MessageTimeStamp)] $Message\"\r\n\r\n\t\tif ($Err)\r\n {\r\n\t\t\tWrite-Error $WriteMessage\r\n\t\t\t$Message = \"ERROR: $Message\"\r\n\t\t}\r\n\t\telseif ($Warn)\r\n {\r\n\t\t\tWrite-Warning $WriteMessage\r\n\t\t\t$Message = \"WARN: $Message\"\r\n\t\t}\r\n\t\telse \r\n {\r\n\t\t\tWrite-Output $WriteMessage\r\n\t\t}\r\n\t}\r\n\t#endregion Functions\r\n\r\n\t# Note: https://stackoverflow.com/questions/41674518/powershell-setting-security-protocol-to-tls-1-2\r\n\t[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12\r\n\r\n\r\n\t#region Azure Authentication\r\n $AccessToken = $null\r\n try\r\n {\r\n\t\t$TokenAuthURI = $env:IDENTITY_ENDPOINT + '?resource=' + $ResourceManagerUrl + '&api-version=2019-08-01'\r\n\t\t$TokenResponse = Invoke-RestMethod -Method Get -Headers @{\"X-IDENTITY-HEADER\"=\"$env:IDENTITY_HEADER\"} -Uri $TokenAuthURI\r\n\t\t$AccessToken = $TokenResponse.access_token\r\n\t\t$Header = @{\r\n\t\t\t'Content-Type'='application/json'\r\n\t\t\t'Authorization'='Bearer ' + $AccessToken\r\n\t\t}\r\n }\r\n catch\r\n {\r\n throw [System.Exception]::new('Failed to authenticate Azure with application ID, tenant ID, subscription ID', $PSItem.Exception)\r\n }\r\n Write-Log -ResourceName \"$SubscriptionId\" -Message \"Successfully authenticated with Azure using a managed identity\"\r\n\t#endregion Azure Authentication\r\n\r\n\t# Get storage accounts\r\n\t$Uri = $ResourceManagerUrl + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $ResourceGroupName + '/providers/Microsoft.Storage/storageAccounts?api-version=2023-05-01'\r\n\t$StorageAccountNames = (Invoke-RestMethod -Headers $Header -Method 'GET' -Uri $Uri).value.name\r\n\r\n\tforeach($StorageAccountName in $StorageAccountNames)\r\n\t{\r\n\t\t$ShareUpdateUri = $ResourceManagerUrl + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $ResourceGroupName + '/providers/Microsoft.Storage/storageAccounts/' + $StorageAccountName + '/fileServices/default/shares/' + $FileShareName + '?api-version=2023-05-01'\r\n\r\n\t\t# Get file share info\r\n\t\t$ShareGetUri = $ResourceManagerUrl + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $ResourceGroupName + '/providers/Microsoft.Storage/storageAccounts/' + $StorageAccountName + '/fileServices/default/shares/' + $FileShareName + '?api-version=2023-05-01&$expand=stats'\r\n\t\t$PFS = (Invoke-RestMethod -Headers $Header -Method 'GET' -Uri $ShareGetUri).properties\r\n\r\n\t\t# Set variables for provisioned capacity and used capacity\r\n\t\t$ProvisionedCapacity = $PFS.shareQuota\r\n\t\t$UsedCapacity = $PFS.ShareUsageBytes\r\n\t\tWrite-Log -ResourceName \"$StorageAccountName/$FileShareName\" -Message \"Share Capacity: $($ProvisionedCapacity)GB\"\r\n\t\tWrite-Log -ResourceName \"$StorageAccountName/$FileShareName\" -Message \"Share Usage: $([math]::Round($UsedCapacity/1GB, 0))GB\"\r\n\r\n\t\t# GB Based Scaling\r\n\t\t# No scaling if no usage\r\n\t\tif($UsedCapacity -eq 0)\r\n\t\t{\r\n\t\t\tWrite-Log -ResourceName \"$StorageAccountName/$FileShareName\" -Message \"Share Usage is 0GB. No Changes.\"\r\n\t\t}\r\n\t\t# Slow scaling up to 500GB\r\n\t\t# Increases share quota by 100GB if less than 50GB remains on the share\r\n\t\t# This allows time for an AVD Stamp to be rolled out \r\n\t\telseif ($ProvisionedCapacity -lt 500)\r\n\t\t{\r\n\t\t\tif (($ProvisionedCapacity - ($UsedCapacity / ([Math]::Pow(2,30)))) -lt 50) {\r\n\t\t\t\tWrite-Log -ResourceName \"$StorageAccountName/$FileShareName\" -Message \"Share Usage has surpassed the Share Quota remaining threshold of 50GB. Increasing the file share quota by 100GB.\" \r\n\t\t\t\t$Quota = $ProvisionedCapacity + 100\r\n\t\t\t\tInvoke-RestMethod `\r\n\t\t\t\t\t-Body (@{properties = @{shareQuota = $Quota}} | ConvertTo-Json) `\r\n\t\t\t\t\t-Headers $Header `\r\n\t\t\t\t\t-Method 'PATCH' `\r\n\t\t\t\t\t-Uri $ShareUpdateUri | Out-Null\r\n\t\t\t\tWrite-Log -ResourceName \"$StorageAccountName/$FileShareName\" -Message \"New Capacity: $($Quota)GB\"\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\tWrite-Log -ResourceName \"$StorageAccountName/$FileShareName\" -Message \"Share Usage is below Share Quota remaining threshold of 50GB. No Changes.\"\r\n\t\t\t}\r\n\t\t}\r\n\t\t# Aggressive scaling\r\n\t\t# Increases share quota by 500GB if less than 500GB remains on the share\r\n\t\t# This ensures plenty of space is available during mass onboarding\r\n\t\telse \r\n\t\t{\r\n\t\t\tif (($ProvisionedCapacity - ($UsedCapacity / ([Math]::Pow(2,30)))) -lt 500) {\r\n\t\t\t\tWrite-Log -ResourceName \"$StorageAccountName/$FileShareName\" -Message \"Share Usage has surpassed the Share Quota remaining threshold of 500GB. Increasing the file share quota by 500GB.\" \r\n\t\t\t\t$Quota = $ProvisionedCapacity + 500\r\n\t\t\t\tInvoke-RestMethod `\r\n\t\t\t\t\t-Body (@{properties = @{shareQuota = $Quota}} | ConvertTo-Json) `\r\n\t\t\t\t\t-Headers $Header `\r\n\t\t\t\t\t-Method 'PATCH' `\r\n\t\t\t\t\t-Uri $ShareUpdateUri | Out-Null\r\n\t\t\t\tWrite-Log -ResourceName \"$StorageAccountName/$FileShareName\" -Message \"New Capacity: $($Quota)GB\"\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\tWrite-Log -ResourceName \"$StorageAccountName/$FileShareName\" -Message \"Share Usage is below Share Quota remaining threshold of 500GB. No Changes.\"\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\ncatch \r\n{\r\n\t$ErrContainer = $PSItem\r\n\t# $ErrContainer = $_\r\n\r\n\t[string]$ErrMsg = $ErrContainer | Format-List -Force | Out-String\r\n\t$ErrMsg += \"Version: $Version`n\"\r\n\r\n\tif (Get-Command 'Write-Log' -ErrorAction:SilentlyContinue)\r\n {\r\n\t\tWrite-Log -ResourceName \"$StorageAccountName/$FileShareName\" -Err -Message $ErrMsg -ErrorAction:Continue\r\n\t}\r\n\telse\r\n {\r\n\t\tWrite-Error $ErrMsg -ErrorAction:Continue\r\n\t}\r\n\r\n\tthrow [System.Exception]::new($ErrMsg, $ErrContainer.Exception)\r\n}", + "$fxv#2": "# Authentication is provided in the script", + "cloudSuffix": "[replace(replace(environment().resourceManager, 'https://management.', ''), '/', '')]", + "functionAppKeyword": "[if(or(equals(environment().name, 'AzureCloud'), equals(environment().name, 'AzureUSGovernment')), 'azurewebsites', 'appservice')]", + "functionAppScmPrivateDnsZoneResourceId": "[format('{0}scm.{1}', parameters('privateDnsZoneResourceIdPrefix'), filter(parameters('privateDnsZones'), lambda('name', contains(lambdaVariables('name'), variables('functionAppKeyword'))))[0])]", + "service": "aipfsq", + "storageSubResources": [ + { + "name": "blob", + "id": "[format('{0}{1}', parameters('privateDnsZoneResourceIdPrefix'), filter(parameters('privateDnsZones'), lambda('name', contains(lambdaVariables('name'), 'blob')))[0])]", + "nic": "[parameters('namingConvention').storageAccountBlobNetworkInterface]", + "pe": "[parameters('namingConvention').storageAccountBlobPrivateEndpoint]" + }, + { + "name": "file", + "id": "[format('{0}{1}', parameters('privateDnsZoneResourceIdPrefix'), filter(parameters('privateDnsZones'), lambda('name', contains(lambdaVariables('name'), 'file')))[0])]", + "nic": "[parameters('namingConvention').storageAccountFileNetworkInterface]", + "pe": "[parameters('namingConvention').storageAccountFilePrivateEndpoint]" + }, + { + "name": "queue", + "id": "[format('{0}{1}', parameters('privateDnsZoneResourceIdPrefix'), filter(parameters('privateDnsZones'), lambda('name', contains(lambdaVariables('name'), 'queue')))[0])]", + "nic": "[parameters('namingConvention').storageAccountQueueNetworkInterface]", + "pe": "[parameters('namingConvention').storageAccountQueuePrivateEndpoint]" + }, + { + "name": "table", + "id": "[format('{0}{1}', parameters('privateDnsZoneResourceIdPrefix'), filter(parameters('privateDnsZones'), lambda('name', contains(lambdaVariables('name'), 'table')))[0])]", + "nic": "[parameters('namingConvention').storageAccountTableNetworkInterface]", + "pe": "[parameters('namingConvention').storageAccountTablePrivateEndpoint]" + } + ] }, "resources": [ { - "type": "Microsoft.DesktopVirtualization/hostPools", - "apiVersion": "2023-09-05", - "name": "[parameters('hostPoolName')]", + "type": "Microsoft.ManagedIdentity/userAssignedIdentities", + "apiVersion": "2023-01-31", + "name": "[replace(parameters('namingConvention').userAssignedIdentity, parameters('serviceToken'), variables('service'))]", + "location": "[parameters('location')]", + "tags": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), coalesce(tryGet(parameters('tags'), 'Microsoft.ManagedIdentity/userAssignedIdentities'), createObject()), parameters('mlzTags'))]" + }, + { + "type": "Microsoft.KeyVault/vaults", + "apiVersion": "2022-07-01", + "name": "[format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id))]", "location": "[parameters('location')]", - "tags": "[union(createObject('cm-resource-parent', format('{0}}}/resourceGroups/{1}/providers/Microsoft.DesktopVirtualization/hostpools/{2}', subscription().id, resourceGroup().name, parameters('hostPoolName'))), if(contains(parameters('tags'), 'Microsoft.DesktopVirtualization/hostPools'), parameters('tags')['Microsoft.DesktopVirtualization/hostPools'], createObject()), parameters('mlzTags'))]", + "tags": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), coalesce(tryGet(parameters('tags'), 'Microsoft.KeyVault/vaults'), createObject()), parameters('mlzTags'))]", "properties": { - "customRdpProperty": "[variables('customRdpProperty_Complete')]", - "hostPoolType": "[parameters('hostPoolType')]", - "loadBalancerType": "[if(equals(parameters('hostPoolType'), 'Pooled'), 'DepthFirst', 'Persistent')]", - "maxSessionLimit": "[parameters('maxSessionLimit')]", - "personalDesktopAssignmentType": "[if(equals(parameters('hostPoolType'), 'Personal'), 'Automatic', null())]", - "preferredAppGroupType": "Desktop", - "publicNetworkAccess": "[parameters('hostPoolPublicNetworkAccess')]", - "registrationInfo": { - "expirationTime": "[dateTimeAdd(parameters('time'), 'PT2H')]", - "registrationTokenOperation": "Update" + "enabledForDeployment": false, + "enabledForDiskEncryption": false, + "enabledForTemplateDeployment": false, + "enablePurgeProtection": true, + "enableRbacAuthorization": true, + "enableSoftDelete": true, + "networkAcls": { + "bypass": "AzureServices", + "defaultAction": "Deny", + "ipRules": [], + "virtualNetworkRules": [] + }, + "publicNetworkAccess": "Disabled", + "sku": { + "family": "A", + "name": "premium" + }, + "softDeleteRetentionInDays": "[if(or(equals(parameters('environmentAbbreviation'), 'dev'), equals(parameters('environmentAbbreviation'), 'test')), 7, 90)]", + "tenantId": "[subscription().tenantId]" + } + }, + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2020-04-01-preview", + "scope": "[format('Microsoft.KeyVault/vaults/{0}', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id)))]", + "name": "[guid(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', replace(parameters('namingConvention').userAssignedIdentity, parameters('serviceToken'), variables('service'))), 'e147488a-f6f5-4113-8e2d-b22465e65bf6', resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id))))]", + "properties": { + "principalId": "[reference(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', replace(parameters('namingConvention').userAssignedIdentity, parameters('serviceToken'), variables('service'))), '2023-01-31').principalId]", + "principalType": "ServicePrincipal", + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', 'e147488a-f6f5-4113-8e2d-b22465e65bf6')]" + }, + "dependsOn": [ + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', replace(parameters('namingConvention').userAssignedIdentity, parameters('serviceToken'), variables('service')))]", + "[resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id)))]" + ] + }, + { + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-04-01", + "name": "[replace(parameters('namingConvention').keyVaultPrivateEndpoint, parameters('serviceToken'), variables('service'))]", + "location": "[parameters('location')]", + "tags": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), coalesce(tryGet(parameters('tags'), 'Microsoft.Network/privateEndpoints'), createObject()), parameters('mlzTags'))]", + "properties": { + "customNetworkInterfaceName": "[replace(parameters('namingConvention').keyVaultNetworkInterface, parameters('serviceToken'), variables('service'))]", + "privateLinkServiceConnections": [ + { + "name": "[replace(parameters('namingConvention').keyVaultPrivateEndpoint, parameters('serviceToken'), variables('service'))]", + "properties": { + "privateLinkServiceId": "[resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id)))]", + "groupIds": [ + "vault" + ] + } + } + ], + "subnet": { + "id": "[parameters('subnetResourceId')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id)))]" + ] + }, + { + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2021-08-01", + "name": "[format('{0}/{1}', replace(parameters('namingConvention').keyVaultPrivateEndpoint, parameters('serviceToken'), variables('service')), format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id)))]", + "properties": { + "privateDnsZoneConfigs": [ + { + "name": "ipconfig1", + "properties": { + "privateDnsZoneId": "[format('{0}{1}', parameters('privateDnsZoneResourceIdPrefix'), filter(parameters('privateDnsZones'), lambda('name', contains(lambdaVariables('name'), 'vaultcore')))[0])]" + } + } + ] + }, + "dependsOn": [ + "[resourceId('Microsoft.Network/privateEndpoints', replace(parameters('namingConvention').keyVaultPrivateEndpoint, parameters('serviceToken'), variables('service')))]", + "[resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id)))]" + ] + }, + { + "type": "Microsoft.KeyVault/vaults/keys", + "apiVersion": "2022-07-01", + "name": "[format('{0}/{1}', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id)), 'StorageEncryptionKey')]", + "properties": { + "attributes": { + "enabled": true + }, + "keySize": 4096, + "kty": "RSA", + "rotationPolicy": { + "attributes": { + "expiryTime": "[format('P{0}D', string(parameters('keyExpirationInDays')))]" + }, + "lifetimeActions": [ + { + "action": { + "type": "Notify" + }, + "trigger": { + "timeBeforeExpiry": "P10D" + } + }, + { + "action": { + "type": "Rotate" + }, + "trigger": { + "timeAfterCreate": "[format('P{0}D', string(sub(parameters('keyExpirationInDays'), 7)))]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id)))]" + ] + }, + { + "type": "Microsoft.Storage/storageAccounts", + "apiVersion": "2022-09-01", + "name": "[uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id)]", + "location": "[parameters('location')]", + "tags": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), coalesce(tryGet(parameters('tags'), 'Microsoft.Storage/storageAccounts'), createObject()), parameters('mlzTags'))]", + "sku": { + "name": "Standard_LRS" + }, + "kind": "StorageV2", + "identity": { + "type": "UserAssigned", + "userAssignedIdentities": { + "[format('{0}', resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', replace(parameters('namingConvention').userAssignedIdentity, parameters('serviceToken'), variables('service'))))]": {} + } + }, + "properties": { + "accessTier": "Hot", + "allowBlobPublicAccess": false, + "allowCrossTenantReplication": false, + "allowedCopyScope": "PrivateLink", + "allowSharedKeyAccess": false, + "azureFilesIdentityBasedAuthentication": { + "directoryServiceOptions": "None" + }, + "defaultToOAuthAuthentication": false, + "dnsEndpointType": "Standard", + "encryption": { + "identity": { + "userAssignedIdentity": "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', replace(parameters('namingConvention').userAssignedIdentity, parameters('serviceToken'), variables('service')))]" + }, + "requireInfrastructureEncryption": true, + "keyvaultproperties": { + "keyvaulturi": "[reference(resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id))), '2022-07-01').vaultUri]", + "keyname": "StorageEncryptionKey" + }, + "services": { + "file": { + "keyType": "Account", + "enabled": true + }, + "table": { + "keyType": "Account", + "enabled": true + }, + "queue": { + "keyType": "Account", + "enabled": true + }, + "blob": { + "keyType": "Account", + "enabled": true + } + }, + "keySource": "Microsoft.KeyVault" }, - "startVMOnConnect": true, - "validationEnvironment": "[parameters('validationEnvironment')]", - "vmTemplate": "[format('{{\"domain\":\"{0}\",\"galleryImageOffer\":{1},\"galleryImagePublisher\":{2},\"galleryImageSKU\":{3},\"imageType\":{4},\"customImageId\":{5},\"namePrefix\":\"{6}\",\"osDiskType\":\"{7}\",\"vmSize\":{{\"id\":\"{8}\",\"cores\":null,\"ram\":null,\"rdmaEnabled\": false,\"supportsMemoryPreservingMaintenance\": true}},\"galleryItemId\":{9},\"hibernate\":false,\"diskSizeGB\":0,\"securityType\":\"TrustedLaunch\",\"secureBoot\":true,\"vTPM\":true,\"vmInfrastructureType\":\"Cloud\",\"virtualProcessorCount\":null,\"memoryGB\":null,\"maximumMemoryGB\":null,\"minimumMemoryGB\":null,\"dynamicMemoryConfig\":false}}', parameters('domainName'), parameters('galleryImageOffer'), parameters('galleryImagePublisher'), parameters('galleryImageSku'), parameters('imageType'), parameters('customImageId'), parameters('sessionHostNamePrefix'), parameters('diskSku'), parameters('virtualMachineSize'), parameters('galleryItemId'))]" - } + "largeFileSharesState": "Disabled", + "minimumTlsVersion": "TLS1_2", + "networkAcls": { + "bypass": "AzureServices", + "virtualNetworkRules": [], + "ipRules": [], + "defaultAction": "Deny" + }, + "publicNetworkAccess": "Disabled", + "supportsHttpsTrafficOnly": true + }, + "dependsOn": [ + "[resourceId('Microsoft.KeyVault/vaults/keys', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id)), 'StorageEncryptionKey')]", + "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', replace(parameters('namingConvention').keyVaultPrivateEndpoint, parameters('serviceToken'), variables('service')), format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id)))]", + "[resourceId('Microsoft.Network/privateEndpoints', replace(parameters('namingConvention').keyVaultPrivateEndpoint, parameters('serviceToken'), variables('service')))]", + "[extensionResourceId(resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id))), 'Microsoft.Authorization/roleAssignments', guid(resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', replace(parameters('namingConvention').userAssignedIdentity, parameters('serviceToken'), variables('service'))), 'e147488a-f6f5-4113-8e2d-b22465e65bf6', resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id)))))]", + "[resourceId('Microsoft.ManagedIdentity/userAssignedIdentities', replace(parameters('namingConvention').userAssignedIdentity, parameters('serviceToken'), variables('service')))]", + "[resourceId('Microsoft.KeyVault/vaults', format('{0}{1}', parameters('resourceAbbreviations').keyVaults, uniqueString(replace(parameters('namingConvention').keyVault, parameters('serviceToken'), variables('service')), resourceGroup().id)))]" + ] + }, + { + "type": "Microsoft.Storage/storageAccounts/blobServices", + "apiVersion": "2021-09-01", + "name": "[format('{0}/{1}', uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id), 'default')]", + "dependsOn": [ + "[resourceId('Microsoft.Storage/storageAccounts', uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id))]" + ] }, { + "copy": { + "name": "privateEndpoints_storage", + "count": "[length(variables('storageSubResources'))]" + }, "type": "Microsoft.Network/privateEndpoints", "apiVersion": "2023-04-01", - "name": "[parameters('hostPoolPrivateEndpointName')]", + "name": "[replace(variables('storageSubResources')[copyIndex()].pe, parameters('serviceToken'), variables('service'))]", "location": "[parameters('location')]", - "tags": "[union(createObject('cm-resource-parent', format('{0}}}/resourceGroups/{1}/providers/Microsoft.DesktopVirtualization/hostpools/{2}', subscription().id, resourceGroup().name, parameters('hostPoolName'))), if(contains(parameters('tags'), 'Microsoft.Network/privateEndpoints'), parameters('tags')['Microsoft.Network/privateEndpoints'], createObject()), parameters('mlzTags'))]", + "tags": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), coalesce(tryGet(parameters('tags'), 'Microsoft.Network/privateEndpoints'), createObject()), parameters('mlzTags'))]", "properties": { - "customNetworkInterfaceName": "[parameters('hostPoolNetworkInterfaceName')]", + "customNetworkInterfaceName": "[replace(variables('storageSubResources')[copyIndex()].nic, parameters('serviceToken'), variables('service'))]", "privateLinkServiceConnections": [ { - "name": "[parameters('hostPoolPrivateEndpointName')]", + "name": "[replace(variables('storageSubResources')[copyIndex()].pe, parameters('serviceToken'), variables('service'))]", "properties": { - "privateLinkServiceId": "[resourceId('Microsoft.DesktopVirtualization/hostPools', parameters('hostPoolName'))]", + "privateLinkServiceId": "[resourceId('Microsoft.Storage/storageAccounts', uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id))]", "groupIds": [ - "connection" + "[variables('storageSubResources')[copyIndex()].name]" ] } } @@ -9610,282 +8493,368 @@ } }, "dependsOn": [ - "[resourceId('Microsoft.DesktopVirtualization/hostPools', parameters('hostPoolName'))]" + "[resourceId('Microsoft.Storage/storageAccounts', uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id))]" ] }, { + "copy": { + "name": "privateDnsZoneGroups_storage", + "count": "[length(variables('storageSubResources'))]" + }, "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", - "apiVersion": "2023-05-01", - "name": "[format('{0}/{1}', parameters('hostPoolPrivateEndpointName'), 'default')]", + "apiVersion": "2021-08-01", + "name": "[format('{0}/{1}', replace(variables('storageSubResources')[copyIndex()].pe, parameters('serviceToken'), variables('service')), uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id))]", "properties": { "privateDnsZoneConfigs": [ { - "name": "[replace(split(parameters('avdPrivateDnsZoneResourceId'), '/')[8], '.', '-')]", + "name": "ipconfig1", "properties": { - "privateDnsZoneId": "[parameters('avdPrivateDnsZoneResourceId')]" + "privateDnsZoneId": "[variables('storageSubResources')[copyIndex()].id]" } } ] }, "dependsOn": [ - "[resourceId('Microsoft.Network/privateEndpoints', parameters('hostPoolPrivateEndpointName'))]" - ] - }, - { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.DesktopVirtualization/hostPools/{0}', parameters('hostPoolName'))]", - "name": "[guid(parameters('deploymentUserAssignedIdentityPrincipalId'), '2ad6aaab-ead9-4eaa-8ac5-da422f562408', resourceId('Microsoft.DesktopVirtualization/hostPools', parameters('hostPoolName')))]", - "properties": { - "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', '2ad6aaab-ead9-4eaa-8ac5-da422f562408')]", - "principalId": "[parameters('deploymentUserAssignedIdentityPrincipalId')]", - "principalType": "ServicePrincipal" - }, - "dependsOn": [ - "[resourceId('Microsoft.DesktopVirtualization/hostPools', parameters('hostPoolName'))]" + "[resourceId('Microsoft.Network/privateEndpoints', replace(variables('storageSubResources')[copyIndex()].pe, parameters('serviceToken'), variables('service')))]", + "[resourceId('Microsoft.Storage/storageAccounts', uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id))]" ] }, { - "condition": "[parameters('enableAvdInsights')]", + "condition": "[parameters('enableApplicationInsights')]", "type": "Microsoft.Insights/diagnosticSettings", - "apiVersion": "2021-05-01-preview", - "scope": "[format('Microsoft.DesktopVirtualization/hostPools/{0}', parameters('hostPoolName'))]", - "name": "[parameters('hostPoolDiagnosticSettingName')]", + "apiVersion": "2017-05-01-preview", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}/blobServices/{1}', uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id), 'default')]", + "name": "[replace(parameters('namingConvention').storageAccountDiagnosticSetting, format('{0}-{1}', parameters('serviceToken'), parameters('resourceAbbreviations').storageAccounts), format('blob-{0}-scale', parameters('resourceAbbreviations').storageAccounts))]", "properties": { "logs": [ { - "categoryGroup": "allLogs", + "category": "StorageWrite", + "enabled": true + } + ], + "metrics": [ + { + "category": "Transaction", "enabled": true } ], "workspaceId": "[parameters('logAnalyticsWorkspaceResourceId')]" }, "dependsOn": [ - "[resourceId('Microsoft.DesktopVirtualization/hostPools', parameters('hostPoolName'))]" + "[resourceId('Microsoft.Storage/storageAccounts/blobServices', uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id), 'default')]" ] - } - ], - "outputs": { - "name": { - "type": "string", - "value": "[parameters('hostPoolName')]" - }, - "resourceId": { - "type": "string", - "value": "[resourceId('Microsoft.DesktopVirtualization/hostPools', parameters('hostPoolName'))]" - } - } - } - } - }, - { - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('deploy-vdag-{0}', parameters('deploymentNameSuffix'))]", - "resourceGroup": "[parameters('resourceGroupControlPlane')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "deploymentNameSuffix": { - "value": "[parameters('deploymentNameSuffix')]" - }, - "deploymentUserAssignedIdentityClientId": { - "value": "[parameters('deploymentUserAssignedIdentityClientId')]" - }, - "deploymentUserAssignedIdentityPrincipalId": { - "value": "[parameters('deploymentUserAssignedIdentityPrincipalId')]" - }, - "desktopApplicationGroupName": { - "value": "[replace(parameters('namingConvention').applicationGroup, parameters('serviceToken'), 'desktop')]" - }, - "hostPoolResourceId": { - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupControlPlane')), 'Microsoft.Resources/deployments', format('deploy-vdpool-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceId.value]" - }, - "locationControlPlane": { - "value": "[parameters('locationControlPlane')]" - }, - "locationVirtualMachines": { - "value": "[parameters('locationVirtualMachines')]" - }, - "mlzTags": { - "value": "[parameters('mlzTags')]" - }, - "resourceGroupManagement": { - "value": "[parameters('resourceGroupManagement')]" - }, - "roleDefinitions": { - "value": "[parameters('roleDefinitions')]" - }, - "securityPrincipalObjectIds": { - "value": "[parameters('securityPrincipalObjectIds')]" - }, - "desktopFriendlyName": { - "value": "[parameters('desktopFriendlyName')]" - }, - "tags": { - "value": "[parameters('tags')]" - }, - "virtualMachineName": { - "value": "[parameters('managementVirtualMachineName')]" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "10581081897017236090" - } - }, - "parameters": { - "deploymentNameSuffix": { - "type": "string" - }, - "deploymentUserAssignedIdentityClientId": { - "type": "string" - }, - "deploymentUserAssignedIdentityPrincipalId": { - "type": "string" - }, - "desktopApplicationGroupName": { - "type": "string" - }, - "desktopFriendlyName": { - "type": "string" - }, - "hostPoolResourceId": { - "type": "string" - }, - "locationControlPlane": { - "type": "string" - }, - "locationVirtualMachines": { - "type": "string" - }, - "mlzTags": { - "type": "object" - }, - "resourceGroupManagement": { - "type": "string" }, - "roleDefinitions": { - "type": "object" + { + "condition": "[parameters('enableApplicationInsights')]", + "type": "Microsoft.Insights/components", + "apiVersion": "2020-02-02", + "name": "[replace(parameters('namingConvention').applicationInsights, parameters('serviceToken'), variables('service'))]", + "location": "[parameters('location')]", + "tags": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), coalesce(tryGet(parameters('tags'), 'Microsoft.Insights/components'), createObject()), parameters('mlzTags'))]", + "properties": { + "Application_Type": "web", + "publicNetworkAccessForIngestion": "Disabled", + "publicNetworkAccessForQuery": "Disabled" + }, + "kind": "web" }, - "securityPrincipalObjectIds": { - "type": "array" + { + "type": "Microsoft.Web/serverfarms", + "apiVersion": "2023-01-01", + "name": "[replace(parameters('namingConvention').appServicePlan, parameters('serviceToken'), variables('service'))]", + "location": "[parameters('location')]", + "tags": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), coalesce(tryGet(parameters('tags'), 'Microsoft.Web/serverfarms'), createObject()), parameters('mlzTags'))]", + "sku": { + "name": "P1v3", + "tier": "PremiumV3", + "size": "P1v3", + "family": "Pv3", + "capacity": 1 + }, + "kind": "functionapp" }, - "tags": { - "type": "object" + { + "type": "Microsoft.Web/sites", + "apiVersion": "2023-01-01", + "name": "[uniqueString(replace(parameters('namingConvention').functionApp, parameters('serviceToken'), variables('service')), resourceGroup().id)]", + "location": "[parameters('location')]", + "tags": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), coalesce(tryGet(parameters('tags'), 'Microsoft.Web/sites'), createObject()), parameters('mlzTags'))]", + "kind": "functionapp", + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "clientAffinityEnabled": false, + "httpsOnly": true, + "publicNetworkAccess": "Disabled", + "serverFarmId": "[resourceId('Microsoft.Web/serverfarms', replace(parameters('namingConvention').appServicePlan, parameters('serviceToken'), variables('service')))]", + "siteConfig": { + "alwaysOn": true, + "appSettings": "[union(createArray(createObject('name', 'AzureWebJobsStorage__blobServiceUri', 'value', format('https://{0}.blob.{1}', uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id), environment().suffixes.storage)), createObject('name', 'AzureWebJobsStorage__credential', 'value', 'managedidentity'), createObject('name', 'AzureWebJobsStorage__queueServiceUri', 'value', format('https://{0}.queue.{1}', uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id), environment().suffixes.storage)), createObject('name', 'AzureWebJobsStorage__tableServiceUri', 'value', format('https://{0}.table.{1}', uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id), environment().suffixes.storage)), createObject('name', 'FUNCTIONS_EXTENSION_VERSION', 'value', '~4'), createObject('name', 'FUNCTIONS_WORKER_RUNTIME', 'value', 'powershell'), createObject('name', 'WEBSITE_LOAD_USER_PROFILE', 'value', '1'), createObject('name', 'FileShareName', 'value', 'profile-containers'), createObject('name', 'ResourceGroupName', 'value', parameters('resourceGroupProfiles')), createObject('name', 'ResourceManagerUrl', 'value', if(endsWith(environment().resourceManager, '/'), environment().resourceManager, format('{0}/', environment().resourceManager))), createObject('name', 'StorageSuffix', 'value', environment().suffixes.storage), createObject('name', 'SubscriptionId', 'value', subscription().subscriptionId)), if(parameters('enableApplicationInsights'), createArray(createObject('name', 'APPLICATIONINSIGHTS_CONNECTION_STRING', 'value', reference(resourceId('Microsoft.Insights/components', replace(parameters('namingConvention').applicationInsights, parameters('serviceToken'), variables('service'))), '2020-02-02').ConnectionString)), createArray()))]", + "cors": { + "allowedOrigins": [ + "[format('{0}', environment().portal)]", + "[format('https://functions-next.{0}', variables('cloudSuffix'))]", + "[format('https://functions-staging.{0}', variables('cloudSuffix'))]", + "[format('https://functions.{0}', variables('cloudSuffix'))]" + ] + }, + "ftpsState": "Disabled", + "netFrameworkVersion": "v6.0", + "powerShellVersion": "7.4", + "publicNetworkAccess": "Disabled", + "use32BitWorkerProcess": false + }, + "virtualNetworkSubnetId": "[parameters('delegatedSubnetResourceId')]", + "vnetContentShareEnabled": false, + "vnetRouteAllEnabled": true + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/components', replace(parameters('namingConvention').applicationInsights, parameters('serviceToken'), variables('service')))]", + "[resourceId('Microsoft.Web/serverfarms', replace(parameters('namingConvention').appServicePlan, parameters('serviceToken'), variables('service')))]", + "[resourceId('Microsoft.Storage/storageAccounts', uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id))]" + ] }, - "virtualMachineName": { - "type": "string" - } - }, - "variables": { - "$fxv#0": "Param(\r\n [string]$ApplicationGroupName,\r\n [string]$FriendlyName,\r\n [string]$ResourceGroupName,\r\n [string]$ResourceManagerUri,\r\n [string]$SubscriptionId,\r\n [string]$UserAssignedIdentityClientId\r\n)\r\n\r\n$ErrorActionPreference = 'Stop'\r\n$WarningPreference = 'SilentlyContinue'\r\n\r\n# Wait for role assignment propagation\r\nStart-Sleep -Seconds 30\r\n\r\n# Fix the resource manager URI since only AzureCloud contains a trailing slash\r\n$ResourceManagerUriFixed = if ($ResourceManagerUri[-1] -eq '/') {$ResourceManagerUri} else {$ResourceManagerUri + '/'}\r\n\r\n# Get an access token for Azure resources\r\n$AzureManagementAccessToken = (Invoke-RestMethod `\r\n -Headers @{Metadata=\"true\"} `\r\n -Uri $('http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=' + $ResourceManagerUriFixed + '&client_id=' + $UserAssignedIdentityClientId)).access_token\r\n\r\n# Set header for Azure Management API\r\n$AzureManagementHeader = @{\r\n 'Content-Type'='application/json'\r\n 'Authorization'='Bearer ' + $AzureManagementAccessToken\r\n}\r\n\r\n# Update the friendly name on the session desktop\r\nInvoke-RestMethod `\r\n -Body (@{properties = @{friendlyName = $FriendlyName.Replace('\"', '')}} | ConvertTo-Json) `\r\n -Headers $AzureManagementHeader `\r\n -Method 'PATCH' `\r\n -Uri $($ResourceManagerUriFixed + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $ResourceGroupName + '/providers/Microsoft.DesktopVirtualization/applicationGroups/' + $ApplicationGroupName + '/desktops/SessionDesktop?api-version=2022-02-10-preview') | Out-Null" - }, - "resources": [ { - "type": "Microsoft.DesktopVirtualization/applicationGroups", - "apiVersion": "2024-04-03", - "name": "[parameters('desktopApplicationGroupName')]", - "location": "[parameters('locationControlPlane')]", - "tags": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), if(contains(parameters('tags'), 'Microsoft.DesktopVirtualization/applicationGroups'), parameters('tags')['Microsoft.DesktopVirtualization/applicationGroups'], createObject()), parameters('mlzTags'))]", + "type": "Microsoft.Network/privateEndpoints", + "apiVersion": "2023-04-01", + "name": "[replace(parameters('namingConvention').functionAppPrivateEndpoint, parameters('serviceToken'), variables('service'))]", + "location": "[parameters('location')]", + "tags": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), coalesce(tryGet(parameters('tags'), 'Microsoft.Network/privateEndpoints'), createObject()), parameters('mlzTags'))]", "properties": { - "hostPoolArmPath": "[parameters('hostPoolResourceId')]", - "applicationGroupType": "Desktop" - } + "customNetworkInterfaceName": "[replace(parameters('namingConvention').functionAppNetworkInterface, parameters('serviceToken'), variables('service'))]", + "privateLinkServiceConnections": [ + { + "name": "[replace(parameters('namingConvention').functionAppPrivateEndpoint, parameters('serviceToken'), variables('service'))]", + "properties": { + "privateLinkServiceId": "[resourceId('Microsoft.Web/sites', uniqueString(replace(parameters('namingConvention').functionApp, parameters('serviceToken'), variables('service')), resourceGroup().id))]", + "groupIds": [ + "sites" + ] + } + } + ], + "subnet": { + "id": "[parameters('subnetResourceId')]" + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Web/sites', uniqueString(replace(parameters('namingConvention').functionApp, parameters('serviceToken'), variables('service')), resourceGroup().id))]" + ] }, { - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.DesktopVirtualization/applicationGroups/{0}', parameters('desktopApplicationGroupName'))]", - "name": "[guid(parameters('deploymentUserAssignedIdentityPrincipalId'), '86240b0e-9422-4c43-887b-b61143f32ba8', resourceId('Microsoft.DesktopVirtualization/applicationGroups', parameters('desktopApplicationGroupName')))]", + "type": "Microsoft.Network/privateEndpoints/privateDnsZoneGroups", + "apiVersion": "2021-08-01", + "name": "[format('{0}/{1}', replace(parameters('namingConvention').functionAppPrivateEndpoint, parameters('serviceToken'), variables('service')), 'default')]", "properties": { - "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', '86240b0e-9422-4c43-887b-b61143f32ba8')]", - "principalId": "[parameters('deploymentUserAssignedIdentityPrincipalId')]", - "principalType": "ServicePrincipal" + "privateDnsZoneConfigs": [ + { + "name": "ipconfig1", + "properties": { + "privateDnsZoneId": "[format('{0}{1}', parameters('privateDnsZoneResourceIdPrefix'), filter(parameters('privateDnsZones'), lambda('name', contains(lambdaVariables('name'), variables('functionAppKeyword'))))[0])]" + } + } + ] }, "dependsOn": [ - "[resourceId('Microsoft.DesktopVirtualization/applicationGroups', parameters('desktopApplicationGroupName'))]" + "[resourceId('Microsoft.Network/privateEndpoints', replace(parameters('namingConvention').functionAppPrivateEndpoint, parameters('serviceToken'), variables('service')))]" ] }, { - "copy": { - "name": "roleAssignment_Users", - "count": "[length(range(0, length(parameters('securityPrincipalObjectIds'))))]" - }, - "type": "Microsoft.Authorization/roleAssignments", - "apiVersion": "2022-04-01", - "scope": "[format('Microsoft.DesktopVirtualization/applicationGroups/{0}', parameters('desktopApplicationGroupName'))]", - "name": "[guid(parameters('securityPrincipalObjectIds')[range(0, length(parameters('securityPrincipalObjectIds')))[copyIndex()]], parameters('roleDefinitions').DesktopVirtualizationUser, parameters('desktopApplicationGroupName'))]", + "type": "Microsoft.Web/sites/functions", + "apiVersion": "2020-12-01", + "name": "[format('{0}/{1}', uniqueString(replace(parameters('namingConvention').functionApp, parameters('serviceToken'), variables('service')), resourceGroup().id), 'auto-increase-file-share-quota')]", "properties": { - "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitions').DesktopVirtualizationUser)]", - "principalId": "[parameters('securityPrincipalObjectIds')[range(0, length(parameters('securityPrincipalObjectIds')))[copyIndex()]]]" + "config": { + "disabled": false, + "bindings": [ + { + "name": "Timer", + "type": "timerTrigger", + "direction": "in", + "schedule": "0 */15 * * * *" + } + ] + }, + "files": { + "requirements.psd1": "[variables('$fxv#0')]", + "run.ps1": "[variables('$fxv#1')]", + "../profile.ps1": "[variables('$fxv#2')]" + } }, "dependsOn": [ - "[resourceId('Microsoft.DesktopVirtualization/applicationGroups', parameters('desktopApplicationGroupName'))]" + "[resourceId('Microsoft.Web/sites', uniqueString(replace(parameters('namingConvention').functionApp, parameters('serviceToken'), variables('service')), resourceGroup().id))]" ] }, { - "condition": "[not(empty(parameters('desktopFriendlyName')))]", + "condition": "[parameters('enableApplicationInsights')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('deploy-vdapp-friendly-name-{0}', parameters('deploymentNameSuffix'))]", - "resourceGroup": "[parameters('resourceGroupManagement')]", + "name": "[format('deploy-private-link-scope-appi-{0}', parameters('deploymentNameSuffix'))]", + "subscriptionId": "[split(parameters('privateLinkScopeResourceId'), '/')[2]]", + "resourceGroup": "[split(parameters('privateLinkScopeResourceId'), '/')[4]]", "properties": { "expressionEvaluationOptions": { "scope": "inner" }, "mode": "Incremental", "parameters": { - "location": { - "value": "[parameters('locationVirtualMachines')]" + "applicationInsightsResourceId": { + "value": "[resourceId('Microsoft.Insights/components', replace(parameters('namingConvention').applicationInsights, parameters('serviceToken'), variables('service')))]" }, - "name": { - "value": "Update-AvdDesktop" + "privateLinkScopeResourceId": { + "value": "[parameters('privateLinkScopeResourceId')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "6666075809401495349" + } }, "parameters": { - "value": [ - { - "name": "ApplicationGroupName", - "value": "[parameters('desktopApplicationGroupName')]" - }, - { - "name": "FriendlyName", - "value": "[parameters('desktopFriendlyName')]" - }, - { - "name": "ResourceGroupName", - "value": "[resourceGroup().name]" - }, - { - "name": "ResourceManagerUri", - "value": "[environment().resourceManager]" - }, - { - "name": "SubscriptionId", - "value": "[subscription().subscriptionId]" - }, - { - "name": "UserAssignedIdentityClientId", - "value": "[parameters('deploymentUserAssignedIdentityClientId')]" + "applicationInsightsResourceId": { + "type": "string", + "defaultValue": "" + }, + "dataCollectionEndpointResourceId": { + "type": "string", + "defaultValue": "" + }, + "logAnalyticsWorkspaceResourceId": { + "type": "string", + "defaultValue": "" + }, + "privateLinkScopeResourceId": { + "type": "string" + } + }, + "resources": [ + { + "condition": "[not(empty(parameters('applicationInsightsResourceId')))]", + "type": "Microsoft.Insights/privateLinkScopes/scopedResources", + "apiVersion": "2021-09-01", + "name": "[format('{0}/{1}', split(parameters('privateLinkScopeResourceId'), '/')[8], if(empty(parameters('applicationInsightsResourceId')), 'applicationInsights', split(parameters('applicationInsightsResourceId'), '/')[8]))]", + "properties": { + "linkedResourceId": "[parameters('applicationInsightsResourceId')]" } - ] + }, + { + "condition": "[not(empty(parameters('dataCollectionEndpointResourceId')))]", + "type": "Microsoft.Insights/privateLinkScopes/scopedResources", + "apiVersion": "2021-09-01", + "name": "[format('{0}/{1}', split(parameters('privateLinkScopeResourceId'), '/')[8], if(empty(parameters('dataCollectionEndpointResourceId')), 'dataCollectionEndpoint', split(parameters('dataCollectionEndpointResourceId'), '/')[8]))]", + "properties": { + "linkedResourceId": "[parameters('dataCollectionEndpointResourceId')]" + } + }, + { + "condition": "[not(empty(parameters('logAnalyticsWorkspaceResourceId')))]", + "type": "Microsoft.Insights/privateLinkScopes/scopedResources", + "apiVersion": "2021-09-01", + "name": "[format('{0}/{1}', split(parameters('privateLinkScopeResourceId'), '/')[8], if(empty(parameters('logAnalyticsWorkspaceResourceId')), 'logAnalyticsWorkspace', split(parameters('logAnalyticsWorkspaceResourceId'), '/')[8]))]", + "properties": { + "linkedResourceId": "[parameters('logAnalyticsWorkspaceResourceId')]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Insights/components', replace(parameters('namingConvention').applicationInsights, parameters('serviceToken'), variables('service')))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('set-role-assignment-storage-{0}', parameters('deploymentNameSuffix'))]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "principalId": { + "value": "[reference(resourceId('Microsoft.Web/sites', uniqueString(replace(parameters('namingConvention').functionApp, parameters('serviceToken'), variables('service')), resourceGroup().id)), '2023-01-01', 'full').identity.principalId]" }, - "script": { - "value": "[variables('$fxv#0')]" + "principalType": { + "value": "ServicePrincipal" }, - "tags": { - "value": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), if(contains(parameters('tags'), 'Microsoft.Compute/virtualMachines'), parameters('tags')['Microsoft.Compute/virtualMachines'], createObject()), parameters('mlzTags'))]" + "roleDefinitionId": { + "value": "b7e6dc6d-f1e8-4753-8033-0f276bb0955b" }, - "virtualMachineName": { - "value": "[parameters('virtualMachineName')]" + "storageAccountName": { + "value": "[uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id)]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "9123948093985510582" + } + }, + "parameters": { + "principalId": { + "type": "string" + }, + "principalType": { + "type": "string" + }, + "roleDefinitionId": { + "type": "string" + }, + "storageAccountName": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "scope": "[format('Microsoft.Storage/storageAccounts/{0}', parameters('storageAccountName'))]", + "name": "[guid(parameters('principalId'), parameters('roleDefinitionId'), resourceGroup().id)]", + "properties": { + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitionId'))]", + "principalId": "[parameters('principalId')]", + "principalType": "[parameters('principalType')]" + } + } + ] + } + }, + "dependsOn": [ + "[resourceId('Microsoft.Web/sites', uniqueString(replace(parameters('namingConvention').functionApp, parameters('serviceToken'), variables('service')), resourceGroup().id))]", + "[resourceId('Microsoft.Storage/storageAccounts', uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), variables('service')), resourceGroup().id))]" + ] + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('deploy-scm-a-record-{0}', parameters('deploymentNameSuffix'))]", + "subscriptionId": "[split(variables('functionAppScmPrivateDnsZoneResourceId'), '/')[2]]", + "resourceGroup": "[split(variables('functionAppScmPrivateDnsZoneResourceId'), '/')[4]]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "functionAppName": { + "value": "[uniqueString(replace(parameters('namingConvention').functionApp, parameters('serviceToken'), variables('service')), resourceGroup().id)]" + }, + "ipv4Address": { + "value": "[filter(reference(resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', replace(parameters('namingConvention').functionAppPrivateEndpoint, parameters('serviceToken'), variables('service')), 'default'), '2021-08-01').privateDnsZoneConfigs[0].properties.recordSets, lambda('record', equals(lambdaVariables('record').recordSetName, uniqueString(replace(parameters('namingConvention').functionApp, parameters('serviceToken'), variables('service')), resourceGroup().id))))[0].ipAddresses[0]]" + }, + "privateDnsZoneName": { + "value": "[split(variables('functionAppScmPrivateDnsZoneResourceId'), '/')[8]]" } }, "template": { @@ -9894,97 +8863,137 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "5317974790875443937" + "version": "0.31.92.45157", + "templateHash": "2293260329720441287" } }, "parameters": { - "asyncExecution": { - "type": "bool", - "defaultValue": false - }, - "location": { - "type": "string" - }, - "name": { + "functionAppName": { "type": "string" }, - "parameters": { - "type": "array", - "defaultValue": [] - }, - "script": { + "ipv4Address": { "type": "string" }, - "tags": { - "type": "object" - }, - "treatFailureAsDeploymentFailure": { - "type": "bool", - "defaultValue": true - }, - "virtualMachineName": { + "privateDnsZoneName": { "type": "string" } }, "resources": [ { - "type": "Microsoft.Compute/virtualMachines/runCommands", - "apiVersion": "2023-09-01", - "name": "[format('{0}/{1}', parameters('virtualMachineName'), parameters('name'))]", - "location": "[parameters('location')]", - "tags": "[parameters('tags')]", + "type": "Microsoft.Network/privateDnsZones/A", + "apiVersion": "2020-06-01", + "name": "[format('{0}/{1}', parameters('privateDnsZoneName'), parameters('functionAppName'))]", "properties": { - "asyncExecution": "[parameters('asyncExecution')]", - "parameters": "[parameters('parameters')]", - "source": { - "script": "[parameters('script')]" - }, - "treatFailureAsDeploymentFailure": "[parameters('treatFailureAsDeploymentFailure')]" + "aRecords": [ + { + "ipv4Address": "[parameters('ipv4Address')]" + } + ], + "ttl": 3600 } } ] } }, "dependsOn": [ - "[resourceId('Microsoft.DesktopVirtualization/applicationGroups', parameters('desktopApplicationGroupName'))]", - "[extensionResourceId(resourceId('Microsoft.DesktopVirtualization/applicationGroups', parameters('desktopApplicationGroupName')), 'Microsoft.Authorization/roleAssignments', guid(parameters('deploymentUserAssignedIdentityPrincipalId'), '86240b0e-9422-4c43-887b-b61143f32ba8', resourceId('Microsoft.DesktopVirtualization/applicationGroups', parameters('desktopApplicationGroupName'))))]" + "[resourceId('Microsoft.Web/sites', uniqueString(replace(parameters('namingConvention').functionApp, parameters('serviceToken'), variables('service')), resourceGroup().id))]", + "[resourceId('Microsoft.Network/privateEndpoints/privateDnsZoneGroups', replace(parameters('namingConvention').functionAppPrivateEndpoint, parameters('serviceToken'), variables('service')), 'default')]" ] } ], "outputs": { - "resourceId": { + "functionAppName": { "type": "string", - "value": "[resourceId('Microsoft.DesktopVirtualization/applicationGroups', parameters('desktopApplicationGroupName'))]" + "value": "[uniqueString(replace(parameters('namingConvention').functionApp, parameters('serviceToken'), variables('service')), resourceGroup().id)]" + }, + "functionAppPrincipalId": { + "type": "string", + "value": "[reference(resourceId('Microsoft.Web/sites', uniqueString(replace(parameters('namingConvention').functionApp, parameters('serviceToken'), variables('service')), resourceGroup().id)), '2023-01-01', 'full').identity.principalId]" } } } }, "dependsOn": [ - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupControlPlane')), 'Microsoft.Resources/deployments', format('deploy-vdpool-{0}', parameters('deploymentNameSuffix')))]" + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-vdpool-{0}', parameters('deploymentNameSuffix')))]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-monitoring-{0}', parameters('deploymentNameSuffix')))]", + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('resourceGroupName'))]" ] } ], "outputs": { "applicationGroupResourceId": { "type": "string", - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupControlPlane')), 'Microsoft.Resources/deployments', format('deploy-vdag-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceId.value]" + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-vdag-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceId.value]" + }, + "dataCollectionRuleResourceId": { + "type": "string", + "value": "[if(parameters('enableAvdInsights'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-monitoring-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.dataCollectionRuleResourceId.value, '')]" + }, + "deploymentUserAssignedIdentityClientId": { + "type": "string", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-id-deployment-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.clientId.value]" + }, + "deploymentUserAssignedIdentityPrincipalId": { + "type": "string", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-id-deployment-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.principalId.value]" + }, + "deploymentUserAssignedIdentityResourceId": { + "type": "string", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-id-deployment-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceId.value]" + }, + "diskAccessPolicyDefinitionId": { + "type": "string", + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-policy-disks-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.policyDefinitionId.value]" + }, + "diskAccessPolicyDisplayName": { + "type": "string", + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-policy-disks-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.policyDisplayName.value]" + }, + "diskAccessResourceId": { + "type": "string", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-disk-access-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceId.value]" + }, + "functionAppPrincipalId": { + "type": "string", + "value": "[if(equals(parameters('fslogixStorageService'), 'AzureFiles Premium'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-function-app-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.functionAppPrincipalId.value, '')]" }, "hostPoolName": { "type": "string", - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupControlPlane')), 'Microsoft.Resources/deployments', format('deploy-vdpool-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.name.value]" + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-vdpool-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.name.value]" }, "hostPoolResourceId": { "type": "string", - "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupControlPlane')), 'Microsoft.Resources/deployments', format('deploy-vdpool-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceId.value]" + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-vdpool-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceId.value]" + }, + "logAnalyticsWorkspaceName": { + "type": "string", + "value": "[if(or(parameters('enableApplicationInsights'), parameters('enableAvdInsights')), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-monitoring-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.logAnalyticsWorkspaceName.value, '')]" + }, + "logAnalyticsWorkspaceResourceId": { + "type": "string", + "value": "[if(or(parameters('enableApplicationInsights'), parameters('enableAvdInsights')), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-monitoring-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.logAnalyticsWorkspaceResourceId.value, '')]" + }, + "recoveryServicesVaultName": { + "type": "string", + "value": "[if(parameters('recoveryServices'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-rsv-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.name.value, '')]" + }, + "resourceGroupName": { + "type": "string", + "value": "[parameters('resourceGroupName')]" + }, + "virtualMachineName": { + "type": "string", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-mgmt-vm-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.name.value]" + }, + "virtualMachineResourceId": { + "type": "string", + "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-mgmt-vm-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceId.value]" } } } }, "dependsOn": [ - "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix')))]", - "[subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-cp-{0}', parameters('deploymentNameSuffix')))]", - "rgs", + "[subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-mgmt-{0}', parameters('deploymentNameSuffix')))]", "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix')))]" ] }, @@ -10001,7 +9010,7 @@ "mode": "Incremental", "parameters": { "applicationGroupResourceId": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-control-plane-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.applicationGroupResourceId.value]" + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.applicationGroupResourceId.value]" }, "avdPrivateDnsZoneResourceId": { "value": "[format('{0}{1}', variables('privateDnsZoneResourceIdPrefix'), filter(reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.privateDnsZones.value, lambda('name', startsWith(lambdaVariables('name'), 'privatelink.wvd')))[0])]" @@ -10026,7 +9035,7 @@ "value": "[not(empty(parameters('existingFeedWorkspaceResourceId')))]" }, "hostPoolName": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-control-plane-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.hostPoolName.value]" + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.hostPoolName.value]" }, "locationControlPlane": { "value": "[reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Network/virtualNetworks', split(parameters('hubVirtualNetworkResourceId'), '/')[8]), '2023-11-01', 'full').location]" @@ -10044,7 +9053,7 @@ "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.mlzTags.value]" }, "resourceGroupManagement": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', variables('resourceGroupServices')[2], parameters('deploymentNameSuffix'))), '2022-09-01').outputs.name.value]" + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceGroupName.value]" }, "sharedServicesSubnetResourceId": { "value": "[parameters('sharedServicesSubnetResourceId')]" @@ -10053,35 +9062,35 @@ "value": "[parameters('tags')]" }, "workspaceFeedDiagnoticSettingName": { - "value": "[replace(replace(reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-hub-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.names.value.workspaceFeedDiagnosticSetting, reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-hub-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value.service, 'feed'), format('-{0}', parameters('stampIndex')), '')]" + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-mgmt-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.names.value.workspaceFeedDiagnosticSetting]" }, "workspaceFeedName": { - "value": "[replace(replace(reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-cp-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.names.value.workspaceFeed, reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-cp-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value.service, 'feed'), format('-{0}', parameters('stampIndex')), '')]" + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-mgmt-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.names.value.workspaceFeed]" }, "workspaceFeedNetworkInterfaceName": { - "value": "[replace(replace(reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-hub-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.names.value.workspaceFeedNetworkInterface, reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-hub-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value.service, 'feed'), format('-{0}', parameters('stampIndex')), '')]" + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-mgmt-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.names.value.workspaceFeedNetworkInterface]" }, "workspaceFeedPrivateEndpointName": { - "value": "[replace(replace(reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-hub-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.names.value.workspaceFeedPrivateEndpoint, reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-hub-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value.service, 'feed'), format('-{0}', parameters('stampIndex')), '')]" + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-mgmt-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.names.value.workspaceFeedPrivateEndpoint]" }, "workspaceFeedResourceGroupName": { - "value": "[replace(replace(reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-cp-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.names.value.resourceGroup, reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-cp-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value.service, 'feedWorkspace'), format('-{0}', parameters('stampIndex')), '')]" + "value": "[replace(replace(reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-mgmt-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.names.value.resourceGroup, reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-mgmt-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value.service, 'feedWorkspace'), format('-{0}', parameters('stampIndex')), '')]" }, - "workspaceFriendlyName": "[if(empty(parameters('workspaceFriendlyName')), createObject('value', replace(replace(reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-cp-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.names.value.workspaceFeed, format('-{0}', reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-cp-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value.service), ''), format('-{0}', parameters('stampIndex')), '')), createObject('value', format('{0} ({1})', parameters('workspaceFriendlyName'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Network/virtualNetworks', split(parameters('hubVirtualNetworkResourceId'), '/')[8]), '2023-11-01', 'full').location)))]", + "workspaceFriendlyName": "[if(empty(parameters('workspaceFriendlyName')), createObject('value', replace(reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-mgmt-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.names.value.workspaceFeed, format('-{0}', reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-mgmt-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value.service), '')), createObject('value', format('{0} ({1})', parameters('workspaceFriendlyName'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Network/virtualNetworks', split(parameters('hubVirtualNetworkResourceId'), '/')[8]), '2023-11-01', 'full').location)))]", "workspaceGlobalName": { - "value": "[replace(replace(replace(reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-cp-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.names.value.workspaceGlobal, reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-cp-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value.service, 'global'), format('-{0}', parameters('stampIndex')), ''), parameters('identifier'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Network/virtualNetworks', split(parameters('hubVirtualNetworkResourceId'), '/')[8]), '2023-11-01', 'full').tags.resourcePrefix)]" + "value": "[replace(reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-mgmt-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.names.value.workspaceGlobal, parameters('identifier'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Network/virtualNetworks', split(parameters('hubVirtualNetworkResourceId'), '/')[8]), '2023-11-01', 'full').tags.resourcePrefix)]" }, "workspaceGlobalNetworkInterfaceName": { - "value": "[replace(replace(replace(reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-hub-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.names.value.workspaceGlobalNetworkInterface, reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-hub-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value.service, 'global'), format('-{0}', parameters('stampIndex')), ''), parameters('identifier'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Network/virtualNetworks', split(parameters('hubVirtualNetworkResourceId'), '/')[8]), '2023-11-01', 'full').tags.resourcePrefix)]" + "value": "[replace(reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-mgmt-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.names.value.workspaceGlobalNetworkInterface, parameters('identifier'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Network/virtualNetworks', split(parameters('hubVirtualNetworkResourceId'), '/')[8]), '2023-11-01', 'full').tags.resourcePrefix)]" }, "workspaceGlobalPrivateDnsZoneResourceId": { "value": "[format('{0}{1}', variables('privateDnsZoneResourceIdPrefix'), filter(reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.privateDnsZones.value, lambda('name', startsWith(lambdaVariables('name'), 'privatelink-global.wvd')))[0])]" }, "workspaceGlobalPrivateEndpointName": { - "value": "[replace(replace(replace(reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-hub-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.names.value.workspaceGlobalPrivateEndpoint, reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-hub-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value.service, 'global'), format('-{0}', parameters('stampIndex')), ''), parameters('identifier'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Network/virtualNetworks', split(parameters('hubVirtualNetworkResourceId'), '/')[8]), '2023-11-01', 'full').tags.resourcePrefix)]" + "value": "[replace(reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-mgmt-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.names.value.workspaceGlobalPrivateEndpoint, parameters('identifier'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Network/virtualNetworks', split(parameters('hubVirtualNetworkResourceId'), '/')[8]), '2023-11-01', 'full').tags.resourcePrefix)]" }, "workspaceGlobalResourceGroupName": { - "value": "[replace(replace(replace(reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-cp-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.names.value.resourceGroup, reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-cp-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value.service, 'globalWorkspace'), format('-{0}', parameters('stampIndex')), ''), parameters('identifier'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Network/virtualNetworks', split(parameters('hubVirtualNetworkResourceId'), '/')[8]), '2023-11-01', 'full').tags.resourcePrefix)]" + "value": "[replace(replace(replace(reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-mgmt-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.names.value.resourceGroup, reference(subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-mgmt-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value.service, 'globalWorkspace'), format('-{0}', parameters('stampIndex')), ''), parameters('identifier'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', split(parameters('hubVirtualNetworkResourceId'), '/')[2], split(parameters('hubVirtualNetworkResourceId'), '/')[4]), 'Microsoft.Network/virtualNetworks', split(parameters('hubVirtualNetworkResourceId'), '/')[8]), '2023-11-01', 'full').tags.resourcePrefix)]" }, "workspacePublicNetworkAccess": { "value": "[parameters('workspacePublicNetworkAccess')]" @@ -10093,8 +9102,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "6641458391732609020" + "version": "0.31.92.45157", + "templateHash": "140763294646618507" } }, "parameters": { @@ -10221,8 +9230,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "17445726037807437290" + "version": "0.31.92.45157", + "templateHash": "9371138343009436350" } }, "parameters": { @@ -10309,8 +9318,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "977555068735579352" + "version": "0.31.92.45157", + "templateHash": "3251230175900717239" } }, "parameters": { @@ -10429,8 +9438,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "17445726037807437290" + "version": "0.31.92.45157", + "templateHash": "9371138343009436350" } }, "parameters": { @@ -10511,8 +9520,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "1315528727979495191" + "version": "0.31.92.45157", + "templateHash": "10174155731090308034" } }, "parameters": { @@ -10572,8 +9581,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "1315528727979495191" + "version": "0.31.92.45157", + "templateHash": "10174155731090308034" } }, "parameters": { @@ -10686,8 +9695,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "11735323070375319176" + "version": "0.31.92.45157", + "templateHash": "10426142527062787497" } }, "parameters": { @@ -10894,8 +9903,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "5317974790875443937" + "version": "0.31.92.45157", + "templateHash": "6896852651895359166" } }, "parameters": { @@ -10959,11 +9968,8 @@ } }, "dependsOn": [ - "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-control-plane-{0}', parameters('deploymentNameSuffix')))]", "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix')))]", - "[subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-cp-{0}', parameters('deploymentNameSuffix')))]", - "[subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-hub-{0}', parameters('deploymentNameSuffix')))]", - "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', variables('resourceGroupServices')[2], parameters('deploymentNameSuffix')))]", + "[subscriptionResourceId('Microsoft.Resources/deployments', format('get-naming-mgmt-{0}', parameters('deploymentNameSuffix')))]", "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix')))]" ] }, @@ -10994,6 +10000,9 @@ "deploymentUserAssignedIdentityClientId": { "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.deploymentUserAssignedIdentityClientId.value]" }, + "deploymentUserAssignedIdentityPrincipalId": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.deploymentUserAssignedIdentityPrincipalId.value]" + }, "dnsServers": { "value": "[string(reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.dnsServers.value)]" }, @@ -11024,8 +10033,11 @@ "fslogixStorageService": { "value": "[parameters('fslogixStorageService')]" }, - "functionAppName": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.functionAppName.value]" + "functionAppPrincipalId": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.functionAppPrincipalId.value]" + }, + "hostPoolResourceId": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.hostPoolResourceId.value]" }, "keyVaultUri": { "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.keyVaultUri.value]" @@ -11051,15 +10063,14 @@ "recoveryServices": { "value": "[parameters('recoveryServices')]" }, - "resourceGroupControlPlane": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', variables('resourceGroupServices')[0], parameters('deploymentNameSuffix'))), '2022-09-01').outputs.name.value]" - }, "resourceGroupManagement": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', variables('resourceGroupServices')[2], parameters('deploymentNameSuffix'))), '2022-09-01').outputs.name.value]" + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceGroupName.value]" + }, + "resourceGroupName": { + "value": "[replace(reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.namingConvention.value.resourceGroup, reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value.service, 'profiles')]" }, - "resourceGroupStorage": "[if(variables('deployFslogix'), createObject('value', reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', variables('resourceGroupServices')[3], parameters('deploymentNameSuffix'))), '2022-09-01').outputs.name.value), createObject('value', ''))]", "securityPrincipalNames": { - "value": "[map(parameters('securityPrincipals'), lambda('item', lambdaVariables('item').name))]" + "value": "[map(parameters('securityPrincipals'), lambda('item', lambdaVariables('item').displayName))]" }, "securityPrincipalObjectIds": { "value": "[map(parameters('securityPrincipals'), lambda('item', lambdaVariables('item').objectId))]" @@ -11101,14 +10112,11 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "17423392577111200184" + "version": "0.31.92.45157", + "templateHash": "14643897230641833486" } }, "parameters": { - "existingSharedActiveDirectoryConnection": { - "type": "bool" - }, "activeDirectorySolution": { "type": "string" }, @@ -11127,6 +10135,9 @@ "deploymentUserAssignedIdentityClientId": { "type": "string" }, + "deploymentUserAssignedIdentityPrincipalId": { + "type": "string" + }, "dnsServers": { "type": "string" }, @@ -11142,19 +10153,25 @@ "encryptionUserAssignedIdentityResourceId": { "type": "string" }, + "existingSharedActiveDirectoryConnection": { + "type": "bool" + }, "fileShares": { "type": "array" }, + "fslogixContainerType": { + "type": "string" + }, "fslogixShareSizeInGB": { "type": "int" }, - "fslogixContainerType": { + "fslogixStorageService": { "type": "string" }, - "fslogixStorageService": { + "functionAppPrincipalId": { "type": "string" }, - "functionAppName": { + "hostPoolResourceId": { "type": "string" }, "keyVaultUri": { @@ -11181,13 +10198,10 @@ "recoveryServices": { "type": "bool" }, - "resourceGroupControlPlane": { - "type": "string" - }, "resourceGroupManagement": { "type": "string" }, - "resourceGroupStorage": { + "resourceGroupName": { "type": "string" }, "securityPrincipalObjectIds": { @@ -11225,17 +10239,144 @@ } }, "variables": { - "hostPoolName": "[parameters('namingConvention').hostPool]", - "tagsNetAppAccount": "[union(createObject('cm-resource-parent', format('{0}}}/resourceGroups/{1}/providers/Microsoft.DesktopVirtualization/hostpools/{2}', subscription().id, parameters('resourceGroupControlPlane'), variables('hostPoolName'))), if(contains(parameters('tags'), 'Microsoft.NetApp/netAppAccounts'), parameters('tags')['Microsoft.NetApp/netAppAccounts'], createObject()), parameters('mlzTags'))]", - "tagsVirtualMachines": "[union(createObject('cm-resource-parent', format('{0}}}/resourceGroups/{1}/providers/Microsoft.DesktopVirtualization/hostpools/{2}', subscription().id, parameters('resourceGroupControlPlane'), variables('hostPoolName'))), if(contains(parameters('tags'), 'Microsoft.Compute/virtualMachines'), parameters('tags')['Microsoft.Compute/virtualMachines'], createObject()), parameters('mlzTags'))]" + "tagsNetAppAccount": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), coalesce(tryGet(parameters('tags'), 'Microsoft.NetApp/netAppAccounts'), createObject()), parameters('mlzTags'))]", + "tagsVirtualMachines": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), coalesce(tryGet(parameters('tags'), 'Microsoft.Compute/virtualMachines'), createObject()), parameters('mlzTags'))]" }, "resources": [ + { + "type": "Microsoft.Resources/resourceGroups", + "apiVersion": "2023-07-01", + "name": "[parameters('resourceGroupName')]", + "location": "[parameters('location')]", + "tags": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), coalesce(tryGet(parameters('tags'), 'Microsoft.Resources/resourceGroups'), createObject()), parameters('mlzTags'))]" + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('assign-role-storage-{0}', parameters('deploymentNameSuffix'))]", + "resourceGroup": "[parameters('resourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "principalId": { + "value": "[parameters('deploymentUserAssignedIdentityPrincipalId')]" + }, + "principalType": { + "value": "ServicePrincipal" + }, + "roleDefinitionId": { + "value": "17d1049b-9a84-46fb-8f53-869881c3d3ab" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "10174155731090308034" + } + }, + "parameters": { + "principalId": { + "type": "string" + }, + "principalType": { + "type": "string" + }, + "roleDefinitionId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(parameters('principalId'), parameters('roleDefinitionId'), resourceGroup().id)]", + "properties": { + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitionId'))]", + "principalId": "[parameters('principalId')]", + "principalType": "[parameters('principalType')]" + } + } + ] + } + }, + "dependsOn": [ + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('resourceGroupName'))]" + ] + }, + { + "condition": "[equals(parameters('fslogixStorageService'), 'AzureFiles Premium')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('set-role-assignment-{0}', parameters('deploymentNameSuffix'))]", + "resourceGroup": "[parameters('resourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "principalId": { + "value": "[parameters('functionAppPrincipalId')]" + }, + "principalType": { + "value": "ServicePrincipal" + }, + "roleDefinitionId": { + "value": "17d1049b-9a84-46fb-8f53-869881c3d3ab" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "10174155731090308034" + } + }, + "parameters": { + "principalId": { + "type": "string" + }, + "principalType": { + "type": "string" + }, + "roleDefinitionId": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/roleAssignments", + "apiVersion": "2022-04-01", + "name": "[guid(parameters('principalId'), parameters('roleDefinitionId'), resourceGroup().id)]", + "properties": { + "roleDefinitionId": "[resourceId('Microsoft.Authorization/roleDefinitions', parameters('roleDefinitionId'))]", + "principalId": "[parameters('principalId')]", + "principalType": "[parameters('principalType')]" + } + } + ] + } + }, + "dependsOn": [ + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('resourceGroupName'))]" + ] + }, { "condition": "[equals(parameters('storageService'), 'AzureNetAppFiles')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('deploy-anf-{0}', parameters('deploymentNameSuffix'))]", - "resourceGroup": "[parameters('resourceGroupStorage')]", + "resourceGroup": "[parameters('resourceGroupName')]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -11312,8 +10453,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "2650625569344244687" + "version": "0.31.92.45157", + "templateHash": "17417543434535800430" } }, "parameters": { @@ -11514,8 +10655,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "4329443301777514730" + "version": "0.31.92.45157", + "templateHash": "849953818393496765" } }, "parameters": { @@ -11586,14 +10727,17 @@ } } } - } + }, + "dependsOn": [ + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('resourceGroupName'))]" + ] }, { "condition": "[equals(parameters('storageService'), 'AzureFiles')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('deploy-azure-files-{0}', parameters('deploymentNameSuffix'))]", - "resourceGroup": "[parameters('resourceGroupStorage')]", + "resourceGroup": "[parameters('resourceGroupName')]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -11636,11 +10780,8 @@ "fslogixShareSizeInGB": { "value": "[parameters('fslogixShareSizeInGB')]" }, - "fslogixStorageService": { - "value": "[parameters('fslogixStorageService')]" - }, - "functionAppName": { - "value": "[parameters('functionAppName')]" + "hostPoolResourceId": { + "value": "[parameters('hostPoolResourceId')]" }, "keyVaultUri": { "value": "[parameters('keyVaultUri')]" @@ -11666,9 +10807,6 @@ "resourceGroupManagement": { "value": "[parameters('resourceGroupManagement')]" }, - "resourceGroupStorage": { - "value": "[parameters('resourceGroupStorage')]" - }, "securityPrincipalNames": { "value": "[parameters('securityPrincipalNames')]" }, @@ -11699,14 +10837,8 @@ "tags": { "value": "[parameters('tags')]" }, - "hostPoolName": { - "value": "[variables('hostPoolName')]" - }, "mlzTags": { "value": "[parameters('mlzTags')]" - }, - "resourceGroupControlPlane": { - "value": "[parameters('resourceGroupControlPlane')]" } }, "template": { @@ -11715,8 +10847,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "10921134071123326707" + "version": "0.31.92.45157", + "templateHash": "2895952650836829415" } }, "parameters": { @@ -11756,13 +10888,7 @@ "fslogixContainerType": { "type": "string" }, - "fslogixStorageService": { - "type": "string" - }, - "functionAppName": { - "type": "string" - }, - "hostPoolName": { + "hostPoolResourceId": { "type": "string" }, "keyVaultUri": { @@ -11789,15 +10915,9 @@ "recoveryServicesVaultName": { "type": "string" }, - "resourceGroupControlPlane": { - "type": "string" - }, "resourceGroupManagement": { "type": "string" }, - "resourceGroupStorage": { - "type": "string" - }, "securityPrincipalObjectIds": { "type": "array" }, @@ -11831,9 +10951,6 @@ }, "variables": { "$fxv#0": "param \r\n(\r\n [String]$ActiveDirectorySolution,\r\n [String]$DomainJoinPassword,\r\n [String]$DomainJoinUserPrincipalName,\r\n [String]$FslogixContainerType,\r\n [String]$Netbios,\r\n [String]$OrganizationalUnitPath,\r\n [string]$ResourceManagerUri,\r\n [String]$SecurityPrincipalNames,\r\n [String]$SmbServerLocation,\r\n [String]$StorageAccountPrefix,\r\n [String]$StorageAccountResourceGroupName,\r\n [Int]$StorageCount,\r\n [Int]$StorageIndex,\r\n [String]$StorageService,\r\n [String]$StorageSuffix,\r\n [String]$SubscriptionId,\r\n [String]$UserAssignedIdentityClientId\r\n)\r\n\r\n$ErrorActionPreference = 'Stop'\r\n$WarningPreference = 'SilentlyContinue'\r\n\r\n##############################################################\r\n# Install Active Directory PowerShell module\r\n##############################################################\r\nif($StorageService -eq 'AzureNetAppFiles' -or ($StorageService -eq 'AzureFiles' -and $ActiveDirectorySolution -eq 'ActiveDirectoryDomainServices'))\r\n{\r\n $RsatInstalled = (Get-WindowsFeature -Name 'RSAT-AD-PowerShell').Installed\r\n if(!$RsatInstalled)\r\n {\r\n Install-WindowsFeature -Name 'RSAT-AD-PowerShell' | Out-Null\r\n }\r\n}\r\n\r\n\r\n##############################################################\r\n# Variables\r\n##############################################################\r\n# Convert Security Principal Names from a JSON array to a PowerShell array\r\n[array]$SecurityPrincipalNames = $SecurityPrincipalNames.Replace('\\','') | ConvertFrom-Json\r\n\r\n# Selects the appropraite share names based on the FslogixContainerType param from the deployment\r\n$Shares = switch($FslogixContainerType)\r\n{\r\n 'CloudCacheProfileContainer' {@('profile-containers')}\r\n 'CloudCacheProfileOfficeContainer' {@('office-containers','profile-containers')}\r\n 'ProfileContainer' {@('profile-containers')}\r\n 'ProfileOfficeContainer' {@('office-containers','profile-containers')}\r\n}\r\n\r\nif($StorageService -eq 'AzureNetAppFiles' -or ($StorageService -eq 'AzureFiles' -and $ActiveDirectorySolution -eq 'ActiveDirectoryDomainServices'))\r\n{\r\n # Create Domain credential\r\n $DomainUsername = $DomainJoinUserPrincipalName\r\n $DomainPassword = ConvertTo-SecureString -String $DomainJoinPassword -AsPlainText -Force\r\n [pscredential]$DomainCredential = New-Object System.Management.Automation.PSCredential ($DomainUsername, $DomainPassword)\r\n\r\n # Get Domain information\r\n $Domain = Get-ADDomain -Credential $DomainCredential -Current 'LocalComputer'\r\n}\r\n\r\nif($StorageService -eq 'AzureFiles')\r\n{\r\n $FilesSuffix = '.file.' + $StorageSuffix\r\n\r\n # Fix the resource manager URI since only AzureCloud contains a trailing slash\r\n $ResourceManagerUriFixed = if ($ResourceManagerUri[-1] -eq '/') { $ResourceManagerUri.Substring(0, $ResourceManagerUri.Length - 1) } else { $ResourceManagerUri }\r\n\r\n # Get an access token for Azure resources\r\n $AzureManagementAccessToken = (Invoke-RestMethod `\r\n -Headers @{Metadata = \"true\" } `\r\n -Uri $('http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=' + $ResourceManagerUriFixed + '&client_id=' + $UserAssignedIdentityClientId)).access_token\r\n\r\n # Set header for Azure Management API\r\n $AzureManagementHeader = @{\r\n 'Content-Type' = 'application/json'\r\n 'Authorization' = 'Bearer ' + $AzureManagementAccessToken\r\n }\r\n}\r\n\r\n\r\n##############################################################\r\n# Process Storage Resources\r\n##############################################################\r\nfor($i = 0; $i -lt $StorageCount; $i++)\r\n{\r\n # Determine Principal for assignment\r\n $SecurityPrincipalName = $SecurityPrincipalNames[$i]\r\n $Group = $Netbios + '\\' + $SecurityPrincipalName\r\n\r\n # Get storage resource details\r\n switch($StorageService)\r\n {\r\n 'AzureNetAppFiles' {\r\n $Credential = $DomainCredential\r\n $SmbServerName = (Get-ADComputer -Filter \"Name -like 'anf-$SmbServerLocation*'\" -Credential $DomainCredential).Name\r\n $FileServer = '\\\\' + $SmbServerName + '.' + $Domain.DNSRoot\r\n }\r\n 'AzureFiles' {\r\n $StorageAccountName = $($StorageAccountPrefix + ($i + $StorageIndex).ToString().PadLeft(2,'0')).Substring(0,15)\r\n $FileServer = '\\\\' + $StorageAccountName + $FilesSuffix\r\n\r\n # Get the storage account key\r\n $StorageKey = (Invoke-RestMethod `\r\n -Headers $AzureManagementHeader `\r\n -Method 'POST' `\r\n -Uri $($ResourceManagerUriFixed + '/subscriptions/' + $SubscriptionId + '/resourceGroups/' + $StorageAccountResourceGroupName + '/providers/Microsoft.Storage/storageAccounts/' + $StorageAccountName + '/listKeys?api-version=2023-05-01')).keys[0].value\r\n\r\n # Create credential for accessing the storage account\r\n $StorageUsername = 'Azure\\' + $StorageAccountName\r\n $StoragePassword = ConvertTo-SecureString -String \"$($StorageKey)\" -AsPlainText -Force\r\n [pscredential]$StorageKeyCredential = New-Object System.Management.Automation.PSCredential ($StorageUsername, $StoragePassword)\r\n $Credential = $StorageKeyCredential\r\n\r\n if($ActiveDirectorySolution -eq 'ActiveDirectoryDomainServices')\r\n {\r\n # Get / create kerberos key for Azure Storage Account\r\n $KerberosKey = ((Invoke-RestMethod `\r\n -Headers $AzureManagementHeader `\r\n -Method 'POST' `\r\n -Uri $($ResourceManagerUriFixed + '/subscriptions/' + $SubscriptionId + '/resourceGroups/' + $StorageAccountResourceGroupName + '/providers/Microsoft.Storage/storageAccounts/' + $StorageAccountName + '/listKeys?api-version=2023-05-01&$expand=kerb')).keys | Where-Object { $_.Keyname -contains 'kerb1' }).Value\r\n \r\n if (!$KerberosKey) \r\n {\r\n Invoke-RestMethod `\r\n -Body (@{keyName = 'kerb1' } | ConvertTo-Json) `\r\n -Headers $AzureManagementHeader `\r\n -Method 'POST' `\r\n -Uri $($ResourceManagerUriFixed + '/subscriptions/' + $SubscriptionId + '/resourceGroups/' + $StorageAccountResourceGroupName + '/providers/Microsoft.Storage/storageAccounts/' + $StorageAccountName + '/regenerateKey?api-version=2023-05-01')\r\n \r\n $Key = ((Invoke-RestMethod `\r\n -Headers $AzureManagementHeader `\r\n -Method 'POST' `\r\n -Uri $($ResourceManagerUriFixed + '/subscriptions/' + $SubscriptionId + '/resourceGroups/' + $StorageAccountResourceGroupName + '/providers/Microsoft.Storage/storageAccounts/' + $StorageAccountName + '/listKeys?api-version=2023-05-01&$expand=kerb')).keys | Where-Object { $_.Keyname -contains 'kerb1' }).Value\r\n } \r\n else \r\n {\r\n $Key = $KerberosKey\r\n }\r\n\r\n # Creates a password for the Azure Storage Account in AD using the Kerberos key\r\n $ComputerPassword = ConvertTo-SecureString -String $Key.Replace(\"'\",\"\") -AsPlainText -Force\r\n\r\n # Create the SPN value for the Azure Storage Account; attribute for computer object in AD \r\n $SPN = 'cifs/' + $StorageAccountName + $FilesSuffix\r\n\r\n # Create the Description value for the Azure Storage Account; attribute for computer object in AD \r\n $Description = \"Computer account object for Azure storage account $($StorageAccountName).\"\r\n\r\n # Create the AD computer object for the Azure Storage Account\r\n $Computer = Get-ADComputer -Credential $DomainCredential -Filter {Name -eq $StorageAccountName}\r\n if($Computer)\r\n {\r\n Remove-ADComputer -Credential $DomainCredential -Identity $StorageAccountName -Confirm:$false\r\n }\r\n $ComputerObject = New-ADComputer -Credential $DomainCredential -Name $StorageAccountName -Path $OrganizationalUnitPath -ServicePrincipalNames $SPN -AccountPassword $ComputerPassword -Description $Description -PassThru\r\n\r\n $Body = (@{\r\n properties = @{\r\n azureFilesIdentityBasedAuthentication = @{\r\n activeDirectoryProperties = @{\r\n accountType = 'Computer'\r\n azureStorageSid = $ComputerObject.SID.Value\r\n domainGuid = $Domain.ObjectGUID.Guid\r\n domainName = $Domain.DNSRoot\r\n domainSid = $Domain.DomainSID.Value\r\n forestName = $Domain.Forest\r\n netBiosDomainName = $Domain.NetBIOSName\r\n samAccountName = $StorageAccountName\r\n }\r\n directoryServiceOptions = 'AD'\r\n }\r\n }\r\n } | ConvertTo-Json -Depth 6 -Compress)\r\n\r\n Invoke-RestMethod `\r\n -Body $Body `\r\n -Headers $AzureManagementHeader `\r\n -Method 'PATCH' `\r\n -Uri $($ResourceManagerUriFixed + '/subscriptions/' + $SubscriptionId + '/resourceGroups/' + $StorageAccountResourceGroupName + '/providers/Microsoft.Storage/storageAccounts/' + $StorageAccountName + '?api-version=2023-05-01')\r\n \r\n # Set the Kerberos encryption on the computer object\r\n $DistinguishedName = 'CN=' + $StorageAccountName + ',' + $OrganizationalUnitPath\r\n Set-ADComputer -Credential $DomainCredential -Identity $DistinguishedName -KerberosEncryptionType 'AES256' | Out-Null\r\n \r\n # Reset the Kerberos key on the Storage Account\r\n Invoke-RestMethod `\r\n -Body (@{keyName = 'kerb1' } | ConvertTo-Json) `\r\n -Headers $AzureManagementHeader `\r\n -Method 'POST' `\r\n -Uri $($ResourceManagerUriFixed + '/subscriptions/' + $SubscriptionId + '/resourceGroups/' + $StorageAccountResourceGroupName + '/providers/Microsoft.Storage/storageAccounts/' + $StorageAccountName + '/regenerateKey?api-version=2023-05-01')\r\n \r\n $Key = ((Invoke-RestMethod `\r\n -Headers $AzureManagementHeader `\r\n -Method 'POST' `\r\n -Uri $($ResourceManagerUriFixed + '/subscriptions/' + $SubscriptionId + '/resourceGroups/' + $StorageAccountResourceGroupName + '/providers/Microsoft.Storage/storageAccounts/' + $StorageAccountName + '/listKeys?api-version=2023-05-01&$expand=kerb')).keys | Where-Object { $_.Keyname -contains 'kerb1' }).Value\r\n \r\n # Update the password on the computer object with the new Kerberos key on the Storage Account\r\n $NewPassword = ConvertTo-SecureString -String $Key -AsPlainText -Force\r\n Set-ADAccountPassword -Credential $DomainCredential -Identity $DistinguishedName -Reset -NewPassword $NewPassword | Out-Null\r\n }\r\n }\r\n }\r\n \r\n foreach($Share in $Shares)\r\n {\r\n # Mount file share\r\n $FileShare = $FileServer + '\\' + $Share\r\n New-PSDrive -Name 'Z' -PSProvider 'FileSystem' -Root $FileShare -Credential $Credential | Out-Null\r\n\r\n # Set recommended NTFS permissions on the file share\r\n $ACL = Get-Acl -Path 'Z:'\r\n $CreatorOwner = New-Object System.Security.Principal.Ntaccount (\"Creator Owner\")\r\n $ACL.PurgeAccessRules($CreatorOwner)\r\n $AuthenticatedUsers = New-Object System.Security.Principal.Ntaccount (\"Authenticated Users\")\r\n $ACL.PurgeAccessRules($AuthenticatedUsers)\r\n $Users = New-Object System.Security.Principal.Ntaccount (\"Users\")\r\n $ACL.PurgeAccessRules($Users)\r\n $DomainUsers = New-Object System.Security.AccessControl.FileSystemAccessRule(\"$Group\",\"Modify\",\"None\",\"None\",\"Allow\")\r\n $ACL.SetAccessRule($DomainUsers)\r\n $CreatorOwner = New-Object System.Security.AccessControl.FileSystemAccessRule(\"Creator Owner\",\"Modify\",\"ContainerInherit,ObjectInherit\",\"InheritOnly\",\"Allow\")\r\n $ACL.AddAccessRule($CreatorOwner)\r\n $ACL | Set-Acl -Path 'Z:' | Out-Null\r\n\r\n # Unmount file share\r\n Remove-PSDrive -Name 'Z' -PSProvider 'FileSystem' -Force | Out-Null\r\n Start-Sleep -Seconds 5 | Out-Null\r\n }\r\n}", - "$fxv#1": "# This file enables modules to be automatically managed by the Functions service.\r\n# See https://aka.ms/functionsmanageddependency for additional information.\r\n#\r\n@{\r\n # For latest supported version, go to 'https://www.powershellgallery.com/packages/Az'. \r\n # To use the Az module in your function app, please uncomment the line below.\r\n # 'Az' = '7.*'\r\n}", - "$fxv#2": "param($Timer)\r\n\r\ntry\r\n{\r\n\t[string]$FileShareName = $env:FileShareName\r\n\t[string]$ResourceGroupName = $env:ResourceGroupName\r\n\t[string]$ResourceManagerUrl = $env:ResourceManagerUrl\r\n\t[string]$StorageSuffix = $env:StorageSuffix\r\n\t[string]$SubscriptionId = $env:SubscriptionId\r\n\r\n\t$ErrorActionPreference = 'Stop'\r\n\t$WarningPreference = 'SilentlyContinue'\r\n\r\n\t#region Functions\r\n\tfunction Write-Log \r\n {\r\n\t\t[CmdletBinding()]\r\n\t\tparam (\r\n\t\t\t[Parameter(Mandatory = $false)]\r\n\t\t\t[switch]$Err,\r\n\r\n\t\t\t[Parameter(Mandatory = $true)]\r\n\t\t\t[string]$Message,\r\n\r\n\t\t\t[Parameter(Mandatory = $true)]\r\n\t\t\t[string]$ResourceName,\r\n\r\n\t\t\t[Parameter(Mandatory = $false)]\r\n\t\t\t[switch]$Warn\r\n\t\t)\r\n\r\n\t\t[string]$MessageTimeStamp = Get-Date -Format 'yyyy-MM-dd HH:mm:ss'\r\n\t\t$Message = \"[$($MyInvocation.ScriptLineNumber)] [$($ResourceName)] $Message\"\r\n\t\t[string]$WriteMessage = \"[$($MessageTimeStamp)] $Message\"\r\n\r\n\t\tif ($Err)\r\n {\r\n\t\t\tWrite-Error $WriteMessage\r\n\t\t\t$Message = \"ERROR: $Message\"\r\n\t\t}\r\n\t\telseif ($Warn)\r\n {\r\n\t\t\tWrite-Warning $WriteMessage\r\n\t\t\t$Message = \"WARN: $Message\"\r\n\t\t}\r\n\t\telse \r\n {\r\n\t\t\tWrite-Output $WriteMessage\r\n\t\t}\r\n\t}\r\n\t#endregion Functions\r\n\r\n\t# Note: https://stackoverflow.com/questions/41674518/powershell-setting-security-protocol-to-tls-1-2\r\n\t[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12\r\n\r\n\r\n\t#region Azure Authentication\r\n $AccessToken = $null\r\n try\r\n {\r\n\t\t$TokenAuthURI = $env:IDENTITY_ENDPOINT + '?resource=' + $ResourceManagerUrl + '&api-version=2019-08-01'\r\n\t\t$TokenResponse = Invoke-RestMethod -Method Get -Headers @{\"X-IDENTITY-HEADER\"=\"$env:IDENTITY_HEADER\"} -Uri $TokenAuthURI\r\n\t\t$AccessToken = $TokenResponse.access_token\r\n\t\t$Header = @{\r\n\t\t\t'Content-Type'='application/json'\r\n\t\t\t'Authorization'='Bearer ' + $AccessToken\r\n\t\t}\r\n }\r\n catch\r\n {\r\n throw [System.Exception]::new('Failed to authenticate Azure with application ID, tenant ID, subscription ID', $PSItem.Exception)\r\n }\r\n Write-Log -ResourceName \"$SubscriptionId\" -Message \"Successfully authenticated with Azure using a managed identity\"\r\n\t#endregion Azure Authentication\r\n\r\n\t# Get storage accounts\r\n\t$Uri = $ResourceManagerUrl + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $ResourceGroupName + '/providers/Microsoft.Storage/storageAccounts?api-version=2023-05-01'\r\n\t$StorageAccountNames = (Invoke-RestMethod -Headers $Header -Method 'GET' -Uri $Uri).value.name\r\n\r\n\tforeach($StorageAccountName in $StorageAccountNames)\r\n\t{\r\n\t\t$ShareUpdateUri = $ResourceManagerUrl + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $ResourceGroupName + '/providers/Microsoft.Storage/storageAccounts/' + $StorageAccountName + '/fileServices/default/shares/' + $FileShareName + '?api-version=2023-05-01'\r\n\r\n\t\t# Get file share info\r\n\t\t$ShareGetUri = $ResourceManagerUrl + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $ResourceGroupName + '/providers/Microsoft.Storage/storageAccounts/' + $StorageAccountName + '/fileServices/default/shares/' + $FileShareName + '?api-version=2023-05-01&$expand=stats'\r\n\t\t$PFS = (Invoke-RestMethod -Headers $Header -Method 'GET' -Uri $ShareGetUri).properties\r\n\r\n\t\t# Set variables for provisioned capacity and used capacity\r\n\t\t$ProvisionedCapacity = $PFS.shareQuota\r\n\t\t$UsedCapacity = $PFS.ShareUsageBytes\r\n\t\tWrite-Log -ResourceName \"$StorageAccountName/$FileShareName\" -Message \"Share Capacity: $($ProvisionedCapacity)GB\"\r\n\t\tWrite-Log -ResourceName \"$StorageAccountName/$FileShareName\" -Message \"Share Usage: $([math]::Round($UsedCapacity/1GB, 0))GB\"\r\n\r\n\t\t# GB Based Scaling\r\n\t\t# No scaling if no usage\r\n\t\tif($UsedCapacity -eq 0)\r\n\t\t{\r\n\t\t\tWrite-Log -ResourceName \"$StorageAccountName/$FileShareName\" -Message \"Share Usage is 0GB. No Changes.\"\r\n\t\t}\r\n\t\t# Slow scaling up to 500GB\r\n\t\t# Increases share quota by 100GB if less than 50GB remains on the share\r\n\t\t# This allows time for an AVD Stamp to be rolled out \r\n\t\telseif ($ProvisionedCapacity -lt 500)\r\n\t\t{\r\n\t\t\tif (($ProvisionedCapacity - ($UsedCapacity / ([Math]::Pow(2,30)))) -lt 50) {\r\n\t\t\t\tWrite-Log -ResourceName \"$StorageAccountName/$FileShareName\" -Message \"Share Usage has surpassed the Share Quota remaining threshold of 50GB. Increasing the file share quota by 100GB.\" \r\n\t\t\t\t$Quota = $ProvisionedCapacity + 100\r\n\t\t\t\tInvoke-RestMethod `\r\n\t\t\t\t\t-Body (@{properties = @{shareQuota = $Quota}} | ConvertTo-Json) `\r\n\t\t\t\t\t-Headers $Header `\r\n\t\t\t\t\t-Method 'PATCH' `\r\n\t\t\t\t\t-Uri $ShareUpdateUri | Out-Null\r\n\t\t\t\tWrite-Log -ResourceName \"$StorageAccountName/$FileShareName\" -Message \"New Capacity: $($Quota)GB\"\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\tWrite-Log -ResourceName \"$StorageAccountName/$FileShareName\" -Message \"Share Usage is below Share Quota remaining threshold of 50GB. No Changes.\"\r\n\t\t\t}\r\n\t\t}\r\n\t\t# Aggressive scaling\r\n\t\t# Increases share quota by 500GB if less than 500GB remains on the share\r\n\t\t# This ensures plenty of space is available during mass onboarding\r\n\t\telse \r\n\t\t{\r\n\t\t\tif (($ProvisionedCapacity - ($UsedCapacity / ([Math]::Pow(2,30)))) -lt 500) {\r\n\t\t\t\tWrite-Log -ResourceName \"$StorageAccountName/$FileShareName\" -Message \"Share Usage has surpassed the Share Quota remaining threshold of 500GB. Increasing the file share quota by 500GB.\" \r\n\t\t\t\t$Quota = $ProvisionedCapacity + 500\r\n\t\t\t\tInvoke-RestMethod `\r\n\t\t\t\t\t-Body (@{properties = @{shareQuota = $Quota}} | ConvertTo-Json) `\r\n\t\t\t\t\t-Headers $Header `\r\n\t\t\t\t\t-Method 'PATCH' `\r\n\t\t\t\t\t-Uri $ShareUpdateUri | Out-Null\r\n\t\t\t\tWrite-Log -ResourceName \"$StorageAccountName/$FileShareName\" -Message \"New Capacity: $($Quota)GB\"\r\n\t\t\t}\r\n\t\t\telse {\r\n\t\t\t\tWrite-Log -ResourceName \"$StorageAccountName/$FileShareName\" -Message \"Share Usage is below Share Quota remaining threshold of 500GB. No Changes.\"\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n}\r\ncatch \r\n{\r\n\t$ErrContainer = $PSItem\r\n\t# $ErrContainer = $_\r\n\r\n\t[string]$ErrMsg = $ErrContainer | Format-List -Force | Out-String\r\n\t$ErrMsg += \"Version: $Version`n\"\r\n\r\n\tif (Get-Command 'Write-Log' -ErrorAction:SilentlyContinue)\r\n {\r\n\t\tWrite-Log -ResourceName \"$StorageAccountName/$FileShareName\" -Err -Message $ErrMsg -ErrorAction:Continue\r\n\t}\r\n\telse\r\n {\r\n\t\tWrite-Error $ErrMsg -ErrorAction:Continue\r\n\t}\r\n\r\n\tthrow [System.Exception]::new($ErrMsg, $ErrContainer.Exception)\r\n}", - "$fxv#3": "# Authentication is provided in the script", "roleDefinitionId": "0c867c2a-1d8c-454a-a3db-ab2ea1bdc8bb", "smbMultiChannel": { "multichannel": { @@ -11848,10 +10965,10 @@ }, "storageAccountNamePrefix": "[uniqueString(replace(parameters('namingConvention').storageAccount, parameters('serviceToken'), 'file-fslogix'), resourceGroup().id)]", "storageRedundancy": "[if(equals(parameters('availability'), 'availabilityZones'), '_ZRS', '_LRS')]", - "tagsPrivateEndpoints": "[union(createObject('cm-resource-parent', format('{0}}}/resourceGroups/{1}/providers/Microsoft.DesktopVirtualization/hostpools/{2}', subscription().id, parameters('resourceGroupControlPlane'), parameters('hostPoolName'))), if(contains(parameters('tags'), 'Microsoft.Network/privateEndpoints'), parameters('tags')['Microsoft.Network/privateEndpoints'], createObject()), parameters('mlzTags'))]", - "tagsStorageAccounts": "[union(createObject('cm-resource-parent', format('{0}}}/resourceGroups/{1}/providers/Microsoft.DesktopVirtualization/hostpools/{2}', subscription().id, parameters('resourceGroupControlPlane'), parameters('hostPoolName'))), if(contains(parameters('tags'), 'Microsoft.Storage/storageAccounts'), parameters('tags')['Microsoft.Storage/storageAccounts'], createObject()), parameters('mlzTags'))]", - "tagsRecoveryServicesVault": "[union(createObject('cm-resource-parent', format('{0}}}/resourceGroups/{1}/providers/Microsoft.DesktopVirtualization/hostpools/{2}', subscription().id, parameters('resourceGroupControlPlane'), parameters('hostPoolName'))), if(contains(parameters('tags'), 'Microsoft.recoveryServices/vaults'), parameters('tags')['Microsoft.recoveryServices/vaults'], createObject()), parameters('mlzTags'))]", - "tagsVirtualMachines": "[union(createObject('cm-resource-parent', format('{0}}}/resourceGroups/{1}/providers/Microsoft.DesktopVirtualization/hostpools/{2}', subscription().id, parameters('resourceGroupControlPlane'), parameters('hostPoolName'))), if(contains(parameters('tags'), 'Microsoft.Compute/virtualMachines'), parameters('tags')['Microsoft.Compute/virtualMachines'], createObject()), parameters('mlzTags'))]" + "tagsPrivateEndpoints": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), coalesce(tryGet(parameters('tags'), 'Microsoft.Network/privateEndpoints'), createObject()), parameters('mlzTags'))]", + "tagsStorageAccounts": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), coalesce(tryGet(parameters('tags'), 'Microsoft.Storage/storageAccounts'), createObject()), parameters('mlzTags'))]", + "tagsRecoveryServicesVault": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), coalesce(tryGet(parameters('tags'), 'Microsoft.recoveryServices/vaults'), createObject()), parameters('mlzTags'))]", + "tagsVirtualMachines": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), coalesce(tryGet(parameters('tags'), 'Microsoft.Compute/virtualMachines'), createObject()), parameters('mlzTags'))]" }, "resources": [ { @@ -12034,8 +11151,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "6140806447963417872" + "version": "0.31.92.45157", + "templateHash": "2560784574355796510" } }, "parameters": { @@ -12131,7 +11248,7 @@ }, { "name": "StorageAccountResourceGroupName", - "value": "[parameters('resourceGroupStorage')]" + "value": "[resourceGroup().name]" }, { "name": "StorageCount", @@ -12175,8 +11292,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "4329443301777514730" + "version": "0.31.92.45157", + "templateHash": "849953818393496765" } }, "parameters": { @@ -12266,7 +11383,7 @@ "value": "[parameters('recoveryServicesVaultName')]" }, "resourceGroupStorage": { - "value": "[parameters('resourceGroupStorage')]" + "value": "[resourceGroup().name]" }, "storageAccountNamePrefix": { "value": "[variables('storageAccountNamePrefix')]" @@ -12287,8 +11404,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "7916275533870259687" + "version": "0.31.92.45157", + "templateHash": "11840486447312746268" } }, "parameters": { @@ -12374,8 +11491,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "8886215231939195200" + "version": "0.31.92.45157", + "templateHash": "13786800560391201998" } }, "parameters": { @@ -12429,86 +11546,6 @@ "dependsOn": [ "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('set-ntfs-permissions-{0}', parameters('deploymentNameSuffix')))]" ] - }, - { - "condition": "[and(equals(parameters('fslogixStorageService'), 'AzureFiles Premium'), greater(parameters('storageCount'), 0))]", - "type": "Microsoft.Resources/deployments", - "apiVersion": "2022-09-01", - "name": "[format('deploy-file-share-scaling-{0}', parameters('deploymentNameSuffix'))]", - "resourceGroup": "[parameters('resourceGroupManagement')]", - "properties": { - "expressionEvaluationOptions": { - "scope": "inner" - }, - "mode": "Incremental", - "parameters": { - "files": { - "value": { - "requirements.psd1": "[variables('$fxv#1')]", - "run.ps1": "[variables('$fxv#2')]", - "../profile.ps1": "[variables('$fxv#3')]" - } - }, - "functionAppName": { - "value": "[parameters('functionAppName')]" - }, - "functionName": { - "value": "auto-increase-file-share-quota" - }, - "schedule": { - "value": "0 */15 * * * *" - } - }, - "template": { - "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", - "contentVersion": "1.0.0.0", - "metadata": { - "_generator": { - "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "15160754302365390942" - } - }, - "parameters": { - "files": { - "type": "object" - }, - "functionAppName": { - "type": "string" - }, - "functionName": { - "type": "string" - }, - "schedule": { - "type": "string" - } - }, - "resources": [ - { - "type": "Microsoft.Web/sites/functions", - "apiVersion": "2020-12-01", - "name": "[format('{0}/{1}', parameters('functionAppName'), parameters('functionName'))]", - "properties": { - "config": { - "disabled": false, - "bindings": [ - { - "name": "Timer", - "type": "timerTrigger", - "direction": "in", - "schedule": "[parameters('schedule')]" - } - ] - }, - "files": "[parameters('files')]" - } - } - ] - } - }, - "dependsOn": [ - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-backup-{0}', parameters('deploymentNameSuffix')))]" - ] } ], "outputs": { @@ -12518,25 +11555,26 @@ } } } - } + }, + "dependsOn": [ + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('resourceGroupName'))]" + ] } ], "outputs": { "netAppShares": { "type": "array", - "value": "[if(equals(parameters('storageService'), 'AzureNetAppFiles'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupStorage')), 'Microsoft.Resources/deployments', format('deploy-anf-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.fileShares.value, createArray('None'))]" + "value": "[if(equals(parameters('storageService'), 'AzureNetAppFiles'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-anf-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.fileShares.value, createArray('None'))]" }, "storageAccountNamePrefix": { "type": "string", - "value": "[if(equals(parameters('storageService'), 'AzureFiles'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupStorage')), 'Microsoft.Resources/deployments', format('deploy-azure-files-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.storageAccountNamePrefix.value, '')]" + "value": "[if(equals(parameters('storageService'), 'AzureFiles'), reference(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-azure-files-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.storageAccountNamePrefix.value, '')]" } } } }, "dependsOn": [ - "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-control-plane-{0}', parameters('deploymentNameSuffix')))]", "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix')))]", - "rgs", "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix')))]" ] }, @@ -12584,6 +11622,15 @@ "deploymentUserAssignedIdentityPrincipalId": { "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.deploymentUserAssignedIdentityPrincipalId.value]" }, + "diskAccessPolicyDefinitionId": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.diskAccessPolicyDefinitionId.value]" + }, + "diskAccessPolicyDisplayName": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.diskAccessPolicyDisplayName.value]" + }, + "diskAccessResourceId": { + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.diskAccessResourceId.value]" + }, "diskEncryptionSetResourceId": { "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.diskEncryptionSetResourceId.value]" }, @@ -12621,10 +11668,10 @@ "value": "[parameters('fslogixContainerType')]" }, "hostPoolName": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-control-plane-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.hostPoolName.value]" + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.hostPoolName.value]" }, "hostPoolResourceId": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-control-plane-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.hostPoolResourceId.value]" + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.hostPoolResourceId.value]" }, "hostPoolType": { "value": "[parameters('hostPoolType')]" @@ -12666,20 +11713,17 @@ "organizationalUnitPath": { "value": "[parameters('organizationalUnitPath')]" }, + "profile": { + "value": "[parameters('profile')]" + }, "recoveryServicesVaultName": { "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.recoveryServicesVaultName.value]" }, - "resourceGroupControlPlane": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', variables('resourceGroupServices')[0], parameters('deploymentNameSuffix'))), '2022-09-01').outputs.name.value]" - }, - "resourceGroupHosts": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', variables('resourceGroupServices')[1], parameters('deploymentNameSuffix'))), '2022-09-01').outputs.name.value]" - }, "resourceGroupManagement": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', variables('resourceGroupServices')[2], parameters('deploymentNameSuffix'))), '2022-09-01').outputs.name.value]" + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceGroupName.value]" }, - "roleDefinitions": { - "value": "[variables('roleDefinitions')]" + "resourceGroupName": { + "value": "[replace(reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.namingConvention.value.resourceGroup, reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.tokens.value.service, 'hosts')]" }, "scalingWeekdaysOffPeakStartTime": { "value": "[parameters('scalingWeekdaysOffPeakStartTime')]" @@ -12743,8 +11787,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "1578632995564263199" + "version": "0.31.92.45157", + "templateHash": "8274254996075869850" } }, "parameters": { @@ -12781,6 +11825,15 @@ "deploymentUserAssignedIdentityPrincipalId": { "type": "string" }, + "diskAccessPolicyDefinitionId": { + "type": "string" + }, + "diskAccessPolicyDisplayName": { + "type": "string" + }, + "diskAccessResourceId": { + "type": "string" + }, "diskEncryptionSetResourceId": { "type": "string" }, @@ -12808,6 +11861,9 @@ "enableAvdInsights": { "type": "bool" }, + "enableRecoveryServices": { + "type": "bool" + }, "environmentAbbreviation": { "type": "string" }, @@ -12862,23 +11918,17 @@ "organizationalUnitPath": { "type": "string" }, - "enableRecoveryServices": { - "type": "bool" - }, - "recoveryServicesVaultName": { - "type": "string" - }, - "resourceGroupControlPlane": { + "profile": { "type": "string" }, - "resourceGroupHosts": { + "recoveryServicesVaultName": { "type": "string" }, "resourceGroupManagement": { "type": "string" }, - "roleDefinitions": { - "type": "object" + "resourceGroupName": { + "type": "string" }, "scalingWeekdaysOffPeakStartTime": { "type": "string" @@ -12939,22 +11989,103 @@ } }, "variables": { - "$fxv#0": "Param(\r\n [string]$HostPoolResourceId,\r\n [string]$ResourceGroupName,\r\n [string]$ResourceManagerUri,\r\n [string]$ScalingPlanName,\r\n [string]$SubscriptionId,\r\n [string]$UserAssignedIdentityClientId\r\n)\r\n\r\n$ErrorActionPreference = 'Stop'\r\n$WarningPreference = 'SilentlyContinue'\r\n\r\n# Fix the resource manager URI since only AzureCloud contains a trailing slash\r\n$ResourceManagerUriFixed = if($ResourceManagerUri[-1] -eq '/'){$ResourceManagerUri} else {$ResourceManagerUri + '/'}\r\n\r\n# Get an access token for Azure resources\r\n$AzureManagementAccessToken = (Invoke-RestMethod `\r\n -Headers @{Metadata=\"true\"} `\r\n -Uri $('http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=' + $ResourceManagerUriFixed + '&client_id=' + $UserAssignedIdentityClientId)).access_token\r\n\r\n# Set header for Azure Management API\r\n$AzureManagementHeader = @{\r\n 'Content-Type'='application/json'\r\n 'Authorization'='Bearer ' + $AzureManagementAccessToken\r\n}\r\n\r\n# Check if the scaling plan exists: https://learn.microsoft.com/rest/api/desktopvirtualization/scaling-plans/list-by-resource-group\r\n$ScalingPlanExists = (Invoke-RestMethod `\r\n -Headers $AzureManagementHeader `\r\n -Method 'GET' `\r\n -Uri $($ResourceManagerUriFixed + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $ResourceGroupName + '/providers/Microsoft.DesktopVirtualization/scalingPlans?api-version=2024-04-03')).value | Where-Object {$_.name -eq $ScalingPlanName}\r\n\r\n# Disable autoscale for the host pool: https://learn.microsoft.com/rest/api/desktopvirtualization/scaling-plans/update\r\nif ($ScalingPlanExists)\r\n{\r\n Invoke-RestMethod `\r\n -Body (@{properties = @{hostPoolReferences = @(@{hostPoolArmPath = $HostPoolResourceId; scalingPlanEnabled = $false})}} | ConvertTo-Json) `\r\n -Headers $AzureManagementHeader `\r\n -Method 'PATCH' `\r\n -Uri $($ResourceManagerUriFixed + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $ResourceGroupName + '/providers/Microsoft.DesktopVirtualization/scalingPlans/' + $ScalingPlanName + '?api-version=2024-04-03') | Out-Null\r\n}", + "$fxv#0": "Param(\r\n [string]$HostPoolResourceId,\r\n [string]$ResourceGroupName,\r\n [string]$ResourceManagerUri,\r\n [string]$ScalingPlanName,\r\n [string]$SubscriptionId,\r\n [string]$UserAssignedIdentityClientId\r\n)\r\n\r\n$ErrorActionPreference = 'Stop'\r\n$WarningPreference = 'SilentlyContinue'\r\n\r\n# Fix the resource manager URI since only AzureCloud contains a trailing slash\r\n$ResourceManagerUriFixed = if($ResourceManagerUri[-1] -eq '/'){$ResourceManagerUri} else {$ResourceManagerUri + '/'}\r\n\r\n# Get an access token for Azure resources\r\n$AzureManagementAccessToken = (Invoke-RestMethod `\r\n -Headers @{Metadata=\"true\"} `\r\n -Uri $('http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=' + $ResourceManagerUriFixed + '&client_id=' + $UserAssignedIdentityClientId)).access_token\r\n\r\n# Set header for Azure Management API\r\n$AzureManagementHeader = @{\r\n 'Content-Type'='application/json'\r\n 'Authorization'='Bearer ' + $AzureManagementAccessToken\r\n}\r\n\r\n# Check if the scaling plan exists: https://learn.microsoft.com/rest/api/desktopvirtualization/scaling-plans/list-by-resource-group\r\n$ScalingPlanExists = (Invoke-RestMethod `\r\n -Headers $AzureManagementHeader `\r\n -Method 'GET' `\r\n -Uri $($ResourceManagerUriFixed + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $ResourceGroupName + '/providers/Microsoft.DesktopVirtualization/scalingPlans?api-version=2023-09-05')).value | Where-Object {$_.name -eq $ScalingPlanName}\r\n\r\n# Disable autoscale for the host pool: https://learn.microsoft.com/rest/api/desktopvirtualization/scaling-plans/update\r\nif ($ScalingPlanExists)\r\n{\r\n Invoke-RestMethod `\r\n -Body (@{properties = @{hostPoolReferences = @(@{hostPoolArmPath = $HostPoolResourceId; scalingPlanEnabled = $false})}} | ConvertTo-Json -Depth 3) `\r\n -Headers $AzureManagementHeader `\r\n -Method 'PATCH' `\r\n -Uri $($ResourceManagerUriFixed + 'subscriptions/' + $SubscriptionId + '/resourceGroups/' + $ResourceGroupName + '/providers/Microsoft.DesktopVirtualization/scalingPlans/' + $ScalingPlanName + '?api-version=2023-09-05') | Out-Null\r\n}", + "$fxv#1": "param (\r\n [string]$ImageOffer,\r\n [string]$ImagePublisher,\r\n [string]$ImageSku,\r\n [string]$ResourceManagerUri,\r\n [string]$SubscriptionId,\r\n [string]$UserAssignedIdentityClientId\r\n)\r\n\r\n# Fix the resource manager URI since only AzureCloud contains a trailing slash\r\n$ResourceManagerUriFixed = if($ResourceManagerUri[-1] -eq '/'){$ResourceManagerUri} else {$ResourceManagerUri + '/'}\r\n\r\n# Get an access token for Azure resources\r\n$AzureManagementAccessToken = (Invoke-RestMethod `\r\n -Headers @{Metadata=\"true\"} `\r\n -Uri $('http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=' + $ResourceManagerUriFixed + '&client_id=' + $UserAssignedIdentityClientId)).access_token\r\n\r\n# Set header for Azure Management API\r\n$AzureManagementHeader = @{\r\n 'Content-Type'='application/json'\r\n 'Authorization'='Bearer ' + $AzureManagementAccessToken\r\n}\r\n\r\n# Use the access token to get the marketplace agreement\r\n$Terms = Invoke-RestMethod `\r\n -Headers $AzureManagementHeader `\r\n -Method 'GET' `\r\n -Uri $($ResourceManagerUriFixed + 'subscriptions/' + $SubscriptionId + '/providers/Microsoft.MarketplaceOrdering/agreements/' + $ImagePublisher + '/offers/' + $ImageOffer + '/plans/' + $ImageSku + '?api-version=2021-01-01')\r\n\r\n# Use the access token to set the marketplace agreement\r\nif($Terms.error)\r\n{\r\n Invoke-RestMethod `\r\n -Headers $AzureManagementHeader `\r\n -Method 'POST' `\r\n -Uri $($ResourceManagerUriFixed + 'subscriptions/' + $SubscriptionId + '/providers/Microsoft.MarketplaceOrdering/agreements/' + $ImagePublisher + '/offers/' + $ImageOffer + '/plans/' + $ImageSku + '/sign?api-version=2021-01-01') | Out-Null\r\n}", "availabilitySetNamePrefix": "[parameters('namingConvention').availabilitySet]", - "tagsAvailabilitySets": "[union(createObject('cm-resource-parent', format('{0}/resourceGroups/{1}/providers/Microsoft.DesktopVirtualization/hostpools/{2}', subscription().id, parameters('resourceGroupManagement'), parameters('hostPoolName'))), if(contains(parameters('tags'), 'Microsoft.Compute/availabilitySets'), parameters('tags')['Microsoft.Compute/availabilitySets'], createObject()), parameters('mlzTags'))]", - "tagsNetworkInterfaces": "[union(createObject('cm-resource-parent', format('{0}/resourceGroups/{1}/providers/Microsoft.DesktopVirtualization/hostpools/{2}', subscription().id, parameters('resourceGroupManagement'), parameters('hostPoolName'))), if(contains(parameters('tags'), 'Microsoft.Network/networkInterfaces'), parameters('tags')['Microsoft.Network/networkInterfaces'], createObject()), parameters('mlzTags'))]", - "tagsRecoveryServicesVault": "[union(createObject('cm-resource-parent', format('{0}/resourceGroups/{1}/providers/Microsoft.DesktopVirtualization/hostpools/{2}', subscription().id, parameters('resourceGroupManagement'), parameters('hostPoolName'))), if(contains(parameters('tags'), 'Microsoft.recoveryServices/vaults'), parameters('tags')['Microsoft.recoveryServices/vaults'], createObject()), parameters('mlzTags'))]", - "tagsVirtualMachines": "[union(createObject('cm-resource-parent', format('{0}/resourceGroups/{1}/providers/Microsoft.DesktopVirtualization/hostpools/{2}', subscription().id, parameters('resourceGroupManagement'), parameters('hostPoolName'))), if(contains(parameters('tags'), 'Microsoft.Compute/virtualMachines'), parameters('tags')['Microsoft.Compute/virtualMachines'], createObject()), parameters('mlzTags'))]", + "tagsVirtualMachines": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), coalesce(tryGet(parameters('tags'), 'Microsoft.Compute/virtualMachines'), createObject()), parameters('mlzTags'))]", "uniqueToken": "[uniqueString(parameters('identifier'), parameters('environmentAbbreviation'), subscription().subscriptionId)]", "virtualMachineNamePrefix": "[replace(parameters('namingConvention').virtualMachine, parameters('serviceToken'), '')]" }, "resources": [ + { + "type": "Microsoft.Resources/resourceGroups", + "apiVersion": "2023-07-01", + "name": "[parameters('resourceGroupName')]", + "location": "[parameters('location')]", + "tags": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), coalesce(tryGet(parameters('tags'), 'Microsoft.Resources/resourceGroups'), createObject()), parameters('mlzTags'))]" + }, + { + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('assign-policy-diskAccess-{0}', parameters('deploymentNameSuffix'))]", + "resourceGroup": "[parameters('resourceGroupName')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "diskAccessResourceId": { + "value": "[parameters('diskAccessResourceId')]" + }, + "location": { + "value": "[parameters('location')]" + }, + "policyDefinitionId": { + "value": "[parameters('diskAccessPolicyDefinitionId')]" + }, + "policyDisplayName": { + "value": "[parameters('diskAccessPolicyDisplayName')]" + }, + "policyName": { + "value": "[parameters('diskAccessPolicyDisplayName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "1275953978872597894" + } + }, + "parameters": { + "diskAccessResourceId": { + "type": "string" + }, + "location": { + "type": "string" + }, + "policyDefinitionId": { + "type": "string" + }, + "policyDisplayName": { + "type": "string" + }, + "policyName": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Authorization/policyAssignments", + "apiVersion": "2022-06-01", + "name": "[parameters('policyName')]", + "location": "[parameters('location')]", + "identity": { + "type": "SystemAssigned" + }, + "properties": { + "displayName": "[parameters('policyDisplayName')]", + "policyDefinitionId": "[parameters('policyDefinitionId')]", + "parameters": "[if(not(empty(parameters('diskAccessResourceId'))), createObject('diskAccessId', createObject('value', parameters('diskAccessResourceId'))), createObject())]" + } + } + ] + } + }, + "dependsOn": [ + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('resourceGroupName'))]" + ] + }, { "condition": "[and(equals(parameters('hostPoolType'), 'Pooled'), equals(parameters('availability'), 'AvailabilitySets'))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('deploy-avail-{0}', parameters('deploymentNameSuffix'))]", - "resourceGroup": "[parameters('resourceGroupHosts')]", + "name": "[format('deploy-avSets-{0}', parameters('deploymentNameSuffix'))]", + "resourceGroup": "[parameters('resourceGroupName')]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -12974,7 +12105,7 @@ "value": "[parameters('location')]" }, "tagsAvailabilitySets": { - "value": "[variables('tagsAvailabilitySets')]" + "value": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), coalesce(tryGet(parameters('tags'), 'Microsoft.Compute/availabilitySets'), createObject()), parameters('mlzTags'))]" } }, "template": { @@ -12983,8 +12114,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "13909498964093142621" + "version": "0.31.92.45157", + "templateHash": "12575584281240773385" } }, "parameters": { @@ -13025,18 +12156,21 @@ } ] } - } + }, + "dependsOn": [ + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('resourceGroupName'))]" + ] }, { "copy": { "name": "roleAssignments", "count": "[length(range(0, length(parameters('securityPrincipalObjectIds'))))]" }, - "condition": "[not(contains(parameters('activeDirectorySolution'), 'DomainServices'))]", + "condition": "[contains(parameters('activeDirectorySolution'), 'EntraId')]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('deploy-role-assignments-{0}-{1}', range(0, length(parameters('securityPrincipalObjectIds')))[copyIndex()], parameters('deploymentNameSuffix'))]", - "resourceGroup": "[parameters('resourceGroupHosts')]", + "name": "[format('assign-role-{0}-{1}', range(0, length(parameters('securityPrincipalObjectIds')))[copyIndex()], parameters('deploymentNameSuffix'))]", + "resourceGroup": "[parameters('resourceGroupName')]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -13050,7 +12184,7 @@ "value": "Group" }, "roleDefinitionId": { - "value": "[parameters('roleDefinitions').VirtualMachineUserLogin]" + "value": "fb879df8-f326-4884-b1cf-06f3ad86be52" } }, "template": { @@ -13059,8 +12193,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "1315528727979495191" + "version": "0.31.92.45157", + "templateHash": "10174155731090308034" } }, "parameters": { @@ -13087,12 +12221,15 @@ } ] } - } + }, + "dependsOn": [ + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('resourceGroupName'))]" + ] }, { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('deploy-disable-autoscale-{0}', parameters('deploymentNameSuffix'))]", + "name": "[format('deploy-disableAutoscale-{0}', parameters('deploymentNameSuffix'))]", "resourceGroup": "[parameters('resourceGroupManagement')]", "properties": { "expressionEvaluationOptions": { @@ -13150,8 +12287,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "5317974790875443937" + "version": "0.31.92.45157", + "templateHash": "6896852651895359166" } }, "parameters": { @@ -13203,6 +12340,124 @@ } } }, + { + "condition": "[equals(parameters('profile'), 'ArcGISPro')]", + "type": "Microsoft.Resources/deployments", + "apiVersion": "2022-09-01", + "name": "[format('set-marketplaceTerms-{0}', parameters('deploymentNameSuffix'))]", + "resourceGroup": "[parameters('resourceGroupManagement')]", + "properties": { + "expressionEvaluationOptions": { + "scope": "inner" + }, + "mode": "Incremental", + "parameters": { + "location": { + "value": "[parameters('location')]" + }, + "name": { + "value": "Set-AzureMarketplaceTerms" + }, + "parameters": { + "value": [ + { + "name": "ImageOffer", + "value": "[parameters('imageOffer')]" + }, + { + "name": "ImagePublisher", + "value": "[parameters('imagePublisher')]" + }, + { + "name": "ImageSku", + "value": "[parameters('imageSku')]" + }, + { + "name": "ResourceManagerUri", + "value": "[environment().resourceManager]" + }, + { + "name": "SubscriptionId", + "value": "[subscription().subscriptionId]" + }, + { + "name": "UserAssignedidentityClientId", + "value": "[parameters('deploymentUserAssignedIdentityClientId')]" + } + ] + }, + "script": { + "value": "[variables('$fxv#1')]" + }, + "tags": { + "value": "[variables('tagsVirtualMachines')]" + }, + "virtualMachineName": { + "value": "[parameters('managementVirtualMachineName')]" + } + }, + "template": { + "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", + "contentVersion": "1.0.0.0", + "metadata": { + "_generator": { + "name": "bicep", + "version": "0.31.92.45157", + "templateHash": "6896852651895359166" + } + }, + "parameters": { + "asyncExecution": { + "type": "bool", + "defaultValue": false + }, + "location": { + "type": "string" + }, + "name": { + "type": "string" + }, + "parameters": { + "type": "array", + "defaultValue": [] + }, + "script": { + "type": "string" + }, + "tags": { + "type": "object" + }, + "treatFailureAsDeploymentFailure": { + "type": "bool", + "defaultValue": true + }, + "virtualMachineName": { + "type": "string" + } + }, + "resources": [ + { + "type": "Microsoft.Compute/virtualMachines/runCommands", + "apiVersion": "2023-09-01", + "name": "[format('{0}/{1}', parameters('virtualMachineName'), parameters('name'))]", + "location": "[parameters('location')]", + "tags": "[parameters('tags')]", + "properties": { + "asyncExecution": "[parameters('asyncExecution')]", + "parameters": "[parameters('parameters')]", + "source": { + "script": "[parameters('script')]" + }, + "treatFailureAsDeploymentFailure": "[parameters('treatFailureAsDeploymentFailure')]" + } + } + ] + } + }, + "dependsOn": [ + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-disableAutoscale-{0}', parameters('deploymentNameSuffix')))]" + ] + }, { "copy": { "name": "virtualMachines", @@ -13213,7 +12468,7 @@ "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", "name": "[format('deploy-vms-{0}-{1}', sub(range(1, parameters('sessionHostBatchCount'))[copyIndex()], 1), parameters('deploymentNameSuffix'))]", - "resourceGroup": "[parameters('resourceGroupHosts')]", + "resourceGroup": "[parameters('resourceGroupName')]", "properties": { "expressionEvaluationOptions": { "scope": "inner" @@ -13307,8 +12562,8 @@ "organizationalUnitPath": { "value": "[parameters('organizationalUnitPath')]" }, - "resourceGroupControlPlane": { - "value": "[parameters('resourceGroupControlPlane')]" + "profile": { + "value": "[parameters('profile')]" }, "resourceGroupManagement": { "value": "[parameters('resourceGroupManagement')]" @@ -13337,7 +12592,7 @@ "value": "[parameters('subnetResourceId')]" }, "tagsNetworkInterfaces": { - "value": "[variables('tagsNetworkInterfaces')]" + "value": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), coalesce(tryGet(parameters('tags'), 'Microsoft.Network/networkInterfaces'), createObject()), parameters('mlzTags'))]" }, "tagsVirtualMachines": { "value": "[variables('tagsVirtualMachines')]" @@ -13364,8 +12619,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "16117911452479927621" + "version": "0.31.92.45157", + "templateHash": "10645307132280074356" } }, "parameters": { @@ -13462,7 +12717,7 @@ "organizationalUnitPath": { "type": "string" }, - "resourceGroupControlPlane": { + "profile": { "type": "string" }, "resourceGroupManagement": { @@ -13603,6 +12858,7 @@ "identity": { "type": "SystemAssigned" }, + "plan": "[if(equals(parameters('profile'), 'ArcGISPro'), createObject('name', parameters('imageSku'), 'publisher', parameters('imagePublisher'), 'product', parameters('imageOffer')), null())]", "zones": "[if(equals(parameters('availability'), 'AvailabilityZones'), createArray(parameters('availabilityZones')[mod(range(0, parameters('sessionHostCount'))[copyIndex()], length(parameters('availabilityZones')))]), null())]", "properties": { "availabilitySet": "[if(equals(parameters('availability'), 'AvailabilitySets'), createObject('id', resourceId('Microsoft.Compute/availabilitySets', format('{0}-{1}', parameters('availabilitySetNamePrefix'), padLeft(div(add(range(0, parameters('sessionHostCount'))[copyIndex()], parameters('sessionHostIndex')), 200), 2, '0')))), null())]", @@ -13802,7 +13058,7 @@ }, "protectedSettings": { "Items": { - "RegistrationInfoToken": "[listRegistrationTokens(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupControlPlane')), 'Microsoft.DesktopVirtualization/hostPools', parameters('hostPoolName')), '2023-09-05').value[0].token]" + "RegistrationInfoToken": "[listRegistrationTokens(extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.DesktopVirtualization/hostPools', parameters('hostPoolName')), '2023-09-05').value[0].token]" } } }, @@ -14003,8 +13259,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "5317974790875443937" + "version": "0.31.92.45157", + "templateHash": "6896852651895359166" } }, "parameters": { @@ -14090,7 +13346,7 @@ }, { "name": "HostPoolResourceGroupName", - "value": "[parameters('resourceGroupControlPlane')]" + "value": "[parameters('resourceGroupManagement')]" }, { "name": "ResourceManagerUri", @@ -14138,8 +13394,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "5317974790875443937" + "version": "0.31.92.45157", + "templateHash": "6896852651895359166" } }, "parameters": { @@ -14198,15 +13454,17 @@ } }, "dependsOn": [ - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupHosts')), 'Microsoft.Resources/deployments', format('deploy-avail-{0}', parameters('deploymentNameSuffix')))]", - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-disable-autoscale-{0}', parameters('deploymentNameSuffix')))]" + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupName')), 'Microsoft.Resources/deployments', format('deploy-avSets-{0}', parameters('deploymentNameSuffix')))]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-disableAutoscale-{0}', parameters('deploymentNameSuffix')))]", + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('resourceGroupName'))]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('set-marketplaceTerms-{0}', parameters('deploymentNameSuffix')))]" ] }, { "condition": "[and(parameters('enableRecoveryServices'), equals(parameters('hostPoolType'), 'Personal'))]", "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('deploy-recovery-services-{0}', parameters('deploymentNameSuffix'))]", + "name": "[format('deploy-recoveryServices-{0}', parameters('deploymentNameSuffix'))]", "resourceGroup": "[parameters('resourceGroupManagement')]", "properties": { "expressionEvaluationOptions": { @@ -14233,7 +13491,7 @@ "value": "[parameters('recoveryServicesVaultName')]" }, "resourceGroupHosts": { - "value": "[parameters('resourceGroupHosts')]" + "value": "[parameters('resourceGroupName')]" }, "resourceGroupManagement": { "value": "[parameters('resourceGroupManagement')]" @@ -14245,7 +13503,7 @@ "value": "[parameters('sessionHostIndex')]" }, "tagsRecoveryServicesVault": { - "value": "[variables('tagsRecoveryServicesVault')]" + "value": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), coalesce(tryGet(parameters('tags'), 'Microsoft.recoveryServices/vaults'), createObject()), parameters('mlzTags'))]" }, "virtualMachineNamePrefix": { "value": "[variables('virtualMachineNamePrefix')]" @@ -14257,8 +13515,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "3032901180833561492" + "version": "0.31.92.45157", + "templateHash": "16414429800719346251" } }, "parameters": { @@ -14343,8 +13601,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "15863312965086940829" + "version": "0.31.92.45157", + "templateHash": "2762553882428643570" } }, "parameters": { @@ -14402,13 +13660,14 @@ } }, "dependsOn": [ + "[subscriptionResourceId('Microsoft.Resources/resourceGroups', parameters('resourceGroupName'))]", "virtualMachines" ] }, { "type": "Microsoft.Resources/deployments", "apiVersion": "2022-09-01", - "name": "[format('deploy-scaling-plan-{0}', parameters('deploymentNameSuffix'))]", + "name": "[format('deploy-scalingPlan-{0}', parameters('deploymentNameSuffix'))]", "resourceGroup": "[parameters('resourceGroupManagement')]", "properties": { "expressionEvaluationOptions": { @@ -14441,7 +13700,7 @@ "value": "[parameters('namingConvention').scalingPlan]" }, "tags": { - "value": "[parameters('tags')]" + "value": "[union(createObject('cm-resource-parent', parameters('hostPoolResourceId')), coalesce(tryGet(parameters('tags'), 'Microsoft.DesktopVirtualization/scalingPlans'), createObject()), parameters('mlzTags'))]" }, "timeZone": { "value": "[parameters('timeZone')]" @@ -14465,8 +13724,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "665492750417866100" + "version": "0.31.92.45157", + "templateHash": "12064357872576819259" } }, "parameters": { @@ -14603,7 +13862,7 @@ }, "condition": "[equals(parameters('hostPoolType'), 'Pooled')]", "type": "Microsoft.DesktopVirtualization/scalingPlans/pooledSchedules", - "apiVersion": "2024-04-03", + "apiVersion": "2023-09-05", "name": "[format('{0}/{1}', parameters('scalingPlanName'), if(equals(range(0, length(variables('schedules')))[copyIndex()], 0), 'Weekdays', 'Weekends'))]", "properties": "[variables('schedules')[range(0, length(variables('schedules')))[copyIndex()]]]", "dependsOn": [ @@ -14617,7 +13876,7 @@ }, "condition": "[equals(parameters('hostPoolType'), 'Personal')]", "type": "Microsoft.DesktopVirtualization/scalingPlans/personalSchedules", - "apiVersion": "2024-04-03", + "apiVersion": "2023-09-05", "name": "[format('{0}/{1}', parameters('scalingPlanName'), if(equals(range(0, length(variables('schedules')))[copyIndex()], 0), 'Weekdays', 'Weekends'))]", "properties": "[variables('schedules')[range(0, length(variables('schedules')))[copyIndex()]]]", "dependsOn": [ @@ -14664,7 +13923,7 @@ } }, "dependsOn": [ - "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-recovery-services-{0}', parameters('deploymentNameSuffix')))]", + "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('resourceGroupManagement')), 'Microsoft.Resources/deployments', format('deploy-recoveryServices-{0}', parameters('deploymentNameSuffix')))]", "virtualMachines" ] } @@ -14672,10 +13931,8 @@ } }, "dependsOn": [ - "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-control-plane-{0}', parameters('deploymentNameSuffix')))]", "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-fslogix-{0}', parameters('deploymentNameSuffix')))]", "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix')))]", - "rgs", "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-tier3-avd-{0}', parameters('deploymentNameSuffix')))]" ] }, @@ -14697,7 +13954,7 @@ "value": "[parameters('locationVirtualMachines')]" }, "resourceGroupManagement": { - "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', variables('resourceGroupServices')[2], parameters('deploymentNameSuffix'))), '2022-09-01').outputs.name.value]" + "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.resourceGroupName.value]" }, "userAssignedIdentityClientId": { "value": "[reference(subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix'))), '2022-09-01').outputs.deploymentUserAssignedIdentityClientId.value]" @@ -14712,8 +13969,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "6161798997000105603" + "version": "0.31.92.45157", + "templateHash": "297418666680418295" } }, "parameters": { @@ -14796,8 +14053,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.31.34.60546", - "templateHash": "5317974790875443937" + "version": "0.31.92.45157", + "templateHash": "6896852651895359166" } }, "parameters": { @@ -14855,7 +14112,6 @@ "dependsOn": [ "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-fslogix-{0}', parameters('deploymentNameSuffix')))]", "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-management-{0}', parameters('deploymentNameSuffix')))]", - "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-rg-{0}-{1}', variables('resourceGroupServices')[2], parameters('deploymentNameSuffix')))]", "[subscriptionResourceId('Microsoft.Resources/deployments', format('deploy-session-hosts-{0}', parameters('deploymentNameSuffix')))]" ] } diff --git a/src/bicep/add-ons/azure-virtual-desktop/uiDefinition.json b/src/bicep/add-ons/azure-virtual-desktop/uiDefinition.json index cce9c2ef3..7ae63ad60 100644 --- a/src/bicep/add-ons/azure-virtual-desktop/uiDefinition.json +++ b/src/bicep/add-ons/azure-virtual-desktop/uiDefinition.json @@ -314,253 +314,6 @@ } ] }, - { - "name": "controlPlane", - "label": "Control Plane", - "elements": [ - { - "name": "hostPool", - "type": "Microsoft.Common.Section", - "visible": true, - "label": "Host Pool", - "elements": [ - { - "name": "validation", - "type": "Microsoft.Common.CheckBox", - "label": "Validation environment", - "visible": "[not(equals(steps('basics').scenario.profile, 'arcGisPro'))]", - "toolTip": "Choose whether to deploy the host pool as a validation environment. This allows you test preview features for AVD before they are released to production.", - "constraints": { - "required": false - } - }, - { - "name": "hostPoolType", - "type": "Microsoft.Common.DropDown", - "visible": true, - "label": "Host Pool Type", - "defaultValue": "Pooled", - "multiLine": true, - "toolTip": "", - "constraints": { - "required": true, - "allowedValues": [ - { - "label": "Pooled", - "value": "Pooled" - }, - { - "label": "Personal", - "value": "Personal" - } - ] - } - }, - { - "name": "workloadType", - "type": "Microsoft.Common.DropDown", - "label": "Workload Type", - "visible": "[or(equals(steps('basics').scenario.profile, 'arcGisPro'), and(equals(steps('basics').scenario.profile, 'generic'), equals(steps('controlPlane').hostPool.hostPoolType, 'Pooled')))]", - "filter": true, - "defaultValue": "[if(equals(steps('basics').scenario.profile, 'arcGisPro'), 'Medium', 'Heavy (2 users per core)')]", - "toolTip": "Select the type of image to deploy on the session hosts.", - "multiLine": true, - "constraints": { - "required": true, - "allowedValues": "[if(equals(steps('basics').scenario.profile, 'arcGisPro'), parse('[{\"label\": \"Light\",\"description\": \"Simple 2-D map visualizations, basic analyses, and reporting.\",\"value\": 6},{\"label\": \"Medium\",\"description\": \"Advanced 2-D or 3-D map visualizations, multi-source data analyses leveraging geoprocessing tools, reporting, and editing.\",\"value\": 4},{\"label\": \"Heavy\",\"description\": \"Complex 2-D or 3-D map visualizations including advanced symbology, analyses with visability and line of site, reporting, and editing.\",\"value\": 3}]'), parse('[{\"label\": \"Light (6 users per core)\",\"description\": \"Basic data entry tasks\",\"value\": \"6.0\"},{\"label\": \"Medium (4 users per core)\",\"description\": \"Consultants and market researchers\",\"value\": \"4.0\"},{\"label\": \"Heavy (2 users per core)\",\"description\": \"Software engineers and content creators\",\"value\": \"2.0\"},{\"label\": \"Power (1 user per core)\",\"description\": \"Graphic designers, 3D model makers, and machine learning researchers\",\"value\": \"1.0\"}]'))]" - } - }, - { - "name": "customRdpProperties", - "type": "Microsoft.Common.TextBox", - "visible": true, - "label": "Custom RDP properties", - "defaultValue": "[if(equals(steps('basics').scenario.profile, 'arcGisPro'), 'use multimon:i:1;drivestoredirect:s:;encode redirected video capture:i:1;redirected video capture encoding quality:i:2;audiomode:i:0;devicestoredirect:s:;redirectclipboard:i:0;redirectcomports:i:0;redirectlocation:i:1;redirectprinters:i:0;redirectsmartcards:i:1;redirectwebauthn:i:1;usbdevicestoredirect:s:;keyboardhook:i:2;', 'audiocapturemode:i:1;camerastoredirect:s:*;use multimon:i:0;drivestoredirect:s:;encode redirected video capture:i:1;redirected video capture encoding quality:i:1;audiomode:i:0;devicestoredirect:s:;redirectclipboard:i:0;redirectcomports:i:0;redirectlocation:i:1;redirectprinters:i:0;redirectsmartcards:i:1;redirectwebauthn:i:1;usbdevicestoredirect:s:;keyboardhook:i:2;')]", - "toolTip": "Specify the configuration for the RDP properties on the AVD host pool.", - "constraints": { - "required": true - } - }, - { - "name": "publicNetworkAccess", - "type": "Microsoft.Common.DropDown", - "visible": true, - "label": "Public network access", - "defaultValue": "Enabled", - "multiLine": true, - "toolTip": "Enabled: allows the host pool to be accessed from both public and private networks. Disabled: allows the host pool to only be accessed via private endpoints.", - "constraints": { - "required": true, - "allowedValues": [ - { - "label": "Disabled", - "value": "Disabled" - }, - { - "label": "Enabled", - "value": "Enabled" - }, - { - "label": "Enabled For Clients Only", - "value": "EnabledForClientsOnly" - }, - { - "label": "Enabled For Session Hosts Only", - "value": "EnabledForSessionHostsOnly" - } - ] - } - } - ] - }, - { - "name": "workspacesApi", - "type": "Microsoft.Solutions.ArmApiControl", - "request": { - "method": "GET", - "path": "[concat(steps('basics').scope.subscription.id, '/providers/Microsoft.DesktopVirtualization/workspaces?api-version=2022-02-10-preview')]" - } - }, - { - "name": "workspace", - "type": "Microsoft.Common.Section", - "visible": "[empty(first(filter(steps('controlPlane').workspacesApi.value, (item) => and(contains(item.name, steps('basics').naming.identifier), contains(item.name, 'feed'), contains(item.name, steps('basics').naming.environment), equals(item.location, steps('basics').hub.azureFirewallApi.location)))))]", - "label": "Workspace", - "elements": [ - { - "name": "publicNetworkAccess", - "type": "Microsoft.Common.DropDown", - "visible": true, - "label": "Public network access (feed)", - "defaultValue": "Enabled", - "multiLine": true, - "toolTip": "Enabled: allows the host pool to be accessed from both public and private networks. Disabled: allows the host pool to only be accessed via private endpoints.", - "constraints": { - "required": true, - "allowedValues": [ - { - "label": "Disabled", - "value": "Disabled" - }, - { - "label": "Enabled", - "value": "Enabled" - } - ] - } - } - ] - }, - { - "name": "assignment", - "type": "Microsoft.Common.Section", - "label": "Assignment", - "visible": true, - "elements": [ - { - "name": "description", - "type": "Microsoft.Common.TextBlock", - "visible": true, - "options": { - "text": "To access Azure Virtual Desktop, your end users will need to be assigned to the Desktop Applicaiton Group and if applicable, be given permissions on the storage service for FSLogix. Select the desired security groups below to give access to this AVD stamp. If deploying FSLogix, storage will be deployed for each group to support sharding. Be sure your groups are sized appropriately for each shard.", - "link": { - "label": "Learn more about FSLogix storage options for sizing your groups.", - "uri": "https://learn.microsoft.com/azure/virtual-desktop/store-fslogix-profile" - } - } - }, - { - "name": "groups", - "type": "Microsoft.Common.EditableGrid", - "ariaLabel": "Input the security groups for access to AVD and if applicable, FSLogix. The object ID is a property on the group and can be found in Entra ID.", - "label": "Security Groups", - "visible": true, - "constraints": { - "width": "Full", - "rows": { - "count": { - "min": 1, - "max": 100 - } - }, - "columns": [ - { - "id": "name", - "header": "Name", - "width": "1fr", - "element": { - "type": "Microsoft.Common.TextBox", - "placeholder": "Security Group Name", - "constraints": { - "required": true, - "validations": [] - } - } - }, - { - "id": "objectId", - "header": "Object ID", - "width": "1fr", - "element": { - "type": "Microsoft.Common.TextBox", - "placeholder": "Security Group Object ID", - "constraints": { - "required": true, - "validations": [] - } - } - } - ] - } - } - ] - }, - { - "name": "friendlyNames", - "type": "Microsoft.Common.Section", - "label": "Friendly Names for AVD Client", - "visible": true, - "elements": [ - { - "name": "custom", - "type": "Microsoft.Common.CheckBox", - "visible": true, - "label": "Custom friendly names?", - "defaultValue": false, - "toolTip": "Choose whether to add custom names to the AVD feed workspace and the desktop for the AVD client." - }, - { - "name": "feedWorkspace", - "type": "Microsoft.Common.TextBox", - "label": "Feed Workspace", - "defaultValue": "", - "placeholder": "Example: Information Technology", - "toolTip": "Input the friendly name for the AVD workspace that will be displayed in the end user's client. This value should apply to all the stamp indexes within the same identifier and would mostly likely represent your business unit or project.", - "visible": "[and(steps('controlPlane').friendlyNames.custom, empty(first(filter(steps('controlPlane').workspacesApi.value, (item) => and(contains(item.name, steps('basics').naming.identifier), contains(item.name, 'feed'), contains(item.name, steps('basics').naming.environment), equals(item.location, steps('basics').hub.azureFirewallApi.location))))))]", - "constraints": { - "required": false, - "regex": "^.{1,64}$", - "validationMessage": "The value must be between 1 and 64 characters in length." - } - }, - { - "name": "sessionDesktop", - "type": "Microsoft.Common.TextBox", - "label": "Desktop", - "defaultValue": "", - "placeholder": "Example: Help Desk", - "toolTip": "Input the friendly name for the AVD Session Desktop application that will be displayed in the end user's client. This value should represent the workload (eg., Help Desk, Development, or Administration) that will be supported on the host pool.", - "visible": "[steps('controlPlane').friendlyNames.custom]", - "constraints": { - "required": false, - "regex": "^.{1,64}$", - "validationMessage": "The value must be between 1 and 64 characters in length." - } - } - ] - } - ] - }, { "name": "hosts", "label": "Session Hosts", @@ -771,7 +524,7 @@ }, "osPlatform": "Windows", "count": "[steps('hosts').virtualMachines.count]", - "visible": "[and(equals(steps('basics').scenario.profile, 'arcGisPro'), and(equals(steps('controlPlane').hostPool.hostPoolType, 'Personal'), equals(steps('controlPlane').hostPool.workloadType, 6)))]" + "visible": "[and(equals(steps('basics').scenario.profile, 'arcGisPro'), and(equals(steps('management').hostPool.hostPoolType, 'Personal'), equals(steps('management').hostPool.workloadType, 6)))]" }, { "name": "sizeArcGisProSingleMedium", @@ -798,7 +551,7 @@ }, "osPlatform": "Windows", "count": "[steps('hosts').virtualMachines.count]", - "visible": "[and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('controlPlane').hostPool.hostPoolType, 'Personal'), equals(steps('controlPlane').hostPool.workloadType, 4))]" + "visible": "[and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('management').hostPool.hostPoolType, 'Personal'), equals(steps('management').hostPool.workloadType, 4))]" }, { "name": "sizeArcGisProSingleHeavy", @@ -823,7 +576,7 @@ }, "osPlatform": "Windows", "count": "[steps('hosts').virtualMachines.count]", - "visible": "[and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('controlPlane').hostPool.hostPoolType, 'Personal'), equals(steps('controlPlane').hostPool.workloadType, 3))]" + "visible": "[and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('management').hostPool.hostPoolType, 'Personal'), equals(steps('management').hostPool.workloadType, 3))]" }, { "name": "sizeArcGisProMulti", @@ -846,7 +599,7 @@ }, "osPlatform": "Windows", "count": "[steps('hosts').virtualMachines.count]", - "visible": "[and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('controlPlane').hostPool.hostPoolType, 'Pooled'))]" + "visible": "[and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('management').hostPool.hostPoolType, 'Pooled'))]" }, { "name": "resourceSkusApi", @@ -1114,6 +867,254 @@ "name": "management", "label": "Management", "elements": [ + { + "name": "hostPool", + "type": "Microsoft.Common.Section", + "visible": true, + "label": "Host Pool", + "elements": [ + { + "name": "validation", + "type": "Microsoft.Common.CheckBox", + "label": "Validation environment", + "visible": "[not(equals(steps('basics').scenario.profile, 'arcGisPro'))]", + "toolTip": "Choose whether to deploy the host pool as a validation environment. This allows you test preview features for AVD before they are released to production.", + "constraints": { + "required": false + } + }, + { + "name": "hostPoolType", + "type": "Microsoft.Common.DropDown", + "visible": true, + "label": "Host Pool Type", + "defaultValue": "Pooled", + "multiLine": true, + "toolTip": "", + "constraints": { + "required": true, + "allowedValues": [ + { + "label": "Pooled", + "value": "Pooled" + }, + { + "label": "Personal", + "value": "Personal" + } + ] + } + }, + { + "name": "workloadType", + "type": "Microsoft.Common.DropDown", + "label": "Workload Type", + "visible": "[or(equals(steps('basics').scenario.profile, 'arcGisPro'), and(equals(steps('basics').scenario.profile, 'generic'), equals(steps('management').hostPool.hostPoolType, 'Pooled')))]", + "filter": true, + "defaultValue": "[if(equals(steps('basics').scenario.profile, 'arcGisPro'), 'Medium', 'Heavy (2 users per core)')]", + "toolTip": "Select the type of image to deploy on the session hosts.", + "multiLine": true, + "constraints": { + "required": true, + "allowedValues": "[if(equals(steps('basics').scenario.profile, 'arcGisPro'), parse('[{\"label\": \"Light\",\"description\": \"Simple 2-D map visualizations, basic analyses, and reporting.\",\"value\": 6},{\"label\": \"Medium\",\"description\": \"Advanced 2-D or 3-D map visualizations, multi-source data analyses leveraging geoprocessing tools, reporting, and editing.\",\"value\": 4},{\"label\": \"Heavy\",\"description\": \"Complex 2-D or 3-D map visualizations including advanced symbology, analyses with visability and line of site, reporting, and editing.\",\"value\": 3}]'), parse('[{\"label\": \"Light (6 users per core)\",\"description\": \"Basic data entry tasks\",\"value\": \"6.0\"},{\"label\": \"Medium (4 users per core)\",\"description\": \"Consultants and market researchers\",\"value\": \"4.0\"},{\"label\": \"Heavy (2 users per core)\",\"description\": \"Software engineers and content creators\",\"value\": \"2.0\"},{\"label\": \"Power (1 user per core)\",\"description\": \"Graphic designers, 3D model makers, and machine learning researchers\",\"value\": \"1.0\"}]'))]" + } + }, + { + "name": "customRdpProperties", + "type": "Microsoft.Common.TextBox", + "visible": true, + "label": "Custom RDP properties", + "defaultValue": "[if(equals(steps('basics').scenario.profile, 'arcGisPro'), 'use multimon:i:1;drivestoredirect:s:;encode redirected video capture:i:1;redirected video capture encoding quality:i:2;audiomode:i:0;devicestoredirect:s:;redirectclipboard:i:0;redirectcomports:i:0;redirectlocation:i:1;redirectprinters:i:0;redirectsmartcards:i:1;redirectwebauthn:i:1;usbdevicestoredirect:s:;keyboardhook:i:2;', 'audiocapturemode:i:1;camerastoredirect:s:*;use multimon:i:0;drivestoredirect:s:;encode redirected video capture:i:1;redirected video capture encoding quality:i:1;audiomode:i:0;devicestoredirect:s:;redirectclipboard:i:0;redirectcomports:i:0;redirectlocation:i:1;redirectprinters:i:0;redirectsmartcards:i:1;redirectwebauthn:i:1;usbdevicestoredirect:s:;keyboardhook:i:2;')]", + "toolTip": "Specify the configuration for the RDP properties on the AVD host pool.", + "constraints": { + "required": true + } + }, + { + "name": "publicNetworkAccess", + "type": "Microsoft.Common.DropDown", + "visible": true, + "label": "Public network access", + "defaultValue": "Enabled", + "multiLine": true, + "toolTip": "Enabled: allows the host pool to be accessed from both public and private networks. Disabled: allows the host pool to only be accessed via private endpoints.", + "constraints": { + "required": true, + "allowedValues": [ + { + "label": "Disabled", + "value": "Disabled" + }, + { + "label": "Enabled", + "value": "Enabled" + }, + { + "label": "Enabled For Clients Only", + "value": "EnabledForClientsOnly" + }, + { + "label": "Enabled For Session Hosts Only", + "value": "EnabledForSessionHostsOnly" + } + ] + } + } + ] + }, + { + "name": "workspacesApi", + "type": "Microsoft.Solutions.ArmApiControl", + "request": { + "method": "GET", + "path": "[concat(steps('basics').scope.subscription.id, '/providers/Microsoft.DesktopVirtualization/workspaces?api-version=2022-02-10-preview')]" + } + }, + { + "name": "workspace", + "type": "Microsoft.Common.Section", + "visible": "[empty(first(filter(steps('management').workspacesApi.value, (item) => and(contains(item.name, steps('basics').naming.identifier), contains(item.name, 'feed'), contains(item.name, steps('basics').naming.environment), equals(item.location, steps('basics').hub.azureFirewallApi.location)))))]", + "label": "Workspace", + "elements": [ + { + "name": "publicNetworkAccess", + "type": "Microsoft.Common.DropDown", + "visible": true, + "label": "Public network access (feed)", + "defaultValue": "Enabled", + "multiLine": true, + "toolTip": "Enabled: allows the host pool to be accessed from both public and private networks. Disabled: allows the host pool to only be accessed via private endpoints.", + "constraints": { + "required": true, + "allowedValues": [ + { + "label": "Disabled", + "value": "Disabled" + }, + { + "label": "Enabled", + "value": "Enabled" + } + ] + } + } + ] + }, + { + "name": "assignment", + "type": "Microsoft.Common.Section", + "label": "Assignment", + "visible": true, + "elements": [ + { + "name": "description", + "type": "Microsoft.Common.TextBlock", + "visible": true, + "options": { + "text": "To access Azure Virtual Desktop, your end users will need to be assigned to the Desktop Applicaiton Group and if applicable, be given permissions on the storage service for FSLogix. Select the desired security groups below to give access to this AVD stamp. If deploying FSLogix, storage will be deployed for each group to support sharding. Be sure your groups are sized appropriately for each shard.", + "link": { + "label": "Learn more about FSLogix storage options for sizing your groups.", + "uri": "https://learn.microsoft.com/azure/virtual-desktop/store-fslogix-profile" + } + } + }, + { + "name": "groupPickerBlade", + "type": "Microsoft.Solutions.BladeInvokeControl", + "openBladeStatus": "[steps('management').assignment.groupSelector.changing]", + "bladeReference": { + "name": "ObjectPickerBlade", + "extension": "Microsoft_AAD_IAM", + "parameters": { + "queries": 32, + "disablers": 4, + "bladeSubtitle": "Pick the groups to assign", + "additionalQueriesOnSearch": 0, + "advancedQueryOptions": { + "suggestedObjectsOptions": {} + }, + "selectionMaximum": 100, + "selectionMinimum": 1, + "bladeTitle": "Select Groups", + "informationHeader": { + "informationText": "Select the groups that require access to the AVD stamp", + "informationLink": "" + }, + "inviteEnabled": true, + "searchBoxLabel": "Search for a group", + "searchBoxPlaceHolderText": "Enter a string in the group name", + "searchBoxTooltip": "This is the tooltip", + "searchGridNoRowsMessage": "No groups found", + "selectButtonText": "Select Groups", + "selectedGridLabel": "Selected Groups", + "selectedGridNoRowsMessage": "You must select at least one group" + }, + "inFullScreen": false + }, + "transforms": { + "selection": "selectedObjects|[*].{displayName:displayName, objectId:id}" + } + }, + { + "name": "groupSelector", + "type": "Microsoft.Common.Selector", + "label": "Select Groups", + "keyPath": "displayName", + "descriptionKeyPath": "id", + "value": "[steps('management').assignment.groupPickerBlade.transformed.selection]", + "visible": true, + "barColor": "[if(empty(steps('management').assignment.groupPickerBlade), '#FF0000', '#7fba00')]", + "constraints": { + "required": true + }, + "link": "[if(empty(steps('management').assignment.groupPickerBlade), 'Select groups', 'Re-select groups')]" + } + ] + }, + { + "name": "friendlyNames", + "type": "Microsoft.Common.Section", + "label": "Friendly Names for AVD Client", + "visible": true, + "elements": [ + { + "name": "custom", + "type": "Microsoft.Common.CheckBox", + "visible": true, + "label": "Custom friendly names?", + "defaultValue": false, + "toolTip": "Choose whether to add custom names to the AVD feed workspace and the desktop for the AVD client." + }, + { + "name": "feedWorkspace", + "type": "Microsoft.Common.TextBox", + "label": "Feed Workspace", + "defaultValue": "", + "placeholder": "Example: Information Technology", + "toolTip": "Input the friendly name for the AVD workspace that will be displayed in the end user's client. This value should apply to all the stamp indexes within the same identifier and would mostly likely represent your business unit or project.", + "visible": "[and(steps('management').friendlyNames.custom, empty(first(filter(steps('management').workspacesApi.value, (item) => and(contains(item.name, steps('basics').naming.identifier), contains(item.name, 'feed'), contains(item.name, steps('basics').naming.environment), equals(item.location, steps('basics').hub.azureFirewallApi.location))))))]", + "constraints": { + "required": false, + "regex": "^.{1,64}$", + "validationMessage": "The value must be between 1 and 64 characters in length." + } + }, + { + "name": "sessionDesktop", + "type": "Microsoft.Common.TextBox", + "label": "Desktop", + "defaultValue": "", + "placeholder": "Example: Help Desk", + "toolTip": "Input the friendly name for the AVD Session Desktop application that will be displayed in the end user's client. This value should represent the workload (eg., Help Desk, Development, or Administration) that will be supported on the host pool.", + "visible": "[steps('management').friendlyNames.custom]", + "constraints": { + "required": false, + "regex": "^.{1,64}$", + "validationMessage": "The value must be between 1 and 64 characters in length." + } + } + ] + }, { "name": "servicePrincipalsApi", "type": "Microsoft.Solutions.GraphApiControl", @@ -1315,7 +1316,7 @@ } }, { - "name": "controlPlaneSubnetAddressCidrRange", + "name": "managementSubnetAddressCidrRange", "label": "Control Plane Subnet CIDR range", "type": "Microsoft.Common.TextBox", "defaultValue": "[concat('10.0.1', string(add(41, mul(2, steps('basics').naming.stampIndex))), '.0/26')]", @@ -1328,19 +1329,19 @@ "message": "Invalid CIDR range. The address prefix must be in the range [24,28]." }, { - "isValid": "[if(greaterOrEquals(last(split(steps('network').vnet.virtualNetworkAddressCidrRange, '/')), 8), equals(last(take(split(first(split(steps('network').vnet.virtualNetworkAddressCidrRange, '/')), '.'), 1)), last(take(split(first(split(steps('network').vnet.controlPlaneSubnetAddressCidrRange, '/')), '.'), 1))), true)]", + "isValid": "[if(greaterOrEquals(last(split(steps('network').vnet.virtualNetworkAddressCidrRange, '/')), 8), equals(last(take(split(first(split(steps('network').vnet.virtualNetworkAddressCidrRange, '/')), '.'), 1)), last(take(split(first(split(steps('network').vnet.managementSubnetAddressCidrRange, '/')), '.'), 1))), true)]", "message": "CIDR range not within virtual network CIDR range (first octet)." }, { - "isValid": "[if(greaterOrEquals(last(split(steps('network').vnet.virtualNetworkAddressCidrRange, '/')), 16), equals(last(take(split(first(split(steps('network').vnet.virtualNetworkAddressCidrRange, '/')), '.'), 2)), last(take(split(first(split(steps('network').vnet.controlPlaneSubnetAddressCidrRange, '/')), '.'), 2))), true)]", + "isValid": "[if(greaterOrEquals(last(split(steps('network').vnet.virtualNetworkAddressCidrRange, '/')), 16), equals(last(take(split(first(split(steps('network').vnet.virtualNetworkAddressCidrRange, '/')), '.'), 2)), last(take(split(first(split(steps('network').vnet.managementSubnetAddressCidrRange, '/')), '.'), 2))), true)]", "message": "CIDR range not within virtual network CIDR range (second octet)." }, { - "isValid": "[if(greaterOrEquals(last(split(steps('network').vnet.virtualNetworkAddressCidrRange, '/')), 24), equals(last(take(split(first(split(steps('network').vnet.virtualNetworkAddressCidrRange, '/')), '.'), 3)), last(take(split(first(split(steps('network').vnet.controlPlaneSubnetAddressCidrRange, '/')), '.'), 3))), true)]", + "isValid": "[if(greaterOrEquals(last(split(steps('network').vnet.virtualNetworkAddressCidrRange, '/')), 24), equals(last(take(split(first(split(steps('network').vnet.virtualNetworkAddressCidrRange, '/')), '.'), 3)), last(take(split(first(split(steps('network').vnet.managementSubnetAddressCidrRange, '/')), '.'), 3))), true)]", "message": "CIDR range not within virtual network CIDR range (third octet)." }, { - "isValid": "[lessOrEquals(last(split(steps('network').vnet.virtualNetworkAddressCidrRange, '/')), last(split(steps('network').vnet.controlPlaneSubnetAddressCidrRange, '/')))]", + "isValid": "[lessOrEquals(last(split(steps('network').vnet.virtualNetworkAddressCidrRange, '/')), last(split(steps('network').vnet.managementSubnetAddressCidrRange, '/')))]", "message": "CIDR range not within virtual network CIDR range (subnet mask)." } ] @@ -1572,6 +1573,8 @@ "Microsoft.Compute/virtualMachines", "Microsoft.DesktopVirtualization/applicationGroups", "Microsoft.DesktopVirtualization/hostPools", + "Microsoft.DesktopVirtualization/scalingPlans", + "Microsoft.DesktopVirtualization/workspaces", "Microsoft.Insights/components", "Microsoft.Insights/dataCollectionEndpoints", "Microsoft.Insights/dataCollectionRules", @@ -1600,34 +1603,34 @@ "parameters": { "activeDirectorySolution": "[if(and(equals(steps('hosts').identity.solution, 'MicrosoftEntraId'), steps('hosts').identity.intune), 'MicrosoftEntraIdIntuneEnrollment', steps('hosts').identity.solution)]", "availability": "[steps('hosts').virtualMachines.availability]", - "availabilityZones": "[first(map(first(map(filter(steps('hosts').virtualMachines.resourceSkusApi.value, (item) => contains(item.name, if(and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('controlPlane').hostPool.hostPoolType, 'Pooled')), steps('hosts').virtualMachines.sizeArcGisProMulti, if(and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('controlPlane').hostPool.hostPoolType, 'Personal'), equals(steps('controlPlane').hostPool.workloadType, 6)), steps('hosts').virtualMachines.sizeArcGisProSingleLight, if(and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('controlPlane').hostPool.hostPoolType, 'Personal'), equals(steps('controlPlane').hostPool.workloadType, 4)), steps('hosts').virtualMachines.sizeArcGisProSingleMedium, if(and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('controlPlane').hostPool.hostPoolType, 'Personal'), equals(steps('controlPlane').hostPool.workloadType, 3)), steps('hosts').virtualMachines.sizeArcGisProSingleHeavy, steps('hosts').virtualMachines.sizeGeneric)))))), (item) => item.locationInfo)), (item) => item.zones))]", + "availabilityZones": "[first(map(first(map(filter(steps('hosts').virtualMachines.resourceSkusApi.value, (item) => contains(item.name, if(and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('management').hostPool.hostPoolType, 'Pooled')), steps('hosts').virtualMachines.sizeArcGisProMulti, if(and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('management').hostPool.hostPoolType, 'Personal'), equals(steps('management').hostPool.workloadType, 6)), steps('hosts').virtualMachines.sizeArcGisProSingleLight, if(and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('management').hostPool.hostPoolType, 'Personal'), equals(steps('management').hostPool.workloadType, 4)), steps('hosts').virtualMachines.sizeArcGisProSingleMedium, if(and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('management').hostPool.hostPoolType, 'Personal'), equals(steps('management').hostPool.workloadType, 3)), steps('hosts').virtualMachines.sizeArcGisProSingleHeavy, steps('hosts').virtualMachines.sizeGeneric)))))), (item) => item.locationInfo)), (item) => item.zones))]", "avdObjectId": "[first(map(steps('management').servicePrincipalsApi.value, (item) => item.id))]", "azureNetAppFilesSubnetAddressPrefix": "[steps('network').vnet.azureNetAppFilesSubnetAddressCidrRange]", - "customRdpProperty": "[steps('controlPlane').hostPool.customRdpProperties]", + "customRdpProperty": "[steps('management').hostPool.customRdpProperties]", "deployActivityLogDiagnosticSetting": "[empty(steps('compliance').diagnosticSettingsApi.value)]", "deployDefender": "[and(steps('compliance').defenderForCloud.deployDefender, empty(steps('compliance').defenderForCloud.workspaceSettingsApi.value))]", "deployNetworkWatcher": "[empty(filter(steps('basics').networkWatchersApi.value, (item) => equals(item.location, steps('basics').scope.location.name)))]", "deployPolicy": "[steps('compliance').policySection.deployPolicy]", - "desktopFriendlyName": "[steps('controlPlane').friendlyNames.sessionDesktop]", + "desktopFriendlyName": "[steps('management').friendlyNames.sessionDesktop]", "diskSku": "[if(equals(steps('basics').scenario.profile, 'arcGisPro'), 'Premium_LRS', steps('hosts').virtualMachines.diskSku)]", "domainJoinPassword": "[steps('hosts').domainJoinCredentials.domainPassword]", "domainJoinUserPrincipalName": "[steps('hosts').domainJoinCredentials.domainUserPrincipalName]", "domainName": "[steps('hosts').identity.domainName]", "drainMode": "[steps('management').drainMode.enableDrainMode]", "emailSecurityContact": "[if(and(steps('compliance').defenderForCloud.deployDefender, empty(steps('compliance').defenderForCloud.workspaceSettingsApi.value)), steps('compliance').defenderForCloud.emailSecurityContact, '')]", - "enableAcceleratedNetworking": "[bool(first(map(filter(first(map(filter(steps('hosts').virtualMachines.resourceSkusApi.value, (item) => contains(item.name, if(and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('controlPlane').hostPool.hostPoolType, 'Pooled')), steps('hosts').virtualMachines.sizeArcGisProMulti, if(and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('controlPlane').hostPool.hostPoolType, 'Personal'), equals(steps('controlPlane').hostPool.workloadType, 6)), steps('hosts').virtualMachines.sizeArcGisProSingleLight, if(and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('controlPlane').hostPool.hostPoolType, 'Personal'), equals(steps('controlPlane').hostPool.workloadType, 4)), steps('hosts').virtualMachines.sizeArcGisProSingleMedium, if(and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('controlPlane').hostPool.hostPoolType, 'Personal'), equals(steps('controlPlane').hostPool.workloadType, 3)), steps('hosts').virtualMachines.sizeArcGisProSingleHeavy, steps('hosts').virtualMachines.sizeGeneric)))))), (item) => item.capabilities)), (item) => equals(item.name, 'AcceleratedNetworkingEnabled')), (item) => item.value)))]", + "enableAcceleratedNetworking": "[bool(first(map(filter(first(map(filter(steps('hosts').virtualMachines.resourceSkusApi.value, (item) => contains(item.name, if(and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('management').hostPool.hostPoolType, 'Pooled')), steps('hosts').virtualMachines.sizeArcGisProMulti, if(and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('management').hostPool.hostPoolType, 'Personal'), equals(steps('management').hostPool.workloadType, 6)), steps('hosts').virtualMachines.sizeArcGisProSingleLight, if(and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('management').hostPool.hostPoolType, 'Personal'), equals(steps('management').hostPool.workloadType, 4)), steps('hosts').virtualMachines.sizeArcGisProSingleMedium, if(and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('management').hostPool.hostPoolType, 'Personal'), equals(steps('management').hostPool.workloadType, 3)), steps('hosts').virtualMachines.sizeArcGisProSingleHeavy, steps('hosts').virtualMachines.sizeGeneric)))))), (item) => item.capabilities)), (item) => equals(item.name, 'AcceleratedNetworkingEnabled')), (item) => item.value)))]", "enableApplicationInsights": "[steps('management').monitoring.enableApplicationInsights]", "enableAvdInsights": "[steps('management').monitoring.enableAvdInsights]", "enableTelemetry": "[steps('basics').scenario.enablePartnerTelemetry]", "environmentAbbreviation": "[steps('basics').naming.environment]", "existingSharedActiveDirectoryConnection": "[steps('profiles').storage.existingSharedActiveDirectoryConnection]", - "existingFeedWorkspaceResourceId": "[if(empty(steps('controlPlane').workspacesApi.value), '', first(map(filter(steps('controlPlane').workspacesApi.value, (item) => and(contains(item.name, steps('basics').naming.identifier), contains(item.name, 'feed'), contains(item.name, steps('basics').naming.environment), equals(item.location, steps('basics').hub.azureFirewallApi.location))), (item) => item.id)))]", + "existingFeedWorkspaceResourceId": "[if(empty(steps('management').workspacesApi.value), '', first(map(filter(steps('management').workspacesApi.value, (item) => and(contains(item.name, steps('basics').naming.identifier), contains(item.name, 'feed'), contains(item.name, steps('basics').naming.environment), equals(item.location, steps('basics').hub.azureFirewallApi.location))), (item) => item.id)))]", "fslogixShareSizeInGB": "[if(equals(steps('profiles').profileSolution, 'local'), 100, steps('profiles').storage.fileShareSize)]", "fslogixContainerType": "[steps('profiles').storage.fslogixContainerType]", "fslogixStorageService": "[if(equals(steps('profiles').profileSolution, 'local'), 'None', steps('profiles').storage.service)]", "functionAppSubnetAddressPrefix": "[steps('network').vnet.functionAppSubnetAddressCidrRange]", - "hostPoolPublicNetworkAccess": "[steps('controlPlane').hostPool.publicNetworkAccess]", - "hostPoolType": "[steps('controlPlane').hostPool.hostPoolType]", + "hostPoolPublicNetworkAccess": "[steps('management').hostPool.publicNetworkAccess]", + "hostPoolType": "[steps('management').hostPool.hostPoolType]", "hubAzureFirewallResourceId": "[steps('basics').hub.azureFirewall]", "hubVirtualNetworkResourceId": "[steps('basics').hub.virtualNetwork]", "identifier": "[steps('basics').naming.identifier]", @@ -1648,24 +1651,24 @@ "scalingWeekdaysPeakStartTime": "[steps('management').scalingPlan.weekdaysPeakStartTime]", "scalingWeekendsOffPeakStartTime": "[steps('management').scalingPlan.weekendsOffPeakStartTime]", "scalingWeekendsPeakStartTime": "[steps('management').scalingPlan.weekendsPeakStartTime]", - "securityPrincipals": "[steps('controlPlane').assignment.groups]", + "securityPrincipals": "[steps('management').assignment.groupPickerBlade.transformed.selection]", "sessionHostCount": "[steps('hosts').virtualMachines.count]", "sessionHostIndex": 0, "sharedServicesSubnetResourceId": "[first(map(steps('basics').sharedServicesSubnetsApi.value, (item) => item.id))]", "stampIndex": "[steps('basics').naming.stampIndex]", - "storageCount": "[if(equals(steps('profiles').profileSolution, 'local'), 0, length(steps('controlPlane').assignment.groups))]", + "storageCount": "[if(equals(steps('profiles').profileSolution, 'local'), 0, length(steps('management').assignment.groupPickerBlade.transformed.selection))]", "storageIndex": 0, - "subnetAddressPrefixes": "[parse(concat('[\"', steps('network').vnet.sessionHostsSubnetAddressCidrRange, '\",\"', steps('network').vnet.controlPlaneSubnetAddressCidrRange, '\"]'))]", + "subnetAddressPrefixes": "[parse(concat('[\"', steps('network').vnet.sessionHostsSubnetAddressCidrRange, '\",\"', steps('network').vnet.managementSubnetAddressCidrRange, '\"]'))]", "tags": "[steps('tags').tags]", - "usersPerCore": "[if(equals(steps('controlPlane').hostPool.hostPoolType, 'Pooled'), steps('controlPlane').hostPool.workloadType, 1)]", - "validationEnvironment": "[steps('controlPlane').hostPool.validation]", + "usersPerCore": "[if(equals(steps('management').hostPool.hostPoolType, 'Pooled'), steps('management').hostPool.workloadType, 1)]", + "validationEnvironment": "[steps('management').hostPool.validation]", "virtualMachinePassword": "[steps('hosts').localAdminCredentials.localAdminPassword]", - "virtualMachineSize": "[if(and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('controlPlane').hostPool.hostPoolType, 'Pooled')), steps('hosts').virtualMachines.sizeArcGisProMulti, if(and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('controlPlane').hostPool.hostPoolType, 'Personal'), equals(steps('controlPlane').hostPool.workloadType, 6)), steps('hosts').virtualMachines.sizeArcGisProSingleLight, if(and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('controlPlane').hostPool.hostPoolType, 'Personal'), equals(steps('controlPlane').hostPool.workloadType, 4)), steps('hosts').virtualMachines.sizeArcGisProSingleMedium, if(and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('controlPlane').hostPool.hostPoolType, 'Personal'), equals(steps('controlPlane').hostPool.workloadType, 3)), steps('hosts').virtualMachines.sizeArcGisProSingleHeavy, steps('hosts').virtualMachines.sizeGeneric))))]", + "virtualMachineSize": "[if(and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('management').hostPool.hostPoolType, 'Pooled')), steps('hosts').virtualMachines.sizeArcGisProMulti, if(and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('management').hostPool.hostPoolType, 'Personal'), equals(steps('management').hostPool.workloadType, 6)), steps('hosts').virtualMachines.sizeArcGisProSingleLight, if(and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('management').hostPool.hostPoolType, 'Personal'), equals(steps('management').hostPool.workloadType, 4)), steps('hosts').virtualMachines.sizeArcGisProSingleMedium, if(and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('management').hostPool.hostPoolType, 'Personal'), equals(steps('management').hostPool.workloadType, 3)), steps('hosts').virtualMachines.sizeArcGisProSingleHeavy, steps('hosts').virtualMachines.sizeGeneric))))]", "virtualMachineUsername": "[steps('hosts').localAdminCredentials.localAdminUsername]", - "virtualMachineVirtualCpuCount": "[first(map(filter(first(map(filter(steps('basics').scenario.virtualMachineSkusApi.value, (item) => equals(item.name, if(and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('controlPlane').hostPool.hostPoolType, 'Pooled')), steps('hosts').virtualMachines.sizeArcGisProMulti, if(and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('controlPlane').hostPool.hostPoolType, 'Personal'), equals(steps('controlPlane').hostPool.workloadType, 6)), steps('hosts').virtualMachines.sizeArcGisProSingleLight, if(and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('controlPlane').hostPool.hostPoolType, 'Personal'), equals(steps('controlPlane').hostPool.workloadType, 4)), steps('hosts').virtualMachines.sizeArcGisProSingleMedium, if(and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('controlPlane').hostPool.hostPoolType, 'Personal'), equals(steps('controlPlane').hostPool.workloadType, 3)), steps('hosts').virtualMachines.sizeArcGisProSingleHeavy, steps('hosts').virtualMachines.sizeGeneric)))))), (item) => item.capabilities)), (item) => equals(item.name, 'vCPUs')), (item) => item.value))]", + "virtualMachineVirtualCpuCount": "[first(map(filter(first(map(filter(steps('basics').scenario.virtualMachineSkusApi.value, (item) => equals(item.name, if(and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('management').hostPool.hostPoolType, 'Pooled')), steps('hosts').virtualMachines.sizeArcGisProMulti, if(and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('management').hostPool.hostPoolType, 'Personal'), equals(steps('management').hostPool.workloadType, 6)), steps('hosts').virtualMachines.sizeArcGisProSingleLight, if(and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('management').hostPool.hostPoolType, 'Personal'), equals(steps('management').hostPool.workloadType, 4)), steps('hosts').virtualMachines.sizeArcGisProSingleMedium, if(and(equals(steps('basics').scenario.profile, 'arcGisPro'), equals(steps('management').hostPool.hostPoolType, 'Personal'), equals(steps('management').hostPool.workloadType, 3)), steps('hosts').virtualMachines.sizeArcGisProSingleHeavy, steps('hosts').virtualMachines.sizeGeneric)))))), (item) => item.capabilities)), (item) => equals(item.name, 'vCPUs')), (item) => item.value))]", "virtualNetworkAddressPrefixes": "[parse(concat('[\"', steps('network').vnet.virtualNetworkAddressCidrRange, '\"]'))]", - "workspaceFriendlyName": "[if(empty(first(map(filter(steps('controlPlane').workspacesApi.value, (item) => and(contains(item.name, steps('basics').naming.identifier), contains(item.name, 'feed'), contains(item.name, steps('basics').naming.environment), equals(item.location, steps('basics').hub.azureFirewallApi.location))), (item) => item.id))), steps('controlPlane').friendlyNames.feedWorkspace, first(map(filter(steps('controlPlane').workspacesApi.value, (item) => and(contains(item.name, steps('basics').naming.identifier), contains(item.name, 'feed'), contains(item.name, steps('basics').naming.environment), equals(item.location, steps('basics').hub.azureFirewallApi.location))), (item) => item.properties.friendlyName)))]", - "workspacePublicNetworkAccess": "[if(empty(first(map(filter(steps('controlPlane').workspacesApi.value, (item) => and(contains(item.name, steps('basics').naming.identifier), contains(item.name, 'feed'), contains(item.name, steps('basics').naming.environment), equals(item.location, steps('basics').hub.azureFirewallApi.location))), (item) => item.id))), steps('controlPlane').workspace.publicNetworkAccess, first(map(filter(steps('controlPlane').workspacesApi.value, (item) => and(contains(item.name, steps('basics').naming.identifier), contains(item.name, 'feed'), contains(item.name, steps('basics').naming.environment), equals(item.location, steps('basics').hub.azureFirewallApi.location))), (item) => item.properties.publicNetworkAccess)))]" + "workspaceFriendlyName": "[if(empty(first(map(filter(steps('management').workspacesApi.value, (item) => and(contains(item.name, steps('basics').naming.identifier), contains(item.name, 'feed'), contains(item.name, steps('basics').naming.environment), equals(item.location, steps('basics').hub.azureFirewallApi.location))), (item) => item.id))), steps('management').friendlyNames.feedWorkspace, first(map(filter(steps('management').workspacesApi.value, (item) => and(contains(item.name, steps('basics').naming.identifier), contains(item.name, 'feed'), contains(item.name, steps('basics').naming.environment), equals(item.location, steps('basics').hub.azureFirewallApi.location))), (item) => item.properties.friendlyName)))]", + "workspacePublicNetworkAccess": "[if(empty(first(map(filter(steps('management').workspacesApi.value, (item) => and(contains(item.name, steps('basics').naming.identifier), contains(item.name, 'feed'), contains(item.name, steps('basics').naming.environment), equals(item.location, steps('basics').hub.azureFirewallApi.location))), (item) => item.id))), steps('management').workspace.publicNetworkAccess, first(map(filter(steps('management').workspacesApi.value, (item) => and(contains(item.name, steps('basics').naming.identifier), contains(item.name, 'feed'), contains(item.name, steps('basics').naming.environment), equals(item.location, steps('basics').hub.azureFirewallApi.location))), (item) => item.properties.publicNetworkAccess)))]" }, "kind": "Subscription", "location": "[steps('basics').scope.location.name]", diff --git a/src/bicep/add-ons/imaging/solution.json b/src/bicep/add-ons/imaging/solution.json index b13319935..bccc542d8 100644 --- a/src/bicep/add-ons/imaging/solution.json +++ b/src/bicep/add-ons/imaging/solution.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "13460307319082964405" + "version": "0.31.92.45157", + "templateHash": "1176843216741362451" } }, "parameters": { @@ -600,8 +600,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "16140764241259827428" + "version": "0.31.92.45157", + "templateHash": "14266827690996546891" } }, "parameters": { @@ -877,8 +877,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "16220429751656280628" + "version": "0.31.92.45157", + "templateHash": "15810721730485220824" } }, "parameters": { @@ -957,8 +957,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "3270228280505109788" + "version": "0.31.92.45157", + "templateHash": "11125044402666498605" } }, "parameters": { @@ -1410,6 +1410,7 @@ "remoteApplicationGroups": "vdag", "resourceGroups": "rg", "routeTables": "rt", + "scalingPlans": "vdscaling", "storageAccounts": "st", "subnets": "snet", "userAssignedIdentities": "id", @@ -1424,7 +1425,7 @@ "namingConvention_Service": "[format('{0}-{1}{2}-{3}-{4}-{5}-{6}', toLower(parameters('resourcePrefix')), if(empty(parameters('stampIndex')), '', format('{0}-', parameters('stampIndex'))), parameters('tokens').resource, parameters('networkName'), parameters('tokens').service, variables('locationAbbreviation'), parameters('environmentAbbreviation'))]", "names": { "actionGroup": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').actionGroups)]", - "applicationGroup": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').applicationGroups)]", + "applicationGroup": "[replace(variables('namingConvention'), parameters('tokens').resource, format('{0}-desktop', variables('resourceAbbreviations').applicationGroups))]", "applicationInsights": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').applicationInsights)]", "appServicePlan": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').appServicePlans)]", "automationAccount": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').automationAccounts)]", @@ -1478,6 +1479,8 @@ "recoveryServicesVaultPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, variables('resourceAbbreviations').recoveryServicesVaults)]", "resourceGroup": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').resourceGroups)]", "routeTable": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').routeTables)]", + "scalingPlan": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').scalingPlans)]", + "scalingPlanDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, variables('resourceAbbreviations').scalingPlans)]", "storageAccount": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').storageAccounts), parameters('networkName'), parameters('networkShortName'))]", "storageAccountDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').storageAccounts))]", "storageAccountBlobNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-blob', variables('resourceAbbreviations').storageAccounts))]", @@ -1495,14 +1498,14 @@ "virtualMachineNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').virtualMachines))]", "virtualNetwork": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').virtualNetworks)]", "virtualNetworkDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, variables('resourceAbbreviations').virtualNetworks)]", - "workspaceFeed": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').workspaces), format('-{0}', parameters('stampIndex')), '')]", - "workspaceFeedDiagnosticSetting": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", - "workspaceFeedNetworkInterface": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", - "workspaceFeedPrivateEndpoint": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", - "workspaceGlobal": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').workspaces), format('-{0}', parameters('stampIndex')), '')]", - "workspaceGlobalDiagnosticSetting": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", - "workspaceGlobalNetworkInterface": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", - "workspaceGlobalPrivateEndpoint": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]" + "workspaceFeed": "[replace(replace(variables('namingConvention'), parameters('tokens').resource, format('{0}-feed', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", + "workspaceFeedDiagnosticSetting": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-feed', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", + "workspaceFeedNetworkInterface": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-feed', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", + "workspaceFeedPrivateEndpoint": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-feed', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", + "workspaceGlobal": "[replace(replace(variables('namingConvention'), parameters('tokens').resource, format('{0}-global', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", + "workspaceGlobalDiagnosticSetting": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-global', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", + "workspaceGlobalNetworkInterface": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-global', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", + "workspaceGlobalPrivateEndpoint": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-global', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]" } }, "resources": [], @@ -1548,8 +1551,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "4151572700986885014" + "version": "0.31.92.45157", + "templateHash": "17619606927129129347" } }, "parameters": { @@ -1688,8 +1691,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "10109660736443081073" + "version": "0.31.92.45157", + "templateHash": "9371138343009436350" } }, "parameters": { @@ -1817,8 +1820,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "2001402118470594055" + "version": "0.31.92.45157", + "templateHash": "12095518102496352105" } }, "parameters": { @@ -1953,8 +1956,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "3535186238457701125" + "version": "0.31.92.45157", + "templateHash": "11818136489056939588" } }, "parameters": { @@ -2071,8 +2074,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "6722359274420487391" + "version": "0.31.92.45157", + "templateHash": "3681943409502537301" } }, "parameters": { @@ -2154,8 +2157,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "12652296496577802490" + "version": "0.31.92.45157", + "templateHash": "18425321023142226965" } }, "parameters": { @@ -2257,8 +2260,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "17592952825859536181" + "version": "0.31.92.45157", + "templateHash": "15002544166394392504" } }, "parameters": { @@ -2334,8 +2337,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "7007835755326231171" + "version": "0.31.92.45157", + "templateHash": "16817025486402215719" } }, "parameters": { @@ -2475,8 +2478,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "3832300165614083813" + "version": "0.31.92.45157", + "templateHash": "10267893616110384815" } }, "parameters": { @@ -2528,8 +2531,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "2015499370398293300" + "version": "0.31.92.45157", + "templateHash": "5574526676512163672" } }, "parameters": { @@ -2602,8 +2605,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "9502319494004310539" + "version": "0.31.92.45157", + "templateHash": "11553909803736438916" } }, "parameters": { @@ -2655,8 +2658,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "2015499370398293300" + "version": "0.31.92.45157", + "templateHash": "5574526676512163672" } }, "parameters": { @@ -2755,8 +2758,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "10999644342250296299" + "version": "0.31.92.45157", + "templateHash": "4304275711041823961" } }, "parameters": { @@ -2813,8 +2816,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "2757774880390840506" + "version": "0.31.92.45157", + "templateHash": "16077950968688123011" } }, "parameters": { @@ -2916,8 +2919,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "11152278840466293351" + "version": "0.31.92.45157", + "templateHash": "12787329163785242553" } }, "parameters": { @@ -3005,8 +3008,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "1874197755006172394" + "version": "0.31.92.45157", + "templateHash": "11761568940379970751" } }, "parameters": { @@ -3263,8 +3266,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "17613382135787640077" + "version": "0.31.92.45157", + "templateHash": "4207798980384159491" } }, "parameters": { @@ -3343,8 +3346,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "8372265102618018066" + "version": "0.31.92.45157", + "templateHash": "1966035938992047983" } }, "parameters": { @@ -3438,8 +3441,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "6675708379514380442" + "version": "0.31.92.45157", + "templateHash": "7930493629995578222" } }, "parameters": { @@ -3596,8 +3599,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "2377877844306490130" + "version": "0.31.92.45157", + "templateHash": "17358288344184718166" } }, "parameters": { @@ -3712,8 +3715,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "13359240625570837130" + "version": "0.31.92.45157", + "templateHash": "1625826941635729014" } }, "parameters": { @@ -3993,8 +3996,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "8480968961555779332" + "version": "0.31.92.45157", + "templateHash": "4345251511078445463" } }, "parameters": { @@ -4068,8 +4071,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "8389358797157027271" + "version": "0.31.92.45157", + "templateHash": "4687229436121899773" } }, "parameters": { @@ -4158,8 +4161,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "3320497816733398371" + "version": "0.31.92.45157", + "templateHash": "1721966359516622278" } }, "parameters": { @@ -4232,8 +4235,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "9851134383266019486" + "version": "0.31.92.45157", + "templateHash": "2073766618455932098" } }, "parameters": { @@ -4310,8 +4313,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "7358671180047253284" + "version": "0.31.92.45157", + "templateHash": "9546260853018527046" } }, "parameters": { @@ -4401,8 +4404,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "7046499602359448652" + "version": "0.31.92.45157", + "templateHash": "15014526386353172066" } }, "parameters": { @@ -4458,8 +4461,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "16614224552992880134" + "version": "0.31.92.45157", + "templateHash": "8545903679924437739" } }, "parameters": { @@ -4634,8 +4637,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "8372265102618018066" + "version": "0.31.92.45157", + "templateHash": "1966035938992047983" } }, "parameters": { @@ -4719,8 +4722,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "3588347827815061814" + "version": "0.31.92.45157", + "templateHash": "17047820191891552534" } }, "parameters": { @@ -5069,8 +5072,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "5226016669112266321" + "version": "0.31.92.45157", + "templateHash": "10919933768401588181" } }, "parameters": { @@ -5143,8 +5146,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "13440676993724521939" + "version": "0.31.92.45157", + "templateHash": "18352816396611024893" } }, "parameters": { @@ -5209,8 +5212,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "17072503057563414192" + "version": "0.31.92.45157", + "templateHash": "6200502377293455151" } }, "parameters": { @@ -5273,8 +5276,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "15644482179055938365" + "version": "0.31.92.45157", + "templateHash": "7660308816591621829" } }, "parameters": { @@ -5332,8 +5335,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "14434555555821069254" + "version": "0.31.92.45157", + "templateHash": "4828751436439954614" } }, "parameters": { @@ -5403,8 +5406,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "18308303526064452173" + "version": "0.31.92.45157", + "templateHash": "505668065736432808" } }, "parameters": { @@ -5493,8 +5496,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "17898789400078556340" + "version": "0.31.92.45157", + "templateHash": "15938438919628281730" } }, "parameters": { @@ -5771,8 +5774,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "14434361731662776378" + "version": "0.31.92.45157", + "templateHash": "3864397971513159924" } }, "parameters": { @@ -6026,8 +6029,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "7158084538460059744" + "version": "0.31.92.45157", + "templateHash": "10151591372092483611" } }, "parameters": { @@ -6112,8 +6115,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "1996155124645705619" + "version": "0.31.92.45157", + "templateHash": "16335756069146472371" } }, "parameters": { @@ -6322,8 +6325,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "2306888083293626312" + "version": "0.31.92.45157", + "templateHash": "17205876022497810397" } }, "parameters": { @@ -8410,8 +8413,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "10068687414279329406" + "version": "0.31.92.45157", + "templateHash": "8601742688955831915" } }, "parameters": { @@ -8777,8 +8780,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "15496706649353968351" + "version": "0.31.92.45157", + "templateHash": "4852229183946608444" } }, "parameters": { @@ -9313,8 +9316,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "2877200692627316157" + "version": "0.31.92.45157", + "templateHash": "8637536946355167151" } }, "parameters": { @@ -9687,8 +9690,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "10741782051085334434" + "version": "0.31.92.45157", + "templateHash": "8072924941165181371" } }, "parameters": { @@ -9961,8 +9964,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "10068687414279329406" + "version": "0.31.92.45157", + "templateHash": "8601742688955831915" } }, "parameters": { @@ -10171,8 +10174,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "8116252798517351972" + "version": "0.31.92.45157", + "templateHash": "6378421340828498206" } }, "parameters": { @@ -10422,8 +10425,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "885927068946767612" + "version": "0.31.92.45157", + "templateHash": "14159413360692546906" } }, "parameters": { @@ -10845,8 +10848,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "13463113052745382608" + "version": "0.31.92.45157", + "templateHash": "3743593020967196481" } }, "parameters": { @@ -10948,8 +10951,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "1633398337190030443" + "version": "0.31.92.45157", + "templateHash": "7677543733842229678" } }, "parameters": { @@ -11035,8 +11038,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "13463113052745382608" + "version": "0.31.92.45157", + "templateHash": "3743593020967196481" } }, "parameters": { @@ -11131,8 +11134,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "15079303238971201589" + "version": "0.31.92.45157", + "templateHash": "1029512189778888686" } }, "parameters": { @@ -11212,8 +11215,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "6420082476378279690" + "version": "0.31.92.45157", + "templateHash": "8843400045516737228" } }, "parameters": { @@ -11333,8 +11336,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "9737618372857567001" + "version": "0.31.92.45157", + "templateHash": "5465461266607898562" } }, "parameters": { @@ -11489,8 +11492,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "13012724632844552518" + "version": "0.31.92.45157", + "templateHash": "4834529006346309280" } }, "parameters": { diff --git a/src/bicep/add-ons/imaging/uiDefinition.json b/src/bicep/add-ons/imaging/uiDefinition.json index 3f07eeaf5..16a18b38a 100644 --- a/src/bicep/add-ons/imaging/uiDefinition.json +++ b/src/bicep/add-ons/imaging/uiDefinition.json @@ -1048,10 +1048,6 @@ "recommendedSizes": [ "Standard_D4ads_v5" ], - "constraints": { - "allowedSizes": [], - "numAvailabilityZonesRequired": 1 - }, "options": { "hideDiskTypeFilter": false }, diff --git a/src/bicep/add-ons/tier3/solution.json b/src/bicep/add-ons/tier3/solution.json index 43f0099ae..d155fc311 100644 --- a/src/bicep/add-ons/tier3/solution.json +++ b/src/bicep/add-ons/tier3/solution.json @@ -4,8 +4,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "16140764241259827428" + "version": "0.31.92.45157", + "templateHash": "14266827690996546891" } }, "parameters": { @@ -281,8 +281,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "16220429751656280628" + "version": "0.31.92.45157", + "templateHash": "15810721730485220824" } }, "parameters": { @@ -361,8 +361,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "3270228280505109788" + "version": "0.31.92.45157", + "templateHash": "11125044402666498605" } }, "parameters": { @@ -814,6 +814,7 @@ "remoteApplicationGroups": "vdag", "resourceGroups": "rg", "routeTables": "rt", + "scalingPlans": "vdscaling", "storageAccounts": "st", "subnets": "snet", "userAssignedIdentities": "id", @@ -828,7 +829,7 @@ "namingConvention_Service": "[format('{0}-{1}{2}-{3}-{4}-{5}-{6}', toLower(parameters('resourcePrefix')), if(empty(parameters('stampIndex')), '', format('{0}-', parameters('stampIndex'))), parameters('tokens').resource, parameters('networkName'), parameters('tokens').service, variables('locationAbbreviation'), parameters('environmentAbbreviation'))]", "names": { "actionGroup": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').actionGroups)]", - "applicationGroup": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').applicationGroups)]", + "applicationGroup": "[replace(variables('namingConvention'), parameters('tokens').resource, format('{0}-desktop', variables('resourceAbbreviations').applicationGroups))]", "applicationInsights": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').applicationInsights)]", "appServicePlan": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').appServicePlans)]", "automationAccount": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').automationAccounts)]", @@ -882,6 +883,8 @@ "recoveryServicesVaultPrivateEndpoint": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, variables('resourceAbbreviations').recoveryServicesVaults)]", "resourceGroup": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').resourceGroups)]", "routeTable": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').routeTables)]", + "scalingPlan": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').scalingPlans)]", + "scalingPlanDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, variables('resourceAbbreviations').scalingPlans)]", "storageAccount": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').storageAccounts), parameters('networkName'), parameters('networkShortName'))]", "storageAccountDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').storageAccounts))]", "storageAccountBlobNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-blob', variables('resourceAbbreviations').storageAccounts))]", @@ -899,14 +902,14 @@ "virtualMachineNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').virtualMachines))]", "virtualNetwork": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').virtualNetworks)]", "virtualNetworkDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, variables('resourceAbbreviations').virtualNetworks)]", - "workspaceFeed": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').workspaces), format('-{0}', parameters('stampIndex')), '')]", - "workspaceFeedDiagnosticSetting": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", - "workspaceFeedNetworkInterface": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", - "workspaceFeedPrivateEndpoint": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", - "workspaceGlobal": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').workspaces), format('-{0}', parameters('stampIndex')), '')]", - "workspaceGlobalDiagnosticSetting": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", - "workspaceGlobalNetworkInterface": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", - "workspaceGlobalPrivateEndpoint": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]" + "workspaceFeed": "[replace(replace(variables('namingConvention'), parameters('tokens').resource, format('{0}-feed', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", + "workspaceFeedDiagnosticSetting": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-feed', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", + "workspaceFeedNetworkInterface": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-feed', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", + "workspaceFeedPrivateEndpoint": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-feed', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", + "workspaceGlobal": "[replace(replace(variables('namingConvention'), parameters('tokens').resource, format('{0}-global', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", + "workspaceGlobalDiagnosticSetting": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-global', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", + "workspaceGlobalNetworkInterface": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-global', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", + "workspaceGlobalPrivateEndpoint": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-global', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]" } }, "resources": [], @@ -952,8 +955,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "4151572700986885014" + "version": "0.31.92.45157", + "templateHash": "17619606927129129347" } }, "parameters": { @@ -1092,8 +1095,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "10109660736443081073" + "version": "0.31.92.45157", + "templateHash": "9371138343009436350" } }, "parameters": { @@ -1221,8 +1224,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "2001402118470594055" + "version": "0.31.92.45157", + "templateHash": "12095518102496352105" } }, "parameters": { @@ -1357,8 +1360,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "3535186238457701125" + "version": "0.31.92.45157", + "templateHash": "11818136489056939588" } }, "parameters": { @@ -1475,8 +1478,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "6722359274420487391" + "version": "0.31.92.45157", + "templateHash": "3681943409502537301" } }, "parameters": { @@ -1558,8 +1561,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "12652296496577802490" + "version": "0.31.92.45157", + "templateHash": "18425321023142226965" } }, "parameters": { @@ -1661,8 +1664,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "17592952825859536181" + "version": "0.31.92.45157", + "templateHash": "15002544166394392504" } }, "parameters": { @@ -1738,8 +1741,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "7007835755326231171" + "version": "0.31.92.45157", + "templateHash": "16817025486402215719" } }, "parameters": { @@ -1879,8 +1882,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "3832300165614083813" + "version": "0.31.92.45157", + "templateHash": "10267893616110384815" } }, "parameters": { @@ -1932,8 +1935,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "2015499370398293300" + "version": "0.31.92.45157", + "templateHash": "5574526676512163672" } }, "parameters": { @@ -2006,8 +2009,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "9502319494004310539" + "version": "0.31.92.45157", + "templateHash": "11553909803736438916" } }, "parameters": { @@ -2059,8 +2062,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "2015499370398293300" + "version": "0.31.92.45157", + "templateHash": "5574526676512163672" } }, "parameters": { @@ -2159,8 +2162,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "10999644342250296299" + "version": "0.31.92.45157", + "templateHash": "4304275711041823961" } }, "parameters": { @@ -2217,8 +2220,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "2757774880390840506" + "version": "0.31.92.45157", + "templateHash": "16077950968688123011" } }, "parameters": { @@ -2320,8 +2323,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "11152278840466293351" + "version": "0.31.92.45157", + "templateHash": "12787329163785242553" } }, "parameters": { @@ -2409,8 +2412,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "1874197755006172394" + "version": "0.31.92.45157", + "templateHash": "11761568940379970751" } }, "parameters": { @@ -2667,8 +2670,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "17613382135787640077" + "version": "0.31.92.45157", + "templateHash": "4207798980384159491" } }, "parameters": { @@ -2747,8 +2750,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "8372265102618018066" + "version": "0.31.92.45157", + "templateHash": "1966035938992047983" } }, "parameters": { @@ -2842,8 +2845,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "6675708379514380442" + "version": "0.31.92.45157", + "templateHash": "7930493629995578222" } }, "parameters": { @@ -3000,8 +3003,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "2377877844306490130" + "version": "0.31.92.45157", + "templateHash": "17358288344184718166" } }, "parameters": { @@ -3116,8 +3119,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "13359240625570837130" + "version": "0.31.92.45157", + "templateHash": "1625826941635729014" } }, "parameters": { @@ -3397,8 +3400,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "8480968961555779332" + "version": "0.31.92.45157", + "templateHash": "4345251511078445463" } }, "parameters": { @@ -3472,8 +3475,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "8389358797157027271" + "version": "0.31.92.45157", + "templateHash": "4687229436121899773" } }, "parameters": { @@ -3562,8 +3565,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "3320497816733398371" + "version": "0.31.92.45157", + "templateHash": "1721966359516622278" } }, "parameters": { @@ -3636,8 +3639,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "9851134383266019486" + "version": "0.31.92.45157", + "templateHash": "2073766618455932098" } }, "parameters": { @@ -3714,8 +3717,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "7358671180047253284" + "version": "0.31.92.45157", + "templateHash": "9546260853018527046" } }, "parameters": { @@ -3805,8 +3808,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "7046499602359448652" + "version": "0.31.92.45157", + "templateHash": "15014526386353172066" } }, "parameters": { @@ -3862,8 +3865,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "16614224552992880134" + "version": "0.31.92.45157", + "templateHash": "8545903679924437739" } }, "parameters": { @@ -4038,8 +4041,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "8372265102618018066" + "version": "0.31.92.45157", + "templateHash": "1966035938992047983" } }, "parameters": { @@ -4123,8 +4126,8 @@ "metadata": { "_generator": { "name": "bicep", - "version": "0.30.23.60470", - "templateHash": "3588347827815061814" + "version": "0.31.92.45157", + "templateHash": "17047820191891552534" } }, "parameters": { diff --git a/src/bicep/mlz.json b/src/bicep/mlz.json index 105425e42..df9569d57 100644 --- a/src/bicep/mlz.json +++ b/src/bicep/mlz.json @@ -5,7 +5,7 @@ "_generator": { "name": "bicep", "version": "0.31.92.45157", - "templateHash": "7670256562437793548" + "templateHash": "4662751976093611386" } }, "parameters": { @@ -888,7 +888,7 @@ "_generator": { "name": "bicep", "version": "0.31.92.45157", - "templateHash": "4114664614478275863" + "templateHash": "15810721730485220824" } }, "parameters": { @@ -968,7 +968,7 @@ "_generator": { "name": "bicep", "version": "0.31.92.45157", - "templateHash": "6411408920489493501" + "templateHash": "11125044402666498605" } }, "parameters": { @@ -1435,7 +1435,7 @@ "namingConvention_Service": "[format('{0}-{1}{2}-{3}-{4}-{5}-{6}', toLower(parameters('resourcePrefix')), if(empty(parameters('stampIndex')), '', format('{0}-', parameters('stampIndex'))), parameters('tokens').resource, parameters('networkName'), parameters('tokens').service, variables('locationAbbreviation'), parameters('environmentAbbreviation'))]", "names": { "actionGroup": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').actionGroups)]", - "applicationGroup": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').applicationGroups)]", + "applicationGroup": "[replace(variables('namingConvention'), parameters('tokens').resource, format('{0}-desktop', variables('resourceAbbreviations').applicationGroups))]", "applicationInsights": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').applicationInsights)]", "appServicePlan": "[replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').appServicePlans)]", "automationAccount": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').automationAccounts)]", @@ -1508,14 +1508,14 @@ "virtualMachineNetworkInterface": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').virtualMachines))]", "virtualNetwork": "[replace(variables('namingConvention'), parameters('tokens').resource, variables('resourceAbbreviations').virtualNetworks)]", "virtualNetworkDiagnosticSetting": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, variables('resourceAbbreviations').virtualNetworks)]", - "workspaceFeed": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').workspaces), format('-{0}', parameters('stampIndex')), '')]", - "workspaceFeedDiagnosticSetting": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", - "workspaceFeedNetworkInterface": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", - "workspaceFeedPrivateEndpoint": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", - "workspaceGlobal": "[replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').workspaces), format('-{0}', parameters('stampIndex')), '')]", - "workspaceGlobalDiagnosticSetting": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", - "workspaceGlobalNetworkInterface": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", - "workspaceGlobalPrivateEndpoint": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-{1}', parameters('tokens').service, variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]" + "workspaceFeed": "[replace(replace(variables('namingConvention'), parameters('tokens').resource, format('{0}-feed', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", + "workspaceFeedDiagnosticSetting": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-feed', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", + "workspaceFeedNetworkInterface": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-feed', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", + "workspaceFeedPrivateEndpoint": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-feed', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", + "workspaceGlobal": "[replace(replace(variables('namingConvention'), parameters('tokens').resource, format('{0}-global', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", + "workspaceGlobalDiagnosticSetting": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').diagnosticSettings), parameters('tokens').service, format('{0}-global', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", + "workspaceGlobalNetworkInterface": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').networkInterfaces), parameters('tokens').service, format('{0}-global', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]", + "workspaceGlobalPrivateEndpoint": "[replace(replace(replace(variables('namingConvention_Service'), parameters('tokens').resource, variables('resourceAbbreviations').privateEndpoints), parameters('tokens').service, format('{0}-global', variables('resourceAbbreviations').workspaces)), format('-{0}', parameters('stampIndex')), '')]" } }, "resources": [], diff --git a/src/bicep/modules/naming-convention.bicep b/src/bicep/modules/naming-convention.bicep index 0a2680fea..b9b746e22 100644 --- a/src/bicep/modules/naming-convention.bicep +++ b/src/bicep/modules/naming-convention.bicep @@ -51,7 +51,7 @@ var namingConvention_Service = '${toLower(resourcePrefix)}-${empty(stampIndex) ? var names = { actionGroup: replace(namingConvention, tokens.resource, resourceAbbreviations.actionGroups) - applicationGroup: replace(namingConvention_Service, tokens.resource, resourceAbbreviations.applicationGroups) + applicationGroup: replace(namingConvention, tokens.resource, '${resourceAbbreviations.applicationGroups}-desktop') applicationInsights: replace(namingConvention_Service, tokens.resource, resourceAbbreviations.applicationInsights) appServicePlan: replace(namingConvention_Service, tokens.resource, resourceAbbreviations.appServicePlans) automationAccount: replace(namingConvention, tokens.resource, resourceAbbreviations.automationAccounts) @@ -124,14 +124,14 @@ var names = { virtualMachineNetworkInterface: replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.networkInterfaces), tokens.service, '${tokens.service}-${resourceAbbreviations.virtualMachines}') virtualNetwork: replace(namingConvention, tokens.resource, resourceAbbreviations.virtualNetworks) virtualNetworkDiagnosticSetting: replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.diagnosticSettings), tokens.service, resourceAbbreviations.virtualNetworks) - workspaceFeed: replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.workspaces), '-${stampIndex}', '') - workspaceFeedDiagnosticSetting: replace(replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.diagnosticSettings), tokens.service, '${tokens.service}-${resourceAbbreviations.workspaces}'), '-${stampIndex}', '') - workspaceFeedNetworkInterface: replace(replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.networkInterfaces), tokens.service, '${tokens.service}-${resourceAbbreviations.workspaces}'), '-${stampIndex}', '') - workspaceFeedPrivateEndpoint: replace(replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.privateEndpoints), tokens.service, '${tokens.service}-${resourceAbbreviations.workspaces}'), '-${stampIndex}', '') - workspaceGlobal: replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.workspaces), '-${stampIndex}', '') - workspaceGlobalDiagnosticSetting: replace(replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.diagnosticSettings), tokens.service, '${tokens.service}-${resourceAbbreviations.workspaces}'), '-${stampIndex}', '') - workspaceGlobalNetworkInterface: replace(replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.networkInterfaces), tokens.service, '${tokens.service}-${resourceAbbreviations.workspaces}'), '-${stampIndex}', '') - workspaceGlobalPrivateEndpoint: replace(replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.privateEndpoints), tokens.service, '${tokens.service}-${resourceAbbreviations.workspaces}'), '-${stampIndex}', '') + workspaceFeed: replace(replace(namingConvention, tokens.resource, '${resourceAbbreviations.workspaces}-feed'), '-${stampIndex}', '') + workspaceFeedDiagnosticSetting: replace(replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.diagnosticSettings), tokens.service, '${resourceAbbreviations.workspaces}-feed'), '-${stampIndex}', '') + workspaceFeedNetworkInterface: replace(replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.networkInterfaces), tokens.service, '${resourceAbbreviations.workspaces}-feed'), '-${stampIndex}', '') + workspaceFeedPrivateEndpoint: replace(replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.privateEndpoints), tokens.service, '${resourceAbbreviations.workspaces}-feed'), '-${stampIndex}', '') + workspaceGlobal: replace(replace(namingConvention, tokens.resource, '${resourceAbbreviations.workspaces}-global'), '-${stampIndex}', '') + workspaceGlobalDiagnosticSetting: replace(replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.diagnosticSettings), tokens.service, '${resourceAbbreviations.workspaces}-global'), '-${stampIndex}', '') + workspaceGlobalNetworkInterface: replace(replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.networkInterfaces), tokens.service, '${resourceAbbreviations.workspaces}-global'), '-${stampIndex}', '') + workspaceGlobalPrivateEndpoint: replace(replace(replace(namingConvention_Service, tokens.resource, resourceAbbreviations.privateEndpoints), tokens.service, '${resourceAbbreviations.workspaces}-global'), '-${stampIndex}', '') } output locations object = locations