Windows Server 2025 Core Domain Controller Migration

This runbook covers the migration from home-dc01 (Windows Server 2022 with AD + AD CS + DNS) to a new Windows Server 2025 Core deployment with AD DS + DNS (no AD CS - PKI migrated to Vault).

Architecture Change

  • DNS (client resolution): VyOS (10.50.1.1) or BIND (10.50.1.90)

  • DNS (AD-integrated zones): Windows DC (for SRV records: _ldap._tcp, _kerberos._tcp)

  • PKI: HashiCorp Vault (DOMUS-ROOT-CA) - replaces HOME-ROOT-CA

  • AD: Remains on Windows Server (this runbook)

  • AD CS: DEPRECATED - HOME-ROOT-CA decommissioned, Vault PKI is authoritative

1. Pre-Migration Checklist

Step Verification Status

DNS on BIND

All clients resolving via 10.50.1.90

[ ]

Vault PKI operational

vault secrets list shows pki/ and pki_int/

[ ]

Vault issuing certs

Test cert issued from domus-server role

[ ]

ISE trusts Vault CA

DOMUS-ROOT-CA in ISE trusted store

[ ]

AD replication healthy

repadmin /replsummary shows no errors

[ ]

Current DC backed up

Full system state backup completed

[ ]

2. Architecture

2.1. Current State (home-dc01)

Component Configuration

Hostname

home-dc01

OS

Windows Server 2022 Standard (Desktop Experience)

IP Address

10.50.1.50

Roles

AD DS, AD CS (HOME-ROOT-CA), DNS

Domain

inside.domusdigitalis.dev

2.2. Target State (home-dc02)

Component Configuration

Hostname

home-dc02

OS

Windows Server 2025 Standard (Core)

IP Address

10.50.1.51

Roles

AD DS + DNS (no AD CS)

Domain

inside.domusdigitalis.dev

3. Phase 0: NTP/Time Synchronization (CRITICAL)

Kerberos requires clock skew < 5 minutes between DC and clients. Without proper NTP configuration, AD joins and authentication will fail with LW_ERROR_CLOCK_SKEW (40087).

3.1. 0.1 Configure DC as NTP Client

The DC must sync time from VyOS (or another reliable NTP source). On home-dc01 PowerShell:

Check current NTP status:

w32tm /query /status

If you see The computer did not resync because no time data was available, NTP is not configured.

Configure NTP to sync from VyOS:

w32tm /config /manualpeerlist:"10.50.1.1" /syncfromflags:manual /reliable:yes /update
Restart-Service w32time
w32tm /resync /force

Verify sync:

w32tm /query /status
Get-Date
Expected output (synchronized)
Leap Indicator: 0(no warning)
Stratum: 4
...
Source: 10.50.1.1

3.2. 0.2 Verify Client Time Sync

From ISE or Linux client, verify time matches DC:

ISE:

show clock
show ntp

Linux:

date
timedatectl status

All systems should be within a few seconds of each other.

3.3. 0.3 Troubleshooting Clock Skew

If AD join fails with LW_ERROR_CLOCK_SKEW:

  1. Check DC time: Get-Date on DC

  2. Check client time: show clock (ISE) or date (Linux)

  3. Force DC resync: w32tm /resync /force

  4. Force client resync: chronyc makestep (Linux) or ntp server <ip> (ISE)

4. Phase 1: Verify Prerequisites

4.1. 1.1 Verify DNS on BIND

From a Linux workstation, test DNS resolution via BIND:

dig @10.50.1.1 home-dc01.inside.domusdigitalis.dev

Verify all clients using BIND DNS (should show nameserver 10.50.1.90):

cat /etc/resolv.conf

4.2. 1.2 Verify Vault PKI

4.2.1. Step 1: Get credentials from dsec (on workstation)

dsource d000 dev/vault

This exports the following variables - copy these values, you’ll need them on vault-01:

  • VAULT_UNSEAL_KEY_1

  • VAULT_UNSEAL_KEY_2

  • VAULT_TOKEN

Threshold is 2 keys (check vault status to confirm).

4.2.2. Step 2: SSH to vault-01

ssh vault-01

