In the previous blogs , we have seen an example how-to deploy a infrastructure in Azure for the network part. Now a step further in the deployment process we will add a Windows Server 2016 to the environment.
In this blog we will show you following examples:
- Deploy a virtual machine with the Azure RM module “compute”
- Deploy a virtual machine with “azurerm_virtual_machine”
- List the pro and contra
- Use the extensions to
- Add the virtual machine to a Windows Domain
- Add desktop background information
Design

Deploy Azure Virtual Machine with Azure RM module “Compute”

Download ./os!!
The download of the module is not working correctly when doing [Terraform Init].
module.windowsservers.os
Getting source “./os”
Error downloading modules: Error loading modules: error downloading
When we look on Github, we found that there is a BUG in the module – chase is still open: GITHUB Issue
Also work around to delete the terraform folder .terraform, did not solve the issue of the ./os download. It seems not to be a provider issue but an Terraform Core issue.
Based on above remark, I could not complete the deployment and this pushed me back to the “azurerm_virtual_machine” Code that was used to deploy a virtual machine with the “compute”- module:
resource "random_string" "AccID" { length=5 special=false number=true min_numeric=5 } #Create local administrator Account ID - Example ID: AccID23756 locals { VarAccID="AccID-${random_string.AccID.result}" } /* resource "aws_db_instance" "example" { password = "${random_string.password.result}" } */ module "windowsservers" { source = "Azure/compute/azurerm" location = "${azurerm_resource_group.RGName.location}" vnet_subnet_id = "${module.network.vnet_subnets[0]}" admin_password = "${var.ARM_VAR_LocalSrvAccSecret}" admin_username = "${local.VarAccID}" boot_diagnostics = "enable" nb_instances = "1" nb_public_ip = "0" resource_group_name = "${azurerm_resource_group.RGName.name}" tags { Environment = "${var.TAGS_ENVPRD}" Departement = "${var.TAGS_DEPIT}" } vm_hostname = "dcsazprois001" vm_os_simple = "WindowsServer" vm_os_sku = "2016-Datacenter" vm_size = "Standard_DS2_V2" }
Deploy Azure Virtual Machine with
“azurerm_virtual_machine“
Deploy Virtual Machine – no extensions
To deploy windows servers we need a storage account. The storage account that I will deploy in the example has following characteristics. The storage account “azrmstgacc-stdssdlrs-001”:
- Account tier: Premium
- Account kind: Storage V2
- Access tier: Hot
- Account replication Type: Local Redundant Storage (LRS)
- Blob encryption: Enabled
Storage Code using “azurerm_Storage_account”, using the terraform file: “storage.tf”
resource "azurerm_storage_account" "azrmstgacc-stdssdlrs-001" { name = "${var.ARM_AZ_STGACC_STDSSD_NAME}" resource_group_name = "${azurerm_resource_group.RGName.name}" location = "${azurerm_resource_group.RGName.location}" account_tier = "Premium" #Storage V2 is the recommend storage type by Microsoft Azure (General purpose) account_kind = "StorageV2" #Storage Access tier = Hot / Cool access_tier = "Hot" /* account replication type of the storage account LRS: Locally Redundant Storage (locally store 3 Copies of the data) ZRS: Zone Redundant Storage GRS: Geo Redundant Storage RAGRS: Read-Acces Geo Redundant Storage */ account_replication_type = "LRS" enable_blob_encryption = "true" tags = { Environment = "${var.TAGS_ENVPRD}" EnvType = "${var.TAGS_EVNTYPE_STORAGE}" Departement = "${var.TAGS_DEPIT}" } } #Storage Container resource "azurerm_storage_container" "vhds" { name = "${var.ARM_AZ_STGCONT_VHDS_NAME}" resource_group_name = "${azurerm_resource_group.RGName.name}" storage_account_name = "${azurerm_storage_account.azrmstgacc-stdssdlrs-001.name}" container_access_type = "private" }

The first deployment of a Windows Server 2016 data center is standard deployment with no extensions added. What has been included is the creation of a local administrator account that will be different for each created virtual machine. Included:
- local administrator account – example AccID45874 (Random generated code of 5 numbers). The result will be included in the outputs.tf
- Put the OS disk on the blob storage
- Not included data disk on the server
- Not included Public IP address to the server Network Interface Card
- Currently also not included is a second Network Interface Card towards the back-end subnet network
Create following files:
- compute.tf : configuration file that will be used to define the virtual machines
- outputs.tf : configuration file that will be used to define output variables that need to be displayed at the end of the Terraform apply – process.
- terraform.tfvars : configuration file that will contain variable that need to be excluded from the version repository (Github/DevOps)
Terraform.tfvars
The file “terraform.tfvars” would be loaded automatically to populate the variables defined.
############################ # Authentication variables # ############################ #example data: this data was generated in previous blog. ARM_CLIENT_ID="a00f125f-54ef-xxxx-a87t-f514ag2541s8" ARM_SUBSCRIPTION_ID="d1ge7156-xxxx-4d44-5f22-7vdg21d542s3" ARM_CLIENT_SECRET="d1ge7156-xxxx-4d44-5f22-7vdg21d542s3" ARM_TENANT_ID="d1ge7156-1g55-xxxx-5f22-7vdg21d542s3" ################## # Domain Account # ################## #example data ARM_VAR_LocalSrvAccSecret="<Password>" #The password that will be used by the local administrator account.
compute.tf
compute.tf – file will contain the information to deploy a virtual machine. Special will be here the random string that will be generated to create the local administrator account for the virtual machine. The string will be a numeric number of 5 numbers.
######################### # COMPUTE CONFIGURATION # ######################### /* Generated random admin account */ resource "random_string" "AccID" { length=5 special=false number=true min_numeric=5 } #Create local administrator Account ID - Example ID: AccID23756 locals { VarAccID="AccID-${random_string.AccID.result}" } #https://www.terraform.io/docs/providers/azurerm/r/network_interface.html resource "azurerm_network_interface" "nic" { name = "primaryNic${count.index}" location = "${azurerm_resource_group.RGName.location}" resource_group_name = "${azurerm_resource_group.RGName.name}" ip_configuration { name = "ipconfig00${count.index}" subnet_id = "${module.network.vnet_subnets[0]}" private_ip_address_allocation = "dynamic" } } #https://www.terraform.io/docs/providers/azurerm/r/virtual_machine.html resource "azurerm_virtual_machine" "InfrastructureServer" { name="dcsazprois002" resource_group_name="${azurerm_resource_group.RGName.name}" location="${azurerm_resource_group.RGName.location}" vm_size="Standard_B2s" #Information about the Virtual Machines Sizes: https://docs.microsoft.com/nl-be/azure/virtual-machines/windows/sizes-general network_interface_ids=["${azurerm_network_interface.nic.id}"] #Front-End Network os_profile_windows_config { provision_vm_agent=true timezone="Romance Standard Time" } #OS Image selection: https://docs.microsoft.com/en-us/azure/virtual-machines/windows/cli-ps-findimage storage_image_reference { publisher="MicrosoftWindowsServer" offer="WindowsServer" sku="2016-Datacenter" version="latest" } storage_os_disk { name = "dcsazprois002-os" vhd_uri = "${azurerm_storage_account.azrmstgacc-stdssdlrs-001.primary_blob_endpoint}${azurerm_storage_container.vhds.name}/dcsazprois002-os.vhd" caching = "ReadWrite" create_option = "FromImage" os_type = "Windows" } os_profile { computer_name = "dcsazprois002" admin_username = "${local.VarAccID}" admin_password = "${var.ARM_VAR_LocalSrvAccSecret}" } tags = { Environment = "${var.TAGS_ENVPRD}" EnvType = "${var.TAGS_EVNTYPE_COMPUTE}" Departement = "${var.TAGS_DEPIT}" } }
Used commands to Deploy the virtual machine, from virtual studio code.
Terraform init
Terraform plan
Terraform apply
After deployment has been completed you will see the machine appear in Azure.

Deploy Virtual Machine – with extensions
Deploying a second virtual machine with extra virtual machine extensions:
- BGInfo Extension
- Add server to Active Directory
- Add extra network card towards the back-end subnet (no extension, extra information how-to add a second Network Interface Card)
More information about extensions.
The setup will be the same as previous, but extensions will be added. Creating Storage, Network Interface card and virtual machine. After the virtual machine has been deployed the extensions will be added to the virtual machine. Following extensions will be added:
- BGInfo: This will display information about the virtual machine on the desktop background.
- Adding the VM to Active Directory – JsonADDomainExtension (You need to have an Active Directory server in Azure or a VPN connection to your on-premises infrastructure)
Extension BGInfo.

Getting the publisher, type and version can be found by using the following one liner in Azure Shell.
Get-AzureRmVmImagePublisher -Location “WestEurope” | `
Get-AzureRmVMExtensionImageType | `
Get-AzureRmVMExtensionImage | Select Type, Version
Or
Get-AzureRmVmImagePublisher -Location “WestEurope” | `
Get-AzureRmVMExtensionImageType | `
Get-AzureRmVMExtensionImage | where-object-property Type -EQ -value “BGInfo”
#Extension BGInfo resource "azurerm_virtual_machine_extension" "BGInfo" { name = "${local.VarSrvName}-BGInfo" location = "${azurerm_resource_group.RGName.location}" resource_group_name = "${azurerm_resource_group.RGName.name}" virtual_machine_name = "${azurerm_virtual_machine.IS01.name}" publisher = "Microsoft.Compute" type = "BGInfo" type_handler_version = "2.1" }
Extension add server to domain.
#Extension: add server to domain resource "azurerm_virtual_machine_extension" "ADD2AD" { name = "${local.VarSrvName}-ADD2AD" location = "${azurerm_resource_group.RGName.location}" resource_group_name = "${azurerm_resource_group.RGName.name}" virtual_machine_name = "${azurerm_virtual_machine.IS01.name}" publisher = "Microsoft.Compute" type = "JsonADDomainExtension" type_handler_version = "1.3" # What the settings means: https://docs.microsoft.com/en-us/windows/desktop/api/lmjoin/nf-lmjoin-netjoindomain settings = <<SETTINGS { "Name": "<NAME>", "OUPath": "OU=AZURE_SERVERS,DC=<DOMAIN>,DC=LOCAL", "User": "DOMAIN\\USERNAME", "Restart": "true", "Options": "3" } SETTINGS protected_settings = <<PROTECTED_SETTINGS { "Password": "<Password>" } PROTECTED_SETTINGS depends_on = ["azurerm_virtual_machine.IS01"] }
Server will be added to your active directory domain. Some pictures.

Next step will be calling custom PowerShell Scripts.
Terraform code for the virtual machine.
resource "random_string" "AccID2" { length=5 special=false number=true min_numeric=5 } #Create local administrator Account ID - Example ID: AccID23756 locals { VarAccID2="AccID-${random_string.AccID2.result}" VarSrvName="${var.ARM_AZ_TEMP_ISSRV}1" VarNIC-FE-Name="${local.VarSrvName}-NIC-FE" VarNIC-BE-Name="${local.VarSrvName}-NIC-BE" varIPNIC-FE-Name="${local.VarSrvName}-IPConf-FE" varIPNIC-BE-Name="${local.VarSrvName}-IPConf-BE" } resource "azurerm_network_interface" "NIC-FE" { name = "${local.VarNIC-FE-Name}" location = "${azurerm_resource_group.RGName.location}" resource_group_name = "${azurerm_resource_group.RGName.name}" ip_configuration { name = "${local.varIPNIC-FE-Name}" subnet_id = "${module.network.vnet_subnets[0]}" #[0] based on the network subnets order creation private_ip_address_allocation = "dynamic" } } resource "azurerm_network_interface" "NIC-BE" { name = "${local.VarNIC-BE-Name}" location = "${azurerm_resource_group.RGName.location}" resource_group_name = "${azurerm_resource_group.RGName.name}" ip_configuration { name = "${local.varIPNIC-BE-Name}" subnet_id = "${module.network.vnet_subnets[1]}" private_ip_address_allocation = "dynamic" } } #https://www.terraform.io/docs/providers/azurerm/r/virtual_machine.html resource "azurerm_virtual_machine" "IS01" { name="${local.VarSrvName}" resource_group_name="${azurerm_resource_group.RGName.name}" location="${azurerm_resource_group.RGName.location}" #Information about the Virtual Machines Sizes: https://docs.microsoft.com/nl-be/azure/virtual-machines/windows/sizes-general vm_size="Standard_B2s" network_interface_ids=["${azurerm_network_interface.NIC-FE.id}","${azurerm_network_interface.NIC-BE.id}"] #Front-End/Back-End Network primary_network_interface_id="${azurerm_network_interface.NIC-FE.id}" os_profile_windows_config { provision_vm_agent=true timezone="Romance Standard Time" } #OS Image selection: https://docs.microsoft.com/en-us/azure/virtual-machines/windows/cli-ps-findimage storage_image_reference { publisher="MicrosoftWindowsServer" offer="WindowsServer" sku="2016-Datacenter" version="latest" } storage_os_disk { name = "${local.VarSrvName}-os" vhd_uri = "${azurerm_storage_account.azrmstgacc-stdssdlrs-001.primary_blob_endpoint}${azurerm_storage_container.vhds.name}/${local.VarSrvName}-os.vhd" caching = "ReadWrite" create_option = "FromImage" os_type = "Windows" } os_profile { computer_name = "${local.VarSrvName}" admin_username = "${local.VarAccID}" admin_password = "${var.ARM_VAR_LocalSrvAccSecret}" } tags = { Environment = "${var.TAGS_ENVPRD}" EnvType = "${var.TAGS_EVNTYPE_COMPUTE}" Departement = "${var.TAGS_DEPIT}" } }