You are on page 1of 19
7128722, 906 PM ARM AVD with Terraform - Microsoft Tech Community ARM AVD with Terraform O EmilyMclaren icnoso=T ARM AVD with Terraform % Deploying Azure Virtual Desktop with Terraform This article has been written in collaboration with my colleagues @Jensheerin , @Stefan Georgiev and Julie NG Terraform is a tool that enables you to completely automate infrastructure builds through configuration files, It provides versioning for configurations, which makes it easy to deploy and maintain your existing Azure Virtual Desktop deployments on Microsoft Azure. This article provides an overview of how to use Terraform to deploy a simple Azure Virtual Desktop environment. This is to deploy ARM AVD, not AVD Classic. There are several pre-requisites required to deploy Azure Virtual Desktop, which we will assume are already in place. 1, Ensure that you meet the requirements for Azure Virtual Desktop 2. Terraform must be installed and configured as outlined here. Ifyou are completely new to Azure Virtual Desktop, please check them out here: What is Azure Virtual Deskto} Zure | Microsoft Docs There are several topics that should be considered when creating a production Azure Virtual Desktop environment, that we haven't been able to include in the scope of this article, such as security, monitoring, BCDR and image build. This article aims to get you started with building a PoC for Azure Virtual Desktop via Terraform. htpsstechcommunity merosof.comnSiazure-vitual-desktoplarm-avd-with raformim-p2699806 19 7128722, 906 PM ARM AVD with Terraform - Microsoft Tech Community All the code in this article can be found in the repo: RDS-TemplatesAwvd-sh/terraform-azurerm-azuresvirtualdesktop at master - Azure/RDS- ‘Templates (github. Note: Terraform is an open source tool hosted in GitHub. As such, it is published *as is" with no implied support from Microsoft or any other organization. However, we would like to welcome you to open issues using GitHub issues to collaborate toward future improvements to the tool. AVD Components To deploy AVD we will need to understand what components are required. We're assuming that your pre-requisites are already in place. ‘© Active Directory - in this worked example, we are using ‘on-prem’ AD running on DCs in a separate VNet. The code could easily be modified to use AADDS though. ‘Users in AAD that will be given access to AVD ‘* AVM Image (or you can use a marketplace image) Components we will deploy in this article * Virtual Desktop Environment Networking Infrastructure Session Hosts Profile Storage Role Based Access Control Our architecture should look like the below once completed (sections in white are pre-reqs, grey will be deployed) Setting up Terraform You'll need to authenticate to Azure to run the templates ~ the steps to do that are here: If you want to use Visual Studio Code please have a look at this article. hitpsstechcommunity merosof.comnSiazure-vitual-dosktoparm-avd-with raformim-p2699806 ane 776122, 908 PM [ARM AVO wit Trraform - Mirosft Tech Community Once your environment is ready, we can start to understand how to deploy all of the required resources. 1. AVD environment First up we will deploy the environment for Azure Virtual Desktop In this section we will deploy the following resources: * Resource Group + Workspace * Hostpool ‘* Hostpool registration expiration date (create a time_rotating resource) ‘* Application Group (our DAG) * Application Group association to workspace Before we create templates for the resources we need to configure the Azure Provider. To do this we will create a providers.tf file and add the following: terraform { required providers { azurerm = { source = "hashicorp/azurerm" version = ""~>2.0" } } } provider “azurerm" { features {} } The full code for provider.tf can be found here. hitps:techcommunity merosof.comSfazuresvitual-desktoplarm-avd-wth-eraformin- (2639808 ane 7128722, 906 PM ARM AVD with Terraform - Microsoft Tech Community The Terraform documentation for AVD is here: azurerm virtual desktop workspace | Resources | hashicorp/azurerm | Terraform Registry Then to create the resources, first create main.tf and start adding resources in the format: resource “azurerm_virtual_desktop workspace" “workspace” { name = var.workspace location = vardeploy location resource_group_name = azurerm_resource_group.rg.name friendly name = "${var.prefix} Workspace" description = "S${var.prefix} Workspace" } Note that there are several dependencies in the order that resources are created. We can specify those using the following dependson = [azurerm_virtual_desktop_host_pool.hostpool,azurerm_virtual_desktop_workspace.workspace] In this case we are specifying the dependency for provisioning the Desktop Application group ~ that the hostpool and workspace must already exist before we try to create this resource. We have also referenced some variables in here, so let's create a variables.tf file for those now and add our variables. They will be in the following form: variable "deploy location” { type = string default = "West Europe” description = "location" } We will also need to add variables for Resource group name hitps:techcommunity merosof.comSfazuresvitual-desktoplarm-avd-wth-eraformin- (2639808 ane 776122, 908 PM [ARM AVO wit Trraform - Mirosft Tech Community ‘Prefix (this will be appended to resources such as session hosts) '* Host pool name A full list of the variables that are referenced are listed at the end of the article in step 7. We can deploy at this point and it will create the basic AVD components, but no session hosts. To add the session hosts, we need to ensure we can access Active Directory. For this example we are assuming that we are using AD rather than AADDS. We are also assuming you have a domain controller in an Azure VNet. 2. Networking infrastructure We will create a new VNet for our session hosts and peer it to our AD VNet. we've also included an NSG here with a sample rule - I'd strongly suggest modifying them to meet your own security requirements. Components we will deploy here: * Session Host Virtual Network * Session Host Subnet © NSG ‘+ NSG—Subnet association ‘+ VNet Peering to Active Directory VNet The full template can be found in networking.tf The new concept we have here is using data to retrieve the properties of our existing Active Directory (Hub) VNet. We can then pass the ID to new peering we are creating. data “azurerm_virtual_network’ id_vnet_data* name = var.ad_vnet resource_group_name = varad.rg } hitpsstechcommunity merosof.comnSiazure-vitual-dosktoparm-avd-with raformim-p2699806 ene 7128722, 906 PM ARM AVD with Terraform - Microsoft Tech Community resource “azurerm_virtual_network peering" “peer! { name = "peer_avd_ad” resource_group.name = azurerm_resource.group.rg.name virtual_network_name azurerm_virtual_network.vnet.name remote_virtual_network.id = data.azurerm_virtual_network, ad_vnet.data.id } resource “azurerm_virtual network peering” “peer2" { name ‘peer_avd.ad" resource_group_name = var.ad_rg virtual_network_name — = var.ad_vnet remote_virtual_network_id } zurerm_virtual_network.vnet.id ‘Again, we can see that we have referenced several variables, so you'll need to add the following to your variables.tf file ad_vnet — the name of the VNet containing our Domain Controllers * ad_tg - resource group containing DCs ‘* dns_servers ~ custom DNS servers that we're using for our new VNet ‘* vnet_range— Address range for our new VNet ‘* subnet_range - Address range for our new subnet Now we should have all our basic infrastructure in place, we can move onto the session hosts. 3. Session Hosts Here we are deploying and configuring our session hosts. In this example, we will create a new Terraform config file, host.tf to do this. Full code is here. We will also add our variables to the variables.tf file. Components to deploy in this section: * NIC for each session host '* Session Host VM(s) hitpsstechcommunity merosof.comnSiazure-vitual-dosktoparm-avd-with aformin-p12639808 ene 712872, 906 PM ARM AVD wit Terafor- Mierosot Tech Community * Domain-join VM extension * Dsc VM extension to register session host, ‘* Random strong for local vm password * Local variable for registration token This part is slightly more complex than the infrastructure deployment. The new concepts in this section are covered below. We firstly need to create a local variable for our registration_info token to allow us to register the VM to the host pool. This is later passed as a protected setting to the dsc extension resource. locals ¢ registration_token = azurerm_virtual_desktop_host_pool.hostpool.registration_info[0].token } We're also creating a random local password — which needs to meet the AVD requirements: resource "random string’ ‘avd local password’ { count (var.rdsh_count}" length = 16 special = true min special = 2 override_special = "*!@#?" } In the password section you will see us referencing rdshcount. This allows us to deploy a variable number of VMs to our host pool. Using this counter will be used for the VM, the NICs, local passwords and the extensions. We are also using the count meta-argument to refer to specific instances: resource “azurerm_windows_virtual_machine* “avd_vm" { count ‘{var.rdsh_count}” name = "${varprefix}-${count.index + 1)" resource_group_name = var.rg name hitpsstechcommunity merosof.comnSiazure-vitual-dosktoparm-avd-with aformin-p12639808 m9 776122, 908 PM [ARM AVO wit Trraform - Mirosft Tech Community location = var.deploy location size = var.vm_size network_interface_ids {azurerm_network interface AVD_vm_nic.*id{count.index]}"] provision. vm_agent = true admin_username = "${var.local_admin_username}" admin_password = "${random_string.AVD-local-password.*.result[count.index]}" os_disk { name = "Stlower(var-prefix)}-${count.index +1)" caching = "ReadWrite” ‘storage_account.type = “Standard LRS" } source_image_reference { publisher = "MicrosoftWindowsDesktop” offer “Windows-10" sku Oh2-evd" version ‘latest’ } depends_on = [azurerm_resource_group.rg, azurerm_network interface AVD_vm_nic] } The VM resource is also where we specific the source image for the build. If you need a different market place image you can get the image SKU details using: Get-AzVMImageSku -Location -PublisherName MicrosoftWindowsDesktop - Offer windows-10 (or -Offer office-365 if you want the image including M365 apps). Deploying a custom image with the shared image gallery is a topic for a follow up article. The additional variables we need to specify now are © rdsh_count hitpsstechcommunity merosof.comnSiazure-vitual-dosktoparm-avd-with raformim-p2699806 ane 776122, 908 PM ARM AVD wit Terafor- Mlerosot Tech Community * domain_name + domain_user_upn ‘* domain_password * vm size © oupath * local_admin_username 4. Profile Storage For this example we'll deploy our profile storage using Azure Files. Step 6 has the steps to configure NetApp Files if you prefer this option. To do this we'll need to deploy the following resources: ‘* Adedicated resource group for our Storage account ‘Azure File Storage account * Azure Storage Share ‘* Assign AAD group to the Storage (Storage File Data SMB Share Contributor) We will deploy a new resource group. We are using a random string to generate a globally unique name for our Storage account. We are creating a file called afstorage.tf for this (and the full code is included here). We are appending a random string to the storage account name to ensure uniqueness — as such we also use the output command so that we can see the name of our new storage account. We can use the outputs.tf file to define our outputs. output “storage_account_name" { value = azurerm_storage_account.storage.name } Further configuration will be needed to enable AD authentication if you choose that direction and to configure NTFS permissions of SMB hitpsstechcommunity merosof.comnSiazure-vitual-dosktoparm-avd-with raformim-p2699806 one 7128722, 906 PM ARM AVD with Terraform - Microsoft Tech Community 5. RBAC Now that we have all our infrastructure deployed, let us give our users access. Again, we will create a new config file for this ~ rbac.te. This can also be modified to assign users to custom roles, or to the other desktop tualization roles that are already built in: Built-in roles Azure Virtual Desktop - Azure | Microsoft Docs The components we're creating here are: © Azure Active Directory Group * AAD group member * AAD role assignment Before we start, we'll need to add the azuread provider to our list of required providers in our provider.tf as we need to use this for some of the AAD resources. terraform { required providers { azurerm = { source = “hashicorp/azurerm* version = "~>2.0" } azuread = { source = “hashicorp/azuread” To assign the RBAC permissions we need to pass a list of existing AAD Users and then add these to a new AAD group that we are creating. hitps:techcommunity merosof.comSfazuresvitual-desktoplarm-avd-wth-eraformin- (2639808 sone 112822, 906 Pat [ARM AVO with Torafrm = Merosot Tech Gommniy We will use the AzureAD_user and azurerm_role_definition datasources to retrieve information about our users and the role we're assigning (in this case the builtin Desktop Virtualization user). data “azuread_user" “aad_user* { for_each = toset(var.avd_users) user principal name = format("%s", each.key) } role_def* { name = "Desktop Virtualization User" } data “azurerm_role_definitio We're also going to use for_each to loop through that list of users (both when getting the UPN from AAD and when adding to the group) resource "azuread_group_member* “aad group. member" { foreach = data.azuread_user.aad_user group object id = azuread_group.aad_group.id member_object_id = each.value["id"] } Lastly we'll scope the role assignment to the application group we created at the start and apply it to the group containing our users, resource “azurerm_role assignment" “role* { scope azurerm_virtual_desktop_application_group.dag.id role_definition id = data.azurerm_role_definition.role_def.id principal.id = azuread_group.aad group.id } We also need to add the 2 new variables: * aad_group_name hitps:techcommunity merosof.comSfazuresvitual-desktoplarm-avd-wth-eraformin- (2639808 nie 7128722, 906 PM ARM AVD with Terraform - Microsoft Tech Community * avd_users avd_users will be an array to allow us to pass multiple users. Up to now we have either specified default values for our variables or will pass them during deployment. To make things simpler we will create an env.tfvars file to pass our environment specific variables. You can add as many (or few) pre-configured variables here, but keep security in mind if you are putting confidential data in there. A sample might look like: deploy location = "west europe” rg.name = “avd-resources-rg” vnet_range = [°10.1.0.0/16" subnet_range = ["10.1.0.0/24"] prefix = "avd" avd_users = [ “user1@.com", “user2@.com" 1 dns_servers [°10.0.0.4", "168.63.129.16"] 6. NetApp Storage As an alternate to Azure Files you also have the option to deploy NetApp Storage for Azure Virtual Desktop profiles. To use NetApp Files you need to request access Register for Azure NetApp Files | Microsoft Docs To deploy the storage we'll need to deploy the following resources: ‘* Adedicated subnet for NetApp hitps:techcommunity merosof.comSfazuresvitual-desktoplarm-avd-wth-eraformin- (2639808 se 776122, 908 PM [ARM AVO wit Trraform - Mirosft Tech Community © NetApp storage Account ‘* NetApp storage Pool ‘* NetApp storage Volume For simplicity we'll deploy our subnet to the same Vnet we created earlier, and will use the same resource group and location variables. You may want separate resource groups and/or more complex networking in a production deployment, We are creating a file called netappstorage.tf for this and the full code can be found in the folder options/netapp. We also need to add some new variables (and you'll probably want to update the default values as well): ‘© NetApp_acct_name ‘+ NetApp_pool_name * NetApp_volume_name ‘+ NetApp_smb_name ‘* NetApp_volume_path ‘© NetApp_subnet_name ‘© NetApp_Address Now we should have created 9 Files © Main.tt © Networking.tt © Host.tf + afstorage:tf (or netappstorage.tf) © Rbactf © Variables.tf © defaults.tfvars © Outputs.tf * Providers.tf hitpsstechcommunity merosof.comnSiazure-vitual-dosktoparm-avd-with raformim-p2699806 rane 7128722, 906 PM 7. Variables ARM AVD with Terraform - Microsoft Tech Community All of the variables that we have referenced so far are described here (they are also in variables.tf) Name rg_name deploy location hostpool ad_ynet dns_servers vnet_range subnet_range avd_users aad_group_name rdsh_count prefix domain_name domain_user_upn domain_password vm size ‘ou_path local_admin_username netapp_acct_name netapp_pool_name netappvolumename netapp_smb_name netapp_volume_path hitpsstechcommunity merosot.comniazure-vitual-desktoplarm-avd-with Description Name of the Resource Group in which to deploy these resources Name of the Azure Virtual Desktop host pool Name of domain controller VNet Custom DNS configuration Address range for deployment VNet Address range for session host subnet The resource group for AD VM Azure Active Directory Group for AVD users Number of AVD machines to deploy Prefix of the name of the AVD machine(s) Name of the domain to join Username for domain join (do not include domain name as thi appended Password of the user to authenticate with the domain Size of the machine to deploy The ou path for AD The local admin username for the VM The NetApp account name The NetApp pool name The NetApp volume name The NetApp smb name The NetApp volume path raformim-p2699806 Default AVD-TF West Europe AVD-TF-HP Standard_DS2_v2 AVD_NetApp AVD_NetApp_pool AVD_NetApp_volume AVDNetApp AVDNetAppVolume sane 776122, 908 PM [ARM AVD wit Terraform - Mirosft Tech Community netapp_subnet_name The NetApp subnet name NetAppSubnet netapp_address The Address range for NetApp Subnet : Note: Variables in italic are optional and only needed if you are deploying NetApp Files, these are included only in the variables template in the netapp folder. 8. Deploy! The templates can be downloaded from github if you now want to deploy this yourself. There are also some additional configuration files for other functionality that we hope to cover in further articles soon, Once Terraform is setup and you have created your Terraform templates, the first step is to initialize Terraform. This step ensures that Terraform has all the prerequisites to build your template in Azure. terraform init The next step is to have Terraform review and validate the template. This step compares the requested resources to the state information saved by Terraform and then outputs the planned execution. The Azure resources aren't created at this point. An execution plan is generated and stored in the file specified by the -out parameter We also need to pass our variable definitions file during the plan. We can either load it automatically by renaming env:tfvars as terraform.tfvars OR envauto.tfvars, We then use the following to create the execution plan: terraform plan -out terraform_azure.tfplan If you don’t rename your variable file, use: hitps:techcommunity merosof.comSfazuresvitual-desktoplarm-avd-wth-eraformin- (2639808 s99 7128722, 906 PM ARM AVD with Terraform - Microsoft Tech Community terraform plan -var-file defaults.tfvars -out terraform_azure.tfplan Note: When you're ready to build the infrastructure in Azure, apply the execution plan - this will deploy the resources terraform apply terraform_azure.tfplan If you update the templates after you have deployed, you will need to rerun the plan and apply steps for them to reflect in Azure. Troubleshooting Terraform deployment Terraform deployment can fail in three main categories: 1. Issues with Terraform code 2. Issues with Desired State Configuration (DSC) 3. Conflict with existing resources Issues Terraform code While it is rare to have issues with the Terraform code it is still possible, however most often errors are due to bad input in variables.tt * If there are errors in the Terraform code, please file a GitHub issue. * If there are warning in the Terraform code feel free to ignore or address for your own instance of that code. ‘* Using Terraform error messages it's a good starting point towards identifying issues with input variables To troubleshoot this type of issue, navigate to the Azure portal and if needed reset the password on the VM that failed DSC. Once you are able to log in to the VM review the log files following the guidance here: Troubleshooting DSC - PowerShell | Microsoft Docs Conflict with Existing resources hitpsstechcommunity merosof.comnSiazure-vitual-dosktoparm-avd-with raformim-p2699806 1619 7128722, 906 PM ARM AVD with Terraform - Microsoft Tech Community If you have previously deployed resources with the same name, you may see a deployment failure, Deployment will stop if any failures occur. You can use: Terraform destroy To clean up resources that were created by the Terraform Apply command, You can pass it the same options as the apply command. The destroy command may fail trying to delete the subnet if associated resources have not been deleted first. In this case you may need to manually delete resources associated with the subnet before running destroy, or you can delete the whole resource group manually. 9. Final Configuration You'll notice we didn’t configure the session hosts to use our profile storage at any point. There is an assumption that we are using GPO to manage FSLogix across our host pools as documented here: Use FSLogix Group Policy Template Files - FSLogix | Microsoft Docs. At a minimum you'll need to configure the registry keys to enable FSLogix and configure the VHD Location to the NetApp Share URI: Profile Container registry configuration settings - FSLogix | Microsoft Docs If not using GPO, the registry keys could be manually added as part of the build to the session host. Please comment below if you have any questions or feedback! 103K Views uly Likes H2Replies E> Reply i All Discussions < Previous Discussion Next Discussion > 2 Replies O MaximSokoloff replied to EmilyMclaren @EmilyMclaren , wi pipelines ) hitpsstechcommunity merosof.comnSiazure-vitual-dosktoplarm-avd-with ing for a second part - automating the above via Azure Devops yaml raformim-p2699806 amie 7128722, 906 PM ARM AVD with Terraform - Microsoft Tech Community wet Like O offcolourt972 replied to EmilyMclaren % I Reply Blowing my own trumpet a little here, but here's my post on doing this completely AAD with Intune, https://gsilt.blogspot.com/2022/06/azure -virtual-desktop-with-pure-azure htm| et Like Reply Share hpstechcomerunty merosof com lazure-vitua-dsktoplarm-avd-vi-torafcenin- 2639605, rans 7128722, 906 PM What's new Surface Pro X Surface Laptop 3 Surface Pro7 Windows 10 Apps Office apps Enterprise Azure AppSource Automotive Government Healthcare Manufacturing Financial Services Sitemap Contact Microsoft About our ads © Microsoft hitpsstechcommunity merosof.comnSiazure-vitual-dosktoparm-avd-with ARM AVD with Terraform - Microsoft Tech Community Microsoft Store Account profile Download Center Microsoft Store support Returns ‘Order tracking Store locations Buy online, pick up in store In-store events Developer Microsoft Visual Studio Window Dev Center Developer Network TechNet Microsoft developer program Channel 9 Education Microsoft in education Office for students Office for schools, Deals for students and parents Microsoft Azure in education Company Careers ‘About Microsoft Company News Privacy at Microsoft investors Diversity and inclusion Office Dev Center Accessibility Microsoft Garage Security Privacy Terms of use Trademarks Safety and eco aformin-p12639808 son

You might also like