4.2.3. Step 3: Set VAULT_ADDR

export VAULT_ADDR='http://127.0.0.1:8200'

4.2.4. Step 4: Check if sealed

vault status

If Sealed = false, skip to Step 6.

4.2.5. Step 5: Unseal (if sealed)

Run this 2 times, pasting a different unseal key each time when prompted:

vault operator unseal
# Paste VAULT_UNSEAL_KEY_1, press Enter
vault operator unseal
# Paste VAULT_UNSEAL_KEY_2, press Enter

After the second key, Sealed should be false.

4.2.6. Step 6: Set VAULT_TOKEN

export VAULT_TOKEN='<paste-VAULT_TOKEN-from-dsec>'

4.2.7. Step 7: Verify PKI

Check PKI mounts:

vault secrets list | grep pki

Verify CA chain:

vault read pki/cert/ca
vault read pki_int/cert/ca

Test certificate issuance:

vault write pki_int/issue/domus-server \
    common_name="test.inside.domusdigitalis.dev" \
    ttl="24h"

Exit back to workstation:

exit

4.3. 1.3 Verify ISE Trust

Load network credentials:

dsource d000 dev/network

List trusted CA certificates:

netapi ise cert list-trusted

Or filter for DOMUS:

netapi ise cert list-trusted | grep DOMUS

Verify both DOMUS-ROOT-CA and DOMUS-ISSUING-CA are present.

Raw API call with jq (quote the URL for zsh)
netapi ise api-call openapi GET '/api/v1/certs/trusted-certificate?size=100' | jq -r '.response[].friendlyName' | grep -i domus

Expected output:

DOMUS-ROOT-CA
DOMUS-ISSUING-CA

4.4. 1.4 Backup Current DC

On home-dc01 (PowerShell as Administrator):

Full system state backup:

wbadmin start systemstatebackup -backuptarget:\\nas-01\backups\dc-migration

Export AD CS configuration:

certutil -backup C:\Backup\ADCS

Document current FSMO roles:

netdom query fsmo

5. Phase 2: Deploy Windows Server 2025 Core

5.1. 2.1 VM Specifications

Resource Value

RAM

4 GB (minimum for Core)

vCPUs

2

Disk

40 GB (Core requires less)

Network

Bridge to VLAN 1 (10.50.1.0/24)

ISO

Windows Server 2025 Standard (Core)

5.2. 2.2 Create VM on KVM

# Download virtio drivers if needed
# https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/stable-virtio/

# Create VM
sudo virt-install \
    --name home-dc02 \
    --ram 4096 \
    --vcpus 2 \
    --disk path=/var/lib/libvirt/images/home-dc02.qcow2,size=40,bus=virtio \
    --os-variant win2k22 \
    --network bridge=br0,model=virtio \
    --graphics vnc,listen=0.0.0.0 \
    --cdrom /var/lib/libvirt/images/iso/windows_server_2025.iso \
    --disk path=/var/lib/libvirt/images/iso/virtio-win.iso,device=cdrom

5.3. 2.3 Windows Server Core Installation

During installation:

  1. Select Windows Server 2025 Standard (Server Core Installation)

  2. Accept license terms

  3. Select Custom: Install Windows only

  4. Load virtio drivers from second CD (virtio-win.iso):

    • Browse to viostor\2k22\amd64

    • Browse to NetKVM\2k22\amd64

  5. Select disk and install

5.4. 2.4 Initial Configuration (sconfig)

After first boot, use sconfig menu:

===============================================================================
                         Server Configuration
===============================================================================

1) Domain/Workgroup:                    Workgroup:  WORKGROUP
2) Computer Name:                       WIN-XXXXXXXX
3) Add Local Administrator
4) Configure Remote Management          Enabled

5) Windows Update Settings:             Manual
6) Download and Install Updates
7) Remote Desktop:                      Disabled

8) Network Settings
9) Date and Time
10) Telemetry settings:                 Required
11) Windows Activation

12) Log Off User
13) Restart Server
14) Shut Down Server
15) Exit to Command Line

Enter number to select an option:

5.4.1. Set Computer Name

# Select option 2
Enter new computer name: home-dc02
Restart now? (Y/N): N

