How to Certificate Authority (CA) Maintenance

You can perform Certificate Authority maintenance operations on your Certificate Authority servers in three different stages according to your needs. Regular maintenance ensures better performance and security for your Certificate Authority system.

  • Certificate Authority Backup
  • Certificate Authority Transaction Log Truncate
  • Certificate Authority Database Defrag

Certificate Authority Backup

If your backup application supports taking a dedicated backup for the Certificate Authority, you can proceed accordingly.

The method described here is related to backing up the Certificate Authority environment without third-party applications.

Exporting Certificate Authority Registry Values: Export the Certificate Authority registry values from both the “CertSvc” directory and the “Configuration” directory to ensure proper backup.

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\CertSvc

The registry contains configuration information related to the Certificate Authority.

Initiating the Backup Process: After exporting the registry, start the backup process from the Certificate Authority console.

Backup Selection: Select “Private key and CA certificate” and “Certificate database and certificate log”, then specify the backup path before proceeding.

Certificate Authority Transaction Log Truncate

When you check the Certificate Authority directory, you will see log files generated by the Certificate Authority.

You can view the database and log files of the Certificate Authority by running the following command in the command prompt:

certutil -databaselocations

Depending on the usage of the Certificate Authority, these log files may accumulate in large numbers and cause disk space issues.

To truncate these log files, simply take a backup through the Certificate Authority console.

Automatic Log Truncation: Completing the backup process automatically truncates the logs.

Certificate Authority Database Defrag

Certificate Authority Database: The Certificate Authority server stores certificates and configurations in a .edb database file.

This database can grow in size depending on the status of the certificates in your environment (revoked, issued, pending, failed) and templates.

As the database size increases, the backup process will take longer. Therefore, it is important to monitor the database size.

Defragmentation Requirement: If the database size increases significantly, run a defragmentation process to optimize performance.

To perform a defrag operation, follow these steps:

  • Stop the CA service

You can do this through the Certificate Authority console or by running the following command in Command Prompt (Run as Administrator)

net stop certsvc

  • Run the defrag operation

Execute the following command in Command Prompt (Run as Administrator)

esentutl /d "FULL-PATH-TO-EDB-FILE"

The duration of the defragmentation process will vary depending on the database size.

  • Restart the CA service

Restarting the CA Service: After completing the defragmentation, restart the CA service to apply changes.

net start certsvc
You can check your database size after the defrag process.
This process helps optimize the database and ensures that the Certificate Authority server operates efficiently.
If you found this article helpful, feel free to share it with your team and check out my blog for more quick tips and insights!

How to Bulk Add Users to an Active Directory Distribution Group

This PowerShell script adds users from a CSV file to a Distribution Group while checking existing memberships.

It saves the group members before and after the operation in “Before.csv” and “After.csv”, making it easy to track changes.

Features:

✅ Checks existing members to avoid duplicate additions.

✅ Lists newly added and already existing users separately.

✅ Identifies users that do not exist in Active Directory.

✅ Saves group members to CSV before and after the update, allowing easy change tracking.

This script is ideal for preventing errors, avoiding redundant operations, and keeping a record of changes while managing AD groups. 🚀

You can also make changes in line with your needs.

 

CSV File

It is not a problem for the email address to be displayed; the script checks user accounts based on the email address.

While performing this task, it will be sufficient to provide the list with email addresses.

Email

bruce.banner@ustundag.com

jackie.chan@ustundag.com

jet.li@ustundag.com

bruce.lee@ustundag.com

yusuf.ustundag@ustundag.com

 

If you found this powershell script helpful, feel free to share it with your team and check out my blog for more quick tips and insights!

Active Directory DSRM Password Change Tool

What is Active Directory Directory Service Restore Mode (DSRM)?

Active Directory Directory Services Restore Mode (DSRM) is a special boot mode in Windows Server operating systems used for recovery and maintenance of the Active Directory database.

What Does It Do?

  • Recover Active Directory Database: When the Active Directory (AD) database becomes corrupted or inaccessible, Directory Services Restore Mode (DSRM) enables the system to boot securely and repair the database efficiently.
  • Restore Active Directory from Backup: Use Directory Services Restore Mode (DSRM) to restore the Active Directory database, ensuring data integrity and a successful system recovery.
  • Troubleshooting Database Issues: DSRM enables maintenance and troubleshooting tasks on the AD database.
  • Isolated Recovery from Attacks: It allows a secure recovery by isolating Active Directory from the network.

DSRM Password Storage: The system stores the DSRM password locally, keeping it independent of the Active Directory domain. This password remains unused during normal operations and is only required for recovery tasks.

Security Recommendations: To enhance Active Directory security, update the DSRM password every 6 to 12 months on each Active Directory server.

Case Example: You may have multiple domain environments in your company’s infrastructure and multiple Active Directory servers in these domain environments.
You can use the following script to perform DSRM password updates centrally on these servers.

Features of the Script:

  • Centralized or Individual Updates: The script can update the DSRM password for all AD servers in a domain or for individual servers.
  • Use Unique, Complex Passwords: Generate passwords with 16, 18, or 20 characters to ensure uniqueness and complexity for every AD server.
  • Export Results: After the password update, the script exports a CSV file containing the domain name, hostname, IP address, and the generated password for each AD server.

Requirements:

  • Domain Admin privileges
  • Administrator Privileges Required: Run the necessary commands with administrator privileges to apply these security measures.

DSRM Password Manager  GUI Interface, If you want to try the script I wrote, you can get it on Github

If you found this powershell script helpful, feel free to share it with your team and check out my blog for more quick tips and insights!

How to Change Active Directory OU Ownership

AD OU Owner Manager is a user-friendly tool that allows you to view and securely change Organizational Unit (OU) ownerships in your Active Directory environment. This tool provides a solution especially for those who want to standardize OU ownerships with Domain Admins or other administrator groups.

Core Features

OU Owner Report Section:

  • Automatically scan all current OU ownership information with the Generate OU Owner Report button
  • All information is automatically exported to a CSV file for later review and documentation purposes

Change OU Owner Section:

  • Select Domain Admins or other administrator groups (containing admin value) from the dropdown menu
  • Assign the selected group as the owner of all OUs using the Change Owner with Selected Group button
  • View change results in the right panel after the operation
  • All changes are automatically recorded in a CSV file for change management and audit purposes

Why You Should Use This Tool

  • Easy to Use: Manage OU ownerships through a simple GUI interface
  • Bulk Operations: Change ownership of all OUs with a single click
  • Audit Compliance: Provide evidence for security audits with automatically generated CSV reports
  • Time Saving: Reduce hours of manual work to minutes
  • Error Prevention: Eliminate human errors that can occur during manual ownership changes

Usage Steps

Document Current State:

Launch the AD OU Owner Manager powershell script on ISE Click on “Generate OU Owner Report” Save the generated CSV report (as evidence of pre-change state)

  • Change Ownerships:

Select “Domain Admins” group or your preferred administrator group from the dropdown menu Click on “Change Owner with Selected Group” Review the results on screen when the process completes Save the automatically generated post-change CSV report

  • Verify Results:

Review the post-change report to ensure all OUs were correctly updated In case of any errors, manual corrections can be made using the previous CSV report

Security Benefits

Using this tool to transfer OU ownerships to the Domain Admins group provides these advantages:

