Creating a Windows VM in Azure using Terraform — which way is best?

Terraform Logo

Examples!

# Demo for presentation creating a resource group in Azure!# Defines the Azure provider and defines what subscription we are connecting to.provider "azurerm" {version = "=2.25.0"features {}subscription_id = var.subscription_idclient_id       = var.client_idclient_secret   = var.client_secrettenant_id       = var.tenant_id}# Specifies we want to use Terraform Cloud to store our state file.# https://app.terraform.io/app/xxxxterraform {backend "remote" {organization = "xxxx"workspaces {name = "azure-vm-windows-demo"}}}
# read in from the terraform.auto.tfvars filevariable "subscription_id" {}variable "client_id" {}variable "client_secret" {}variable "tenant_id" {}variable "global_settings" {}variable "desktop_vm_image_publisher" {}variable "desktop_vm_image_offer" {}variable "desktop_vm_image_sku" {}variable "desktop_vm_image_version" {}variable "desktop_vm_size" {}variable "server_vm_image_publisher" {}variable "server_vm_image_offer" {}variable "server_vm_image_sku" {}variable "server_vm_image_version" {}variable "server_vm_size" {}
# This file should not be checked into source control (add to .gitignore)subscription_id = "xxxxxxxxxxx"client_id       = "xxxxxxxxxxx"client_secret   = "xxxxxxxxxxx"tenant_id       = "xxxxxxxxxxx"## globalsettingsglobal_settings = {#Set of tagstags = {applicationName = "Windows VM Demo"businessUnit    = "Technical Solutions"costCenter      = "MPN Sponsorship"DR              = "NON-DR-ENABLED"deploymentType  = "Terraform"environment     = "Dev"owner           = "Jack Roper"version         = "0.1"}}# Desktop VM variablesdesktop_vm_image_publisher = "MicrosoftWindowsDesktop"desktop_vm_image_offer     = "Windows-10"desktop_vm_image_sku       = "20h1-pro"desktop_vm_image_version   = "latest"desktop_vm_size            = "Standard_B1s"# Server VM Variablesserver_vm_image_publisher = "MicrosoftWindowsServer"server_vm_image_offer     = "WindowsServer"server_vm_image_sku       = "2019-Datacenter"server_vm_image_version   = "latest"server_vm_size            = "Standard_B1s"
# Creating a base resource groupresource "azurerm_resource_group" "rg" {name     = "azure-vm-demo-rg"location = "UK South"}
# Create Networkmodule "network" {source              = "Azure/network/azurerm"version             = "3.2.1"resource_group_name = azurerm_resource_group.rg.namevnet_name           = "tfdemovnet"address_space       = "10.0.0.0/16"subnet_prefixes     = ["10.0.1.0/24", "10.0.2.0/24"]subnet_names        = ["desktops", "servers"]tags                = var.global_settings.tagsdepends_on = [azurerm_resource_group.rg]}# Create NSG# https://registry.terraform.io/modules/Azure/network-security-group/azurerm/latestmodule "network-security-group" {source                = "Azure/network-security-group/azurerm"version               = "3.4.1"resource_group_name   = azurerm_resource_group.rg.namelocation              = "UKSouth" # Optional; if not provided, will use Resource Group locationsecurity_group_name   = "tfdemonsg"source_address_prefix = ["*"]predefined_rules = [{name     = "RDP"priority = "500"}]tags = var.global_settings.tagsdepends_on = [azurerm_resource_group.rg]}# Associate NSG with Subnetresource "azurerm_subnet_network_security_group_association" "nsg_association0" {subnet_id                 = module.network.vnet_subnets[0]network_security_group_id = module.network-security-group.network_security_group_id}# Associate NSG with Subnetresource "azurerm_subnet_network_security_group_association" "nsg_association1" {subnet_id                 = module.network.vnet_subnets[1]network_security_group_id = module.network-security-group.network_security_group_id}

1. Create a VM using the Terraform Azurerm provider