5.4.2. Configure Network (Option 8)

# Select adapter index (usually 1)
# Select option 1 - Set Network Adapter Address

Select (D)HCP or (S)tatic IP address: S
Enter static IP address: 10.50.1.51
Enter subnet mask: 255.255.255.0
Enter default gateway: 10.50.1.1

5.4.3. Set DNS Server

# Still in Network Settings
# Select option 2 - Set DNS Servers

Enter new preferred DNS server: 10.50.1.50
Enter alternate DNS server: 10.50.1.1

5.4.4. Enable Remote Desktop (Option 7)

# Select option 7
Enable Remote Desktop? (E)nable or (D)isable: E
Allow only NLA connections? (1) More secure or (2) Less secure: 1

5.4.5. Restart Server

# Select option 13 to restart

5.5. 2.5 Post-Restart Configuration (PowerShell)

Press 15 in sconfig to exit to command line, then start PowerShell:

powershell

Verify network configuration:

Get-NetIPConfiguration

Test connectivity to existing DC:

Test-Connection home-dc01.inside.domusdigitalis.dev

Test DNS resolution:

Resolve-DnsName home-dc01.inside.domusdigitalis.dev

6. Phase 3: Install AD DS and Promote

6.1. 3.1 Install AD DS and DNS Roles

Install-WindowsFeature AD-Domain-Services, DNS -IncludeManagementTools

Verify installation:

Get-WindowsFeature AD-Domain-Services, DNS

6.2. 3.2 Promote to Domain Controller

Choose ONE option based on your scenario:

6.2.1. Option A: New Forest (No Existing DC)

Use this when the old DC has been decommissioned and removed.

Gopass Credential Structure

Use separate entries for each credential type:

# Generate Administrator password
gopass generate ADMINISTRATIO/servers/home-dc01/Administrator 32

# Generate DSRM password
gopass generate ADMINISTRATIO/servers/home-dc01/dsrm 32

Store metadata using heredoc:

gopass insert ADMINISTRATIO/servers/home-dc01/meta << 'EOF'
hostname: home-dc01
fqdn: home-dc01.inside.domusdigitalis.dev
ip: 10.50.1.50
domain: inside.domusdigitalis.dev
netbios: DOMUS
ssh_port: 22022
os: Windows Server 2025 Core
roles: AD-DS, DNS
pki: Vault (DOMUS-ROOT-CA)
created: 2026-02-09
EOF

Retrieve credentials:

# Get Administrator password
gopass show ADMINISTRATIO/servers/home-dc01/Administrator

# Get DSRM password
gopass show ADMINISTRATIO/servers/home-dc01/dsrm

# Copy DSRM to clipboard
gopass show -c ADMINISTRATIO/servers/home-dc01/dsrm

# View metadata
gopass show ADMINISTRATIO/servers/home-dc01/meta

Promote to DC (creates new forest):

Install-ADDSForest -DomainName "inside.domusdigitalis.dev" -DomainNetbiosName "DOMUS" -ForestMode "WinThreshold" -DomainMode "WinThreshold" -InstallDns:$true -SafeModeAdministratorPassword (Read-Host -AsSecureString "DSRM Password") -Force:$true

When prompted for DSRM Password:, paste from clipboard (Ctrl+Shift+V or right-click).

Server will restart automatically after promotion.

6.2.2. Option B: Join Existing Domain (Adding Second DC)

Use this when adding a DC to an existing domain with a running DC.

Join domain first:

Add-Computer -DomainName inside.domusdigitalis.dev -Credential (Get-Credential)
Restart-Computer

After restart, promote to DC (replicating from existing DC):

Install-ADDSDomainController `
    -DomainName "inside.domusdigitalis.dev" `
    -SiteName "Default-First-Site-Name" `
    -ReplicationSourceDC "home-dc01.inside.domusdigitalis.dev" `
    -DatabasePath "C:\Windows\NTDS" `
    -LogPath "C:\Windows\NTDS" `
    -SysvolPath "C:\Windows\SYSVOL" `
    -NoGlobalCatalog:$false `
    -InstallDns:$true `
    -SafeModeAdministratorPassword (Read-Host -AsSecureString "DSRM Password") `
    -Force:$true

