DSCResources/DSC_CMSoftwareUpdatePointComponent/DSC_CMSoftwareUpdatePointComponent.psm1

$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 a site code for the Configuration Manager site that manages the system role for the software update point component.
 
    .Notes
        This component is only fully functional at the top level of the hierarchy. Downlevel Sites inherit most properties from top level.
#>

function Get-TargetResource
{
    [CmdletBinding()]
    [OutputType([System.Collections.Hashtable])]
    param
    (
        [Parameter(Mandatory = $true)]
        [String]
        $SiteCode
    )

    Write-Verbose -Message $script:localizedData.RetrieveSettingValue
    Import-ConfigMgrPowerShellModule -SiteCode $SiteCode
    Set-Location -Path "$($SiteCode):\"

    if ([string]::IsNullOrEmpty((Get-CMSite -SiteCode $SiteCode).ReportingSiteCode))
    {
        $child = $false
    }
    else
    {
        $child = $true
    }

    $supConfig = Get-CMSoftwareUpdatePointComponent -SiteCode $SiteCode

    if ($supConfig)
    {
        $supConfigProps = $supConfig.Props

        foreach ($supConfigProp in $supConfigProps)
        {
            switch ($supConfigProp.PropertyName)
            {
                'Call WSUS Cleanup'                     { $wsusCleanup = $supConfigProp.Value }
                'ClientReportingLevel'                  { $reportLevel = @('DoNotCreateWsusReportingEvents','CreateOnlyWsusStatusReportingEvents','CreateAllWsusReportingEvents')[($supConfigProp.Value)] }
                'DefaultWSUS'                           { $defaultWsus = $supConfigProp.Value2 }
                'DefaultUseParentWSUS'                  { $syncAction = @('SynchronizeFromMicrosoftUpdate','SynchronizeFromAnUpstreamDataSourceLocation','DoNotSynchronizeFromMicrosoftUpdateOrUpstreamDataSource')[($supConfigProp.Value)] }
                'ParentWSUS'                            { $parentWsus = 'http://' + $supConfigProp.Value2 }
                'SupportedTitleLanguages'               { $titleLanguages = $supConfigProp.Value2 }
                'SupportedUpdateLanguages'              { $updateLanguages = $supConfigProp.Value2 }
                'Sync Supersedence Age For Feature'     { $supAgeFeature = $supConfigProp.Value }
                'Sync Supersedence Age For NonFeature'  { $supAge = $supConfigProp.Value }
                'Sync Supersedence Mode For Feature'    { $supModeFeature = @($true,$false)[($supConfigProp.Value)] }
                'Sync Supersedence Mode For NonFeature' { $supMode = @($true,$false)[($supConfigProp.Value)] }
            }
        }
    }

    $languageTitles = @()
    $titleLangs = $titleLanguages.Split(',')

    foreach ($lang in $titleLangs)
    {
        $langName = [System.Globalization.CultureInfo]::GetCultures('InstalledWin32Cultures') | Where-Object -FilterScript {$_.Name -eq $lang}
        $fullName = $langName.DisplayName
        $languageTitles += $fullName
    }

    $languageUpdates = @()
    $updateLangs = $updateLanguages.Split(',')

    foreach ($lang in $updateLangs)
    {
        $langName = [System.Globalization.CultureInfo]::GetCultures('InstalledWin32Cultures') | Where-Object -FilterScript {$_.Name -eq $lang}
        $fullName = $langName.DisplayName
        $languageUpdates += $fullName
    }

    $wsusSyncProps = (Get-CMSiteComponent -SiteCode $SiteCode -ComponentName SMS_WSUS_Sync_Manager).Props

    foreach ($wsusSyncProp in $wsusSyncProps)
    {
        switch ($wsusSyncProp.PropertyName)
        {
            'Sync ExpressFiles'         {
                                            if ($wsusSyncProp.Value -eq '0')
                                            {
                                                $syncExpress = 'FullFilesOnly'
                                            }
                                            else
                                            {
                                                $syncExpress = 'ExpressForWindows10Only'
                                            }
                                        }
            'Sync Schedule'             {
                                            $syncSchedule = $wsusSyncProp.Value1
                                            if ([string]::IsNullOrEmpty($wsusSyncProp.Value1))
                                            {
                                                $enableSync = $false
                                            }
                                            else
                                            {
                                                $enableSync = $true
                                            }
                                        }
            'EnableThirdPartyUpdates'   {
                                            if ($wsusSyncProp.Value -eq '0')
                                            {
                                                $thirdParty = $false
                                            }
                                            elseif ($wsusSyncProp.Value -eq '1')
                                            {
                                                $thirdParty = $true
                                                $manualCert = $true
                                            }
                                            else
                                            {
                                                $thirdParty = $true
                                                $manualCert = $false
                                            }
                                         }
            'MaxInstallTime-ServicePack' { $fInstallTime = ($wsusSyncProp.Value)/60 }
            'MaxInstallTime-Windows'     { $uInstallTime = ($wsusSyncProp.Value)/60 }
        }
    }

    if (-not [string]::IsNullOrEmpty($syncSchedule))
    {
        $schedule = Get-CMSchedule -ScheduleString $syncSchedule
    }

    $syncFailureAlert = Get-CMAlert | Where-Object -FilterScript {$_.Name -match "Synchronization failure alert for software update point:"}

    if ($syncFailureAlert)
    {
        $enableFailureAlert = $true
    }
    else
    {
        $enableFailureAlert = $false
    }

    $available = (Get-CMSoftwareUpdateCategory -Fast)
    $updateCats = ($available | Where-Object -FilterScript {($_.SourceSite -eq $SiteCode) -and ($_.IsSubscribed -eq $true)})
    $products = @()
    $classifications = @()
    foreach ($cat in $updateCats)
    {
        switch ($cat.CategoryTypeName)
        {
            'Product'              { $products += $cat.LocalizedCategoryInstanceName }
            'UpdateClassification' { $classifications += $cat.LocalizedCategoryInstanceName }
        }
    }

    return @{
        SiteCode                                = $SiteCode
        ContentFileOption                       = $syncExpress
        DefaultWsusServer                       = $defaultWsus
        EnableCallWsusCleanupWizard             = $wsusCleanup
        EnableSyncFailureAlert                  = $enableFailureAlert
        EnableSynchronization                   = $enableSync
        ImmediatelyExpireSupersedence           = $supMode
        ImmediatelyExpireSupersedenceForFeature = $supModeFeature
        LanguageUpdateFiles                     = $languageUpdates
        LanguageSummaryDetails                  = $languageTitles
        ReportingEvent                          = $reportLevel
        Start                                   = $schedule.Start
        ScheduleType                            = $schedule.ScheduleType
        DayOfWeek                               = $schedule.DayofWeek
        MonthlyWeekOrder                        = $schedule.WeekOrder
        DayofMonth                              = $schedule.MonthDay
        RecurInterval                           = $schedule.RecurInterval
        Products                                = $products
        UpdateClassifications                   = $classifications
        SynchronizeAction                       = $syncAction
        UpstreamSourceLocation                  = $parentWsus
        WaitMonth                               = $supAge
        WaitMonthForFeature                     = $supAgeFeature
        EnableThirdPartyUpdates                 = $thirdParty
        EnableManualCertManagement              = $manualCert
        FeatureUpdateMaxRuntimeMins             = $fInstallTime
        NonFeatureUpdateMaxRuntimeMins          = $uInstallTime
        ChildSite                               = $child
        AvailableCats                           = $available.LocalizedCategoryInstanceName
    }
}