# Create a VM directly using Azurerm Creates 1 for Windows 10 desktop and 1 for Windows 2019 Server.# Getting VM admin passwords from keyvaultdata "azurerm_key_vault" "keyvault" {name                = "tfdemokv123"resource_group_name = azurerm_resource_group.rg.namedepends_on = [module.keyvault,module.access_policy_master,]}data "azurerm_key_vault_secret" "password" {name         = "password"key_vault_id = data.azurerm_key_vault.keyvault.iddepends_on = [module.keyvault,module.access_policy_master,]}### Create Desktop VM# Configure Availiablility setresource "azurerm_availability_set" "avset" {name                         = "tfdtnomod-avset"location                     = "uksouth"resource_group_name          = azurerm_resource_group.rg.nameplatform_fault_domain_count  = 2platform_update_domain_count = 2managed                      = truetags = var.global_settings.tags}# Create Public IPresource "azurerm_public_ip" "pip" {name                = "tfdtnomod-pip"location            = "uksouth"resource_group_name = azurerm_resource_group.rg.nameallocation_method   = "Dynamic"tags = var.global_settings.tags}# Create network interface for VMresource "azurerm_network_interface" "nic" {name                = "tfdtnomod-NIC"location            = "uksouth"resource_group_name = azurerm_resource_group.rg.nameip_configuration {name                          = "tfdtnomod-NIC"subnet_id                     = module.network.vnet_subnets[0]private_ip_address_allocation = "Static"private_ip_address            = "10.0.1.25"public_ip_address_id          = azurerm_public_ip.pip.id}tags = var.global_settings.tags}# Create virtual machineresource "azurerm_virtual_machine" "vm-desktop" {name                             = "tfdtnomod"location                         = "uksouth"resource_group_name              = azurerm_resource_group.rg.namenetwork_interface_ids            = [azurerm_network_interface.nic.id]vm_size                          = var.desktop_vm_sizeavailability_set_id              = azurerm_availability_set.avset.iddelete_os_disk_on_termination    = truedelete_data_disks_on_termination = truestorage_image_reference {publisher = var.desktop_vm_image_publisheroffer     = var.desktop_vm_image_offersku       = var.desktop_vm_image_skuversion   = var.desktop_vm_image_version}storage_os_disk {name              = "tfdtnomod-OsDisk"caching           = "ReadWrite"create_option     = "FromImage"managed_disk_type = "StandardSSD_LRS"disk_size_gb      = "127"}os_profile {computer_name  = "tfdtnomod"admin_username = "admin"admin_password = data.azurerm_key_vault_secret.password.value}os_profile_windows_config {timezone                  = "GMT Standard Time"provision_vm_agent        = trueenable_automatic_upgrades = true}tags = var.global_settings.tags}### Create Server VM# Configure Availiablility setresource "azurerm_availability_set" "avsetsvr" {name                         = "tfsvrnomod-avset"location                     = "uksouth"resource_group_name          = azurerm_resource_group.rg.nameplatform_fault_domain_count  = 2platform_update_domain_count = 2managed                      = truetags = var.global_settings.tags}# Create Public IPresource "azurerm_public_ip" "pipsv" {name                = "tfsvrnomod-pip"location            = "uksouth"resource_group_name = azurerm_resource_group.rg.nameallocation_method   = "Dynamic"tags = var.global_settings.tags}# Create network interface for VMresource "azurerm_network_interface" "nicsv" {name                = "tfsvrnomod-NIC"location            = "uksouth"resource_group_name = azurerm_resource_group.rg.nameip_configuration {name                          = "tfsvrnomod-NIC"subnet_id                     = module.network.vnet_subnets[1]private_ip_address_allocation = "Static"private_ip_address            = "10.0.2.26"public_ip_address_id          = azurerm_public_ip.pipsv.id}tags = var.global_settings.tags}# Create virtual machineresource "azurerm_virtual_machine" "vm-server" {name                             = "tfsvrnomod"location                         = "uksouth"resource_group_name              = azurerm_resource_group.rg.namenetwork_interface_ids            = [azurerm_network_interface.nicsv.id]vm_size                          = var.server_vm_sizeavailability_set_id              = azurerm_availability_set.avsetsvr.iddelete_os_disk_on_termination    = truedelete_data_disks_on_termination = truestorage_image_reference {publisher = var.server_vm_image_publisheroffer     = var.server_vm_image_offersku       = var.server_vm_image_skuversion   = var.server_vm_image_version}storage_os_disk {name              = "tfsvrnomod-OsDisk"caching           = "ReadWrite"create_option     = "FromImage"managed_disk_type = "StandardSSD_LRS"disk_size_gb      = "127"}os_profile {computer_name  = "tfsvrnomod"admin_username = "admin"admin_password = data.azurerm_key_vault_secret.password.value}os_profile_windows_config {timezone                  = "GMT Standard Time"provision_vm_agent        = trueenable_automatic_upgrades = true}tags = var.global_settings.tagsdepends_on = [module.keyvault,module.access_policy_master,]}

2. Create a VM using a custom module

