Občas je potřeba synchronizace PTR záznamů v DNS s aktuálními IP adresami stanic.
Jako vstup si vezme název zény dopředného vyhledávání a zkontroluje všechny záznamy, zda mají korespondující PTR záznam v reverzní zóně. Pokud ne, aktualizuje je nebo vytvoří nový.
Na konci běhu zobrazí přehledný souhrn:

<#
.SYNOPSIS
Synchronizuje PTR záznamy z forward lookup zóny na DNS serveru (DC).
.DESCRIPTION
Prochází A záznamy v zadané forward lookup zóně a pro každý záznam:
- Zkontroluje, zda existuje odpovídající PTR záznam v reverse zóně.
- Pokud existuje a hostname se shoduje → přeskočí.
- Pokud existuje, ale hostname se liší → aktualizuje PTR záznam.
- Pokud neexistuje → vytvoří nový PTR záznam.
.PARAMETER ForwardZone
Název forward lookup zóny (např. "firma.local").
.PARAMETER DnsServer
Hostname nebo IP adresa DNS serveru. Výchozí: localhost.
.PARAMETER WhatIf
Pouze zobrazí, co by se stalo – nic nemění.
.EXAMPLE
.\Sync-DnsPtrRecords.ps1 -ForwardZone "firma.local"
.EXAMPLE
.\Sync-DnsPtrRecords.ps1 -ForwardZone "firma.local" -DnsServer "dc01.firma.local" -WhatIf
#>
[CmdletBinding(SupportsShouldProcess)]
param (
[Parameter(Mandatory)]
[string]$ForwardZone,
[string]$DnsServer = $env:COMPUTERNAME
)
#region Pomocné funkce
function Get-ReverseZoneName {
<#
.SYNOPSIS
Zjistí název reverse zóny pro zadanou IP adresu z dostupných zón na DNS serveru.
Testuje postupně /24, /16, /8 varianty.
#>
param (
[string]$IpAddress,
[string[]]$AvailableReverseZones
)
$octets = $IpAddress.Split('.')
# Kandidáti od nejpřesnějšího (/24) po nejméně přesný (/8)
$candidates = @(
"$($octets[2]).$($octets[1]).$($octets[0]).in-addr.arpa", # /24
"$($octets[1]).$($octets[0]).in-addr.arpa", # /16
"$($octets[0]).in-addr.arpa" # /8
)
foreach ($candidate in $candidates) {
if ($AvailableReverseZones -contains $candidate) {
return $candidate
}
}
return $null
}
function Get-PtrRecordName {
<#
.SYNOPSIS
Vrátí část PTR záznamu (hostovou část) pro danou IP a reverse zónu.
Např. IP 192.168.1.100 v zóně "1.168.192.in-addr.arpa" → "100"
#>
param (
[string]$IpAddress,
[string]$ReverseZone
)
$octets = $IpAddress.Split('.')
$zoneDepth = ($ReverseZone -replace '\.in-addr\.arpa$','').Split('.').Count
# Reverzní pořadí oktetů: 10.20.18.81 → @('81','18','20','10')
$reversedOctets = @($octets[3], $octets[2], $octets[1], $octets[0])
# Hostová část = prvních (4 - zoneDepth) reverzních oktetů
$hostPart = $reversedOctets[0..(3 - $zoneDepth)]
return ($hostPart -join '.')
}
function Normalize-Fqdn {
param ([string]$Name, [string]$Zone)
if ($Name -notmatch '\.$') {
# není absolutní FQDN, přidáme zónu
if ($Name -match "\.$([regex]::Escape($Zone))$") {
return "$Name."
}
return "$Name.$Zone."
}
return $Name
}
#endregion
#region Hlavní logika
# Ověření modulu
if (-not (Get-Module -ListAvailable -Name DnsServer)) {
Write-Error "Modul DnsServer není dostupný. Spusťte skript na DNS serveru nebo nainstalujte RSAT: Install-WindowsFeature RSAT-DNS-Server"
exit 1
}
Import-Module DnsServer -ErrorAction Stop
Write-Host "`n=== Sync-DnsPtrRecords ===" -ForegroundColor Cyan
Write-Host "DNS Server : $DnsServer"
Write-Host "Forward Zone: $ForwardZone"
Write-Host ""
# Načtení všech reverse zón ze serveru
Write-Verbose "Načítám dostupné reverse zóny..."
try {
$allReverseZones = Get-DnsServerZone -ComputerName $DnsServer |
Where-Object { $_.ZoneName -like '*.in-addr.arpa' -and -not $_.IsAutoCreated } |
Select-Object -ExpandProperty ZoneName
}
catch {
Write-Error "Nelze načíst zóny z DNS serveru '$DnsServer': $_"
exit 1
}
if (-not $allReverseZones) {
Write-Warning "Na serveru '$DnsServer' nebyly nalezeny žádné reverse lookup zóny."
exit 0
}
Write-Verbose "Dostupné reverse zóny: $($allReverseZones -join ', ')"
# Načtení A záznamů z forward zóny
Write-Verbose "Načítám A záznamy ze zóny '$ForwardZone'..."
try {
$aRecords = Get-DnsServerResourceRecord -ComputerName $DnsServer -ZoneName $ForwardZone -RRType A -ErrorAction Stop
}
catch {
Write-Error "Nelze načíst záznamy ze zóny '$ForwardZone': $_"
exit 1
}
if (-not $aRecords) {
Write-Warning "V zóně '$ForwardZone' nebyly nalezeny žádné A záznamy."
exit 0
}
Write-Host "Nalezeno A záznamů: $($aRecords.Count)" -ForegroundColor Green
Write-Host ""
# Statistiky
$stats = @{ Skipped = 0; Created = 0; Updated = 0; NoZone = 0; Errors = 0 }
foreach ($record in $aRecords) {
$hostname = $record.HostName
$ip = $record.RecordData.IPv4Address.ToString()
$fqdn = if ($hostname -eq '@') { "$ForwardZone." } else { "$hostname.$ForwardZone." }
Write-Verbose "Zpracovávám: $fqdn → $ip"
# Najdi příslušnou reverse zónu
$reverseZone = Get-ReverseZoneName -IpAddress $ip -AvailableReverseZones $allReverseZones
if (-not $reverseZone) {
Write-Warning " [$ip] $fqdn — reverse zóna nenalezena, přeskočeno."
$stats.NoZone++
continue
}
# Hostová část PTR záznamu (např. "100" pro 192.168.1.100 v zóně 1.168.192.in-addr.arpa)
$ptrName = Get-PtrRecordName -IpAddress $ip -ReverseZone $reverseZone
# Načti existující PTR záznam (pokud existuje)
try {
$existingPtr = Get-DnsServerResourceRecord -ComputerName $DnsServer `
-ZoneName $reverseZone -Name $ptrName -RRType Ptr -ErrorAction SilentlyContinue
}
catch {
$existingPtr = $null
}
if ($existingPtr) {
$existingFqdn = $existingPtr.RecordData.PtrDomainName
if ($existingFqdn -eq $fqdn) {
# Shoduje se – přeskoč
Write-Host " [OK] $ip → $fqdn" -ForegroundColor DarkGray
$stats.Skipped++
}
else {
# Neshoda – aktualizuj
Write-Host " [UPDATE] $ip : '$existingFqdn' → '$fqdn'" -ForegroundColor Yellow
if ($PSCmdlet.ShouldProcess("$ip v zóně $reverseZone", "Aktualizovat PTR '$existingFqdn' → '$fqdn'")) {
try {
# DNS modul nemá přímý Set pro PTR – odstraníme a vytvoříme znovu
Write-host "DNS modul nemá přímý Set pro PTR – odstraníme a vytvoříme znovu" -ForegroundColor Yellow
# Remove-DnsServerResourceRecord -ComputerName $DnsServer `
# -ZoneName $reverseZone -Name $ptrName -RRType Ptr -Force -ErrorAction Stop
Write-host "Vytvářím nový PTR záznam..." -ForegroundColor Yellow
# Add-DnsServerResourceRecordPtr -ComputerName $DnsServer `
# -ZoneName $reverseZone -Name $ptrName -PtrDomainName $fqdn -ErrorAction Stop
Write-Host " Aktualizováno." -ForegroundColor Green
$stats.Updated++
}
catch {
Write-Warning " [CHYBA] Aktualizace selhala pro $ip : $_"
$stats.Errors++
}
}
}
}
else {
# PTR neexistuje – vytvoř
Write-Host " [CREATE] $ip → $fqdn" -ForegroundColor Cyan
# if ($PSCmdlet.ShouldProcess("$ip v zóně $reverseZone", "Vytvořit PTR '$fqdn'")) {
# try {
# Add-DnsServerResourceRecordPtr -ComputerName $DnsServer `
# -ZoneName $reverseZone -Name $ptrName -PtrDomainName $fqdn -ErrorAction Stop
# Write-Host " Vytvořeno." -ForegroundColor Green
# $stats.Created++
# }
# catch {
# Write-Warning " [CHYBA] Vytvoření selhalo pro $ip : $_"
# $stats.Errors++
# }
# }
}
}
#endregion
#region Souhrn
Write-Host ""
Write-Host "=== Souhrn ===" -ForegroundColor Cyan
Write-Host " Přeskočeno (shoda) : $($stats.Skipped)"
Write-Host " Vytvořeno : $($stats.Created)"
Write-Host " Aktualizováno : $($stats.Updated)"
Write-Host " Bez reverse zóny : $($stats.NoZone)"
Write-Host " Chyby : $($stats.Errors)"
Write-Host ""
#endregion