KVM: creating a Windows2019 Domain Controller using Powershell

Based on my previous article on creating a Windows2019 base template using Sysprep, we can now move on to creating a Windows Domain Controller using this template as a backing file.

Once this new Windows 2019 guest OS is provisioned, we will prepare it and then run Powershell scripts that will enable the Active Directory features and create a new AD forest and DNS server.

This guest VM is located on the default KVM 192.168.122.0/24 NAT network, so it is only available from our Host machine and other VMs on its subnet.

Prerequisites

For this article, there is a strict prerequisite for having implemented the steps in my previous article where we create a Windows2019 base template that goes through sysprep.

Create Domain Controller child disk

First, we need to create a child disk, that is backed by our Win2019 base that has gone through sysprep.

# get full path to base disk
fullpath=$(virsh dumpxml win2k19base | grep -Po "<source file=\K.*'" | tr -d "'")

# go into directory
cd $(dirname $fullpath)

# create new child disk, with original as backing
sudo qemu-img create -f qcow2 -F qcow2 -b $(basename $fullpath) win2k19-dc1.qcow2

# validate size of new child disk (will be less than 1Mb)
sudo qemu-img info win2k19-dc1.qcow2 --force-share

Create Domain Controller guest OS

Using this new child disk, we can create the new “win2k19-dc1” guest VM.

virt-install --name=win2k19-dc1 \
--os-type=windows --os-variant=win2k19 \
--cpu host-passthrough \
--ram 2048 --vcpus=1 \
--virt-type=kvm --hvm \
--network network=default,model=e1000 \
--graphics vnc --video=qxl \
--disk $(dirname $fullpath)/win2k19-dc1.qcow2 \
--boot=hd --noautoconsole --force

The Windows logo will show as the new Guest OS goes through the steps detailed in the Sysprep unattend.xml that you prepared in the earlier section.

Login using “Administrator” and the password specified in the original unattend.xml file (ThisIsMyP4ss!).

Prepare for creating Domain

Before we make this host a Domain Controller, we need to prepare the host.  Open a command console as Administrator, and start Powershell.

cd c:\users\Administrator\Downloads
powershell -executionpolicy bypass

# get required scripts
$project="https://raw.githubusercontent.com/fabianlee/blogcode/master/windows"
Invoke-WebRequest $project/make-ip-static.ps1 -OutFile make-ip-static.ps1
Invoke-WebRequest $project/create-ca-and-cert.ps1 -OutFile create-ca-and-cert.ps1
Invoke-WebRequest $project/install-dc-2019.ps1 -OutFile install-dc-2019.ps1
Invoke-WebRequest $project/create-test-users-2019.ps1 -OutFile create-test-users-2019.ps1

Rename host

The host has an arbitrary name when first deployed, name it “win2k19-dc1”.

# rename
rename-computer win2k19-dc1

Set Network to static IP

Domain Controllers should not use dynamic IP, so if we are assigned a dynamic IP address, make it static.

./make-ip-static.ps1

Create SAN certificates

# create CA and SAN certificate for DC and future ADFS server
.\create-ca-and-certs.ps1 myCA 'win2k19-dc1.fabian.lee,win2k19-dc1.FABIAN.LEE,win2k19-adfs1.fabian.lee'

# show certificates created on file system
ls c:\certs

# show same certificates loaded into certificate store
get-ChildItem -Path 'Cert:\LocalMachine\Root' | where-object { $_.Subject -eq 'CN=myCA' }
get-ChildItem -Path 'Cert:\LocalMachine\My'

Reboot

For the hostname change to take affect, reboot.

restart-computer

Create the Active Directory domain

When the guest comes back up, log back in, go to the console again, and start powershell just as before so we can continue with the install-dc-2019.ps1 script.

cd c:\users\Administrator\Downloads
powershell -executionpolicy bypass

# add required features, then create Domain
.\install-dc-2019.ps1

After the domain is created, the system will reboot itself.  Log back in as “Administrator” and go to powershell again.

cd c:\users\Administrator\Downloads
powershell -executionpolicy bypass

# check AD domain
Get-ADDomain -current LoggedOnUser

# check AD DNS upstream
Get-DNSServerForwarder

# check local DNS resolution
nslookup win2k19-dc1 127.0.0.1

# check public DNS reslution
nslookup google.com

Create test AD users and groups

To create a suite of test users, you can use the create-test-users-2019.ps1 script.

