From a6e51ad377c7ecd9e07fad5fba2abcea7769b2f1 Mon Sep 17 00:00:00 2001 From: Dan Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Mon, 3 Feb 2025 17:35:01 +0000 Subject: [PATCH 01/12] Add initial class infra and start on DnsDynamicUpdates --- RequiredModules.psd1 | 1 + build.yaml | 21 +- source/Classes/001.DhcpServerReason.ps1 | 21 ++ .../020.DhcpServerDnsDynamicUpdates.ps1 | 186 ++++++++++++++++++ source/DhcpServerDsc.psm1 | 4 - source/Enum/AddressFamilyType.ps1 | 5 + source/Enum/DhcpTargetScopeType.ps1 | 7 + source/Enum/DynamicUpdatesType.ps1 | 6 + source/prefix.ps1 | 7 + 9 files changed, 248 insertions(+), 10 deletions(-) create mode 100644 source/Classes/001.DhcpServerReason.ps1 create mode 100644 source/Classes/020.DhcpServerDnsDynamicUpdates.ps1 delete mode 100644 source/DhcpServerDsc.psm1 create mode 100644 source/Enum/AddressFamilyType.ps1 create mode 100644 source/Enum/DhcpTargetScopeType.ps1 create mode 100644 source/Enum/DynamicUpdatesType.ps1 create mode 100644 source/prefix.ps1 diff --git a/RequiredModules.psd1 b/RequiredModules.psd1 index e16f52a..d46b5c9 100644 --- a/RequiredModules.psd1 +++ b/RequiredModules.psd1 @@ -27,6 +27,7 @@ # Build dependent modules 'DscResource.Common' = 'latest' + 'DscResource.Base' = 'latest' # Analyzer rules 'DscResource.AnalyzerRules' = 'latest' diff --git a/build.yaml b/build.yaml index 4140066..020a6b1 100644 --- a/build.yaml +++ b/build.yaml @@ -7,6 +7,7 @@ CopyPaths: - en-US - DSCResources - Modules +Prefix: prefix.ps1 Encoding: UTF8 VersionedOutputDirectory: true BuiltModuleSubdirectory: builtModule @@ -16,11 +17,16 @@ BuiltModuleSubdirectory: builtModule #################################################### NestedModule: - DscResource.Common: - CopyOnly: true - Path: ./output/RequiredModules/DscResource.Common - AddToManifest: false - Exclude: PSGetModuleInfo.xml + DscResource.Common: + CopyOnly: true + Path: ./output/RequiredModules/DscResource.Common + AddToManifest: false + Exclude: PSGetModuleInfo.xml + DscResource.Base: + CopyOnly: true + Path: ./output/RequiredModules/DscResource.Base + AddToManifest: false + Exclude: PSGetModuleInfo.xml #################################################### # Pipeline Configuration # @@ -72,6 +78,7 @@ Pester: OutputFormat: NUnitXML ExcludeFromCodeCoverage: - Modules/DscResource.Common + - Modules/DscResource.Base Script: - tests/Unit Tag: @@ -85,12 +92,14 @@ CodeCoverage: DscTest: ExcludeTag: - - "Common Tests - New Error-Level Script Analyzer Rules" + - 'Common Tests - New Error-Level Script Analyzer Rules' Tag: ExcludeSourceFile: - output ExcludeModuleFile: - Modules/DscResource.Common + - Modules/DscResource.Base + - DhcpServerDsc.psm1 MainGitBranch: main ModuleBuildTasks: diff --git a/source/Classes/001.DhcpServerReason.ps1 b/source/Classes/001.DhcpServerReason.ps1 new file mode 100644 index 0000000..88f17b7 --- /dev/null +++ b/source/Classes/001.DhcpServerReason.ps1 @@ -0,0 +1,21 @@ +<# + .SYNOPSIS + The reason a property of a DSC resource is not in desired state. + + .DESCRIPTION + A DSC resource can have a read-only property `Reasons` that the compliance + part (audit via Azure Policy) of Azure AutoManage Machine Configuration + uses. The property Reasons holds an array of DhcpServerReason. Each DhcpServerReason + explains why a property of a DSC resource is not in desired state. +#> + +class DhcpServerReason +{ + [DscProperty()] + [System.String] + $Code + + [DscProperty()] + [System.String] + $Phrase +} diff --git a/source/Classes/020.DhcpServerDnsDynamicUpdates.ps1 b/source/Classes/020.DhcpServerDnsDynamicUpdates.ps1 new file mode 100644 index 0000000..2e2838f --- /dev/null +++ b/source/Classes/020.DhcpServerDnsDynamicUpdates.ps1 @@ -0,0 +1,186 @@ +[DscResource()] +class DhcpServerDnsDynamicUpdates : ResourceBase +{ + [DscProperty(Key)] + [DhcpTargetScopeType] + $TargetScope + + [DscProperty(Key)] + [AddressFamilyType] + $AddressFamily + + [DscProperty(Mandatory)] + [Ensure] + $Ensure + + # Common Properties + #NameProtection Bool + #DeleteDnsRROnLeaseExpiry Bool + #DynamicUpdates String + #IPAddress String/IPAddress + + [DscProperty()] + [System.Nullable[System.Boolean]] + $NameProtection + + [DscProperty()] + [System.Nullable[System.Boolean]] + $DeleteDnsRROnLeaseExpiry + + [DscProperty()] + [DynamicUpdatesType] + $DynamicUpdates + + [DscProperty()] + [System.Nullable[System.Net.IPAddress]] + $IPAddress + + # IPv4 Properties + #UpdateDnsRRForOlderClients Bool + #ScopeId String/IPAddress + #PolicyName String + #DisableDnsPtrRRUpdate Bool + #DnsSuffix String + + [DscProperty()] + [System.Nullable[System.Boolean]] + $UpdateDnsRRForOlderClients + + [DscProperty()] + [System.Nullable[System.Net.IPAddress]] + $ScopeId + + [DscProperty()] + [System.String] + $PolicyName + + [DscProperty()] + [System.Nullable[System.Boolean]] + $DisableDnsPtrRRUpdate + + [DscProperty()] + [System.String] + $DnsSuffix + + # IPv6 properties + #Prefix String/IPAddress + + [DscProperty()] + [System.Nullable[System.Net.IPAddress]] + $Prefix + + [DscProperty(NotConfigurable)] + [DhcpServerReason[]] + $Reasons + + DhcpServerDnsDynamicUpdates () : base ($PSScriptRoot) + { + $this.ExcludeDscProperties = @() + + $this.FeatureOptionalEnums = $true + } + + # Required DSC Methods, these call the method in the base class. + [DhcpServerDnsDynamicUpdates] Get() + { + # Call the base method to return the properties. + return ([ResourceBase] $this).Get() + } + + [void] Set() + { + # Call the base method to enforce the properties. + ([ResourceBase] $this).Set() + } + + [System.Boolean] Test() + { + # Call the base method to test all of the properties that should be enforced. + return ([ResourceBase] $this).Test() + } + + # Base method Get() call this method to get the current state as a Hashtable. + [System.Collections.Hashtable] GetCurrentState([System.Collections.Hashtable] $properties) + { + return @{} + } + + <# + Base method Set() call this method with the properties that should be + enforced and that are not in desired state. + #> + hidden [void] Modify([System.Collections.Hashtable] $properties) + { + } + + <# + Base method Assert() call this method with the properties that was assigned + a value. + #> + hidden [void] AssertProperties([System.Collections.Hashtable] $properties) + { + Assert-Module -ModuleName 'DHCPServer' + + if ($properties.AddressFamily -eq [AddressFamilyType]::IPv4) { + + + return + } + + if ($properties.AddressFamily -eq [AddressFamilyType]::IPv6) { + + + return + } + + # Check IPv4 Properties + <# + Must supply one of + DeleteDnsRROnLeaseExpiry + DynamicUpdates + NameProtection + UpdateDnsRRForOlderClients + DnsSuffix + DisableDnsPtrRRUpdate + + Scopes + Server + !ScopeId + !IPAddress + !PolicyName + Scope + ScopeId + Server Policy + PolicyName + Scope Policy + ScopeId + PolicyName + Reservation + IPAddress + !ScopeId + !PolicyName + #> + + ## + + + # Check IPv6 Properties + <# + Must supply one of + DeleteDnsRROnLeaseExpiry + DynamicUpdates + NameProtection + + Scopes + Server + !Prefix + !IPAddress + Scope + Prefix + Reservation + IPAddress + + #> + + } +} diff --git a/source/DhcpServerDsc.psm1 b/source/DhcpServerDsc.psm1 deleted file mode 100644 index 81d556e..0000000 --- a/source/DhcpServerDsc.psm1 +++ /dev/null @@ -1,4 +0,0 @@ -<# - This file is intentionally left empty. It is must be left here for the module - manifest to refer to. It is recreated during the build process. -#> diff --git a/source/Enum/AddressFamilyType.ps1 b/source/Enum/AddressFamilyType.ps1 new file mode 100644 index 0000000..a2d60ab --- /dev/null +++ b/source/Enum/AddressFamilyType.ps1 @@ -0,0 +1,5 @@ +enum AddressFamilyType +{ + IPv4 = 1 + IPv6 +} diff --git a/source/Enum/DhcpTargetScopeType.ps1 b/source/Enum/DhcpTargetScopeType.ps1 new file mode 100644 index 0000000..38ced8b --- /dev/null +++ b/source/Enum/DhcpTargetScopeType.ps1 @@ -0,0 +1,7 @@ +enum DhcpTargetScopeType +{ + Server = 1 + Scope + Policy + Reservation +} diff --git a/source/Enum/DynamicUpdatesType.ps1 b/source/Enum/DynamicUpdatesType.ps1 new file mode 100644 index 0000000..e6438ba --- /dev/null +++ b/source/Enum/DynamicUpdatesType.ps1 @@ -0,0 +1,6 @@ +enum DynamicUpdatesType +{ + Always = 1 + Never + OnClientRequest +} diff --git a/source/prefix.ps1 b/source/prefix.ps1 new file mode 100644 index 0000000..168dd74 --- /dev/null +++ b/source/prefix.ps1 @@ -0,0 +1,7 @@ +using module .\Modules\DscResource.Base + +# Import nested, 'DscResource.Common' module +$script:dscResourceCommonModulePath = Join-Path -Path $PSScriptRoot -ChildPath 'Modules\DscResource.Common' +Import-Module -Name $script:dscResourceCommonModulePath + +$script:localizedData = Get-LocalizedData -DefaultUICulture 'en-US' From 8386e49933bd260b0d722ea08331cb7ecd8c6d68 Mon Sep 17 00:00:00 2001 From: Dan Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Sun, 9 Feb 2025 13:53:15 +0000 Subject: [PATCH 02/12] Add dhcp specific spellings --- .vscode/settings.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index f8efa40..6d73575 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -36,7 +36,9 @@ "keepachangelog", "notin", "pscmdlet", - "steppable" + "steppable", + "Serverv", + "Dhcpv" ], "cSpell.ignorePaths": [ ".git" From 47d34858ca0f2472631c72e1c290d1a1ee43c8c3 Mon Sep 17 00:00:00 2001 From: Dan Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Sun, 9 Feb 2025 13:54:07 +0000 Subject: [PATCH 03/12] Split ipv4 and ipv6 resources --- .../020.DhcpServerDnsDynamicUpdates.ps1 | 186 ---------- .../020.DhcpServerv4DnsDynamicUpdates.ps1 | 330 ++++++++++++++++++ source/Enum/DhcpTargetScopeType.ps1 | 7 - source/Enum/Dhcpv4TargetScopeType.ps1 | 8 + ...DhcpServerv4DnsDynamicUpdates.strings.psd1 | 19 + 5 files changed, 357 insertions(+), 193 deletions(-) delete mode 100644 source/Classes/020.DhcpServerDnsDynamicUpdates.ps1 create mode 100644 source/Classes/020.DhcpServerv4DnsDynamicUpdates.ps1 delete mode 100644 source/Enum/DhcpTargetScopeType.ps1 create mode 100644 source/Enum/Dhcpv4TargetScopeType.ps1 create mode 100644 source/en-US/DhcpServerv4DnsDynamicUpdates.strings.psd1 diff --git a/source/Classes/020.DhcpServerDnsDynamicUpdates.ps1 b/source/Classes/020.DhcpServerDnsDynamicUpdates.ps1 deleted file mode 100644 index 2e2838f..0000000 --- a/source/Classes/020.DhcpServerDnsDynamicUpdates.ps1 +++ /dev/null @@ -1,186 +0,0 @@ -[DscResource()] -class DhcpServerDnsDynamicUpdates : ResourceBase -{ - [DscProperty(Key)] - [DhcpTargetScopeType] - $TargetScope - - [DscProperty(Key)] - [AddressFamilyType] - $AddressFamily - - [DscProperty(Mandatory)] - [Ensure] - $Ensure - - # Common Properties - #NameProtection Bool - #DeleteDnsRROnLeaseExpiry Bool - #DynamicUpdates String - #IPAddress String/IPAddress - - [DscProperty()] - [System.Nullable[System.Boolean]] - $NameProtection - - [DscProperty()] - [System.Nullable[System.Boolean]] - $DeleteDnsRROnLeaseExpiry - - [DscProperty()] - [DynamicUpdatesType] - $DynamicUpdates - - [DscProperty()] - [System.Nullable[System.Net.IPAddress]] - $IPAddress - - # IPv4 Properties - #UpdateDnsRRForOlderClients Bool - #ScopeId String/IPAddress - #PolicyName String - #DisableDnsPtrRRUpdate Bool - #DnsSuffix String - - [DscProperty()] - [System.Nullable[System.Boolean]] - $UpdateDnsRRForOlderClients - - [DscProperty()] - [System.Nullable[System.Net.IPAddress]] - $ScopeId - - [DscProperty()] - [System.String] - $PolicyName - - [DscProperty()] - [System.Nullable[System.Boolean]] - $DisableDnsPtrRRUpdate - - [DscProperty()] - [System.String] - $DnsSuffix - - # IPv6 properties - #Prefix String/IPAddress - - [DscProperty()] - [System.Nullable[System.Net.IPAddress]] - $Prefix - - [DscProperty(NotConfigurable)] - [DhcpServerReason[]] - $Reasons - - DhcpServerDnsDynamicUpdates () : base ($PSScriptRoot) - { - $this.ExcludeDscProperties = @() - - $this.FeatureOptionalEnums = $true - } - - # Required DSC Methods, these call the method in the base class. - [DhcpServerDnsDynamicUpdates] Get() - { - # Call the base method to return the properties. - return ([ResourceBase] $this).Get() - } - - [void] Set() - { - # Call the base method to enforce the properties. - ([ResourceBase] $this).Set() - } - - [System.Boolean] Test() - { - # Call the base method to test all of the properties that should be enforced. - return ([ResourceBase] $this).Test() - } - - # Base method Get() call this method to get the current state as a Hashtable. - [System.Collections.Hashtable] GetCurrentState([System.Collections.Hashtable] $properties) - { - return @{} - } - - <# - Base method Set() call this method with the properties that should be - enforced and that are not in desired state. - #> - hidden [void] Modify([System.Collections.Hashtable] $properties) - { - } - - <# - Base method Assert() call this method with the properties that was assigned - a value. - #> - hidden [void] AssertProperties([System.Collections.Hashtable] $properties) - { - Assert-Module -ModuleName 'DHCPServer' - - if ($properties.AddressFamily -eq [AddressFamilyType]::IPv4) { - - - return - } - - if ($properties.AddressFamily -eq [AddressFamilyType]::IPv6) { - - - return - } - - # Check IPv4 Properties - <# - Must supply one of - DeleteDnsRROnLeaseExpiry - DynamicUpdates - NameProtection - UpdateDnsRRForOlderClients - DnsSuffix - DisableDnsPtrRRUpdate - - Scopes - Server - !ScopeId - !IPAddress - !PolicyName - Scope - ScopeId - Server Policy - PolicyName - Scope Policy - ScopeId - PolicyName - Reservation - IPAddress - !ScopeId - !PolicyName - #> - - ## - - - # Check IPv6 Properties - <# - Must supply one of - DeleteDnsRROnLeaseExpiry - DynamicUpdates - NameProtection - - Scopes - Server - !Prefix - !IPAddress - Scope - Prefix - Reservation - IPAddress - - #> - - } -} diff --git a/source/Classes/020.DhcpServerv4DnsDynamicUpdates.ps1 b/source/Classes/020.DhcpServerv4DnsDynamicUpdates.ps1 new file mode 100644 index 0000000..16a678b --- /dev/null +++ b/source/Classes/020.DhcpServerv4DnsDynamicUpdates.ps1 @@ -0,0 +1,330 @@ +[DscResource()] +class DhcpServerv4DnsDynamicUpdates : ResourceBase +{ + [DscProperty(Key)] + [Dhcpv4TargetScopeType] + $TargetScope + + [DscProperty(Mandatory)] + [Ensure] + $Ensure = [Ensure]::Present + + [DscProperty()] + [System.Nullable[System.Boolean]] + $NameProtection + + [DscProperty()] + [System.Nullable[System.Boolean]] + $DeleteDnsRROnLeaseExpiry + + [DscProperty()] + [DynamicUpdatesType] + $DynamicUpdates + + [DscProperty()] + [System.String] + $IPAddress + + [DscProperty()] + [System.Nullable[System.Boolean]] + $UpdateDnsRRForOlderClients + + [DscProperty()] + [System.String] + $ScopeId + + [DscProperty()] + [System.String] + $PolicyName + + [DscProperty()] + [System.Nullable[System.Boolean]] + $DisableDnsPtrRRUpdate + + [DscProperty()] + [System.String] + $DnsSuffix + + [DscProperty(NotConfigurable)] + [DhcpServerReason[]] + $Reasons + + DhcpServerv4DnsDynamicUpdates () : base ($PSScriptRoot) + { + $this.FeatureOptionalEnums = $true + } + + # Required DSC Methods, these call the method in the base class. + [DhcpServerv4DnsDynamicUpdates] Get() + { + # Call the base method to return the properties. + return ([ResourceBase] $this).Get() + } + + [void] Set() + { + # Call the base method to enforce the properties. + ([ResourceBase] $this).Set() + } + + [System.Boolean] Test() + { + # Call the base method to test all of the properties that should be enforced. + return ([ResourceBase] $this).Test() + } + + # Base method Get() call this method to get the current state as a Hashtable. + [System.Collections.Hashtable] GetCurrentState([System.Collections.Hashtable] $properties) + { + $state = @{} + $getParameters = @{} + + switch ($properties.TargetScope) + { + ServerPolicy + { + # Need to check the Policy exists + $policy = Get-DhcpServerv4Policy -Name $this.PolicyName -ErrorAction SilentlyContinue + + if ($null -eq $policy) + { + New-InvalidOperationException -Message ($this.localizedData.ServerPolicyDoesNotExist -f $this.PolicyName) + } + + $getParameters.PolicyName = $this.PolicyName + break + } + + Scope + { + # Need to check the Scope exists + $scope = Get-DhcpServerv4Scope -ScopeId $this.ScopeId -ErrorAction SilentlyContinue + + if ($null -eq $scope) + { + New-InvalidOperationException -Message ($this.localizedData.ScopeDoesNotExist -f $this.ScopeId) + } + + $getParameters.ScopeId = $this.ScopeId + break + } + + ScopePolicy + { + # Need to check the ScopePolicy exists + $scope = Get-DhcpServerv4Scope -ScopeId $this.ScopeId -ErrorAction SilentlyContinue + + if ($null -eq $scope) + { + New-InvalidOperationException -Message ($this.localizedData.ScopeDoesNotExist -f $this.ScopeId) + } + + $policy = Get-DhcpServerv4Policy -Name $this.PolicyName -ScopeId $scope.ScopeId -ErrorAction SilentlyContinue + + if ($null -eq $policy) + { + New-InvalidOperationException -Message ($this.localizedData.ScopePolicyDoesNotExist -f $this.ScopeId, $this.PolicyName) + } + + $getParameters.ScopeId = $this.ScopeId + $getParameters.PolicyName = $this.PolicyName + break + } + + Reservation + { + # Need to check the reservation exists + $reservation = Get-DhcpServerv4Reservation -IPAddress $this.IPAddress -ErrorAction SilentlyContinue + + if ($null -eq $reservation) + { + New-InvalidOperationException -Message ($this.localizedData.ReservationDoesNotExist -f $this.IPAddress) + } + + $getParameters.IPAddress = $this.IPAddress + break + } + } + + $getCurrentStateResult = Get-DhcpServerv4DnsSetting @getParameters + + if ($getCurrentStateResult) + { + $state = @{ + TargetScope = [Dhcpv4TargetScopeType] $properties.TargetScope + DeleteDnsRROnLeaseExpiry = $getCurrentStateResult.DeleteDnsRROnLeaseExpiry + DisableDnsPtrRRUpdate = $getCurrentStateResult.DisableDnsPtrRRUpdate + DnsSuffix = $getCurrentStateResult.DnsSuffix + DynamicUpdates = [DynamicUpdatesType] $getCurrentStateResult.DynamicUpdates + NameProtection = $getCurrentStateResult.NameProtection + UpdateDnsRRForOlderClients = $getCurrentStateResult.UpdateDnsRRForOlderClients + } + + switch ($properties.TargetScope) + { + ServerPolicy + { + $state.PolicyName = $this.PolicyName + break + } + + Scope + { + $state.ScopeId = $this.ScopeId + break + } + + Scope + { + $state.ScopeId = $this.ScopeId + $state.PolicyName = $this.PolicyName + break + } + + Reservation + { + $state.IPAddress = $this.IPAddress + break + } + } + } + + return $state + } + + <# + Base method Set() call this method with the properties that should be + enforced and that are not in desired state. + #> + hidden [void] Modify([System.Collections.Hashtable] $properties) + { + } + + <# + Base method Assert() call this method with the properties that was assigned + a value. + #> + hidden [void] AssertProperties([System.Collections.Hashtable] $properties) + { + Assert-Module -ModuleName 'DHCPServer' + + # Test at least one of the following exists + $assertBoundParameterParameters = @{ + BoundParameterList = $properties + RequiredParameter = @( + 'DeleteDnsRROnLeaseExpiry' + 'DynamicUpdates' + 'NameProtection' + 'UpdateDnsRRForOlderClients' + 'DnsSuffix' + 'DisableDnsPtrRRUpdate' + ) + RequiredBehavior = 'Any' + } + + Assert-BoundParameter @assertBoundParameterParameters + + # Scopes + switch ($properties.TargetScope) + { + Server + { + $disallowedParameters = @( + 'ScopeId' + 'IPAddress' + 'PolicyName' + ) + + $assertBoundParameterParameters = @{ + BoundParameterList = $properties + MutuallyExclusiveList1 = $disallowedParameters + MutuallyExclusiveList2 = $disallowedParameters + } + + Assert-BoundParameter @assertBoundParameterParameters + break + } + + ServerPolicy + { + $assertBoundParameterParameters = @{ + BoundParameterList = $properties + RequiredParameter = @( + 'PolicyName' + ) + } + + Assert-BoundParameter @assertBoundParameterParameters + break + } + + Scope + { + $assertBoundParameterParameters = @{ + BoundParameterList = $properties + RequiredParameter = @( + 'ScopeId' + ) + } + + Assert-BoundParameter @assertBoundParameterParameters + break + } + + ScopePolicy + { + $assertBoundParameterParameters = @{ + BoundParameterList = $properties + RequiredParameter = @( + 'ScopeId' + 'PolicyName' + ) + } + + Assert-BoundParameter @assertBoundParameterParameters + break + } + + Reservation + { + $assertBoundParameterParameters = @{ + BoundParameterList = $properties + RequiredParameter = @( + 'IPAddress' + ) + } + + Assert-BoundParameter @assertBoundParameterParameters + break + } + + default + { + # This should fail as TargetScope is not a valid value + throw + } + } + + $ipStringsToValidate = @( + @{ + ParameterName = 'IPAddress' + } + @{ + ParameterName = 'ScopeId' + } + ) + + foreach ($ipString in $ipStringsToValidate) + { + if ($properties.ContainsKey($ipString.ParameterName)) + { + $getValidIpAddressParams = @{ + Address = $properties.($ipString.ParameterName) + AddressFamily = 'IPv4' + } + + Assert-IPAddress @getValidIpAddressParams + } + } + } +} diff --git a/source/Enum/DhcpTargetScopeType.ps1 b/source/Enum/DhcpTargetScopeType.ps1 deleted file mode 100644 index 38ced8b..0000000 --- a/source/Enum/DhcpTargetScopeType.ps1 +++ /dev/null @@ -1,7 +0,0 @@ -enum DhcpTargetScopeType -{ - Server = 1 - Scope - Policy - Reservation -} diff --git a/source/Enum/Dhcpv4TargetScopeType.ps1 b/source/Enum/Dhcpv4TargetScopeType.ps1 new file mode 100644 index 0000000..a4f4d39 --- /dev/null +++ b/source/Enum/Dhcpv4TargetScopeType.ps1 @@ -0,0 +1,8 @@ +enum Dhcpv4TargetScopeType +{ + Server = 1 + ServerPolicy + Scope + ScopePolicy + Reservation +} diff --git a/source/en-US/DhcpServerv4DnsDynamicUpdates.strings.psd1 b/source/en-US/DhcpServerv4DnsDynamicUpdates.strings.psd1 new file mode 100644 index 0000000..eee1610 --- /dev/null +++ b/source/en-US/DhcpServerv4DnsDynamicUpdates.strings.psd1 @@ -0,0 +1,19 @@ +<# + .SYNOPSIS + The localized resource strings in English (en-US) for the + resource DhcpServerDnsDynamicUpdates module. This file should only contain + localized strings for private functions, public command, and + classes (that are not a DSC resource). +#> + +ConvertFrom-StringData @' + ## Strings overrides for the ResourceBase's default strings. + # None + + ## Strings directly used by the derived class DhcpServerv4DnsDynamicUpdates. + SpecificParametersOneMustBeSet = At least one of the parameters '{0}' must be specified. (DS4DDU0001) + ServerPolicyDoesNotExist = The Server Policy '{0}' does not exist. (DS4DDU0001) + ScopeDoesNotExist = The Scope '{0}' does not exist. (DS4DDU0002) + ScopePolicyDoesNotExist = The Scope Policy '{0}', '{1}' does not exist. (DS4DDU0003) + ReservationDoesNotExist = The Reservation '{0}' does not exist. (DS4DDU0004) +'@ From 8b3bac708068f203799c2d86e6863ddb0f3db7d5 Mon Sep 17 00:00:00 2001 From: Dan Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Sun, 9 Feb 2025 13:58:01 +0000 Subject: [PATCH 04/12] Update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e5c800..9e25cb1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +Resource `DhcpServerv4DnsDynamicUpdates`. Fixes [#65](https://github.com/dsccommunity/DhcpServerDsc/issues/65) + ## [4.0.0] - 2025-01-20 ### Changed From e322e9f9296962d0e5edd9b8ccd16515f90b7c05 Mon Sep 17 00:00:00 2001 From: Dan Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Tue, 11 Feb 2025 17:27:55 +0000 Subject: [PATCH 05/12] Add latest code --- .../020.DhcpServerv4DnsDynamicUpdates.ps1 | 135 +++++++++++++++--- 1 file changed, 115 insertions(+), 20 deletions(-) diff --git a/source/Classes/020.DhcpServerv4DnsDynamicUpdates.ps1 b/source/Classes/020.DhcpServerv4DnsDynamicUpdates.ps1 index 16a678b..7c6db40 100644 --- a/source/Classes/020.DhcpServerv4DnsDynamicUpdates.ps1 +++ b/source/Classes/020.DhcpServerv4DnsDynamicUpdates.ps1 @@ -1,3 +1,48 @@ +<# + .SYNOPSIS + The `DhcpServerv4DnsDynamicUpdates` DSC resource is used to create, modify or remove + IPv4 DnsDynamicUpdates on a Dhcp Server, ServerPolicy, Scope, ScopePolicy or Reservation. + + .DESCRIPTION + This resource is used to create, edit or remove DnsDynamicUpdate settings on DhcpServers. + + .PARAMETER TargetScope + The target scope type of the operation. + + .PARAMETER Ensure + Specifies whether the configuration should exist. + + .PARAMETER NameProtection + Specifies whether Name Protection is enabled. + + .PARAMETER DeleteDnsRROnLeaseExpiry + Whether the setting to discard A and PTR records when a lease is deleted. + + .PARAMETER DynamicUpdates + The dynamic update mode, this can be Always, Never or OnClientRequest. + + .PARAMETER DisableDnsPtrRRUpdate + + + .PARAMETER UpdateDnsRRForOlderClients + + + .PARAMETER IPAddress + The IpAddress to target. This is only applicable to Reservation TargetScope. + + .PARAMETER ScopeId + The scope Id to target. This is only applicable to Scope and ScopePolicy TargetScope. + + .PARAMETER PolicyName + The name of the policy to target. This is only applicable to ServerPolicy and ScopePolicy TargetScope. + + .PARAMETER DnsSuffix + The DNS Suffix to register DHCP clients to. This is only applicable to ServerPolicy and ScopePolicy TargetScope. + + .PARAMETER Reasons + Returns the reason a property is not in desired state. +#> + [DscResource()] class DhcpServerv4DnsDynamicUpdates : ResourceBase { @@ -79,6 +124,14 @@ class DhcpServerv4DnsDynamicUpdates : ResourceBase $state = @{} $getParameters = @{} + #TODO: Make this stop outputting 'loading module' + $SavedVerbosePreference = $global:VerbosePreference + $SavedOutputPreference = $global:OutputPreference + $global:VerbosePreference = $global:OutputPreference = 'SilentlyContinue' + $null = Import-Module 'DhcpServer' + $global:VerbosePreference = $SavedVerbosePreference + $global:OutputPreference = $SavedOutputPreference + switch ($properties.TargetScope) { ServerPolicy @@ -174,7 +227,7 @@ class DhcpServerv4DnsDynamicUpdates : ResourceBase break } - Scope + ScopePolicy { $state.ScopeId = $this.ScopeId $state.PolicyName = $this.PolicyName @@ -198,6 +251,37 @@ class DhcpServerv4DnsDynamicUpdates : ResourceBase #> hidden [void] Modify([System.Collections.Hashtable] $properties) { + $setParams = @{} + + switch ($this.TargetScope) + { + ServerPolicy + { + $setParams.PolicyName = $this.PolicyName + break + } + + Scope + { + $setParams.ScopeId = $this.ScopeId + break + } + + ScopePolicy + { + $setParams.ScopeId = $this.ScopeId + $setParams.PolicyName = $this.PolicyName + break + } + + Reservation + { + $setParams.IPAddress = $this.IPAddress + break + } + } + + Set-DhcpServerv4DnsSetting @setParams @properties } <# @@ -233,11 +317,12 @@ class DhcpServerv4DnsDynamicUpdates : ResourceBase 'ScopeId' 'IPAddress' 'PolicyName' + 'DnsSuffix' ) $assertBoundParameterParameters = @{ BoundParameterList = $properties - MutuallyExclusiveList1 = $disallowedParameters + MutuallyExclusiveList1 = 'TargetScope' MutuallyExclusiveList2 = $disallowedParameters } @@ -267,6 +352,18 @@ class DhcpServerv4DnsDynamicUpdates : ResourceBase ) } + Assert-BoundParameter @assertBoundParameterParameters + + $disallowedParameters = @( + 'DnsSuffix' + ) + + $assertBoundParameterParameters = @{ + BoundParameterList = $properties + MutuallyExclusiveList1 = 'TargetScope' + MutuallyExclusiveList2 = $disallowedParameters + } + Assert-BoundParameter @assertBoundParameterParameters break } @@ -295,31 +392,29 @@ class DhcpServerv4DnsDynamicUpdates : ResourceBase } Assert-BoundParameter @assertBoundParameterParameters - break - } - default - { - # This should fail as TargetScope is not a valid value - throw - } - } + $disallowedParameters = @( + 'DnsSuffix' + 'NameProtection' + ) - $ipStringsToValidate = @( - @{ - ParameterName = 'IPAddress' - } - @{ - ParameterName = 'ScopeId' + $assertBoundParameterParameters = @{ + BoundParameterList = $properties + MutuallyExclusiveList1 = 'TargetScope' + MutuallyExclusiveList2 = $disallowedParameters + } + + Assert-BoundParameter @assertBoundParameterParameters + break } - ) + } - foreach ($ipString in $ipStringsToValidate) + foreach ($ipString in @('IPAddress', 'ScopeId')) { - if ($properties.ContainsKey($ipString.ParameterName)) + if ($properties.ContainsKey($ipString)) { $getValidIpAddressParams = @{ - Address = $properties.($ipString.ParameterName) + Address = $properties.$ipString AddressFamily = 'IPv4' } From 8c0fd4fb60c352ff3fd704c40ffd5ee4c3cc76ae Mon Sep 17 00:00:00 2001 From: Dan Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Tue, 11 Feb 2025 17:28:15 +0000 Subject: [PATCH 06/12] Use prerelease docgenerator for classes --- RequiredModules.psd1 | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/RequiredModules.psd1 b/RequiredModules.psd1 index d46b5c9..a488226 100644 --- a/RequiredModules.psd1 +++ b/RequiredModules.psd1 @@ -34,6 +34,11 @@ 'Indented.ScriptAnalyzerRules' = 'latest' # Prerequisite modules for documentation. - 'DscResource.DocGenerator' = 'latest' + 'DscResource.DocGenerator' = @{ + Version = 'latest' + Parameters = @{ + AllowPrerelease = $true + } + } PlatyPS = 'latest' } From bae7f51b8bc3867609a0e3eb5ddb428372f040f4 Mon Sep 17 00:00:00 2001 From: Dan Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Tue, 11 Feb 2025 17:32:47 +0000 Subject: [PATCH 07/12] Add v6 --- .../020.DhcpServerv6DnsDynamicUpdates.ps1 | 183 ++++++++++++++++++ source/Enum/AddressFamilyType.ps1 | 5 - source/Enum/Dhcpv6TargetScopeType.ps1 | 6 + ...DhcpServerv6DnsDynamicUpdates.strings.psd1 | 15 ++ 4 files changed, 204 insertions(+), 5 deletions(-) create mode 100644 source/Classes/020.DhcpServerv6DnsDynamicUpdates.ps1 delete mode 100644 source/Enum/AddressFamilyType.ps1 create mode 100644 source/Enum/Dhcpv6TargetScopeType.ps1 create mode 100644 source/en-US/DhcpServerv6DnsDynamicUpdates.strings.psd1 diff --git a/source/Classes/020.DhcpServerv6DnsDynamicUpdates.ps1 b/source/Classes/020.DhcpServerv6DnsDynamicUpdates.ps1 new file mode 100644 index 0000000..3420249 --- /dev/null +++ b/source/Classes/020.DhcpServerv6DnsDynamicUpdates.ps1 @@ -0,0 +1,183 @@ +<# + .SYNOPSIS + The `DhcpServerv6DnsDynamicUpdates` DSC resource is used to create, modify or remove + IPv6 DnsDynamicUpdates on a Dhcp Server, ServerPolicy, Scope, ScopePolicy or Reservation. + + .DESCRIPTION + This resource is used to create, edit or remove DnsDynamicUpdate settings on DhcpServers. + + .PARAMETER TargetScope + The target scope type of the operation. + + .PARAMETER Ensure + Specifies whether the configuration should exist. + + .PARAMETER NameProtection + + + .PARAMETER DeleteDnsRROnLeaseExpiry + + + .PARAMETER DynamicUpdates + + + .PARAMETER IPAddress + + + .PARAMETER Prefix + + .PARAMETER Reasons + Returns the reason a property is not in desired state. +#> + +[DscResource()] +class DhcpServerv6DnsDynamicUpdates : ResourceBase +{ + [DscProperty(Key)] + [Dhcpv6TargetScopeType] + $TargetScope + + [DscProperty(Mandatory)] + [Ensure] + $Ensure = [Ensure]::Present + + [DscProperty()] + [System.Nullable[System.Boolean]] + $NameProtection + + [DscProperty()] + [System.Nullable[System.Boolean]] + $DeleteDnsRROnLeaseExpiry + + [DscProperty()] + [DynamicUpdatesType] + $DynamicUpdates + + [DscProperty()] + [System.String] + $IPAddress + + [DscProperty()] + [System.String] + $Prefix + + [DscProperty(NotConfigurable)] + [DhcpServerReason[]] + $Reasons + + DhcpServerv6DnsDynamicUpdates () : base ($PSScriptRoot) + { + $this.ExcludeDscProperties = @() + + $this.FeatureOptionalEnums = $true + } + + # Required DSC Methods, these call the method in the base class. + [DhcpServerv6DnsDynamicUpdates] Get() + { + # Call the base method to return the properties. + return ([ResourceBase] $this).Get() + } + + [void] Set() + { + # Call the base method to enforce the properties. + ([ResourceBase] $this).Set() + } + + [System.Boolean] Test() + { + # Call the base method to test all of the properties that should be enforced. + return ([ResourceBase] $this).Test() + } + + # Base method Get() call this method to get the current state as a Hashtable. + [System.Collections.Hashtable] GetCurrentState([System.Collections.Hashtable] $properties) + { + return @{} + } + + <# + Base method Set() call this method with the properties that should be + enforced and that are not in desired state. + #> + hidden [void] Modify([System.Collections.Hashtable] $properties) + { + } + + <# + Base method Assert() call this method with the properties that was assigned + a value. + #> + hidden [void] AssertProperties([System.Collections.Hashtable] $properties) + { + Assert-Module -ModuleName 'DHCPServer' + + # Test at least one of the following exists + $mustHaveOne = @( + 'DeleteDnsRROnLeaseExpiry' + 'DynamicUpdates' + 'NameProtection' + ) + + $optionalProperties = $properties.Keys.Where({ $_ -in $mustHaveOne }) + + if (-not $optionalProperties) + { + $errorMessage = $script:localizedData.SpecificParametersOneMustBeSet -f ($mustHaveOne -join ''', ''') + + $PSCmdlet.ThrowTerminatingError( + [System.Management.Automation.ErrorRecord]::new( + $errorMessage, + 'DS6DDU0001', # cspell: disable-line + [System.Management.Automation.ErrorCategory]::InvalidOperation, + 'Required optional parameters' + ) + ) + } + + # Scopes + switch ($properties.TargetScope) + { + Server + { + $disallowedParameters = @( + 'Prefix' + 'IPAddress' + ) + + $assertBoundParameterParameters = @{ + BoundParameterList = $properties + MutuallyExclusiveList1 = $disallowedParameters + MutuallyExclusiveList2 = $disallowedParameters + } + + Assert-BoundParameter @assertBoundParameterParameters + } + + Scope + { + $assertBoundParameterParameters = @{ + BoundParameterList = $properties + RequiredParameter = @( + 'Prefix' + ) + } + + Assert-BoundParameter @assertBoundParameterParameters + } + + Reservation + { + $assertBoundParameterParameters = @{ + BoundParameterList = $properties + RequiredParameter = @( + 'IPAddress' + ) + } + + Assert-BoundParameter @assertBoundParameterParameters + } + } + } +} diff --git a/source/Enum/AddressFamilyType.ps1 b/source/Enum/AddressFamilyType.ps1 deleted file mode 100644 index a2d60ab..0000000 --- a/source/Enum/AddressFamilyType.ps1 +++ /dev/null @@ -1,5 +0,0 @@ -enum AddressFamilyType -{ - IPv4 = 1 - IPv6 -} diff --git a/source/Enum/Dhcpv6TargetScopeType.ps1 b/source/Enum/Dhcpv6TargetScopeType.ps1 new file mode 100644 index 0000000..9760fcd --- /dev/null +++ b/source/Enum/Dhcpv6TargetScopeType.ps1 @@ -0,0 +1,6 @@ +enum Dhcpv6TargetScopeType +{ + Server = 1 + Scope + Reservation +} diff --git a/source/en-US/DhcpServerv6DnsDynamicUpdates.strings.psd1 b/source/en-US/DhcpServerv6DnsDynamicUpdates.strings.psd1 new file mode 100644 index 0000000..51a0501 --- /dev/null +++ b/source/en-US/DhcpServerv6DnsDynamicUpdates.strings.psd1 @@ -0,0 +1,15 @@ +<# + .SYNOPSIS + The localized resource strings in English (en-US) for the + resource DhcpServerDnsDynamicUpdates module. This file should only contain + localized strings for private functions, public command, and + classes (that are not a DSC resource). +#> + +ConvertFrom-StringData @' + ## Strings overrides for the ResourceBase's default strings. + # None + + ## Strings directly used by the derived class WSManListener. + SpecificParametersOneMustBeSet = At least one of the parameters '{0}' must be specified. (DS4DDU0001). +'@ From 51395bc7b65670f4d83cde84005665858db97320 Mon Sep 17 00:00:00 2001 From: Dan Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Sun, 16 Feb 2025 15:01:58 +0000 Subject: [PATCH 08/12] Remove .Base from code coverage --- build.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/build.yaml b/build.yaml index 259a9e3..f4cd810 100644 --- a/build.yaml +++ b/build.yaml @@ -93,6 +93,7 @@ Pester: OutputEncoding: ascii ExcludeFromCodeCoverage: - Modules/DscResource.Common + - Modules/DscResource.Base Script: - tests/Unit Tag: From 6adcaecaa68a790779d2ab80fac1fb3368d01b45 Mon Sep 17 00:00:00 2001 From: Dan Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Sun, 16 Feb 2025 15:51:08 +0000 Subject: [PATCH 09/12] Fix HQRM --- source/en-US/DhcpServerv4DnsDynamicUpdates.strings.psd1 | 1 - 1 file changed, 1 deletion(-) diff --git a/source/en-US/DhcpServerv4DnsDynamicUpdates.strings.psd1 b/source/en-US/DhcpServerv4DnsDynamicUpdates.strings.psd1 index eee1610..09b6e00 100644 --- a/source/en-US/DhcpServerv4DnsDynamicUpdates.strings.psd1 +++ b/source/en-US/DhcpServerv4DnsDynamicUpdates.strings.psd1 @@ -11,7 +11,6 @@ ConvertFrom-StringData @' # None ## Strings directly used by the derived class DhcpServerv4DnsDynamicUpdates. - SpecificParametersOneMustBeSet = At least one of the parameters '{0}' must be specified. (DS4DDU0001) ServerPolicyDoesNotExist = The Server Policy '{0}' does not exist. (DS4DDU0001) ScopeDoesNotExist = The Scope '{0}' does not exist. (DS4DDU0002) ScopePolicyDoesNotExist = The Scope Policy '{0}', '{1}' does not exist. (DS4DDU0003) From aff98249d41efc8ef7c76483eab102fa37de28ae Mon Sep 17 00:00:00 2001 From: Dan Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Sun, 16 Feb 2025 16:12:04 +0000 Subject: [PATCH 10/12] Fix hqrm --- source/Classes/020.DhcpServerv6DnsDynamicUpdates.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/Classes/020.DhcpServerv6DnsDynamicUpdates.ps1 b/source/Classes/020.DhcpServerv6DnsDynamicUpdates.ps1 index 3420249..50cda5a 100644 --- a/source/Classes/020.DhcpServerv6DnsDynamicUpdates.ps1 +++ b/source/Classes/020.DhcpServerv6DnsDynamicUpdates.ps1 @@ -124,7 +124,7 @@ class DhcpServerv6DnsDynamicUpdates : ResourceBase if (-not $optionalProperties) { - $errorMessage = $script:localizedData.SpecificParametersOneMustBeSet -f ($mustHaveOne -join ''', ''') + $errorMessage = $this.localizedData.SpecificParametersOneMustBeSet -f ($mustHaveOne -join ''', ''') $PSCmdlet.ThrowTerminatingError( [System.Management.Automation.ErrorRecord]::new( From e3f8c90b6bf300e4a558e904b03af6f66a873686 Mon Sep 17 00:00:00 2001 From: Dan Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Sun, 16 Feb 2025 16:12:21 +0000 Subject: [PATCH 11/12] Remove unused config --- build.yaml | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/build.yaml b/build.yaml index f4cd810..0a86d21 100644 --- a/build.yaml +++ b/build.yaml @@ -94,12 +94,6 @@ Pester: ExcludeFromCodeCoverage: - Modules/DscResource.Common - Modules/DscResource.Base - Script: - - tests/Unit - Tag: - CodeCoverageThreshold: 65 - CodeCoverageOutputFile: CodeCoverage.xml - CodeCoverageOutputFileEncoding: ascii CodeCoverage: CodeCoverageMergedOutputFile: CodeCov_Merged.xml @@ -125,8 +119,9 @@ DscTest: ExcludeModuleFile: - Modules/DscResource.Common - Modules/DscResource.Base + # Must exclude built module file because it should not be tested like MOF-based resources - DhcpServerDsc.psm1 - MainGitBranch: main + MainGitBranch: main ModuleBuildTasks: Sampler: From b301f21b5199dbac1eff528872a19f0593650b48 Mon Sep 17 00:00:00 2001 From: Dan Hughes <2237515+dan-hughes@users.noreply.github.com> Date: Sun, 13 Apr 2025 14:06:30 +0100 Subject: [PATCH 12/12] Move common parameters to base class --- .../010.DhcpServerDnsDynamicUpdatesBase.ps1 | 42 ++++++++++ .../020.DhcpServerv4DnsDynamicUpdates.ps1 | 32 +------- .../020.DhcpServerv6DnsDynamicUpdates.ps1 | 30 +------ ...cpServerDnsDynamicUpdatesBase.strings.psd1 | 10 +++ .../DhcpServerDnsDynamicUpdatesBase.Tests.ps1 | 79 +++++++++++++++++++ .../DhcpServerv4DnsDynamicUpdates.Tests.ps1 | 79 +++++++++++++++++++ 6 files changed, 214 insertions(+), 58 deletions(-) create mode 100644 source/Classes/010.DhcpServerDnsDynamicUpdatesBase.ps1 create mode 100644 source/en-US/DhcpServerDnsDynamicUpdatesBase.strings.psd1 create mode 100644 tests/Unit/Classes/DhcpServerDnsDynamicUpdatesBase.Tests.ps1 create mode 100644 tests/Unit/Classes/DhcpServerv4DnsDynamicUpdates.Tests.ps1 diff --git a/source/Classes/010.DhcpServerDnsDynamicUpdatesBase.ps1 b/source/Classes/010.DhcpServerDnsDynamicUpdatesBase.ps1 new file mode 100644 index 0000000..bfc5201 --- /dev/null +++ b/source/Classes/010.DhcpServerDnsDynamicUpdatesBase.ps1 @@ -0,0 +1,42 @@ +<# + .SYNOPSIS + A class with DSC properties that are used in multiple child classes. + + .DESCRIPTION + A class with DSC properties that are used in multiple child classes. + + .PARAMETER Reasons + Returns the reason a property is not in desired state. +#> + +class DhcpServerDnsDynamicUpdatesBase : ResourceBase +{ + [DscProperty(Mandatory)] + [Ensure] + $Ensure = [Ensure]::Present + + [DscProperty()] + [System.Nullable[System.Boolean]] + $NameProtection + + [DscProperty()] + [System.Nullable[System.Boolean]] + $DeleteDnsRROnLeaseExpiry + + [DscProperty()] + [DynamicUpdatesType] + $DynamicUpdates + + [DscProperty()] + [System.String] + $IPAddress + + [DscProperty(NotConfigurable)] + [DhcpServerReason[]] + $Reasons + + DhcpServerDnsDynamicUpdatesBase () : base ($PSScriptRoot) + { + $this.FeatureOptionalEnums = $true + } +} diff --git a/source/Classes/020.DhcpServerv4DnsDynamicUpdates.ps1 b/source/Classes/020.DhcpServerv4DnsDynamicUpdates.ps1 index 7c6db40..d2729ec 100644 --- a/source/Classes/020.DhcpServerv4DnsDynamicUpdates.ps1 +++ b/source/Classes/020.DhcpServerv4DnsDynamicUpdates.ps1 @@ -38,38 +38,15 @@ .PARAMETER DnsSuffix The DNS Suffix to register DHCP clients to. This is only applicable to ServerPolicy and ScopePolicy TargetScope. - - .PARAMETER Reasons - Returns the reason a property is not in desired state. #> [DscResource()] -class DhcpServerv4DnsDynamicUpdates : ResourceBase +class DhcpServerv4DnsDynamicUpdates : DhcpServerDnsDynamicUpdatesBase { [DscProperty(Key)] [Dhcpv4TargetScopeType] $TargetScope - [DscProperty(Mandatory)] - [Ensure] - $Ensure = [Ensure]::Present - - [DscProperty()] - [System.Nullable[System.Boolean]] - $NameProtection - - [DscProperty()] - [System.Nullable[System.Boolean]] - $DeleteDnsRROnLeaseExpiry - - [DscProperty()] - [DynamicUpdatesType] - $DynamicUpdates - - [DscProperty()] - [System.String] - $IPAddress - [DscProperty()] [System.Nullable[System.Boolean]] $UpdateDnsRRForOlderClients @@ -90,13 +67,8 @@ class DhcpServerv4DnsDynamicUpdates : ResourceBase [System.String] $DnsSuffix - [DscProperty(NotConfigurable)] - [DhcpServerReason[]] - $Reasons - - DhcpServerv4DnsDynamicUpdates () : base ($PSScriptRoot) + DhcpServerv4DnsDynamicUpdates () : base () { - $this.FeatureOptionalEnums = $true } # Required DSC Methods, these call the method in the base class. diff --git a/source/Classes/020.DhcpServerv6DnsDynamicUpdates.ps1 b/source/Classes/020.DhcpServerv6DnsDynamicUpdates.ps1 index 50cda5a..1bbc253 100644 --- a/source/Classes/020.DhcpServerv6DnsDynamicUpdates.ps1 +++ b/source/Classes/020.DhcpServerv6DnsDynamicUpdates.ps1 @@ -31,45 +31,19 @@ #> [DscResource()] -class DhcpServerv6DnsDynamicUpdates : ResourceBase +class DhcpServerv6DnsDynamicUpdates : DhcpServerDnsDynamicUpdatesBase { [DscProperty(Key)] [Dhcpv6TargetScopeType] $TargetScope - [DscProperty(Mandatory)] - [Ensure] - $Ensure = [Ensure]::Present - - [DscProperty()] - [System.Nullable[System.Boolean]] - $NameProtection - - [DscProperty()] - [System.Nullable[System.Boolean]] - $DeleteDnsRROnLeaseExpiry - - [DscProperty()] - [DynamicUpdatesType] - $DynamicUpdates - - [DscProperty()] - [System.String] - $IPAddress - [DscProperty()] [System.String] $Prefix - [DscProperty(NotConfigurable)] - [DhcpServerReason[]] - $Reasons - - DhcpServerv6DnsDynamicUpdates () : base ($PSScriptRoot) + DhcpServerv6DnsDynamicUpdates () : base () { $this.ExcludeDscProperties = @() - - $this.FeatureOptionalEnums = $true } # Required DSC Methods, these call the method in the base class. diff --git a/source/en-US/DhcpServerDnsDynamicUpdatesBase.strings.psd1 b/source/en-US/DhcpServerDnsDynamicUpdatesBase.strings.psd1 new file mode 100644 index 0000000..f056c5b --- /dev/null +++ b/source/en-US/DhcpServerDnsDynamicUpdatesBase.strings.psd1 @@ -0,0 +1,10 @@ +<# + .SYNOPSIS + The localized resource strings in English (en-US) for the + resource DhcpServerDnsDynamicUpdatesBase base class. This file should only contain + localized strings for private functions, public command, and + classes (that are not a DSC resource). +#> + +ConvertFrom-StringData @' +'@ diff --git a/tests/Unit/Classes/DhcpServerDnsDynamicUpdatesBase.Tests.ps1 b/tests/Unit/Classes/DhcpServerDnsDynamicUpdatesBase.Tests.ps1 new file mode 100644 index 0000000..ece395b --- /dev/null +++ b/tests/Unit/Classes/DhcpServerDnsDynamicUpdatesBase.Tests.ps1 @@ -0,0 +1,79 @@ +<# + .SYNOPSIS + Unit test for DhcpServerDnsDynamicUpdatesBase base class. +#> + +# 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 = 'DhcpServerDsc' + + Import-Module -Name $script:dscModuleName + + $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:dscModuleName + $PSDefaultParameterValues['Mock:ModuleName'] = $script:dscModuleName + $PSDefaultParameterValues['Should:ModuleName'] = $script:dscModuleName +} + +AfterAll { + $PSDefaultParameterValues.Remove('InModuleScope:ModuleName') + $PSDefaultParameterValues.Remove('Mock:ModuleName') + $PSDefaultParameterValues.Remove('Should:ModuleName') + + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe 'DhcpServerDnsDynamicUpdatesBase' { + Context 'When class is instantiated' { + It 'Should not throw an exception' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + { [DhcpServerDnsDynamicUpdatesBase]::new() } | Should -Not -Throw + } + } + + It 'Should have a default or empty constructor' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $instance = [DhcpServerDnsDynamicUpdatesBase]::new() + $instance | Should -Not -BeNullOrEmpty + } + } + + It 'Should be the correct type' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $instance = [DhcpServerDnsDynamicUpdatesBase]::new() + $instance.GetType().Name | Should -Be 'DhcpServerDnsDynamicUpdatesBase' + } + } + } +} diff --git a/tests/Unit/Classes/DhcpServerv4DnsDynamicUpdates.Tests.ps1 b/tests/Unit/Classes/DhcpServerv4DnsDynamicUpdates.Tests.ps1 new file mode 100644 index 0000000..ee43c96 --- /dev/null +++ b/tests/Unit/Classes/DhcpServerv4DnsDynamicUpdates.Tests.ps1 @@ -0,0 +1,79 @@ +<# + .SYNOPSIS + Unit test for DhcpServerv4DnsDynamicUpdates 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 = 'DhcpServerDsc' + + Import-Module -Name $script:dscModuleName + + $PSDefaultParameterValues['InModuleScope:ModuleName'] = $script:dscModuleName + $PSDefaultParameterValues['Mock:ModuleName'] = $script:dscModuleName + $PSDefaultParameterValues['Should:ModuleName'] = $script:dscModuleName +} + +AfterAll { + $PSDefaultParameterValues.Remove('InModuleScope:ModuleName') + $PSDefaultParameterValues.Remove('Mock:ModuleName') + $PSDefaultParameterValues.Remove('Should:ModuleName') + + # Unload the module being tested so that it doesn't impact any other tests. + Get-Module -Name $script:dscModuleName -All | Remove-Module -Force +} + +Describe 'DhcpServerv4DnsDynamicUpdates' { + Context 'When class is instantiated' { + It 'Should not throw an exception' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + { [DhcpServerv4DnsDynamicUpdates]::new() } | Should -Not -Throw + } + } + + It 'Should have a default or empty constructor' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $instance = [DhcpServerv4DnsDynamicUpdates]::new() + $instance | Should -Not -BeNullOrEmpty + } + } + + It 'Should be the correct type' { + InModuleScope -ScriptBlock { + Set-StrictMode -Version 1.0 + + $instance = [DhcpServerv4DnsDynamicUpdates]::new() + $instance.GetType().Name | Should -Be 'DhcpServerv4DnsDynamicUpdates' + } + } + } +}