From a05a1bb83c29cc51d87c3d0c0511088a520e690a Mon Sep 17 00:00:00 2001 From: Sven Aelterman <17446043+SvenAelterman@users.noreply.github.com> Date: Mon, 16 May 2022 15:39:26 -0700 Subject: [PATCH 01/11] Add Bicep tools to devcontainer --- .devcontainer/devcontainer.json | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index cdf6aaca26..6f7e3f5772 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -25,11 +25,11 @@ ], "remoteUser": "vscode", "containerEnv": { - "DOCKER_BUILDKIT": "1", + "DOCKER_BUILDKIT": "1" }, "remoteEnv": { // this is used for SuperLinter - "LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}", + "LOCAL_WORKSPACE_FOLDER": "${localWorkspaceFolder}" }, // Set *default* container specific settings.json values on container create. "settings": { @@ -191,7 +191,7 @@ }, "command": "pytest", "args": [ - "--ignore=e2e_tests", + "--ignore=e2e_tests" ] }, { @@ -205,7 +205,7 @@ "-m", "pytest", "-m", - "smoke", + "smoke" ] } ] @@ -223,6 +223,7 @@ "mikestead.dotenv", "humao.rest-client", "timonwong.shellcheck", + "ms-azuretools.vscode-bicep" ], "forwardPorts": [ 8000 From c26402e9095632d785d946f3fb8e2a8b2ced6f21 Mon Sep 17 00:00:00 2001 From: Sven Aelterman <17446043+SvenAelterman@users.noreply.github.com> Date: Tue, 17 May 2022 12:10:13 -0700 Subject: [PATCH 02/11] Initialcommit from Paul Yu and marrobi --- .../workspace_services/avd-aad/.dockerignore | 1 + .../workspace_services/avd-aad/.env.sample | 12 ++ .../avd-aad/Dockerfile.tmpl | 13 ++ .../workspace_services/avd-aad/README.md | 29 ++++ .../avd-aad/bicep/main.bicep | 82 +++++++++++ .../bicep/modules/applicationGroup.bicep | 16 +++ .../avd-aad/bicep/modules/hostPools.bicep | 34 +++++ .../bicep/modules/roleAssignment.bicep | 14 ++ .../avd-aad/bicep/modules/sessionHost.bicep | 136 ++++++++++++++++++ .../avd-aad/bicep/modules/workspace.bicep | 17 +++ .../avd-aad/parameters.json | 62 ++++++++ .../workspace_services/avd-aad/porter.yaml | 81 +++++++++++ .../avd-aad/template_schema.json | 49 +++++++ 13 files changed, 546 insertions(+) create mode 100644 templates/workspace_services/avd-aad/.dockerignore create mode 100644 templates/workspace_services/avd-aad/.env.sample create mode 100644 templates/workspace_services/avd-aad/Dockerfile.tmpl create mode 100644 templates/workspace_services/avd-aad/README.md create mode 100644 templates/workspace_services/avd-aad/bicep/main.bicep create mode 100644 templates/workspace_services/avd-aad/bicep/modules/applicationGroup.bicep create mode 100644 templates/workspace_services/avd-aad/bicep/modules/hostPools.bicep create mode 100644 templates/workspace_services/avd-aad/bicep/modules/roleAssignment.bicep create mode 100644 templates/workspace_services/avd-aad/bicep/modules/sessionHost.bicep create mode 100644 templates/workspace_services/avd-aad/bicep/modules/workspace.bicep create mode 100644 templates/workspace_services/avd-aad/parameters.json create mode 100644 templates/workspace_services/avd-aad/porter.yaml create mode 100644 templates/workspace_services/avd-aad/template_schema.json diff --git a/templates/workspace_services/avd-aad/.dockerignore b/templates/workspace_services/avd-aad/.dockerignore new file mode 100644 index 0000000000..cb804790e6 --- /dev/null +++ b/templates/workspace_services/avd-aad/.dockerignore @@ -0,0 +1 @@ +Dockerfile.tmpl \ No newline at end of file diff --git a/templates/workspace_services/avd-aad/.env.sample b/templates/workspace_services/avd-aad/.env.sample new file mode 100644 index 0000000000..efdcfe25b5 --- /dev/null +++ b/templates/workspace_services/avd-aad/.env.sample @@ -0,0 +1,12 @@ +WORKSPACE_ID=__CHANGE_ME__ +ID=__CHANGE_ME__ +AZURE_LOCATION=__CHANGE_ME__ +LOCAL_ADMIN_NAME="adminuser" +LOCAL_ADMIN_PASSWORD=__CHANGE_ME__ +VM_SIZE="Standard_D2s_v3" +VM_COUNT="1" +VM_LICENSE_TYPE="Windows_Client" + +ARM_CLIENT_ID=__CHANGE_ME__ +ARM_CLIENT_SECRET=__CHANGE_ME__ +ARM_TENANT_ID=__CHANGE_ME__ \ No newline at end of file diff --git a/templates/workspace_services/avd-aad/Dockerfile.tmpl b/templates/workspace_services/avd-aad/Dockerfile.tmpl new file mode 100644 index 0000000000..c1658e5170 --- /dev/null +++ b/templates/workspace_services/avd-aad/Dockerfile.tmpl @@ -0,0 +1,13 @@ +FROM debian:buster + +# PORTER_MIXINS + +ARG BUNDLE_DIR + +COPY . $BUNDLE_DIR + +WORKDIR $BUNDLE_DIR + +ENV DOTNET_SYSTEM_GLOBALIZATION_INVARIANT=1 + +RUN az bicep build --file ./bicep/main.bicep --outfile main.json diff --git a/templates/workspace_services/avd-aad/README.md b/templates/workspace_services/avd-aad/README.md new file mode 100644 index 0000000000..71f736221b --- /dev/null +++ b/templates/workspace_services/avd-aad/README.md @@ -0,0 +1,29 @@ +# azure-virtual-desktop-bicep + +AzureAD-Joined session hosts requires the following: +- Non-overlapping private IP space +- Permissions to grant users the "Virtual Machine User Login" role at the resource group level for to login + +## Resources + +- https://github.com/Azure/bicep/blob/main/docs/cicd-with-bicep.md +- https://docs.microsoft.com/en-us/azure/templates +- https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/bicep-modules +- https://docs.microsoft.com/en-us/azure/azure-resource-manager/bicep/loop-resources#resource-iteration-with-condition +- https://docs.microsoft.com/en-us/azure/templates/ +- https://docs.microsoft.com/en-us/azure/templates/microsoft.network/virtualnetworks?tabs=bicep +- https://docs.microsoft.com/en-us/azure/templates/microsoft.network/networksecuritygroups?tabs=bicep +- https://docs.microsoft.com/en-us/azure/templates/microsoft.compute/virtualmachines?tabs=bicep +- https://docs.microsoft.com/en-us/rest/api/desktopvirtualization/host-pools/create-or-update +- https://docs.microsoft.com/en-us/rest/api/desktopvirtualization/application-groups/create-or-update +- https://docs.microsoft.com/en-us/rest/api/desktopvirtualization/workspaces/create-or-update +- https://docs.microsoft.com/en-us/azure/api-management/api-management-using-with-vnet#-common-network-configuration-issues +- https://github.com/Azure/bicep-types-az/blob/main/generated/desktopvirtualization/microsoft.desktopvirtualization/2021-07-12/types.md +- https://catalogartifact.azureedge.net/publicartifacts/Microsoft.Hostpool-ARM-1.10.0/managedDisks-galleryvm.json +- https://docs.microsoft.com/en-us/cli/azure/desktopvirtualization?view=azure-cli-latest + +## Some notes + +The `Microsoft.DesktopVirtualization` namespace isn't well documented yet in https://docs.microsoft.com/en-us/azure/templates/, so I recommend you reference the REST API docs to determine which API versions you should be using https://docs.microsoft.com/en-us/rest/api/desktopvirtualization/. + +Common VM extension error messages: https://docs.microsoft.com/en-us/troubleshoot/azure/virtual-machines/error-messages diff --git a/templates/workspace_services/avd-aad/bicep/main.bicep b/templates/workspace_services/avd-aad/bicep/main.bicep new file mode 100644 index 0000000000..eda3f742cc --- /dev/null +++ b/templates/workspace_services/avd-aad/bicep/main.bicep @@ -0,0 +1,82 @@ +targetScope = 'subscription' + +param id string +param workspaceId string +param treId string +param tags object = {} +param localAdminName string = 'adminuser' +@secure() +param localAdminPassword string +param vmSize string = 'Standard_D2as_v4' +param vmCount int = 1 +param vmLicenseType string = 'Windows_Client' + +var shortWorkspaceId = substring(workspaceId, length(workspaceId) - 4, 4) +var shortServiceId = substring(id, length(id) - 4, 4) +var workspaceResourceNameSuffix = '${treId}-ws-${shortWorkspaceId}' +var serviceResourceNameSuffix = '${workspaceResourceNameSuffix}-svc-${shortServiceId}' + +resource workspaceResourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' existing = { + name: 'rg-${workspaceResourceNameSuffix}' +} + +resource workspaceVirtualNetwork 'Microsoft.Network/virtualNetworks@2019-11-01' existing = { + scope: workspaceResourceGroup + name: 'vnet-${workspaceResourceNameSuffix}' +} + +module hostPool 'modules/hostPools.bicep' = { + scope: workspaceResourceGroup + name: 'hostPoolDeploy' + params: { + name: serviceResourceNameSuffix + tags: tags + location: workspaceResourceGroup.location + hostPoolType: 'Pooled' + } +} + +module applicationGroup 'modules/applicationGroup.bicep' = { + scope: workspaceResourceGroup + name: 'applicationGroupDeploy' + params: { + name: serviceResourceNameSuffix + tags: tags + location: workspaceResourceGroup.location + hostPoolId: hostPool.outputs.id + } +} + +module workspace 'modules/workspace.bicep' = { + scope: workspaceResourceGroup + name: 'workspaceDeploy' + params: { + name: serviceResourceNameSuffix + tags: tags + location: workspaceResourceGroup.location + applicationGroupId: applicationGroup.outputs.id + } +} + +module sessionHost 'modules/sessionHost.bicep' = { + scope: workspaceResourceGroup + name: 'sessionHostDeploy' + params: { + name: serviceResourceNameSuffix + tags: tags + location: workspaceResourceGroup.location + localAdminName: localAdminName + localAdminPassword: localAdminPassword + subnetName: 'ServicesSubnet' + vmSize: vmSize + count: vmCount + licenseType: vmLicenseType + vnetId: workspaceVirtualNetwork.id + } + + dependsOn: [ + hostPool + ] +} + +output connection_uri string = 'https://rdweb.wvd.microsoft.com/arm/webclient/index.html' diff --git a/templates/workspace_services/avd-aad/bicep/modules/applicationGroup.bicep b/templates/workspace_services/avd-aad/bicep/modules/applicationGroup.bicep new file mode 100644 index 0000000000..affd3f5ed4 --- /dev/null +++ b/templates/workspace_services/avd-aad/bicep/modules/applicationGroup.bicep @@ -0,0 +1,16 @@ +param name string +param tags object +param location string +param hostPoolId string + +resource applicationGroup 'Microsoft.DesktopVirtualization/applicationGroups@2021-03-09-preview' = { + name: 'ag-${name}' + location: location + tags: tags + properties: { + applicationGroupType: 'Desktop' + hostPoolArmPath: hostPoolId + } +} + +output id string = applicationGroup.id diff --git a/templates/workspace_services/avd-aad/bicep/modules/hostPools.bicep b/templates/workspace_services/avd-aad/bicep/modules/hostPools.bicep new file mode 100644 index 0000000000..03d36452b7 --- /dev/null +++ b/templates/workspace_services/avd-aad/bicep/modules/hostPools.bicep @@ -0,0 +1,34 @@ +param name string +param tags object +param location string +@allowed([ + 'Personal' + 'Pooled' +]) +param hostPoolType string + +param baseTime string = utcNow('u') + +var expirationTime = dateTimeAdd(baseTime, 'PT48H') + +resource hostPool 'Microsoft.DesktopVirtualization/hostPools@2021-03-09-preview' = { + name: 'hp-${name}' + location: location + tags: tags + properties: { + hostPoolType: hostPoolType + loadBalancerType: 'BreadthFirst' + preferredAppGroupType: 'Desktop' + maxSessionLimit: 999999 + startVMOnConnect: false + validationEnvironment: false + customRdpProperty: 'drivestoredirect:s:*;audiomode:i:0;videoplaybackmode:i:1;redirectclipboard:i:1;redirectprinters:i:1;devicestoredirect:s:*;redirectcomports:i:1;redirectsmartcards:i:1;usbdevicestoredirect:s:*;enablecredsspsupport:i:1;use multimon:i:1;targetisaadjoined:i:1' + registrationInfo: { + expirationTime: expirationTime + token: null + registrationTokenOperation: 'Update' + } + } +} + +output id string = hostPool.id diff --git a/templates/workspace_services/avd-aad/bicep/modules/roleAssignment.bicep b/templates/workspace_services/avd-aad/bicep/modules/roleAssignment.bicep new file mode 100644 index 0000000000..a94034d513 --- /dev/null +++ b/templates/workspace_services/avd-aad/bicep/modules/roleAssignment.bicep @@ -0,0 +1,14 @@ +param name string + +resource roleAssignment 'Microsoft.Authorization/roleAssignments@2020-10-01-preview' = { + name: 'rbac${name}' + properties: { + roleDefinitionId: 'string' + principalId: 'string' + principalType: 'string' + description: 'string' + condition: 'string' + conditionVersion: 'string' + delegatedManagedIdentityResourceId: 'string' + } +} diff --git a/templates/workspace_services/avd-aad/bicep/modules/sessionHost.bicep b/templates/workspace_services/avd-aad/bicep/modules/sessionHost.bicep new file mode 100644 index 0000000000..106ca9db11 --- /dev/null +++ b/templates/workspace_services/avd-aad/bicep/modules/sessionHost.bicep @@ -0,0 +1,136 @@ +param name string +param tags object +param location string +param count int +param vnetId string +param subnetName string +param localAdminName string +@secure() +param localAdminPassword string +param vmSize string +@allowed([ + 'Windows_Client' + 'Windows_Server' +]) +param licenseType string = 'Windows_Client' +param installNVidiaGPUDriver bool = false + +// Retrieve the host pool info to pass into the module that builds session hosts. These values will be used when invoking the VM extension to install AVD agents +resource hostPoolToken 'Microsoft.DesktopVirtualization/hostPools@2021-01-14-preview' existing = { + name: 'hp-${name}' +} + +resource networkInterface 'Microsoft.Network/networkInterfaces@2019-07-01' = [for i in range(0, count): { + name: 'nic-${take(name, 10)}-${i + 1}' + location: location + tags: tags + properties: { + ipConfigurations: [ + { + name: 'ipconfig1' + properties: { + subnet: { + id: '${vnetId}/subnets/${subnetName}' + } + privateIPAllocationMethod: 'Dynamic' + } + } + ] + } +}] + +resource sessionHost 'Microsoft.Compute/virtualMachines@2019-07-01' = [for i in range(0, count): { + name: 'vm${take(name, 10)}-${i + 1}' + location: location + tags: tags + identity: { + type: 'SystemAssigned' + } + properties: { + osProfile: { + computerName: 'vm${take(name, 10)}-${i + 1}' + adminUsername: localAdminName + adminPassword: localAdminPassword + } + hardwareProfile: { + vmSize: vmSize + } + storageProfile: { + imageReference: { + publisher: 'microsoftwindowsdesktop' + offer: 'office-365' + sku: '20h2-evd-o365pp' + version: 'latest' + } + osDisk: { + createOption: 'FromImage' + } + } + licenseType: licenseType + networkProfile: { + networkInterfaces: [ + { + properties: { + primary: true + } + id: networkInterface[i].id + } + ] + } + } + + // dependsOn: [ + // networkInterface[i] + // ] +}] + +// Run this if we are Azure AD joining the session hosts +resource sessionHostAADLogin 'Microsoft.Compute/virtualMachines/extensions@2020-06-01' = [for i in range(0, count): { + name: '${sessionHost[i].name}/AADLoginForWindows' + location: location + tags: tags + properties: { + publisher: 'Microsoft.Azure.ActiveDirectory' + type: 'AADLoginForWindows' + typeHandlerVersion: '1.0' + autoUpgradeMinorVersion: true + } +}] + +resource sessionHostAVDAgent 'Microsoft.Compute/virtualMachines/extensions@2020-06-01' = [for i in range(0, count): { + name: '${sessionHost[i].name}/AddSessionHost' + location: location + tags: tags + properties: { + publisher: 'Microsoft.Powershell' + type: 'DSC' + typeHandlerVersion: '2.73' + autoUpgradeMinorVersion: true + settings: { + modulesUrl: 'https://wvdportalstorageblob.blob.core.windows.net/galleryartifacts/Configuration_8-16-2021.zip' + configurationFunction: 'Configuration.ps1\\AddSessionHost' + properties: { + hostPoolName: hostPoolToken.name + registrationInfoToken: hostPoolToken.properties.registrationInfo.token + aadJoin: true + } + } + } + + // dependsOn: [ + // sessionHostAADLogin + // ] +}] + +resource sessionHostGPUDriver 'Microsoft.Compute/virtualMachines/extensions@2020-06-01' = [for i in range(0, count): if (installNVidiaGPUDriver) { + name: '${sessionHost[i].name}/InstallNvidiaGpuDriverWindows' + location: location + tags: tags + properties: { + publisher: 'Microsoft.HpcCompute' + type: 'NvidiaGpuDriverWindows' + typeHandlerVersion: '1.3' + autoUpgradeMinorVersion: true + settings: {} + } +}] diff --git a/templates/workspace_services/avd-aad/bicep/modules/workspace.bicep b/templates/workspace_services/avd-aad/bicep/modules/workspace.bicep new file mode 100644 index 0000000000..62d2b18069 --- /dev/null +++ b/templates/workspace_services/avd-aad/bicep/modules/workspace.bicep @@ -0,0 +1,17 @@ +param name string +param tags object +param location string +param applicationGroupId string + +resource workspace 'Microsoft.DesktopVirtualization/workspaces@2021-03-09-preview' = { + name: 'ws-${name}' + tags: tags + location: location + properties: { + friendlyName: name + description: name + applicationGroupReferences: [ + applicationGroupId + ] + } +} diff --git a/templates/workspace_services/avd-aad/parameters.json b/templates/workspace_services/avd-aad/parameters.json new file mode 100644 index 0000000000..e0ab503552 --- /dev/null +++ b/templates/workspace_services/avd-aad/parameters.json @@ -0,0 +1,62 @@ +{ + "schemaVersion": "1.0.0-DRAFT+TODO", + "name": "avd-aad", + "created": "2021-06-03T11:54:54.0225968Z", + "modified": "2021-06-03T11:54:54.0225968Z", + "parameters": [ + { + "name": "id", + "source": { + "env": "ID" + } + }, + { + "name": "workspace_id", + "source": { + "env": "WORKSPACE_ID" + } + }, + { + "name": "tre_id", + "source": { + "env": "TRE_ID" + } + }, + { + "name": "azure_location", + "source": { + "env": "AZURE_LOCATION" + } + }, + { + "name": "localAdminName", + "source": { + "env": "LOCAL_ADMIN_NAME" + } + }, + { + "name": "localAdminPassword", + "source": { + "env": "LOCAL_ADMIN_PASSWORD" + } + }, + { + "name": "vmSize", + "source": { + "env": "VM_SIZE" + } + }, + { + "name": "vmCount", + "source": { + "env": "VM_COUNT" + } + }, + { + "name": "vmLicenseType", + "source": { + "env": "VM_LICENSE_TYPE" + } + } + ] +} \ No newline at end of file diff --git a/templates/workspace_services/avd-aad/porter.yaml b/templates/workspace_services/avd-aad/porter.yaml new file mode 100644 index 0000000000..d12fc1faf0 --- /dev/null +++ b/templates/workspace_services/avd-aad/porter.yaml @@ -0,0 +1,81 @@ +name: tre-service-avd-aad +version: 0.1.0 +description: "An Azure TRE service Azure Virtual Desktop" +registry: azuretre +dockerfile: Dockerfile.tmpl + +credentials: + - name: azure_tenant_id + env: ARM_TENANT_ID + - name: azure_subscription_id + env: ARM_SUBSCRIPTION_ID + - name: azure_client_id + env: ARM_CLIENT_ID + - name: azure_client_secret + env: ARM_CLIENT_SECRET + +parameters: + - name: workspace_id + type: string + - name: tre_id + type: string + - name: id + type: string + - name: azure_location + type: string + - name: localAdminName + type: string + default: adminuser + - name: localAdminPassword + type: string + - name: vmSize + type: string + default: "Standard_DS2as_v4" + - name: vmCount + type: integer + default: 1 + - name: vmLicenseType + type: string + default: "Windows_Client" + +outputs: + - name: connection_uri + type: string + applyTo: + - install + +mixins: + - exec + - az + +install: + - az: + description: "az login" + arguments: + - login + flags: + service-principal: + username: "{{ bundle.credentials.azure_client_id}}" + password: "{{ bundle.credentials.azure_client_secret}}" + tenant: "{{ bundle.credentials.azure_tenant_id}}" + - az: + description: "Deploy Azure Virtual Desktop Service" + name: "tre-service-avd-aad" + arguments: + - deployment + - sub + - create + flags: + template-file: main.json + location: "{{ bundle.parameters.azure_location }}" + name: "{{ bundle.parameters.id }}" + parameters: id={{ bundle.parameters.id }} workspaceId={{ bundle.parameters.workspace_id }} treId={{ bundle.parameters.tre_id }} localAdminName={{ bundle.parameters.localAdminName }} localAdminPassword={{ bundle.parameters.localAdminPassword }} vmSize={{ bundle.parameters.vmSize }} vmCount={{ bundle.parameters.vmCount }} vmLicenseType={{ bundle.parameters.vmLicenseType }} + outputs: + - name: connection_uri + +uninstall: + - exec: + description: "Uninstall Azure Virtual Desktop Service" + command: echo + arguments: + - "This service does not yet implement uninstall action." diff --git a/templates/workspace_services/avd-aad/template_schema.json b/templates/workspace_services/avd-aad/template_schema.json new file mode 100644 index 0000000000..64f6fe3aa9 --- /dev/null +++ b/templates/workspace_services/avd-aad/template_schema.json @@ -0,0 +1,49 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema", + "$id": "https://github.com/microsoft/AzureTRE/templates/workspace_services/avd-aad/template_schema.json", + "type": "object", + "title": "Azure Virtual Desktop", + "description": "Azure Virtual Desktop hostpool joined to Azure Active Directory", + "required": [], + "properties": { + "localAdminName": { + "type": "string", + "title": "Local Admin Name", + "description": "The name of the local administrator account to be created on the virtual machine.", + "default": "adminuser" + }, + "localAdminPassword": { + "type": "string", + "title": "Local Admin Password", + "description": "The password of the local administrator account to be created on the virtual machine." + }, + "vmSize": { + "type": "string", + "title": "Virtual Machine Size", + "description": "The size of the virtual machine to be created.", + "enum": [ + "Standard_D2as_v4", + "Standard_D4as_v4", + "Standard_D8as_v4", + "Standard_D16as_v4" + ], + "default": "Standard_D2as_v4" + }, + "vmCount": { + "type": "integer", + "title": "Number of Virtual Machines", + "description": "The number of virtual machines to be created.", + "default": 1 + }, + "vmLicenseType": { + "type": "string", + "title": "Virtual Machine License Type", + "description": "The license type of the virtual machine to be created.", + "enum": [ + "Windows_Server", + "Windows_Client" + ], + "default": "Windows_Client" + } + } +} From ca691f7041764a8d25893bd65f4bae92298446de Mon Sep 17 00:00:00 2001 From: Sven Aelterman <17446043+SvenAelterman@users.noreply.github.com> Date: Wed, 18 May 2022 10:53:54 -0700 Subject: [PATCH 03/11] Create Az FW rules for deployment --- .../workspace_services/avd-aad/porter.yaml | 64 ++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/templates/workspace_services/avd-aad/porter.yaml b/templates/workspace_services/avd-aad/porter.yaml index d12fc1faf0..db804cfd4d 100644 --- a/templates/workspace_services/avd-aad/porter.yaml +++ b/templates/workspace_services/avd-aad/porter.yaml @@ -41,12 +41,15 @@ parameters: outputs: - name: connection_uri type: string + default: "https://aka.ms/wvdarmweb" applyTo: - install mixins: - exec - - az + - az: + extensions: + - azure-firewall install: - az: @@ -58,6 +61,49 @@ install: username: "{{ bundle.credentials.azure_client_id}}" password: "{{ bundle.credentials.azure_client_secret}}" tenant: "{{ bundle.credentials.azure_tenant_id}}" + - az: + description: "Set Az FW rules for AVD - TODO: use David's solution" + arguments: + - network + - firewall + - network-rule + - create + flags: + # TODO: Use WS ID as a param + collection-name: "nrc-mrtredev-ws-0821-svc-f62b" + priority: 1000 + destination-ports: "443" + # TODO: Do not hardcode + firewall-name: "fw-mrtredev" + name: "AVDInfra" + protocols: "TCP" + action: "Allow" + description: "'Allow access to AVD infrastructure'" + # Uses Service Tag + dest-addr: "WindowsVirtualDesktop" + # TODO: Do not hardcode + source-addresses: "10.0.9.0/25" + resource-group: "rg-mrtredev" + - az: + description: "Set Az FW rules for AVD - TODO: use David's solution" + arguments: + - network + - firewall + - network-rule + - create + flags: + # TODO: Use WS ID as a param + collection-name: "nrc-mrtredev-ws-0821-svc-f62b" + destination-ports: "443" + # TODO: Do not hardcode + firewall-name: "fw-mrtredev" + name: "AVD_PS_DSC_Modules" + protocols: "TCP" + description: "'Allow access to PowerShell DSC Modules'" + # TODO: Do not hardcode + source-addresses: "10.0.9.0/25" + destination-fqdns: "'wvdportalstorageblob.blob.core.windows.net'" + resource-group: "rg-mrtredev" - az: description: "Deploy Azure Virtual Desktop Service" name: "tre-service-avd-aad" @@ -71,7 +117,23 @@ install: name: "{{ bundle.parameters.id }}" parameters: id={{ bundle.parameters.id }} workspaceId={{ bundle.parameters.workspace_id }} treId={{ bundle.parameters.tre_id }} localAdminName={{ bundle.parameters.localAdminName }} localAdminPassword={{ bundle.parameters.localAdminPassword }} vmSize={{ bundle.parameters.vmSize }} vmCount={{ bundle.parameters.vmCount }} vmLicenseType={{ bundle.parameters.vmLicenseType }} outputs: + # TODO: Porter doesn't see this - name: connection_uri + jsonPath: $.properties.outputs["connection_uri"].value + - az: + description: "Remove Az FW rule only needed during deployment" + arguments: + - network + - firewall + - network-rule + - delete + flags: + # TODO: Use WS ID as a param + collection-name: "nrc-mrtredev-ws-0821-svc-f62b" + # TODO: Do not hardcode + firewall-name: "fw-mrtredev" + name: "AVD_PS_DSC_Modules" + resource-group: "rg-mrtredev" uninstall: - exec: From ee546b51894b1c592568f57d61e847f1e8c90534 Mon Sep 17 00:00:00 2001 From: Sven Aelterman <17446043+SvenAelterman@users.noreply.github.com> Date: Wed, 18 May 2022 10:55:15 -0700 Subject: [PATCH 04/11] Update Bicep output to use shortlink for connection_uri --- templates/workspace_services/avd-aad/bicep/main.bicep | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/workspace_services/avd-aad/bicep/main.bicep b/templates/workspace_services/avd-aad/bicep/main.bicep index eda3f742cc..bb43a77d12 100644 --- a/templates/workspace_services/avd-aad/bicep/main.bicep +++ b/templates/workspace_services/avd-aad/bicep/main.bicep @@ -79,4 +79,4 @@ module sessionHost 'modules/sessionHost.bicep' = { ] } -output connection_uri string = 'https://rdweb.wvd.microsoft.com/arm/webclient/index.html' +output connection_uri string = 'https://aka.ms/wvdarmweb' From 6b4d071097921bbfcbaaaba470e4380eca7c5a14 Mon Sep 17 00:00:00 2001 From: Sven Aelterman <17446043+SvenAelterman@users.noreply.github.com> Date: Wed, 18 May 2022 10:57:48 -0700 Subject: [PATCH 05/11] Use latest AVD artifacts for session host join --- .../workspace_services/avd-aad/bicep/modules/sessionHost.bicep | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/workspace_services/avd-aad/bicep/modules/sessionHost.bicep b/templates/workspace_services/avd-aad/bicep/modules/sessionHost.bicep index 106ca9db11..c83e17615b 100644 --- a/templates/workspace_services/avd-aad/bicep/modules/sessionHost.bicep +++ b/templates/workspace_services/avd-aad/bicep/modules/sessionHost.bicep @@ -107,7 +107,7 @@ resource sessionHostAVDAgent 'Microsoft.Compute/virtualMachines/extensions@2020- typeHandlerVersion: '2.73' autoUpgradeMinorVersion: true settings: { - modulesUrl: 'https://wvdportalstorageblob.blob.core.windows.net/galleryartifacts/Configuration_8-16-2021.zip' + modulesUrl: 'https://wvdportalstorageblob.blob.core.windows.net/galleryartifacts/Configuration_02-23-2022.zip' configurationFunction: 'Configuration.ps1\\AddSessionHost' properties: { hostPoolName: hostPoolToken.name From ca2069819b052ce36591ca0c718d2d5b1d135ff8 Mon Sep 17 00:00:00 2001 From: Sven Aelterman <17446043+SvenAelterman@users.noreply.github.com> Date: Wed, 18 May 2022 15:23:57 -0700 Subject: [PATCH 06/11] Use AVD standard ARM template for session host deployment --- .../avd-aad/bicep/main.bicep | 10 +- .../avd-aad/bicep/modules/hostPools.bicep | 2 + .../avd-aad/bicep/modules/sessionHost.bicep | 236 ++++++++++-------- .../workspace_services/avd-aad/porter.yaml | 6 +- 4 files changed, 144 insertions(+), 110 deletions(-) diff --git a/templates/workspace_services/avd-aad/bicep/main.bicep b/templates/workspace_services/avd-aad/bicep/main.bicep index bb43a77d12..2e4b7ee215 100644 --- a/templates/workspace_services/avd-aad/bicep/main.bicep +++ b/templates/workspace_services/avd-aad/bicep/main.bicep @@ -9,7 +9,6 @@ param localAdminName string = 'adminuser' param localAdminPassword string param vmSize string = 'Standard_D2as_v4' param vmCount int = 1 -param vmLicenseType string = 'Windows_Client' var shortWorkspaceId = substring(workspaceId, length(workspaceId) - 4, 4) var shortServiceId = substring(id, length(id) - 4, 4) @@ -69,14 +68,11 @@ module sessionHost 'modules/sessionHost.bicep' = { localAdminPassword: localAdminPassword subnetName: 'ServicesSubnet' vmSize: vmSize - count: vmCount - licenseType: vmLicenseType + vmCount: vmCount vnetId: workspaceVirtualNetwork.id + hostPoolName: hostPool.outputs.name + hostPoolRegToken: hostPool.outputs.token } - - dependsOn: [ - hostPool - ] } output connection_uri string = 'https://aka.ms/wvdarmweb' diff --git a/templates/workspace_services/avd-aad/bicep/modules/hostPools.bicep b/templates/workspace_services/avd-aad/bicep/modules/hostPools.bicep index 03d36452b7..755d29e69d 100644 --- a/templates/workspace_services/avd-aad/bicep/modules/hostPools.bicep +++ b/templates/workspace_services/avd-aad/bicep/modules/hostPools.bicep @@ -32,3 +32,5 @@ resource hostPool 'Microsoft.DesktopVirtualization/hostPools@2021-03-09-preview' } output id string = hostPool.id +output name string = hostPool.name +output token string = string(hostPool.properties.registrationInfo.token) diff --git a/templates/workspace_services/avd-aad/bicep/modules/sessionHost.bicep b/templates/workspace_services/avd-aad/bicep/modules/sessionHost.bicep index c83e17615b..2bca0ce9fa 100644 --- a/templates/workspace_services/avd-aad/bicep/modules/sessionHost.bicep +++ b/templates/workspace_services/avd-aad/bicep/modules/sessionHost.bicep @@ -1,136 +1,172 @@ param name string param tags object param location string -param count int param vnetId string param subnetName string param localAdminName string @secure() param localAdminPassword string param vmSize string -@allowed([ - 'Windows_Client' - 'Windows_Server' -]) -param licenseType string = 'Windows_Client' -param installNVidiaGPUDriver bool = false +param hostPoolRegToken string -// Retrieve the host pool info to pass into the module that builds session hosts. These values will be used when invoking the VM extension to install AVD agents -resource hostPoolToken 'Microsoft.DesktopVirtualization/hostPools@2021-01-14-preview' existing = { - name: 'hp-${name}' -} +param intuneEnroll bool = false +param hostPoolName string +param vmCount int = 1 + +// All N-series except for NV_v4 use Nvidia +var installNVidiaGPUDriver = (startsWith(vmSize, 'Standard_N') && !(endsWith(vmSize, '_v4'))) ? true : false +// NV_v4 uses AMD +var installAmdGPUDriver = (startsWith(vmSize, 'Standard_NV') && endsWith(vmSize, '_v4')) ? true : false + +param deploymentNameStructure string = 'AVD' + +// Use the same VM templates as used by the Add VM to hostpool process +var nestedTemplatesLocation = 'https://wvdportalstorageblob.blob.core.windows.net/galleryartifacts/armtemplates/Hostpool_12-9-2021/nestedTemplates/' +var vmTemplateUri = '${nestedTemplatesLocation}managedDisks-galleryvm.json' -resource networkInterface 'Microsoft.Network/networkInterfaces@2019-07-01' = [for i in range(0, count): { - name: 'nic-${take(name, 10)}-${i + 1}' +var rdshPrefix = 'vm-${take(name, 10)}-' +var subnetId = '${vnetId}/subnets/${subnetName}' + +resource availabilitySet 'Microsoft.Compute/availabilitySets@2021-11-01' = { + name: 'avail-${name}' location: location - tags: tags properties: { - ipConfigurations: [ - { - name: 'ipconfig1' - properties: { - subnet: { - id: '${vnetId}/subnets/${subnetName}' - } - privateIPAllocationMethod: 'Dynamic' - } - } - ] + platformFaultDomainCount: 2 + platformUpdateDomainCount: 5 } -}] - -resource sessionHost 'Microsoft.Compute/virtualMachines@2019-07-01' = [for i in range(0, count): { - name: 'vm${take(name, 10)}-${i + 1}' - location: location - tags: tags - identity: { - type: 'SystemAssigned' + sku: { + name: 'Aligned' } + tags: tags +} + +// Deploy the session host VMs just like the Add VM to hostpool process would +resource vmDeployment 'Microsoft.Resources/deployments@2021-04-01' = { + name: replace(deploymentNameStructure, '{rtype}', 'avdvm') properties: { - osProfile: { - computerName: 'vm${take(name, 10)}-${i + 1}' - adminUsername: localAdminName - adminPassword: localAdminPassword - } - hardwareProfile: { - vmSize: vmSize + mode: 'Incremental' + templateLink: { + uri: vmTemplateUri + contentVersion: '1.0.0.0' } - storageProfile: { - imageReference: { - publisher: 'microsoftwindowsdesktop' - offer: 'office-365' - sku: '20h2-evd-o365pp' - version: 'latest' + parameters: { + artifactsLocation: { + value: 'https://wvdportalstorageblob.blob.core.windows.net/galleryartifacts/Configuration_02-23-2022.zip' } - osDisk: { - createOption: 'FromImage' + availabilityOption: { + value: 'AvailabilitySet' + } + availabilitySetName: { + value: availabilitySet.name + } + vmGalleryImageOffer: { + value: 'office-365' + } + vmGalleryImagePublisher: { + value: 'microsoftwindowsdesktop' + } + vmGalleryImageHasPlan: { + value: false + } + vmGalleryImageSKU: { + value: 'win11-21h2-avd-m365' + } + rdshPrefix: { + value: rdshPrefix + } + rdshNumberOfInstances: { + value: vmCount + } + rdshVMDiskType: { + value: 'StandardSSD_LRS' + } + rdshVmSize: { + value: vmSize + } + enableAcceleratedNetworking: { + value: true + } + vmAdministratorAccountUsername: { + value: localAdminName + } + vmAdministratorAccountPassword: { + value: localAdminPassword + } + // These values are required but unused for AAD join + administratorAccountUsername: { + value: '' + } + administratorAccountPassword: { + value: '' + } + // End required but unused for AAD join + 'subnet-id': { + value: subnetId + } + vhds: { + value: 'vhds/${rdshPrefix}' + } + location: { + value: location + } + createNetworkSecurityGroup: { + value: false + } + vmInitialNumber: { + value: 0 + } + hostpoolName: { + value: hostPoolName + } + hostpoolToken: { + value: hostPoolRegToken + } + aadJoin: { + value: true + } + intune: { + // In the CSE TRE DEMO tenant, Intune does not appear to be config'd + value: intuneEnroll + } + securityType: { + value: 'TrustedLaunch' + } + secureBoot: { + value: true + } + vTPM: { + value: true + } + vmImageVhdUri: { + value: '' } - } - licenseType: licenseType - networkProfile: { - networkInterfaces: [ - { - properties: { - primary: true - } - id: networkInterface[i].id - } - ] } } +} - // dependsOn: [ - // networkInterface[i] - // ] -}] - -// Run this if we are Azure AD joining the session hosts -resource sessionHostAADLogin 'Microsoft.Compute/virtualMachines/extensions@2020-06-01' = [for i in range(0, count): { - name: '${sessionHost[i].name}/AADLoginForWindows' - location: location - tags: tags - properties: { - publisher: 'Microsoft.Azure.ActiveDirectory' - type: 'AADLoginForWindows' - typeHandlerVersion: '1.0' - autoUpgradeMinorVersion: true - } -}] - -resource sessionHostAVDAgent 'Microsoft.Compute/virtualMachines/extensions@2020-06-01' = [for i in range(0, count): { - name: '${sessionHost[i].name}/AddSessionHost' +resource sessionHostGPUDriver 'Microsoft.Compute/virtualMachines/extensions@2020-06-01' = [for i in range(0, vmCount): if (installNVidiaGPUDriver) { + name: '${rdshPrefix}${i}/InstallNvidiaGpuDriverWindows' location: location tags: tags properties: { - publisher: 'Microsoft.Powershell' - type: 'DSC' - typeHandlerVersion: '2.73' + publisher: 'Microsoft.HpcCompute' + type: 'NvidiaGpuDriverWindows' + typeHandlerVersion: '1.3' autoUpgradeMinorVersion: true - settings: { - modulesUrl: 'https://wvdportalstorageblob.blob.core.windows.net/galleryartifacts/Configuration_02-23-2022.zip' - configurationFunction: 'Configuration.ps1\\AddSessionHost' - properties: { - hostPoolName: hostPoolToken.name - registrationInfoToken: hostPoolToken.properties.registrationInfo.token - aadJoin: true - } - } } - - // dependsOn: [ - // sessionHostAADLogin - // ] + dependsOn: [ + vmDeployment + ] }] -resource sessionHostGPUDriver 'Microsoft.Compute/virtualMachines/extensions@2020-06-01' = [for i in range(0, count): if (installNVidiaGPUDriver) { - name: '${sessionHost[i].name}/InstallNvidiaGpuDriverWindows' +resource sessionHostAMDGPUDriver 'Microsoft.Compute/virtualMachines/extensions@2021-11-01' = [for i in range(0, vmCount): if (installAmdGPUDriver) { + name: '${rdshPrefix}${i}/AmdGpuDriverWindows' location: location tags: tags properties: { publisher: 'Microsoft.HpcCompute' - type: 'NvidiaGpuDriverWindows' - typeHandlerVersion: '1.3' + type: 'AmdGpuDriverWindows' + typeHandlerVersion: '1.0' autoUpgradeMinorVersion: true - settings: {} } }] diff --git a/templates/workspace_services/avd-aad/porter.yaml b/templates/workspace_services/avd-aad/porter.yaml index db804cfd4d..0fdf9973e8 100644 --- a/templates/workspace_services/avd-aad/porter.yaml +++ b/templates/workspace_services/avd-aad/porter.yaml @@ -115,11 +115,11 @@ install: template-file: main.json location: "{{ bundle.parameters.azure_location }}" name: "{{ bundle.parameters.id }}" - parameters: id={{ bundle.parameters.id }} workspaceId={{ bundle.parameters.workspace_id }} treId={{ bundle.parameters.tre_id }} localAdminName={{ bundle.parameters.localAdminName }} localAdminPassword={{ bundle.parameters.localAdminPassword }} vmSize={{ bundle.parameters.vmSize }} vmCount={{ bundle.parameters.vmCount }} vmLicenseType={{ bundle.parameters.vmLicenseType }} + parameters: id={{ bundle.parameters.id }} workspaceId={{ bundle.parameters.workspace_id }} treId={{ bundle.parameters.tre_id }} localAdminName={{ bundle.parameters.localAdminName }} localAdminPassword={{ bundle.parameters.localAdminPassword }} vmSize={{ bundle.parameters.vmSize }} vmCount={{ bundle.parameters.vmCount }} outputs: # TODO: Porter doesn't see this - name: connection_uri - jsonPath: $.properties.outputs["connection_uri"].value + jsonPath: "$.properties.outputs['connection_uri'].value" - az: description: "Remove Az FW rule only needed during deployment" arguments: @@ -140,4 +140,4 @@ uninstall: description: "Uninstall Azure Virtual Desktop Service" command: echo arguments: - - "This service does not yet implement uninstall action." + - "This service does not yet implement the uninstall action." From 5b95ca04c156bdc0d8296d7bd31520eb28a14da7 Mon Sep 17 00:00:00 2001 From: Sven Aelterman <17446043+SvenAelterman@users.noreply.github.com> Date: Wed, 18 May 2022 15:36:49 -0700 Subject: [PATCH 07/11] Suppress linter warning on required URL --- .../avd-aad/bicep/bicepconfig.json | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 templates/workspace_services/avd-aad/bicep/bicepconfig.json diff --git a/templates/workspace_services/avd-aad/bicep/bicepconfig.json b/templates/workspace_services/avd-aad/bicep/bicepconfig.json new file mode 100644 index 0000000000..93bb8bb405 --- /dev/null +++ b/templates/workspace_services/avd-aad/bicep/bicepconfig.json @@ -0,0 +1,17 @@ +{ + "analyzers": { + "core": { + "verbose": false, + "enabled": true, + "rules": { + "no-hardcoded-env-urls": { + "level": "warning", + "excludedhosts": [ + "schema.management.azure.com", + "wvdportalstorageblob.blob.core.windows.net" + ] + } + } + } + } +} From 56316e260923a4279c9177fbe36bbe691edf32b8 Mon Sep 17 00:00:00 2001 From: Sven Aelterman <17446043+SvenAelterman@users.noreply.github.com> Date: Wed, 18 May 2022 16:06:10 -0700 Subject: [PATCH 08/11] Create unique ARM deployment names --- .../workspace_services/avd-aad/bicep/main.bicep | 13 +++++++++---- .../avd-aad/bicep/modules/sessionHost.bicep | 7 +++---- templates/workspace_services/avd-aad/porter.yaml | 2 +- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/templates/workspace_services/avd-aad/bicep/main.bicep b/templates/workspace_services/avd-aad/bicep/main.bicep index 2e4b7ee215..343e765fde 100644 --- a/templates/workspace_services/avd-aad/bicep/main.bicep +++ b/templates/workspace_services/avd-aad/bicep/main.bicep @@ -8,13 +8,17 @@ param localAdminName string = 'adminuser' @secure() param localAdminPassword string param vmSize string = 'Standard_D2as_v4' + param vmCount int = 1 +param deploymentTime string = utcNow() var shortWorkspaceId = substring(workspaceId, length(workspaceId) - 4, 4) var shortServiceId = substring(id, length(id) - 4, 4) var workspaceResourceNameSuffix = '${treId}-ws-${shortWorkspaceId}' var serviceResourceNameSuffix = '${workspaceResourceNameSuffix}-svc-${shortServiceId}' +var deploymentNamePrefix = '${serviceResourceNameSuffix}-{rtype}-${deploymentTime}' + resource workspaceResourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' existing = { name: 'rg-${workspaceResourceNameSuffix}' } @@ -26,7 +30,7 @@ resource workspaceVirtualNetwork 'Microsoft.Network/virtualNetworks@2019-11-01' module hostPool 'modules/hostPools.bicep' = { scope: workspaceResourceGroup - name: 'hostPoolDeploy' + name: replace(deploymentNamePrefix, '{rtype}', 'AVD-HostPool') params: { name: serviceResourceNameSuffix tags: tags @@ -37,7 +41,7 @@ module hostPool 'modules/hostPools.bicep' = { module applicationGroup 'modules/applicationGroup.bicep' = { scope: workspaceResourceGroup - name: 'applicationGroupDeploy' + name: replace(deploymentNamePrefix, '{rtype}', 'AVD-ApplicationGroup') params: { name: serviceResourceNameSuffix tags: tags @@ -48,7 +52,7 @@ module applicationGroup 'modules/applicationGroup.bicep' = { module workspace 'modules/workspace.bicep' = { scope: workspaceResourceGroup - name: 'workspaceDeploy' + name: replace(deploymentNamePrefix, '{rtype}', 'AVD-Workspace') params: { name: serviceResourceNameSuffix tags: tags @@ -59,7 +63,7 @@ module workspace 'modules/workspace.bicep' = { module sessionHost 'modules/sessionHost.bicep' = { scope: workspaceResourceGroup - name: 'sessionHostDeploy' + name: replace(deploymentNamePrefix, '{rtype}', 'AVD-SessionHosts') params: { name: serviceResourceNameSuffix tags: tags @@ -72,6 +76,7 @@ module sessionHost 'modules/sessionHost.bicep' = { vnetId: workspaceVirtualNetwork.id hostPoolName: hostPool.outputs.name hostPoolRegToken: hostPool.outputs.token + deploymentNameStructure: deploymentNamePrefix } } diff --git a/templates/workspace_services/avd-aad/bicep/modules/sessionHost.bicep b/templates/workspace_services/avd-aad/bicep/modules/sessionHost.bicep index 2bca0ce9fa..fe4b58dad6 100644 --- a/templates/workspace_services/avd-aad/bicep/modules/sessionHost.bicep +++ b/templates/workspace_services/avd-aad/bicep/modules/sessionHost.bicep @@ -9,6 +9,7 @@ param localAdminPassword string param vmSize string param hostPoolRegToken string +param deploymentNameStructure string = '${name}-${utcNow()}' param intuneEnroll bool = false param hostPoolName string param vmCount int = 1 @@ -18,9 +19,7 @@ var installNVidiaGPUDriver = (startsWith(vmSize, 'Standard_N') && !(endsWith(vmS // NV_v4 uses AMD var installAmdGPUDriver = (startsWith(vmSize, 'Standard_NV') && endsWith(vmSize, '_v4')) ? true : false -param deploymentNameStructure string = 'AVD' - -// Use the same VM templates as used by the Add VM to hostpool process +// Use the same VM templates as used by the Add VM to hostpool Portal var nestedTemplatesLocation = 'https://wvdportalstorageblob.blob.core.windows.net/galleryartifacts/armtemplates/Hostpool_12-9-2021/nestedTemplates/' var vmTemplateUri = '${nestedTemplatesLocation}managedDisks-galleryvm.json' @@ -42,7 +41,7 @@ resource availabilitySet 'Microsoft.Compute/availabilitySets@2021-11-01' = { // Deploy the session host VMs just like the Add VM to hostpool process would resource vmDeployment 'Microsoft.Resources/deployments@2021-04-01' = { - name: replace(deploymentNameStructure, '{rtype}', 'avdvm') + name: replace(deploymentNameStructure, '{rtype}', 'AVD-VMs') properties: { mode: 'Incremental' templateLink: { diff --git a/templates/workspace_services/avd-aad/porter.yaml b/templates/workspace_services/avd-aad/porter.yaml index 0fdf9973e8..bb7fb8640f 100644 --- a/templates/workspace_services/avd-aad/porter.yaml +++ b/templates/workspace_services/avd-aad/porter.yaml @@ -1,5 +1,5 @@ name: tre-service-avd-aad -version: 0.1.0 +version: 0.3.0 description: "An Azure TRE service Azure Virtual Desktop" registry: azuretre dockerfile: Dockerfile.tmpl From c13aacc889c5f46fc009443875d8ad4a2e314dfb Mon Sep 17 00:00:00 2001 From: Sven Aelterman <17446043+SvenAelterman@users.noreply.github.com> Date: Wed, 18 May 2022 16:24:04 -0700 Subject: [PATCH 09/11] Remove naked URLs in README.md (linting) --- .../workspace_services/avd-aad/README.md | 32 +++++++++---------- 1 file changed, 15 insertions(+), 17 deletions(-) diff --git a/templates/workspace_services/avd-aad/README.md b/templates/workspace_services/avd-aad/README.md index 71f736221b..1d51c43bdb 100644 --- a/templates/workspace_services/avd-aad/README.md +++ b/templates/workspace_services/avd-aad/README.md @@ -6,24 +6,22 @@ AzureAD-Joined session hosts requires the following: ## Resources -- https://github.com/Azure/bicep/blob/main/docs/cicd-with-bicep.md -- https://docs.microsoft.com/en-us/azure/templates -- https://docs.microsoft.com/en-us/azure/azure-resource-manager/templates/bicep-modules -- https://docs.microsoft.com/en-us/azure/azure-resource-manager/bicep/loop-resources#resource-iteration-with-condition -- https://docs.microsoft.com/en-us/azure/templates/ -- https://docs.microsoft.com/en-us/azure/templates/microsoft.network/virtualnetworks?tabs=bicep -- https://docs.microsoft.com/en-us/azure/templates/microsoft.network/networksecuritygroups?tabs=bicep -- https://docs.microsoft.com/en-us/azure/templates/microsoft.compute/virtualmachines?tabs=bicep -- https://docs.microsoft.com/en-us/rest/api/desktopvirtualization/host-pools/create-or-update -- https://docs.microsoft.com/en-us/rest/api/desktopvirtualization/application-groups/create-or-update -- https://docs.microsoft.com/en-us/rest/api/desktopvirtualization/workspaces/create-or-update -- https://docs.microsoft.com/en-us/azure/api-management/api-management-using-with-vnet#-common-network-configuration-issues -- https://github.com/Azure/bicep-types-az/blob/main/generated/desktopvirtualization/microsoft.desktopvirtualization/2021-07-12/types.md -- https://catalogartifact.azureedge.net/publicartifacts/Microsoft.Hostpool-ARM-1.10.0/managedDisks-galleryvm.json -- https://docs.microsoft.com/en-us/cli/azure/desktopvirtualization?view=azure-cli-latest +### Learning Bicep + +- [Define resources with Bicep and ARM templates](https://docs.microsoft.com/azure/templates) +- [Bicep modules](https://docs.microsoft.com/azure/azure-resource-manager/templates/bicep-modules) +- [Iterative loops in Bicep](https://docs.microsoft.com/azure/azure-resource-manager/bicep/loop-resources#resource-iteration-with-condition) + +### Specific Azure Resource Definitions in Bicep + +- [Bicep reference: Microsoft.DesktopVirtualization/hostPools](https://docs.microsoft.com/azure/templates/microsoft.desktopvirtualization/hostpools?tabs=bicep) +- [Bicep reference: Microsoft.DesktopVirtualization/applicationGroups](https://docs.microsoft.com/azure/templates/microsoft.desktopvirtualization/applicationgroups?tabs=bicep) +- [Bicep reference: Microsoft.DesktopVirtualization/workspaces](https://docs.microsoft.com/azure/templates/microsoft.desktopvirtualization/workspaces?tabs=bicep) +- [ARM reference: Microsoft.DesktopVirtualization@2021-07-12](https://github.com/Azure/bicep-types-az/blob/main/generated/desktopvirtualization/microsoft.desktopvirtualization/2021-07-12/types.md) +- [ARM template for VM creation](https://wvdportalstorageblob.blob.core.windows.net/galleryartifacts/armtemplates/Hostpool_12-9-2021/nestedTemplates/managedDisks-galleryvm.json) ## Some notes -The `Microsoft.DesktopVirtualization` namespace isn't well documented yet in https://docs.microsoft.com/en-us/azure/templates/, so I recommend you reference the REST API docs to determine which API versions you should be using https://docs.microsoft.com/en-us/rest/api/desktopvirtualization/. +The `Microsoft.DesktopVirtualization` namespace isn't well documented yet, so I recommend you reference the [REST API docs](https://docs.microsoft.com/rest/api/desktopvirtualization/) to determine which API versions you should be using. -Common VM extension error messages: https://docs.microsoft.com/en-us/troubleshoot/azure/virtual-machines/error-messages +To research common VM extension error messages, see [Understand common error messages when you manage virtual machines in Azure](https://docs.microsoft.com/troubleshoot/azure/virtual-machines/error-messages). From d6b41636eb3d869a5c7d3a4c22ee7613697011d9 Mon Sep 17 00:00:00 2001 From: Sven Aelterman <17446043+SvenAelterman@users.noreply.github.com> Date: Wed, 18 May 2022 16:47:45 -0700 Subject: [PATCH 10/11] Configure RDP properties for TRE --- .../workspace_services/avd-aad/bicep/modules/hostPools.bicep | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/workspace_services/avd-aad/bicep/modules/hostPools.bicep b/templates/workspace_services/avd-aad/bicep/modules/hostPools.bicep index 755d29e69d..d44b389443 100644 --- a/templates/workspace_services/avd-aad/bicep/modules/hostPools.bicep +++ b/templates/workspace_services/avd-aad/bicep/modules/hostPools.bicep @@ -22,7 +22,7 @@ resource hostPool 'Microsoft.DesktopVirtualization/hostPools@2021-03-09-preview' maxSessionLimit: 999999 startVMOnConnect: false validationEnvironment: false - customRdpProperty: 'drivestoredirect:s:*;audiomode:i:0;videoplaybackmode:i:1;redirectclipboard:i:1;redirectprinters:i:1;devicestoredirect:s:*;redirectcomports:i:1;redirectsmartcards:i:1;usbdevicestoredirect:s:*;enablecredsspsupport:i:1;use multimon:i:1;targetisaadjoined:i:1' + customRdpProperty: 'drivestoredirect:s:0;audiomode:i:0;videoplaybackmode:i:1;redirectclipboard:i:0;redirectprinters:i:0;devicestoredirect:s:0;redirectcomports:i:0;redirectsmartcards:i:1;usbdevicestoredirect:s:0;enablecredsspsupport:i:1;use multimon:i:1;targetisaadjoined:i:1' registrationInfo: { expirationTime: expirationTime token: null From 84e0a4a3dea7d3e3f64959c6474b3dfb54144deb Mon Sep 17 00:00:00 2001 From: Sven Aelterman <17446043+SvenAelterman@users.noreply.github.com> Date: Wed, 18 May 2022 18:40:31 -0700 Subject: [PATCH 11/11] Remove localAdminPassword parameter --- .../workspace_services/avd-aad/.env.sample | 3 +- .../avd-aad/bicep/main.bicep | 40 +++++++++++++++++-- .../bicep/modules/keyVaultSecret.bicep | 11 +++++ .../avd-aad/parameters.json | 8 +--- .../workspace_services/avd-aad/porter.yaml | 6 +-- .../avd-aad/template_schema.json | 5 --- 6 files changed, 52 insertions(+), 21 deletions(-) create mode 100644 templates/workspace_services/avd-aad/bicep/modules/keyVaultSecret.bicep diff --git a/templates/workspace_services/avd-aad/.env.sample b/templates/workspace_services/avd-aad/.env.sample index efdcfe25b5..c10799a6d1 100644 --- a/templates/workspace_services/avd-aad/.env.sample +++ b/templates/workspace_services/avd-aad/.env.sample @@ -2,11 +2,10 @@ WORKSPACE_ID=__CHANGE_ME__ ID=__CHANGE_ME__ AZURE_LOCATION=__CHANGE_ME__ LOCAL_ADMIN_NAME="adminuser" -LOCAL_ADMIN_PASSWORD=__CHANGE_ME__ VM_SIZE="Standard_D2s_v3" VM_COUNT="1" VM_LICENSE_TYPE="Windows_Client" ARM_CLIENT_ID=__CHANGE_ME__ ARM_CLIENT_SECRET=__CHANGE_ME__ -ARM_TENANT_ID=__CHANGE_ME__ \ No newline at end of file +ARM_TENANT_ID=__CHANGE_ME__ diff --git a/templates/workspace_services/avd-aad/bicep/main.bicep b/templates/workspace_services/avd-aad/bicep/main.bicep index 343e765fde..361dcd966f 100644 --- a/templates/workspace_services/avd-aad/bicep/main.bicep +++ b/templates/workspace_services/avd-aad/bicep/main.bicep @@ -5,12 +5,12 @@ param workspaceId string param treId string param tags object = {} param localAdminName string = 'adminuser' -@secure() -param localAdminPassword string param vmSize string = 'Standard_D2as_v4' param vmCount int = 1 param deploymentTime string = utcNow() +@secure() +param passwordSeed string = newGuid() var shortWorkspaceId = substring(workspaceId, length(workspaceId) - 4, 4) var shortServiceId = substring(id, length(id) - 4, 4) @@ -23,6 +23,40 @@ resource workspaceResourceGroup 'Microsoft.Resources/resourceGroups@2021-04-01' name: 'rg-${workspaceResourceNameSuffix}' } +resource workspaceKeyVault 'Microsoft.KeyVault/vaults@2021-11-01-preview' existing = { + name: 'kv-${workspaceResourceNameSuffix}' + scope: workspaceResourceGroup +} + +// Doubling up the unique string with the same seed does not increase password entropy, +// but it guarantees that there will be at least three character classes present in the password +// to meet operating system password complexity requirements +// This could be enhanced by specifying a second, different seed GUID +var localAdminPasswordGenerated = '${uniqueString(passwordSeed)}_${toUpper(uniqueString(passwordSeed))}' + +var secrets = [ + { + secretValue: passwordSeed + secretName: '${shortServiceId}-${deploymentTime}-localadminpwdseed' + } + { + // Generate a new password for the required local VM admin + secretValue: localAdminPasswordGenerated + secretName: '${shortServiceId}-${deploymentTime}-localadminpwd' + } +] + +// Persist the new password in the workspace's Key Vault +module keyVaultSecrets 'modules/keyVaultSecret.bicep' = [for (secret, i) in secrets: { + scope: workspaceResourceGroup + name: '${replace(deploymentNamePrefix, '{rtype}', 'Secret')}-${i}' + params: { + workspaceKeyVaultName: workspaceKeyVault.name + secretValue: secrets[i].secretValue + secretName: secrets[i].secretName + } +}] + resource workspaceVirtualNetwork 'Microsoft.Network/virtualNetworks@2019-11-01' existing = { scope: workspaceResourceGroup name: 'vnet-${workspaceResourceNameSuffix}' @@ -69,7 +103,7 @@ module sessionHost 'modules/sessionHost.bicep' = { tags: tags location: workspaceResourceGroup.location localAdminName: localAdminName - localAdminPassword: localAdminPassword + localAdminPassword: localAdminPasswordGenerated subnetName: 'ServicesSubnet' vmSize: vmSize vmCount: vmCount diff --git a/templates/workspace_services/avd-aad/bicep/modules/keyVaultSecret.bicep b/templates/workspace_services/avd-aad/bicep/modules/keyVaultSecret.bicep new file mode 100644 index 0000000000..1d35149291 --- /dev/null +++ b/templates/workspace_services/avd-aad/bicep/modules/keyVaultSecret.bicep @@ -0,0 +1,11 @@ +param workspaceKeyVaultName string +param secretName string +@secure() +param secretValue string + +resource localAdminPasswordSecret 'Microsoft.KeyVault/vaults/secrets@2021-11-01-preview' = { + name: '${workspaceKeyVaultName}/${secretName}' + properties: { + value: secretValue + } +} diff --git a/templates/workspace_services/avd-aad/parameters.json b/templates/workspace_services/avd-aad/parameters.json index e0ab503552..db94d670c0 100644 --- a/templates/workspace_services/avd-aad/parameters.json +++ b/templates/workspace_services/avd-aad/parameters.json @@ -34,12 +34,6 @@ "env": "LOCAL_ADMIN_NAME" } }, - { - "name": "localAdminPassword", - "source": { - "env": "LOCAL_ADMIN_PASSWORD" - } - }, { "name": "vmSize", "source": { @@ -59,4 +53,4 @@ } } ] -} \ No newline at end of file +} diff --git a/templates/workspace_services/avd-aad/porter.yaml b/templates/workspace_services/avd-aad/porter.yaml index bb7fb8640f..17ca232e50 100644 --- a/templates/workspace_services/avd-aad/porter.yaml +++ b/templates/workspace_services/avd-aad/porter.yaml @@ -1,5 +1,5 @@ name: tre-service-avd-aad -version: 0.3.0 +version: 0.4.0 description: "An Azure TRE service Azure Virtual Desktop" registry: azuretre dockerfile: Dockerfile.tmpl @@ -26,8 +26,6 @@ parameters: - name: localAdminName type: string default: adminuser - - name: localAdminPassword - type: string - name: vmSize type: string default: "Standard_DS2as_v4" @@ -115,7 +113,7 @@ install: template-file: main.json location: "{{ bundle.parameters.azure_location }}" name: "{{ bundle.parameters.id }}" - parameters: id={{ bundle.parameters.id }} workspaceId={{ bundle.parameters.workspace_id }} treId={{ bundle.parameters.tre_id }} localAdminName={{ bundle.parameters.localAdminName }} localAdminPassword={{ bundle.parameters.localAdminPassword }} vmSize={{ bundle.parameters.vmSize }} vmCount={{ bundle.parameters.vmCount }} + parameters: id={{ bundle.parameters.id }} workspaceId={{ bundle.parameters.workspace_id }} treId={{ bundle.parameters.tre_id }} localAdminName={{ bundle.parameters.localAdminName }} vmSize={{ bundle.parameters.vmSize }} vmCount={{ bundle.parameters.vmCount }} outputs: # TODO: Porter doesn't see this - name: connection_uri diff --git a/templates/workspace_services/avd-aad/template_schema.json b/templates/workspace_services/avd-aad/template_schema.json index 64f6fe3aa9..a8ce71553f 100644 --- a/templates/workspace_services/avd-aad/template_schema.json +++ b/templates/workspace_services/avd-aad/template_schema.json @@ -12,11 +12,6 @@ "description": "The name of the local administrator account to be created on the virtual machine.", "default": "adminuser" }, - "localAdminPassword": { - "type": "string", - "title": "Local Admin Password", - "description": "The password of the local administrator account to be created on the virtual machine." - }, "vmSize": { "type": "string", "title": "Virtual Machine Size",