Build a Secure Windows Server 2025 Active Directory Virtual Lab in VirtualBox — Step-by-Step
This tutorial walks you through building an isolated VirtualBox lab with Windows Server 2025 as a Domain Controller, joining Windows 10/11 clients, configuring DHCP/DNS, and performing advanced troubleshooting and automation using PowerShell and Microsoft Graph API.
- Overview & Objectives
- Requirements & Downloads
- VirtualBox VM Setup (summary)
- Deploy Active Directory & Promote DC
- DHCP & DNS Configuration
- Advanced Troubleshooting (PowerShell)
- Microsoft Graph API Automation
- Security Hardening
- Testing & Verification
- FAQs & Troubleshooting Tips
- Conclusion & Next Steps
Overview & Objectives
This guide helps IT pros, students, and cybersecurity learners build an isolated, production-like Active Directory environment using VirtualBox, Windows Server 2025, and Windows 10 clients. The lab teaches:
- How to provision Windows Server 2025 and promote it to an Active Directory Domain Controller (AD DS).
- Configure DHCP and DNS for automated address assignment and name resolution.
- Join Windows 10 clients to the domain.
- Perform advanced troubleshooting with PowerShell scripts for AD replication, DNS, and GPO issues.
- Automate tasks (user creation, group assignment, device registration) using the Microsoft Graph API.
- Apply best-practice security hardening to make the lab resilient and realistic.
Requirements & Downloads
Hardware: 16GB RAM recommended, 4+ CPU cores, 60GB+ free disk space. For a lightweight lab a machine with 8GB can run 2-3 VMs (but expect limited performance).
Software & resources:
- Oracle VirtualBox (latest)
- Windows Server 2025 ISO (evaluation or licensed)
- Windows 10 / Windows 11 ISO for clients
- Guest Additions for VirtualBox
- PowerShell 7.x (optional but recommended)
- Microsoft Graph PowerShell module (for automation examples later)
VirtualBox VM Setup (concise, adapted from your PDF)
Follow these condensed steps to create the VMs (detailed screenshots are in your uploaded PDF):
- Create a new VM in VirtualBox: select Windows type and the matching version, allocate 4GB+ RAM for the server (8GB preferred if you have it), 2+ CPUs, and create a dynamically allocated VDI disk (50GB+).
- Attach the Windows Server 2025 ISO to the VM's optical drive (Settings → Storage → Controller). Configure network adapters: Adapter 1 → NAT (for internet), Adapter 2 → Internal Network (e.g., "Lab-Internal").
- Install the OS, apply updates, set a static IP on the server (e.g., 192.168.50.2/24), and configure DNS to point to itself (127.0.0.1 or its static IP).
- Snapshot the VM after initial OS install and updates.
- Repeat for Windows 10 clients (smaller RAM/disk footprint). Join clients to the internal network and install VirtualBox Guest Additions on each.
Deploy Active Directory & Promote Domain Controller
On the prepared Windows Server 2025 VM:
- Install the AD DS role using Server Manager or PowerShell. Recommended: use PowerShell for repeatable automation.
# Install AD DS and DNS via PowerShell (run as Administrator) Install-WindowsFeature -Name AD-Domain-Services, DNS -IncludeManagementTools Import-Module ADDSDeployment Install-ADDSForest -DomainName "lab.local" -DomainNetbiosName "LAB" -SafeModeAdministratorPassword (ConvertTo-SecureString "P@ssw0rd!" -AsPlainText -Force) -InstallDNS:$true -Force
After the server reboots it will be a domain controller for lab.local. Ensure the server's DNS service is running and the NIC is configured to use the server's IP as its DNS resolver.
DHCP & DNS Configuration
Option 1 (DHCP on server): install DHCP role and create a scope for your client subnet (e.g., 192.168.50.101–192.168.50.200). Option 2 (static addressing): assign static IPs to clients and use DHCP only if you need dynamic assignment.
# DHCP Role install & scope (PowerShell) Install-WindowsFeature -Name DHCP -IncludeManagementTools # Create a scope for 192.168.50.0/24 Add-DhcpServerv4Scope -Name "LabScope" -StartRange 192.168.50.101 -EndRange 192.168.50.200 -SubnetMask 255.255.255.0 # Set DNS server option for the scope (set to DC IP) Set-DhcpServerv4OptionValue -ScopeId 192.168.50.0 -DnsServer 192.168.50.2
Important DNS checks:
- Ensure the forward lookup zone for
lab.localexists and SRV records were created by the DC. - Confirm dynamic updates are allowed (default) so client machines register their A records.
Advanced Troubleshooting — PowerShell Recipes
Below are targeted PowerShell scripts and commands to troubleshoot common AD/DNS/DHCP issues. These are copy-paste friendly and color-coded for readability when pasted into WordPress.
1. AD Replication — check replication health
# Check replication status Get-ADReplicationFailure -Scope Site -Target "lab.local" | Format-Table # Force replication (example approach) Get-ADDomainController -Filter * | ForEach-Object { Sync-ADObject -Object (Get-ADUser -Filter * | Select -First 1) -Source $_.Name -Destination (Get-ADDomainController -Filter {IsWritable -eq $true}).Name } # Use repadmin (classic but effective) repadmin /replsummary repadmin /showrepl *
2. DNS: verify zones and name resolution
# Check DNS server status Get-Service -Name DNS # List zones from server Get-DnsServerZone -ComputerName 192.168.50.2 # Lookup SRV record for DC Resolve-DnsName -Name "_ldap._tcp.dc._msdcs.lab.local" -Server 192.168.50.2 -Type SRV
3. GPO & SYSVOL issues (common after promotion)
# Verify SYSVOL and NETLOGON shares Get-SmbShare | Where-Object Name -match "SYSVOL|NETLOGON" # Check GPO health (exports HTML report) Get-GPOReport -All -ReportType Html -Path "C:\GPOReport.html"
4. DNS cache & client-side checks
# On client (run elevated) ipconfig /flushdns ipconfig /registerdns nltest /dsgetdc:lab.local nltest /sc_verify:lab\DOMAIN
5. AD user & computer quick checks
# Find locked out users Search-ADAccount -LockedOut # Check last logon Get-ADUser -Filter * -Properties LastLogonDate | Sort-Object LastLogonDate -Descending | Select-Object Name,LastLogonDate -First 20
RSAT or ActiveDirectory module where needed.Microsoft Graph API — Automation & Troubleshooting
The Microsoft Graph API can be used to automate Azure AD tasks — useful when your lab extends into Entra ID or hybrid scenarios. Below are examples using the Microsoft Graph PowerShell module and raw REST calls. These examples include colored PowerShell for easy copying into WordPress.
Install Graph PowerShell module & connect (PowerShell)
# Install and connect to Microsoft Graph (run as admin) Install-Module Microsoft.Graph -Scope CurrentUser -Force Connect-MgGraph -Scopes "User.ReadWrite.All","Group.ReadWrite.All","Directory.ReadWrite.All" # Verify connection Get-MgUser -Top 5
Create a user and add to group (Graph PS)
# Create a user $pwd = "P@ssw0rd!2025" | ConvertTo-SecureString -AsPlainText -Force $params = @{ AccountEnabled = $true DisplayName = "Lab User" MailNickname = "labuser" UserPrincipalName = "labuser@contoso.onmicrosoft.com" PasswordProfile = @{ ForceChangePasswordNextSignIn = $false; Password = $pwd } } New-MgUser -BodyParameter $params # Create a security group and add member $g = New-MgGroup -DisplayName "LabAdmins" -MailEnabled:$false -SecurityEnabled:$true -MailNickname "labadmins" Add-MgGroupMember -GroupId $g.Id -DirectoryObjectId (Get-MgUser -UserId "labuser@contoso.onmicrosoft.com").Id
Using raw REST calls (client credentials flow)
Use these steps when building CI/CD pipelines or automation scripts (Python, Bash). You need an App registration with client secret or cert and the right permissions (app-only).
# Example: Acquire token (PowerShell) - Client Credentials $tenantId = "YOUR_TENANT_ID" $clientId = "YOUR_APP_ID" $clientSecret = "YOUR_CLIENT_SECRET" $body = @{ client_id = $clientId scope = "https://graph.microsoft.com/.default" client_secret = $clientSecret grant_type = "client_credentials" } $token = Invoke-RestMethod -Uri "https://login.microsoftonline.com/$tenantId/oauth2/v2.0/token" -Method POST -Body $body $accessToken = $token.access_token # Use token to call Graph $headers = @{ Authorization = "Bearer $accessToken" } Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/users" -Headers $headers -Method GET
These Graph calls are especially relevant for hybrid labs where Azure AD Connect is present and you want to sync or automate user lifecycle operations from scripts.
Security Hardening — Make the Lab Realistic
While the lab is isolated, apply these hardening steps to simulate a production environment and practice security operations:
- Patch Management: Keep server and clients updated; snapshot before major updates so you can roll back.
- Least Privilege: Use non-privileged accounts for daily tasks; create a separate admin account for emergency tasks.
- RDP & Remote Access: Disable RDP on domain-joined clients unless needed; restrict RDP via firewall rules.
- Account & Password Policies: Implement strong password policies and consider testing Fine-Grained Password Policies in the lab.
- Secure LDAP & LDAPS: Practice configuring LDAPS (certificate enrollment) to protect LDAP traffic.
- Audit & Event Forwarding: Enable auditing for logons and important AD changes. Forward logs to a collector if testing SIEM scenarios.
- Enable Windows Defender & Baselines: Use security baselines (CIS, Microsoft) to harden OS settings and GPOs.
Sample GPO Lockdown (PowerShell)
# Example: enforce password policy via built-in domain policy (modify carefully) Import-Module GroupPolicy Set-GPRegistryValue -Name "Default Domain Policy" -Key "HKLM\SYSTEM\CurrentControlSet\Control\Lsa" -ValueName "LimitBlankPasswordUse" -Type DWord -Value 1
Testing & Verification — What to validate
After deployment and hardening, verify the following:
- Clients can resolve
lab.localand domain controllers via DNS. - Authenticate domain users successfully on clients.
- AD replication status is healthy between DCs (if multiple DCs exist).
- DHCP leases are assigned correctly and DNS records are registered for clients.
- Audit logs reflect administrative operations and user authentications.
FAQs & Common Troubleshooting Scenarios
Q: Client fails to join the domain — what to check?
- Ensure client DNS points to the domain controller (not to external DNS).
- Check network connectivity (ping DC IP and DNS name).
- Verify time sync (Kerberos requires clocks within 5 minutes). Use
w32tm /resync. - Check firewall settings on DC and client; disable temporarily to isolate issue.
Q: GPOs not applying?
Run gpupdate /force then gpresult /r on client to see policy application and errors.
Q: SYSVOL or NETLOGON shares missing?
Verify replication and FRS/DFS-R status (depending on your OS). Use Get-SmbShare and examine event logs for DFS-R / SYSVOL errors.
Conclusion & Next Steps
This lab provides a safe environment to learn real-world Active Directory operations and admin workflows. Once comfortable, expand the lab to include:
- Multiple Domain Controllers across sites (test replication latency & site links)
- AD Certificate Services for PKI and LDAPS
- Azure AD Connect for hybrid identity scenarios
- SIEM integration (e.g., Splunk, Elastic, Defender) for event monitoring
If you want, I can now:
- Produce exportable PowerShell scripts as downloadable `.ps1` files
- Generate step-by-step screenshot-ready pages for each major task
- Create a separate article covering Azure AD Connect & hybrid sync using Graph API automation
Appendix A — Additional PowerShell Utilities
Useful one-liners and small utilities to keep handy in a lab:
# List domain controllers Get-ADDomainController -Filter * # Check FSMO role holders netdom query fsmo # Get DHCP lease list (server-side) Get-DhcpServerv4Lease -ComputerName 192.168.50.2 -ScopeId 192.168.50.0 # Export DNS zone to file Export-DnsServerZone -Name "lab.local" -ComputerName 192.168.50.2 -FileName "C:\lab.local.dns"
Appendix B — Quick Microsoft Graph Snippets (REST)
Some practical examples for automating user lifecycle tasks with Graph REST API using token from client credentials flow (see earlier snippet).
# Create User (REST) $body = @{ accountEnabled = $true displayName = "Lab User REST" mailNickname = "labuserrest" userPrincipalName = "labuserrest@contoso.onmicrosoft.com" passwordProfile = @{ forceChangePasswordNextSignIn = $false; password = "P@ssw0rd!2025" } } | ConvertTo-Json Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/users" -Headers $headers -Method POST -Body $body -ContentType "application/json" # Add a user to a group (REST) Invoke-RestMethod -Uri "https://graph.microsoft.com/v1.0/groups/{group-id}/members/$ref" -Method POST -Headers $headers -Body (@{ "@odata.id" = "https://graph.microsoft.com/v1.0/directoryObjects/{user-id}"} | ConvertTo-Json) -ContentType "application/json"











Leave a Reply