azureazure-active-directoryazure-virtual-machineterraform-provider-azureazure-availability-set

How to configure the VMs in two different zones using Availability Sets and install the Active Directory Domain services


I’m trying to create the two windows virtual machines in two different zones using the following terraform code:

## Import exisiting resource group
## Use this data source to access information about an existing Resource Group
data "azurerm_resource_group" "resource_group" {
  name = var.existing_rg_name
}

## Import exisiting virtual network
## Use this data source to access information about an existing Virtual Network.
data "azurerm_virtual_network" "virtual_network" {
  resource_group_name = var.existing_rg_name
  name                = var.existing_vnet_name
}
## Import exisiting subnet with in a virtual network
## Use this data source to access information about an existing Subnet within a Virtual Network.
data "azurerm_subnet" "subnet" {
  name                 = var.existing_subnet_name
  virtual_network_name = var.existing_vnet_name
  resource_group_name  = var.existing_rg_name
}

## Configure Availiablility set
resource "azurerm_availability_set" "availability_set" {
  name                         = var.avset_name
  resource_group_name          = data.azurerm_resource_group.resource_group.name
  location                     = data.azurerm_resource_group.resource_group.location
  platform_fault_domain_count  = 2
  platform_update_domain_count = 2
  managed                      = true
}

## Create Public IP
resource "azurerm_public_ip" "public_ip" {
  name                = var.pip_name
  resource_group_name = data.azurerm_resource_group.resource_group.name
  location            = data.azurerm_resource_group.resource_group.location
  allocation_method   = "Dynamic"
}

## Create network interface for VM
resource "azurerm_network_interface" "vm_nic" {
  name                = var.nic_name
  resource_group_name = data.azurerm_resource_group.resource_group.name
  location            = data.azurerm_resource_group.resource_group.location

  ip_configuration {
    name                          = "internal"
    subnet_id                     = data.azurerm_subnet.subnet.id
    private_ip_address_allocation = "Dynamic"
    public_ip_address_id          = azurerm_public_ip.public_ip.id
  }
}

## Create Windows Virtual Machine
resource "azurerm_windows_virtual_machine" "virtual_machine" {
  name                = var.vm_name
  resource_group_name = data.azurerm_resource_group.resource_group.name
  location            = data.azurerm_resource_group.resource_group.location
  size                = var.vm_size
  admin_username      = var.vm_username
  admin_password      = var.vm_password

  network_interface_ids = [
    azurerm_network_interface.vm_nic.id
  ]

  availability_set_id = azurerm_availability_set.availability_set.id

  os_disk {
    caching              = "ReadWrite"
    storage_account_type = "Standard_LRS"
  }

  source_image_reference {
    publisher = "MicrosoftWindowsServer"
    offer     = "WindowsServer"
    sku       = "2019-Datacenter"
    version   = "latest"
  }
  depends_on = [
    azurerm_network_interface.vm_nic
  ]
}

I want to configure the VMs in two different zones using Availability Sets and install the Active Directory Domain services using terraform.


