Skip to content

Instantly share code, notes, and snippets.

@r0ckbeard
Forked from compwiz32/Backup-DNSZoneData.ps1
Last active February 20, 2026 15:43
Show Gist options
  • Select an option

  • Save r0ckbeard/82646ea4baf0c6a7e4652b79386d22fb to your computer and use it in GitHub Desktop.

Select an option

Save r0ckbeard/82646ea4baf0c6a7e4652b79386d22fb to your computer and use it in GitHub Desktop.
DNS backup script with logging and error handling
<#
.SYNOPSIS
DNS Backup Script.
.DESCRIPTION
This script connects to a specified DNS server, retrieves DNS zone and forwarder information, exports the zones and forwarders to files,
compresses the files, and performs cleanup operations. It logs all actions and sends an email notification if an error occurs.
.PARAMETER LogFilePath
The path to the log file where script actions and messages will be recorded. If run from ISE script pane with "Run selection", log file will be saved to C:\Temp\Backup
.PARAMETER DNSServer
The name of the DNS server to connect to and retrieve zone information from.
.PARAMETER BackupFolder
The directory where backup files will be stored.
.EXAMPLE
.\DNSBackupScript.ps1
.NOTES
Author: Sebastian Rock
Date: 31/05/2024
Modified and extended script from https://gist.github.com/compwiz32
#>
# Define the global variable for the log file path
if ($PSScriptRoot) {
$Global:LogFilePath = Join-Path -Path $PSScriptRoot -ChildPath ("$(Get-Date -Format 'yyyy-MM-dd_HH-mm-ss').log")
} else {
New-Item -Path "C:\Temp\DNSBackup" -ItemType Directory -Force
$Global:LogFilePath = "C:\Temp\DNSBackup\DNSBackup_$(Get-Date -Format 'yyyy-MM-dd_HH-mm-ss').log"
}
# If you want to set a custom log file name path, uncomment the line below
# $Global:LogFilePath = "C:\Your\Custom\Path\DNSBackup_$(Get-Date -Format 'yyyy-MM-dd_HH-mm-ss').log"
Function Log-Message {
param (
[Parameter(Mandatory=$true)] [string] $Message,
[switch] $Warning,
[switch] $Error
)
Try {
# Add Content to the Log File
$TimeStamp = (Get-Date).ToString("yyyy/MM/dd HH:mm:ss:fff")
$Line = "$TimeStamp - $Message"
Add-Content -Path $Global:LogFilePath -Value $Line
# Set the default color and prefix
$Color = "Green"
$Prefix = "Message"
# Change color and prefix based on the parameters
if ($Warning.IsPresent) {
$Color = "Yellow"
$Prefix = "Warning"
} elseif ($Error.IsPresent) {
$Color = "Red"
$Prefix = "Error "
}
Write-Host "$Prefix : '$Line'" -ForegroundColor $Color
} Catch {
Write-Host "Error: $_.Exception.Message" -ForegroundColor Red
}
}
Function Send-ErrorEmail {
param (
[Parameter(Mandatory=$true)] [string] $LogFilePath
)
$SmtpServer = "" # smtp.your.company
$From = "" # Sender
$To = "" # Reciever
$Subject = "DNS Backup Script Failed"
$Body = @"
Example body:
The DNS backup script encountered an error. Please find the log file attached for more details.
The script and job is automatically paused on error, it will try to run again in three days.
Actions to take:
1. Troubleshoot
2. Fix problem
3. Stop the task in Task Manager
4. Run manual test in Task Manager
Backupatron 4000 signing off
God speed
"@
Try {
Send-MailMessage -SmtpServer $SmtpServer -From $From -To $To -Subject $Subject -Body $Body -Attachments $LogFilePath
Log-Message "Error email sent to $To."
} Catch {
Log-Message "Failed to send error email: $_" -Warning
}
}
#Set file paths
$BackupFolder = "" # use a valid path like "\\Server01\Path\" or "C:\Backup\"
$DNSServer = ""
#Date variable
$LongDate = Get-Date -Format "yyyy-MM-dd_HH-mm"
#Log Start Time of the Script
$StartTime = (Get-Date)
Log-Message "Script Started at: $(Get-date -format 'yyyy/MM/dd HH:mm:ss')"
# Main try-catch block for the script
Try {
# Connect to DC
Try {
$CIMSession = New-CimSession -ComputerName $DNSServer
Log-Message "Successfully connected to DNS server $DNSServer."
} Catch {
Log-Message "Failed to connect to DNS server $DNSServer $_" -Error
throw
}
# Read DNS info from DC
Try {
$ZoneInfo = Get-DnsServerZone -CimSession $CIMSession
Log-Message "Successfully retrieved DNS zone information. Total zones: $($ZoneInfo.Count)"
} Catch {
Log-Message "Failed to retrieve DNS zone information: $_. Exiting script." -Error
throw
}
# Create temporary ps drive
Try {
New-PSDrive -Name DC -PSProvider FileSystem -Root \\$DNSServer\c$\windows\system32\dns
Log-Message "Temporary PSDrive created."
} Catch {
Log-Message "Failed to create temporary PSDrive: $_" -Error
throw
}
# Filter out the forward zones, reverse zones, and conditional forwarders
$ForwardZones = $ZoneInfo | Where-Object { $_.ZoneName -notlike "*.arpa" -and $_.ZoneType -eq "Primary" } | Select-Object -ExpandProperty ZoneName
$ReverseZones = $ZoneInfo | Where-Object { $_.ZoneName -like "*.arpa" -and $_.ZoneName -notin @("0.in-addr.arpa", "127.in-addr.arpa", "255.in-addr.arpa") } | Select-Object -ExpandProperty ZoneName
$ConditionalForwarder = $ZoneInfo | Where-Object { $_.ZoneType -eq "Forwarder" } | Select-Object -ExpandProperty ZoneName
# Check if ForwardZones, ReverseZones, and ConditionalForwarders are populated
if ($ForwardZones) {
Log-Message "ForwardZones are populated: $($ForwardZones.Count) zones"
Log-Message "$ForwardZones"
} else {
Log-Message "No ForwardZones found." -Warning
}
if ($ReverseZones) {
Log-Message "ReverseZones are populated: $($ReverseZones.Count) zones"
Log-Message "$ReverseZones"
} else {
Log-Message "No ReverseZones found." -Warning
}
if ($ConditionalForwarder) {
Log-Message "ConditionalForwarders are populated: $($ConditionalForwarder.Count) zones"
Log-Message "$ConditionalForwarder"
} else {
Log-Message "No ConditionalForwarders found." -Warning
}
# Throw an error if any of the collections are empty
$EmptyCollections = @()
if (-not $ForwardZones) {
$EmptyCollections += "ForwardZones"
}
if (-not $ReverseZones) {
$EmptyCollections += "ReverseZones"
}
if (-not $ConditionalForwarder) {
$EmptyCollections += "ConditionalForwarder"
}
if ($EmptyCollections.Count -gt 0) {
$ErrorMessage = "The following collection(s) are empty: $($EmptyCollections -join ', ')"
Log-Message $ErrorMessage -Error
throw $ErrorMessage
}
# Export data for each forward lookup zone
foreach ($DNSZone in $ForwardZones) {
$JobScope = "DNSBackup_ForwardZone"
$FileName = -join ($JobScope, "_", $($DNSZone.Replace(".", "_")), "_", $LongDate, ".dns")
Try {
Export-DnsServerZone -Name $DNSZone -FileName $FileName -CimSession $CIMSession
Log-Message "Successfully exported forward lookup zone $DNSZone to $FileName."
} Catch {
Log-Message "Failed to export zone $DNSZone $_" -Error
}
}
# Export data for each reverse lookup zone
foreach ($DNSZone in $ReverseZones) {
$JobScope = "DNSBackup_ReverseZone"
$FileName = -join ($JobScope, "_", $($DNSZone.Replace(".", "_")), "_", $LongDate, ".dns")
Try {
Export-DnsServerZone -Name $DNSZone -FileName $FileName -CimSession $CIMSession
Log-Message "Successfully exported reverse lookup zone $DNSZone to $FileName."
} Catch {
Log-Message "Failed to export zone $DNSZone $_" -Error
}
}
# Export data for each conditional forwarder
foreach ($DNSZone in $ConditionalForwarder) {
$JobScope = "DNSBackup_ConditionalForwarder"
$FileName = -join ($JobScope, "_", $($DNSZone.Replace(".", "_")), "_", $LongDate, ".csv")
Try {
Get-DnsServerZone -Name $DNSZone -ComputerName $DNSServer | Export-Csv -NoTypeInformation -Path DC:\$FileName
Log-Message "Successfully exported conditional forwarder $DNSZone to $FileName."
} Catch {
Log-Message "Failed to export forwarder $DNSZone $_" -Error
}
}
# Compress and copy the files
$ZipFileName = "DNSBackup_$LongDate.zip"
$ZipFilePath = Join-Path -Path "$BackupFolder" -ChildPath $ZipFileName
Try {
Compress-Archive -Path DC:\DNSBackup* -DestinationPath $ZipFilePath
Log-Message "Successfully compressed backup files to $ZipFilePath."
} Catch {
Log-Message "Failed to compress backup files: $_" -Error
throw
}
# Cleanup files, drives, and CIM sessions
Get-ChildItem DC: | Where-Object Name -Like "DNSBackup*" | Remove-Item
Log-Message "Temporary files removed."
Remove-PSDrive -Name DC -ErrorAction Stop
Log-Message "PSDrive DC removed."
Remove-CimSession -CimSession $CIMSession -ErrorAction Stop
Log-Message "CIM session removed."
#Log End Time
$EndTime = (Get-Date)
Log-Message "Script Ended at: $(Get-date -format 'yyyy/MM/dd HH:mm:ss')"
#Get Elapsed Time
$ElapsedTime = ($EndTime - $StartTime).Seconds
Log-Message "Script Execution Time: $ElapsedTime Seconds"
} Catch {
Log-Message "Script encountered a fatal error: $_" -Error
Send-ErrorEmail -LogFilePath $Global:LogFilePath
Read-Host -Prompt "Script paused until manual input"
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment