Learning Terraform > WVD-as-a-Module

Learning Terraform Series
01. Deploying WVD
02. Remote State
03. WVD-as-a-Module [This Post]

In this third post in my Learning Terraform series I’ll explore the concept of Modules.

What is a Module?

“With Terraform, you can put your code inside of a Terraform module and reuse that module in multiple places throughout your code. Instead of having the same code copy/pasted in the staging and production environments, you’ll be able to have both environments reuse code from the same module.

This is a big deal. Modules are the key ingredient to writing reusable, maintainable, and testable Terraform code. Once you start using them, there’s no going back. You’ll start building everything as a module, creating a library of modules to share within your company, start leveraging modules you find online, and start thinking of your entire infrastructure as a collection of reusable modules.”

Source: https://blog.gruntwork.io/how-to-create-reusable-infrastructure-with-terraform-modules-25526d65f73d

Learning about Modules has completely changed how I approach Terraform, now rather than thinking of every Terraform file as a standalone entity I’m instead looking at what common elements I can make into a module.

I’ve used Windows Virtual Desktop (WVD) as a common theme for learning Terraform in my previous posts, and this fits extremely well into the model of a Module as the architecture of WVD is static, that is to say, the relationship between a Workspace, Application Group and Host Pool doesn’t change.

The Anatomy of a Module

The beauty of a Module is in its simplicity.

In short, any Terraform file is pretty much a module by default.

There is no discernible difference between the syntax and structure of a standard configuration file and that of a module other than when calling a module you pass in all unique resource values from the main configuration file rather than a variables file.

I’ve tried to show this in the figure below, in a standard Terraform configuration you would create a folder for your code and within it store the main and variable files. The main.tf file contain the Terraform provider (Azure, AWS etc) and the resources to create, you could pass in values from a variables.tf file in the same folder.

Figure 1 – Standard Terraform Configuration Architecture

When calling or referencing a module however you would specify the variable values within the main configuration file, shown below in blue.

The module would commonly reside in its own folder structure, a central module library perhaps, the structure of which is identical to a standard Terraform configuration.

The biggest notable difference, and this will become evident in the code, is that modules’ variable file (shown in green) doesn’t contain any default values as those are passed in from outside.

Figure 2 – Terraform Module Architecture


Building on from the code in my previous posts I’ve now converted the code to deploy WVD into a reusable module.

This code is available from my GitHub repo, here

Firstly, the below code is the main.tf that will call the module, in figure 2 above this is the script in blue.