Standardized Permissions: Consistent ownership and permission structure for all OUs Reduced Attack Surface: Elimination of security risks from scattered permission structures Simplified Management: Easier tracking of changes through centralized management Audit Readiness: Easy demonstration that OU ownerships are correctly configured during security audits

Periodically use the tool (e.g., quarterly) to check OU ownerships

GUI Interfaces , If you want to try the script I wrote, you can get it on Github

If you found this powershell script helpful, feel free to share it with your team and check out my blog for more quick tips and insights!

 

Windows DNS Name Server (NS) Record Management: Analysis & Reverse Lookup Zone Updates

Analyzing and Updating Name Server (NS) Records on Windows DNS Servers

Managing Windows DNS is crucial for ensuring that network infrastructures remain up-to-date and function correctly. The accuracy and timeliness of Name Server (NS) records are essential elements that must be frequently monitored. In this article, we will discuss how a script operates to analyze and update Name Server records on Windows DNS servers.

How Does the Script Work?

The developed script runs on Windows DNS servers and performs the following key operations:

  1. Scanning All Name Server Records:
    • When the “Analyze” button is pressed, the script scans all Forward Lookup Zone and Reverse Lookup Zone areas and lists the current Name Server (NS) records.
  2. Forward Lookup Zone and Reverse Lookup Zone Scanning:
    • It collects the existing Name Server records for the Forward Lookup Zone and Reverse Lookup Zone but does not make any modifications.
  3. Updating Reverse Lookup Zone:
    • When the “Update NS” button is pressed, the NS records in the Reverse Lookup Zone are checked.
    • Missing NS records are added.
    • Old and unused NS records are removed.
  4. Listing Old Name Server Records:
    • No updates are made for the Forward Lookup Zone, but outdated and unused NS records are displayed for the system administrator’s review.

Name Server Checker & Reverse Lookup Zone Update

# DNS Active NS Record Manager GUI Application

Add-Type -AssemblyName System.Windows.Forms

Add-Type -AssemblyName System.Drawing

# Create the main form

$form = New-Object System.Windows.Forms.Form

$form.Text = 'DNS Active NS Record Manager'

$form.Size = New-Object System.Drawing.Size(900,700)

$form.StartPosition = 'CenterScreen'

$form.FormBorderStyle = 'FixedDialog'

$form.MaximizeBox = $false

$form.BackColor = [System.Drawing.Color]::FromArgb(240,240,240)

# Create banner panel

$bannerPanel = New-Object System.Windows.Forms.Panel

$bannerPanel.Size = New-Object System.Drawing.Size(900,60)

$bannerPanel.Location = New-Object System.Drawing.Point(0,0)

$bannerPanel.BackColor = [System.Drawing.Color]::FromArgb(0,120,215)

$form.Controls.Add($bannerPanel)

# Create banner title

$bannerTitle = New-Object System.Windows.Forms.Label

$bannerTitle.Text = "DNS Active NS Record Management"

$bannerTitle.ForeColor = [System.Drawing.Color]::White

$bannerTitle.Font = New-Object System.Drawing.Font("Segoe UI", 16, [System.Drawing.FontStyle]::Regular)

$bannerTitle.AutoSize = $true

$bannerTitle.Location = New-Object System.Drawing.Point(20,15)

$bannerPanel.Controls.Add($bannerTitle)

# Create main panel for controls

$mainPanel = New-Object System.Windows.Forms.Panel

$mainPanel.Size = New-Object System.Drawing.Size(860,580)

$mainPanel.Location = New-Object System.Drawing.Point(20,80)

$mainPanel.BackColor = [System.Drawing.Color]::White

$form.Controls.Add($mainPanel)

# Create DNS Server label and textbox

$lblDNSServer = New-Object System.Windows.Forms.Label

$lblDNSServer.Location = New-Object System.Drawing.Point(20,20)

$lblDNSServer.Size = New-Object System.Drawing.Size(100,20)

$lblDNSServer.Text = 'DNS Server:'

$lblDNSServer.Font = New-Object System.Drawing.Font("Segoe UI", 9)

$mainPanel.Controls.Add($lblDNSServer)

$txtDNSServer = New-Object System.Windows.Forms.TextBox

$txtDNSServer.Location = New-Object System.Drawing.Point(120,20)

$txtDNSServer.Size = New-Object System.Drawing.Size(200,20)

$txtDNSServer.Text = 'localhost'

$txtDNSServer.Font = New-Object System.Drawing.Font("Segoe UI", 9)

$mainPanel.Controls.Add($txtDNSServer)

# Create Active NS Servers ListView

$lblActiveNS = New-Object System.Windows.Forms.Label

$lblActiveNS.Location = New-Object System.Drawing.Point(20,60)

$lblActiveNS.Size = New-Object System.Drawing.Size(150,20)

$lblActiveNS.Text = 'Active NS Servers:'

$lblActiveNS.Font = New-Object System.Drawing.Font("Segoe UI", 9)

$mainPanel.Controls.Add($lblActiveNS)

$listActiveNS = New-Object System.Windows.Forms.ListView

$listActiveNS.Location = New-Object System.Drawing.Point(20,85)

$listActiveNS.Size = New-Object System.Drawing.Size(400,150)

$listActiveNS.View = [System.Windows.Forms.View]::Details

$listActiveNS.FullRowSelect = $true

$listActiveNS.GridLines = $true

$listActiveNS.Font = New-Object System.Drawing.Font("Segoe UI", 9)

$listActiveNS.Columns.Add("NS Server", 380)

$mainPanel.Controls.Add($listActiveNS)

# Create button panel

$buttonPanel = New-Object System.Windows.Forms.Panel

$buttonPanel.Size = New-Object System.Drawing.Size(420,30)

$buttonPanel.Location = New-Object System.Drawing.Point(440,85)

$buttonPanel.BackColor = [System.Drawing.Color]::White

$mainPanel.Controls.Add($buttonPanel)

# Create Refresh button

$btnRefresh = New-Object System.Windows.Forms.Button

$btnRefresh.Location = New-Object System.Drawing.Point(0,0)

$btnRefresh.Size = New-Object System.Drawing.Size(100,30)

$btnRefresh.Text = 'Refresh'

$btnRefresh.FlatStyle = 'Flat'

$btnRefresh.BackColor = [System.Drawing.Color]::FromArgb(0,120,215)

$btnRefresh.ForeColor = [System.Drawing.Color]::White

$btnRefresh.Font = New-Object System.Drawing.Font("Segoe UI", 9)

$btnRefresh.Cursor = [System.Windows.Forms.Cursors]::Hand

$buttonPanel.Controls.Add($btnRefresh)

# Create Analyze button

$btnAnalyze = New-Object System.Windows.Forms.Button

$btnAnalyze.Location = New-Object System.Drawing.Point(105,0)

$btnAnalyze.Size = New-Object System.Drawing.Size(100,30)

$btnAnalyze.Text = 'Analyze'

$btnAnalyze.FlatStyle = 'Flat'

$btnAnalyze.BackColor = [System.Drawing.Color]::FromArgb(0,120,215)

$btnAnalyze.ForeColor = [System.Drawing.Color]::White

$btnAnalyze.Font = New-Object System.Drawing.Font("Segoe UI", 9)

$btnAnalyze.Cursor = [System.Windows.Forms.Cursors]::Hand

$buttonPanel.Controls.Add($btnAnalyze)

# Create Clean button

$btnClean = New-Object System.Windows.Forms.Button

$btnClean.Location = New-Object System.Drawing.Point(210,0)

