From f623062b1f8f369a06a721e496d94938c8bb3e92 Mon Sep 17 00:00:00 2001 From: EJansenMSFT Date: Thu, 10 Apr 2025 18:28:44 -0400 Subject: [PATCH 01/16] Added a DnsServerStubZone resource for managing file-backed stub zones. --- .../DSC_DnsServerStubZone.psm1 | 196 ++++++++++++++++++ .../DSC_DnsServerStubZone.schema.mof | Bin 0 -> 1170 bytes .../DSC_DNSServerStubZone/README.md | 3 + .../en-US/DSC_DnsServerStubZone.strings.psd1 | 8 + .../en-US/about_DnsServerStubZone.help.txt | 135 ++++++++++++ 5 files changed, 342 insertions(+) create mode 100644 source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 create mode 100644 source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.schema.mof create mode 100644 source/DSCResources/DSC_DNSServerStubZone/README.md create mode 100644 source/DSCResources/DSC_DNSServerStubZone/en-US/DSC_DnsServerStubZone.strings.psd1 create mode 100644 source/DSCResources/DSC_DNSServerStubZone/en-US/about_DnsServerStubZone.help.txt diff --git a/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 b/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 new file mode 100644 index 00000000..21e66afd --- /dev/null +++ b/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 @@ -0,0 +1,196 @@ +$script:dscResourceCommonPath = Join-Path -Path $PSScriptRoot -ChildPath '..\..\Modules\DscResource.Common' +$script:dnsServerDscCommonPath = Join-Path -Path $PSScriptRoot -ChildPath '..\..\Modules\DnsServerDsc.Common' + +Import-Module -Name $script:dscResourceCommonPath +Import-Module -Name $script:dnsServerDscCommonPath + +$script:localizedData = Get-LocalizedData -DefaultUICulture 'en-US' + +function Get-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $Name, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [System.String] + $ZoneFile = "$Name.dns", + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string[]] + $MasterServers, + + [Parameter()] + [ValidateSet('Present','Absent')] + [System.String] + $Ensure = 'Present' + ) + + Assert-Module -ModuleName 'DNSServer' + + Write-Verbose ($script:localizedData.CheckingZoneMessage -f $Name, $Ensure) + + $dnsServerZone = Get-DnsServerZone -Name $Name -ErrorAction SilentlyContinue + + + $targetResource = @{ + Name = $Name + ZoneFile = $dnsServerZone.ZoneFile + MasterServers = $dnsServerZone.MasterServers | ForEach-Object { $_.IPAddressToString } | Sort-Object + Ensure = if ($null -eq $dnsServerZone) { 'Absent' } else { 'Present' } + } + + return $targetResource + +} #end function Get-TargetResource + +function Test-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $Name, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [System.String] + $ZoneFile = "$Name.dns", + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string[]] + $MasterServers, + + [Parameter()] + [ValidateSet('Present','Absent')] + [System.String] + $Ensure = 'Present' + ) + + $targetResource = Get-TargetResource @PSBoundParameters + + $targetResourceInCompliance = $true + + #If we specify that we want to ensure the zone should be PRESENT, check compliance. + if ($Ensure -eq 'Present') + { + #If the results of our Get-TargetResource shows that the zone is PRESENT, move on to the next validation check. + if ($targetResource.Ensure -eq 'Present') + { + #If the zonefile name doesn't equal what we defined, set non-compliance. + if ($targetResource.ZoneFile -ne $ZoneFile) + { + #ZoneFile name differs from the desired configuration 0}' '{1}' '{2}' <--- Definitions in the DSC_DnsServerStubZone.strings.psd1 + Write-Verbose ($script:localizedData.NotDesiredPropertyMessage -f 'ZoneFile', $targetResource.ZoneFile, $ZoneFile) + + $targetResourceInCompliance = $false + } + #If the Master Servers differ from the desired configuration, set non-compliance. + if (($targetResource.MasterServers -join ',' | Sort-Object) -ne ($MasterServers -join ',' | Sort-Object)) + { + #Zone Master Servers differ from the desired configuration '{0}' '{1}' '{2}' <--- Definitions in the DSC_DnsServerStubZone.strings.psd1 + Write-Verbose ($script:localizedData.NotDesiredPropertyMessage -f 'MasterServers', ($MasterServers -join ','), ($targetResource.MasterServers -join ',')) + $targetResourceInCompliance = $false + } + } + #Otherwise, if the results of our Get-TargetResource shows that the zone is ABSENT, set non-compliance, because we want it to exist. + else + { + # Dns zone is absent and should be present. '{0}' '{1}' '{2}' <--- Definitions in the DSC_DnsServerStubZone.strings.psd1 + Write-Verbose ($script:localizedData.NotDesiredPropertyMessage -f 'Ensure', 'Present', 'Absent') + + $targetResourceInCompliance = $false + } + } + #Otherwise, if $Ensure is not PRESENT we specify that means we want to ensure the zone is ABSENT, so check compliance. + else + { + #If the results of our Get-TargetResource shows that the zone is present, set non-compliance. + if ($targetResource.Ensure -eq 'Present') + { + # Dns zone is present and needs removing. '{0}' '{1}' '{2}' <--- Definitions in the DSC_DnsServerStubZone.strings.psd1 + Write-Verbose ($script:localizedData.NotDesiredPropertyMessage -f 'Ensure', 'Absent', 'Present') + + $targetResourceInCompliance = $false + } + } + + return $targetResourceInCompliance + +} #end function Test-TargetResource + +function Set-TargetResource +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [System.String] + $Name, + + [Parameter()] + [ValidateNotNullOrEmpty()] + [System.String] + $ZoneFile = "$Name.dns", + + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [string[]] + $MasterServers, + + [Parameter()] + [ValidateSet('Present','Absent')] + [System.String] + $Ensure = 'Present' + ) + + Assert-Module -ModuleName 'DNSServer' + + if ($Ensure -eq 'Present') + { + Write-Verbose ($script:localizedData.CheckingZoneMessage -f $Name, $Ensure) + + $dnsServerZone = Get-DnsServerZone -Name $Name -ErrorAction SilentlyContinue + + if ($dnsServerZone) + { + + $DesiredMasterServers = $MasterServers | Sort-Object + $CurrentMasterServers = $dnsServerZone.MasterServers | ForEach-Object { $_.IPAddressToString } | Sort-Object + + + if($CurrentMasterServers -join ',' -ne $DesiredMasterServers -join ',' ) + { + # Update the Master Servers list + Set-DnsServerStubZone -Name $Name -MasterServers $MasterServers + + Write-Verbose ($script:localizedData.SetPropertyMessage -f 'MasterServers') + } + + } + elseif (-not $dnsServerZone) + { + # Create the DNS Server zone. + Write-Verbose ($script:localizedData.AddingZoneMessage -f $Name) + + Add-DnsServerStubZone -Name $Name -ZoneFile $ZoneFile -MasterServers $MasterServers + } + } + elseif ($Ensure -eq 'Absent') + { + # Remove the DNS Server zone. + Write-Verbose ($script:localizedData.RemovingZoneMessage -f $Name) + + Get-DnsServerZone -Name $Name | Remove-DnsServerZone -Force + } + +} #end function Set-TargetResource diff --git a/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.schema.mof b/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.schema.mof new file mode 100644 index 0000000000000000000000000000000000000000..1c05b0c40b77a6033da63a9995c1e13cb7b25cac GIT binary patch literal 1170 zcmcJOQA+|*5QWck(0{o2QbI(%)>CCdR2B&eiG;dqEvT8h1}6UX>YKZ+x*LKovAFko zch1Z?Gw1$%w{)d}I*NH33N>Mk^sbQ(Rnn=Bb+Y=ERMwH4szgP^jue2MsRn+?e?wF_ zjaB!VKK;9{L{oM3?6Zc%l-1V0_S9QdcHrtTdD#K)bOwh9-Rp+iV{ML#yEsk7$P>J4%HGVZ1x|}jc#Z;TqS5BS9B|iH| zub{H5=LJShACpkQ`ucyY99kl}9WqNt-%QyE#tCbzG3PzLTTIAIjZ46;Pj9E75@M_q z@4OFIbIddy6KgX+Aa^?3qWWy|)g^~w7i@MPREp?ER)2dh?ufI#XA>HF2Urg$oW*`k zoo7lPIqSeM@VU+9<`YLX+Xd{pcw#;0zZ2c2>VBi{rr%?n)>cvDu9I$hzoo7-Jw^Xz zJ~O3nOvL7ssxX(1mn!XUrX8{z9WG! literal 0 HcmV?d00001 diff --git a/source/DSCResources/DSC_DNSServerStubZone/README.md b/source/DSCResources/DSC_DNSServerStubZone/README.md new file mode 100644 index 00000000..0d79afed --- /dev/null +++ b/source/DSCResources/DSC_DNSServerStubZone/README.md @@ -0,0 +1,3 @@ +# Description + +The DnsServerStubZone DSC resource manages a standalone file-backed Stub zone on a given Domain Name System (DNS) server. diff --git a/source/DSCResources/DSC_DNSServerStubZone/en-US/DSC_DnsServerStubZone.strings.psd1 b/source/DSCResources/DSC_DNSServerStubZone/en-US/DSC_DnsServerStubZone.strings.psd1 new file mode 100644 index 00000000..b2e41d11 --- /dev/null +++ b/source/DSCResources/DSC_DNSServerStubZone/en-US/DSC_DnsServerStubZone.strings.psd1 @@ -0,0 +1,8 @@ +# culture="en-US" +ConvertFrom-StringData @' + CheckingZoneMessage = Checking DNS server zone with name '{0}' is '{1}'... + AddingZoneMessage = Adding DNS server zone '{0}' ... + RemovingZoneMessage = Removing DNS server zone '{0}' ... + NotDesiredPropertyMessage = DNS server zone property '{0}' is not correct. Expected '{1}', Actual '{2}' + SetPropertyMessage = DNS server zone property '{0}' is set +'@ diff --git a/source/DSCResources/DSC_DNSServerStubZone/en-US/about_DnsServerStubZone.help.txt b/source/DSCResources/DSC_DNSServerStubZone/en-US/about_DnsServerStubZone.help.txt new file mode 100644 index 00000000..bd16901d --- /dev/null +++ b/source/DSCResources/DSC_DNSServerStubZone/en-US/about_DnsServerStubZone.help.txt @@ -0,0 +1,135 @@ +.NAME + DnsServerStubZone + +.DESCRIPTION + The DnsServerStubZone DSC resource manages a file-backed DNS Stub zone on a Domain Name System (DNS) server. + Stub zones contain only the authoritative name server records (NS, SOA, A) from another DNS zone, and are typically used for forwarding queries to specific DNS servers for that zone. + +.PARAMETER Name + Key - String + Name of the DNS Server stub zone + +.PARAMETER ZoneFile + Write - String + Name of the DNS Server stub zone file. If not specified, defaults to 'ZoneName.dns'. + +.PARAMETER MasterServers + Write - String + Allowed values: Server IPs + +.PARAMETER Ensure + Write - String + Allowed values: Present, Absent + Whether the DNS zone should be present or absent + +.EXAMPLE 1 + +This configuration will add a file-backed classful reverse Stub zone +using the resource default parameter values. + +Configuration DnsServerStubZone_AddClassfulReverseStubZone_Config +{ + Import-DscResource -ModuleName 'DnsServerDsc' + + Node localhost + { + DnsServerStubZone 'AddStubZone' + { + Name = '1.168.192.in-addr.arpa' + } + } +} + +.EXAMPLE 2 + +This configuration will add a file-backed classless reverse Stub zone +using the resource default parameter values. + +Configuration DnsServerStubZone_AddClasslessReverseStubZone_Config +{ + Import-DscResource -ModuleName 'DnsServerDsc' + + Node localhost + { + DnsServerStubZone 'AddStubZone' + { + Name = '64-26.100.168.192.in-addr.arpa' + } + } +} + +.EXAMPLE 3 + +This configuration will add a file-backed Stub Stub using the resource +default parameter values. + +Configuration DnsServerStubZone_AddStubZoneUsingDefaults_Config +{ + Import-DscResource -ModuleName 'DnsServerDsc' + + Node localhost + { + DnsServerStubZone 'AddStubZone' + { + Name = 'demo.contoso.com' + } + } +} + +.EXAMPLE 4 + +This configuration will add a file-backed Stub zone using the resource +default parameter values. + +Configuration DnsServerStubZone_AddStubZoneWithSpecificValues_Config +{ + Import-DscResource -ModuleName 'DnsServerDsc' + + Node localhost + { + DnsServerStubZone 'AddStubZone' + { + Ensure = 'Present' + Name = 'demo.contoso.com' + ZoneFile = 'demo.contoso.com.dns' + MasterServers = @('192.168.1.10', '192.168.1.11') + } + } +} + +.EXAMPLE 5 + +This configuration will remove a file-backed Stub zone. + +Configuration DnsServerStubZone_RemoveStubZone_Config +{ + Import-DscResource -ModuleName 'DnsServerDsc' + + Node localhost + { + DnsServerStubZone 'RemoveStubZone' + { + Ensure = 'Absent' + Name = 'demo.contoso.com' + } + } +} + +.EXAMPLE 6 + +This configuration will remove a file-backed Stub zone. + +Configuration DnsServerStubZone_RemoveReverseStubZone_Config +{ + Import-DscResource -ModuleName 'DnsServerDsc' + + Node localhost + { + DnsServerStubZone 'RemoveStubZone' + { + Ensure = 'Absent' + Name = '1.168.192.in-addr.arpa' + } + } +} + From 00a9e9e5df9bd1b6322a50f7eb32cc6075d73bc6 Mon Sep 17 00:00:00 2001 From: EJansenMSFT Date: Thu, 10 Apr 2025 18:44:58 -0400 Subject: [PATCH 02/16] Updated the help file for the DSC_DNSServerStubZone resource --- .../en-US/about_DnsServerStubZone.help.txt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/source/DSCResources/DSC_DNSServerStubZone/en-US/about_DnsServerStubZone.help.txt b/source/DSCResources/DSC_DNSServerStubZone/en-US/about_DnsServerStubZone.help.txt index bd16901d..6b1738b3 100644 --- a/source/DSCResources/DSC_DNSServerStubZone/en-US/about_DnsServerStubZone.help.txt +++ b/source/DSCResources/DSC_DNSServerStubZone/en-US/about_DnsServerStubZone.help.txt @@ -2,7 +2,7 @@ DnsServerStubZone .DESCRIPTION - The DnsServerStubZone DSC resource manages a file-backed DNS Stub zone on a Domain Name System (DNS) server. + The DnsServerStubZone DSC resource manages a file-backed DNS Stub zone on a Domain Name System (DNS) server. Stub zones contain only the authoritative name server records (NS, SOA, A) from another DNS zone, and are typically used for forwarding queries to specific DNS servers for that zone. .PARAMETER Name @@ -15,7 +15,7 @@ .PARAMETER MasterServers Write - String - Allowed values: Server IPs + Allowed values: Server IPs .PARAMETER Ensure Write - String @@ -36,6 +36,7 @@ Configuration DnsServerStubZone_AddClassfulReverseStubZone_Config DnsServerStubZone 'AddStubZone' { Name = '1.168.192.in-addr.arpa' + MasterServers = @('192.168.1.10', '192.168.1.11') } } } @@ -54,13 +55,14 @@ Configuration DnsServerStubZone_AddClasslessReverseStubZone_Config DnsServerStubZone 'AddStubZone' { Name = '64-26.100.168.192.in-addr.arpa' + MasterServers = @('192.168.1.10', '192.168.1.11') } } } .EXAMPLE 3 -This configuration will add a file-backed Stub Stub using the resource +This configuration will add a file-backed Stub Zone using the resource default parameter values. Configuration DnsServerStubZone_AddStubZoneUsingDefaults_Config @@ -72,6 +74,7 @@ Configuration DnsServerStubZone_AddStubZoneUsingDefaults_Config DnsServerStubZone 'AddStubZone' { Name = 'demo.contoso.com' + MasterServers = @('192.168.1.10', '192.168.1.11') } } } @@ -132,4 +135,3 @@ Configuration DnsServerStubZone_RemoveReverseStubZone_Config } } } - From c3b0641a203e1f03b035e9d0d66cda148eb26753 Mon Sep 17 00:00:00 2001 From: EJansenMSFT Date: Thu, 10 Apr 2025 19:12:57 -0400 Subject: [PATCH 03/16] Updated ChangeLog.md --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index eae3f5cc..9fd75df2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,6 +67,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - DnsServerDsc.Common - Removed `Test-DnsDscParameterState` and associated localization entries. + ### Added + +- DnsServerDsc + - Added new resource + -DSC_DnsServerStubZone - Added a new resource to manage file-backed DNS Stub Zones since it didn't previously exist and has been requested - https://github.com/dsccommunity/DnsServerDsc/issues/30 + -Added on 4/10/2025 + ## [3.0.0] - 2021-05-26 ### Removed From 94e9a81619f145254dd1447bd7c8947fc06afbe2 Mon Sep 17 00:00:00 2001 From: EJansenMSFT <72317762+EJansenMSFT@users.noreply.github.com> Date: Sat, 12 Apr 2025 18:15:57 -0400 Subject: [PATCH 04/16] Update source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 Co-authored-by: Chris Dent --- .../DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 b/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 index 21e66afd..b2538c6e 100644 --- a/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 +++ b/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 @@ -37,8 +37,6 @@ function Get-TargetResource Write-Verbose ($script:localizedData.CheckingZoneMessage -f $Name, $Ensure) $dnsServerZone = Get-DnsServerZone -Name $Name -ErrorAction SilentlyContinue - - $targetResource = @{ Name = $Name ZoneFile = $dnsServerZone.ZoneFile From ca9a3bc90067a334018134fa485bfcc66f27fba3 Mon Sep 17 00:00:00 2001 From: EJansenMSFT Date: Sat, 12 Apr 2025 23:28:51 -0400 Subject: [PATCH 05/16] Updated code based on most of the recommendations from Chris - but still need to test. --- .../DSC_DnsServerStubZone.psm1 | 160 +++++++++++------- 1 file changed, 102 insertions(+), 58 deletions(-) diff --git a/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 b/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 index 21e66afd..28a944b8 100644 --- a/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 +++ b/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 @@ -16,16 +16,16 @@ function Get-TargetResource [System.String] $Name, + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [ipaddress[]] + $MasterServers, + [Parameter()] [ValidateNotNullOrEmpty()] [System.String] $ZoneFile = "$Name.dns", - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string[]] - $MasterServers, - [Parameter()] [ValidateSet('Present','Absent')] [System.String] @@ -37,13 +37,12 @@ function Get-TargetResource Write-Verbose ($script:localizedData.CheckingZoneMessage -f $Name, $Ensure) $dnsServerZone = Get-DnsServerZone -Name $Name -ErrorAction SilentlyContinue - $targetResource = @{ Name = $Name + MasterServers = $dnsServerZone.MasterServers | ForEach-Object { $_.IPAddressToString } ZoneFile = $dnsServerZone.ZoneFile - MasterServers = $dnsServerZone.MasterServers | ForEach-Object { $_.IPAddressToString } | Sort-Object - Ensure = if ($null -eq $dnsServerZone) { 'Absent' } else { 'Present' } + Ensure = if (-not $dnsServerZone) { 'Absent' } else { 'Present' } } return $targetResource @@ -60,16 +59,16 @@ function Test-TargetResource [System.String] $Name, + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [ipaddress[]] + $MasterServers, + [Parameter()] [ValidateNotNullOrEmpty()] [System.String] $ZoneFile = "$Name.dns", - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string[]] - $MasterServers, - [Parameter()] [ValidateSet('Present','Absent')] [System.String] @@ -77,54 +76,82 @@ function Test-TargetResource ) $targetResource = Get-TargetResource @PSBoundParameters - $targetResourceInCompliance = $true + $dnsServerZone = Get-DnsServerZone -Name $Name -ErrorAction SilentlyContinue + #If we specify that we want to ensure the zone should be ABSENT, check compliance. + if ($Ensure -eq 'Absent') + { + #If the results of our Get-TargetResource shows that the zone is PRESENT, set compliance, because we want it to be absent. + if ($targetResource.Ensure -eq 'Present') + { + # Dns zone is present and should be absent. '{0}' '{1}' '{2}' <--- Definitions in the DSC_DnsServerStubZone.strings.psd1 + Write-Verbose ($script:localizedData.NotDesiredPropertyMessage -f 'Ensure', 'Absent', 'Present') + + $targetResourceInCompliance = $false + return $targetResourceInCompliance + + } + } #If we specify that we want to ensure the zone should be PRESENT, check compliance. if ($Ensure -eq 'Present') { + + #If the results of our Get-TargetResource shows that the zone is PRESENT, set compliance, because we want it to be absent. + if ($targetResource.Ensure -eq 'Absent') + { + # Dns zone is absent and should be present. + Write-Verbose ($script:localizedData.NotDesiredPropertyMessage -f 'Ensure', 'Present', 'Absent') + + $targetResourceInCompliance = $false + return $targetResourceInCompliance + } + #If the results of our Get-TargetResource shows that the zone is PRESENT, move on to the next validation check. if ($targetResource.Ensure -eq 'Present') { - #If the zonefile name doesn't equal what we defined, set non-compliance. + + #If the Zone is AD integrated, set non-compliance. + if ($dnsServerZone.IsDSIntegrated -eq $true) + { + #Zone Storage differs from the desired configuration + Write-Verbose ($script:localizedData.NotDesiredPropertyMessage -f 'ZoneStorageLocation', 'Active Directory', 'File') + + $targetResourceInCompliance = $false + return $targetResourceInCompliance + } + #If the ZoneType isn't of type Stub, set non-compliance. + if ($dnsServerZone.ZoneType -ne 'Stub') + { + #Zone Type differs from the desired configuration + Write-Verbose ($script:localizedData.NotDesiredPropertyMessage -f 'ZoneType', 'Stub', $targetResource.AllZoneData.ZoneType) + + $targetResourceInCompliance = $false + return $targetResourceInCompliance + } + #If the Zone File name differ from the desired configuration, set non-compliance. if ($targetResource.ZoneFile -ne $ZoneFile) { - #ZoneFile name differs from the desired configuration 0}' '{1}' '{2}' <--- Definitions in the DSC_DnsServerStubZone.strings.psd1 + #ZoneFile name differs from the desired configuration Write-Verbose ($script:localizedData.NotDesiredPropertyMessage -f 'ZoneFile', $targetResource.ZoneFile, $ZoneFile) $targetResourceInCompliance = $false + return $targetResourceInCompliance } #If the Master Servers differ from the desired configuration, set non-compliance. - if (($targetResource.MasterServers -join ',' | Sort-Object) -ne ($MasterServers -join ',' | Sort-Object)) + $Comparison = Compare-Object -ReferenceObject $MasterServers -DifferenceObject $targetResource.MasterServers + if ($Comparison) { - #Zone Master Servers differ from the desired configuration '{0}' '{1}' '{2}' <--- Definitions in the DSC_DnsServerStubZone.strings.psd1 + #Zone Master Servers differ from the desired configuration Write-Verbose ($script:localizedData.NotDesiredPropertyMessage -f 'MasterServers', ($MasterServers -join ','), ($targetResource.MasterServers -join ',')) $targetResourceInCompliance = $false } - } - #Otherwise, if the results of our Get-TargetResource shows that the zone is ABSENT, set non-compliance, because we want it to exist. - else - { - # Dns zone is absent and should be present. '{0}' '{1}' '{2}' <--- Definitions in the DSC_DnsServerStubZone.strings.psd1 - Write-Verbose ($script:localizedData.NotDesiredPropertyMessage -f 'Ensure', 'Present', 'Absent') - $targetResourceInCompliance = $false - } - } - #Otherwise, if $Ensure is not PRESENT we specify that means we want to ensure the zone is ABSENT, so check compliance. - else - { - #If the results of our Get-TargetResource shows that the zone is present, set non-compliance. - if ($targetResource.Ensure -eq 'Present') - { - # Dns zone is present and needs removing. '{0}' '{1}' '{2}' <--- Definitions in the DSC_DnsServerStubZone.strings.psd1 - Write-Verbose ($script:localizedData.NotDesiredPropertyMessage -f 'Ensure', 'Absent', 'Present') + return $targetResourceInCompliance - $targetResourceInCompliance = $false } - } - return $targetResourceInCompliance + } } #end function Test-TargetResource @@ -137,16 +164,16 @@ function Set-TargetResource [System.String] $Name, + [Parameter(Mandatory = $true)] + [ValidateNotNullOrEmpty()] + [ipaddress[]] + $MasterServers, + [Parameter()] [ValidateNotNullOrEmpty()] [System.String] $ZoneFile = "$Name.dns", - [Parameter(Mandatory = $true)] - [ValidateNotNullOrEmpty()] - [string[]] - $MasterServers, - [Parameter()] [ValidateSet('Present','Absent')] [System.String] @@ -155,20 +182,44 @@ function Set-TargetResource Assert-Module -ModuleName 'DNSServer' - if ($Ensure -eq 'Present') + if ($Ensure -eq 'Absent') { Write-Verbose ($script:localizedData.CheckingZoneMessage -f $Name, $Ensure) - $dnsServerZone = Get-DnsServerZone -Name $Name -ErrorAction SilentlyContinue + IF($dnsServerZone.type -eq 'Stub' -and $dnsServerZone.IsDSIntegrated -eq $false) + { + + # Remove the DNS Server zone. + Write-Verbose ($script:localizedData.RemovingZoneMessage -f $Name) + Get-DnsServerZone -Name $Name | Remove-DnsServerZone -Force - if ($dnsServerZone) + } + else { + IF($dnsServerZone.type -ne 'Stub'){ + + # Zone is not a stub zone. + Write-Verbose ($script:localizedData.NotDesiredPropertyMessage -f 'ZoneType', 'Stub', $dnsServerZone.ZoneType) + } + If($dnsServerZone.IsDSIntegrated -ne $false){ + + # Zone is AD integrated. + Write-Verbose ($script:localizedData.NotDesiredPropertyMessage -f 'ZoneStorageLocation', 'Active Directory', 'File') + } - $DesiredMasterServers = $MasterServers | Sort-Object - $CurrentMasterServers = $dnsServerZone.MasterServers | ForEach-Object { $_.IPAddressToString } | Sort-Object + } + } + if ($Ensure -eq 'Present') + { + Write-Verbose ($script:localizedData.CheckingZoneMessage -f $Name, $Ensure) - if($CurrentMasterServers -join ',' -ne $DesiredMasterServers -join ',' ) + if ($dnsServerZone.type -eq 'Stub' -and $dnsServerZone.IsDSIntegrated -eq $false) + { + # Compare the Desired master servers to the Existing master servers - if Existing doesn't match Desired, update the master servers for the zone. + $Comparison = Compare-Object -ReferenceObject $MasterServers -DifferenceObject $targetResource.MasterServers + + if (-not $Comparison) { # Update the Master Servers list Set-DnsServerStubZone -Name $Name -MasterServers $MasterServers @@ -180,17 +231,10 @@ function Set-TargetResource elseif (-not $dnsServerZone) { # Create the DNS Server zone. - Write-Verbose ($script:localizedData.AddingZoneMessage -f $Name) - Add-DnsServerStubZone -Name $Name -ZoneFile $ZoneFile -MasterServers $MasterServers - } - } - elseif ($Ensure -eq 'Absent') - { - # Remove the DNS Server zone. - Write-Verbose ($script:localizedData.RemovingZoneMessage -f $Name) - Get-DnsServerZone -Name $Name | Remove-DnsServerZone -Force + Write-Verbose ($script:localizedData.AddingZoneMessage -f $Name) + } } } #end function Set-TargetResource From 1c3771821c842352fe1b1f4c545856d89f915cbd Mon Sep 17 00:00:00 2001 From: EJansenMSFT Date: Sun, 13 Apr 2025 00:45:16 -0400 Subject: [PATCH 06/16] Fixed a couple more things. --- .../DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 b/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 index 28a944b8..0b094b9b 100644 --- a/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 +++ b/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 @@ -124,7 +124,7 @@ function Test-TargetResource if ($dnsServerZone.ZoneType -ne 'Stub') { #Zone Type differs from the desired configuration - Write-Verbose ($script:localizedData.NotDesiredPropertyMessage -f 'ZoneType', 'Stub', $targetResource.AllZoneData.ZoneType) + Write-Verbose ($script:localizedData.NotDesiredPropertyMessage -f 'ZoneType', 'Stub', $dnsServerZone.ZoneType) $targetResourceInCompliance = $false return $targetResourceInCompliance @@ -181,6 +181,7 @@ function Set-TargetResource ) Assert-Module -ModuleName 'DNSServer' + $dnsServerZone = Get-DnsServerZone -Name $Name -ErrorAction SilentlyContinue if ($Ensure -eq 'Absent') { From 14180f470a22d6ba095910248c95dad2c1e2f033 Mon Sep 17 00:00:00 2001 From: EJansenMSFT Date: Sun, 13 Apr 2025 01:29:35 -0400 Subject: [PATCH 07/16] fixing small issues post testing --- .../DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 b/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 index 0b094b9b..3e2e6b9c 100644 --- a/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 +++ b/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 @@ -215,7 +215,7 @@ function Set-TargetResource { Write-Verbose ($script:localizedData.CheckingZoneMessage -f $Name, $Ensure) - if ($dnsServerZone.type -eq 'Stub' -and $dnsServerZone.IsDSIntegrated -eq $false) + if ($dnsServerZone.ZoneType -eq 'Stub' -and $dnsServerZone.IsDSIntegrated -eq $false) { # Compare the Desired master servers to the Existing master servers - if Existing doesn't match Desired, update the master servers for the zone. $Comparison = Compare-Object -ReferenceObject $MasterServers -DifferenceObject $targetResource.MasterServers From 404618e622c5cf253f70ad72f3bd03822d55fa97 Mon Sep 17 00:00:00 2001 From: EJansenMSFT Date: Sun, 13 Apr 2025 01:50:55 -0400 Subject: [PATCH 08/16] Updating Notes --- .../DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 b/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 index 3e2e6b9c..d0ba2d2a 100644 --- a/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 +++ b/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 @@ -181,6 +181,7 @@ function Set-TargetResource ) Assert-Module -ModuleName 'DNSServer' + $targetResource = Get-TargetResource @PSBoundParameters $dnsServerZone = Get-DnsServerZone -Name $Name -ErrorAction SilentlyContinue if ($Ensure -eq 'Absent') @@ -220,7 +221,8 @@ function Set-TargetResource # Compare the Desired master servers to the Existing master servers - if Existing doesn't match Desired, update the master servers for the zone. $Comparison = Compare-Object -ReferenceObject $MasterServers -DifferenceObject $targetResource.MasterServers - if (-not $Comparison) + #If the Master Servers differ from the desired configuration, set them to the desired configuration. + if ($Comparison) { # Update the Master Servers list Set-DnsServerStubZone -Name $Name -MasterServers $MasterServers From 53f03e879216717a68b217c1f27e36b937cbd243 Mon Sep 17 00:00:00 2001 From: EJansenMSFT Date: Sun, 13 Apr 2025 02:03:30 -0400 Subject: [PATCH 09/16] more styling fixes --- .../DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 b/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 index d0ba2d2a..cd3b58cc 100644 --- a/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 +++ b/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 @@ -198,12 +198,12 @@ function Set-TargetResource } else { - IF($dnsServerZone.type -ne 'Stub'){ + iF ($dnsServerZone.type -ne 'Stub'){ # Zone is not a stub zone. Write-Verbose ($script:localizedData.NotDesiredPropertyMessage -f 'ZoneType', 'Stub', $dnsServerZone.ZoneType) } - If($dnsServerZone.IsDSIntegrated -ne $false){ + if ($dnsServerZone.IsDSIntegrated -ne $false){ # Zone is AD integrated. Write-Verbose ($script:localizedData.NotDesiredPropertyMessage -f 'ZoneStorageLocation', 'Active Directory', 'File') From 83d1c5934007da3dcb4ec6dac2effb693898f342 Mon Sep 17 00:00:00 2001 From: EJansenMSFT Date: Mon, 14 Apr 2025 15:01:33 -0400 Subject: [PATCH 10/16] Changing [ipaddress[]] back to [System.String[]] --- .../DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 b/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 index cd3b58cc..ee6e9bf8 100644 --- a/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 +++ b/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 @@ -18,7 +18,7 @@ function Get-TargetResource [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [ipaddress[]] + [System.String[]] $MasterServers, [Parameter()] @@ -61,7 +61,7 @@ function Test-TargetResource [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [ipaddress[]] + [System.String[]] $MasterServers, [Parameter()] @@ -166,7 +166,7 @@ function Set-TargetResource [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] - [ipaddress[]] + [System.String[]] $MasterServers, [Parameter()] From e267bf62f7256f51fbfd8efce62634d135134ec7 Mon Sep 17 00:00:00 2001 From: EJansenMSFT Date: Mon, 14 Apr 2025 15:04:33 -0400 Subject: [PATCH 11/16] changing [ipaddress[]] back to [system.string[]] --- .vscode/settings.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 3f005cad..dd3d358d 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -54,5 +54,8 @@ "pester.runTestsInNewProcess": true, "pester.pesterModulePath": "./output/RequiredModules/Pester", "powershell.pester.codeLens": true, - "pester.suppressCodeLensNotice": true + "pester.suppressCodeLensNotice": true, + "githubPullRequests.ignoredPullRequestBranches": [ + "main" + ] } From 303eefb0f9f5e4fdd2fc480879cfebdb023e0165 Mon Sep 17 00:00:00 2001 From: EJansenMSFT Date: Mon, 14 Apr 2025 17:22:09 -0400 Subject: [PATCH 12/16] Looks like it was saved as UTF16LE - changed to UTF8 --- .../DSC_DnsServerStubZone.schema.mof | Bin 1170 -> 578 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.schema.mof b/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.schema.mof index 1c05b0c40b77a6033da63a9995c1e13cb7b25cac..ceb9a59c193c95c86e1a4b4e7744b75aa3d57886 100644 GIT binary patch literal 578 zcmb7>QES356oudWD-L~8p_J`)K5ce_Y&x-^FcFFE?F`hk%S{GO{O?OzS2i{VOI`x! z-uvBivL2O!c}bdOrTXD@eC2OAh%nZYR9+ru;z0c{QJj)~Bb^$%d#)6PgH4e6UL@&g z`$NVJo~D!SonTrJS5{{foC3JBexPH7ggDc(HVxGyP!%wHLNZIylE4my_m+j!XsKS{ zONZ^8m6nG7ISGXCCdR2B&eiG;dqEvT8h1}6UX>YKZ+x*LKovAFko zch1Z?Gw1$%w{)d}I*NH33N>Mk^sbQ(Rnn=Bb+Y=ERMwH4szgP^jue2MsRn+?e?wF_ zjaB!VKK;9{L{oM3?6Zc%l-1V0_S9QdcHrtTdD#K)bOwh9-Rp+iV{ML#yEsk7$P>J4%HGVZ1x|}jc#Z;TqS5BS9B|iH| zub{H5=LJShACpkQ`ucyY99kl}9WqNt-%QyE#tCbzG3PzLTTIAIjZ46;Pj9E75@M_q z@4OFIbIddy6KgX+Aa^?3qWWy|)g^~w7i@MPREp?ER)2dh?ufI#XA>HF2Urg$oW*`k zoo7lPIqSeM@VU+9<`YLX+Xd{pcw#;0zZ2c2>VBi{rr%?n)>cvDu9I$hzoo7-Jw^Xz zJ~O3nOvL7ssxX(1mn!XUrX8{z9WG! From 59f19fad807ce1e3c43e557a8ea2d428479740e3 Mon Sep 17 00:00:00 2001 From: EJansenMSFT Date: Mon, 14 Apr 2025 17:36:44 -0400 Subject: [PATCH 13/16] Fixing more styling issues based on the PS Script Analyzer. --- .../DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 b/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 index ee6e9bf8..4bc787d7 100644 --- a/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 +++ b/source/DSCResources/DSC_DNSServerStubZone/DSC_DnsServerStubZone.psm1 @@ -96,7 +96,6 @@ function Test-TargetResource #If we specify that we want to ensure the zone should be PRESENT, check compliance. if ($Ensure -eq 'Present') { - #If the results of our Get-TargetResource shows that the zone is PRESENT, set compliance, because we want it to be absent. if ($targetResource.Ensure -eq 'Absent') { @@ -110,7 +109,6 @@ function Test-TargetResource #If the results of our Get-TargetResource shows that the zone is PRESENT, move on to the next validation check. if ($targetResource.Ensure -eq 'Present') { - #If the Zone is AD integrated, set non-compliance. if ($dnsServerZone.IsDSIntegrated -eq $true) { @@ -188,7 +186,7 @@ function Set-TargetResource { Write-Verbose ($script:localizedData.CheckingZoneMessage -f $Name, $Ensure) - IF($dnsServerZone.type -eq 'Stub' -and $dnsServerZone.IsDSIntegrated -eq $false) + if ($dnsServerZone.type -eq 'Stub' -and $dnsServerZone.IsDSIntegrated -eq $false) { # Remove the DNS Server zone. @@ -198,12 +196,14 @@ function Set-TargetResource } else { - iF ($dnsServerZone.type -ne 'Stub'){ + if ($dnsServerZone.type -ne 'Stub') + { # Zone is not a stub zone. Write-Verbose ($script:localizedData.NotDesiredPropertyMessage -f 'ZoneType', 'Stub', $dnsServerZone.ZoneType) } - if ($dnsServerZone.IsDSIntegrated -ne $false){ + if ($dnsServerZone.IsDSIntegrated -ne $false) + { # Zone is AD integrated. Write-Verbose ($script:localizedData.NotDesiredPropertyMessage -f 'ZoneStorageLocation', 'Active Directory', 'File') From 198f6d12c971d8cc91a1365cc6eb1ef6cf87646a Mon Sep 17 00:00:00 2001 From: EJansenMSFT Date: Mon, 14 Apr 2025 21:08:28 -0400 Subject: [PATCH 14/16] Testing out integration tests. --- ...SC_DnsServerStubZone.Integration.Tests.ps1 | 282 ++++++++++++++++++ .../DSC_DnsServerStubZone.config.ps1 | 75 +++++ 2 files changed, 357 insertions(+) create mode 100644 tests/Integration/DSC_DnsServerStubZone.Integration.Tests.ps1 create mode 100644 tests/Integration/DSC_DnsServerStubZone.config.ps1 diff --git a/tests/Integration/DSC_DnsServerStubZone.Integration.Tests.ps1 b/tests/Integration/DSC_DnsServerStubZone.Integration.Tests.ps1 new file mode 100644 index 00000000..2f438e84 --- /dev/null +++ b/tests/Integration/DSC_DnsServerStubZone.Integration.Tests.ps1 @@ -0,0 +1,282 @@ +# Suppressing this rule because Script Analyzer does not understand Pester's syntax. +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] +param () + +BeforeDiscovery { + try + { + if (-not (Get-Module -Name 'DscResource.Test')) + { + # Assumes dependencies has been resolved, so if this module is not available, run 'noop' task. + if (-not (Get-Module -Name 'DscResource.Test' -ListAvailable)) + { + # Redirect all streams to $null, except the error stream (stream 2) + & "$PSScriptRoot/../../build.ps1" -Tasks 'noop' 3>&1 4>&1 5>&1 6>&1 > $null + } + + # If the dependencies has not been resolved, this will throw an error. + Import-Module -Name 'DscResource.Test' -Force -ErrorAction 'Stop' + } + } + catch [System.IO.FileNotFoundException] + { + throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -ResolveDependency -Tasks build" first.' + } + + <# + Need to define that variables here to be used in the Pester Discover to + build the ForEach-blocks. + #> + $script:dscModuleName = 'DnsServerDsc' + $script:dscResourceFriendlyName = 'DnsServerStubZone' + $script:dscResourceName = "DSC_$($script:dscResourceFriendlyName)" + + # Ensure that the tests can be performed on this computer + $script:skipIntegrationTests = $false +} + +BeforeAll { + $script:dscModuleName = 'DnsServerDsc' + $script:dscResourceFriendlyName = 'DnsServerStubZone' + $script:dscResourceName = "DSC_$($script:dscResourceFriendlyName)" + + $script:testEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $script:dscModuleName ` + -DSCResourceName $script:dscResourceName ` + -ResourceType 'Mof' ` + -TestType 'Integration' +} + +AfterAll { + Restore-TestEnvironment -TestEnvironment $script:testEnvironment +} + +Describe "$($script:dscResourceName)_Integration" { + BeforeAll { + $configFile = Join-Path -Path $PSScriptRoot -ChildPath "$($script:dscResourceName).config.ps1" + . $configFile + + $resourceId = "[$($script:dscResourceFriendlyName)]Integration_Test" + } + + Context ('When using configuration <_>') -ForEach @( + "$($script:dscResourceName)_AddStubZoneUsingDefaultValues_Config" + ) { + BeforeAll { + $configurationName = $_ + } + + AfterAll { + Wait-ForIdleLcm -Clear + } + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName ` + -and $_.ResourceId -eq $resourceId + } + + $resourceCurrentState.Ensure | Should -BeNullOrEmpty + $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.StubZoneName + $resourceCurrentState.MasterServers | Should -Be $ConfigurationData.AllNodes.StubMasterServers + $resourceCurrentState.ZoneFile | Should -BeNullOrEmpty + } + + It 'Should return ''True'' when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be 'True' + } + } + + Context ('When using configuration <_>') -ForEach @( + "$($script:dscResourceName)_RemoveStubZone_Config" + ) { + BeforeAll { + $configurationName = $_ + } + + AfterAll { + Wait-ForIdleLcm -Clear + } + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName ` + -and $_.ResourceId -eq $resourceId + } + + $resourceCurrentState.Ensure | Should -Be 'Absent' + $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.StubZoneName + $resourceCurrentState.MasterServers | Should -BeNullOrEmpty + $resourceCurrentState.ZoneFile | Should -BeNullOrEmpty + } + + It 'Should return ''True'' when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be 'True' + } + } + + Context ('When using configuration <_>') -ForEach @( + "$($script:dscResourceName)_AddStubZone_Config" + ) { + BeforeAll { + $configurationName = $_ + } + + AfterAll { + Wait-ForIdleLcm -Clear + } + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName ` + -and $_.ResourceId -eq $resourceId + } + + $resourceCurrentState.Ensure | Should -Be 'Present' + $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.StubZoneName + $resourceCurrentState.MasterServers | Should -Be $ConfigurationData.AllNodes.StubMasterServers + $resourceCurrentState.ZoneFile | Should -Be $ConfigurationData.AllNodes.StubZoneFile + } + + It 'Should return ''True'' when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be 'True' + } + } + + Context ('When using configuration <_>') -ForEach @( + "$($script:dscResourceName)_RemoveStubZone_Config" + ) { + BeforeAll { + $configurationName = $_ + } + + AfterAll { + Wait-ForIdleLcm -Clear + } + It 'Should compile and apply the MOF without throwing' { + { + $configurationParameters = @{ + OutputPath = $TestDrive + ConfigurationData = $ConfigurationData + } + + & $configurationName @configurationParameters + + $startDscConfigurationParameters = @{ + Path = $TestDrive + ComputerName = 'localhost' + Wait = $true + Verbose = $true + Force = $true + ErrorAction = 'Stop' + } + + Start-DscConfiguration @startDscConfigurationParameters + } | Should -Not -Throw + } + + It 'Should be able to call Get-DscConfiguration without throwing' { + { + $script:currentConfiguration = Get-DscConfiguration -Verbose -ErrorAction Stop + } | Should -Not -Throw + } + + It 'Should have set the resource and all the parameters should match' { + $resourceCurrentState = $script:currentConfiguration | Where-Object -FilterScript { + $_.ConfigurationName -eq $configurationName ` + -and $_.ResourceId -eq $resourceId + } + + $resourceCurrentState.Ensure | Should -Be 'Absent' + $resourceCurrentState.Name | Should -Be $ConfigurationData.AllNodes.StubZoneName + $resourceCurrentState.ZoneFile | Should -BeNullOrEmpty + $resourceCurrentState.DynamicUpdate | Should -BeNullOrEmpty + } + + It 'Should return ''True'' when Test-DscConfiguration is run' { + Test-DscConfiguration -Verbose | Should -Be 'True' + } + } + +} diff --git a/tests/Integration/DSC_DnsServerStubZone.config.ps1 b/tests/Integration/DSC_DnsServerStubZone.config.ps1 new file mode 100644 index 00000000..d4be587d --- /dev/null +++ b/tests/Integration/DSC_DnsServerStubZone.config.ps1 @@ -0,0 +1,75 @@ +$ConfigurationData = @{ + AllNodes = @( + @{ + NodeName = 'localhost' + CertificateFile = $env:DscPublicCertificatePath + + # Stub zone + StubZoneName = 'dsc.test' + StubZoneFile = 'dsc.test.file.dns' + StubMasterServers = '192.168.1.1','192.168.1.2' + + } + ) +} + +<# + .SYNOPSIS + Creates a file-backed stub zone using the default values for parameters. +#> +configuration DSC_DnsServerStubZone_AddStubZoneUsingDefaultValues_Config +{ + Import-DscResource -ModuleName 'DnsServerDsc' + + node $AllNodes.NodeName + { + DnsServerStubZone 'Integration_Test' + { + Name = $Node.StubZoneName + MasterServers = $Node.StubMasterServers + } + } +} + +<# + .SYNOPSIS + Removes a file-backed stub zone. + + .NOTES + This configuration is used multiple times to remove the file-backed stub zone. +#> +configuration DSC_DnsServerStubZone_RemoveStubZone_Config +{ + Import-DscResource -ModuleName 'DnsServerDsc' + + node $AllNodes.NodeName + { + DnsServerStubZone 'Integration_Test' + { + Ensure = 'Absent' + Name = $Node.StubZoneName + MasterServers = $Node.StubMasterServers + ZoneFile = $Node.StubZoneFile + } + } +} + +<# + .SYNOPSIS + Creates a file-backed stub zone by specifying values for each parameter. +#> +configuration DSC_DnsServerStubZone_AddForwardZone_Config +{ + Import-DscResource -ModuleName 'DnsServerDsc' + + node $AllNodes.NodeName + { + DnsServerStubZone 'Integration_Test' + { + Ensure = 'Present' + Name = $Node.StubZoneName + ZoneFile = $Node.StubZoneFile + MasterServers = $Node.StubMasterServers + } + } +} From 02feec1201076f062affdf6a774b99bfc16745c4 Mon Sep 17 00:00:00 2001 From: EJansenMSFT Date: Mon, 14 Apr 2025 21:34:40 -0400 Subject: [PATCH 15/16] Fixed Typo in the config.ps1 --- tests/Integration/DSC_DnsServerStubZone.config.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Integration/DSC_DnsServerStubZone.config.ps1 b/tests/Integration/DSC_DnsServerStubZone.config.ps1 index d4be587d..f99dfd2d 100644 --- a/tests/Integration/DSC_DnsServerStubZone.config.ps1 +++ b/tests/Integration/DSC_DnsServerStubZone.config.ps1 @@ -58,7 +58,7 @@ configuration DSC_DnsServerStubZone_RemoveStubZone_Config .SYNOPSIS Creates a file-backed stub zone by specifying values for each parameter. #> -configuration DSC_DnsServerStubZone_AddForwardZone_Config +configuration DSC_DnsServerStubZone_AddStubZone_Config { Import-DscResource -ModuleName 'DnsServerDsc' From 080b38ebcae048f4c5e903428a2fcb817dcfa0c8 Mon Sep 17 00:00:00 2001 From: EJansenMSFT Date: Mon, 14 Apr 2025 22:54:22 -0400 Subject: [PATCH 16/16] Attempting to copy / paste / modify an existing Unit test to see how she fairs. --- tests/Unit/DSC_DnsServerStubZone.Tests.ps1 | 450 +++++++++++++++++++++ 1 file changed, 450 insertions(+) create mode 100644 tests/Unit/DSC_DnsServerStubZone.Tests.ps1 diff --git a/tests/Unit/DSC_DnsServerStubZone.Tests.ps1 b/tests/Unit/DSC_DnsServerStubZone.Tests.ps1 new file mode 100644 index 00000000..5b56f36b --- /dev/null +++ b/tests/Unit/DSC_DnsServerStubZone.Tests.ps1 @@ -0,0 +1,450 @@ +<# + .SYNOPSIS + Unit test for DSC_DnsServerStubZone DSC resource. +#> + +# Suppressing this rule because Script Analyzer does not understand Pester's syntax. +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', '')] +param () + +BeforeDiscovery { + try + { + if (-not (Get-Module -Name 'DscResource.Test')) + { + # Assumes dependencies has been resolved, so if this module is not available, run 'noop' task. + if (-not (Get-Module -Name 'DscResource.Test' -ListAvailable)) + { + # Redirect all streams to $null, except the error stream (stream 2) + & "$PSScriptRoot/../../build.ps1" -Tasks 'noop' 3>&1 4>&1 5>&1 6>&1 > $null + } + + # If the dependencies has not been resolved, this will throw an error. + Import-Module -Name 'DscResource.Test' -Force -ErrorAction 'Stop' + } + } + catch [System.IO.FileNotFoundException] + { + throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -ResolveDependency -Tasks build" first.' + } +} + +BeforeAll { + $script:dscModuleName = 'DnsServerDsc' + $script:dscResourceName = 'DSC_DnsServerStubZone' + + $script:testEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $script:dscModuleName ` + -DSCResourceName $script:dscResourceName ` + -ResourceType 'Mof' ` + -TestType 'Unit' + + Import-Module (Join-Path -Path $PSScriptRoot -ChildPath 'Stubs\DnsServer.psm1') -Force + + $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:dscResourceName + $PSDefaultParameterValues['Mock:ModuleName'] = $script:dscResourceName + $PSDefaultParameterValues['Should:ModuleName'] = $script:dscResourceName +} + +AfterAll { + $PSDefaultParameterValues.Remove('InModuleScope:ModuleName') + $PSDefaultParameterValues.Remove('Mock:ModuleName') + $PSDefaultParameterValues.Remove('Should:ModuleName') + + Restore-TestEnvironment -TestEnvironment $script:testEnvironment + + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscResourceName -All | Remove-Module -Force + + Remove-Module -Name DnsServer -Force +} + +Describe 'DSC_DnsServerStubZone\Get-TargetResource' -Tag 'Get' { + BeforeAll { + Mock -CommandName Assert-Module + + $testZoneName = 'example.com' + $testZoneFile = 'example.com.dns' + $testZoneMasterServers = '192.168.1.1','192.168.1.2' + $fakeDnsFileZone = [PSCustomObject] @{ + DistinguishedName = $null + ZoneName = $testZoneName + ZoneType = 'Stub' + MasterServers = $testZoneMasterServers + ReplicationScope = 'None' + DirectoryPartitionName = $null + ZoneFile = $testZoneFile + } + } + + BeforeEach { + InModuleScope -Parameters @{ + testZoneName = $testZoneName + } -ScriptBlock { + Set-StrictMode -Version 1.0 + + $script:testParams = @{ + Name = $testZoneName + Verbose = $false + } + } + } + + Context 'When DNS zone exists' { + BeforeAll { + Mock -CommandName Get-DnsServerZone -MockWith { return $fakeDnsFileZone } + } + + It 'Should return a "System.Collections.Hashtable" object type' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + Get-TargetResource @testParams | Should -BeOfType [System.Collections.Hashtable] + } + } + + It 'Should return "Present" when "Ensure" = "Present"' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams += @{ + ZoneFile = 'example.com.dns' + Ensure = 'Present' + } + + $targetResource = Get-TargetResource @testParams + $targetResource.Ensure | Should -Be 'Present' + } + } + + It 'Should return "Present" when "Ensure" = "Absent"' { + Mock -CommandName Get-DnsServerZone -MockWith { return $fakeDnsFileZone } + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams += @{ + ZoneFile = 'example.com.dns' + Ensure = 'Absent' + } + + $targetResource = Get-TargetResource @testParams + $targetResource.Ensure | Should -Be 'Present' + } + } + } + + Context 'When DNS zone does not exist' { + BeforeAll { + Mock -CommandName Get-DnsServerZone + } + + It 'Should return "Absent" when "Ensure" = "Present"' { + + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams += @{ + ZoneFile = 'example.com.dns' + } + + $targetResource = Get-TargetResource @testParams + $targetResource.Ensure | Should -Be 'Absent' + } + } + + It 'Should return "Absent" when "Ensure" = "Absent"' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams += @{ + ZoneFile = 'example.com.dns' + Ensure = 'Absent' + } + + $targetResource = Get-TargetResource @testParams + $targetResource.Ensure | Should -Be 'Absent' + } + } + } +} + +Describe 'DSC_DnsServerStubZone\Test-TargetResource' -Tag 'Test' { + BeforeAll { + Mock -CommandName Assert-Module + + $testZoneName = 'example.com' + $testZoneFile = 'example.com.dns' + $testZoneMasterServers = '192.168.1.1','192.168.1.2' + $fakeDnsFileZone = [PSCustomObject] @{ + DistinguishedName = $null + ZoneName = $testZoneName + ZoneType = 'Stub' + MasterServers = $testZoneMasterServers + ReplicationScope = 'None' + DirectoryPartitionName = $null + ZoneFile = $testZoneFile + } + } + + BeforeEach { + InModuleScope -Parameters @{ + testZoneName = $testZoneName + } -ScriptBlock { + Set-StrictMode -Version 1.0 + + $script:testParams = @{ + Name = $testZoneName + Verbose = $false + } + } + } + + Context 'When the DNS zone exists' { + BeforeAll { + Mock -CommandName Get-DnsServerZone -MockWith { return $fakeDnsFileZone } + } + + Context 'When the zone is in the desired state' { + It 'Should return a "System.Boolean" object type' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + Test-TargetResource @testParams | Should -BeOfType [System.Boolean] + } + } + + It 'Should be $true when "Ensure" = "Present"' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams += @{ + Ensure = 'Present' + } + + Test-TargetResource @testParams | Should -BeTrue + } + } + + It 'Should be $true "MasterServers" is correct' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams += @{ + Ensure = 'Present' + MasterServers = $testZoneMasterServers + } + + Test-TargetResource @testParams | Should -BeTrue + } + } + } + + Context 'When the zone is not in the desired state' { + It 'Should be $false "Ensure" = "Absent"' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams += @{ + Ensure = 'Absent' + } + + Test-TargetResource @testParams | Should -BeFalse + } + } + + It 'Should be $false when "MasterServers" is incorrect' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams += @{ + ZoneFile = 'example.com.dns' + Ensure = 'Present' + MasterServers = '8.8.8.8' + } + + Test-TargetResource @testParams | Should -BeFalse + } + } + + It 'Should be $false when "ZoneFile" is incorrect' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams += @{ + ZoneFile = 'nonexistent.com.dns' + Ensure = 'Present' + MasterServers = $testZoneMasterServers + } + + Test-TargetResource @testParams | Should -BeFalse + } + } + } + } + + Context 'When the DNS zone does not exist' { + BeforeAll { + Mock -CommandName Get-DnsServerZone + } + + Context 'When the zone is in the desired state' { + It 'Should be $true' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams += @{ + Ensure = 'Absent' + } + + Test-TargetResource @testParams | Should -BeTrue + } + } + } + + Context 'When the zone is not in the desired state' { + It 'Should be $false' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams += @{ + Ensure = 'Present' + } + + Test-TargetResource @testParams | Should -BeFalse + } + } + } + } +} + +Describe 'DSC_DnsServerStubZone\Set-TargetResource' -Tag 'Set' { + BeforeAll { + Mock -CommandName 'Assert-Module' + + $testZoneName = 'example.com' + $testZoneFile = 'example.com.dns' + $testZoneMasterServers = '192.168.1.1','192.168.1.2' + $fakeDnsFileZone = [PSCustomObject] @{ + DistinguishedName = $null + ZoneName = $testZoneName + ZoneType = 'Stub' + MasterServers = $testZoneMasterServers + ReplicationScope = 'None' + DirectoryPartitionName = $null + ZoneFile = $testZoneFile + } + } + + BeforeEach { + InModuleScope -Parameters @{ + testZoneName = $testZoneName + } -ScriptBlock { + Set-StrictMode -Version 1.0 + + $script:testParams = @{ + Name = $testZoneName + Verbose = $false + } + } + } + + Context 'When the DNS zone does not exist' { + BeforeAll { + Mock -CommandName Get-DnsServerZone + Mock -CommandName Add-DnsServerStubZone -ParameterFilter { $Name -eq $testZoneName } + } + + It 'Should call expected mocks' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams += @{ + Ensure = 'Present' + MasterServers = $testZoneMasterServers + ZoneFile = 'example.com.dns' + } + + Set-TargetResource @testParams + } + + Should -Invoke -CommandName Add-DnsServerStubZone -ParameterFilter { $Name -eq $testZoneName } -Scope It -Times 1 -Exactly + should -Invoke -CommandName Get-DnsServerZone -Scope It -Times 1 -Exactly + } + } + + Context 'When the DNS zone does exist' { + BeforeAll { + Mock -CommandName Get-DnsServerZone -MockWith { return $fakeDnsFileZone } + Mock -CommandName Remove-DnsServerZone + } + + Context 'When the zone needs creating' { + It 'Should call expected mocks' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams += @{ + Ensure = 'Absent' + MasterServers = $testZoneMasterServers + ZoneFile = 'example.com.dns' + } + + Set-TargetResource @testParams + } + + Should -Invoke -CommandName Remove-DnsServerZone -Scope It -Times 1 -Exactly + Should -Invoke -CommandName Get-DnsServerZone -Scope It -Times 1 -Exactly + } + } + + Context 'When the zone needs updating' { + Context 'when DNS zone "MasterServers" are incorrect' { + BeforeAll { + Mock -CommandName Get-DnsServerZone -MockWith { return $fakeDnsFileZone } + Mock -CommandName Set-DnsServerStubZone -ParameterFilter { $MasterServers -eq '8.8.8.8' } + } + + It 'Should call expected mocks' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams += @{ + Ensure = 'Present' + MasterServers = $testZoneMasterServers + ZoneFile = 'example.com.dns' + } + + Set-TargetResource @testParams + } + + Should -Invoke -CommandName Set-DnsServerStubZone -ParameterFilter { $MasterServers -eq '8.8.8.8' } -Scope It -Times 1 -Exactly + Should -Invoke -CommandName Get-DnsServerZone -Scope It -Times 1 -Exactly + } + } + + Context 'When DNS zone "ZoneFile" is incorrect' { + BeforeAll { + Mock -CommandName Get-DnsServerZone -MockWith { return $fakeDnsFileZone } + Mock -CommandName Set-DnsServerStubZone -ParameterFilter { $ZoneFile -eq 'nonexistent.com.dns' } + } + + It 'Should call expected mocks' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $testParams += @{ + Ensure = 'Present' + MasterServers = $testZoneMasterServers + ZoneFile = 'nonexistent.com.dns' + } + + Set-TargetResource @testParams + } + + Should -Invoke -CommandName Set-DnsServerStubZone -ParameterFilter { $ZoneFile -eq 'nonexistent.com.dns' } -Scope It -Times 1 -Exactly + Should -Invoke -CommandName Get-DnsServerZone -Scope It -Times 1 -Exactly + } + } + } + } +}