> ## Content Index
> Fetch the complete content index at: https://blog.sudowl.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Join Windows Server to AD Domain with PowerShell
- URL: https://blog.sudowl.com/join-windows-server-ad-domain-powershell/
- Published: 2026-04-15T10:00:00.000Z
- Updated: 2026-10-11T10:16:27.000Z
- Description: Domain-join a Windows Server with Add-Computer: point DNS at the DC first, land in the right OU, verify the secure channel, fix the usual join errors.
- Author: Lukas
- Tags: Windows Server, Security

Domain-joining a server is one cmdlet. Getting it to work on the first try is three checks before that cmdlet. Almost every failed join I've looked at was DNS, and the rest was time or the OU path.

**TL;DR:** DNS to the DC, check you can find the DC, then join straight into the right OU.

```powershell
$idx = (Get-NetAdapter | Where-Object Status -eq 'Up').ifIndex
Set-DnsClientServerAddress -InterfaceIndex $idx -ServerAddresses 10.0.0.10

Resolve-DnsName _ldap._tcp.dc._msdcs.lab.example.com -Type SRV

Add-Computer -DomainName 'lab.example.com' `
  -Credential (Get-Credential 'LAB\Administrator') `
  -OUPath 'OU=Servers,OU=Computers,OU=Corp,DC=lab,DC=example,DC=com' `
  -NewName 'srv01' `
  -Restart

```

Run it in **Windows PowerShell 5.1** (`powershell.exe`), not PowerShell 7\. Details below.

Domain, OU names and DC naming match my AD series: [Part 1 — the first DC](/active-directory-promote-first-domain-controller/) built `lab.example.com` on `dc01`, [Part 2 — OUs, users, GPO basics](/active-directory-part-2-ous-users-gpo-basics/) built the `OU=Corp` tree. `10.0.0.10` is the DC's IP — swap in yours.

## Before the join: the server itself

The member server needs the same basics as any new box — static IP, patched, sensible hostname. My list for that is the [Windows Server initial setup checklist](/windows-server-initial-setup-checklist/). Two things matter specifically for the join:

- **Static IP.** Not strictly required for a member server, but a server that changes IP and has stale DNS records is a support ticket waiting to happen.
- **Hostname.** If it's still `WIN-8F3K2...`, fix it. You can rename in the same step as the join with `-NewName` (above), or rename first with `Rename-Computer -NewName srv01 -Restart` and join after. Both work. Renaming *after* the join is possible but renames the AD object too and is one more thing to go sideways.

## Step 1: DNS must point at the DC

This is the one. A domain-joined machine finds its DC through DNS SRV records, and those only exist on AD-integrated DNS — usually the DC itself. If the server is still using your router, `1.1.1.1` or the hoster's resolver, the join fails with "An Active Directory Domain Controller for the domain could not be contacted".

```powershell
# What am I using right now?
Get-DnsClientServerAddress -AddressFamily IPv4 |
  Where-Object ServerAddresses | Format-Table InterfaceAlias, ServerAddresses

# Point at the DC (two DCs? list both)
$idx = (Get-NetAdapter | Where-Object Status -eq 'Up').ifIndex
Set-DnsClientServerAddress -InterfaceIndex $idx -ServerAddresses 10.0.0.10
Clear-DnsClientCache

```

Don't add a public resolver as secondary "just in case". Windows will happily ask it at some point, get NXDOMAIN for `lab.example.com`, and you get intermittent weirdness that's hell to reproduce. Public names get resolved through the forwarders on the DC — that's what they're for (set up in Part 1).

If the NIC has IPv6 enabled and gets DNS via router advertisements, check that too. An IPv6 resolver that doesn't know your domain can win the race.

## Step 2: prove you can see the DC