We install DNS (-InstallDns:$true) for AD-integrated zones. BIND handles client DNS resolution, but the DC hosts AD-specific SRV records (_ldap._tcp, _kerberos._tcp, _gc._tcp) required for domain functionality.

6.3. 3.4 Verify Replication

After restart, verify AD replication.

Check replication status:

repadmin /replsummary

Check replication partners:

repadmin /showrepl

Verify DC registration:

Get-ADDomainController -Filter * | Select-Object Name, IPv4Address, OperatingSystem

Test LDAP connectivity:

Get-ADUser -Filter * -Server home-dc02.inside.domusdigitalis.dev | Select-Object -First 5

7. Phase 4: Transfer FSMO Roles

7.1. 4.1 Check Current FSMO Holders

View current FSMO role holders:

netdom query fsmo

Or via PowerShell:

Get-ADForest | Select-Object DomainNamingMaster, SchemaMaster
Get-ADDomain | Select-Object InfrastructureMaster, PDCEmulator, RIDMaster

7.2. 4.2 Transfer All FSMO Roles

From home-dc02 (PowerShell as Administrator), transfer all roles:

Move-ADDirectoryServerOperationMasterRole `
    -Identity "home-dc02" `
    -OperationMasterRole SchemaMaster, DomainNamingMaster, PDCEmulator, RIDMaster, InfrastructureMaster `
    -Force

Verify transfer:

netdom query fsmo

7.3. 4.3 Update DNS Records

On BIND DNS server (bind-01):

# Update A records
home-dc02.inside.domusdigitalis.dev -> 10.50.1.51

# Update SRV records for AD
_ldap._tcp.inside.domusdigitalis.dev -> home-dc02.inside.domusdigitalis.dev
_kerberos._tcp.inside.domusdigitalis.dev -> home-dc02.inside.domusdigitalis.dev

7.4. 4.4 Update ISE LDAP Configuration

# Via netapi (if configured)
dsource d000 dev/network

# Check current AD configuration
netapi ise ers ad

# Update primary LDAP server to home-dc02
# This may require ISE GUI:
# Administration > Identity Management > External Identity Sources > Active Directory

8. Phase 5: Decommission Old DC

Do not proceed until:

  • All FSMO roles transferred

  • AD replication confirmed healthy

  • ISE/services updated to use home-dc02

  • Test authentication from Linux workstations

8.1. 5.1 Verify Services Using New DC

# From Linux workstation - test 802.1X auth
nmcli connection up "Wired-802.1X"

# Check ISE auth logs
netapi ise mnt sessions

# Verify LDAP binds going to new DC
netapi ise dc session --filter "home-dc02"

8.2. 5.2 Demote Old DC

On home-dc01 (PowerShell as Administrator):

# Demote to member server
Uninstall-ADDSDomainController `
    -DemoteOperationMasterRole:$true `
    -RemoveDnsDelegation:$true `
    -LocalAdministratorPassword (Read-Host -AsSecureString "Local Admin Password") `
    -Force

# Server will restart

8.3. 5.3 Remove AD CS Role

After demotion, remove the deprecated CA:

# Remove AD CS role
Uninstall-WindowsFeature AD-Certificate-Services -IncludeManagementTools

# Clean up CA database
Remove-Item -Recurse -Force C:\Windows\System32\CertLog\

8.4. 5.4 Unjoin and Shutdown

# Remove from domain
Remove-Computer -UnjoinDomainCredential (Get-Credential) -Force -Restart

After restart, shut down the old VM:

# On KVM host
sudo virsh shutdown home-dc01

# Optional: Remove VM after verification period
# sudo virsh undefine home-dc01 --remove-all-storage

9. Phase 6: Post-Migration Verification

9.1. 6.1 AD Health Check

# On home-dc02
# Check AD services
Get-Service NTDS, Netlogon, KDC | Select-Object Name, Status

# Check replication (should show only home-dc02)
Get-ADDomainController -Filter * | Select-Object Name, IPv4Address

# Verify FSMO roles
netdom query fsmo

9.2. 6.2 Linux Client Verification

# Test Kerberos
kinit user@INSIDE.DOMUSDIGITALIS.DEV
klist

# Test LDAP
ldapsearch -H ldap://home-dc02.inside.domusdigitalis.dev -b "dc=inside,dc=domusdigitalis,dc=dev" -Y GSSAPI

# Test 802.1X authentication
nmcli connection up "Wired-802.1X"
netapi ise mnt sessions

9.3. 6.3 ISE Verification

# Check AD join status
netapi ise ers ad

# Test authentication
netapi ise mnt auth-status --last 10

10. Phase 7: Documentation Updates

10.1. 7.1 Update Infrastructure Docs

Document Update Required

domus-infra-ops/architecture/ip-addressing.adoc

Update DC IP from 10.50.1.50 to 10.50.1.51

domus-ise-linux/02-pki/windows-adcs.adoc

Mark as deprecated, reference Vault

All antora.yml files

Update ad-dc-ip and homedc-hostname attributes

10.2. 7.2 Update Antora Attributes

In domus-infra-ops/docs/asciidoc/antora.yml:

# Update DC references
homedc-hostname: home-dc02.inside.domusdigitalis.dev
homedc-ip: 10.50.1.51

10.3. 7.3 Archive Old Documentation

# Move deprecated AD CS docs to archive
git mv docs/asciidoc/modules/ROOT/pages/runbooks/windows-dc-adcs.adoc \
       docs/asciidoc/modules/ROOT/pages/archive/

# Update nav.adoc to reflect changes

11. Rollback Procedure

If issues occur before decommissioning old DC:

  1. Transfer FSMO roles back to home-dc01

  2. Update DNS/ISE to point to home-dc01

  3. Demote home-dc02

  4. Investigate and retry

# On home-dc01 - reclaim FSMO roles
Move-ADDirectoryServerOperationMasterRole `
    -Identity "home-dc01" `
    -OperationMasterRole SchemaMaster, DomainNamingMaster, PDCEmulator, RIDMaster, InfrastructureMaster `
    -Force

