Skip to content

Commit

Permalink
DnsRecordPtr: Re-implemented as a class-based resource with IPv6 supp…
Browse files Browse the repository at this point in the history
…ort. (#213)

- DnsRecordPtr
  - Added new resource to manage PTR records
  • Loading branch information
Sudman1 authored Mar 23, 2021
1 parent 61aeedd commit ab78fb6
Show file tree
Hide file tree
Showing 17 changed files with 2,156 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added new resource to manage CNAME records
- DnsRecordCnameScoped
- Added new resource to manage scoped CNAME records
- DnsRecordPtr
- Added new resource to manage PTR records

### Changed

Expand Down
223 changes: 223 additions & 0 deletions source/Classes/002.DnsRecordPtr.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
<#
.SYNOPSIS
The DnsRecordPtr DSC resource manages PTR DNS records against a specific zone on a Domain Name System (DNS) server.
.DESCRIPTION
The DnsRecordPtr DSC resource manages PTR DNS records against a specific zone on a Domain Name System (DNS) server.
.PARAMETER IpAddress
Specifies the IP address to which the record is associated (Can be either IPv4 or IPv6. (Key Parameter)
.PARAMETER Name
Specifies the FQDN of the host when you add a PTR resource record. (Key Parameter)
.NOTES
Reverse lookup zones do not support scopes, so there should be no DnsRecordPtrScoped subclass created.
#>

[DscResource()]
class DnsRecordPtr : DnsRecordBase
{
[DscProperty(Key)]
[System.String]
$IpAddress

[DscProperty(Key)]
[System.String]
$Name

hidden [System.String] $recordHostName

[DnsRecordPtr] Get()
{
# Ensure $recordHostName is set
$this.recordHostName = $this.getRecordHostName($this.IpAddress)

return ([DnsRecordBase] $this).Get()
}

[void] Set()
{
# Ensure $recordHostName is set
$this.recordHostName = $this.getRecordHostName($this.IpAddress)

([DnsRecordBase] $this).Set()
}

[System.Boolean] Test()
{
# Ensure $recordHostName is set
$this.recordHostName = $this.getRecordHostName($this.IpAddress)

return ([DnsRecordBase] $this).Test()
}

hidden [Microsoft.Management.Infrastructure.CimInstance] GetResourceRecord()
{
Write-Verbose -Message ($this.localizedData.GettingDnsRecordMessage -f 'Ptr', $this.ZoneName, $this.ZoneScope, $this.DnsServer)

$dnsParameters = @{
ZoneName = $this.ZoneName
ComputerName = $this.DnsServer
RRType = 'PTR'
Name = $this.recordHostName
}

$record = Get-DnsServerResourceRecord @dnsParameters -ErrorAction SilentlyContinue | Where-Object -FilterScript {
$_.RecordData.PtrDomainName -eq "$($this.Name)."
}

return $record
}

hidden [DnsRecordPtr] NewDscResourceObjectFromRecord([Microsoft.Management.Infrastructure.CimInstance] $record)
{
$dscResourceObject = [DnsRecordPtr] @{
ZoneName = $this.ZoneName
IpAddress = $this.IpAddress
Name = $this.Name
TimeToLive = $record.TimeToLive.ToString()
DnsServer = $this.DnsServer
Ensure = 'Present'
}

return $dscResourceObject
}

hidden [void] AddResourceRecord()
{
$dnsParameters = @{
ZoneName = $this.ZoneName
ComputerName = $this.DnsServer
PTR = $true
Name = $this.recordHostName
PtrDomainName = $this.Name
}

if ($null -ne $this.TimeToLive)
{
$dnsParameters.Add('TimeToLive', $this.TimeToLive)
}

Write-Verbose -Message ($this.localizedData.CreatingDnsRecordMessage -f 'PTR', $this.ZoneName, $this.ZoneScope, $this.DnsServer)

Add-DnsServerResourceRecord @dnsParameters
}

hidden [void] ModifyResourceRecord([Microsoft.Management.Infrastructure.CimInstance] $existingRecord, [System.Collections.Hashtable[]] $propertiesNotInDesiredState)
{
$dnsParameters = @{
ZoneName = $this.ZoneName
ComputerName = $this.DnsServer
}

# Copy the existing record and modify values as appropriate
$newRecord = [Microsoft.Management.Infrastructure.CimInstance]::new($existingRecord)

foreach ($propertyToChange in $propertiesNotInDesiredState)
{
switch ($propertyToChange.Property)
{
# Key parameters will never be affected, so only include Mandatory and Optional values in the switch statement
'TimeToLive'
{
$newRecord.TimeToLive = [System.TimeSpan] $propertyToChange.ExpectedValue
}

}
}

Set-DnsServerResourceRecord @dnsParameters -OldInputObject $existingRecord -NewInputObject $newRecord -Verbose
}

# Take a compressed IPv6 string (i.e.: fd00::1) and expand it out to the full notation (i.e.: fd00:0000:0000:0000:0000:0000:0000:0001)
hidden [System.String] expandIPv6String($string)
{
# Split the string on the colons
$segments = [System.Collections.ArrayList]::new(($string -split ':'))

# Determine how many segments need to be added to reach the 8 required
$blankSegmentCount = 8 - $segments.count

# Insert missing segments
for ($i = 0; $i -lt $blankSegmentCount; $i++)
{
$segments.Insert(1, '0000')
}

# Pad out all segments with leading zeros
$paddedSegments = $segments | ForEach-Object {
$_.PadLeft(4, '0')
}
return ($paddedSegments -join ':')
}

# Translate the IP address to the reverse notation used by the DNS server
hidden [System.String] getReverseNotation([System.Net.IpAddress] $ipAddressObj)
{
$significantData = [System.Collections.ArrayList]::New()

switch ($ipAddressObj.AddressFamily)
{
'InterNetwork'
{
$significantData.AddRange(($ipAddressObj.IPAddressToString -split '\.'))
break
}

'InterNetworkV6'
{
# Get the hex values into an ArrayList
$significantData.AddRange(($this.expandIPv6String($ipAddressObj.IPAddressToString) -replace ':', '' -split ''))
break
}
}

$significantData.Reverse()

# The reverse lookup notation puts a '.' between each hex value
return ($significantData -join '.').Trim('.')
}

# Determine the record host name
hidden [System.String] getRecordHostName([System.Net.IpAddress] $ipAddressObj)
{
$reverseLookupAddressComponent = ""

switch ($ipAddressObj.AddressFamily)
{
'InterNetwork'
{
if (-not $this.ZoneName.ToLower().EndsWith('.in-addr.arpa'))
{
throw ($this.localizedData.NotAnIPv4Zone -f $this.ZoneName)
}
$reverseLookupAddressComponent = $this.ZoneName.Replace('.in-addr.arpa', '')
break
}

'InterNetworkV6'
{
if (-not $this.ZoneName.ToLower().EndsWith('.ip6.arpa'))
{
throw ($this.localizedData.NotAnIPv6Zone -f $this.ZoneName)
}
$reverseLookupAddressComponent = $this.ZoneName.Replace('.ip6.arpa', '')
break
}
}

$reverseNotation = $this.getReverseNotation($ipAddressObj)

# Check to make sure that the ip address actually belongs in this zone
if ($reverseNotation -notmatch "$($reverseLookupAddressComponent)`$")
{
throw $this.localizedData.WrongZone -f $ipAddressObj.IPAddressToString, $this.ZoneName
}

# Strip the zone name from the reversed IP using a regular expression
$ptrRecordHostName = $reverseNotation -replace "\.$([System.Text.RegularExpressions.Regex]::Escape($reverseLookupAddressComponent))`$", ""

return $ptrRecordHostName
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<#PSScriptInfo
.VERSION 1.0.1
.GUID 1133365f-c781-4f18-9cb5-96ca1c282c87
.AUTHOR DSC Community
.COMPANYNAME DSC Community
.COPYRIGHT DSC Community contributors. All rights reserved.
.TAGS DSCConfiguration
.LICENSEURI https://github.com/dsccommunity/xDnsServer/blob/main/LICENSE
.PROJECTURI https://github.com/dsccommunity/xDnsServer
.ICONURI https://dsccommunity.org/images/DSC_Logo_300p.png
.EXTERNALMODULEDEPENDENCIES
.REQUIREDSCRIPTS
.EXTERNALSCRIPTDEPENDENCIES
.RELEASENOTES
Updated author, copyright notice, and URLs.
.PRIVATEDATA 2016-Datacenter,2016-Datacenter-Server-Core
#>

#Requires -Module xDnsServer


<#
.DESCRIPTION
This configuration will ensure a DNS PTR record exists when only the mandatory properties are specified.
#>

Configuration DnsRecordPtr_Mandatory_config
{
Import-DscResource -ModuleName 'xDnsServer'

Node localhost
{
DnsRecordPtr 'TestRecord'
{
ZoneName = '0.168.192.in-addr.arpa'
IpAddress = '192.168.0.9'
Name = 'quarks.contoso.com'
Ensure = 'Present'
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<#PSScriptInfo
.VERSION 1.0.1
.GUID 5aa5cffa-4e3a-4861-b601-73f234015bc9
.AUTHOR DSC Community
.COMPANYNAME DSC Community
.COPYRIGHT DSC Community contributors. All rights reserved.
.TAGS DSCConfiguration
.LICENSEURI https://github.com/dsccommunity/xDnsServer/blob/main/LICENSE
.PROJECTURI https://github.com/dsccommunity/xDnsServer
.ICONURI https://dsccommunity.org/images/DSC_Logo_300p.png
.EXTERNALMODULEDEPENDENCIES
.REQUIREDSCRIPTS
.EXTERNALSCRIPTDEPENDENCIES
.RELEASENOTES
Updated author, copyright notice, and URLs.
.PRIVATEDATA 2016-Datacenter,2016-Datacenter-Server-Core
#>

#Requires -Module xDnsServer


<#
.DESCRIPTION
This configuration will ensure a DNS PTR record exists when all properties are specified.
#>

Configuration DnsRecordPtr_Full_config
{
Import-DscResource -ModuleName 'xDnsServer'

Node localhost
{
DnsRecordPtr 'TestRecord'
{
ZoneName = '0.168.192.in-addr.arpa'
IpAddress = '192.168.0.9'
Name = 'quarks.contoso.com'
TimeToLive = '01:00:00'
DnsServer = 'localhost'
Ensure = 'Present'
}
}
}
Loading

0 comments on commit ab78fb6

Please sign in to comment.