The netlogon log file exists on all Active Directory domain controllers and contains a wealth of information. But, how it records information is a mess.
In this post, you’re going to learn how to use PowerShell to read and parse the netlogon log file by solving a real problem; tracking down roaming clients.
As long as AD has been around, there have been roaming clients. Roaming clients are those domain-joined machines that aren’t assigned to an AD site. These computers don’t have an Active Directory subnet defined to a site. They have no way to know what site they’re in.
These computers are problematic because they randomly choose a domain controller to authenticate to. Authentication requests could take much longer than expected if the client decides to select a domain controller (DC) across the globe.
It’s important to recognize these roaming clients and to remediate them whenever possible.
Searching the netlogon log File
The process of hunting down these clients is pretty simple. You need to query a log file on each domain controller in your AD forest. This log file contains lines with the string NO_CLIENT_SITE in them. You can be sure if you see an instance of this line you’ve got a client that’s gone roaming.
You could check this log file yourself across your DCs, but that’s not too fun. Let’s automate this task with PowerShell!
You’re looking for for a log file called netlogon.log on each DC. If any clients start roaming, the DC that they authenticate to will record that activity in this file. This file is located in the C:\Windows\Debug folder of each domain controller. This file is where we’ll look for that NO_CLIENT_SITE reference.
Enumerating all DCs in a Forest
To account for all clients across the domain, you’ll need to find all DCs in the forest. To do that, use both the Get-ADForest
and Get-AdDomainController
PowerShell cmdlets.
Get-AdForest
returns a property called Domains
that will show all domains in the forest. Once you have all the domains in the forest, you can then find all domain controllers in each of those domains with Get-ADDomainController
.
$dcs = ((Get-ADForest).Domains | foreach {(Get-ADDomainController-Server $_ -Filter *) }).HostName
In the above example, I’m only outputting the HostName
, which is the FQDN of each domain controller.
Automating Text File Searching on a DC
Now that you have all the DCs in the forest, you’ll need to develop some code to query each of them. As good practice, I always prepare the code against one first. Once you have this, it’s easy to expand to all domain controllers.
One way to find search text files with PowerShell is to use the Select-String
cmdlet. Select-String
is a cmdlet that allows you to specify a regular expression as a pattern to search for. Select-String
can search for patterns inside of a string or a file with the Path
parameter.
In the example below, I’m searching for all lines in the netlogon log (netlogon.log) file with the string NO_CLIENT_SITE that look like this:
12/25 19:36:41 CHILD: NO_CLIENT_SITE: MYCLIENT 192.168.0.10
If there’s a match, pull the name (MYCLIENT) out of that line. To do that, use the regular expression NO_CLIENT_SITE: (.*) \d
. Then, pass that regular expression and the path to the netlogon.log file to Select-String
.
Select-String -Pattern 'NO_CLIENT_SITE: (.*) \d' -Path "\\MYDOMAINCONTROLLER\c$\windows\debug\netlogon.log"
This output is great, but I only want to see client names. To do this, look at each of the objects that Select-String
outputs. Then find the value that came from that regular expression we used.
That value can be found buried inside of the second Groups
object inside of the Matches
property for each line that was matched.
$_.Matches.Groups[1].Value
You now have code that looks something like this:
Select-String -Pattern 'NO_CLIENT_SITE: (.*) \d' -Path '\\MYDOMAINCONTROLLER\c$\windows\debug\netlogon.log' | foreach {
$_.Matches.Groups[1].Value
}
You’re still not done, however. You’ll find that if a client has been roaming for a while, it will be recorded in the log many times.
To remove all the duplicates, pipe the objects from Select-String
to the Group-Object
cmdlet. This process will remove all duplicates and give you a way to find unique client names.
Select-String -Pattern 'NO_CLIENT_SITE: (.*) \d' -Path '\\MYDOMAINCONTROLLER\c$\windows\debug\netlogon.log' | foreach {
$_.Matches.Groups[1].Value
} | Group-Object
The final step is to output only the client names from the output of Group-Object
.
$clients = Select-String -Pattern 'NO_CLIENT_SITE: (.*) \d' -Path '\\MYDOMAINCONTROLLER\c$\windows\debug\netlogon.log' | foreach {
$_.Matches.Groups[1].Value
} | Group-Object
$clients | foreach {
$_.Name
}
Expanding the netlogon log Search to all DCs
You now have the code to do a single DC. Querying all DCs, at this point, is a piece of cake with a foreach loop.
Below is the whole code block you can use.
$dcs = ((Get-ADForest).Domains | foreach { (Get-ADDomainController -Server $_ -Filter *) }).HostName
foreach ($d in $dcs) {
$output = @{'DomainController' = $d}
$clients = Select-String -Pattern 'NO_CLIENT_SITE: (.*) \d' -Path "\\$d\c`$\windows\debug\netlogon.log" | foreach {
$_.Matches.Groups[1].Value
} | Group-Object
if ($clients) {
$clients | foreach {
$output.Client = $_.Name
[pscustomobject]$output
}
}
}
Summary
With a little knowledge of where roaming clients can be found in and some PowerShell, you can knock out this problem quickly. Build a script that will query each netlogon.log file in the entire domain, sit back and see how bad the problem is. Now actually fixing those issues is up to you!