How to deploy domain controllers with Terraform

Hi everyone,

Welcome to another deploying Azure resources with Terraform blogpost. As you all know the support for Server 2012 and Server 2012 R2 is stopping. Because of this a lot of companies need to deploy new domain controllers. In this blogpost I’ll show you the deployment of 2 domain controllers in West Europe with the best practices from Microsoft.

Prerequisites

  • Log Analytics Workspace for diagnostic settings
  • storage account for boot diagnostics
  • Identity subnet in the hub virtual network with 3 ip’s
  • Azure Key Vault with local admin secret

Before we can start deploying the domain controllers we need to add some data sources for the prerequisites.

data "azurerm_log_analytics_workspace" "law" {
  name = "law-hub-${var.prefix}-01"
  resource_group_name = "rg-hub-${var.prefix}-management-01" 
}
data "azurerm_virtual_network" "hub" {
   provider = azurerm.hub
  name = "vnet-hub-${var.prefix}-we-01"
  resource_group_name = "rg-hub-${var.prefix}-networking-01"
}
data "azurerm_subnet" "identity" {
  provider = azurerm.hub
  name = "snet-hub-${var.prefix}-identity-01"
  virtual_network_name = data.azurerm_virtual_network.hub.name
  resource_group_name = data.azurerm_virtual_network.hub.resource_group_name
}
data "azurerm_storage_account" "bootdiagdc" {
  provider = azurerm.hub
  name = "sthub${var.prefix}bootdiag01"
  resource_group_name = "rg-hub-${var.prefix}-storage-01" 
}

data "azurerm_resource_group" "rg-dc" {
  name = "rg-hub-${var.prefix}-dc-01"
}
data "azurerm_key_vault" "kv-hub" {
  name = "kv-hub-${var.prefix}-80"
  resource_group_name = "rg-hub-${var.prefix}-management-01"
  
}
data "azurerm_key_vault_secret" "loc-admin" {
  name = "loc-admin"
  key_vault_id = data.azurerm_key_vault.kv-hub.id
}

Now that we have the prerequisites it’s time to start with the code for the virtual machines.

I want to highlight a couple of pieces of code for these domain controllers.

  • vm is added to an availability set for redundancy
  • System assigned identity is added for security
  • Secure boot and vtpm are enabled
  • Provisioning of the vm agent
resource "azurerm_windows_virtual_machine" "dc1" {
  provider = azurerm.hub
  name = "vm-${var.prefix}-dc-01"
  resource_group_name = data.azurerm_resource_group.rg-dc.name
  location = data.azurerm_resource_group.rg-dc.location
  network_interface_ids = [azurerm_network_interface.nic-dc-01.id]
  size = "Standard_F2s_v2"
  admin_username = data.azurerm_key_vault_secret.loc-admin.name
  admin_password = data.azurerm_key_vault_secret.loc-admin.value
  patch_mode = "Manual"
   identity {
    type = "SystemAssigned"
  }
  secure_boot_enabled = true
  vtpm_enabled = true
  provision_vm_agent = true
  os_disk {
    name = "c-vm-${var.prefix}-dc-01"
    caching = "ReadWrite"
    storage_account_type = "Premium_LRS"
    disk_size_gb = 64
    
  }
  source_image_reference {
    publisher = "MicrosoftWindowsServer"
    offer     = "WindowsServer"
    sku       = "2022-Datacenter-smalldisk"
    version   = "latest"
  }
  tags = {
    "Critical"    = "Yes"
    "Solution"    = "Domain Controller"
    "Costcenter"  = "IT"
    "Environment" = "Hub"
  } 
  availability_set_id = azurerm_availability_set.avs-dc.id
  boot_diagnostics {
    storage_account_uri = data.azurerm_storage_account.bootdiagdc.primary_blob_endpoint
  }

}

It is recommended by Microsoft not to put the ntds on the c drive of your domain controller. For this reason I add an extra disk and turn off the caching on the disk.

resource "azurerm_managed_disk" "dc1-datadisk" {
  name = "f-vm-${var.prefix}-dc-01"
  resource_group_name = azurerm_windows_virtual_machine.dc1.resource_group_name
  location = data.azurerm_resource_group.rg-dc.location
  storage_account_type = "Premium_LRS"
  disk_size_gb = 32
  create_option = "Empty"
}
resource "azurerm_virtual_machine_data_disk_attachment" "dc1-datadisk-attach" {
  managed_disk_id    = azurerm_managed_disk.dc1-datadisk.id
  virtual_machine_id = azurerm_windows_virtual_machine.dc1.id
  lun                = "1"
  caching            = "None"
}

The last resource that we need is the network interface. when deploying the nic the allocation type will be dynamic. Don’t forget to change it to static.

The allocation type of the nic won’t be visible when promoting the machine to domain controller.

resource "azurerm_network_interface" "nic-dc-01" {
  provider = azurerm.hub
    name = "nic-01-vm-${var.prefix}-dc-01"
    resource_group_name = data.azurerm_resource_group.rg-dc.name
    location = var.location
    ip_configuration {
      name = "sip-vm-${var.prefix}-dc-01"
      subnet_id = data.azurerm_subnet.identity.id
      private_ip_address_allocation = "Dynamic"
    } 
      tags = {
    "Critical"    = "Yes"
    "Solution"    = "Network interface"
    "Costcenter"  = "IT"
    "Environment" = "Hub"
  } 
}
resource "azurerm_monitor_diagnostic_setting" "diag-nic1" {
  name = "diag-nic"
  target_resource_id = azurerm_network_interface.nic-dc-01.id
  log_analytics_workspace_id = data.azurerm_log_analytics_workspace.law.id
  metric {
    category = "AllMetrics"

    retention_policy {
      enabled = true
    }
  }
}

After the deployment you can choose to enable Azure Disk Encryption on the domain controllers. I already a blog post about this. You can read it here.

If you want to use the code, you can find it on my Github here.

I hope this blog post will help you to deploy domain controllers in Azure. If you have any questions about this, feel free to contact me.

Author

  • Johan Vanneuville is a Microsoft MVP for Microsoft Enterprise Mobility from Belgium and he is one of the first expert contributors in this community. Make sure to follow him as he guides us in the world of Azure Virtual Desktop, Infrastructure-as-Code and Terraform!

    View all posts

Leave a Reply

Your email address will not be published. Required fields are marked *