<#
    .SYNOPSIS
        This will set the desired state.
 
    .PARAMETER SiteCode
        Specifies a site code for the Configuration Manager site that manages the system role for the software update point component.
 
    .PARAMETER LanguageSummaryDetails
        Specifies an array of languages desired for the languages supported for software updates summary details at the specified site.
 
    .PARAMETER LanguageSummaryDetailsToInclude
        Specifies an array of languages to include in the languages supported for software updates summary details at the specified site.
 
    .PARAMETER LanguageSummaryDetailsToExclude
        Specifies an array of languages to exclude from the languages supported for software updates summary details at the specified site.
 
    .PARAMETER LanguageUpdateFiles
        Specifies an array of languages desired for the languages supported for software updates at the specified site.
 
    .PARAMETER LanguageUpdateFilesToInclude
        Specifies an array of languages to include in the languages supported for software updates at the specified site.
 
    .PARAMETER LanguageUpdateFilesToExclude
        Specifies an array of languages to exclude from the languages supported for software updates at the specified site.
 
    .PARAMETER Products
        Specifies an array of products desired for software updates to synchronize.
 
    .PARAMETER ProductsToInclude
        Specifies an array of products to include in software updates to synchronize.
 
    .PARAMETER ProductsToExclude
        Specifies an array of products to exclude from software updates to synchronize.
 
    .PARAMETER UpdateClassifications
        Specifies an array of software update classifications desired for the classifications supported for software updates at this site.
 
    .PARAMETER UpdateClassificationsToInclude
        Specifies an array of software update classifications to include in the classifications supported for software updates at this site.
 
    .PARAMETER UpdateClassificationsToExclude
        Specifies an array of software update classifications to exclude from the classifications supported for software updates at this site.
 
    .PARAMETER ContentFileOption
        Specifies whether express updates will be downloaded for Windows 10. The acceptable values for this parameter are:
 
        FullFilesOnly
        ExpressForWindows10Only
 
    .PARAMETER DefaultWsusServer
        Specifies the default WSUS server that the software update point is pointed to.
 
    .PARAMETER EnableCallWsusCleanupWizard
        Specifies whether to decline expired updates in WSUS according to superscedence rules.
 
    .PARAMETER EnableSyncFailureAlert
        Specifies whether Configuration Manager creates an alert when synchronization fails on a site.
 
    .PARAMETER EnableSynchronization
        Indicates whether this site automatically synchronizes updates according to a schedule.
 
    .PARAMETER ImmediatelyExpireSupersedence
        Indicates whether a software update expires immediately after another update supersedes it or after a specified period of time.
        If you specify a value of $false for this parameter, specify the number of months to wait for expiration by using the WaitMonth parameter.
        If you specify a value of $true for this parameter, do not specify the WaitMonth parameter.
 
    .PARAMETER ImmediatelyExpireSupersedenceForFeature
        Indicates whether a feature update expires immediately after another update supersedes it or after a specified period of time.
        If you specify a value of $false for this parameter, specify the number of months to wait for expiration by using the WaitMonthForFeature parameter.
        If you specify a value of $true for this parameter, do not specify the WaitMonthForFeature parameter.
 
    .PARAMETER ReportingEvent
        Specifies whether to create event messages for WSUS reporting for status reporting events or for all reporting events. The acceptable values for this parameter are:
 
        CreateAllWsusReportingEvents
        CreateOnlyWsusStatusReportingEvents
        DoNotCreateWsusReportingEvents
 
    .PARAMETER SynchronizeAction
        Specifies a source for synchronization for this software update point. The acceptable values for this parameter are:
 
        SynchronizeFromMicrosoftUpdate
        SynchronizeFromAnUpstreamDataSourceLocation
        DoNotSynchronizeFromMicrosoftUpdateOrUpstreamDataSource
 
        If you select a value of SynchronizeFromAnUpstreamDataSourceLocation, specify the data source location by using the UpstreamSourceLocation parameter.
 
    .PARAMETER UpstreamSourceLocation
        Specifies an upstream data location as a URL.
        To use this location, specify a value of SynchronizeFromAnUpstreamDataSourceLocation for the SynchronizeAction parameter.
 
    .PARAMETER WaitMonth
        Specifies how long, in months, to wait before a software update expires after another update supersedes it.
        Specify a value of $True for the ImmediatelyExpireSupersedence parameter for software updates to expire immediately.
        If $True is specified for the ImmediatelyExpireSupersedence parameter, do not use this parameter.
 
    .PARAMETER WaitMonthForFeature
        Specifies how long, in months, to wait before a feature update expires after another update supersedes it.
        Specify a value of $True for the ImmediatelyExpireSupersedenceForFeature parameter for software updates to expire immediately.
        If $True is specified for the ImmediatelyExpireSupersedenceForFeature parameter, do not use this parameter.
 
    .PARAMETER Start
        Specifies the start date and start time for the synchronization schedule Month/Day/Year, example 1/1/2020 02:00.
 
    .PARAMETER ScheduleType
        Specifies the schedule type for the synchronization schedule.
 
    .PARAMETER RecurInterval
        Specifies how often the ScheduleType is run.
 
    .PARAMETER MonthlyWeekOrder
        Specifies week order for MonthlyByWeek schedule type.
 
    .PARAMETER DayOfWeek
        Specifies the day of week name for MonthlyByWeek and Weekly schedules.
 
    .PARAMETER DayOfMonth
        Specifies the day number for MonthlyByDay schedules.
        Note specifying 0 sets the schedule to run the last day of the month.
 
    .PARAMETER EnableManualCertManagement
        Specifies whether manual management of the WSUS signing certificate is enabled.
 
    .PARAMETER EnableThirdPartyUpdates
        Specifies whether third-party updates are enabled on the Software Update Point Component.
 
    .PARAMETER FeatureUpdateMaxRuntimeMins
        Specifies the maximum runtime, in minutes, for windows feature updates.
 
    .PARAMETER NonFeatureUpdateMaxRuntimeMins
        Specifies the maximum runtime, in minutes, for Office 365 updates and windows non-feature updates.
 
    .Notes
        After disabling or enabling synchronization, the GUI is slow to update, but the values are set correctly.
#>