```powershell
# SRV record for domain controllers — must return dc01
Resolve-DnsName _ldap._tcp.dc._msdcs.lab.example.com -Type SRV

# DC locator — the same path the join uses
nltest /dsgetdc:lab.example.com

# Ports: LDAP, Kerberos, SMB
Test-NetConnection dc01.lab.example.com -Port 389
Test-NetConnection dc01.lab.example.com -Port 88
Test-NetConnection dc01.lab.example.com -Port 445

```

If `Resolve-DnsName` fails, you're still on the wrong DNS — go back to step 1\. If DNS works but ports fail, it's a firewall between the server and the DC. Part 1 lists the full port set.

## Step 3: time

Kerberos tolerates 5 minutes of clock skew. A fresh VM restored from an old template can easily be further off than that.

```powershell
w32tm /stripchart /computer:dc01.lab.example.com /samples:3 /dataonly

```

If the offset is minutes, fix it before joining. After the join, the member syncs from the domain hierarchy automatically — you don't configure NTP peers on member servers.

## Step 4: the join

```powershell
$cred = Get-Credential 'LAB\Administrator'

Add-Computer -DomainName 'lab.example.com' `
  -Credential $cred `
  -OUPath 'OU=Servers,OU=Computers,OU=Corp,DC=lab,DC=example,DC=com' `
  -Restart

```

What each piece does:

- `-DomainName` — the DNS name of the domain, not the NetBIOS name. `lab.example.com`, not `LAB`.
- `-Credential` — an account allowed to create computer objects in that OU. Domain Admin works, but see below.
- `-OUPath` — where the computer object lands. Without it, the object goes into the default computers container, which in Part 2 got redirected to `OU=Workstations` via `redircmp`. Servers belong in `OU=Servers`, so I always pass `-OUPath` explicitly.
- `-Restart` — the join only takes effect after a reboot. Leave it off if you want to reboot later, but don't forget.

Never hardcode the password into the script. `Get-Credential` prompts; in automation, pull a credential from a vault or use a pre-staged object (below).

## Don't use Domain Admin for joins

Typing Domain Admin credentials into every new box is exactly the habit [Part 3 — GPO hardening](/active-directory-part-3-gpo-hardening/) is trying to break. Two better options:

**A delegated join account.** Create a service account and give it "Create/Delete Computer objects" on `OU=Servers` only (GPMC/ADUC delegation wizard, or `dsacls`). It can join machines into that OU and do nothing else.

**Pre-stage the computer object.** On the DC, create the object first:

```powershell
New-ADComputer -Name 'srv01' `
  -Path 'OU=Servers,OU=Computers,OU=Corp,DC=lab,DC=example,DC=com'

```

