2013/10/21

PowerShell - Report the AD Missing Subnets from the NETLOGON.log

Today I will share with you a script that report the Missing Subnets detected in the NetLogon file(s) of your Active Directory Domain Controller(s).

Update: See my Github repository for the most recent version

Missing Subnets

When a computer is joined to a domain It knows for sure of which AD domain it is a member. However once the computer is joined to the domain, It may or may not know which AD site it belongs to. Even if it thinks it knows the AD site, it may not even be in the correct AD site (e.g. because it was moved, AD site was renamed, Subnet not declared, Subnet was removed from a site and add to another...etc.).



Fixing this issue

In the Active Directory Sites and Services console, your need to associate create all of your subnets in these subnets with the appropriate site(s). It is important to note that with Windows Server 2012 R2 some new cmdlets are available with the Active Directory module to manage the Site subnets: Get-ADReplicationSubnetNew-ADReplicationSubnetSet-ADReplicationSubnet and Remove-ADReplicationSubnet.


NETLOGON.log

If some subnets are not declared in your Active Directory and/or not assigned to Site, you might start to see those kind of message in your NetLogon.log file.

Path of the NETLOGON.log file on a Domain Controller:
\\<dcname>\admin$\debug\netlogon.log

Missing subnets errors in NetLogon.log
10/02 10:02:32 FX: NO_CLIENT_SITE: WORKSTATION01 10.126.76.146
10/02 10:02:32 FX: NO_CLIENT_SITE: WORKSTATION02 172.16.32.16
10/02 10:03:07 FX: NO_CLIENT_SITE: WORKSTATION03 1.2.3.4

A NetLogon.log exists on all the Domain Controllers of your domain, so you need to check every single of them to have the full list of subnets to add.


PowerShell Reporting

So I created a PowerShell script to handle this task and report all the Missing subnets automatically (every month in my case). Here is a screenshot of the final report. In my opinion, this does not need to run everyday or every week.




How the script work





  1. Get the list of Domain Controllers in the Domain using .NET
  2. Get the Last 200 Lines from the NETLOGON.log on each Domain controllers (200 is default)
  3. Process Logs and Compile in one list and keep one entry per IP
  4. Export the AD Missing Subnet to a CSV file locally
    1. Exported in: $scriptPathOutput\$DateFormat-AD-SITE-MissingSubnets.csv
  5. Send an Email Report.
The report will contains:

  • One table with the Missings Subnets from all the Domain Controllers
  • The other error(s) found in the last 200 lines of each NETLOGON.log on each Domain Controllers (200 is default)


Requirement

  • A Task scheduler to execute the script every x weeks
  • Permission to Read \\DC\admin$, a basic account without specific rights will do it
  • Permission to write locally in the Output folder ($ScriptPath\Output)

Running the script

With Verbose

PS C:\User\Francois-Xavier> ./TOOL-AD-SITE-Report_Missing_Subnets.ps1 -Verbose -EmailServer mail.fx.lab -EmailTo "catfx@fx.lab" -EmailFrom Reporting@fx.lab -EmailSubject "AD - MIssing Subnets"
VERBOSE: Domain: FX.LAB
VERBOSE: Getting all Domain Controllers from FX.LAB ...
VERBOSE: Gathering Logs from Domain controllers
VERBOSE: Gathering Logs from DC: LAB1DC01.FX.LAB
VERBOSE: LAB1DC01.FX.LAB - NETLOGON.LOG - Copying...
VERBOSE: LAB1DC01.FX.LAB - NETLOGON.LOG - Copied
VERBOSE: Importing exported data to a CSV format...
VERBOSE: Missing Subnet(s) Found: 6
VERBOSE: Other Error(s) Found: 11
VERBOSE: Building the HTML Report
VERBOSE: CSV file (backup) - Exporting...
VERBOSE: CSV file (backup) - Exported to: 20131010_031517-AD-SITE-MissingSubnets.csv
VERBOSE: Preparing the Email
VERBOSE: Email Sent!
VERBOSE: Cleanup txt and log files...
VERBOSE: Script Completed

This will generate the report inserted at the beginning of this article.


Validating the Email Addresses