function Set-TargetResource
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [String]
        $SiteCode,

        [Parameter()]
        [String[]]
        $LanguageSummaryDetails,

        [Parameter()]
        [String[]]
        $LanguageSummaryDetailsToInclude,

        [Parameter()]
        [String[]]
        $LanguageSummaryDetailsToExclude,

        [Parameter()]
        [String[]]
        $LanguageUpdateFiles,

        [Parameter()]
        [String[]]
        $LanguageUpdateFilesToInclude,

        [Parameter()]
        [String[]]
        $LanguageUpdateFilesToExclude,

        [Parameter()]
        [String[]]
        $Products,

        [Parameter()]
        [String[]]
        $ProductsToInclude,

        [Parameter()]
        [String[]]
        $ProductsToExclude,

        [Parameter()]
        [String[]]
        $UpdateClassifications,

        [Parameter()]
        [String[]]
        $UpdateClassificationsToInclude,

        [Parameter()]
        [String[]]
        $UpdateClassificationsToExclude,

        [Parameter()]
        [ValidateSet('FullFilesOnly', 'ExpressForWindows10Only')]
        [String]
        $ContentFileOption,

        [Parameter()]
        [String]
        $DefaultWsusServer,

        [Parameter()]
        [Boolean]
        $EnableCallWsusCleanupWizard,

        [Parameter()]
        [Boolean]
        $EnableSyncFailureAlert,

        [Parameter()]
        [Boolean]
        $EnableSynchronization,

        [Parameter()]
        [Boolean]
        $ImmediatelyExpireSupersedence,

        [Parameter()]
        [Boolean]
        $ImmediatelyExpireSupersedenceForFeature,

        [Parameter()]
        [ValidateSet('CreateAllWsusReportingEvents', 'CreateOnlyWsusStatusReportingEvents', 'DoNotCreateWsusReportingEvents')]
        [String]
        $ReportingEvent,

        [Parameter()]
        [ValidateSet('SynchronizeFromMicrosoftUpdate', 'SynchronizeFromAnUpstreamDataSourceLocation', 'DoNotSynchronizeFromMicrosoftUpdateOrUpstreamDataSource')]
        [String]
        $SynchronizeAction,

        [Parameter()]
        [String]
        $UpstreamSourceLocation,

        [Parameter()]
        [ValidateRange(1,99)]
        [UInt32]
        $WaitMonth,

        [Parameter()]
        [ValidateRange(1,99)]
        [UInt32]
        $WaitMonthForFeature,

        [Parameter()]
        [String]
        $Start,

        [Parameter()]
        [ValidateSet('MonthlyByDay','MonthlyByWeek','Weekly','Days','Hours')]
        [String]
        $ScheduleType,

        [Parameter()]
        [ValidateRange(1,31)]
        [UInt32]
        $RecurInterval,

        [Parameter()]
        [ValidateSet('First','Second','Third','Fourth','Last')]
        [String]
        $MonthlyWeekOrder,

        [Parameter()]
        [ValidateSet('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday')]
        [String]
        $DayOfWeek,

        [Parameter()]
        [ValidateRange(0,31)]
        [UInt32]
        $DayOfMonth,

        [Parameter()]
        [Boolean]
        $EnableManualCertManagement,

        [Parameter()]
        [Boolean]
        $EnableThirdPartyUpdates,

        [Parameter()]
        [ValidateRange(5,9999)]
        [UInt32]
        $FeatureUpdateMaxRuntimeMins,

        [Parameter()]
        [ValidateRange(5,9999)]
        [UInt32]
        $NonFeatureUpdateMaxRuntimeMins
    )

    Import-ConfigMgrPowerShellModule -SiteCode $SiteCode
    Set-Location -Path "$($SiteCode):\"
    $state = Get-TargetResource -SiteCode $SiteCode

    try
    {
        $nonChildParams = @('LanguageSummaryDetails','LanguageSummaryDetailsToInclude','LanguageSummaryDetailsToExclude','Products','ProductsToInclude','ProductsToExclude','UpdateClassifications',
        'UpdateClassificationsToInclude','UpdateClassificationsToExclude','ContentFileOption','DefaultWsusServer','EnableCallWsusCleanupWizard','EnableSyncFailureAlert','EnableSynchronization',
        'ImmediatelyExpireSupersedence','ImmediatelyExpireSupersedenceForFeature','SynchronizeAction','UpstreamSourceLocation','WaitMonth','WaitMonthForFeature','EnableThirdPartyUpdates',
        'EnableManualCertManagement','FeatureUpdateMaxRuntimeMins','NonFeatureUpdateMaxRuntimeMins','ScheduleType','RecurInterval','MonthlyWeekOrder','DayOfWeek','DayOfMonth','Start')

        $availableLangs = @('Arabic','Bulgarian','Chinese (Simplified, PRC)','Chinese (Traditional, Hong Kong S.A.R.)','Chinese (Traditional, Taiwan)','Croatian','Czech','Danish','Dutch','English',
        'Estonian','Finnish','French','German','Greek','Hebrew','Hindi','Hungarian','Italian','Japanese','Korean','Latvian','Lithuanian','Norwegian','Polish','Portuguese','Portuguese (Brazil)',
        'Romanian','Russian','Serbian','Slovak','Slovenian','Spanish','Swedish','Thai','Turkish','Ukrainian')

        $langs = $LanguageUpdateFiles + $LanguageUpdateFilesToInclude + $LanguageUpdateFilesToExclude + $LanguageSummaryDetails + $LanguageSummaryDetailsToInclude + $LanguageSummaryDetailsToExclude
        $cats = $Products + $ProductsToInclude + $ProductsToExclude + $UpdateClassifications + $UpdateClassificationsToInclude + $UpdateClassificationsToExclude

        foreach ($lang in $langs.Where({ $_ -ne $null }))
        {
            if ($availableLangs -notcontains $lang)
            {
                throw ($script:localizedData.InvalidLangs -f $lang)
            }
        }

        foreach ($cat in $cats.Where({ $_ -ne $null }))
        {
            if ($state.AvailableCats -notcontains $cat)
            {
                throw ($script:localizedData.InvalidCats -f $cat)
            }
        }

        if ($state.ChildSite -eq $true)
        {
            foreach ($child in $nonChildParams)
            {
                if ($PSBoundParameters.ContainsKey($child))
                {
                    Write-Warning -Message ($script:localizedData.ChildBadParams -f $child)
                }
            }

            if ($LanguageUpdateFiles -or $LanguageUpdateFilesToInclude -or $LanguageUpdateFilesToExclude)
            {
                if ($LanguageUpdateFilesToInclude -and $LanguageUpdateFilesToExclude)
                {
                    foreach ($item in $LanguageUpdateFilesToInclude)
                    {
                        if ($LanguageUpdateFilesToExclude -contains $item)
                        {
                            throw ($script:localizedData.LangFilesInEx -f $item)
                        }
                    }
                }

                $langFileArray = @{
                    Match        = $LanguageUpdateFiles
                    Include      = $LanguageUpdateFilesToInclude
                    Exclude      = $LanguageUpdateFilesToExclude
                    CurrentState = $state.LanguageUpdateFiles
                }

                $langFileCompare = Compare-MultipleCompares @langFileArray

                if ($langFileCompare.Missing)
                {
                    $buildingParams += @{
                        AddLanguageUpdateFile = $langFileCompare.Missing
                    }
                }

                if ($langFileCompare.Remove)
                {
                    $buildingParams += @{
                        RemoveLanguageUpdateFile = $langFileCompare.Remove
                    }
                }

                if ($state.LanguageUpdateFiles.Count + $langFileCompare.Missing.Count - $langFileCompare.Remove.Count -eq 0)
                {
                    throw $script:localizedData.AllUpdateRemoved
                }
            }

            if ($ReportingEvent -ne $state.ReportingEvent)
            {
                Write-Verbose -Message ($script:localizedData.SettingValue -f 'ReportingEvent', $ReportingEvent)
                $buildingParams += @{
                    ReportingEvent = $ReportingEvent
                }
            }
        }
        else
        {
            if (($PSBoundParameters.ImmediatelyExpireSupersedence -eq $false) -and
               (-not $PSBoundParameters.ContainsKey('WaitMonth')))
            {
                throw $script:localizedData.WaitMonthNull
            }

            if (($PSBoundParameters.ImmediatelyExpireSupersedence -eq $true) -and
               ($PSBoundParameters.ContainsKey('WaitMonth')))
            {
                throw $script:localizedData.WaitMonthNeeded
            }

            if (($PSBoundParameters.ImmediatelyExpireSupersedenceForFeature -eq $false) -and
               (-not $PSBoundParameters.ContainsKey('WaitMonthForFeature')))
            {
                throw $script:localizedData.WaitFeatureNull
            }

            if (($PSBoundParameters.ImmediatelyExpireSupersedenceForFeature -eq $true) -and
               ($PSBoundParameters.ContainsKey('WaitMonthForFeature')))
            {
                throw $script:localizedData.WaitFeatureNeeded
            }

            if (($PSBoundParameters.SynchronizeAction -eq 'SynchronizeFromAnUpstreamDataSourceLocation') -and
               (-not $PSBoundParameters.ContainsKey('UpstreamSourceLocation')))
            {
                throw $script:localizedData.UpstreamSourceNull
            }

            if (($PSBoundParameters.EnableThirdPartyUpdates -eq $false) -and
               ($PSBoundParameters.ContainsKey('EnableManualCertManagement')))
            {
                throw $script:localizedData.CertMgmtSpecified
            }

            if (($PSBoundParameters.EnableSynchronization -eq $false) -and
               ($PSBoundParameters.ContainsKey('ScheduleType')))
            {
                throw $script:localizedData.ScheduleNoSync
            }

            if (($PSBoundParameters.EnableSynchronization -eq $true) -and
               (-not $PSBoundParameters.ContainsKey('ScheduleType')))
            {
                throw $script:localizedData.SyncNoSchedule
            }

            if ((-not $PSBoundParameters.ContainsKey('ScheduleType')) -and ($PSBoundParameters.ContainsKey('Start') -or
                $PSBoundParameters.ContainsKey('RecurInterval') -or $PSBoundParameters.ContainsKey('MonthlyWeekOrder') -or
                $PSBoundParameters.ContainsKey('DayOfWeek') -or $PSBoundParameters.ContainsKey('DayOfMonth')))
            {
                throw $script:localizedData.MissingScheduleType
            }

            if ($LanguageSummaryDetails -or $LanguageSummaryDetailsToInclude -or $LanguageSummaryDetailsToExclude)
            {
                if ($LanguageSummaryDetailsToInclude -and $LanguageSummaryDetailsToExclude)
                {
                    foreach ($item in $LanguageSummaryDetailsToInclude)
                    {
                        if ($LanguageSummaryDetailsToExclude -contains $item)
                        {
                            throw ($script:localizedData.LangSumInEx -f $item)
                        }
                    }
                }

                $langSumArray = @{
                    Match        = $LanguageSummaryDetails
                    Include      = $LanguageSummaryDetailsToInclude
                    Exclude      = $LanguageSummaryDetailsToExclude
                    CurrentState = $state.LanguageSummaryDetails
                }

                $langSumCompare = Compare-MultipleCompares @langSumArray

                if ($langSumCompare.Missing)
                {
                    $buildingParams += @{
                        AddLanguageSummaryDetail = $langSumCompare.Missing
                    }
                }

                if ($langSumCompare.Remove)
                {
                    $buildingParams += @{
                        RemoveLanguageSummaryDetail = $langSumCompare.Remove
                    }
                }

                if ($state.LanguageSummaryDetails.Count + $langSumCompare.Missing.Count - $langSumCompare.Remove.Count -eq 0)
                {
                    throw $script:localizedData.AllSummaryRemoved
                }
            }

            if ($LanguageUpdateFiles -or $LanguageUpdateFilesToInclude -or $LanguageUpdateFilesToExclude)
            {
                if ($LanguageUpdateFilesToInclude -and $LanguageUpdateFilesToExclude)
                {
                    foreach ($item in $LanguageUpdateFilesToInclude)
                    {
                        if ($LanguageUpdateFilesToExclude -contains $item)
                        {
                            throw ($script:localizedData.LangFilesInEx -f $item)
                        }
                    }
                }

                $langFileArray = @{
                    Match        = $LanguageUpdateFiles
                    Include      = $LanguageUpdateFilesToInclude
                    Exclude      = $LanguageUpdateFilesToExclude
                    CurrentState = $state.LanguageUpdateFiles
                }

                $langFileCompare = Compare-MultipleCompares @langFileArray

                if ($langFileCompare.Missing)
                {
                    $buildingParams += @{
                        AddLanguageUpdateFile = $langFileCompare.Missing
                    }
                }

                if ($langFileCompare.Remove)
                {
                    $buildingParams += @{
                        RemoveLanguageUpdateFile = $langFileCompare.Remove
                    }
                }

                if ($state.LanguageUpdateFiles.Count + $langFileCompare.Missing.Count - $langFileCompare.Remove.Count -eq 0)
                {
                    throw $script:localizedData.AllUpdateRemoved
                }
            }

            if ($Products -or $ProductsToInclude -or $ProductsToExclude)
            {
                if ($ProductsToInclude -and $ProductsToExclude)
                {
                    foreach ($item in $ProductsToInclude)
                    {
                        if ($ProductsToExclude -contains $item)
                        {
                            throw ($script:localizedData.ProductsInEx -f $item)
                        }
                    }
                }

                $productsArray = @{
                    Match        = $Products
                    Include      = $ProductsToInclude
                    Exclude      = $ProductsToExclude
                    CurrentState = $state.Products
                }

                $productsCompare = Compare-MultipleCompares @productsArray

                if ($productsCompare.Missing)
                {
                    $buildingParams += @{
                        AddProduct = $productsCompare.Missing
                    }
                }

                if ($productsCompare.Remove)
                {
                    $buildingParams += @{
                        RemoveProduct = $productsCompare.Remove
                    }
                }

            }

            if ($UpdateClassifications -or $UpdateClassificationsToInclude -or $UpdateClassificationsToExclude)
            {
                if ($UpdateClassificationsToInclude -and $UpdateClassificationsToExclude)
                {
                    foreach ($item in $UpdateClassificationsToInclude)
                    {
                        if ($UpdateClassificationsToExclude -contains $item)
                        {
                            throw ($script:localizedData.UpdateClassInEx -f $item)
                        }
                    }
                }

                $classArray = @{
                    Match        = $UpdateClassifications
                    Include      = $UpdateClassificationsToInclude
                    Exclude      = $UpdateClassificationsToExclude
                    CurrentState = $state.UpdateClassifications
                }

                $classCompare = Compare-MultipleCompares @classArray

                if ($classCompare.Missing)
                {
                    $buildingParams += @{
                        AddUpdateClassification = $classCompare.Missing
                    }
                }

                if ($classCompare.Remove)
                {
                    $buildingParams += @{
                        RemoveUpdateClassification = $classCompare.Remove
                    }
                }

            }

            if ($ScheduleType)
            {
                $valuesToValidate = @('ScheduleType','RecurInterval','MonthlyWeekOrder','DayOfWeek','DayOfMonth','Start')
                foreach ($item in $valuesToValidate)
                {
                    if ($PSBoundParameters.ContainsKey($item))
                    {
                        $scheduleCheck += @{
                            $item = $PSBoundParameters[$item]
                        }
                    }
                }

                $schedResult = Test-CMSchedule @scheduleCheck -State $state

                if ($schedResult -eq $false)
                {
                    $sched = Set-CMSchedule @scheduleCheck
                    $newSchedule = New-CMSchedule @sched

                    Write-Verbose -Message $script:localizedData.NewSchedule
                    $buildingParams += @{
                        Schedule = $newSchedule
                    }
                }
            }

            $evalList = @('ContentFileOption','DefaultWsusServer','EnableCallWsusCleanupWizard','EnableSyncFailureAlert','EnableSynchronization',
                'ImmediatelyExpireSupersedence','ImmediatelyExpireSupersedenceForFeature','ReportingEvent','SynchronizeAction','UpstreamSourceLocation',
                'WaitMonth','WaitMonthForFeature','EnableThirdPartyUpdates','EnableManualCertManagement','FeatureUpdateMaxRuntimeMins','NonFeatureUpdateMaxRuntimeMins')

            foreach ($param in $PSBoundParameters.GetEnumerator())
            {
                if ($evalList -contains $param.Key)
                {
                    if ($param.Value -ne $state[$param.Key])
                    {
                        Write-Verbose -Message ($script:localizedData.SettingValue -f $param.Key, $param.Value)
                        $buildingParams += @{
                            $param.Key = $param.Value
                        }
                    }

                    if (($param.Value -ne $state[$param.Key]) -and ($param.Key -eq 'EnableSynchronization') -and ($param.Value -eq $false))
                    {
                        Write-Verbose -Message ($script:localizedData.SettingValue -f $param.Key, $param.Value)
                        $buildingParams += @{
                            Schedule = $null
                        }
                    }
                }
            }
        }

        if ($buildingParams)
        {
            Set-CMSoftwareUpdatePointComponent @buildingParams
        }
    }
    catch
    {
        throw $_
    }
    finally
    {
        Set-Location -Path "$env:temp"
    }
}

<#
    .SYNOPSIS
        This will test the desired state.
 
    .PARAMETER SiteCode
        Specifies a site code for the Configuration Manager site that manages the system role for the software update point component.
 
    .PARAMETER LanguageSummaryDetails
        Specifies an array of languages desired for the languages supported for software updates summary details at the specified site.
 
    .PARAMETER LanguageSummaryDetailsToInclude
        Specifies an array of languages to include in the languages supported for software updates summary details at the specified site.
 
    .PARAMETER LanguageSummaryDetailsToExclude
        Specifies an array of languages to exclude from the languages supported for software updates summary details at the specified site.
 
    .PARAMETER LanguageUpdateFiles
        Specifies an array of languages desired for the languages supported for software updates at the specified site.
 
    .PARAMETER LanguageUpdateFilesToInclude
        Specifies an array of languages to include in the languages supported for software updates at the specified site.
 
    .PARAMETER LanguageUpdateFilesToExclude
        Specifies an array of languages to exclude from the languages supported for software updates at the specified site.
 
    .PARAMETER Products
        Specifies an array of products desired for software updates to synchronize.
 
    .PARAMETER ProductsToInclude
        Specifies an array of products to include in software updates to synchronize.
 
    .PARAMETER ProductsToExclude
        Specifies an array of products to exclude from software updates to synchronize.
 
    .PARAMETER UpdateClassifications
        Specifies an array of software update classifications desired for the classifications supported for software updates at this site.
 
    .PARAMETER UpdateClassificationsToInclude
        Specifies an array of software update classifications to include in the classifications supported for software updates at this site.
 
    .PARAMETER UpdateClassificationsToExclude
        Specifies an array of software update classifications to exclude from the classifications supported for software updates at this site.
 
    .PARAMETER ContentFileOption
        Specifies whether express updates will be downloaded for Windows 10. The acceptable values for this parameter are:
 
        FullFilesOnly
        ExpressForWindows10Only
 
    .PARAMETER DefaultWsusServer
        Specifies the default WSUS server that the software update point is pointed to.
 
    .PARAMETER EnableCallWsusCleanupWizard
        Specifies whether to decline expired updates in WSUS according to superscedence rules.
 
    .PARAMETER EnableSyncFailureAlert
        Specifies whether Configuration Manager creates an alert when synchronization fails on a site.
 
    .PARAMETER EnableSynchronization
        Indicates whether this site automatically synchronizes updates according to a schedule.
 
    .PARAMETER ImmediatelyExpireSupersedence
        Indicates whether a software update expires immediately after another update supersedes it or after a specified period of time.
        If you specify a value of $false for this parameter, specify the number of months to wait for expiration by using the WaitMonth parameter.
        If you specify a value of $true for this parameter, do not specify the WaitMonth parameter.
 
    .PARAMETER ImmediatelyExpireSupersedenceForFeature
        Indicates whether a feature update expires immediately after another update supersedes it or after a specified period of time.
        If you specify a value of $false for this parameter, specify the number of months to wait for expiration by using the WaitMonthForFeature parameter.
        If you specify a value of $true for this parameter, do not specify the WaitMonthForFeature parameter.
 
    .PARAMETER ReportingEvent
        Specifies whether to create event messages for WSUS reporting for status reporting events or for all reporting events. The acceptable values for this parameter are:
 
        CreateAllWsusReportingEvents
        CreateOnlyWsusStatusReportingEvents
        DoNotCreateWsusReportingEvents
 
    .PARAMETER SynchronizeAction
        Specifies a source for synchronization for this software update point. The acceptable values for this parameter are:
 
        SynchronizeFromMicrosoftUpdate
        SynchronizeFromAnUpstreamDataSourceLocation
        DoNotSynchronizeFromMicrosoftUpdateOrUpstreamDataSource
 
        If you select a value of SynchronizeFromAnUpstreamDataSourceLocation, specify the data source location by using the UpstreamSourceLocation parameter.
 
    .PARAMETER UpstreamSourceLocation
        Specifies an upstream data location as a URL.
        To use this location, specify a value of SynchronizeFromAnUpstreamDataSourceLocation for the SynchronizeAction parameter.
 
    .PARAMETER WaitMonth
        Specifies how long, in months, to wait before a software update expires after another update supersedes it.
        Specify a value of $True for the ImmediatelyExpireSupersedence parameter for software updates to expire immediately.
        If $True is specified for the ImmediatelyExpireSupersedence parameter, do not use this parameter.
 
    .PARAMETER WaitMonthForFeature
        Specifies how long, in months, to wait before a feature update expires after another update supersedes it.
        Specify a value of $True for the ImmediatelyExpireSupersedenceForFeature parameter for software updates to expire immediately.
        If $True is specified for the ImmediatelyExpireSupersedenceForFeature parameter, do not use this parameter.
 
    .PARAMETER Start
        Specifies the start date and start time for the synchronization schedule Month/Day/Year, example 1/1/2020 02:00.
 
    .PARAMETER ScheduleType
        Specifies the schedule type for the synchronization schedule.
 
    .PARAMETER RecurInterval
        Specifies how often the ScheduleType is run.
 
    .PARAMETER MonthlyWeekOrder
        Specifies week order for MonthlyByWeek schedule type.
 
    .PARAMETER DayOfWeek
        Specifies the day of week name for MonthlyByWeek and Weekly schedules.
 
    .PARAMETER DayOfMonth
        Specifies the day number for MonthlyByDay schedules.
        Note specifying 0 sets the schedule to run the last day of the month.
 
    .PARAMETER EnableManualCertManagement
        Specifies whether manual management of the WSUS signing certificate is enabled.
 
    .PARAMETER EnableThirdPartyUpdates
        Specifies whether third-party updates are enabled on the Software Update Point Component.
 
    .PARAMETER FeatureUpdateMaxRuntimeMins
        Specifies the maximum runtime, in minutes, for windows feature updates.
 
    .PARAMETER NonFeatureUpdateMaxRuntimeMins
        Specifies the maximum runtime, in minutes, for Office 365 updates and windows non-feature updates.
#>

function Test-TargetResource
{
    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param
    (
        [Parameter(Mandatory = $true)]
        [String]
        $SiteCode,

        [Parameter()]
        [String[]]
        $LanguageSummaryDetails,

        [Parameter()]
        [String[]]
        $LanguageSummaryDetailsToInclude,

        [Parameter()]
        [String[]]
        $LanguageSummaryDetailsToExclude,

        [Parameter()]
        [String[]]
        $LanguageUpdateFiles,

        [Parameter()]
        [String[]]
        $LanguageUpdateFilesToInclude,

        [Parameter()]
        [String[]]
        $LanguageUpdateFilesToExclude,

        [Parameter()]
        [String[]]
        $Products,

        [Parameter()]
        [String[]]
        $ProductsToInclude,

        [Parameter()]
        [String[]]
        $ProductsToExclude,

        [Parameter()]
        [String[]]
        $UpdateClassifications,

        [Parameter()]
        [String[]]
        $UpdateClassificationsToInclude,

        [Parameter()]
        [String[]]
        $UpdateClassificationsToExclude,

        [Parameter()]
        [ValidateSet('FullFilesOnly', 'ExpressForWindows10Only')]
        [String]
        $ContentFileOption,

        [Parameter()]
        [String]
        $DefaultWsusServer,

        [Parameter()]
        [Boolean]
        $EnableCallWsusCleanupWizard,

        [Parameter()]
        [Boolean]
        $EnableSyncFailureAlert,

        [Parameter()]
        [Boolean]
        $EnableSynchronization,

        [Parameter()]
        [Boolean]
        $ImmediatelyExpireSupersedence,

        [Parameter()]
        [Boolean]
        $ImmediatelyExpireSupersedenceForFeature,

        [Parameter()]
        [ValidateSet('CreateAllWsusReportingEvents', 'CreateOnlyWsusStatusReportingEvents', 'DoNotCreateWsusReportingEvents')]
        [String]
        $ReportingEvent,

        [Parameter()]
        [ValidateSet('SynchronizeFromMicrosoftUpdate', 'SynchronizeFromAnUpstreamDataSourceLocation', 'DoNotSynchronizeFromMicrosoftUpdateOrUpstreamDataSource')]
        [String]
        $SynchronizeAction,

        [Parameter()]
        [String]
        $UpstreamSourceLocation,

        [Parameter()]
        [ValidateRange(1,99)]
        [UInt32]
        $WaitMonth,

        [Parameter()]
        [ValidateRange(1,99)]
        [UInt32]
        $WaitMonthForFeature,

        [Parameter()]
        [String]
        $Start,

        [Parameter()]
        [ValidateSet('MonthlyByDay','MonthlyByWeek','Weekly','Days','Hours')]
        [String]
        $ScheduleType,

        [Parameter()]
        [ValidateRange(1,31)]
        [UInt32]
        $RecurInterval,

        [Parameter()]
        [ValidateSet('First','Second','Third','Fourth','Last')]
        [String]
        $MonthlyWeekOrder,

        [Parameter()]
        [ValidateSet('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday')]
        [String]
        $DayOfWeek,

        [Parameter()]
        [ValidateRange(0,31)]
        [UInt32]
        $DayOfMonth,

        [Parameter()]
        [Boolean]
        $EnableManualCertManagement,

        [Parameter()]
        [Boolean]
        $EnableThirdPartyUpdates,

        [Parameter()]
        [ValidateRange(5,9999)]
        [UInt32]
        $FeatureUpdateMaxRuntimeMins,

        [Parameter()]
        [ValidateRange(5,9999)]
        [UInt32]
        $NonFeatureUpdateMaxRuntimeMins
    )

    Import-ConfigMgrPowerShellModule -SiteCode $SiteCode
    Set-Location -Path "$($SiteCode):\"
    $state = Get-TargetResource -SiteCode $SiteCode
    $result = $true

    $nonChildParams = @('LanguageSummaryDetails','LanguageSummaryDetailsToInclude','LanguageSummaryDetailsToExclude','Products','ProductsToInclude','ProductsToExclude','UpdateClassifications',
        'UpdateClassificationsToInclude','UpdateClassificationsToExclude','ContentFileOption','DefaultWsusServer','EnableCallWsusCleanupWizard','EnableSyncFailureAlert','EnableSynchronization',
        'ImmediatelyExpireSupersedence','ImmediatelyExpireSupersedenceForFeature','SynchronizeAction','UpstreamSourceLocation','WaitMonth','WaitMonthForFeature','EnableThirdPartyUpdates',
        'EnableManualCertManagement','FeatureUpdateMaxRuntimeMins','NonFeatureUpdateMaxRuntimeMins','ScheduleType','RecurInterval','MonthlyWeekOrder','DayOfWeek','DayOfMonth','Start')

    $availableLangs = @('Arabic','Bulgarian','Chinese (Simplified, PRC)','Chinese (Traditional, Hong Kong S.A.R.)','Chinese (Traditional, Taiwan)','Croatian','Czech','Danish','Dutch','English',
        'Estonian','Finnish','French','German','Greek','Hebrew','Hindi','Hungarian','Italian','Japanese','Korean','Latvian','Lithuanian','Norwegian','Polish','Portuguese','Portuguese (Brazil)',
        'Romanian','Russian','Serbian','Slovak','Slovenian','Spanish','Swedish','Thai','Turkish','Ukrainian')

    $langs = $LanguageUpdateFiles + $LanguageUpdateFilesToInclude + $LanguageUpdateFilesToExclude + $LanguageSummaryDetails + $LanguageSummaryDetailsToInclude + $LanguageSummaryDetailsToExclude
    $cats = $Products + $ProductsToInclude + $ProductsToExclude + $UpdateClassifications + $UpdateClassificationsToInclude + $UpdateClassificationsToExclude

    foreach ($lang in $langs.Where({ $_ -ne $null }))
    {
        if ($availableLangs -notcontains $lang)
        {
            Write-Warning -Message ($script:localizedData.InvalidLangs -f $lang)
            $result = $false
        }
    }

    foreach ($cat in $cats.Where({ $_ -ne $null }))
    {
        if ($state.AvailableCats -notcontains $cat)
        {
            Write-Warning -Message ($script:localizedData.InvalidCats -f $cat)
            $result = $false
        }
    }

    if ($state.ChildSite -eq $true)
    {
        foreach ($child in $nonChildParams)
        {
            if ($PSBoundParameters.ContainsKey($child))
            {
                Write-Warning -Message ($script:localizedData.ChildBadParams -f $child)
            }
        }

        if ($LanguageUpdateFiles -or $LanguageUpdateFilesToInclude -or $LanguageUpdateFilesToExclude)
        {
            if ($LanguageUpdateFilesToInclude -and $LanguageUpdateFilesToExclude)
            {
                foreach ($item in $LanguageUpdateFilesToInclude)
                {
                    if ($LanguageUpdateFilesToExclude -contains $item)
                    {
                        Write-Warning -Message ($script:localizedData.LangFilesInEx -f $item)
                        $result = $false
                    }
                }
            }

            $languageUpdateFilesArray = @{
                Match        = $LanguageUpdateFiles
                Include      = $LanguageUpdateFilesToInclude
                Exclude      = $LanguageUpdateFilesToExclude
                CurrentState = $state.LanguageUpdateFiles
            }

            $languageUpdateFilesCompare = Compare-MultipleCompares @languageUpdateFilesArray

            if ($PSBoundParameters.ContainsKey('LanguageUpdateFiles'))
            {
                if ($PSBoundParameters.ContainsKey('LanguageUpdateFilesToInclude') -or $PSBoundParameters.ContainsKey('LanguageUpdateFilesToExclude'))
                {
                    Write-Warning -Message $script:localizedData.LanguageUpdateIgnore
                }
            }

            if ($languageUpdateFilesCompare.Missing)
            {
                Write-Verbose -Message ($script:localizedData.LanguageUpdateMissing -f ($languageUpdateFilesCompare.Missing | Out-String))
                $result = $false
            }

            if ($languageUpdateFilesCompare.Remove)
            {
                Write-Verbose -Message ($script:localizedData.LanguageUpdateRemove -f ($languageUpdateFilesCompare.Remove | Out-String))
                $result = $false
            }

            if ($state.LanguageUpdateFiles.Count + $languageUpdateFilesCompare.Missing.Count - $languageUpdateFilesCompare.Remove.Count -eq 0)
            {
                Write-Warning -Message $script:localizedData.AllUpdateRemoved
                $result = $false
            }
        }

        $testParams = @{
            CurrentValues = $state
            DesiredValues = $PSBoundParameters
            ValuesToCheck = @('ReportingEvent')
        }

        $mainState = Test-DscParameterState @testParams -TurnOffTypeChecking -Verbose

        if (-not [string]::IsNullOrEmpty($mainState) -and $mainState -eq $false)
        {
            $result = $false
        }
    }
    else
    {
        if (($PSBoundParameters.ImmediatelyExpireSupersedence -eq $false) -and
           (-not $PSBoundParameters.ContainsKey('WaitMonth')))
        {
            Write-Warning -Message $script:localizedData.WaitMonthNull
            $result = $false
        }

        if (($PSBoundParameters.ImmediatelyExpireSupersedence -eq $true) -and
           ($PSBoundParameters.ContainsKey('WaitMonth')))
        {
            Write-Warning -Message $script:localizedData.WaitMonthNeeded
            $result = $false
        }

        if (($PSBoundParameters.ImmediatelyExpireSupersedenceForFeature -eq $false) -and
           (-not $PSBoundParameters.ContainsKey('WaitMonthForFeature')))
        {
            Write-Warning -Message $script:localizedData.WaitFeatureNull
            $result = $false
        }

        if (($PSBoundParameters.ImmediatelyExpireSupersedenceForFeature -eq $true) -and
           ($PSBoundParameters.ContainsKey('WaitMonthForFeature')))
        {
            Write-Warning -Message $script:localizedData.WaitFeatureNeeded
            $result = $false
        }

        if (($PSBoundParameters.SynchronizeAction -eq 'SynchronizeFromAnUpstreamDataSourceLocation') -and
           (-not $PSBoundParameters.ContainsKey('UpstreamSourceLocation')))
        {
            Write-Warning -Message $script:localizedData.UpstreamSourceNull
            $result = $false
        }

        if (($PSBoundParameters.EnableThirdPartyUpdates -eq $false) -and
           ($PSBoundParameters.ContainsKey('EnableManualCertManagement')))
        {
            Write-Warning -Message $script:localizedData.CertMgmtSpecified
            $result = $false
        }

        if (($PSBoundParameters.EnableSynchronization -eq $false) -and
           ($PSBoundParameters.ContainsKey('ScheduleType')))
        {
            Write-Warning -Message $script:localizedData.ScheduleNoSync
            $result = $false
        }

        if (($PSBoundParameters.EnableSynchronization -eq $true) -and
           (-not $PSBoundParameters.ContainsKey('ScheduleType')))
        {
            Write-Warning -Message $script:localizedData.SyncNoSchedule
            $result = $false
        }

        if ((-not $PSBoundParameters.ContainsKey('ScheduleType')) -and ($PSBoundParameters.ContainsKey('Start') -or
                $PSBoundParameters.ContainsKey('RecurInterval') -or $PSBoundParameters.ContainsKey('MonthlyWeekOrder') -or
                $PSBoundParameters.ContainsKey('DayOfWeek') -or $PSBoundParameters.ContainsKey('DayOfMonth')))
        {
            Write-Warning -Message $script:localizedData.MissingScheduleType
            $result = $false
        }

        if ($LanguageSummaryDetails -or $LanguageSummaryDetailsToInclude -or $LanguageSummaryDetailsToExclude)
        {
            if ($LanguageSummaryDetailsToInclude -and $LanguageSummaryDetailsToExclude)
            {
                foreach ($item in $LanguageSummaryDetailsToInclude)
                {
                    if ($LanguageSummaryDetailsToExclude -contains $item)
                    {
                        Write-Warning -Message ($script:localizedData.LangSumInEx -f $item)
                        $result = $false
                    }
                }
            }

            $languageSummaryDetailsArray = @{
                Match        = $LanguageSummaryDetails
                Include      = $LanguageSummaryDetailsToInclude
                Exclude      = $LanguageSummaryDetailsToExclude
                CurrentState = $state.LanguageSummaryDetails
            }

            $languageSummaryDetailsCompare = Compare-MultipleCompares @languageSummaryDetailsArray

            if ($PSBoundParameters.ContainsKey('LanguageSummaryDetails'))
            {
                if ($PSBoundParameters.ContainsKey('LanguageSummaryDetailsToInclude') -or $PSBoundParameters.ContainsKey('LanguageSummaryDetailsToExclude'))
                {
                    Write-Warning -Message $script:localizedData.LanguageSummaryIgnore
                }
            }

            if ($languageSummaryDetailsCompare.Missing)
            {
                Write-Verbose -Message ($script:localizedData.LanguageSummaryMissing -f ($languageSummaryDetailsCompare.Missing | Out-String))
                $result = $false
            }

            if ($languageSummaryDetailsCompare.Remove)
            {
                Write-Verbose -Message ($script:localizedData.LanguageSummaryRemove -f ($languageSummaryDetailsCompare.Remove | Out-String))
                $result = $false
            }

            if ($state.LanguageSummaryDetails.Count + $languageSummaryDetailsCompare.Missing.Count - $languageSummaryDetailsCompare.Remove.Count -eq 0)
            {
                Write-Warning -Message $script:localizedData.AllSummaryRemoved
                $result = $false
            }
        }

        if ($LanguageUpdateFiles -or $LanguageUpdateFilesToInclude -or $LanguageUpdateFilesToExclude)
        {
            if ($LanguageUpdateFilesToInclude -and $LanguageUpdateFilesToExclude)
            {
                foreach ($item in $LanguageUpdateFilesToInclude)
                {
                    if ($LanguageUpdateFilesToExclude -contains $item)
                    {
                        Write-Warning -Message ($script:localizedData.LangFilesInEx -f $item)
                        $result = $false
                    }
                }
            }

            $languageUpdateFilesArray = @{
                Match        = $LanguageUpdateFiles
                Include      = $LanguageUpdateFilesToInclude
                Exclude      = $LanguageUpdateFilesToExclude
                CurrentState = $state.LanguageUpdateFiles
            }

            $languageUpdateFilesCompare = Compare-MultipleCompares @languageUpdateFilesArray

            if ($PSBoundParameters.ContainsKey('LanguageUpdateFiles'))
            {
                if ($PSBoundParameters.ContainsKey('LanguageUpdateFilesToInclude') -or $PSBoundParameters.ContainsKey('LanguageUpdateFilesToExclude'))
                {
                    Write-Warning -Message $script:localizedData.LanguageUpdateIgnore
                }
            }

            if ($languageUpdateFilesCompare.Missing)
            {
                Write-Verbose -Message ($script:localizedData.LanguageUpdateMissing -f ($languageUpdateFilesCompare.Missing | Out-String))
                $result = $false
            }

            if ($languageUpdateFilesCompare.Remove)
            {
                Write-Verbose -Message ($script:localizedData.LanguageUpdateRemove -f ($languageUpdateFilesCompare.Remove | Out-String))
                $result = $false
            }

            if ($state.LanguageUpdateFiles.Count + $languageUpdateFilesCompare.Missing.Count - $languageUpdateFilesCompare.Remove.Count -eq 0)
            {
                Write-Warning -Message $script:localizedData.AllUpdateRemoved
                $result = $false
            }
        }

        if ($Products -or $ProductsToInclude -or $ProductsToExclude)
        {
            if ($ProductsToInclude -and $ProductsToExclude)
            {
                foreach ($item in $ProductsToInclude)
                {
                    if ($ProductsToExclude -contains $item)
                    {
                        Write-Warning -Message ($script:localizedData.ProductsInEx -f $item)
                        $result = $false
                    }
                }
            }

            $productsArray = @{
                Match        = $Products
                Include      = $ProductsToInclude
                Exclude      = $ProductsToExclude
                CurrentState = $state.Products
            }

            $productsCompare = Compare-MultipleCompares @productsArray

            if ($PSBoundParameters.ContainsKey('Products'))
            {
                if ($PSBoundParameters.ContainsKey('ProductsToInclude') -or $PSBoundParameters.ContainsKey('ProductsToExclude'))
                {
                    Write-Warning -Message $script:localizedData.ProductsIgnore
                }
            }

            if ($productsCompare.Missing)
            {
                Write-Verbose -Message ($script:localizedData.ProductsMissing -f ($productsCompare.Missing | Out-String))
                $result = $false
            }

            if ($productsCompare.Remove)
            {
                Write-Verbose -Message ($script:localizedData.ProductsRemove -f ($productsCompare.Remove | Out-String))
                $result = $false
            }
        }

        if ($UpdateClassifications -or $UpdateClassificationsToInclude -or $UpdateClassificationsToExclude)
        {
            if ($UpdateClassificationsToInclude -and $UpdateClassificationsToExclude)
            {
                foreach ($item in $UpdateClassificationsToInclude)
                {
                    if ($UpdateClassificationsToExclude -contains $item)
                    {
                        Write-Warning -Message ($script:localizedData.UpdateClassInEx -f $item)
                        $result = $false
                    }
                }
            }

            $updateClassificationsArray = @{
                Match        = $UpdateClassifications
                Include      = $UpdateClassificationsToInclude
                Exclude      = $UpdateClassificationsToExclude
                CurrentState = $state.UpdateClassifications
            }

            $updateClassificationsCompare = Compare-MultipleCompares @updateClassificationsArray

            if ($PSBoundParameters.ContainsKey('UpdateClassifications'))
            {
                if ($PSBoundParameters.ContainsKey('UpdateClassificationsToInclude') -or $PSBoundParameters.ContainsKey('UpdateClassificationsToExclude'))
                {
                    Write-Warning -Message $script:localizedData.UpdateClassificationsIgnore
                }
            }

            if ($updateClassificationsCompare.Missing)
            {
                Write-Verbose -Message ($script:localizedData.UpdateClassificationsMissing -f ($updateClassificationsCompare.Missing | Out-String))
                $result = $false
            }

            if ($updateClassificationsCompare.Remove)
            {
                Write-Verbose -Message ($script:localizedData.UpdateClassificationsRemove -f ($updateClassificationsCompare.Remove | Out-String))
                $result = $false
            }
        }

        if ($ScheduleType)
        {
            $valuesToValidate = @('ScheduleType','RecurInterval','MonthlyWeekOrder','DayOfWeek','DayOfMonth','Start')
            foreach ($item in $valuesToValidate)
            {
                if ($PSBoundParameters.ContainsKey($item))
                {
                    $scheduleCheck += @{
                        $item = $PSBoundParameters[$item]
                    }
                }
            }

            $schedResult = Test-CMSchedule @scheduleCheck -State $state

            if ($schedResult -ne $true)
            {
                $result = $false
            }
        }

        $testParams = @{
            CurrentValues = $state
            DesiredValues = $PSBoundParameters
            ValuesToCheck = @('ContentFileOption','DefaultWsusServer','EnableCallWsusCleanupWizard','EnableSyncFailureAlert','EnableSynchronization',
                'ImmediatelyExpireSupersedence','ImmediatelyExpireSupersedenceForFeature','ReportingEvent','SynchronizeAction','UpstreamSourceLocation',
                'WaitMonth','WaitMonthForFeature','EnableThirdPartyUpdates','EnableManualCertManagement','FeatureUpdateMaxRuntimeMins','NonFeatureUpdateMaxRuntimeMins')
        }

        $mainState = Test-DscParameterState @testParams -TurnOffTypeChecking -Verbose

        if (-not [string]::IsNullOrEmpty($mainState) -and $mainState -eq $false)
        {
            $result = $false
        }
    }

    Write-Verbose -Message ($script:localizedData.TestState -f $result)
    Set-Location -Path "$env:temp"
    return $result
}

Export-ModuleMember -Function *-TargetResource