I built a Windows domain helpdesk lab — then broke it on purpose
Reading about Active Directory only gets you so far. I wanted somewhere I could actually do the work an entry-level helpdesk tech does every day: unlock accounts, reset passwords, onboard new hires, and figure out why a drive stopped mapping.
So I built a full domain environment on Proxmox, wired it to a real ticketing system, and started working tickets against it.
Here's the build, the mistakes, and the tickets.
The environment
Four VMs on a single Proxmox host, all on an isolated internal network:
| VM | Role | IP |
|---|---|---|
| OPNsense | Router / DHCP / firewall | 10.10.10.1 |
| DC01 | Windows Server 2022 — AD DS, DNS, file shares | 10.10.10.10 |
| WIN11-CLIENT | Windows 11 Pro workstation | 10.10.10.20 |
| ticketing | Debian 13 running osTicket | 10.10.10.30 |
The design decision that matters most: isolation. I created a
second Linux bridge in Proxmox (vmbr1) with no IP on the host and no
physical NIC attached — a pure virtual switch. OPNsense straddles both worlds,
with one leg on my home network and one on the lab network.
That means my domain controller's DNS and DHCP never leak onto my home network, and I can break things without taking down my actual internet.
Building it (and what went wrong)
VirtIO drivers, twice
Proxmox presents virtual disks and NICs using VirtIO — fast, but Windows has no built-in drivers for it. This bit me twice on the same VM.
During install, Windows insisted there were no drives to install to. The 80GB
disk was right there; Windows just couldn't see the controller. Fixed by
attaching virtio-win.iso as a second CD drive and loading
vioscsi\amd64\2k22 at the partition screen.
Then after install, Network Connections was completely empty — not
"disconnected," genuinely no adapter. Same root cause, different device.
Installed the NIC driver from Device Manager against
NetKVM\2k22\amd64.
On a Linux hypervisor, Windows guests need VirtIO drivers loaded twice — storage during setup, networking after.
Share permissions vs NTFS permissions
Created C:\Shares\SalesShare, shared it, and tried to reach it
from the client at \\DC01\SalesShare.
Windows file shares have two independent permission layers, and the more restrictive one wins:
- Share permissions govern access over the network (SMB)
- NTFS permissions govern access to the files on disk
My share permissions were wide open — Everyone with Change +
Read. NTFS was the problem: the Security tab listed only Administrators, SYSTEM,
and CREATOR OWNER. SMB let the connection in and the filesystem slammed the
door.
icacls "C:\Shares\SalesShare" /grant "HOMELAB\Domain Users:(OI)(CI)M"
"Access denied" on a share means checking both layers. One being permissive tells you nothing about the other.
The GPO that saved perfectly and did nothing
I configured a domain password policy, ran gpupdate /force, and
verified:
Get-ADDefaultDomainPasswordPolicy
Got back the defaults. Minimum length 7. Max age 42 days. Lockout threshold 0. None of my settings had applied.
The settings were saved. The syntax was fine. gpupdate reported
success. The problem was in the title bar of the editor window — I'd been editing
the Default Domain Controllers Policy, not the
Default Domain Policy.
| GPO | Linked to | Governs |
|---|---|---|
| Default Domain Policy | Domain root | Domain-wide account & password policy |
| Default Domain Controllers Policy | Domain Controllers OU | DC-specific security settings |
Domain account policy only takes effect from a GPO linked at the domain root. Mine was valid, saved, and scoped to entirely the wrong place.
"Configured" and "applying" are different claims. Verify effective state, not the config screen.
The policy baseline
Once I was editing the right GPO:


Worth knowing: the out-of-box lockout threshold is 0, meaning accounts never lock out no matter how many times someone fumbles their password.

Automated drive mapping
Group Policy handles department drives — Sales gets S:, HR gets
H:.

I used Update rather than Create. Update works whether or not the mapping already exists, so it's safe to re-apply. Create fails noisily if the drive is already there.

