Series intro: this is a 3-part walkthrough of setting up Active Directory from zero on Windows Server. Part 1 (this post) is the first DC. Part 2 covers OUs, users, GPO basics. Part 3 covers GPO hardening.
If you're standing up your first AD, this is the order to do it in. If you're doing your tenth, the PowerShell below is faster than the wizard.
TL;DR:
Install-WindowsFeature AD-Domain-Services -IncludeManagementTools
Install-ADDSForest -DomainName lab.example.com `
-DomainNetbiosName LAB -InstallDns -SafeModeAdministratorPassword (Read-Host -AsSecureString) `
-Force
# Reboots automatically when done. ~10 minutes total.
After the reboot, you're an AD admin on lab.example.com. The rest of the setup is users, OUs, GPOs — that's Part 2.
Pre-flight
The DC needs:
- A static IP (not DHCP). AD relies on stable DNS, DHCP-rotated DCs are a recipe for pain.
- DNS pointing at itself (127.0.0.1) — set this AFTER promotion, but make sure no other DNS server is listed as primary.
- Windows Server 2019 or newer. 2016 works but is past extended support; don't start a new forest on it.
- A reasonable hostname. The hostname can't be changed after promotion without considerable effort.
dc01ordc-lab-01— short, descriptive. - VM with 2 vCPU + 4 GB RAM minimum. AD itself is light; allocate more if you'll run other roles.
Check the basics:
Get-NetIPAddress -AddressFamily IPv4 | Format-Table InterfaceAlias, IPAddress
hostname
Get-WindowsFeature AD-Domain-Services
If hostname is something like WIN-XXXX, rename first:
Rename-Computer -NewName 'dc01' -Restart
Install the AD DS role
Install-WindowsFeature AD-Domain-Services -IncludeManagementTools
-IncludeManagementTools brings in RSAT (the AD admin GUI tools). Useful even on a Core install — the cmdlets need them.
This is fast (1-2 minutes). No reboot required at this stage; promotion is the next step.
Promote — create a new forest
For the very first DC, you're creating a new forest. Pick the domain name carefully:
.localis bad. Apple uses it for mDNS / Bonjour, breaks name resolution on some macOS clients.- Use a real subdomain you own (e.g.
corp.example.comif you ownexample.com). Avoids future split-DNS pain when the company gets a real public domain. lab.localstyle only for throwaway labs.
$securePass = Read-Host -AsSecureString -Prompt "DSRM password"
Install-ADDSForest `
-DomainName 'lab.example.com' `
-DomainNetbiosName 'LAB' `
-ForestMode 'WinThreshold' `
-DomainMode 'WinThreshold' `
-InstallDns:$true `
-CreateDnsDelegation:$false `
-DatabasePath 'C:\Windows\NTDS' `
-LogPath 'C:\Windows\NTDS' `
-SysvolPath 'C:\Windows\SYSVOL' `
-SafeModeAdministratorPassword $securePass `
-NoRebootOnCompletion:$false `
-Force
The DSRM (Directory Services Restore Mode) password is separate from your domain admin password. You'll never use it unless you have to boot into AD recovery mode after a database disaster. Save it in your password manager. Losing it isn't catastrophic but means a complex recovery if you ever need DSRM.
WinThreshold is the most current functional level. Pin to a specific level only if you have older DCs to coexist with.
The promotion process takes 5-10 minutes and reboots the box. Expect a forced reboot — you'll lose your RDP session.
After the reboot — sanity checks
Log back in (use the local Administrator account with its existing password — it's now the domain Administrator):
# Confirm the DC is healthy
Get-ADDomain
Get-ADForest
Get-ADDomainController
# Confirm DNS is integrated
Get-DnsServerZone
# Should list lab.example.com, _msdcs.lab.example.com, plus reverse zones
# Replication health (only one DC right now, so this is just sanity)
Get-ADReplicationFailure -Target dc01
# All-in-one health summary
dcdiag /v
dcdiag runs ~30 tests. The output is dense but searchable for "passed" / "failed". Failed tests on a fresh first DC almost always trace to DNS misconfiguration.
Set DNS to itself
This is the step that traps people. The DC must use itself as its primary DNS server. After promotion, the network adapter still has whatever DNS it had before:
Get-DnsClientServerAddress -InterfaceIndex (Get-NetAdapter | Where-Object Status -eq 'Up').ifIndex
If it's not 127.0.0.1 (or a sibling DC for the second+ DCs), fix it:
$idx = (Get-NetAdapter | Where-Object Status -eq 'Up').ifIndex
Set-DnsClientServerAddress -InterfaceIndex $idx -ServerAddresses 127.0.0.1
After this, AD lookups work locally. Without this, every domain operation does a slow timeout-and-fallback on whatever external DNS was configured.
Forwarders
Your DC's DNS doesn't know about the public internet. Set forwarders so domain-joined clients can resolve external names:
Set-DnsServerForwarder -IPAddress 1.1.1.1, 8.8.8.8
Pick whatever DNS resolvers you trust. Cloudflare and Google are the easy defaults; Quad9 if you want filtered.
Windows Time
DCs are time-authoritative for the domain. The first DC (PDC emulator FSMO holder) is authoritative for the forest. Configure it to sync from a real NTP source:
w32tm /config /manualpeerlist:"time.cloudflare.com,0x9 time.google.com,0x9" /syncfromflags:manual /reliable:yes /update
Restart-Service w32time
w32tm /resync /force
Time skew kills Kerberos. Domain auth fails after about 5 minutes of skew. Get this right before joining anything to the domain.
Open ports — UDP/TCP 53, 88, 389, 445, 636, 3268, 3269
Default Windows Firewall opens these when AD DS installs. Verify:
Get-NetFirewallRule -DisplayGroup "Active Directory Domain Services" |
Select DisplayName, Enabled, Direction
If you have an external firewall in front of the DC (host firewall, network firewall), allow these from the LAN range. tcp/53 udp/53 for DNS, tcp/389 tcp/636 for LDAP/LDAPS, tcp/88 udp/88 for Kerberos, tcp/445 for SMB, tcp/3268 tcp/3269 for global catalog.
This applies to UFW-style allowlists on Linux clients too — domain-joined Linux boxes need outbound access to these ports.
Backup the DC
A dead DC with no backup means rebuilding the forest. Set up Windows Server Backup before you put anything important in AD:
Install-WindowsFeature Windows-Server-Backup
System State backups via wbadmin:
wbadmin start systemstatebackup -backupTarget:E: -quiet
Schedule nightly via Register-ScheduledTask. Test restore — bring up an isolated VM and do a dry run. You don't want to find out your backup is broken at 2am during an outage.
For broader backup hygiene, Restic to S3 / MinIO covers the encrypted-offsite pattern.
What's next
Part 2: OUs, users, GPO basics. Part 3: GPO hardening.
Things to do before adding member servers:
- Second DC for redundancy. One DC = single point of failure. Promote a second member server with
Install-ADDSDomainController(similar pattern, different cmdlet). - DHCP scope adjustment so clients receive the DC as primary DNS.
- Reverse DNS zones for the IP ranges in use.
Member-server joins themselves are a separate post.
Gotchas
- DSRM password ≠ domain admin password. Confused easily. Document both.
- Don't promote a DC behind a NAT. AD doesn't tolerate IP rewrite. DCs need real direct addresses to other DCs.
- VM snapshots break replication. Reverting a DC snapshot causes USN rollback — silently corrupts replication. Don't snapshot DCs; back up via System State instead.
- Time matters more than you think. Kerberos has 5-minute skew tolerance. NTP not configured = domain works for a day, breaks mysteriously on day 2.
- First DC is special. The first DC in a new forest holds all FSMO roles by default. After you have multiple DCs, you should distribute FSMO. Single-DC labs can ignore this.