$btnClean.Size = New-Object System.Drawing.Size(100,30)

$btnClean.Text = 'Update NS'

$btnClean.FlatStyle = 'Flat'

$btnClean.BackColor = [System.Drawing.Color]::FromArgb(0,120,215)

$btnClean.ForeColor = [System.Drawing.Color]::White

$btnClean.Font = New-Object System.Drawing.Font("Segoe UI", 9)

$btnClean.Enabled = $false

$btnClean.Cursor = [System.Windows.Forms.Cursors]::Hand

$buttonPanel.Controls.Add($btnClean)

# Create Exit button

$btnExit = New-Object System.Windows.Forms.Button

$btnExit.Location = New-Object System.Drawing.Point(315,0)

$btnExit.Size = New-Object System.Drawing.Size(100,30)

$btnExit.Text = 'Exit'

$btnExit.FlatStyle = 'Flat'

$btnExit.BackColor = [System.Drawing.Color]::FromArgb(232,17,35)

$btnExit.ForeColor = [System.Drawing.Color]::White

$btnExit.Font = New-Object System.Drawing.Font("Segoe UI", 9)

$btnExit.Cursor = [System.Windows.Forms.Cursors]::Hand

$buttonPanel.Controls.Add($btnExit)

# Create RichTextBox for output

$txtOutput = New-Object System.Windows.Forms.RichTextBox

$txtOutput.Location = New-Object System.Drawing.Point(20,250)

$txtOutput.Size = New-Object System.Drawing.Size(820,310)

$txtOutput.ReadOnly = $true

$txtOutput.BackColor = [System.Drawing.Color]::FromArgb(30,30,30)

$txtOutput.ForeColor = [System.Drawing.Color]::White

$txtOutput.Font = New-Object System.Drawing.Font("Consolas", 10)

$txtOutput.BorderStyle = 'None'

$mainPanel.Controls.Add($txtOutput)

# Function to write colored output

function Write-ColorOutput {

    param([string]$Message, [string]$Color = "White")

    $txtOutput.SelectionColor = [System.Drawing.Color]::$Color

    $txtOutput.AppendText("$Message`n")

    $txtOutput.ScrollToCaret()

}

# Function to write separator line

function Write-Separator {

    Write-ColorOutput ("-" * 80) "DarkGray"

}

# Function to test if a server is online with simple checks

function Test-ServerConnection {

    param([string]$ServerName)

    try {

        Write-ColorOutput "Testing NS server: $ServerName" "Yellow"

        

        # Try to get IP address

        try {

            $ipAddress = [System.Net.Dns]::GetHostAddresses($ServerName) |

                        Where-Object { $_.AddressFamily -eq 'InterNetwork' } |

                        Select-Object -First 1

            

            if ($ipAddress) {

                Write-ColorOutput "  - IP Address: $($ipAddress.IPAddressToString)" "Gray"

            } else {

                Write-ColorOutput "  - Could not resolve IP address" "Red"

                return $false

            }

        } catch {

            Write-ColorOutput "  - DNS resolution failed" "Red"

            return $false

        }

        

        # Try ping

        $ping = Test-Connection -ComputerName $ServerName -Count 2 -Quiet

        if ($ping) {

            Write-ColorOutput "  - Ping successful" "Green"

            

            # Try basic DNS port check

            try {

                $tcp = New-Object System.Net.Sockets.TcpClient

                $tcp.SendTimeout = 2000

                $tcp.ReceiveTimeout = 2000

                $result = $tcp.BeginConnect($ServerName, 53, $null, $null)

                $success = $result.AsyncWaitHandle.WaitOne(2000, $false)

                if ($success) {

                    Write-ColorOutput "  - DNS port is accessible" "Green"

                    $tcp.EndConnect($result)

                    return $true

                } else {

                    Write-ColorOutput "  - DNS port is not accessible" "Red"

                    return $false

                }

            } catch {

                Write-ColorOutput "  - DNS port check failed" "Red"

                return $false

            } finally {

                if ($tcp) { $tcp.Close() }

            }

        } else {

            Write-ColorOutput "  - Ping failed" "Red"

            return $false

        }

    } catch {

        Write-ColorOutput "  - Connection test failed: $_" "Red"

        return $false

    }

}

# Function to validate NS server with simple logic

function Test-NSServerValidity {

    param([string]$NSServer)

    return Test-ServerConnection -ServerName $NSServer

}

# Function to get active NS records

function Get-ActiveNSRecords {

    $listActiveNS.Items.Clear()

    Write-ColorOutput "Loading active NS records..." "Yellow"

    Write-Separator

    

    try {

        # Get all zones (both forward and reverse)

        $allZones = Get-DnsServerZone -ComputerName $txtDNSServer.Text |

                   Where-Object { $_.ZoneName -ne "TrustAnchors" }

        

        $script:activeNSServers = @()

        $script:inactiveNSServers = @{}  # Track inactive NS servers and their source zones

        $potentialNSServers = @{}

        

        foreach ($zone in $allZones) {

            Write-ColorOutput "Checking zone: $($zone.ZoneName)" "Cyan"

            

            # Get NS records for each zone

            $nsRecords = Get-DnsServerResourceRecord -ZoneName $zone.ZoneName -RRType "NS" -ComputerName $txtDNSServer.Text |

                        Where-Object { $_.RecordType -eq "NS" }

            

            foreach ($ns in $nsRecords) {

                $nsHost = $ns.RecordData.NameServer.Trim('.')

                if (-not $potentialNSServers.ContainsKey($nsHost)) {

                    $potentialNSServers[$nsHost] = $zone.ZoneName

                }

            }

        }

        

        Write-ColorOutput "`nValidating NS servers..." "Yellow"

        Write-Separator

        

        foreach ($nsServer in $potentialNSServers.Keys) {

            Write-ColorOutput "Processing NS server from zone '$($potentialNSServers[$nsServer])'" "Cyan"

            

            if (Test-NSServerValidity -NSServer $nsServer) {

                if (-not $script:activeNSServers.Contains($nsServer)) {

                    $script:activeNSServers += $nsServer

                    $listActiveNS.Items.Add($nsServer)

                    Write-ColorOutput "Confirmed active NS: $nsServer" "Green"

                }

            } else {

                Write-ColorOutput "NS server validation failed: $nsServer" "Red"

                if (-not $script:inactiveNSServers.ContainsKey($nsServer)) {

                    $script:inactiveNSServers[$nsServer] = $potentialNSServers[$nsServer]

                }

            }

            Write-Separator

        }

        

        Write-ColorOutput "`nTotal active NS servers found: $($script:activeNSServers.Count)" "Cyan"

        Write-ColorOutput "Active NS Servers:" "Yellow"

        foreach ($ns in $script:activeNSServers) {

            Write-ColorOutput "  $ns" "White"

        }

        Write-Separator

        

    } catch {

        Write-ColorOutput "Error getting active NS records: $_" "Red"

        Write-Separator

    }

}

# Function to analyze DNS zones

