-
-
Save r0ckbeard/82646ea4baf0c6a7e4652b79386d22fb to your computer and use it in GitHub Desktop.
DNS backup script with logging and error handling
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <# | |
| .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