Building Highly-Available Windows Infrastructure: Command-line Style. AD DS. Part 1 — Installation


Up to this day, Active Directory Domain Services (AD DS) has been the core of the Windows infrastructure. With each release of Windows Server, AD DS receives new features while keeping great backward compatibility. Windows Server 2016 brings following enhancements to AD DS:

In this blog we shall install the corner stone of our future infrastructure: a highly-available AD DS instance of two domain controllers. Our AD DS layout is going to be quite simple: two writable domain controllers in a single site.

Variables used in this article:
$DomainFQDN = ''
$DomainNBName = 'LAB'
$DC1Name = 'DC1'
$DC2Name = 'DC2'
$DC1IPAddress = ''
$DC2IPAddress = ''
$DefaultGWIPAddress = ''
$SubNetPrefixLen = 24
$NICName = 'Ethernet'
$TempDirectoryName = 'Temp'
$TempDirectoryPath = $env:SystemDrive
$SSUFileName = 'windows10.0-kb3211320-x64_2abc94fceb4d1cdd908b3bdba473e28e0c061a3d.msu'
$CUFileName = 'windows10.0-kb4010672-x64_e12a6da8744518197757d978764b6275f9508692.msu'


Even if the rest of your infrastructure is not highly available, it is recommended to always install at least two writable AD DS controllers, due to their critical nature for the infrastructure. In case of a single domain controller, its maintenance will interrupt almost every service in a domain. See more about it here:


I’m going to install domain controllers manually, w/o using unattended options. Probably, we’ll talk about automating Windows installation in later posts.

First DC Installation

OS Installation

Grab the latest available Windows Server 2016 ISO image and boot the machine, which will be our first domain controller. When choosing an OS edition to install, be sure to choose the option w/o “Desktop Experience” in the name:

After the usual installation steps, a text-based logon screen appears:

Set Administrator’s password and continue:

Save this password in a secure location — later it will become the password of the built-in Active Directory “Administrator” account — you will need to login with it at least once.


Always use strong passwords for administrative accounts. In general, it means to use randomly generated unique passwords at least 15 characters long (to avoid LM hashes creation). Of course it is nearly impossible to remember such passwords, therefore it is considered to be a best practice to store them in password managers like KeePass or CyberArk Enterprise Password Vault. See more on this topic here and here.
Another approach would be to create pass-phrases. You can see an example of one here:


Each logon session in Server Core starts with a cmd.exe instance. Run PowerShell inside that cmd.exe instance by typing powershell. I shall start each logon session with this command, but shall not mention it anymore, implying this by default.

First thing to do, is to choose a name for our first domain controller. As mentioned in the variable block above, I have chosen “DC1”, but you should use the one which suits your environment best.
Let’s rename DC1 and, since renaming requires a restart, immediately restart the server:
Rename-Computer $DC1Name; Restart-Computer

After the reboot, we are presented with a fresh logon session: login and run powershell first, remember?

Network Configuration

To make a server available through a network, one should configure its network interface. To see which interfaces DC1 has, run Get-NetAdapter cmdlet:

Usually, Hyper-V virtual machines have a single network interface named ‘Ethernet’. Configure IP address on the network interface according to your subnet settings:

Let’s test the network configuration. Try to execute the following from another Windows host in the same subnet:

Note that the cmdlet has returned “False” in “PingSucceeded” property. If you are sure, that you have configured network interface correctly, usually it means that the Windows Firewall is enabled on the host, which is, generally, a good thing, but challenges us with correct exclusions to setup.


Never disable Windows Firewall completely, but manage it with accurately crafted exceptions instead. Disabling it, you not only make your hosts susceptible to network attacks, but also put your system in an unsupported state: Microsoft tests all their products and updates with Windows Firewall enabled.


To allow incoming ICMP packets (“pings”) on the host, run the following:
Set-NetFirewallRule -Name 'FPS-ICMP4-ERQ-In' -Enabled True

Then, execute the test again – it should succeed this time:

Enable Remote Management

Up to this moment we were working locally on the server – this is quite inconvenient. But remote administration via Remote Desktop is disabled by default, therefore, we must enable it manually. First, let’s ensure that the RDP is disabled indeed:

Yep, that “1” in the fDenyTSConnections’s value means that the server will decline our attempts to connect to it using Remote Desktop Protocol. To enable Remote Desktop, you need to modify the registry directly (this is a supported way):
Set-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server\' -Name 'fDenyTSConnections' -Value 0

And do not forget about Windows Firewall (we shall automate these settings via Group Policies later):