# Create an Azure VM cluster with Terraform calling a Module. Creates 1 for Windows 10 desktop and 1 for Windows 2019 Server.module windows_desktop_vm_using_local_module {source              = "./vm"resource_group_name = azurerm_resource_group.rg.namelocation            = "uksouth"sloc                = "uks"vm_subnet_id        = module.network.vnet_subnets[0]vm_name             = "tfdtlocmod"vm_size             = var.desktop_vm_sizepublisher           = var.desktop_vm_image_publisheroffer               = var.desktop_vm_image_offersku                 = var.desktop_vm_image_skustatic_ip_address   = "10.0.1.15"activity_tag        = "Windows Desktop"admin_password      = module.vmpassword.secretvalue}module windows_server_vm_using_local_module {source              = "./vm"resource_group_name = azurerm_resource_group.rg.namelocation            = "uksouth"sloc                = "uks"vm_subnet_id        = module.network.vnet_subnets[1]vm_name             = "tfsvlocmod"vm_size             = var.server_vm_sizepublisher           = var.server_vm_image_publisheroffer               = var.server_vm_image_offersku                 = var.server_vm_image_skustatic_ip_address   = "10.0.2.15"activity_tag        = "Windows Server"admin_password      = module.vmpassword.secretvalue}
resource "random_string" "nic_prefix" {length  = 4special = false}resource "azurerm_network_interface" "vm_nic" {name                = "${var.vm_name}-nic1"location            = var.locationresource_group_name = var.resource_group_nameip_configuration {name                          = "${var.vm_name}_nic_${random_string.nic_prefix.result}"subnet_id                     = var.vm_subnet_idprivate_ip_address_allocation = "Static"private_ip_address            = var.static_ip_address}tags = var.tags}resource "azurerm_network_interface_security_group_association" "vm_nic_sg" {network_interface_id      = azurerm_network_interface.vm_nic.idnetwork_security_group_id = var.network_security_group_idcount                     = var.network_security_group_id == "" ? 0 : 1}resource "azurerm_virtual_machine" "windows_vm" {name                = var.vm_namevm_size             = var.vm_sizelocation            = var.locationresource_group_name = var.resource_group_nametags = merge(var.tags, { activityName = "${var.activity_tag} " })network_interface_ids = ["${azurerm_network_interface.vm_nic.id}",]storage_image_reference {publisher = var.publisheroffer     = var.offersku       = var.skuversion   = "latest"}identity {type = "SystemAssigned"}storage_os_disk {name              = "${var.vm_name}-os-disk"caching           = "ReadWrite"create_option     = "FromImage"managed_disk_type = "Standard_LRS"}os_profile {admin_password = module.vmpassword.secretvalueadmin_username = "azureuser"computer_name  = var.vm_name}os_profile_windows_config {provision_vm_agent = true}delete_os_disk_on_termination    = var.vm_os_disk_delete_flagdelete_data_disks_on_termination = var.vm_data_disk_delete_flag}
output "vm_id" {value = "${azurerm_virtual_machine.windows_vm.id}"}output "vm_name" {value = "${azurerm_virtual_machine.windows_vm.name}"}output "vm_location" {value = "${azurerm_virtual_machine.windows_vm.location}"}output "vm_resource_group_name" {value = "${azurerm_virtual_machine.windows_vm.resource_group_name}"}
variable "resource_group_name" {}variable "location" {}variable "sloc" {}variable "vm_size" {default = "Standard_B1s"}variable "vm_subnet_id" {}variable "vm_name" {}variable "vm_os_disk_delete_flag" {default = true}variable "vm_data_disk_delete_flag" {default = true}variable "network_security_group_id" {default = ""}variable "static_ip_address" {}variable "publisher" {}variable "offer" {}variable "sku" {}variable "tags" {type        = mapdescription = "All mandatory tags to use on all assets"default = {activityName       = "AzureVMWindowsDemo"automation         = "Terraform"costCenter1        = "A00000"dataClassification = "Demo"managedBy          = "example@test.com"solutionOwner      = "example@test.com"}}variable "activity_tag" {}variable "admin_password" {}

3. Create a VM using the Terraform registry module

# Create an Azure VM cluster with Terraform using the Module Registry. Creates 1 for Windows 10 desktop and 1 for Windows 2019 Server using the module registry.# https://docs.microsoft.com/en-us/azure/developer/terraform/create-vm-cluster-module# Windows 10 desktop VM(s)module "windows_desktop_vm_using_registry_module" {source              = "Azure/compute/azurerm"version             = "3.10.0"resource_group_name = azurerm_resource_group.rg.nameis_windows_image    = truevm_hostname         = "tfdtregmod" // line can be removed if only one VM module per resource groupadmin_username      = "admin"admin_password      = var.admin_passwordpublic_ip_dns       = ["tfdtregmod"] // change to a unique name per datacenter regionvm_os_publisher     = var.desktop_vm_image_publishervm_os_offer         = var.desktop_vm_image_offervm_os_sku           = var.desktop_vm_image_skuvm_size             = var.desktop_vm_sizeremote_port         = "3389"nb_instances        = "1"vnet_subnet_id      = module.network.vnet_subnets[0]tags                = var.global_settings.tagsdepends_on = [azurerm_resource_group.rg]}# Windows Server 2019 VM(s)module "windows_server_vm_using_registry_module" {source              = "Azure/compute/azurerm"version             = "3.10.0"resource_group_name = azurerm_resource_group.rg.nameis_windows_image    = truevm_hostname         = "tfsvregmod" // line can be removed if only one VM module per resource groupadmin_username      = "admin"admin_password      = var.admin_passwordpublic_ip_dns       = ["tfsvregmod"] // change to a unique name per datacenter regionvm_os_publisher     = var.server_vm_image_publishervm_os_offer         = var.server_vm_image_offervm_os_sku           = var.server_vm_image_skuvm_size             = var.server_vm_sizeremote_port         = "3389"nb_instances        = "1"vnet_subnet_id      = module.network.vnet_subnets[1]tags                = var.global_settings.tagsdepends_on = [azurerm_resource_group.rg]}

An experienced IT professional, focused on cloud tech and DevOps. Specialising in Azure, AWS, & Terraform. Currently working at BT Enterprise as a consultant.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store