
Find AD Computer Accounts That Have No DNS Record
Before you run this
This guide gives you a read-only PowerShell script that lists every enabled AD computer account and flags the ones whose DNS A record can't be resolved — the classic symptom of a machine that was renamed, rebuilt, or removed without cleaning up DNS, or a zone where scavenging has drifted out of sync with AD. It reads AD and queries DNS; it does not create, change, or delete anything.
Because it only reads, it does not need Domain Admin or an elevated shell. It needs an account that can read computer objects in the domain (any authenticated user can, by default) and the ActiveDirectory PowerShell module from RSAT. You do need to be able to query your DNS servers, which is normal from a domain-joined host.
Even so:
- Read the script before you run it. Understand what
Get-ADComputerandResolve-DnsNameare doing before you paste it. - Test the scope first. Run it against a single OU or a handful of machines (shown below) before you turn it loose on the whole domain, so you can sanity-check the output against machines you know are healthy.
- This script itself is safe — there is nothing to undo. The danger is in what you might do next. The "no A record" list is a starting point for investigation, not a delete list. A machine can be powered off, on a network segment your DNS view doesn't cover, or waiting on scavenging. Do not feed this output straight into
Remove-ADComputer. Confirm each candidate by hand first.
There is no firewall or appliance change here and nothing to roll back, so the usual out-of-band/maintenance-window caution doesn't apply — but the "don't act on the results blindly" caution very much does.
What I'm assuming
- Windows Server 2019/2022 AD with AD-integrated DNS.
- You're running from a domain-joined admin workstation or a DC with Windows PowerShell 5.1 and the RSAT ActiveDirectory module installed.
- The host you run from resolves DNS against the same servers that hold your AD zone. If it doesn't, point the query at a specific server with
Resolve-DnsName -Server(noted below), or the results will be misleading.
If you're on PowerShell 7, the ActiveDirectory module works via the compatibility layer, but I'm writing for 5.1.
The script
Save this as Find-MissingDnsComputers.ps1. It pulls enabled computer accounts, tries to resolve each one's DNSHostName to an A record, and reports the ones that fail — sorted by last logon so the genuinely dead machines float to the top.
#Requires -Modules ActiveDirectory
# --- Settings you may want to change ---
$SearchBase = $null # $null = whole domain. Or "OU=Servers,DC=example,DC=com"
$OutputCsv = "C:\path\to\missing-dns.csv" # where to write the report
$DnsServer = $null # $null = use this host's resolver, or e.g. "10.0.0.10"
# ---------------------------------------
# Build the Get-ADComputer parameters; only add -SearchBase if one was set
$adParams = @{
Filter = 'Enabled -eq $true'
Properties = 'DNSHostName', 'LastLogonDate'
}
if ($SearchBase) { $adParams['SearchBase'] = $SearchBase }
$computers = Get-ADComputer @adParams
$results = foreach ($c in $computers) {
$status = 'OK'
$ip = $null
if (-not $c.DNSHostName) {
# Some accounts (rare) never populated this attribute
$status = 'No DNSHostName attribute'
}
else {
# Build Resolve-DnsName args so we only pass -Server when one is set
$dnsParams = @{ Name = $c.DNSHostName; Type = 'A'; ErrorAction = 'Stop' }
if ($DnsServer) { $dnsParams['Server'] = $DnsServer }
try {
$answer = Resolve-DnsName @dnsParams
$ip = ($answer | Where-Object { $_.Type -eq 'A' }).IPAddress -join ', '
if (-not $ip) { $status = 'No A record' }
}
catch {
# Resolve-DnsName throws when the name doesn't resolve
$status = 'No A record'
}
}
if ($status -ne 'OK') {
[pscustomobject]@{
Name = $c.Name
DNSHostName = $c.DNSHostName
LastLogonDate = $c.LastLogonDate
Status = $status
IPAddress = $ip
}
}
}
# Show on screen and save a copy
$results | Sort-Object LastLogonDate | Format-Table -AutoSize
$results | Export-Csv -Path $OutputCsv -NoTypeInformation -Encoding UTF8
Write-Host "Flagged $($results.Count) computer account(s). Report: $OutputCsv"
Test the scope before the whole domain
Set $SearchBase to one OU you know well and run it there first:
# In the script, set:
$SearchBase = "OU=Servers,DC=example,DC=com" # replace with a real OU of yours
Replace example.com/DC=example,DC=com, the OU path, and C:\path\to\ with your own values. Confirm the machines it flags really are absent from DNS before you trust the full run.
A note on the two moving parts
LastLogonDateis per-DC and approximate. It's replicated but not precise. It's fine for triage — a machine that hasn't logged on in a year and has no DNS record is a strong candidate — but don't treat the date as authoritative.Resolve-DnsNamecan read the client cache. A recently-resolved name can show as present even if the zone record is gone. If you want to be strict, target the authoritative server directly with$DnsServer, which forces the query at that server rather than through your local resolver.
If you'd rather compare AD against the zone data itself — which sidesteps the resolver entirely and also surfaces the opposite problem, stale records with no matching computer — the DnsServer module's Get-DnsServerResourceRecord (run against your DNS server) is the standard tool for that. I'm not walking through it here, but it's the right cmdlet to look up in Microsoft Learn if the resolver-based approach isn't strict enough for you.
Verifying the results
Pick one machine the report flagged and confirm it by hand from the same host:
Resolve-DnsName -Name "flagged-host.example.com" -Type A -Server "10.0.0.10"
nslookup flagged-host.example.com 10.0.0.10
Replace the name and server with real values. If both agree there's no A record, the flag is real. Do the reverse too: pick a machine you know is healthy and confirm the script did not flag it — that tells you your DNS view is correct and you're not generating false positives because the query host can't see the right zone.
Open the CSV and eyeball the LastLogonDate column. Machines that logged on today but have no A record are the interesting ones — those are live hosts with a real DNS problem, not just old accounts.
What to do next (carefully)
There's nothing to undo in this script, but the follow-up is where mistakes happen. Before removing anything:
- Confirm the machine is genuinely gone — ping it, check your inventory, check with the team that owns it.
- Consider disabling the computer account first and leaving it a couple of weeks, rather than deleting immediately.
- If you do remove accounts,
Remove-ADComputersupports-WhatIf. Run it with-WhatIffirst, review exactly what it would delete, and only then run it for real. Deleting a computer object is not something you want to reverse from a backup restore.
For the cmdlet details — Get-ADComputer, Remove-ADComputer, and Resolve-DnsName — see their reference pages on Microsoft Learn for the exact parameters supported by your module version.
Runs enterprise networks and security for a living, and writes Shore Up to turn two decades of hands-on Linux, Windows and mail-server work into guides you can actually use.
More about the author →Was this article helpful?
Tap a star — no sign-in needed.
Be the first to rate this article.
Related guides
Find Disabled AD Users That Still Hold Group Membership
A disabled account is not a harmless account. If it still holds membership in security groups, it still carries the access rights those groups grant — file shares, application roles, delegated permissions. If that account is ever re-enabled (by mistake, or by an attacker who compromised a helpdesk process), it lights up with all its old access instantly. Periodically finding disabled users that still sit in security groups is a basic hygiene task, and it is easy to script.
Find and Merge Duplicate Active Directory User Accounts
Before I start, one honesty note that shapes this whole guide: Active Directory has no merge operation . There is no Merge-ADUser cmdlet, and there never was. When people say "merge duplicate accounts," what they actually need is a repeatable process to find the duplicates, decide which one survives , copy the things that matter (mainly group memberships) onto the survivor , and then retire the other . That is what this guide automates. The detection half is safe and read-only. The consolidation half changes and can delete accounts, so it is gated hard.
Automate Moving AD Users Between OUs From a CSV
This procedure reads a CSV of user accounts and their destination OUs, then moves each account to its target OU with PowerShell's Move-ADObject . Moving a user changes its distinguished name (DN). That matters because anything scoped by DN or OU — Group Policy links, delegated permissions, and OU-based filters — will start or stop applying to the account the moment it moves. The move itself does not delete the account or its group memberships, and it is reversible if you know where the account came from — which is why the script below records the original OU of every user before it touches anything.
Automate DNS Record Audits on Windows DNS with PowerShell
This guide builds a read-only audit of a Microsoft DNS server: it enumerates the zones, exports every resource record to CSV, and produces a short report of records that look stale (dynamic records whose aging timestamp is older than a threshold you set). The audit script itself creates nothing and deletes nothing — its worst case is a CSV file on disk.