OK, we have remote access now. Are we ready to install roles and features on the server? Not quite: We need to install the latest available updates first.
Download the latest Windows Server 2016 CU using this KB article. Before you install the CU, it is also recommended to install the latest Servicing Stack update: There is no article with Servicing Stack updates history, but one can use Google to find the latest. At the moment of writing the article, the latest is KB3211320.


Always install the latest available updates when you spin up a new instance of any software:

  1. It protects you against known security flaws in the software.
  2. Sometimes software contains errors which appear at installation and configuration stages.


I assume that you have download updates to your workstation. Therefore we can deliver them to the server using SMB:

  1. Create a firewall exception at the DC1:
    Set-NetFirewallRule -Name 'FPS-SMB-In-TCP' -Enabled True
  2. At the server, create a temporary directory and copy the update files into it:
    $TempDirectory = New-Item -Type Directory -Name $TempDirectoryName -Path (Join-Path -Path $TempDirectoryPath -ChildPath '\')
  3. Copy the update files using a tool of your choice.
  4. Install the updates:
    Start-Process -FilePath 'wusa' -ArgumentList "$(Join-Path -Path ($TempDirectory.FullName) -ChildPath $SSUFileName) /quiet" -Wait;Start-Process -FilePath 'wusa' -ArgumentList "$(Join-Path -Path ($TempDirectory.FullName) -ChildPath $CUFileName) /quiet" -Wait

The system will be automatically rebooted. After the reboot, you can safely delete the update files.


After the reboot, set the time zone, if needed.
Use Get-TimeZone -ListAvailable command to find an appropriate one, then set it using Set-TimeZone cmdlet:
Set-TimeZone -Id 'Russian Standard Time'

Make sure the date and time are correct, using Get-Date cmdlet.

Now let’s remove some unnecessary components:
Remove-WindowsFeature FS-SMB1, WoW64-Support; Restart-Computer

You may learn why to remove SMB1 here.
Since we do not plan running any 32-bit applications on the DCs, we can safely delete the WoW64-subsystem.

AD DS Installation

We are ready to install Active Directory Domain Services. Finally!

After the role bits have been installed, we can create a new AD DS forest and promote the server to a domain controller:

The password in SafeModeAdministratorPassword parameter will be used shall the system lose a connection to AD DS database. Store it in a secure location too.


Use the same password generation best practices, as discussed earlier. Do not re-use the password used at the server installation step – that password will be used for the default domain Administrator account.


Second DC Installation

Repeat all steps up to the features installation, but using $DC2IPAddress and $DC2Name variables instead of $DC1IPAddress and $DC1Name.

AD DS Installation

When joining an additional controller to a domain, that controller should be able to resolve the domain’s name (FQDN). We achieve that by configuring its network interface as follows:
Set-DnsClientServerAddress -InterfaceAlias $NICName -ServerAddresses $DC1IPAddress

Next, promote the second DC. The system will be restarted automatically:
Install-ADDSDomainController -DomainName $DomainFQDN -Credential (Get-Credential) -Confirm:$false


Be careful when you enter the user name: If you do not include the domain name here, the promotion process will stuck. Learn how to deal with such situations here.


Finalize DNS configuration

As the last step, we must correctly configure DNS-servers at the network interfaces: DC1 will use DC2 as a primary DNS-server and vice versa.
At DC1 execute:
Set-DnsClientServerAddress -InterfaceAlias $NICName -ServerAddresses @($DC2IPAddress,'')

At DC2 execute:
Set-DnsClientServerAddress -InterfaceAlias $NICName -ServerAddresses @($DC1IPAddress,'')


Why to include Learn best practices for NS addresses configuration at DC’s NIC here!


Voilà! Now we have two fully working AD DS domain controllers and we’ve achieved this using nothing more but PowerShell. Stay tuned for next posts in the series!

3 thoughts on “Building Highly-Available Windows Infrastructure: Command-line Style. AD DS. Part 1 — Installation”

    1. Hi Azat! Thank you for your suggestion — I have reviewed Packer and I see that it is a tool for OS images creation, specifically VM images, which effectively prevent one from using it for bare-metal deployments (correct me if I’m wrong). But in the process of preparing an image and after you deploy it, you need to (pre-)configure the system somehow — that’s, basically, what I discuss in this series. The first installation step in this particular post is for illustration purposes only — you are welcome to deploy OS using your favorite tool/method.
      Actually, I have plans to discuss OS installation automation in later blogs. Stay tuned!

Leave a Reply

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