function Analyze-ReverseLookupZones {

    Write-ColorOutput "Starting DNS zone analysis..." "Yellow"

    Write-Separator

    

    try {

        # Get all zones for information

        $allZones = Get-DnsServerZone -ComputerName $txtDNSServer.Text |

                   Where-Object { $_.ZoneName -ne "TrustAnchors" }

        

        # Get only reverse lookup zones for updates

        $reverseLookupZones = $allZones | Where-Object { $_.ZoneName -like "*.in-addr.arpa" }

        

        $script:inactiveNSCount = 0

        $script:analyzedZones = @{}  # Will only contain reverse lookup zones

        $script:zonesToUpdate = @{}  # Will only contain reverse lookup zones

        

        # First, show inactive NS servers from all zones (informational)

        Write-ColorOutput "`nInactive NS Servers in All Zones (Informational):" "Yellow"

        Write-Separator

        foreach ($inactiveNS in $script:inactiveNSServers.Keys) {

            Write-ColorOutput "Found inactive NS server: $inactiveNS" "Red"

            Write-ColorOutput "Originally detected in zone: $($script:inactiveNSServers[$inactiveNS])" "Yellow"

            Write-Separator

        }

        

        # Process all zones for information

        Write-ColorOutput "`nAnalyzing All Zones (Informational):" "Yellow"

        Write-Separator

        foreach ($zone in $allZones) {

            if ($zone.ZoneName -notlike "*.in-addr.arpa") {

                Write-ColorOutput "Checking zone: $($zone.ZoneName)" "Cyan"

                

                $nsRecords = Get-DnsServerResourceRecord -ZoneName $zone.ZoneName -RRType "NS" -ComputerName $txtDNSServer.Text |

                            Where-Object { $_.RecordType -eq "NS" }

                

                foreach ($ns in $nsRecords) {

                    $nsHost = $ns.RecordData.NameServer.Trim('.')

                    if ($script:inactiveNSServers.ContainsKey($nsHost)) {

                        Write-ColorOutput "  - Inactive NS found: $nsHost" "Red"

                    } else {

                        Write-ColorOutput "  - Active NS found: $nsHost" "Green"

                    }

                }

                Write-Separator

            }

        }

        

        # Process reverse lookup zones for updates

        Write-ColorOutput "`nAnalyzing Reverse Lookup Zones (Will Be Updated):" "Yellow"

        Write-Separator

        foreach ($zone in $reverseLookupZones) {

            Write-ColorOutput "Analyzing zone: $($zone.ZoneName)" "Cyan"

            

            $nsRecords = Get-DnsServerResourceRecord -ZoneName $zone.ZoneName -RRType "NS" -ComputerName $txtDNSServer.Text |

                        Where-Object { $_.RecordType -eq "NS" }

            

            $inactiveNS = @()

            $existingNS = @()

            $missingNS = @()

            

            # Check for inactive NS records

            foreach ($ns in $nsRecords) {

                $nsHost = $ns.RecordData.NameServer.Trim('.')

                $existingNS += $nsHost

                

                if ($script:inactiveNSServers.ContainsKey($nsHost) -or -not $script:activeNSServers.Contains($nsHost)) {

                    $inactiveNS += $nsHost

                    $script:inactiveNSCount++

                    Write-ColorOutput "  - Inactive NS found: $nsHost" "Red"

                } else {

                    Write-ColorOutput "  - Active NS found: $nsHost" "Green"

                }

            }

            

            # Check for missing active NS records

            foreach ($activeNS in $script:activeNSServers) {

                if (-not $existingNS.Contains($activeNS)) {

                    $missingNS += $activeNS

                    Write-ColorOutput "  + Missing active NS: $activeNS" "Yellow"

                }

            }

            

            if ($inactiveNS.Count -gt 0) {

                $script:analyzedZones[$zone.ZoneName] = $inactiveNS

            }

            

            if ($missingNS.Count -gt 0) {

                $script:zonesToUpdate[$zone.ZoneName] = $missingNS

            }

            

            if ($inactiveNS.Count -eq 0 -and $missingNS.Count -eq 0) {

                Write-ColorOutput "  Zone is properly configured." "Green"

            }

            Write-Separator

        }

        

        Write-ColorOutput "`nAnalysis Summary:" "Cyan"

        Write-ColorOutput "Total zones analyzed: $($allZones.Count)" "White"

        Write-ColorOutput "Reverse lookup zones to update: $($reverseLookupZones.Count)" "White"

        Write-ColorOutput "Total inactive NS records in reverse zones: $script:inactiveNSCount" "Yellow"

        Write-ColorOutput "Total reverse zones needing NS records: $($script:zonesToUpdate.Count)" "Yellow"

        Write-Separator

        

        if ($script:inactiveNSCount -gt 0 -or $script:zonesToUpdate.Count -gt 0) {

            $btnClean.Enabled = $true

        } else {

            $btnClean.Enabled = $false

        }

        

    } catch {

        Write-ColorOutput "Error analyzing zones: $_" "Red"

        Write-Separator

    }

}

# Function to clean inactive NS records and add missing ones

