'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 1:[1]

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 = "[email protected]"
}

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 [email protected] and password.

enter image description here

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1