[Parameter(Mandatory=$true,HelpMessage="You must specify the Sender Email Address")]
[ValidatePattern("[a-z0-9!#\$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#\$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")]
[String]$EmailFrom,
[Parameter(Mandatory=$true,HelpMessage="You must specify the Destination Email Address")]
[ValidatePattern("[a-z0-9!#\$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#\$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?")]
[String[]]$EmailTo,

For the email addresses validation, at first I wanted to use the [mailaddress] class, but this only work since PowerShell v3.0 so I decided to add the previous regex so it is supported on PowerShell v2.0 too.

Note that I also use the [ValidatePattern] attribute declaration, which is super useful! +Jeffery Hicks  wrote a great article about it last year.

Gathering the information from all the different Netlogon.log files

# NETLOGON.LOG path for the current Domain Controller
$path = "\\$DCName\admin`$\debug\netlogon.log"

# Testing the $path
IF ((Test-Path -Path $path) -and ((Get-Item -Path $path).Length -ne $null))
{
    IF ((Get-Content -Path $path | Measure-Object -Line).lines -gt 0){
        #Copy the NETLOGON.log locally for the current DC
        Write-Verbose -Message "$DCName - NETLOGON.LOG - Copying..."
        Copy-Item -Path $path -Destination $ScriptPathOutput\$($dc.Name)-$DateFormat-netlogon.log 
        
        #Export the $LogsLines last lines of the NETLOGON.log and send it to a file
        ((Get-Content -Path $ScriptPathOutput\$DCName-$DateFormat-netlogon.log -ErrorAction Continue)[$LogsLines .. -1]) | 
            Out-File -FilePath "$ScriptPathOutput\$DCName.txt" -ErrorAction 'Continue' -ErrorVariable ErrorOutFileNetLogon
        Write-Verbose -Message "$DCName - NETLOGON.LOG - Copied"
    }#IF
    ELSE {Write-Verbose -Message "File Empty"}
}ELSE{Write-Warning -Message "$DCName NETLOGON.log is not reachable"}

Combining the logs

