0

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.

Pradeep
  • 5,101
  • 14
  • 68
  • 140
  • hello @Pradeep , may i know if you are try to do the same mentioned in this [document](https://learn.microsoft.com/en-us/windows-server/identity/ad-ds/deploy/virtual-dc/adds-on-azure-vm) using terraform – Ansuman Bal Dec 14 '21 at 11:37
  • Yes @AnsumanBal-MT, I have gone through the above documentation. But need to install manually using Azure CLI. But I want to do it using terraform. Is it possible? – Pradeep Dec 14 '21 at 11:49
  • yes , its possible .. so let me get it clear , in 1st vm we will create new forest and promote the vm as domain controller and 2nd one we will just add to existing forest right ? – Ansuman Bal Dec 14 '21 at 11:58
  • Yes @AnsumanBal-MT, I want to install Active Directory services in both virtual machines. – Pradeep Dec 14 '21 at 14:34

1 Answers1

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 = "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

Ansuman Bal
  • 9,705
  • 2
  • 10
  • 27
  • Thanks for your answer. But I want to create two virtual machines in two different zones. if possible please update your answer. – Pradeep Dec 15 '21 at 01:58
  • I didn't get any errors with the terraform code. But I didn't see the domain name in the second virtual machine. Is there any manual operation to do it? – Pradeep Dec 15 '21 at 03:32
  • No, we don't need to perform any manual operation . To verify if the second vm is added to domain or not you login through domain of like "ansuman@contoso.local" – Ansuman Bal Dec 15 '21 at 03:41
  • @Pradeep, may I know if you are talking about availability zones? Or you want to create 2 vms in whole different regions ? – Ansuman Bal Dec 15 '21 at 03:42
  • 1
    I'm talking about availability zones. – Pradeep Dec 15 '21 at 06:00
  • @Pradeep , availability set does the same and has higher availabilty than zones you can refer this [MS Document](https://techcommunity.microsoft.com/t5/itops-talk-blog/understanding-availability-sets-and-availability-zones/ba-p/1992518) , but if you still need zone instead of set then you will need to add availabilty zone in public ip and zone in vm instead of creating availability set .. let me test it out and update the answer – Ansuman Bal Dec 15 '21 at 06:48
  • Thanks @Ansuman for your help. – Pradeep Dec 15 '21 at 10:25