Read and write Bind zone files in pure PowerShell
This module can be installed directly from the gallery.
PS> Install-Module BW.Utils.BindZoneFile -Force
$ORIGIN @
@ 3600 IN SOA ns1-205.azure-dns.com. azuredns-hostmaster.microsoft.com. 1 3600 300 2419200 300
@ 43200 IN NS ns3-205.azure-dns.org.
@ 43200 IN NS ns4-205.azure-dns.info.
@ 43200 IN NS ns2-205.azure-dns.net.
@ 43200 IN NS ns1-205.azure-dns.com.
PS> $Zone = Import-BindZone .\contoso-com.zone
PS> $Zone | ft
HostName TimeToLive RecordClass RecordType RecordData Comment
-------- ---------- ----------- ---------- ---------- -------
@ 3600 IN SOA ns1-205.azure-dns.com. azuredns-hostmaster.microsoft.com. 1 3600 300 2419200 300
@ 43200 IN NS ns3-205.azure-dns.org.
@ 43200 IN NS ns4-205.azure-dns.info.
@ 43200 IN NS ns2-205.azure-dns.net.
@ 43200 IN NS ns1-205.azure-dns.com.
# using parameters
PS> New-BindRecord -HostName '@' -TimeToLive 3600 -RecordClass IN -RecordType A -RecordData '40.76.4.15' -BindZone $Zone
PS> New-BindRecord -HostName '@' -TimeToLive 3600 -RecordClass IN -RecordType A -RecordData '40.112.72.205' -BindZone $Zone
PS> New-BindRecord -HostName '@' -TimeToLive 3600 -RecordClass IN -RecordType A -RecordData '40.113.200.201' -BindZone $Zone
# using bind format, note that we can pass bind format directly on the pipeline
PS> '@ 3600 IN A 104.215.148.63', '@ 3600 IN A 13.77.161.179' | New-BindRecord -BindZone $Zone
PS> $Zone | ft
HostName TimeToLive RecordClass RecordType RecordData Comment
-------- ---------- ----------- ---------- ---------- -------
@ 3600 IN SOA ns1-205.azure-dns.com. azuredns-hostmaster.microsoft.com. 1 3600 300 2419200 300
@ 43200 IN NS ns3-205.azure-dns.org.
@ 43200 IN NS ns4-205.azure-dns.info.
@ 43200 IN NS ns2-205.azure-dns.net.
@ 43200 IN NS ns1-205.azure-dns.com.
@ 3600 IN A 40.76.4.15
@ 3600 IN A 40.112.72.205
@ 3600 IN A 40.113.200.201
@ 3600 IN A 104.215.148.63
@ 3600 IN A 13.77.161.179
Note that the data section is not validated at this time.
PS> $MxRecord = New-BindRecord '@ 3600 IN MX 10 contoso-com.mail.protection.outlook.com. ; using Office 365'
PS> $MxRecord
HostName : @
TimeToLive : 3600
RecordClass : IN
RecordType : MX
RecordData : 10 contoso-com.mail.protection.outlook.com.
Comment : using Office 365
Note that the comment is parsed. Multi-line comments are not supported.
Here we are inserting the new MX record after the last NS record...
PS> $LastNSRecord = $Zone | Where-Object RecordType -eq 'NS' | Select-Object -Last 1
PS> Add-BindRecordToZone -Zone $Zone -Record $MxRecord -After $LastNSRecord
PS> $Zone | ft
HostName TimeToLive RecordClass RecordType RecordData Comment
-------- ---------- ----------- ---------- ---------- -------
@ 3600 IN SOA ns1-205.azure-dns.com. azuredns-hostmaster.microsoft.com. 1 3600 300 2419200 300
@ 43200 IN NS ns3-205.azure-dns.org.
@ 43200 IN NS ns4-205.azure-dns.info.
@ 43200 IN NS ns2-205.azure-dns.net.
@ 43200 IN NS ns1-205.azure-dns.com.
@ 3600 IN MX 10 contoso-com.mail.protection.outlook.com. using Office 365
@ 3600 IN A 40.76.4.15
@ 3600 IN A 40.112.72.205
@ 3600 IN A 40.113.200.201
@ 3600 IN A 104.215.148.63
@ 3600 IN A 13.77.161.179
First let's add the MS verification record...
PS> New-BindRecord '@ 3600 IN TXT "MS=ms47806392" ; Microsoft verification for Office 365' -BindZone $Zone
PS> $Zone | Where-Object RecordType -eq 'TXT' | ft
HostName TimeToLive RecordClass RecordType RecordData Comment
-------- ---------- ----------- ---------- ---------- -------
@ 3600 IN TXT "MS=ms47806392" Microsoft verification for Office 365
What happens if we add a record with a different TTL on the same domain?
PS> New-BindRecord -HostName '@' -TimeToLive 600 -RecordClass IN -RecordType TXT -RecordData '"v=spf1 include:spf.protection.outlook.com -all"' -BindZone $Zone
PS> $Zone | Where-Object RecordType -eq 'TXT' | ft
HostName TimeToLive RecordClass RecordType RecordData Comment
-------- ---------- ----------- ---------- ---------- -------
@ 3600 IN TXT "MS=ms47806392" Microsoft verification for Office 365
@ 600 IN TXT "v=spf1 include:spf.protection.outlook.com -all"
As you can see it allows you to add an invalid TXT record, but if you try to test it...
PS> Test-BindZone -Zone $Zone
WARNING: Zone contains one or more TXT records with dissimilar TTL values, all TTL values at the same level must match for TXT records
True
If you try to export a zone file with errors you will get an exception...
PS> Export-BindZone -Zone $Zone -Path .\contoso-com.zone
WARNING: Zone contains one or more TXT records with dissimilar TTL values, all TTL values at the same level must match for TXT records
Zone Errors Present
At line:1 char:1
+ Export-BindZone -Zone $Zone -Path .\contoso-com.zone
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : NotSpecified: (:) [Write-Error], WriteErrorException
+ FullyQualifiedErrorId : Microsoft.PowerShell.Commands.WriteErrorException
First, correct any errors...
PS> $Zone | Where-Object { $_.HostName -eq '@' -and $_.RecordType -eq 'TXT' } | Set-BindRecord -TimeToLive 3600
PS> $Zone | Where-Object RecordType -eq 'TXT' | ft
HostName TimeToLive RecordClass RecordType RecordData Comment
-------- ---------- ----------- ---------- ---------- -------
@ 3600 IN TXT "MS=ms47806392" Microsoft verification for Office 365
@ 3600 IN TXT "v=spf1 include:spf.protection.outlook.com -all"
Then export the zone
PS> Export-BindZone -Zone $Zone -Path .\contoso-com.zone
The resulting zone file
$ORIGIN contoso.com.
@ 3600 IN SOA ns1-205.azure-dns.com. azuredns-hostmaster.microsoft.com. 1 3600 300 2419200 300
@ 43200 IN NS ns3-205.azure-dns.org.
@ 43200 IN NS ns4-205.azure-dns.info.
@ 43200 IN NS ns2-205.azure-dns.net.
@ 43200 IN NS ns1-205.azure-dns.com.
@ 3600 IN MX 10 contoso-com.mail.protection.outlook.com. ; using Office 365
@ 3600 IN A 40.76.4.15
@ 3600 IN A 40.112.72.205
@ 3600 IN A 40.113.200.201
@ 3600 IN A 104.215.148.63
@ 3600 IN A 13.77.161.179
@ 3600 IN TXT "MS=ms47806392" ; Microsoft verification for Office 365
@ 3600 IN TXT "v=spf1 include:spf.protection.outlook.com -all"
You can easily sort a zone file
PS> Invoke-BindZoneSort -Zone $Zone
You can also use the builtin Sort method
PS> $Zone.Sort()
This will move the SOA and NS records to the top of each subdomain and sort the remaining records by HostName, RecordType, and RecordData.
If you want to bulk reformat and sort zones...
PS> $ZoneFiles = Get-ChildItem -Path .\path\to\zones\ -Filter *.zone
PS> $ZoneFiles | ForEach-Object { $Zone = $_ | Import-BindZone; Invoke-BindZoneSort -Zone $Zone; Export-BindZone -Zone $Zone -Path $_ }