function Clean-InactiveNSRecords {

    if ([System.Windows.Forms.MessageBox]::Show(

        "Are you sure you want to update NS records in reverse lookup zones?`n`nThis will:`n- Remove inactive NS records from reverse lookup zones`n- Add missing active NS records to reverse lookup zones`n`nOther zones will not be modified.",

        "Confirm Update",

        [System.Windows.Forms.MessageBoxButtons]::YesNo,

        [System.Windows.Forms.MessageBoxIcon]::Warning) -eq 'Yes')

    {

        Write-ColorOutput "Starting reverse lookup zone NS records update..." "Yellow"

        Write-Separator

        

        try {

            $removedCount = 0

            $addedCount = 0

            

            # Process only reverse lookup zones

            foreach ($zone in $script:analyzedZones.Keys) {

                Write-ColorOutput "Processing zone: $zone" "Cyan"

                

                foreach ($inactiveNS in $script:analyzedZones[$zone]) {

                    try {

                        Remove-DnsServerResourceRecord -ZoneName $zone -RRType "NS" -Name "@" `

                            -RecordData $inactiveNS -Force -ComputerName $txtDNSServer.Text

                        Write-ColorOutput "Removed NS record: $inactiveNS" "Green"

                        $removedCount++

                    } catch {

                        Write-ColorOutput "Error removing NS record $inactiveNS : $_" "Red"

                    }

                }

            }

            

            foreach ($zone in $script:zonesToUpdate.Keys) {

                Write-ColorOutput "`nAdding missing NS records to zone: $zone" "Cyan"

                

                foreach ($missingNS in $script:zonesToUpdate[$zone]) {

                    try {

                        Add-DnsServerResourceRecord -ZoneName $zone -NS -Name "@" `

                            -NameServer $missingNS -ComputerName $txtDNSServer.Text

                        Write-ColorOutput "Added NS record: $missingNS" "Green"

                        $addedCount++

                    } catch {

                        Write-ColorOutput "Error adding NS record $missingNS : $_" "Red"

                    }

                }

            }

            

            Write-ColorOutput "`nUpdate Summary:" "Cyan"

            Write-ColorOutput "Total NS records removed from reverse zones: $removedCount" "Yellow"

            Write-ColorOutput "Total NS records added to reverse zones: $addedCount" "Yellow"

            Write-Separator

            

            $btnClean.Enabled = $false

            $script:analyzedZones.Clear()

            $script:zonesToUpdate.Clear()

            $script:inactiveNSCount = 0

            

        } catch {

            Write-ColorOutput "Error during update: $_" "Red"

            Write-Separator

        }

    }

}

# Button click events

$btnRefresh.Add_Click({ Get-ActiveNSRecords })

$btnAnalyze.Add_Click({ Analyze-ReverseLookupZones })

$btnClean.Add_Click({ Clean-InactiveNSRecords })

$btnExit.Add_Click({ $form.Close() })

# Load active NS records when form loads

$form.Add_Shown({

    Get-ActiveNSRecords

})

# Show the form

$form.ShowDialog() 

 

What Is the Purpose of This Script?

This script was developed to improve DNS management efficiency. Over time, DNS servers may experience performance issues due to outdated records, making regular updates crucial. It is particularly useful in the following scenarios:

  • When making major changes to the network.
  • When updating existing Name Server records.
  • When identifying and removing old NS records.
  • When regularly optimizing Reverse Lookup Zone areas.

As a result, with this script

To ensure that Windows DNS servers function correctly, NS records must be continuously monitored and updated. This script provides a great convenience for DNS administrators by offering both analysis and update functions. Using automation like this to keep your DNS infrastructure healthy and up to date is highly beneficial.

If you found this trick helpful, feel free to share it with your team and check out my blog for more quick tips and insights!

Can’t See Missing Subnet Information? Don’t Forget to Enable Debug Logging !

Check the Netlogon Log for Missing Subnets
The netlogon.log file contains valuable information about client site associations. Specifically, lines containing the phrase “no client site” indicate subnets that are not yet defined in Active Directory. These unidentified subnets might be the root cause of certain connectivity issues.

 

Netlogon Debug

To review the log, open the netlogon.log file using your preferred method or PowerShell:

Get-Content C:\Windows\Debug\netlogon.log

Search for lines with “no client site” to identify any missing subnets.

Enable Debug Logging (If Necessary)
If your netlogon.log file is empty, debug logging might not be enabled. To enable it, follow these steps:

Open the Registry or PowerShell:

for Regedit :
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Netlogon\Parameters

Create or modify the DBFlag registry value with the following hexadecimal value:
2080FFFF

for Powershell :

nltest /DBFlag:2080FFFF

Restart the Netlogon service to apply the changes. You can do this via PowerShell:

Restart-Service Netlogon

Analyze the Debug Logs
After enabling debug logging and restarting the service, check the netlogon.log file again. Use PowerShell to monitor the log in real time:

Get-Content C:\Windows\Debug\netlogon.log -Wait

Look for entries with “no client site” to identify the subnets that need to be added to Active Directory.

Debug logging and the netlogon.log file are invaluable tools for identifying and resolving missing subnet configurations in Active Directory. By following these steps, you can easily pinpoint the subnets that require definition and ensure smoother network operations.

If you want to see other articles on the subject;

If you found this trick helpful, feel free to share it with your team and check out my blog for more quick tips and insights!

How To : Subnet Creation Dates in Active Directory

Managing an Active Directory (AD) environment requires a clear understanding of its configuration, including its network subnets. Knowing when specific subnets were added can provide valuable insights for audits, troubleshooting, and change tracking. However, this information is not readily visible in the default Active Directory tools. To address this, I created a script that retrieves and displays the creation dates of subnets in Active Directory.

In this article, I’ll share the purpose of this script, explain how it works, and provide usage instructions. If you’ve been looking for a way to streamline your subnet management tasks, this solution might be just what you need.

Subnet creation dates can be important for:

  1. Auditing: Keeping a record of when changes were made.
  2. Troubleshooting: Identifying if a recent subnet addition aligns with network changes.
  3. Change Management: Ensuring compliance with organizational policies.

While the default AD tools allow you to manage subnets, they lack detailed tracking capabilities. This script bridges that gap by retrieving and displaying the creation timestamps.

The script leverages PowerShell and Active Directory cmdlets to query the CN=Subnets container. By parsing the whenCreated attribute of each subnet object, it provides a clear list of subnets along with their creation dates. Below is a summary of its functionality:

  1. Connects to the Active Directory environment.
  2. Queries the CN=Subnets container.
  3. Retrieves the whenCreated attribute for each subnet.
  4. Outputs the data in a readable format (e.g., table or CSV).

Before running the script, ensure the following:

  • You have administrative privileges to access the CN=Subnets container.
  • PowerShell 5.1 or later is installed on your machine.
  • The Active Directory module for Windows PowerShell is installed.

 

When you execute the script, you’ll see

 

Active Directory Subnet History ;

# Import the Active Directory module

Import-Module ActiveDirectory

try {

    # Retrieve the Configuration container

    $configNC = (Get-ADRootDSE).configurationNamingContext

    

    # Retrieve subnet information

    $subnets = Get-ADObject -Filter 'objectClass -eq "subnet"' `

        -SearchBase "CN=Subnets,CN=Sites,$configNC" `

        -Properties Name, Description, Location, whenCreated, whenChanged, siteObject

    # Process the results

    $subnetInfo = $subnets | Select-Object @{

        Name = "Subnet"

        Expression = { $_.Name }

    },

    @{

        Name = "Created Date"

        Expression = { $_.whenCreated }

    },

    @{

        Name = "Last Modified"

        Expression = { $_.whenChanged }

    },

    @{

        Name = "Location"

        Expression = { if ($_.Location) { $_.Location } else { "Not Specified" } }

    },

    @{

        Name = "Description"

        Expression = { if ($_.Description) { $_.Description } else { "No Description" } }

    },

    @{

        Name = "Associated Site"

        Expression = {

            if ($_.siteObject) {

                ($_.siteObject -split ',')[0] -replace 'CN='

            } else {

                "No Site Associated"

            }

        }

    }

    # Display the total count

    Write-Host "Total Number of Subnets:" $subnets.Count -ForegroundColor Green

    Write-Host "`nDetailed Subnet Information:" -ForegroundColor Yellow

    Write-Host "------------------------`n"

    # Print to screen

    $subnetInfo | Format-Table -AutoSize

    # Export to CSV

    $exportPath = "AD_Subnets_Export_$(Get-Date -Format 'yyyyMMdd_HHmmss').csv"

    $subnetInfo | Export-Csv -Path $exportPath -NoTypeInformation -Encoding UTF8

    Write-Host "`nExported to:" $exportPath -ForegroundColor Cyan

} catch {

    Write-Host "Error occurred: $_" -ForegroundColor Red

    Write-Host "`nCheck if you have the following prerequisites:" -ForegroundColor Yellow

    Write-Host "1. Running PowerShell as Administrator" -ForegroundColor Yellow

    Write-Host "2. Domain Admin or appropriate permissions" -ForegroundColor Yellow

    Write-Host "3. Active Directory PowerShell module is installed" -ForegroundColor Yellow

    Write-Host "4. Running on a domain-joined machine" -ForegroundColor Yellow

}

 

While this script fulfills its primary purpose, there’s always room for improvement. Some potential enhancements include:

Adding filtering options to display subnets created within a specific timeframe.
Incorporating logging functionality for audit purposes.
Automating the script to run periodically and generate reports.

This script provides a straightforward way to retrieve subnet creation dates in Active Directory, making it easier to manage and audit your network environment. Feel free to try it out.

You can also check this article to check for missing subnets in your environment.

 

Have a nice day !

SMBv1: Monitoring and Analyzing Access on Domain Controllers

What is SMB1 and What Does It Do?

Server Message Block Version 1 (SMB1) is an older protocol that facilitates file and printer sharing over a network. Widely used in Windows systems, it allows devices on a network to share data with each other.
However, due to security vulnerabilities, more modern and secure alternatives are recommended.

Advantages:

  • Compatible with older hardware.
  • Simplifies file and printer sharing over a network.

Disadvantages:

  • Prone to security issues and more vulnerable to ransomware attacks.
  • May have lower performance.


Finding SMB1 Access on Domain Controllers

To locate SMB1 access on Domain Controller (DC) servers, the first step is to ensure that the Audit-SMB1Access feature is enabled. This feature collects SMB1 access control logs, making them available for analysis.
These logs focus on specific periods (e.g., the last 10 days).

 

SMBAnalysis
SMBAnalysis

 

Enabling the Audit Feature
To enable SMB1 access logging on Domain Controller servers, use the following PowerShell command:

# Get Domain Controllers

$DomainControllers = Get-ADDomainController -Filter * | Select-Object -ExpandProperty Name

# Checking SMBServerConfiguration AuditSmb1Access

foreach ($DC in $DomainControllers) {

    try {

        # Set-SmbServerConfiguration

        $AuditSmb1Access = Invoke-Command -ComputerName $DC -ScriptBlock {

            Set-SmbServerConfiguration -AuditSmb1Access $true -Force

        }

        Write-Host ("Audit SMB1 Access on {0}: {1}" -f $DC, $true) -ForegroundColor Green

    } catch {

        Write-Host ("Failed to enable Audit SMB1 Access on {0}: {1}" -f $DC, $_.Exception.Message) -ForegroundColor Red

    }

}


To check SMB1 access logging on Domain Controller servers, use the following PowerShell command:

# Get Domain Controller

$DomainControllers = Get-ADDomainController -Filter * | Select-Object -ExpandProperty Name

# Checking SMBServerConfiguration AuditSmb1Access

foreach ($DC in $DomainControllers) {

    try {

        # Get-SmbServerConfiguration

        $AuditSmb1Access = Invoke-Command -ComputerName $DC -ScriptBlock {

            Get-SmbServerConfiguration | Select-Object -ExpandProperty AuditSmb1Access

        }

        Write-Host ("Audit SMB1 Access on {0}: {1}" -f $DC, $AuditSmb1Access) -ForegroundColor Green

    } catch {

        Write-Host ("Failed to check Audit SMB1 Access on {0}: {1}" -f $DC, $_.Exception.Message) -ForegroundColor Red

    }

}

Exporting SMB1 Audit Logs

To analyze SMB1 access logs on Domain Controller servers, use PowerShell to deduplicate this data and review activities over the last 10 days.

$DCList = Get-ADDomainController -Filter *

# Calculate the date 10 days ago

$CutOffDate = (Get-Date).AddDays(-10)

foreach ($DC in $DCList) {

    $DCname = $DC.HostName

    Write-Host "Processing domain controller: $DCname"

    Write-Host "---------------------------------------"

    # HashTable to store unique entries (IP as key, latest timestamp as value)

    $UniqueEntries = @{}

    # Retrieve SMB1 audit logs

    $SMB1Audits = Get-WinEvent -LogName Microsoft-Windows-SMBServer/Audit -ComputerName $DCname

    # Process each log message

    $SMB1Audits | Where-Object { $_.TimeCreated -ge $CutOffDate } | ForEach-Object {

        $eventTime = $_.TimeCreated # Get the event timestamp

        $text = $_.Message.ToString().Split([Environment]::NewLine)

        $client = $text | Where-Object {$_ -like "Client*"}

        if ($client) {

            # Extract and clean the IP address

            $clientIP = $client.Trim()

            # Update the hashtable with the latest event time for the IP

            if ($UniqueEntries.ContainsKey($clientIP)) {

                if ($eventTime -gt $UniqueEntries[$clientIP]) {

                    $UniqueEntries[$clientIP] = $eventTime

                }

            } else {

                $UniqueEntries[$clientIP] = $eventTime

            }

        }

    }

    # Write unique entries to the output file

    $OutputPath = "C:\Scripts\SMB1Analysis\$DCname.txt"

    $UniqueEntries.GetEnumerator() | ForEach-Object {

        "$($_.Value) - $($_.Key)"

    } | Set-Content -Path $OutputPath

    Write-Host "Unique client entries (last 10 days) saved to: $OutputPath"

}

Addressing Security Concerns

  • Identify Security Vulnerabilities: SMB1 has inherent security vulnerabilities and, as an older protocol, is more susceptible to ransomware attacks and network threats. Analyze SMB1 logs to identify potential security risks and breaches.
  • Remediation and Updates: If critical security flaws are found in devices using SMB1, it is essential to update them with security patches. Additionally, consider disabling SMB1 or migrating these devices to a more secure SMB version (SMB2 or SMB3).

Protocol Transition and Reconfiguration

  • Transition from SMB1 to SMB2 or SMB3: To close security gaps associated with SMB1 and enhance overall network security, transition all devices from SMB1 to a more modern and secure SMB version (SMB2 or SMB3). This transition can also improve network performance.
  • Configuration Changes: Adjust Domain Controller settings to disable SMB1 access. This can be done via PowerShell or Group Policy.

Updating Network Security Policies

  • Firewall Rules: SMB1 should only be used on trusted networks, with firewall rules restricting SMB1 access. If necessary, SMB1 should not be accessible to the outside world.
  • Monitoring and Auditing: Continuously monitor and audit SMB1 access on all network devices to detect security breaches early. Regularly review audit logs and address any issues promptly.

User Awareness and Training

  • Limit SMB1 Usage: Educate users about the security risks associated with SMB1 and discourage its use. Provide guidance and training to migrate users to more secure alternatives like SMB2 or SMB3.
  • Documentation: Maintain detailed documentation on changes made and security measures taken. This will serve as a reference in case of similar situations in the future and can be used as a resource for network management.

Periodic Checks and Maintenance

  • Regular Checks: SMB1 usage and security audits should be performed regularly. Periodically check SMB1 logs and network security settings to enhance overall network security and quickly identify potential issues.
  • Security Updates and Patching: Regularly update devices with security patches to protect against new vulnerabilities. This is particularly critical for older hardware.

These steps summarize the fundamental processes needed to enhance your network’s security and resilience against potential threats.

Have a nice day !

Efficient DNS Zone Management: Scripts for Forward, Reverse Lookup Zones and Recovery

At its core, DNS is a system that eliminates the complexity of accessing services via IP addresses by allowing users to connect effortlessly using domain names.

  • It facilitates domain name resolution and management.
  • Directs queries to the appropriate destinations.

DNS zones and reverse lookup zones are crucial components of a robust and secure network infrastructure.

In this article, I will share several scripts that you can use as needed. These scripts are designed to address the following requirements:

  • Exporting the names of all zones in DNS
  • Extracting all records associated with the zones listed in the exported file
  • Exporting all records from a specific zone of your choice
  • Restoring a deleted zone if necessary
  • Add Reverse Lookup Zones

Exporting All Zones in DNS

# Output file path

$outputFile = "C:\Script\DNSOperation\DNSZoneNameExport.csv"

try {

    # Fetching DNS zones

    $zones = Get-DnsServerZone

    if ($zones) {

        # Preparing data for export

        $zoneData = $zones | Select-Object -Property ZoneName

        # Exporting to CSV

        $zoneData | Export-Csv -Path $outputFile -NoTypeInformation -Encoding UTF8

        Write-Host "DNS zones have been successfully exported to $outputFile" -ForegroundColor Green

    } else {

        Write-Host "No DNS zones found." -ForegroundColor Yellow

    }

} catch {

    Write-Host "An error occurred while fetching DNS zones: $_" -ForegroundColor Red

}

Exporting All Zones in Records

# Zones CSV Path

$zonesFile = "C:\Script\DNSOperation\DNSZoneNameExport.csv"

$zones = Import-Csv -Path $zonesFile

foreach ($zone in $zones) {

    $zoneName = $zone.ZoneName

    $OutputFile = "C:\Script\DNSOperation\$zoneName.csv"

    $Report = [System.Collections.Generic.List[Object]]::new()

    $zoneInfo = Get-DnsServerResourceRecord -ZoneName $zoneName

    foreach ($info in $zoneInfo) {

        $timestamp = if ($info.Timestamp) { $info.Timestamp } else { "static" }

        $timetolive = $info.TimeToLive.TotalSeconds

        $recordData = switch ($info.RecordType) {

            'A' { $info.RecordData.IPv4Address }

            'CNAME' { $info.RecordData.HostnameAlias }

            'NS' { $info.RecordData.NameServer }

            'SOA' { "[$($info.RecordData.SerialNumber)] $($info.RecordData.PrimaryServer), $($info.RecordData.ResponsiblePerson)" }

            'SRV' { $info.RecordData.DomainName }

            'PTR' { $info.RecordData.PtrDomainName }

            'MX' { $info.RecordData.MailExchange }

            'AAAA' { $info.RecordData.IPv6Address }

            'TXT' { $info.RecordData.DescriptiveText }

            default { $null }

        }

        $ReportLine = [PSCustomObject]@{

            Name       = $zoneName

            Hostname   = $info.Hostname

            Type       = $info.RecordType

            Data       = $recordData

            Timestamp  = $timestamp

            TimeToLive = $timetolive

        }

        $Report.Add($ReportLine)

    }

    $Report | Export-Csv -Path $OutputFile -NoTypeInformation -Encoding utf8

    Write-Host "DNS records for $zoneName exported successfully." -ForegroundColor Green

}


Exporting Specific Zone in Records

try {

    $Report = [System.Collections.Generic.List[Object]]::new()

    $zoneName = "yusufustundag.com"

    # Fetching zone information

    try {

        $zoneInfo = Get-DnsServerResourceRecord -ZoneName $zoneName -ErrorAction Stop

    } catch {

        Write-Host "Error: Unable to fetch zone information. Zone name: $zoneName" -ForegroundColor Red

        throw $_

    }

    # Processing records

    foreach ($info in $zoneInfo) {

        try {

            $timestamp = if ($info.Timestamp) { $info.Timestamp } else { "static" }

            $timetolive = if ($info.TimeToLive) { $info.TimeToLive.TotalSeconds } else { "N/A" }

            $recordData = switch ($info.RecordType) {

                'A'      { if ($info.RecordData.IPv4Address) { $info.RecordData.IPv4Address } else { "No IP" } }

                'CNAME'  { if ($info.RecordData.HostnameAlias) { $info.RecordData.HostnameAlias } else { "No Alias" } }

                'NS'     { if ($info.RecordData.NameServer) { $info.RecordData.NameServer } else { "No NameServer" } }

                'SOA'    { if ($info.RecordData) { "[$($info.RecordData.SerialNumber)] $($info.RecordData.PrimaryServer), $($info.RecordData.ResponsiblePerson)" } else { "No SOA Information" } }

                'SRV'    { if ($info.RecordData.DomainName) { $info.RecordData.DomainName } else { "No Domain" } }

                'PTR'    { if ($info.RecordData.PtrDomainName) { $info.RecordData.PtrDomainName } else { "No PTR" } }

                'MX'     { if ($info.RecordData.MailExchange) { $info.RecordData.MailExchange } else { "No Mail Exchange" } }

                'AAAA'   { if ($info.RecordData.IPv6Address) { $info.RecordData.IPv6Address } else { "No IPv6" } }

                'TXT'    { if ($info.RecordData.DescriptiveText) { $info.RecordData.DescriptiveText } else { "No TXT" } }

                default  { "Unsupported record type: $($info.RecordType)" }

            }

            $ReportLine = [PSCustomObject]@{

                Name       = $zoneName

                Hostname   = $info.Hostname

                Type       = $info.RecordType

                Data       = $recordData

                Timestamp  = $timestamp

                TimeToLive = $timetolive

            }

            $Report.Add($ReportLine)

        } catch {

            Write-Host "Error: An issue occurred while processing the record. Hostname: $($info.Hostname), Type: $($info.RecordType)" -ForegroundColor Red

            Write-Host "Details: $_" -ForegroundColor Yellow

        }

    }

    # Writing to CSV

    try {

        $Report | Export-Csv "C:\Script\DNSOperation\DNSZoneRecord.csv" -NoTypeInformation -Encoding utf8

        Write-Host "DNS records have been successfully exported." -ForegroundColor Green

    } catch {

        Write-Host "Error: Failed to write to the CSV file." -ForegroundColor Red

        throw $_

    }

} catch {

    Write-Host "An unexpected error occurred: $_" -ForegroundColor Red

}

 

These scripts might seem a bit complex, but the .csv data they generate can effectively meet your needs in situations requiring reporting or verification.

Reading data may not always be easier than working with a .csv file, but in the event of a zone deletion, our other script utilizing the Export-DnsServerZone and dnscmd commands will provide a quick recovery solution.

Recovery – Export Import Specific Zone

Export-DNSServerZone -name yusufustundag.com -FileName yusufustundagExport

After completing the export process, you can find the exported file under the C:\Windows\System32\dns directory.

Now, here’s a critical point: suppose you’ve exported all your zones using this method, but someone accidentally deleted one or more zones. How can you quickly recover them in such a scenario?

The script we will use for this recovery must include the zone name and the name of the exported zone, as shown below.

dnscmd /zoneadd "yusufustundag.com" /primary /file yusufustundagExport /Load

 

Add Reverse Lookup Zone

$Zones = @(

    "1.1.0.0/24",  # Subnet /24 (255.255.255.0)

    "2.2.0.0/24",

    "10.10.151.0/24"

)

foreach ($Zone in $Zones) {

    try {

        # Split the NetworkID and Subnet Mask

        $SubnetArray = $Zone -split '/'

        $IPAddress = $SubnetArray[0]

        $SubnetMask = [int]$SubnetArray[1]

        # Generate the Reverse Lookup Zone Name based on Subnet Mask

        $Octets = $IPAddress -split '\.'

        if ($SubnetMask -le 8) {

            $RevZone = "$($Octets[0]).in-addr.arpa"

        } elseif ($SubnetMask -le 16) {

            $RevZone = "$($Octets[1]).$($Octets[0]).in-addr.arpa"

        } elseif ($SubnetMask -le 24) {

            $RevZone = "$($Octets[2]).$($Octets[1]).$($Octets[0]).in-addr.arpa"

        } else {

            $RevZone = "$($Octets[3]).$($Octets[2]).$($Octets[1]).$($Octets[0]).in-addr.arpa"

        }

        # Check if the Reverse Zone Already Exists

        $ExistingZone = Get-DnsServerZone -Name $RevZone -ErrorAction SilentlyContinue

        

        if ($ExistingZone) {

            Write-Output "Reverse Lookup Zone '$RevZone' already exists."

        } else {

            # Add the Reverse Lookup Zone

            Add-DnsServerPrimaryZone -NetworkID $Zone -ReplicationScope "Domain"

            Write-Output "Reverse Lookup Zone '$RevZone' added successfully."

        }

    } catch {

        # Handle Errors Gracefully

        if ($_.Exception.Message -match "ResourceExists") {

            Write-Output "Reverse Lookup Zone '$RevZone' already exists (error caught)."

        } else {

            Write-Error "Failed to add Reverse Lookup Zone '$RevZone': $_"

        }

    }

}

 

Have a nice day!

How to Find Missing Subnet for Active Directory ?

If your Active Directory environment is large and distributed with numerous network blocks, it is essential to add these network blocks as subnets in Active Directory Sites and Services.

Failing to add these subnets can result in several disadvantages.

Disadvantages of Missing Subnet Definitions in Active Directory Environments

In an Active Directory (AD) environment, missing or incomplete subnet definitions can lead to various issues and inefficiencies. Especially in large and complex networks, correctly defining subnets is critical for AD to function properly. Below are the key disadvantages:

1. Site and Replication Issues

  • In AD, sites are used to optimize network traffic. Subnet definitions associate specific subnets with sites to direct traffic efficiently.
  • If subnets are missing, clients and servers might be associated with incorrect sites, leading to unnecessary WAN traffic and replication delays.

2. Delayed Authentication and Group Policy Application

  • Missing or incorrect subnet definitions may prevent clients from locating the nearest Domain Controller (DC). As a result, clients may attempt to authenticate with a DC in a remote location.
  • This can lead to longer login times and delayed Group Policy Object (GPO) applications.

3. Performance Degradation and Bandwidth Overuse

  • Without accurate subnet definitions, clients and servers may connect to DCs in distant sites, which can impact performance, especially in environments with slow WAN links.
  • Replication traffic between incorrectly associated sites may also increase WAN bandwidth usage unnecessarily.

4. Incorrect Site-Link Utilization

  • Sites and subnets are interconnected using site links. Missing subnet definitions can result in clients using inappropriate site links to access DCs or other AD services.
  • This can cause replication delays and incorrect DC selection.

5. DNS Resolution Issues

  • DNS is vital for authentication and replication processes in an AD environment. Missing subnet definitions may cause clients to use inappropriate DNS servers, resulting in delayed or failed DNS queries.
  • This can lead to slow AD services or failures in certain processes.

6. Complications in Log Analysis and Network Management

  • Missing subnet definitions complicate log analysis and network management. For instance, identifying which site specific IP ranges belong to becomes challenging.
  • Troubleshooting network-related issues becomes more complex and time-consuming.

Result

Properly defining subnets in an Active Directory environment is crucial for authentication, replication, and traffic management. Missing subnet definitions, particularly in large and distributed networks, can lead to performance bottlenecks and operational challenges. To avoid these problems, it is essential to define and regularly update subnet configurations for each location.

To avoid encountering these disadvantages, we need to use the Netlogon.log file to identify missing subnets. It is not necessary to enable Netlogon debug parameters to obtain information about missing subnets. By default, No_Client_Site entries can be found in the Netlogon file.

While detecting No_Client_Site information in the Netlogon file is relatively straightforward in environments with a single Domain Controller, it can become time-consuming in environments with multiple Domain Controllers, as you would need to search each Netlogon file individually.

For large and distributed environments, the following PowerShell script can be used to gather missing subnet information:

The script retrieves all Domain Controllers in the environment and categorizes them as accessible or inaccessible.
For accessible Domain Controllers, it accesses the Windows\debug directory, extracts the No_Client_Site entries from the Netlogon.log file, deduplicates the data, and exports the results.

<#
This script analyzes missing subnets in an Active Directory environment.
It uses the Get-ADDomainController parameter to retrieve all Domain Controller servers in the environment.  
The output is divided into two categories based on their accessibility.  
For accessible Domain Controllers, the script examines the lines from the netlogon.log file within the last day.  
Only unique entries are included in the output.  
If you want to analyze the netlogon.log file for the last 5 days instead, you can update the relevant line in the script:  
$lastFiveDays = (Get-Date).AddDays(-5)
#>

#Active Directory Missing Subnet Analysis#
# Output files
$outputPathAccessible = "C:\script\MissingSubnet\Output\Accessible_DCs.txt"
$outputPathInaccessible = "C:\script\MissingSubnet\Output\Inaccessible_DCs.txt"
$outputPathNoClientSite = "C:\script\MissingSubnet\Output\Missing_Subnet.txt"
# Clear or create the output files
if (Test-Path $outputPathAccessible) { Clear-Content -Path $outputPathAccessible } else { New-Item -Path $outputPathAccessible -ItemType File }
if (Test-Path $outputPathInaccessible) { Clear-Content -Path $outputPathInaccessible } else { New-Item -Path $outputPathInaccessible -ItemType File }
if (Test-Path $outputPathNoClientSite) { Clear-Content -Path $outputPathNoClientSite } else { New-Item -Path $outputPathNoClientSite -ItemType File }
# Add header rows
Add-Content -Path $outputPathAccessible -Value "Accessible Domain Controllers"
Add-Content -Path $outputPathInaccessible -Value "Inaccessible Domain Controllers"
Add-Content -Path $outputPathNoClientSite -Value '"Domain Controller" | "Computer Name" | "IP Address"'
# Get all Domain Controllers
$servers = (Get-ADDomainController -Filter *).Hostname
$uniqueEntries = @()  # Temporary list to store unique entries
$yesterday = (Get-Date).AddDays(-1)  # Get the date for one day ago
# Check if each Domain Controller is accessible
foreach ($server in $servers) {
    $logPath = "\\$server\c$\Windows\debug\netlogon.log"
    # Check if the server is reachable by ping
    if (Test-Connection -ComputerName $server -Count 1 -Quiet) {
        # Write reachable servers to the file
        Add-Content -Path $outputPathAccessible -Value $server
        # Perform Netlogon processing
        if (Test-Path $logPath) {
            $lines = Get-Content -Path $logPath
            # Check each line in the log file
            foreach ($line in $lines) {
                # Extract date information from the line and compare with the last 1 day
                if ($line -match "^\d{2}/\d{2}") {
                    $datePart = $line.Substring(0, 5)  # Extract the date part (MM/dd format)
                    $timePart = $line.Substring(6, 8)  # Extract the time part
                    # Combine date and time and convert to a DateTime object
                    $entryDate = Get-Date -Month $datePart.Split("/")[0] -Day $datePart.Split("/")[1] -Hour $timePart.Split(":")[0] -Minute $timePart.Split(":")[1] -Second $timePart.Split(":")[2]
                    # Process only if the entry is within the last 1 day
                    if ($entryDate -ge $yesterday) {
                        if ($line -match "NO_CLIENT_SITE") {
                            # Extract client name and IP address
                            $client = $line.Split(":")[4].Trim().Split(" ")[0]
                            $ip = $line.Split(":")[4].Trim().Split(" ")[1]
                            # Create the entry format
                            $entry = "$server | $client | $ip"
                            # Add to unique list (ignore duplicates)
                            if ($uniqueEntries -notcontains $entry) {
                                $uniqueEntries += $entry
                            }
                        }
                    }
                }
            }
        } else {
            Write-Host "Log file not found: $logPath"
        }
    } else {
        # Write unreachable servers to the file
        Add-Content -Path $outputPathInaccessible -Value $server
        Write-Host "$server is not reachable."
    }
}
# Write unique entries to "No_client_site.txt"
$uniqueEntries | ForEach-Object { Add-Content -Value $_ -Path $outputPathNoClientSite }

 

Simple , Easy, Useful

Have a nice day !