Then the join attaches to the existing object instead of creating one. The account doing the join needs rights on that object (you can grant them in the object's Security tab, or give your delegated group rights on the OU). The upside: the object is in the right OU with the right name before the server even exists, and GPOs linked there apply on first boot.

Side note: by default any authenticated domain user can join up to 10 machines to the domain (`ms-DS-MachineAccountQuota`). That's a default most hardening guides tell you to set to 0 once you have proper delegation. Do it after your join process works, not before.

## After the reboot: verify

Log in with a domain account (`LAB\youradmin`), then:

```powershell
# Am I in the domain?
(Get-CimInstance Win32_ComputerSystem).Domain

# Is the secure channel to the DC healthy?
Test-ComputerSecureChannel -Verbose

# Which DC am I talking to?
nltest /sc_verify:lab.example.com

# Did the GPOs from the Servers OU apply?
gpresult /r /scope:computer

```

And from the DC side:

```powershell
Get-ADComputer srv01 | Select-Object Name, DistinguishedName, Enabled

```

The `DistinguishedName` should end in `OU=Servers,OU=Computers,OU=Corp,...`. If it's in `CN=Computers` or `OU=Workstations`, `-OUPath` didn't make it — move it with `Move-ADObject` and run `gpupdate /force` on the server.

The network profile should also flip to `DomainAuthenticated`:

```powershell
Get-NetConnectionProfile | Select-Object InterfaceAlias, NetworkCategory

```

That matters for firewall rules scoped to the Domain profile — RDP rules from the [RDP hardening post](/rdp-hardening-windows-server-nla-firewall/) depend on it.

## Server Core

Same commands. Core has `powershell.exe` and the `Add-Computer` cmdlet; `sconfig` also has a menu option for domain join if you prefer menus. More Core-specific stuff in [Windows Server Core administration](/windows-server-core-administration/).

## Gotcha: Add-Computer doesn't exist in PowerShell 7

`Add-Computer` and `Remove-Computer` were dropped from the built-in module in PowerShell 6+. In `pwsh` you get "The term 'Add-Computer' is not recognized". Open `powershell.exe` (5.1, ships with every Windows Server) and run it there. Same for anything else that leans on the old WMI-based computer cmdlets.

## Gotcha: the usual error messages and what they mean

- **"An Active Directory Domain Controller for the domain could not be contacted"** — DNS. Every time I've seen it. Step 1 and 2.
- **"The specified domain either does not exist or could not be contacted"** — also DNS, or you used the NetBIOS name where it wants the FQDN.
- **"Access is denied"** — the credential can't create objects in that OU, or the computer object already exists and the account doesn't have rights on it.
- **Join fails with an OU error** — typo in the DN, or the OU doesn't exist. Copy the DN from `Get-ADOrganizationalUnit -Filter 'Name -eq "Servers"'` instead of typing it.
- **Join works, domain logon fails after reboot** — time skew, or the secure channel broke. See below.

## Gotcha: cloned VMs

If you clone a VM template, sysprep it (`sysprep /generalize`) before the clone gets joined. Microsoft only supports sysprepped images for domain members, and two machines that started as the same unprepped image can trip over each other in ways that don't show up on day one. Same rule as Part 1's "don't snapshot DCs": don't get clever with VM copies in AD.

## When the trust breaks

"The trust relationship between this workstation and the primary domain failed." Usually after restoring a member server from an old backup or snapshot — its machine password no longer matches what AD has. Fix without leaving the domain:

```powershell
Test-ComputerSecureChannel -Repair -Credential (Get-Credential 'LAB\Administrator')

```

Log in with a local admin account to run it (domain logon is what's broken). If that fails, the nuclear option is unjoin and rejoin:

```powershell
Remove-Computer -UnjoinDomainCredential (Get-Credential 'LAB\Administrator') `
  -WorkgroupName 'WORKGROUP' -Restart

```

Then join again as above. Unjoining with domain credentials typically disables the computer object in AD rather than deleting it — check it with `Get-ADComputer srv01` before the rejoin, and decide whether you want to reuse it (keeps group memberships) or delete it and start clean.

## Where this fits

Promote DC, build OUs, join members, then harden. Once servers sit in `OU=Servers`, the policies from [Part 3](/active-directory-part-3-gpo-hardening/) and the [SMBv1 / LLMNR / NTLMv1 lockdown](/windows-server-disable-smbv1-llmnr-ntlmv1/) actually have something to apply to. For the PowerShell basics underneath all of this, my [PowerShell sysadmin cheatsheet](/powershell-sysadmin-cheatsheet/) covers the syntax.

---

### Related posts

- [Reset a Windows Password with the Sticky Keys Trick (sethc.exe)](https://blog.diengdoh.com/windows-password-reset-sticky-keys-sethc/?ref=blog.sudowl.com)
- [RDP Hardening on Windows Server — NLA, IP Allowlist, and Audit](https://blog.diengdoh.com/rdp-hardening-windows-server-nla-firewall/?ref=blog.sudowl.com)
- [Windows Server Hardening — Kill SMBv1, LLMNR, and NTLMv1](https://blog.diengdoh.com/windows-server-disable-smbv1-llmnr-ntlmv1/?ref=blog.sudowl.com)