# Combine all the TXT file in one
$FilesToCombine = Get-Content -Path $ScriptPathOutput\*.txt -ErrorAction SilentlyContinue
if ($FilesToCombine){
    $FilesToCombine| Out-File -FilePath $ScriptPathOutput\$dateformat-All_Export.txt

    # Convert the TXT file to a CSV format
    Write-Verbose -Message "Importing exported data to a CSV format..."
    $importString = Import-Csv -Path $scriptpathOutput\$dateformat-All_Export.txt -Delimiter ' ' -Header Date,Time,Domain,Error,Name,IPAddress
    
    #  Get Only the entries for the Missing Subnets
    $MissingSubnets = $importString | Where-Object {$_.Error -like "*NO_CLIENT_SITE*"}
    Write-Verbose -Message "Missing Subnet(s) Found: $($MissingSubnets.count)"
    #  Get the other errors from the log
    $OtherErrors    = Get-Content $scriptpathOutput\$dateformat-All_Export.txt | Where-Object {$_ -notlike "*NO_CLIENT_SITE*"} | Sort-Object -Unique
    Write-Verbose -Message "Other Error(s) Found: $($OtherErrors.count)"

Checking the Comment based Help

PS C:\LazyWinAdmin> Get-help .\AD-Find_missing_subnets_in_ActiveDirectory.ps1 -full
NAME
    C:\LazyWinAdmin\AD-Find_missing_subnets_in_ActiveDirectory_20131020.ps1

SYNOPSIS
    This script goal is to get all the missing subnets from the
    NETLOGON.LOG file from each Domain Controllers in the Active Directory.
    It will copy all the NETLOGON.LOG locally and parse them.

SYNTAX
    C:\LazyWinAdmin\AD-Find_missing_subnets_in_ActiveDirectory_20131020.ps1
    [-EmailFrom] <String> [-EmailTo] <String[]> [-EmailServer]<String>
    [[-EmailSubject] <String>] [[-LogsLines] <Int32>] [<CommonParameters>]


DESCRIPTION
    This script goal is to get all the missing subnets from the
    NETLOGON.LOG file from each Domain Controllers in the Active Directory.
    It will copy all the NETLOGON.LOG locally and parse them.


PARAMETERS
    -EmailFrom <String>
        Specifies the Email Address of the Sender

        Required?                    true
        Position?                    1
        Default value
        Accept pipeline input?       false
        Accept wildcard characters?  false

    -EmailTo <String[]>
        Specifies the Email Address(es) of the Destination

        Required?                    true
        Position?                    2
        Default value
        Accept pipeline input?       false
        Accept wildcard characters?  false

    -EmailServer <String>
        Specifies the Email Server IPAddress/FQDN

        Required?                    true
        Position?                    3
        Default value
        Accept pipeline input?       false
        Accept wildcard characters?  false

    -EmailSubject <String>
        Specifies the Email Subject

        Required?                    false
        Position?                    4
        Default value                Report - Active Directory - SITE - Missing Subnets
        Accept pipeline input?       false
        Accept wildcard characters?  false

    -LogsLines <Int32>
        Specifies the number of Lines to check in the NETLOGON.LOG files
        Default is '-200'.
        This number is negative, so the script check the last x lines (newest entry).
        If you put a positive number it will check the first lines (oldest entry).

        Required?                    false
        Position?                    5
        Default value                -200
        Accept pipeline input?       false
        Accept wildcard characters?  false

    <CommonParameters>
        This cmdlet supports the common parameters: Verbose, Debug,
        ErrorAction, ErrorVariable, WarningAction, WarningVariable,
        OutBuffer, PipelineVariable, and OutVariable. For more information, see
        about_CommonParameters (http://go.microsoft.com/fwlink/?LinkID=113216).

INPUTS

OUTPUTS

NOTES


        NAME:    TOOL-AD-SITE-Report_Missing_Subnets.ps1
        AUTHOR:  Francois-Xavier CAT
        DATE:    2011.10.11
        EMAIL:   info@lazywinadmin.com

        REQUIREMENTS:
        -A Task scheduler to execute the script every x weeks
        -Permission to Read \\DC\admin$, a basic account without specific rights will do it
        -Permission to write locally in the Output folder ($ScriptPath\Output)

        VERSION HISTORY:
        1.0 2011.10.11
            Initial Version.
        1.1 2011.11.12
            FIX System.OutOfMemoryException Error when too many logs to process
                Now the script will copy the file locally.
        1.2 2012.09.22
            UPDATE Code to report via CSV/Email
        1.3 2013.10.14
            UPDATE the syntax of the script
        1.4 2013.10.20
            ADD ValidatePattern on Email parameters, instead of [mailaddress] which is only supported on PS v3

    -------------------------- EXAMPLE 1 --------------------------

    C:\PS>./TOOL-AD-SITE-Report_Missing_Subnets.ps1 -Verbose -EmailServer mail.fx.local -EmailTo
    "Contact1@fx.local","Contact2@fx.local" -EmailFrom ADREPORT@fx.local -EmailSubject "Report - AD - Missing Subnets"


    This example will query all the Domain Controllers in the Active Directory and get the last 200 lines (Default) of
    each NETLOGON.log files. It will then send an email report to Contact1@fx.local and Contact2@fx.local.






RELATED LINKS




Download

TechNet Repository
Github Repository (Most updated version)

More Information

Using Catch-All Subnets in Active Directory
DC Locator – What Does "NO_CLIENT_SITE" Mean In Netlogon.log
Ask the Directory Services Team - Sites Sites Everywhere…
How Domain Controllers Are Located in Windows XP


Thanks for Reading! If you have any questions, leave a comment or send me an email at fxcat@lazywinadmin.com. I invite you to follow me on Twitter @lazywinadm / Google+ / LinkedIn You can also follow the LazyWinAdmin Blog on Facebook Page and Google+ Page

5 comments:

  1. Hi, I am not sure if i am doing something wrong or not. I have run this script a number of times but I never ever get the email. I can run other scripts and I receive the emails fine.

    ReplyDelete
    Replies
    1. Hi Guji,

      Did you run the script in the command line (which sufficient permissions) and with verbose ?
      What errors are returned and does it give you an error ?

      Thank you

      Delete