This lab builds a working Active Directory environment on Hyper-V: two domain controllers, a member server, and a client, on an isolated network. It is the foundation for practising Group Policy, certificate services, replication troubleshooting, and every AD-adjacent topic worth knowing.
Everything runs on a single machine with 32 GB of RAM. Evaluation ISOs from Microsoft are free for 180 days.
What you will build
Domain: lab.internal. Never use a domain name you do not control, and never .local — it collides with mDNS.
Prerequisites
- Windows 10/11 Pro or Windows Server with Hyper-V enabled
- 32 GB RAM (16 GB works if you run three VMs instead of four)
- 200 GB free on an SSD
- Windows Server 2022 and Windows 11 evaluation ISOs
Enable Hyper-V if it is not already:
Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All
# Reboot required
Step 1 — Networking
Create an internal switch. Internal, not private: the host gets an adapter on it, which lets you provide NAT for updates.
New-VMSwitch -Name "LAB-Internal" -SwitchType Internal
# The host's adapter on that switch becomes the lab's gateway
$ifIndex = (Get-NetAdapter -Name "vEthernet (LAB-Internal)").ifIndex
New-NetIPAddress -IPAddress 10.10.10.1 -PrefixLength 24 -InterfaceIndex $ifIndex
New-NetNat -Name "LAB-NAT" -InternalIPInterfaceAddressPrefix 10.10.10.0/24
Verify:
Get-NetNat
Get-NetIPAddress -InterfaceIndex $ifIndex | Format-Table IPAddress, PrefixLength
Use a checkpoint after each major stage.
Checkpoint-VM -Name DC01 -SnapshotName "pre-dcpromo"costs seconds and saves you rebuilding when an experiment goes wrong. That is the entire point of a lab.
Step 2 — Create the VMs
$vmPath = "D:\Hyper-V"
$isoServer = "D:\ISO\WindowsServer2022.iso"
function New-LabVM {
param([string]$Name, [int64]$MemoryGB, [string]$Iso)
New-VM -Name $Name -MemoryStartupBytes ($MemoryGB * 1GB) `
-Generation 2 -Path $vmPath `
-NewVHDPath "$vmPath\$Name\$Name.vhdx" -NewVHDSizeBytes 80GB `
-SwitchName "LAB-Internal"
Set-VM -Name $Name -DynamicMemory `
-MemoryMinimumBytes 1GB -MemoryMaximumBytes ($MemoryGB * 1GB) `
-AutomaticCheckpointsEnabled $false
Add-VMDvdDrive -VMName $Name -Path $Iso
$dvd = Get-VMDvdDrive -VMName $Name
Set-VMFirmware -VMName $Name -FirstBootDevice $dvd
}
New-LabVM -Name "DC01" -MemoryGB 4 -Iso $isoServer
New-LabVM -Name "DC02" -MemoryGB 4 -Iso $isoServer
New-LabVM -Name "FS01" -MemoryGB 4 -Iso $isoServer
New-LabVM -Name "WS01" -MemoryGB 4 -Iso "D:\ISO\Windows11.iso"
AutomaticCheckpointsEnabled $false matters. Automatic checkpoints on a domain controller cause USN rollback, which breaks replication in ways that are genuinely difficult to repair.
Never restore a domain controller from a Hyper-V checkpoint in a production environment. AD detects the rolled-back USN and quarantines the DC. In this lab it is fine — that is what a lab is for — but build the habit of using Windows Server Backup for real DCs.
Step 3 — Promote the first domain controller
Install Windows Server on DC01, then configure networking:
Rename-Computer -NewName "DC01" -Restart
New-NetIPAddress -InterfaceAlias "Ethernet" -IPAddress 10.10.10.10 `
-PrefixLength 24 -DefaultGateway 10.10.10.1
# Points at itself — corrected after promotion
Set-DnsClientServerAddress -InterfaceAlias "Ethernet" -ServerAddresses 127.0.0.1
Promote it:
Install-WindowsFeature AD-Domain-Services, DNS -IncludeManagementTools
Install-ADDSForest `
-DomainName "lab.internal" `
-DomainNetbiosName "LAB" `
-ForestMode "WinThreshold" `
-DomainMode "WinThreshold" `
-InstallDns:$true `
-SafeModeAdministratorPassword (Read-Host -AsSecureString "DSRM password") `
-NoRebootOnCompletion:$false `
-Force:$true
After reboot, verify:
Get-ADDomain
Get-ADForest
dcdiag /v /c
dcdiag should pass every test. Investigate any failure now — a broken forest root only gets harder to diagnose once you add a second DC.
Step 4 — Second domain controller
Configure DC02 with 10.10.10.11 and DNS pointing at 10.10.10.10 — it must resolve the domain to find it.
Install-WindowsFeature AD-Domain-Services, DNS -IncludeManagementTools
Install-ADDSDomainController `
-DomainName "lab.internal" `
-Credential (Get-Credential "LAB\Administrator") `
-InstallDns:$true `
-SafeModeAdministratorPassword (Read-Host -AsSecureString "DSRM password") `
-Force:$true
Once up, fix DNS on both so each prefers its partner and falls back to itself. This avoids the classic "island" problem where a DC that only knows itself cannot start AD services cleanly after a restart:
# On DC01
Set-DnsClientServerAddress -InterfaceAlias "Ethernet" -ServerAddresses 10.10.10.11,127.0.0.1
# On DC02
Set-DnsClientServerAddress -InterfaceAlias "Ethernet" -ServerAddresses 10.10.10.10,127.0.0.1
Verify replication:
repadmin /replsummary
repadmin /showrepl
Get-ADReplicationPartnerMetadata -Target "lab.internal" -Scope Domain |
Format-Table Server, Partner, LastReplicationSuccess
Step 5 — DHCP and OU structure
Install DHCP on DC01 for the client subnet:
Install-WindowsFeature DHCP -IncludeManagementTools
Add-DhcpServerInDC -DnsName "DC01.lab.internal" -IPAddress 10.10.10.10
Add-DhcpServerv4Scope -Name "LAB" -StartRange 10.10.10.100 `
-EndRange 10.10.10.200 -SubnetMask 255.255.255.0
Set-DhcpServerv4OptionValue -ScopeId 10.10.10.0 `
-DnsServer 10.10.10.10,10.10.10.11 `
-Router 10.10.10.1 -DnsDomain "lab.internal"
Build an OU structure worth practising against — mirroring how real environments separate by function rather than dumping everything in Users:
$base = "DC=lab,DC=internal"
New-ADOrganizationalUnit -Name "LAB" -Path $base
New-ADOrganizationalUnit -Name "Users" -Path "OU=LAB,$base"
New-ADOrganizationalUnit -Name "Servers" -Path "OU=LAB,$base"
New-ADOrganizationalUnit -Name "Clients" -Path "OU=LAB,$base"
New-ADOrganizationalUnit -Name "Groups" -Path "OU=LAB,$base"
New-ADOrganizationalUnit -Name "Service Accounts" -Path "OU=LAB,$base"
# Populate with test users
1..25 | ForEach-Object {
$name = "user{0:D2}" -f $_
New-ADUser -Name $name -SamAccountName $name `
-UserPrincipalName "$name@lab.internal" `
-Path "OU=Users,OU=LAB,$base" `
-AccountPassword (ConvertTo-SecureString "LabP@ssw0rd!" -AsPlainText -Force) `
-Enabled $true -ChangePasswordAtLogon $false
}
Step 6 — Join and verify
Join FS01 and WS01 to the domain:
Add-Computer -DomainName "lab.internal" `
-Credential (Get-Credential "LAB\Administrator") `
-OUPath "OU=Servers,OU=LAB,DC=lab,DC=internal" `
-Restart
Verification checklist — all of these should succeed:
nltest /dsgetdc:lab.internal # locates a DC
gpresult /r # policy applies
Test-ComputerSecureChannel -Verbose # secure channel healthy
Resolve-DnsName _ldap._tcp.lab.internal -Type SRV # SRV records present
What to practise next
The lab exists so you can break things safely. In rough order of value:
- Group Policy — build a baseline GPO, apply security filtering, use WMI filters, and work out precedence with
gpresult /h report.html. - Fine-grained password policies — a PSO applied to a group, overriding the domain default.
- AD Certificate Services on FS01, then issue certificates via autoenrolment.
- Break replication deliberately — take DC02 offline for longer than the tombstone lifetime and learn what recovery involves.
- Seize FSMO roles after simulating the permanent loss of DC01.
ntdsutilis not a tool you want to learn during an outage. - Delegated administration — grant a helpdesk group password-reset rights on the Users OU only, then verify the boundary holds.
Checkpoint before each. Roll back after. That loop is worth more than any amount of reading.
0 comments
Sign in to join the discussion.
No comments yet. If something here is wrong or incomplete, say so — corrections are welcome.