# Get AzureRM Terraforn Provider
provider "azurerm" {
  version = "2.31.1" #Required for WVD
  features {}

# Remote State, replace with your resource group, storage account and container name
terraform {
  backend "azurerm" {
    storage_account_name = "vfftfstateusw2"
    container_name       = "tfstate"
    key                  = "terraform.tfstate"
    resource_group_name  = "VFF-USE-RG-WVD-REMOTE"

# Create resource group
resource "azurerm_resource_group" "default" {
name     = "VFF-USW-RG-WVD-FromMod"
location = "West US 2"

# Call WVD-as-a-Module and pass in variables
module "WVD-as-a-Module" {
  source                         = "../Modules/WVD-as-a-Module"
  rgname                         = azurerm_resource_group.default.name
  region                         = azurerm_resource_group.default.location
  pooledhpname                   = "VFF-WUS-TFRM-Mod"
  pooledhpfriendlyname           = "VFF Pooled Host Pool"
  pooledhpdescription            = "VFF Pooled Host Pool"
  pooledhpremoteappname          = "VFF-WUS-TFRM-Mod-RA"
  pooledhpremoteappfriendlyname  = "VFF Pooled Host Pool Remote Apps"
  pooledhpremoteappdescription   = "VFF Pooled Host Pool Remote Apps"
  pooledhpdesktopappname         = "VFF-WUS-TFRM-Mod-DT"
  pooledhpdesktopappfriendlyname = "VFF Pooled Host Pool Remote Apps"
  pooledhpdesktopappdescription  = "VFF Pooled Host Pool Remote Apps"
  workspace                      = "VFF-Terraform-Wkspc-Mod"
  workspacefriendlyname          = "VFF-Terraform-Workspace"
  workspacedesc                  = "VFF-Terraform-Workspace"
  pooledhpmaxsessions            = 50

This next code is the WVD-as-a-Module main configuration File, in figure 2 this is show in orange.

Note, I recommend create a new folder structure for your modules.

terraform {
  required_version = ">=0.12"

# Create "Pooled" WVD Host Pool
resource "azurerm_virtual_desktop_host_pool" "pooleddepthfirst" {
  location                 = var.region
  resource_group_name      = var.rgname
  name                     = var.pooledhpname
  friendly_name            = var.pooledhpfriendlyname
  description              = var.pooledhpdescription
  type                     = "Pooled"
  maximum_sessions_allowed = var.pooledhpmaxsessions
  load_balancer_type       = "DepthFirst"

#Create RemoteApp Application Group
resource "azurerm_virtual_desktop_application_group" "pooledremoteapp" {
  name                = var.pooledhpremoteappname
  location            = var.region
  resource_group_name = var.rgname
  type                = "RemoteApp"
  host_pool_id        = azurerm_virtual_desktop_host_pool.pooleddepthfirst.id
  friendly_name       = var.pooledhpremoteappfriendlyname
  description         = var.pooledhpremoteappdescription

#Create Desktop Application Group
resource "azurerm_virtual_desktop_application_group" "pooleddesktopapp" {
  name                = var.pooledhpdesktopappname
  location            = var.region
  resource_group_name = var.rgname
  type                = "Desktop"
  host_pool_id        = azurerm_virtual_desktop_host_pool.pooleddepthfirst.id
  friendly_name       = var.pooledhpdesktopappfriendlyname
  description         = var.pooledhpdesktopappdescription

# Create Workspace
resource "azurerm_virtual_desktop_workspace" "workspace" {
  name                = var.workspace
  location            = var.region
  resource_group_name = var.rgname
  friendly_name       = var.workspacefriendlyname
  description         = var.workspacedesc

# Associate RemoteApp Application Group with Workspace
resource "azurerm_virtual_desktop_workspace_application_group_association" "workspaceremoteapp" {
  workspace_id         = azurerm_virtual_desktop_workspace.workspace.id
  application_group_id = azurerm_virtual_desktop_application_group.pooledremoteapp.id

# Associate Desktop Application Group with Workspace
resource "azurerm_virtual_desktop_workspace_application_group_association" "workspacedesktop" {
  workspace_id         = azurerm_virtual_desktop_workspace.workspace.id
  application_group_id = azurerm_virtual_desktop_application_group.pooleddesktopapp.id

Lastly, this is the associated variables file for the module, this is shown in green in figure 2.

variable "rgname" {
  description = "Resource Group Name"
  type        = string

variable "region" {
  description = "Region"
  type        = string

variable "pooledhpname" {
  description = "Pooled Host Pool Name"
  type        = string

variable "pooledhpmaxsessions" {
  description = "Max sessions per pooled host"
  type        = number

variable "pooledhpfriendlyname" {
  description = "Pooled Host Pool Friendly Name"
  type        = string

variable "pooledhpdescription" {
  description = "Pooled Host Pool Description"
  type        = string

variable "pooledhpremoteappname" {
  description = "Pooled Host Pool RemoteApp App Group Name"
  type        = string

variable "pooledhpremoteappfriendlyname" {
  description = "Pooled Host Pool RemoteApp App Group Friendly Name"
  type        = string

variable "pooledhpremoteappdescription" {
  description = "Pooled Host Pool RemoteApp App Group Description"
  type        = string

variable "pooledhpdesktopappname" {
  description = "Pooled Host Pool Desktop App Group Friendly Name"
  type        = string

variable "pooledhpdesktopappfriendlyname" {
  description = "Pooled Host Pool Desktop App Group Friendly Name"
  type        = string

variable "pooledhpdesktopappdescription" {
  description = "Pooled Host Pool Desktop App Group Description"
  type        = string

variable "workspace" {
  description = "WVD Workspace Name"
  type        = string

variable "workspacefriendlyname" {
  description = "WVD Workspace Friendly Name"
  type        = string

variable "workspacedesc" {
  description = "WVD Workspace Description"
  type        = string

This code is available from my GitHub repo, here

If you have any questions or queries please get in touch on Twitter


4 thoughts on “Learning Terraform > WVD-as-a-Module”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: