diff --git a/CHANGELOG.md b/CHANGELOG.md index 1bf19de..f2c84e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,6 +27,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Added CMFallbackStatusPoint Resource - Added CMSoftwareUpdatePoint Resource - Added CMDsitributionPoint Resource +- Added CMHeartbeatDiscovery module +- Added ConvertTo-ScheduleInterval to ResourceHelper ### Changed diff --git a/README.md b/README.md index 3ea6c78..92bbe85 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,8 @@ Please check out common DSC Community [contributing guidelines](https://dsccommu the SCCM Software Update Point role. - **CMDistributionPoint**: Provides a resource for creating and managing the distribution point role. +- **CMHeartbeatDiscovery**: Provides a resource to manage the Configuration Manager + Heartbeat Discovery method. ### CMAccounts @@ -472,3 +474,23 @@ Please check out common DSC Community [contributing guidelines](https://dsccommu - [CMDistributionPoint_Absent](Source\Examples\Resources\CMDistributionPoint\CMDistributionPoint_Absent.ps1) - [CMDistributionPoint_Present](Source\Examples\Resources\CMDistributionPoint\CMDistributionPoint_Present.ps1) + +### CMHeartbeatDiscovery + +- **[String] SiteCode** _(Key)_: Specifies the Site Code for the Configuration + Manager site. +- **[Boolean] Enabled** _(Required)_: Specifies the enablement of the heartbeat + discovery method. If settings is set to $false no other value provided will be + evaluated for compliance. +- **[String] ScheduleInterval** _(Write)_: Specifies the time when the scheduled + event recurs in hours and days. + - Values include: { Hours | Days } +- **[String] ScheduleCount** _(Write)_: Specifies how often the recur interval + is run. If hours are specified the max value is 23. Anything over 23 will result + in 23 to be set. If days are specified the max value is 31. Anything over 31 will + result in 31 to be set. + +#### CMHeartbeatDiscovery Examples + +- [CMHeartbeatDiscovery_Disabled](Source\Examples\Resources\CMHeartbeatDiscovery\CMHeartbeatDiscovery_Disabled.ps1) +- [CMHeartbeatDiscovery_Enabled](Source\Examples\Resources\CMHeartbeatDiscovery\CMHeartbeatDiscovery_Enabled.ps1) diff --git a/source/ConfigMgrCBDsc.psd1 b/source/ConfigMgrCBDsc.psd1 index 5cd47ad..b7544f8 100644 --- a/source/ConfigMgrCBDsc.psd1 +++ b/source/ConfigMgrCBDsc.psd1 @@ -54,6 +54,7 @@ 'CMFallbackStatusPoint' 'CMSoftwareUpdatePoint' 'CMDistributionPoint' + 'CMHeartbeatDiscovery' ) <# @@ -68,7 +69,7 @@ # Tags applied to this module. These help with module discovery in online galleries. Tags = @('DesiredStateConfiguration', 'DSC', 'DSCResourceKit', 'DSCResource', 'ConfigMgrCBDsc','CMAccounts','CMIniFile','Collections', 'Boundaries','ForestDiscovery','ClientStatusSettings','BoundaryGroups','ManagementPoint','AssetIntelligencePoint','FallbackStatusPoint', - 'SoftwareUpdatePoint','DistrubtionPoint') + 'SoftwareUpdatePoint','DistrubtionPoint','HeartbeatDiscovery') # A URL to the license for this module. LicenseUri = 'https://github.com/dsccommunity/ConfigMgrCBDsc/blob/master/LICENSE' diff --git a/source/DSCResources/DSC_CMHeartbeatDiscovery/DSC_CMHeartbeatDiscovery.psm1 b/source/DSCResources/DSC_CMHeartbeatDiscovery/DSC_CMHeartbeatDiscovery.psm1 new file mode 100644 index 0000000..69f94dd --- /dev/null +++ b/source/DSCResources/DSC_CMHeartbeatDiscovery/DSC_CMHeartbeatDiscovery.psm1 @@ -0,0 +1,242 @@ +$script:dscResourceCommonPath = Join-Path -Path $PSScriptRoot -ChildPath '..\..\Modules\DscResource.Common' +$script:configMgrResourcehelper = Join-Path -Path $PSScriptRoot -ChildPath '..\..\Modules\ConfigMgrCBDsc.ResourceHelper' + +Import-Module -Name $script:dscResourceCommonPath +Import-Module -Name $script:configMgrResourcehelper + +$script:localizedData = Get-LocalizedData -DefaultUICulture 'en-US' + +<# + .SYNOPSIS + This will return a hashtable of results. + + .PARAMETER SiteCode + Specifies the site code for Configuration Manager site. + + .PARAMETER Enabled + Specifies the enablement of the Heatbeat discovery method. + + Not used in Get-TargetResource. +#> +function Get-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Collections.Hashtable])] + param + ( + [Parameter(Mandatory = $true)] + [String] + $SiteCode, + + [Parameter(Mandatory = $true)] + [Boolean] + $Enabled + ) + + Write-Verbose -Message $script:localizedData.RetrieveSettingValue + Import-ConfigMgrPowerShellModule -SiteCode $SiteCode + Set-Location -Path "$($SiteCode):\" + + $heartbeat = (Get-CMDiscoveryMethod -Name HeartbeatDiscovery -SiteCode $SiteCode).Props + $state = ($heartbeat | Where-Object -FilterScript {$_.PropertyName -eq 'Enable Heartbeat DDR'}).Value + $heartbeatSchedule = ($heartbeat | Where-Object -FilterScript {$_.PropertyName -eq 'DDR Refresh Interval'}).Value2 + + $scheduleConvert = ConvertTo-ScheduleInterval -ScheduleString $heartbeatSchedule + + return @{ + SiteCode = $SiteCode + Enabled = $state + ScheduleInterval = $scheduleConvert.Interval + ScheduleCount = $scheduleConvert.Count + } +} + +<# + .SYNOPSIS + This will set the desired state. + + .PARAMETER SiteCode + Specifies the site code for Configuration Manager site. + + .PARAMETER Enabled + Specifies the enablement of the Heatbeat discovery method. + + .PARAMETER ScheduleInterval + Specifies the time when the scheduled event recurs in hours and days. + + .PARAMETER ScheduleCount + Specifies how often the recur interval is run. If hours are specified the max value + is 23. Anything over 23 will result in 23 to be set. If days are specified the max value + is 31. Anything over 31 will result in 31 to be set. +#> +function Set-TargetResource +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [String] + $SiteCode, + + [Parameter(Mandatory = $true)] + [Boolean] + $Enabled, + + [Parameter()] + [ValidateSet('Hours','Days')] + [String] + $ScheduleInterval, + + [Parameter()] + [UInt32] + $ScheduleCount + ) + + Import-ConfigMgrPowerShellModule -SiteCode $SiteCode + Set-Location -Path "$($SiteCode):\" + + try + { + $state = Get-TargetResource -SiteCode $SiteCode -Enabled $Enabled + + if ($Enabled -ne $state.Enabled) + { + Write-Verbose -Message ($script:localizedData.SettingEnable -f $state.Enabled, $Enabled) + $buildingParams = @{ + Enabled = $Enabled + } + } + + if ($Enabled -eq $true) + { + if (($PSBoundParameters.ContainsKey('ScheduleInterval') -and -not $PSBoundParameters.ContainsKey('ScheduleCount')) -or + ($PSBoundParameters.ContainsKey('ScheduleCount') -and -not $PSBoundParameters.ContainsKey('ScheduleInterval'))) + { + throw $script:localizedData.IntervalCount + } + + if ($PSBoundParameters.ContainsKey('ScheduleInterval')) + { + if ($ScheduleInterval -ne $state.ScheduleInterval) + { + Write-Verbose -Message ($script:localizedData.SIntervalSet -f $ScheduleInterval) + $setSchedule = $true + } + + if ($ScheduleCount -ne $state.ScheduleCount) + { + Write-Verbose -Message ($script:localizedData.SCountSet -f $ScheduleCount) + $setSchedule = $true + } + + if ($setSchedule -eq $true) + { + $pScheduleSet = @{ + RecurInterval = $ScheduleInterval + RecurCount = $ScheduleCount + } + + $pschedule = New-CMSchedule @pScheduleSet + + $buildingParams += @{ + PollingSchedule = $pSchedule + } + } + } + } + + if ($buildingParams) + { + Set-CMDiscoveryMethod -Heartbeat -SiteCode $SiteCode @buildingParams + } + } + catch + { + throw $_ + } + finally + { + Set-Location -Path "$env:temp" + } +} + +<# + .SYNOPSIS + This will test the desired state. + + .PARAMETER SiteCode + Specifies the site code for Configuration Manager site. + + .PARAMETER Enabled + Specifies the enablement of the Heatbeat discovery method. + + .PARAMETER ScheduleInterval + Specifies the time when the scheduled event recurs in hours and days. + + .PARAMETER ScheduleCount + Specifies how often the recur interval is run. If hours are specified the max value + is 23. Anything over 23 will result in 23 to be set. If days are specified the max value + is 31. Anything over 31 will result in 31 to be set. +#> +function Test-TargetResource +{ + [CmdletBinding()] + [OutputType([System.Boolean])] + param + ( + [Parameter(Mandatory = $true)] + [String] + $SiteCode, + + [Parameter(Mandatory = $true)] + [Boolean] + $Enabled, + + [Parameter()] + [ValidateSet('Hours','Days')] + [String] + $ScheduleInterval, + + [Parameter()] + [UInt32] + $ScheduleCount + ) + + Import-ConfigMgrPowerShellModule -SiteCode $SiteCode + Set-Location -Path "$($SiteCode):\" + $state = Get-TargetResource -SiteCode $SiteCode -Enabled $Enabled + $result = $true + + if ($Enabled -ne $state.Enabled) + { + Write-Verbose -Message ($script:localizedData.EnableStatus -f $Enabled, $state.Enabled) + $result = $false + } + + if (($PSBoundParameters.ContainsKey('ScheduleInterval') -and -not $PSBoundParameters.ContainsKey('ScheduleCount')) -or + ($PSBoundParameters.ContainsKey('ScheduleCount') -and -not $PSBoundParameters.ContainsKey('ScheduleInterval'))) + { + Write-Verbose -Message $script:localizedData.IntervalCountTest + $result = $false + } + + if ($PSBoundParameters.ContainsKey('ScheduleInterval') -and $PSBoundParameters.ContainsKey('ScheduleCount')) + { + if ($ScheduleInterval -ne $state.SCheduleInterval) + { + Write-Verbose -Message ($script:localizedData.SIntervalTest -f $ScheduleInterval, $State.ScheduleInterval) + $result = $false + } + + if (($ScheduleInterval -ne 'None') -and ($ScheduleCount -ne $state.ScheduleCount)) + { + Write-Verbose -Message ($script:localizedData.SCountTest -f $ScheduleCount, $State.ScheduleCount) + $result = $false + } + } + + Write-Verbose -Message ($script:localizedData.TestState -f $result) + return $result +} + +Export-ModuleMember -Function *-TargetResource diff --git a/source/DSCResources/DSC_CMHeartbeatDiscovery/DSC_CMHeartbeatDiscovery.schema.mof b/source/DSCResources/DSC_CMHeartbeatDiscovery/DSC_CMHeartbeatDiscovery.schema.mof new file mode 100644 index 0000000..7f2236b --- /dev/null +++ b/source/DSCResources/DSC_CMHeartbeatDiscovery/DSC_CMHeartbeatDiscovery.schema.mof @@ -0,0 +1,8 @@ +[ClassVersion("1.0.0"), FriendlyName("CMHeartbeatDiscovery")] +class DSC_CMHeartbeatDiscovery : OMI_BaseResource +{ + [Key, Description("Specifies the SiteCode for the Configuration Manager site.")] String SiteCode; + [Required, Description("Specifies the enablement of the heartbeat discovery method.")] Boolean Enabled; + [Write, Description("Specifies the recur interval of days, hours"),ValueMap{"Days","Hours"},Values{"Days","Hours"}] String ScheduleInterval; + [Write, Description("Specifies how often the recur interval is run. If hours are specified the max value is 23. Anything over 23 will result in 23 to be set. If days are specified the max value is 31. Anything over 31 will result in 31 to be set.")] UInt32 ScheduleCount; +}; diff --git a/source/DSCResources/DSC_CMHeartbeatDiscovery/en-US/DSC_CMHeartbeatDiscovery.strings.psd1 b/source/DSCResources/DSC_CMHeartbeatDiscovery/en-US/DSC_CMHeartbeatDiscovery.strings.psd1 new file mode 100644 index 0000000..5c9cdb2 --- /dev/null +++ b/source/DSCResources/DSC_CMHeartbeatDiscovery/en-US/DSC_CMHeartbeatDiscovery.strings.psd1 @@ -0,0 +1,12 @@ +ConvertFrom-StringData @' + RetrieveSettingValue = Getting results for Configuration Manager heartbeat discovery method. + EnableStatus = Heartbeat discovery is set to {0} return {1}. + IntervalCountTest = Missing ScheduleInterval or ScheduleCount unable to evaluate schedule. + SIntervalTest = NOT MATCH: Schedule interval expected: {0} returned {1}. + SCountTest = NOT MATCH: Schedule count expected: {0} returned {1}. + TestState = Test-TargetResource compliance check returned: {0}. + SettingEnable = Heartbeat discovery is currently {0}, setting to {1}. + IntervalCount = Invalid parameter usage must specify ScheduleInterval and ScheduleCount. + SIntervalSet = Setting Schedule interval to {0}. + SCountSet = Setting Schedule count to {0}. +'@ diff --git a/source/Examples/Resources/CMHeartbeatDiscovery/CMHeartbeatDiscovery_Disabled.ps1 b/source/Examples/Resources/CMHeartbeatDiscovery/CMHeartbeatDiscovery_Disabled.ps1 new file mode 100644 index 0000000..7386076 --- /dev/null +++ b/source/Examples/Resources/CMHeartbeatDiscovery/CMHeartbeatDiscovery_Disabled.ps1 @@ -0,0 +1,17 @@ +<# + .SYNOPSIS + A DSC configuration script to disable heartbeat discovery in Configuration Manager. +#> +Configuration Example +{ + Import-DscResource -ModuleName ConfigMgrCBDsc + + Node localhost + { + CMHeartbeatDiscovery ExampleSettings + { + SiteCode = 'Lab' + Enabled = $false + } + } +} diff --git a/source/Examples/Resources/CMHeartbeatDiscovery/CMHeartbeatDiscovery_Enabled.ps1 b/source/Examples/Resources/CMHeartbeatDiscovery/CMHeartbeatDiscovery_Enabled.ps1 new file mode 100644 index 0000000..7349779 --- /dev/null +++ b/source/Examples/Resources/CMHeartbeatDiscovery/CMHeartbeatDiscovery_Enabled.ps1 @@ -0,0 +1,19 @@ +<# + .SYNOPSIS + A DSC configuration script to enable heartbeat discovery in Configuration Manager. +#> +Configuration Example +{ + Import-DscResource -ModuleName ConfigMgrCBDsc + + Node localhost + { + CMHeartbeatDiscovery ExampleSettings + { + SiteCode = 'Lab' + Enabled = $true + ScheduleInterval = 'Days' + ScheduleCount = '4' + } + } +} diff --git a/source/Modules/ConfigMgrCBDsc.ResourceHelper/ConfigMgrCBDsc.ResourceHelper.psd1 b/source/Modules/ConfigMgrCBDsc.ResourceHelper/ConfigMgrCBDsc.ResourceHelper.psd1 index 2fbf1f9..8efab3f 100644 --- a/source/Modules/ConfigMgrCBDsc.ResourceHelper/ConfigMgrCBDsc.ResourceHelper.psd1 +++ b/source/Modules/ConfigMgrCBDsc.ResourceHelper/ConfigMgrCBDsc.ResourceHelper.psd1 @@ -39,6 +39,7 @@ 'ConvertTo-CimBoundaries' 'Convert-BoundariesIPSubnets' 'Get-BoundaryInfo' + 'ConvertTo-ScheduleInterval' ) # Cmdlets to export from this module diff --git a/source/Modules/ConfigMgrCBDsc.ResourceHelper/ConfigMgrCBDsc.ResourceHelper.psm1 b/source/Modules/ConfigMgrCBDsc.ResourceHelper/ConfigMgrCBDsc.ResourceHelper.psm1 index d88c409..65566b2 100644 --- a/source/Modules/ConfigMgrCBDsc.ResourceHelper/ConfigMgrCBDsc.ResourceHelper.psm1 +++ b/source/Modules/ConfigMgrCBDsc.ResourceHelper/ConfigMgrCBDsc.ResourceHelper.psm1 @@ -334,6 +334,42 @@ function Get-BoundaryInfo ($_.Value -eq $Value) }).BoundaryID } +<# + .SYNOPSIS + Returns Interval and count from the CM Schedule. + + .PARAMETER ScheduleString + Specifies the string value of a CM Schedule to convert. +#> +function ConvertTo-ScheduleInterval +{ + [CmdletBinding()] + param + ( + [Parameter(Mandatory = $true)] + [String] + $ScheduleString + ) + + $schedule = Convert-CMSchedule -ScheduleString $ScheduleString + $itemList = @('DaySpan','MinuteSpan','HourSpan') + $recurInterval = 'None' + + foreach ($item in $itemList) + { + if ($schedule.$item -gt 0) + { + $recurInterval = $item.Replace('Span','s') + $recurCount = $schedule.$item + } + } + + return @{ + Interval = $recurInterval + Count = $recurCount + } +} + Export-ModuleMember -Function @( 'Import-ConfigMgrPowerShellModule' 'Convert-CidrToIP' @@ -341,4 +377,5 @@ Export-ModuleMember -Function @( 'ConvertTo-CimBoundaries' 'Convert-BoundariesIPSubnets' 'Get-BoundaryInfo' + 'ConvertTo-ScheduleInterval' ) diff --git a/tests/Unit/CMHeartbeatDiscovery.tests.ps1 b/tests/Unit/CMHeartbeatDiscovery.tests.ps1 new file mode 100644 index 0000000..c88c946 --- /dev/null +++ b/tests/Unit/CMHeartbeatDiscovery.tests.ps1 @@ -0,0 +1,318 @@ +[System.Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingConvertToSecureStringWithPlainText', '')] +param () + +$script:dscModuleName = 'ConfigMgrCBDsc' +$script:dscResourceName = 'DSC_CMHeartbeatDiscovery' + +function Invoke-TestSetup +{ + try + { + Import-Module -Name DscResource.Test -Force -ErrorAction 'Stop' + } + catch [System.IO.FileNotFoundException] + { + throw 'DscResource.Test module dependency not found. Please run ".\build.ps1 -Tasks build" first.' + } + + $script:testEnvironment = Initialize-TestEnvironment ` + -DSCModuleName $script:dscModuleName ` + -DSCResourceName $script:dscResourceName ` + -ResourceType 'Mof' ` + -TestType 'Unit' + + # Import Stub function + $script:moduleRoot = Split-Path -Parent (Split-Path -Parent $PSScriptRoot) + Import-Module (Join-Path -Path $PSScriptRoot -ChildPath 'Stubs\ConfigMgrCBDscStub.psm1') -Force -WarningAction SilentlyContinue +} + +function Invoke-TestCleanup +{ + Restore-TestEnvironment -TestEnvironment $script:testEnvironment +} + +Invoke-TestSetup + +# Begin Testing +try +{ + InModuleScope $script:dscResourceName { + + Describe 'ConfigMgrCBDsc - DSC_CMHeartbeatDiscovery\Get-TargetResource' -Tag 'Get' { + BeforeAll { + $standardGetDiscoveryOutput = @{ + Props = @( + @{ + PropertyName = 'Enable Heartbeat DDR' + Value = 1 + } + @{ + PropertyName = 'DDR Refresh Interval' + Value2 = '0001200000100038' + } + ) + } + + $standardGetInput = @{ + SiteCode = 'Lab' + Enabled = $true + } + + $convertScheduleDays = @{ + Interval = 'Days' + Count = 7 + } + + $convertScheduleHours = @{ + Interval = 'Hours' + Count = 8 + } + + Mock -CommandName Import-ConfigMgrPowerShellModule + Mock -CommandName Set-Location + } + + Context 'When retrieving Collection settings' { + + It 'Should return desired result for hearbeat discovery days' { + Mock -CommandName Get-CMDiscoveryMethod -MockWith { $standardGetDiscoveryOutput } + Mock -CommandName ConvertTo-ScheduleInterval -MockWith { $convertScheduleDays } + + $result = Get-TargetResource @standardGetInput + $result | Should -BeOfType System.Collections.HashTable + $result.SiteCode | Should -Be -ExpectedValue 'Lab' + $result.Enabled | Should -Be -ExpectedValue $true + $result.ScheduleInterval | Should -Be -ExpectedValue 'Days' + $result.ScheduleCount | Should -Be -ExpectedValue 7 + } + + It 'Should return desired result for hearbeat discovery hours' { + Mock -CommandName Get-CMDiscoveryMethod -MockWith { $standardGetDiscoveryOutput } + Mock -CommandName ConvertTo-ScheduleInterval -MockWith { $convertScheduleHours } + + $result = Get-TargetResource @standardGetInput + $result | Should -BeOfType System.Collections.HashTable + $result.SiteCode | Should -Be -ExpectedValue 'Lab' + $result.Enabled | Should -Be -ExpectedValue $true + $result.ScheduleInterval | Should -Be -ExpectedValue 'Hours' + $result.ScheduleCount | Should -Be -ExpectedValue 8 + } + } + } + + Describe 'ConfigMgrCBDsc - DSC_CMHeartbeatDiscovery\Set-TargetResource' -Tag 'Set' { + BeforeAll { + $returnEnabledDaysMismatch = @{ + SiteCode = 'Lab' + Enabled = $true + ScheduleInterval = 'Days' + ScheduleCount = 6 + } + + $getReturnEnabledDays = @{ + SiteCode = 'Lab' + Enabled = $true + ScheduleInterval = 'Days' + ScheduleCount = 7 + } + + $getReturnEnabledHours = @{ + SiteCode = 'Lab' + Enabled = $true + ScheduleInterval = 'Hours' + ScheduleCount = 7 + } + + $getReturnDisabled = @{ + SiteCode = 'Lab' + Enabled = $false + } + + $inputScheduleNoCount = @{ + SiteCode = 'Lab' + Enabled = $true + ScheduleInterval = 'Days' + } + + $standardGetInput = @{ + SiteCode = 'Lab' + Enabled = $true + } + + $scheduleConvertDaysMismatch = @{ + DayDuration = 0 + DaySpan = 6 + HourDuration = 0 + HourSpan = 0 + MinuteDuration = 0 + MinuteSpan = 0 + } + + $scheduleNoCountThrow = 'Invalid parameter usage must specify ScheduleInterval and ScheduleCount.' + + Mock -CommandName Import-ConfigMgrPowerShellModule + Mock -CommandName Set-Location + Mock -CommandName Set-CMDiscoveryMethod + } + + Context 'When Set-TargetResource runs successfully' { + + It 'Should call expected commands enabling discovery' { + Mock -CommandName Get-TargetResource -MockWith { $getReturnDisabled } + Mock -CommandName New-CMSchedule + + Set-TargetResource @standardGetInput + Assert-MockCalled Import-ConfigMgrPowerShellModule -Exactly -Times 1 -Scope It + Assert-MockCalled Set-Location -Exactly -Times 2 -Scope It + Assert-MockCalled Get-TargetResource -Exactly -Times 1 -Scope It + Assert-MockCalled New-CMSchedule -Exactly -Times 0 -Scope It + Assert-MockCalled Set-CMDiscoveryMethod -Exactly -Times 1 -Scope It + } + + It 'Should call expected commands enabling discovery and changing the schedule' { + Mock -CommandName Get-TargetResource -MockWith { $getReturnDisabled } + Mock -CommandName New-CMSchedule -MockWith { $scheduleConvertDaysMismatch } + + Set-TargetResource @returnEnabledDaysMismatch + Assert-MockCalled Import-ConfigMgrPowerShellModule -Exactly -Times 1 -Scope It + Assert-MockCalled Set-Location -Exactly -Times 2 -Scope It + Assert-MockCalled Get-TargetResource -Exactly -Times 1 -Scope It + Assert-MockCalled New-CMSchedule -Exactly -Times 1 -Scope It + Assert-MockCalled Set-CMDiscoveryMethod -Exactly -Times 1 -Scope It + } + + It 'Should call expected commands disabling discovery' { + Mock -CommandName Get-TargetResource -MockWith { $getReturnEnabledDays } + Mock -CommandName New-CMSchedule + + Set-TargetResource @getReturnDisabled + Assert-MockCalled Import-ConfigMgrPowerShellModule -Exactly -Times 1 -Scope It + Assert-MockCalled Set-Location -Exactly -Times 2 -Scope It + Assert-MockCalled Get-TargetResource -Exactly -Times 1 -Scope It + Assert-MockCalled New-CMSchedule -Exactly -Times 0 -Scope It + Assert-MockCalled Set-CMDiscoveryMethod -Exactly -Times 1 -Scope It + } + } + + Context 'When running Set-TargetResource should throw' { + + It 'Should call expected commands and throw when setting schedule interval without count' { + Mock -CommandName Get-TargetResource -MockWith { $getReturnEnabledDays } + Mock -CommandName New-CMSchedule + MOck -CommandName Set-CMDiscoveryMethod + + { Set-TargetResource @inputScheduleNoCount } | Should -Throw -ExpectedMessage $scheduleNoCountThrow + Assert-MockCalled Import-ConfigMgrPowerShellModule -Exactly -Times 1 -Scope It + Assert-MockCalled Set-Location -Exactly -Times 2 -Scope It + Assert-MockCalled Get-TargetResource -Exactly -Times 1 -Scope It + Assert-MockCalled New-CMSchedule -Exactly -Times 0 -Scope It + Assert-MockCalled Set-CMDiscoveryMethod -Exactly -Times 0 -Scope It + } + + It 'Should call expected commands and throw if query membership throws' { + Mock -CommandName Get-TargetResource -MockWith { $getReturnEnabledDays } + Mock -CommandName New-CMSchedule + MOck -CommandName Set-CMDiscoveryMethod -MockWith { throw } + + { Set-TargetResource @getReturnDisabled } | Should -Throw + Assert-MockCalled Import-ConfigMgrPowerShellModule -Exactly -Times 1 -Scope It + Assert-MockCalled Set-Location -Exactly -Times 2 -Scope It + Assert-MockCalled Get-TargetResource -Exactly -Times 1 -Scope It + Assert-MockCalled New-CMSchedule -Exactly -Times 0 -Scope It + Assert-MockCalled Set-CMDiscoveryMethod -Exactly -Times 1 -Scope It + } + + It 'Should call expected commands enabling discovery and changing the schedule' { + Mock -CommandName Get-TargetResource -MockWith { $getReturnDisabled } + Mock -CommandName New-CMSchedule -MockWith { throw } + + { Set-TargetResource @returnEnabledDaysMismatch } | Should -Throw + Assert-MockCalled Import-ConfigMgrPowerShellModule -Exactly -Times 1 -Scope It + Assert-MockCalled Set-Location -Exactly -Times 2 -Scope It + Assert-MockCalled Get-TargetResource -Exactly -Times 1 -Scope It + Assert-MockCalled New-CMSchedule -Exactly -Times 1 -Scope It + Assert-MockCalled Set-CMDiscoveryMethod -Exactly -Times 0 -Scope It + } + } + } + + Describe 'ConfigMgrCBDsc - DSC_CMHeartbeatDiscovery\Test-TargetResource' -Tag 'Test' { + BeforeAll { + $returnEnabledDaysMismatch = @{ + SiteCode = 'Lab' + Enabled = $true + ScheduleInterval = 'Days' + ScheduleCount = 6 + } + + $getReturnEnabledDays = @{ + SiteCode = 'Lab' + Enabled = $true + ScheduleInterval = 'Days' + ScheduleCount = 7 + } + + $getReturnEnabledHours = @{ + SiteCode = 'Lab' + Enabled = $true + ScheduleInterval = 'Hours' + ScheduleCount = 7 + } + + $getReturnDisabled = @{ + SiteCode = 'Lab' + Enabled = $false + } + + $inputScheduleNoCount = @{ + SiteCode = 'Lab' + Enabled = $true + ScheduleInterval = 'Days' + } + + Mock -CommandName Import-ConfigMgrPowerShellModule + Mock -CommandName Set-Location + } + + Context 'When running Test-TargetResource device settings and Heartbeat Discovery is enabled' { + BeforeEach { + Mock -CommandName Get-TargetResource -MockWith { $getReturnEnabledDays } + } + + It 'Should return desired result true schedule matches' { + Test-TargetResource @getReturnEnabledDays | Should -Be $true + } + + It 'Should return desired result false schedule days mismatch' { + Test-TargetResource @returnEnabledDaysMismatch | Should -Be $false + } + + It 'Should return desired result false schedule hours mismatch' { + Test-TargetResource @getReturnEnabledHours | Should -Be $false + } + + It 'Should return desired result false when setting is enabled and disabled expected disabled' { + Test-TargetResource @getReturnDisabled | Should -Be $false + } + + It 'Should return desired result false when setting schedule interval without count' { + Test-TargetResource @inputScheduleNoCount | Should -Be $false + } + } + + Context 'When running Test-TargetResource device settings and Heartbeat Discovery is disabled' { + BeforeEach { + Mock -CommandName Get-TargetResource -MockWith { $getReturnDisabled } + } + + It 'Should return desired result true when discovery is disabled' { + Test-TargetResource @getReturnDisabled | Should -Be $true + } + } + } + } +} +finally +{ + Invoke-TestCleanup +} diff --git a/tests/Unit/ConfigMgrCBDsc.ResourceHelper.tests.ps1 b/tests/Unit/ConfigMgrCBDsc.ResourceHelper.tests.ps1 index 8804f81..ff18ea5 100644 --- a/tests/Unit/ConfigMgrCBDsc.ResourceHelper.tests.ps1 +++ b/tests/Unit/ConfigMgrCBDsc.ResourceHelper.tests.ps1 @@ -347,4 +347,104 @@ InModuleScope $script:subModuleName { } } } + + Describe "$moduleResourceName\ConvertTo-ScheduleInterval" { + BeforeAll { + $inputdays = @{ + ScheduleString = '0001200000100020' + } + + $daysReturn = @{ + DayDuration = 0 + DaySpan = 4 + HourDuration = 0 + HourSpan = 0 + IsGMT = $False + MinuteDuration = 0 + MinuteSpan = 0 + StartTime = '2/1/1970 12:00:00 AM' + } + + $inputHours = @{ + ScheduleString = '0001200000100400' + } + + $hoursReturn = @{ + DayDuration = 0 + DaySpan = 0 + HourDuration = 0 + HourSpan = 4 + IsGMT = $False + MinuteDuration = 0 + MinuteSpan = 0 + StartTime = '2/1/1970 12:00:00 AM' + } + + $inputMins = @{ + ScheduleString = '0001200000164000' + } + + $minsReturn = @{ + DayDuration = 0 + DaySpan = 0 + HourDuration = 0 + HourSpan = 0 + IsGMT = $False + MinuteDuration = 0 + MinuteSpan = 50 + StartTime = '2/1/1970 12:00:00 AM' + } + + $inputNonrecurring = @{ + ScheduleString = '0001200000080000' + } + + $nonrecurringReturn = @{ + DayDuration = 0 + HourDuration = 0 + IsGMT = $False + MinuteDuration = 0 + StartTime = '2/1/1970 12:00:00 AM' + } + } + + Context 'When results are as expected' { + + It 'Should return desired output for days' { + Mock -CommandName Convert-CMSchedule -MockWith { $daysReturn } + + $result = ConvertTo-ScheduleInterval @inputdays + $result.Interval | Should -Be -ExpectedValue 'Days' + $result.Count | Should -Be -ExpectedValue 4 + Assert-MockCalled Convert-CMSchedule -Exactly -Times 1 -Scope It + } + + It 'Should return desired output for hours' { + Mock -CommandName Convert-CMSchedule -MockWith { $hoursReturn } + + $result = ConvertTo-ScheduleInterval @inputHours + $result.Interval | Should -Be -ExpectedValue 'Hours' + $result.Count | Should -Be -ExpectedValue 4 + Assert-MockCalled Convert-CMSchedule -Exactly -Times 1 -Scope It + } + + It 'Should return desired output for minutes' { + Mock -CommandName Convert-CMSchedule -MockWith { $minsReturn } + + $result = ConvertTo-ScheduleInterval @inputMins + $result.Interval | Should -Be -ExpectedValue 'Minutes' + $result.Count | Should -Be -ExpectedValue 50 + Assert-MockCalled Convert-CMSchedule -Exactly -Times 1 -Scope It + } + + It 'Should return desired output for schedule set to none' { + Mock -CommandName Convert-CMSchedule -MockWith { $nonrecurringReturn } + + $result = ConvertTo-ScheduleInterval @inputNonrecurring + $result.Interval | Should -Be -ExpectedValue 'None' + $result.Count | Should -Be -ExpectedValue $null + Assert-MockCalled Convert-CMSchedule -Exactly -Times 1 -Scope It + } + } + } }