Following on from this article covering how to deploy Windows Virtual Desktop resources, this article will show how you can also deploy WVD session hosts using Terraform. The previous article detailed how to use Terraform to deploy a AVD workspace, app group and host pool. This article will show how you can use Terraform to add session hosts to the empty host pool.
To create a session host we need to define the following resources in a Terraform plan:
azurerm_network_interface
– Network Interface resource for the session hostazurerm_virtual_machine
– Session host virtual machineazurerm_virtual_machine_extension
– Virtual Machine Domain Join Extensionazurerm_virtual_machine_extension
– Virtual Machine Powershell DSC Extension
Really we are just deploying a Azure virtual machine, then registering it with WVD (or Azure Virtual Desktop as it’s now called). Let’s go through though each of the resources defined in the Terraform plan to explain how each part works. If you have experience already of deploying Azure virtual machines using Terraform, then a lot of this will look very familiar.
To start with, I have defined the following variables. I have put some example values in here for demonstration purposes, but you should use a terraform.tfvars file to populate your variables, especially for anything sensitive such as passwords. Note there are some resources referenced which are not part of the Terraform plan. We are assuming here that a number of things already exist such as the vNet and Subnet to deploy the VM into, a domain to which the VMs can be joined, and a WVD / AVD host pool to which the new session host(s) can be registered.
Azure Virtual Desktop Terraform Plan Variables
variable "resource_group" {
description = "The name of the resource group in which to create the Azure resources"
default = "myResourceGroup"
}
variable "location" {
description = "The location/region where the session hosts are created"
default = "East US"
}
variable "vm_size" {
description = "Specifies the size of the virtual machine."
default = "Standard_B2s"
}
variable "image_publisher" {
description = "Image Publisher"
default = "MicrosoftWindowsServer"
}
variable "image_offer" {
description = "Image Offer"
default = "WindowsServer"
}
variable "image_sku" {
description = "Image SKU"
default = "2016-Datacenter"
}
variable "image_version" {
description = "Image Version"
default = "latest"
}
variable "admin_username" {
description = "Local Admin Username"
default = "azureadmin"
}
variable "admin_password" {
description = "Admin Password"
default = "P@55W0rD!!"
}
variable "subnet_id" {
description = "Azure Subnet ID"
default = "/subscriptions/xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxx/resourceGroups/wvd-rg/providers/Microsoft.Network/virtualNetworks/wvd-vnet/subnets/wvd"
}
variable "vm_name" {
description = "Virtual Machine Name"
default = "avd-vm-host-"
}
variable "vm_count" {
description = "Number of Session Host VMs to create"
default = "5"
}
variable "domain" {
description = "Domain to join"
default = "avd.local"
}
variable "domainuser" {
description = "Domain Join User Name"
default = "admin@avd.local"
}
variable "oupath" {
description = "OU Path"
default = "OU=AVD,DC=avd,DC=local"
}
variable "domainpassword" {
description = "Domain User Password"
default = "password"
}
variable "regtoken" {
description = "Host Pool Registration Token"
default = "registration token generated by hostpool goes here"
}
variable "hostpoolname" {
description = "Host Pool Name to Register Session Hosts"
default = "avd-host-pool"
}
variable "artifactslocation" {
description = "Location of WVD Artifacts"
default = "https://wvdportalstorageblob.blob.core.windows.net/galleryartifacts/Configuration.zip"
}
With the variables set, we can move on to defining the required resources to deploy a Azure Virtual Desktop session host.
Creating the AVD Session Host’s Network Interface
As with creating any Azure VM using Terraform, we first need to define a network interface resource:
resource "azurerm_network_interface" "nic" {
name = "${var.vm_name}${count.index + 1}"
location = "${var.location}"
resource_group_name = "${var.res_group}"
count = "${var.vm_count}"
ip_configuration {
name = "webipconfig${count.index}"
subnet_id = "${var.subnet_id}"
private_ip_address_allocation = "Dynamic"
}
}
I am using variables to set the name for the NIC
, Location
, Resource Group
and SubnetID
. Note that I am also using a count index. This allows us to create multiple session hosts, based on the value assigned to the count
variable.
Create the Session Host Virtual Machines
Like with the network interfaces, the following will look familiar if you have used Terraform to deploy Azure virtual machines:
resource "azurerm_virtual_machine" "vm" {
name = "${var.vm_name}${count.index + 1}"
location = "${var.location}"
resource_group_name = "${var.res_group}"
vm_size = "${var.vm_size}"
network_interface_ids = ["${element(azurerm_network_interface.nic.*.id, count.index)}"]
count = "${var.vm_count}"
delete_os_disk_on_termination = true
storage_image_reference {
publisher = "${var.image_publisher}"
offer = "${var.image_offer}"
sku = "${var.image_sku}"
version = "${var.image_version}"
}
storage_os_disk {
name = "${var.vm_name}${count.index + 1}"
create_option = "FromImage"
}
os_profile {
computer_name = "${var.vm_name}${count.index + 1}"
admin_username = "${var.admin_username}"
admin_password = "${var.admin_password}"
}
os_profile_windows_config {
provision_vm_agent = true
}
}
Again we are using the count index
to ensure each VM gets paired with a network interface. Aside from that, we are just pulling a virtual machine image from the Azure marketplace, deploying it into the specified resource group, and setting it’s size, along with OS parameters for the local administrator account and password. As such, this is a fairly minimal example of a VM deployment, but additional configuration could be added here if that is a requirement. Or, for example, rather than using an Azure Marketplace machine image, a customised image from a shared image gallery could be used.
Note, all of these parameter values used here to configure the virtual machine are taken from the Terraform variables defined earlier.
Join the Session Hosts to a Active Directory Domain
The next resource we need to define is the domain join extension, so that the session host virtual machines will be joined to Active Directory when they have been powered on for the first time. Being a member of a domain is a requirement of running a AVD Session Host. This uses the built in domain join extension:
resource "azurerm_virtual_machine_extension" "domainjoinext" {
name = "join-domain"
virtual_machine_id = element(azurerm_virtual_machine.vm.*.id, count.index)
publisher = "Microsoft.Compute"
type = "JsonADDomainExtension"
type_handler_version = "1.0"
depends_on = ["azurerm_virtual_machine.vm"]
count = "${var.vm_count}"
settings = <<SETTINGS
{
"Name": "${var.domain}",
"OUPath": "${var.oupath}",
"User": "${var.domainuser}",
"Restart": "true",
"Options": "3"
}
SETTINGS
protected_settings = <<PROTECTED_SETTINGS
{
"Password": "${var.domainpassword}"
}
PROTECTED_SETTINGS
}
We’re using variables to pass the resource the parameters it needs, including the domain
to join, domain user account
, password
and OU
. These are passed to the extension using the settings, and protected settings for the password.
Azure Virtual Desktop Agent and Configuration
This bit is where the magic happens! So far we have pretty much a regular Azure virtual machine (or machines if the count
variable has been set higher than 1), which are joined to Active Directory. Again we are going to use a virtual machine extension:
resource "azurerm_virtual_machine_extension" "registersessionhost" {
name = "registersessionhost"
virtual_machine_id = element(azurerm_virtual_machine.vm.*.id, count.index)
publisher = "Microsoft.Powershell"
depends_on = ["azurerm_virtual_machine_extension.domainjoinext"]
count = "${var.vm_count}"
type = "DSC"
type_handler_version = "2.73"
auto_upgrade_minor_version = true
settings = <<SETTINGS
{
"ModulesUrl": "${var.artifactslocation}",
"ConfigurationFunction" : "Configuration.ps1\\AddSessionHost",
"Properties": {
"hostPoolName": "${var.hostpoolname}",
"registrationInfoToken": "${var.regtoken}"
}
}
SETTINGS
}
This one is a Powershell DSC extension, which downloads the scripts necessary from Microsoft to join the session hosts to the specified host pool. The var.artifactslocation
variable is populated with the URL to download the necessary scripts. These are found at https://wvdportalstorageblob.blob.core.windows.net/galleryartifacts/Configuration.zip and are the same as what are used when manually adding a session host using the Azure portal. Specifically we are telling it to run the AddSessionHost
function in the configuration.ps1
file contained within in the zip. The configuration.zip
file also contains the agent required to register the session host with the AVD host pool. I’d encourage you to take a look at the contents of the zip file so you can see how the scripts work.
For the AddSessionHost
function, we just need to provide the host pool name and the host pool registration token, which is done using the var.hostpoolname
and var.regtoken
variables. And that’s all we need. With these 4 resources and the variables defined we are ready to deploy!
Running the Terraform Plan to Deploy AVD Session Hosts
Once you have created the necessary terraform files containing the resources, and the variables / tfvars, we are ready to test the plan, using terraform plan
:
$ terraform plan
This will show all the resources that Terraform will create for us if we run the plan for real. Always review the output from this before proceeding, but in summary it should show that we will be created 20 new resources as part of this plan:
Plan: 20 to add, 0 to change, 0 to destroy.
Now we can run the plan for real using terraform apply
:
$ terraform apply
You will be prompted to confirm that you want Terraform to go ahead and build the resources. Enter ‘Yes’ when prompted to do so. The plan doesn’t take long to run – once it’s finished you should see:
Apply complete! Resources: 20 added, 0 changed, 0 destroyed.
There should now be a bunch of session hosts attached to the new host pool!
Summary
In this article you have seen how to use Terraform to deploy Azure Virtual Desktop (Windows Virtual Desktop) Session hosts into a new host pool. Remember to check out this previous article on how to deploy the Azure Virtual Desktop host pool.