12. Appendix: Windows Server Core Quick Reference

12.1. Common Commands

Task Command

Open Server Configuration

sconfig

Open PowerShell

powershell

Check IP Configuration

ipconfig /all or Get-NetIPConfiguration

Restart Server

shutdown /r /t 0 or Restart-Computer

Check Services

Get-Service

Check Event Logs

Get-EventLog -LogName System -Newest 20

Remote Management

winrm quickconfig

12.2. Useful PowerShell Modules

# AD management
Import-Module ActiveDirectory

# Check DC health
dcdiag /v

# Check DNS (if installed)
Import-Module DnsServer

13. Post-Migration: Linux Client Updates

After promoting a new DC (or replacing an old one), existing domain-joined Linux workstations must be updated to point to the new DC.

13.1. Update Kerberos Configuration

Preview the change first (never sed blindly):

sed -n 's/dc-01\.inside\.domusdigitalis\.dev/home-dc01.inside.domusdigitalis.dev/gp' /etc/krb5.conf
Expected output (lines that will be modified):
        kdc = home-dc01.inside.domusdigitalis.dev
        admin_server = home-dc01.inside.domusdigitalis.dev

Apply the change:

sudo sed -i 's/dc-01\.inside\.domusdigitalis\.dev/home-dc01.inside.domusdigitalis.dev/g' /etc/krb5.conf

Verify:

cat /etc/krb5.conf

13.2. Refresh Kerberos and SSSD

# Clear old Kerberos tickets
kdestroy -A
# Restart SSSD
sudo systemctl restart sssd
# Test Kerberos authentication
kinit Administrator@INSIDE.DOMUSDIGITALIS.DEV
# Verify SSSD connects to new DC
sudo sssctl domain-status inside.domusdigitalis.dev
Expected output:
Online status: Online

Active servers:
AD Domain Controller: home-dc01.inside.domusdigitalis.dev

Discovered AD Domain Controller servers:
- home-dc01.inside.domusdigitalis.dev

See DC Migration in domus-ise-linux (ise-linux: 04-linux-client/domain-join.adoc) for complete troubleshooting.