DNS Backups in PowerShell

DNS backups are a bit complicated, you need to backup and restore each zone individually with files that are in a static location in System32. Most sources I found used dnscmd which is too DOS for me, we need to PowerShell it up. I ended up finding that Add-DnsServerPrimaryZone would work for my needs despite what some sources said about there being no PowerShell way to do this.

Code

Backup

$log = 'C:\dns_backup.log'
Start-Transcript $log     

#Requires -RunAsAdministrator

# --- Initializations --- #
If (Test-Path '.\send-mail\send-mail.ps1') {
    . .\send-mail\send-mail.ps1
    } Else {
    Throw "send-mail is missing"
}

# --- Declarations --- #
#user vars, change these
$backupLocation = "\\dsk7\backups-smb\dns\"
$archive = "C:\$(Get-Date -UFormat %Y-%m-%d)-$env:COMPUTERNAME.dns.zip"

#system vars, you likely don't change these
$dnsRoot = 'C:\Windows\System32\dns\'
$primarys = Get-DnsServerZone | Where-Object { $_.ZoneType -eq 'Primary' }

# use an array to catch bad things and put it in our email
$failureArray = @()
$result = "success"

# --- Functions --- #
Function mail {
    # stop must be here so that the file can be unlocked whenever we want to mail
    Stop-Transcript
    If ($failureArray.Count -gt 0) {
        $result = 'failure'
    } Else {
        $result = 'success'
    }
    send-mail -to 'user@contoso.com' -subject "DNS Backup on $env:COMPUTERNAME $result" -body "Failures: $failureArray" -attachment $log
}

# --- Execution --- #
ForEach ($z in $primarys) {
    $zone = $z.ZoneName
    # auto created zones fail to export
    If ($z.IsAutoCreated -eq $false -And $z.ZoneName -notLike "*arpa") {
        Write-Host "Zone $zone" -ForegroundColor Green
        $file = "$zone.dns.bak"
        Write-Host "Exporting $zone to $file" -ForegroundColor Green
        Try {
            Export-DnsServerZone -Name $zone -FileName $file
        } Catch {
            Write-Warning "Failed to backup $zone"
            $failureArray += $zone
            Write-Host $_
        }
        $backupFile = $dnsRoot + $file
    }
}
# zip-em and move-em
$zipFiles = "$dnsRoot\*.dns.bak"

Write-Host "Compressing archive $archive" -ForegroundColor Green
Compress-Archive -Path $zipFiles -DestinationPath $archive

Write-Host "Moving $archive to $backupLocation" -ForegroundColor Green
Try {
    Move-Item -Path $archive -Destination $backupLocation
} Catch {
    $failureArray += "move failure"
    Write-Warning "move failure"
    Write-Host $_
}

Try {
    Remove-Item -Path "$dnsRoot\*.dns.bak" -Recurse
} Catch {
    Write-Warning "failed to remove backup dir"
    $failureArray += 'backup dir failed to remove'
    Write-Host $_
}

# --- Ending Tasks --- #
mail
Note

This script utilizes my email script submodule.

Recovery

Start-Transcript 'C:\dns_recovery.log'

#Requires -RunAsAdministrator

Function dns_recovery {
    param (
        [Parameter(Mandatory=$True)]
        [Object]$archive
    )
    
    # this cannot be changed, the commands will only load files from this dir
    $dnsRoot = "C:\Windows\System32\dns"

    If (!(Test-Path $archive)) {
        Throw "$archive does not exist, check the path and try again"
    }

    Write-Host "Expanding $archive at $dnsRoot" -ForegroundColor Green
    Expand-Archive -Path $archive -DestinationPath $dnsRoot

    $allFiles = Get-ChildItem $dnsRoot
    $backupFiles = $allFiles | Where {$_.Name -Like "*.dns.bak"}

    ForEach ($file in $backupFiles) {
        $zone = $file.Name.Replace('.dns.bak','')
        Write-Host "Loading $zone from $file..." -ForegroundColor Green
        Try { 
            Add-DnsServerPrimaryZone -ZoneName $zone -ZoneFile $file.Name -LoadExisting
        } Catch {
            Write-Warning "Failed to restore $zone"
        }
    }

    Write-Host "Removing files from $dnsRoot"
    Remove-Item -Path "$dnsRoot\*.dns.bak"
 
}
dns_recovery

Stop-Transcript

Notes

Updates: 2022-04-22: Added a check for arpa specifically as it was an issue on my home DNS server.

References

Another basic script

An overview on the cmdlets and commands

Using PowerShell

Docs on DNSServer Module