Drive mappings apply at logon, not at policy refresh.
gpupdate /force alone won't do it.
Working the tickets
Infrastructure is only half of it. Every scenario below was logged in osTicket, diagnosed, resolved, and closed with notes.

Account locked

User reported being unable to log in after repeated failed password attempts.
The instinct is to unlock immediately. The better habit is to confirm the state first, because "I can't log in" has at least four different causes — locked account, expired password, disabled account, or a mistyped username — and each has a different fix.

# Diagnose
Get-ADUser -Identity hank_sales -Properties LockedOut,BadLogonCount,LastBadPasswordAttempt,PasswordExpired
# Resolve
Unlock-ADAccount -Identity hank_sales
Worth knowing for real environments: Event ID 4740 logs lockouts along with the source workstation. Repeated mystery lockouts are often a stale mapped drive or a service running under an old password — not the user at all.
Password reset

The checkbox is the important part. Admins shouldn't know user passwords — forcing a change makes the temporary password single-use, and the user ends up with something only they know.
Set-ADAccountPassword -Identity peggy_hr -Reset -NewPassword (ConvertTo-SecureString "TempPass2026!" -AsPlainText -Force)
Set-ADUser -Identity peggy_hr -ChangePasswordAtLogon $true
Then the policy showed up in the real world:

That screenshot is the whole point of the exercise — a policy I configured in a GPO editor, refusing a password on a real login screen.
New hire onboarding

Onboarding is a workflow, not a single fix. My checklist:
- Account created in the correct OU
- Naming convention followed (first_department)
- Temp password set, forced change enabled
- Group membership verified
- Test login successful
- Department drive mapped and writable
The OU placement is the part that quietly matters. Create the account in the default Users container instead of the Sales OU and everything looks fine — until the new hire logs in on day one with no department drive, because the drive mapping GPO is scoped to the OU.
I verified the whole chain by logging in as the new user: forced password
change, desktop loads, S: maps automatically, file writes to the
share.
Drive not mapping
For this one I broke my own environment on purpose. I changed the Sales drive
mapping GPO to point at \\DC01\SalesShare2 — a path that doesn't
exist — then worked the ticket blind.

This is a different kind of ticket. Nothing is "denied," nothing errors on screen. The drive is simply absent, and the user has no information to give you beyond "it's gone."
The workflow:
gpresult /r # Is the GPO even reaching this user?
gpresult /h C:\gpreport.html # Full report, including failures
That first command splits the problem in half. If the GPO appears under Applied Group Policy Objects, delivery is fine and the setting is at fault. If it's missing, it's a linking, scoping, or filtering problem — a completely different investigation.
Mine was applying, which pointed at the setting. Testing the target directly confirmed it:
The policy was working perfectly. It was faithfully mapping a drive to nothing.
Narrow scope in order — is the policy reaching the user, is the setting correct, does the target actually exist. Jumping straight to recreating the GPO would have "fixed" it while teaching me nothing.
What I actually learned
The build taught me more than the tickets did, mostly because the build is where I made real mistakes with no answer key.
Three patterns kept repeating:
Layered systems fail quietly at the layer you're not looking at. Share permissions vs NTFS. GPO configured vs GPO applied. A policy that's valid but scoped wrong. In every case the surface I was looking at said everything was fine.
Verify effective state, not configuration.
Get-ADDefaultDomainPasswordPolicy told me the truth when the GPO
editor looked correct. gpresult told me the truth when the drive
mapping looked correct.
Diagnose before you fix. Unlocking an account takes ten seconds. Knowing it was actually a lockout — and not an expired password or a disabled account — is what makes the ten seconds the right ten seconds.
Every VM is snapshotted at a known-good baseline, so I can keep breaking things and rolling back.
Next up: offboarding workflows, shared mailbox permissions, and building out a knowledge base in osTicket so the fixes are documented for someone other than me.