.\create-test-users-2019.ps1

This will create the following users all with the default password ‘ThisIsMyP4ss!’ if you do not override with a parameter value.

  • myadmin, groups=Administrators; for alternate administration
  • jdoe, groups=Backup Operators; able to login via console
  • adfs1; use later as ADFS service account
  • ldap1; use for any LDAP client integrations
  • engineer1, groups=engineers
  • engineer2, groups=engineers
  • manager1, groups=managers

Validate Active Directory connection from inside guest

You can validate a secure LDAP connection over port 636 from inside the guest VM Domain Controller.

Download Microsoft sysinternals ADExplorer.  Unzip it and run “ADExplorer.exe”.

Select File > Connect to pull up the connection dialog and specify the Domain Controller name and secure port “win2k19-dc1:636”.  Use the ‘ldap1’ credentials, and press OK.

This secure connection is possible because the myCA root certificate is loaded into the local computer root certificate store.  We could have made a connection to the insecure port 389 without the root cert, but TLS over port 636 is only possible when the custom root CA certificate is loaded.

Clicking into “CN=Users,DC=FABIAN,DC=LEE”, shows the list of users.

Validate Active Directory connection from parent Host

Let’s step out of the guest OS and back to the parent KVM Host.  We should also be able to use the secure LDAP connection from the host as well.

Host DNS resolution

You will need to either add an entry to your local hosts file, modify your local dnsmasq.conf, or add an entry on your upstream DNS so that name resolution works.

Let’s assume the simplest case, where we add a line to the local /etc/hosts file.

# add IP address of DC, tailor to your environment
echo "192.168.122.4 win2k19-dc1.fabian.lee" | sudo tee -a /etc/hosts

# test lookup
# if using dnsmasq.conf, may need to add 'local=/fabian.lee/'
nslookup win2k19-dc1.fabian.lee

# validate ports are available
nc -vz win2k19-dc1.fabian.lee {389,636}

Test  LDAP connection

First, we will test the insecure LDAP connection on port 389 to get a list of all users under “CN=Users”.

ldapsearch -LLL -H ldap://win2k19-dc1.fabian.lee:389 -D ldap1@fabian.lee -w "ThisIsMyP4ss!" -b CN=Users,DC=FABIAN,DC=LEE -s sub | grep ^dn

If this works, then we will move on to testing ‘ldaps’ on port 636 which is a secure port.  But we need to copy over the custom root myCA.crt that is on the domain controller at c:\certs\myCA.crt.  And actually, it is in DER format, so we also need to covert it to PEM so it can be used by ldapsearch.

# copy myCA.crt from Domain Controller, convert to PEM
openssl x509 -in myCA.crt -inform DER -out myCA.pem

# environment variable to specify custom CA
LDAPTLS_CACERT=myCA.pem ldapsearch -LLL -H ldaps://win2k19-dc1.fabian.lee:636 -D ldap1@fabian.lee -w "ThisIsMyP4ss!" -b CN=Users,DC=FABIAN,DC=LEE -s sub | grep ^dn

You can copy the root CA certificate from the guest Domain Controller to the Host in any way you choose.  Newer Windows versions like 2019 have ssh.exe and scp.exe clients that can be used to communicate back to your Linux based Host.

If you would like to check if the leaf certificate returned from the secure port is the expected “win2k19-dc1”, you can use openssl as shown below to see the Subject and SAN names.

echo | openssl s_client -showcerts -servername win2k19-dc1.fabian.lee -connect win2k19-dc1.fabian.lee:636 2>/dev/null | sed -ne '/-BEGIN CERTIFICATE-/,/-END CERTIFICATE-/p' | openssl x509 -in - -text -noout | grep -e Subject -e DNS -e After

 

REFERENCES

microsoft, step-by-step installing 2019 DC with powershell

petri.com, install 2019 DC with powershell

chetan naik, AD DS with powershell shows how to add DNS

techexpert.tips, use powershell to set static IP

microsoft, set-netipaddress

microsoft, install-ADDSForest

microsoft, set-dnsserverforwarder

fabianlee.org, KVM backing file and linked clones

microsoft, AD Explorer GUI

NOTES

Windows license expiration and renewal

This article uses a trial ISO for Windows 2019.  If the licensing watermark as shown in the bottom right of the screen is expired, make sure to run “slmgr.vbs /rearm” and reboot to give it another 180 grace period or your machine will shutdown after about 30 minutes.