Solution

  • You can use something like below to deploy 2 VM's and Create a new active directory forest in one and in other you can just add it to domain and promote both as Domain Controllers:

    Availability Set:

    Main.tf:

    provider "azurerm" {
      features{}
    }
    ## Import exisiting resource group
    ## Use this data source to access information about an existing Resource Group
    data "azurerm_resource_group" "resource_group" {
      name = "ansumantest"
    }
    
    ## Import exisiting virtual network
    ## Use this data source to access information about an existing Virtual Network.
    data "azurerm_virtual_network" "virtual_network" {
      resource_group_name = data.azurerm_resource_group.resource_group.name
      name                = "ansuman-vnet"
    }
    ## Import exisiting subnet with in a virtual network
    ## Use this data source to access information about an existing Subnet within a Virtual Network.
    data "azurerm_subnet" "subnet" {
      name                 = "default"
      virtual_network_name = data.azurerm_virtual_network.virtual_network.name
      resource_group_name  = data.azurerm_resource_group.resource_group.name
    }
    
    ## Configure Availiablility set
    resource "azurerm_availability_set" "availability_set" {
      name                         = "ansuman-avset"
      resource_group_name          = data.azurerm_resource_group.resource_group.name
      location                     = data.azurerm_virtual_network.virtual_network.location
      platform_fault_domain_count  = 2
      platform_update_domain_count = 2
      managed                      = true
    }
    
    ## Create 2 Public IP
    resource "azurerm_public_ip" "public_ip" {
      count = 2
      name                = "ansuman-pip-${count.index}"
      resource_group_name = data.azurerm_resource_group.resource_group.name
      location            = data.azurerm_virtual_network.virtual_network.location
      allocation_method   = "Dynamic"
    }
    #Static Private address to be used by the server each
    variable "PrivateIP" {
      default=["10.0.0.5","10.0.0.6"]
    }
    ## Create network interface for VM with adding the static Private IP's in the DNS server list
    resource "azurerm_network_interface" "vm_nic" {
      count = 2
      name                = "vm-${count.index}-nic"
      resource_group_name = data.azurerm_resource_group.resource_group.name
      location            = data.azurerm_virtual_network.virtual_network.location
      dns_servers                   = var.PrivateIP
    
      ip_configuration {
        name                          = "internal"
        subnet_id                     = data.azurerm_subnet.subnet.id
        private_ip_address_allocation = "Static"
        private_ip_address            = var.PrivateIP[count.index]
        public_ip_address_id          = azurerm_public_ip.public_ip[count.index].id
      }
    }
    
    ## Create 2 Windows Virtual Machine
    resource "azurerm_windows_virtual_machine" "virtual_machine" {
      count = 2
      name                = "AZDC-${count.index}"
      resource_group_name = data.azurerm_resource_group.resource_group.name
      location            = data.azurerm_virtual_network.virtual_network.location
      size                = "Standard_F8s_v2"
      admin_username      = "ansuman"
      admin_password      = "Password@1234"
    
      network_interface_ids = [
        azurerm_network_interface.vm_nic[count.index].id
      ]
    
      availability_set_id = azurerm_availability_set.availability_set.id
    
      os_disk {
        caching              = "ReadWrite"
        storage_account_type = "Standard_LRS"
      }
    
      source_image_reference {
        publisher = "MicrosoftWindowsServer"
        offer     = "WindowsServer"
        sku       = "2019-Datacenter"
        version   = "latest"
      }
      depends_on = [
        azurerm_network_interface.vm_nic
      ]
    }
    
    #Powershell commands to run the ADDS in the VM's
    locals { 
      import_command       = "Import-Module ADDSDeployment"
      password_command     = "$password = ConvertTo-SecureString ${var.admin_password} -AsPlainText -Force"
      credentials_command  = "$credentials = new-object -typename System.Management.Automation.PSCredential -argumentlist ${var.domainAdminUsername},$password"
      install_ad_command   = "Add-WindowsFeature -name ad-domain-services,dns -IncludeManagementTools"
      configure_ad_command = "Install-ADDSForest -CreateDnsDelegation:$false -DomainMode Win2012R2 -DomainName ${var.active_directory_domain} -DomainNetbiosName ${var.active_directory_netbios_name} -ForestMode Win2012R2 -InstallDns:$true -SafeModeAdministratorPassword $password -Force:$true"
      promote_adds_command = "Install-ADDSDomainController -DomainName ${var.active_directory_domain} -InstallDns -Credential $credentials -SafeModeAdministratorPassword $password -Force:$true"
      shutdown_command     = "shutdown -r -t 10"
      exit_code_hack       = "exit 0"
      powershell_command   = "${local.import_command}; ${local.password_command}; ${local.install_ad_command}; ${local.configure_ad_command}; ${local.shutdown_command}; ${local.exit_code_hack}"
      powershell_promote_command   = "${local.password_command};${local.credentials_command}; ${local.install_ad_command}; ${local.promote_adds_command}; ${local.shutdown_command}; ${local.exit_code_hack}"
    
    }
    
    #creating a forest and promoting the Primary server as a DC
    resource "azurerm_virtual_machine_extension" "create-active-directory-forest" {
      name                 = "create-active-directory-forest"
      virtual_machine_id =    azurerm_windows_virtual_machine.virtual_machine[0].id
      publisher            = "Microsoft.Compute"
      type                 = "CustomScriptExtension"
      type_handler_version = "1.9"
    
      settings = <<SETTINGS
        {
            "commandToExecute": "powershell.exe -Command \"${local.powershell_command}\""
        }
    SETTINGS
    }
    # Adding Secondary server to the Domain and promoting it as DC
    resource "azurerm_virtual_machine_extension" "promote-to-domain-controller" {
      name                 = "promote-to-domain-controller"
       virtual_machine_id = azurerm_windows_virtual_machine.virtual_machine[1].id
      publisher            = "Microsoft.Compute"
      type                 = "CustomScriptExtension"
      type_handler_version = "1.9"
    
      settings = <<SETTINGS
        {
            "commandToExecute": "powershell.exe -Command \"${local.powershell_promote_command}\""
        }
    SETTINGS
    
    depends_on = [
      azurerm_virtual_machine_extension.create-active-directory-forest
    ]
    }
    

    Variable.tf:

    variable "active_directory_domain" {
      description = "The name of the Active Directory domain, for example `consoto.local`"
      default = "contoso.local"
    }
    
    variable "admin_password" {
      description = "The password associated with the local administrator account on the virtual machine"
      default = "Password@1234"
    }
    
    variable "active_directory_netbios_name" {
      description = "The netbios name of the Active Directory domain, for example `consoto`"
      default = "Contoso"
    }
    
    variable "domainAdminUsername" {
        description = "The local administrator account on the Domain"
        default = "ansuman@contoso.local"
    }
    

    Output:

    enter image description here

    enter image description here

    enter image description here

    enter image description here

    enter image description here


    Availability Zone:

    main.tf

    provider "azurerm" {
      features{}
    }
    ## Import exisiting resource group
    ## Use this data source to access information about an existing Resource Group
    data "azurerm_resource_group" "resource_group" {
      name = "ansumantest"
    }
    
    ## Import exisiting virtual network
    ## Use this data source to access information about an existing Virtual Network.
    data "azurerm_virtual_network" "virtual_network" {
      resource_group_name = data.azurerm_resource_group.resource_group.name
      name                = "ansuman-vnet"
    }
    ## Import exisiting subnet with in a virtual network
    ## Use this data source to access information about an existing Subnet within a Virtual Network.
    data "azurerm_subnet" "subnet" {
      name                 = "default"
      virtual_network_name = data.azurerm_virtual_network.virtual_network.name
      resource_group_name  = data.azurerm_resource_group.resource_group.name
    }
    
    ##availabilty zones
    variable "Zone" {
      default=["1","2"]
    }
    
    resource "azurerm_network_security_group" "example" {
      name                = "ansuman-nsg"
      location            = data.azurerm_virtual_network.virtual_network.location
      resource_group_name = data.azurerm_resource_group.resource_group.name
    
      security_rule {
        name                       = "test123"
        priority                   = 100
        direction                  = "Inbound"
        access                     = "Allow"
        protocol                   = "Tcp"
        source_port_range          = "*"
        destination_port_range     = "*"
        source_address_prefix      = "*"
        destination_address_prefix = "*"
      }
    }
    
    resource "azurerm_subnet_network_security_group_association" "example" {
      subnet_id                 = data.azurerm_subnet.subnet.id
      network_security_group_id = azurerm_network_security_group.example.id
    }
    ## Create 2 Public IP
    resource "azurerm_public_ip" "public_ip" {
      count = 2
      name                = "ansuman-pip-${count.index}"
      sku                 = "Standard"
      availability_zone   = var.Zone[count.index]
      resource_group_name = data.azurerm_resource_group.resource_group.name
      location            = data.azurerm_virtual_network.virtual_network.location
      allocation_method   = "Static"
    }
    #Static Private address to be used by the server each
    variable "PrivateIP" {
      default=["10.0.0.5","10.0.0.6"]
    }
    ## Create network interface for VM with adding the static Private IP's in the DNS server list
    resource "azurerm_network_interface" "vm_nic" {
      count = 2
      name                = "vm-${count.index}-nic"
      resource_group_name = data.azurerm_resource_group.resource_group.name
      location            = data.azurerm_virtual_network.virtual_network.location
      dns_servers         = var.PrivateIP
    
      ip_configuration {
        name                          = "internal"
        subnet_id                     = data.azurerm_subnet.subnet.id
        private_ip_address_allocation = "Static"
        private_ip_address            = var.PrivateIP[count.index]
        public_ip_address_id          = azurerm_public_ip.public_ip[count.index].id
      }
    }
    
    ## Create 2 Windows Virtual Machine
    resource "azurerm_windows_virtual_machine" "virtual_machine" {
      count = 2
      name                = "AZDC-${count.index}"
      resource_group_name = data.azurerm_resource_group.resource_group.name
      location            = data.azurerm_virtual_network.virtual_network.location
      size                = "Standard_F8s_v2"
      admin_username      = "ansuman"
      admin_password      = "Password@1234"
      zone                = var.Zone[count.index]
    
      network_interface_ids = [
        azurerm_network_interface.vm_nic[count.index].id
      ]
    
      os_disk {
        caching              = "ReadWrite"
        storage_account_type = "Standard_LRS"
      }
    
      source_image_reference {
        publisher = "MicrosoftWindowsServer"
        offer     = "WindowsServer"
        sku       = "2019-Datacenter"
        version   = "latest"
      }
      depends_on = [
        azurerm_network_interface.vm_nic
      ]
    }
    
    #Powershell commands to run the ADDS in the VM's
    locals { 
      import_command       = "Import-Module ADDSDeployment"
      password_command     = "$password = ConvertTo-SecureString ${var.admin_password} -AsPlainText -Force"
      credentials_command  = "$credentials = new-object -typename System.Management.Automation.PSCredential -argumentlist ${var.domainAdminUsername},$password"
      install_ad_command   = "Add-WindowsFeature -name ad-domain-services,dns -IncludeManagementTools"
      configure_ad_command = "Install-ADDSForest -CreateDnsDelegation:$false -DomainMode Win2012R2 -DomainName ${var.active_directory_domain} -DomainNetbiosName ${var.active_directory_netbios_name} -ForestMode Win2012R2 -InstallDns:$true -SafeModeAdministratorPassword $password -Force:$true"
      promote_adds_command = "Install-ADDSDomainController -DomainName ${var.active_directory_domain} -InstallDns -Credential $credentials -SafeModeAdministratorPassword $password -Force:$true"
      shutdown_command     = "shutdown -r -t 10"
      exit_code_hack       = "exit 0"
      powershell_command   = "${local.import_command}; ${local.password_command}; ${local.install_ad_command}; ${local.configure_ad_command}; ${local.shutdown_command}; ${local.exit_code_hack}"
      powershell_promote_command   = "${local.password_command};${local.credentials_command}; ${local.install_ad_command}; ${local.promote_adds_command}; ${local.shutdown_command}; ${local.exit_code_hack}"
    
    }
    
    #creating a forest and promoting the Primary server as a DC
    resource "azurerm_virtual_machine_extension" "create-active-directory-forest" {
      name                 = "create-active-directory-forest"
      virtual_machine_id =    azurerm_windows_virtual_machine.virtual_machine[0].id
      publisher            = "Microsoft.Compute"
      type                 = "CustomScriptExtension"
      type_handler_version = "1.9"
    
      settings = <<SETTINGS
        {
            "commandToExecute": "powershell.exe -Command \"${local.powershell_command}\""
        }
    SETTINGS
    }
    # Adding Secondary server to the Domain and promoting it as DC
    resource "azurerm_virtual_machine_extension" "promote-to-domain-controller" {
      name                 = "promote-to-domain-controller"
       virtual_machine_id = azurerm_windows_virtual_machine.virtual_machine[1].id
      publisher            = "Microsoft.Compute"
      type                 = "CustomScriptExtension"
      type_handler_version = "1.9"
    
      settings = <<SETTINGS
        {
            "commandToExecute": "powershell.exe -Command \"${local.powershell_promote_command}\""
        }
    SETTINGS
    
    depends_on = [
      azurerm_virtual_machine_extension.create-active-directory-forest
    ]
    }
    

    Note: Availability Set and Availability Zones cannot be configured together. It can be either or , If you want to use Zone then Set cannot be used. You can also refer this Microsoft Community Blog for more details.

    Output:

    enter image description here

    enter image description here

    enter image description here

    For testing login to the secondary server using your domain admin username i.e. in my case ansuman@consto.local and password.

    enter image description here