Build a Windows Server 2025 Active Directory Lab in VirtualBox — End-to-End Guide (with Hybrid Entra ID)
In this extensive tutorial, you’ll set up a modern, reproducible Active Directory lab using Oracle VirtualBox, configure DNS, create a domain controller, join Windows 10/11 clients, and explore hybrid integration with Microsoft Entra ID. You’ll also get practical PowerShell and Microsoft Graph troubleshooting playbooks you can paste into your environment.
High-level view of the VirtualBox lab: internal network, domain controller, client, and optional Entra ID.
Why build a VirtualBox AD lab today?
Physical hardware is costly and inflexible. VirtualBox on a laptop lets you quickly spin up a safe playground for Group Policy, joins, Kerberos, and hybrid identity. This lab is repeatable, snapshot-friendly, and perfect for certifications and interviews.
Prerequisites
- A host with ≥ 16 GB RAM, 4+ cores, 60+ GB free SSD
- Oracle VirtualBox installed (latest)
- ISOs for Windows Server 2025 and Windows 10/11
- Basic Windows admin familiarity
Tip: snapshots save hours—take one after each milestone.
Designing the lab topology
We’ll use two NICs on the domain controller: Adapter 1 = NAT (for Internet updates) and Adapter 2 = Internal Network (IntNet) providing services to clients. Clients use only Internal Network and point DNS at the DC.
| Component | Role | Notes |
|---|---|---|
| DC01 | AD DS, DNS | 2 vCPUs, 4–6 GB RAM, 60 GB disk |
| WIN10-01 | Client | 2 vCPUs, 4 GB RAM, 40 GB disk |
| IntNet | Internal network | No default gateway; DNS = DC01 |
Create the VMs and network
CAC-Internal).Install Windows Server 2025 and promote to a domain controller
During OS setup, choose Desktop Experience, set a strong Administrator password, and apply updates. Then configure static IP on the Internal NIC (e.g., 10.10.10.10/24), leave gateway empty, and set DNS to itself (10.10.10.10).
PowerShell: Core DC configuration
# Set static IP on internal NIC (rename if needed)
$nic = Get-NetAdapter | Where-Object { $_.Status -eq 'Up' -and $_.Name -like '*Ethernet*' } | Select-Object -First 1
New-NetIPAddress -InterfaceAlias $nic.Name -IPAddress 10.10.10.10 -PrefixLength 24
Set-DnsClientServerAddress -InterfaceAlias $nic.Name -ServerAddresses 10.10.10.10
# Install AD DS & DNS, then promote
Install-WindowsFeature AD-Domain-Services,DNS -IncludeManagementTools
Import-Module ADDSDeployment
Install-ADDSForest `
-DomainName "lab.contoso.local" `
-DomainNetbiosName "LAB" `
-SafeModeAdministratorPassword (Read-Host "DSRM Password" -AsSecureString) `
-InstallDNS:$true -Force
Reboot completes promotion. Take a snapshot named “DC-Promoted”.
Install Windows 10/11 and join the domain
- On the client, set the Internal NIC to
10.10.10.20/24, DNS =10.10.10.10. - Test name resolution:
nslookup dc01.lab.contoso.local. - Join: System > Rename this PC (advanced) > Change > Domain →
lab.contoso.local. Reboot.
nltest /dsgetdc:lab.contoso.local and gpupdate /force. You should see a healthy DC and policy refresh.
Essential DNS and AD checks (quick wins)
- Forward lookup zone
lab.contoso.localexists with_msdcssub-zone. - Reverse zone optional but recommended (PTR records help troubleshooting).
dcdiag /vshows all PASS,repadmin /replsummaryis clean (single-DC will be simple).
Creating baseline users, groups, and GPO
PowerShell: Create users & baseline GPO
# Create OU structure
New-ADOrganizationalUnit -Name "Corp" -Path "DC=lab,DC=contoso,DC=local"
New-ADOrganizationalUnit -Name "Users" -Path "OU=Corp,DC=lab,DC=contoso,DC=local"
New-ADOrganizationalUnit -Name "Workstations" -Path "OU=Corp,DC=lab,DC=contoso,DC=local"
# Add a test user
New-ADUser -Name "Ava Admin" -SamAccountName ava.admin -UserPrincipalName "ava.admin@lab.contoso.local" `
-AccountPassword (ConvertTo-SecureString "P@ssw0rd123!" -AsPlainText -Force) -Enabled $true `
-Path "OU=Users,OU=Corp,DC=lab,DC=contoso,DC=local"
# Baseline security GPO
New-GPO -Name "Baseline-Workstations" | New-GPLink -Target "OU=Workstations,OU=Corp,DC=lab,DC=contoso,DC=local"
# Example: Turn on Windows Defender Cloud-delivered protection
Set-GPRegistryValue -Name "Baseline-Workstations" `
-Key "HKLM\SOFTWARE\Policies\Microsoft\Windows Defender\MpEngine" `
-ValueName "MpCloudBlockLevel" -Type DWord -Value 2
Optional: Make it hybrid with Microsoft Entra ID
If you want single sign-on to SaaS and conditional access testing, add Microsoft Entra ID connect-style synchronization (using Microsoft Entra Connect or Cloud Sync). In a lab, a single forest, single domain, and a scoped OU are enough.
Troubleshooting playbooks (copy-paste friendly)
Real labs shine when things go wrong. Below are concise recipes for the most common issues.
Playbook 1 — Client can’t join domain
- DNS: Client must query DC’s DNS.
ipconfig /allshould show only10.10.10.10on the Internal NIC. - Time: Skew > 5 minutes breaks Kerberos. Sync to DC:
w32tm /resync /rediscover. - Firewall: Ensure File and Printer Sharing, Remote Admin, and RPC are allowed between client and DC.
# On client: quick DNS & time checks
Resolve-DnsName lab.contoso.local
Test-Connection dc01 -Count 2
w32tm /query /status
w32tm /resync /rediscover
Playbook 2 — AD DS health basics
dcdiag /v
repadmin /replsummary
Get-EventLog -LogName "Directory Service" -Newest 20 | Format-Table -Auto
Playbook 3 — DNS lookups fail intermittently
# Restart DNS service and clear cache on DC
Restart-Service DNS
Clear-DnsServerCache
# On client, flush & test
ipconfig /flushdns
nslookup dc01.lab.contoso.local
Playbook 4 — Group Policy not applying
gpupdate /force
gpresult /h c:\temp\gp.html
Get-GPO -All | Select DisplayName,Id,CreationTime
Get-GPResultantSetOfPolicy -ReportType Html -Path C:\temp\rsop.html
Playbook 5 — LDAP/Kerberos sanity
nltest /dsgetdc:lab.contoso.local
klist purge
klist get host/dc01.lab.contoso.local
Test-ComputerSecureChannel -Verbose
Hybrid troubleshooting with Microsoft Graph (Entra ID)
When you enable sync (Connect or Cloud Sync), sign-ins and sync status often raise questions. The Microsoft Graph PowerShell SDK lets you pull live diagnostics. Install once on any Internet-connected management host (can be your DC if NAT is enabled).
Graph: Install & sign in
Install-Module Microsoft.Graph -Scope CurrentUser
Import-Module Microsoft.Graph
Connect-MgGraph -Scopes "User.Read.All","AuditLog.Read.All","Directory.Read.All"
Select-MgProfile -Name beta # for some audit endpoints; switch to v1.0 where available
Graph: Check directory sync status
# Useful when using Entra Connect / Cloud Sync
Get-MgDirectoryOnPremisesSynchronization | Format-List
Graph: Inspect recent sign-ins (filter by failure)
Get-MgAuditLogSignIn -All `
| Where-Object { $_.Status.ErrorCode -ne 0 } `
| Select-Object CreatedDateTime,UserDisplayName,IpAddress,AppDisplayName,Status `
| Sort-Object CreatedDateTime -Descending | Select-Object -First 25
Graph: List risky users & sign-ins (P2)
Get-MgRiskyUser -All | Select DisplayName, RiskLevel, RiskState
Get-MgIdentityProtectionRiskySignIn -All `
| Select CreatedDateTime, UserDisplayName, RiskLevel, RiskDetail
Graph: Export 30-day risky sign-ins to CSV
# Adjust days as needed
$since = (Get-Date).AddDays(-30)
Get-MgIdentityProtectionRiskySignIn -All `
| Where-Object { $_.CreatedDateTime -ge $since } `
| Select-Object CreatedDateTime,UserDisplayName,UserPrincipalName,IpAddress,Location,AppDisplayName,RiskLevel,RiskDetail `
| Export-Csv -Path "$env:USERPROFILE\Desktop\risky-signins-30days.csv" -NoTypeInformation
Security hardening basics for the lab
- Create separate admin and user identities; avoid day-to-day browsing on domain admin.
- Enable Windows Firewall and limit RDP to the internal network.
- Use Local Administrator Password Solution (LAPS) for local admin rotation.
- Use GPO to disable SMB1, enforce NTP, and deploy Defender baselines.
PowerShell: Quick hardening snippets
# Disable SMB1
Set-SmbServerConfiguration -EnableSMB1Protocol $false -Force
# NTP: DC as reliable time source
w32tm /config /manualpeerlist:"time.windows.com,0x9" /syncfromflags:manual /reliable:yes /update
w32tm /resync /force
Lab “Day 2” exercises
- Deploy a file server and test NTFS vs. share permissions.
- Create and link a GPO for Windows Update rings on clients.
- Add a second DC and test
repadminfailover. - Enable Azure AD Connect staging mode, then cut over.
Common error messages decoded
| Error | Root cause | Fix |
|---|---|---|
| “The specified domain either does not exist or could not be contacted.” | DNS not pointing to DC; time skew | Set DNS to DC IP only; w32tm /resync |
| “KDC_ERR_S_PRINCIPAL_UNKNOWN” | SPN missing/mismatched | Check with setspn -Q */hostname; fix SPN |
| Group Policy “Access is denied” | SYSVOL/NETLOGON share or perms | Verify shares, DFSR health; permissions on GPO |
Inline “royalty-free” visuals for documentation
The SVG above and the following diagram are embedded shapes you’re free to reuse inside your WordPress post.
Backup & snapshot strategy
Adopt a naming convention: “00-CleanOS”, “10-DC-Promoted”, “20-Client-Joined”. Snapshot just before risky changes (schema updates, GPO templates, Connect configuration) to save yourself from rebuilds.
Performance tuning tips for tiny hosts
- Use fixed-size VDI or VHD where possible; it performs better than thin on heavy I/O.
- Allocate only the RAM you need—swap hurts more than conservative sizing helps.
- Keep ISO and VM disks on SSD; move backups to HDD/external.
Frequently asked “why” answers
Why an internal network? Isolation. It keeps your lab from colliding with home/office DNS or DHCP, and it’s safer for experiments.
Why NAT on the DC? For Windows Update, downloads, and Graph module installs without exposing your lab externally.
Why not bridge the client? You can, but then you must carefully manage DNS and routes—less predictable on Wi-Fi.
Copy-ready scripts to accelerate lab rebuilds
Script: Build baseline OUs, groups, and join logic
# Run on DC01 after promotion
$root = "DC=lab,DC=contoso,DC=local"
$corp = New-ADOrganizationalUnit -Name "Corp" -Path $root -ProtectedFromAccidentalDeletion $false
$ous = "Users","Admins","Workstations","Servers"
$ous | ForEach-Object { New-ADOrganizationalUnit -Name $_ -Path $corp.DistinguishedName }
# Security groups
New-ADGroup -Name "Helpdesk Operators" -GroupScope Global -Path "OU=Admins,OU=Corp,$root"
New-ADGroup -Name "Workstation Users" -GroupScope Global -Path "OU=Users,OU=Corp,$root"
Entra ID Conditional Access & sign-in insights in the lab
With hybrid users available in Entra, you can test Conditional Access basics: block legacy auth, require MFA for privileged roles, or enforce compliant device for admin portals. Use Graph queries (above) to see effects in near real-time.
Checklist before you publish your lab notes
- Document IPs, DNS zones, OU paths, GPO names.
- Export GPOs and scripts to a repo (e.g., GitHub private).
- Redact secrets from screenshots and commands.
Appendix A — Quick reference commands
# DC health
dcdiag /v
repadmin /showrepl
# DNS
dnscmd /enumrecords lab.contoso.local @
Resolve-DnsName _ldap._tcp.dc._msdcs.lab.contoso.local
# Client
ipconfig /all
nltest /sc_verify:lab
Test-ComputerSecureChannel -Verbose
Appendix B — Minimal Entra Connect notes
- Use a cloud-only global admin for setup; do not reuse the on-prem Administrator.
- Scope sync to
OU=Users,OU=Corp,DC=lab,DC=contoso,DC=localwhile testing. - Consider Cloud Sync agent if you want lighter footprint.
Appendix C — Sample HTML callouts for WordPress blocks
AD DS
DNS
Group Policy
Entra ID
Microsoft Graph
Authored for learners building hands-on identity skills. Internal links point to CloudKnowledge resources for deeper reading.











Leave a Reply