DSCResources/MSFT_UpdateServicesServer/MSFT_UpdateServicesServer.psm1

# DSC resource to initialize and configure WSUS Server.

# Classifications ID reference...
#
# Applications = 5C9376AB-8CE6-464A-B136-22113DD69801
# Connectors = 434DE588-ED14-48F5-8EED-A15E09A991F6
# Critical Updates = E6CF1350-C01B-414D-A61F-263D14D133B4
# Definition Updates = E0789628-CE08-4437-BE74-2495B842F43B
# Developer Kits = E140075D-8433-45C3-AD87-E72345B36078
# Feature Packs = B54E7D24-7ADD-428F-8B75-90A396FA584F
# Guidance = 9511D615-35B2-47BB-927F-F73D8E9260BB
# Security Updates = 0FA1201D-4330-4FA8-8AE9-B877473B6441
# Service Packs = 68C5B0A3-D1A6-4553-AE49-01D3A7827828
# Tools = B4832BD8-E735-4761-8DAF-37F882276DAB
# Update Rollups = 28BC880E-0592-4CBF-8F95-C79B17911D5F
# Updates = CD5FFD1E-E932-4E3A-BF74-18BF0B1BBD83

$currentPath = Split-Path -Parent $MyInvocation.MyCommand.Path
Write-Debug -Message "CurrentPath: $currentPath"

# Load Common Code
Import-Module $currentPath\..\..\UpdateServicesHelper.psm1 -Verbose:$false -ErrorAction Stop

<#
    .SYNOPSIS
    Returns the current configuration of WSUS
    .PARAMETER Ensure
    Determinse if the configuration should be added or removed
#>

function Get-TargetResource
{
    [CmdletBinding()]
    [OutputType([System.Collections.Hashtable])]
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateSet("Present","Absent")]
        [System.String]
        $Ensure
    )

    Write-Verbose -Message 'Getting WSUSServer'
    try
    {
        if($WsusServer = Get-WsusServer)
        {
            $Ensure = 'Present'
        }
        else
        {
            $Ensure = 'Absent'
        }
    }
    catch
    {
        $Ensure = 'Absent'
    }

    Write-Verbose -Message "WSUSServer is $Ensure"
    if($Ensure -eq 'Present')
    {
        Write-Verbose -Message 'Getting WSUSServer configuration'
        $WsusConfiguration = $WsusServer.GetConfiguration()
        Write-Verbose -Message 'Getting WSUSServer subscription'
        $WsusSubscription = $WsusServer.GetSubscription()
            
        Write-Verbose -Message 'Getting WSUSServer SQL Server'
        $SQLServer = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Update Services\Server\Setup' `
            -Name 'SQLServerName').SQLServerName
        Write-Verbose -Message "WSUSServer SQL Server is $SQLServer"
        Write-Verbose -Message 'Getting WSUSServer content directory'
        $ContentDir = (Get-ItemProperty -Path 'HKLM:\SOFTWARE\Microsoft\Update Services\Server\Setup' `
            -Name 'ContentDir').ContentDir
        Write-Verbose -Message "WSUSServer content directory is $ContentDir"
        
        Write-Verbose -Message 'Getting WSUSServer update improvement program'
        $UpdateImprovementProgram = $WsusConfiguration.MURollupOptin
        Write-Verbose -Message "WSUSServer content update improvement program is $UpdateImprovementProgram"

        if(!$WsusConfiguration.SyncFromMicrosoftUpdate)
        {
            Write-Verbose -Message 'Getting WSUSServer upstream server'
            $UpstreamServerName = $WsusConfiguration.UpstreamWsusServerName
            $UpstreamServerPort = $WsusConfiguration.UpstreamWsusServerPortNumber
            $UpstreamServerSSL = $WsusConfiguration.UpstreamWsusServerUseSsl
            $UpstreamServerReplica = $WsusConfiguration.IsReplicaServer
            Write-Verbose -Message "WSUSServer upstream server is $UpstreamServerName, `
                port $UpstreamServerPort, use SSL $UpstreamServerSSL, replica $UpstreamServerReplica"

        }
        else
        {
            $UpstreamServerName = ""
            $UpstreamServerPort = $null
            $UpstreamServerSSL = $null
            $UpstreamServerReplica = $null
        }
   
        if($WsusConfiguration.UseProxy)
        {
            Write-Verbose -Message 'Getting WSUSServer proxy server'
            $ProxyServerName = $WsusConfiguration.ProxyName
            $ProxyServerPort = $WsusConfiguration.ProxyServerPort
            $ProxyServerBasicAuthentication = $WsusConfiguration.AllowProxyCredentialsOverNonSsl
            if (!($WsusConfiguration.AnonymousProxyAccess))
            {
                $ProxyServerCredentialUsername = "$($WsusConfiguration.ProxyUserDomain)\ `
                    $($WsusConfiguration.ProxyUserName)"
.Trim('\')
            }
            Write-Verbose -Message "WSUSServer proxy server is $ProxyServerName, port $ProxyServerPort, `
                basic authentication $ProxyServerBasicAuthentication"

        }
        else
        {
            $ProxyServerName = ""
            $ProxyServerPort = $null
            $ProxyServerBasicAuthentication = $null
        }

        Write-Verbose -Message 'Getting WSUSServer languages'
        if($WsusConfiguration.AllUpdateLanguagesEnabled)
        {
            $Languages = @("*")
        }
        else
        {
            $Languages = $WsusConfiguration.GetEnabledUpdateLanguages() | Out-String
        }
        Write-Verbose -Message "WSUSServer languages are $Languages"

        Write-Verbose -Message 'Getting WSUSServer classifications'
        if ($Classifications = @($WsusSubscription.GetUpdateClassifications().ID.Guid))
        {
            if($null -eq (Compare-Object -ReferenceObject ($Classifications | Sort-Object -Unique) -DifferenceObject (($WsusServer.GetUpdateClassifications().ID.Guid) | Sort-Object -Unique) -SyncWindow 0))
            {
                $Classifications = @("*")
            }
        }
        else {
            $Classifications = @("*")
        }
        Write-Verbose -Message "WSUSServer classifications are $Classifications"
        Write-Verbose -Message 'Getting WSUSServer products'
        if ($Products = @($WsusSubscription.GetUpdateCategories().Title))
        {
            if($null -eq (Compare-Object -ReferenceObject ($Products | Sort-Object -Unique) -DifferenceObject (($WsusServer.GetUpdateCategories().Title) | Sort-Object -Unique) -SyncWindow 0))
            {
                $Products = @("*")
            }
        }
        else {
            $Products = @("*")
        }
        Write-Verbose -Message "WSUSServer products are $Products"
        Write-Verbose -Message 'Getting WSUSServer synchronization settings'
        $SynchronizeAutomatically = $WsusSubscription.SynchronizeAutomatically
        Write-Verbose -Message "WSUSServer synchronize automatically is $SynchronizeAutomatically"
        $SynchronizeAutomaticallyTimeOfDay = $WsusSubscription.SynchronizeAutomaticallyTimeOfDay
        Write-Verbose -Message "WSUSServer synchronize automatically time of day is $SynchronizeAutomaticallyTimeOfDay"
        $SynchronizationsPerDay = $WsusSubscription.NumberOfSynchronizationsPerDay
        Write-Verbose -Message "WSUSServer number of synchronizations per day is $SynchronizationsPerDay"
    }

    $returnValue = @{
        Ensure                            = $Ensure
        SQLServer                         = $SQLServer
        ContentDir                        = $ContentDir
        UpdateImprovementProgram          = $UpdateImprovementProgram
        UpstreamServerName                = $UpstreamServerName
        UpstreamServerPort                = $UpstreamServerPort
        UpstreamServerSSL                 = $UpstreamServerSSL
        UpstreamServerReplica             = $UpstreamServerReplica
        ProxyServerName                   = $ProxyServerName
        ProxyServerPort                   = $ProxyServerPort
        ProxyServerCredentialUsername     = $ProxyServerCredentialUsername
        ProxyServerBasicAuthentication    = $ProxyServerBasicAuthentication
        Languages                         = $Languages
        Products                          = $Products
        Classifications                   = $Classifications
        SynchronizeAutomatically          = $SynchronizeAutomatically
        SynchronizeAutomaticallyTimeOfDay = $SynchronizeAutomaticallyTimeOfDay
        SynchronizationsPerDay            = $SynchronizationsPerDay
    }

    $returnValue
}

<#
    .SYNOPSIS
    Configures a WSUS server instance
    .PARAMETER Ensure
    Determines if the task should be created or removed.
    Accepts 'Present'(default) or 'Absent'.
    .PARAMETER SetupCredential
    Credentisal to use when running setup.
    Applicable when using SQL as data store.
    .PARAMETER SQLServer
    Optionally specify a SQL instance to store WSUS data
    .PARAMETER ContentDir
    Location to store WSUS content files
    .PARAMETER UpdateImprovementProgram
    Provide feedback to Microsoft to help imrpove WSUS
    .PARAMETER UpstreamServerName
    Name of another WSUS server to retrieve content from
    .PARAMETER UpstreamServerPort
    If getting content from another server, port for traffic
    .PARAMETER UpstreamServerSSL
    If getting content from another server, whether to encrypt the traffic
    .PARAMETER UpstreamServerReplica
    Boolean to specify whether to retrieve content from anotehr server
    .PARAMETER ProxyServerName
    Host name of proxy server
    .PARAMETER ProxyServerPort
    Port of proxy server
    .PARAMETER ProxyServerCredential
    Credential to use when authenticating to proxy server
    .PARAMETER ProxyServerBasicAuthentication
    Use basic auth for proxy
    .PARAMETER Languages
    Specify list of languages for content, or "*" for all
    .PARAMETER Products
    List of products to include when synchronizing, by default Windows and Office
    .PARAMETER Classifications
    List of content classifications to synchronize to the WSUS server
    .PARAMETER SynchronizeAutomatically
    Automatically synchronize the WSUS instance
    .PARAMETER SynchronizeAutomaticallyTimeOfDay
    Time of day to schedule an automatic synchronization
    .PARAMETER SynchronizationsPerDay
    Number of automatic synchronizations per day
    .PARAMETER Synchronize
    Run a synchronization immediately when running Set
#>

function Set-TargetResource
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateSet("Present","Absent")]
        [System.String]
        $Ensure,

        [Parameter()]
        [System.Management.Automation.PSCredential]
        $SetupCredential,

        [Parameter()]
        [System.String]
        $SQLServer,

        [Parameter()]
        [System.String]
        $ContentDir = "%SystemDrive%\WSUS",

        [Parameter()]
        [System.Boolean]
        $UpdateImprovementProgram,

        [Parameter()]
        [System.String]
        $UpstreamServerName,

        [Parameter()]
        [System.UInt16]
        $UpstreamServerPort = 8530,

        [Parameter()]
        [System.Boolean]
        $UpstreamServerSSL,

        [Parameter()]
        [System.Boolean]
        $UpstreamServerReplica,

        [Parameter()]
        [System.String]
        $ProxyServerName,

        [Parameter()]
        [System.UInt16]
        $ProxyServerPort = 80,

        [Parameter()]
        [System.Management.Automation.PSCredential]
        $ProxyServerCredential,

        [Parameter()]
        [System.Boolean]
        $ProxyServerBasicAuthentication,

        [Parameter()]
        [System.String[]]
        $Languages = "*",

        [Parameter()]
        [System.String[]]
        $Products = @("Windows","Office"),

        [Parameter()]
        [System.String[]]
        $Classifications = @('E6CF1350-C01B-414D-A61F-263D14D133B4','E0789628-CE08-4437-BE74-2495B842F43B','0FA1201D-4330-4FA8-8AE9-B877473B6441'),

        [Parameter()]
        [System.Boolean]
        $SynchronizeAutomatically,

        [Parameter()]
        [System.String]
        $SynchronizeAutomaticallyTimeOfDay,

        [Parameter()]
        [System.UInt16]
        $SynchronizationsPerDay = 1,

        [Parameter()]
        [System.Boolean]
        $Synchronize
    )

    # Is WSUS configured?
    try
    {
        if($WsusServer = Get-WsusServer)
        {
            $PostInstall = $false
        }
    }
    catch
    {
        $PostInstall = $true
    }

    # Complete initial confiugration
    if($PostInstall)
    {
        Write-Verbose -Message "Running WSUS postinstall"

        Import-Module $PSScriptRoot\..\..\PDT\PDT.psm1

        $Path = "$($env:ProgramFiles)\Update Services\Tools\WsusUtil.exe"
        $Path = Invoke-ResolvePath $Path
        Write-Verbose -Message "Path: $Path"

        $Arguments = "postinstall "
        if($PSBoundParameters.ContainsKey('SQLServer'))
        {
            $Arguments += "SQL_INSTANCE_NAME=$SQLServer "
        }
        $Arguments += "CONTENT_DIR=$([Environment]::ExpandEnvironmentVariables($ContentDir))"

        Write-Verbose -Message "Arguments: $Arguments"

        if ($SetupCredential)
        {
            $Process = Start-Win32Process -Path $Path -Arguments $Arguments -Credential $SetupCredential
            Write-Verbose -Message [string]$Process
            Wait-Win32ProcessEnd -Path $Path -Arguments $Arguments
        }
        else 
        {
            $Process = Start-Win32Process -Path $Path -Arguments $Arguments
            Write-Verbose -Message [string]$Process
            Wait-Win32ProcessEnd -Path $Path -Arguments $Arguments        
        }
    }

    # Get WSUS server
    try
    {
        if($WsusServer = Get-WsusServer)
        {
            $Wsus = $true
        }
    }
    catch
    {
        $Wsus = $false

        throw New-TerminatingError -ErrorType WSUSConfigurationFailed
    }

    # Configure WSUS
    if($Wsus)
    {
        Write-Verbose -Message "Configuring WSUS"

        # Get configuration and make sure that the configuration can be saved before continuing
        $WsusConfiguration = $WsusServer.GetConfiguration()
        $WsusSubscription = $WsusServer.GetSubscription()
        Write-Verbose -Message "Check for previous configuration change"
        SaveWsusConfiguration

        # Configure Update Improvement Program
        Write-Verbose -Message "Configuring WSUS Update Improvement Program"
        $WsusConfiguration.MURollupOptin = $UpdateImprovementProgram

        # Configure Upstream Server
        if($PSBoundParameters.ContainsKey('UpstreamServerName'))
        {
            Write-Verbose -Message "Configuring WSUS Upstream Server"
            $WsusConfiguration.SyncFromMicrosoftUpdate = $false
            $WsusConfiguration.UpstreamWsusServerName = $UpstreamServerName
            $WsusConfiguration.UpstreamWsusServerPortNumber = $UpstreamServerPort
            $WsusConfiguration.UpstreamWsusServerUseSsl = $UpstreamServerSSL
            $WsusConfiguration.IsReplicaServer = $UpstreamServerReplica
        }
        else
        {
            Write-Verbose -Message "Configuring WSUS for Microsoft Update"
            $WsusConfiguration.SyncFromMicrosoftUpdate = $true
        }

        # Configure Proxy Server
        if($PSBoundParameters.ContainsKey('ProxyServerName'))
        {
            Write-Verbose -Message "Configuring WSUS proxy server"
            $WsusConfiguration.UseProxy = $true
            $WsusConfiguration.ProxyName = $ProxyServerName
            $WsusConfiguration.ProxyServerPort = $ProxyServerPort
            if($PSBoundParameters.ContainsKey('ProxyServerCredential'))
            {
                Write-Verbose -Message "Configuring WSUS proxy server credential"
                $WsusConfiguration.ProxyUserDomain = $ProxyServerCredential.GetNetworkCredential().Domain
                $WsusConfiguration.ProxyUserName = $ProxyServerCredential.GetNetworkCredential().UserName
                $WsusConfiguration.SetProxyPassword($ProxyServerCredential.GetNetworkCredential().Password)
                $WsusConfiguration.AllowProxyCredentialsOverNonSsl = $ProxyServerBasicAuthentication
                $WsusConfiguration.AnonymousProxyAccess = $false
            }
            else
            {
                Write-Verbose -Message "Removing WSUS proxy server credential"
                $WsusConfiguration.AnonymousProxyAccess = $true
            }
        }
        else
        {
            Write-Verbose -Message "Configuring WSUS no proxy server"
            $WsusConfiguration.UseProxy = $false
        }

        #Languages
        Write-Verbose -Message "Setting WSUS languages"
        if($Languages -eq "*")
        {
            $WsusConfiguration.AllUpdateLanguagesEnabled = $true
        }
        else
        {
            $WsusConfiguration.AllUpdateLanguagesEnabled = $false
            $WsusConfiguration.SetEnabledUpdateLanguages($Languages)
        }

        # Save configuration before initial sync
        SaveWsusConfiguration

        # Post Install
        if($PostInstall)
        {
            Write-Verbose -Message "Removing default products and classifications before initial sync"
            foreach($Product in ($WsusServer.GetSubscription().GetUpdateCategories().Title))
            {
                Get-WsusProduct | Where-Object {$_.Product.Title -eq $Product} | `
                    Set-WsusProduct -Disable
            }
            foreach($Classification in `
                ($WsusServer.GetSubscription().GetUpdateClassifications().ID.Guid))
            {
                Get-WsusClassification | Where-Object {$_.Classification.ID -eq $Classification} | `
                    Set-WsusClassification -Disable
            }

            if($Synchronize)
            {
                Write-Verbose -Message "Running WSUS initial synchronization online"
                $WsusServer.GetSubscription().StartSynchronizationForCategoryOnly()
                while($WsusServer.GetSubscription().GetSynchronizationStatus() -eq 'Running')
                {
                    Start-Sleep -Seconds 1
                }

                if($WsusServer.GetSubscription().GetSynchronizationHistory()[0].Result -eq 'Succeeded')
                {
                    Write-Verbose -Message "Initial WSUS synchronization succeeded"
                    $WsusConfiguration.OobeInitialized = $true
                    SaveWsusConfiguration
                }
                else
                {
                    Write-Verbose -Message "Initial WSUS synchronization failed"
                }
            }
            else
            {
                Write-Verbose -Message "Running WSUS initial synchronization offline"

                $TempFile = [IO.Path]::GetTempFileName()

                $CABPath = Join-Path -Path $PSScriptRoot -ChildPath "\WSUS.cab"

                $Arguments = "import "
                $Arguments += "`"$CABPath`" $TempFile"

                Write-Verbose -Message "Arguments: $Arguments"

                if ($SetupCredential)
                {
                    $Process = Start-Win32Process -Path $Path -Arguments $Arguments -Credential $SetupCredential
                    Write-Verbose -Message [string]$Process
                    Wait-Win32ProcessEnd -Path $Path -Arguments $Arguments
                }
                else 
                {
                    $Process = Start-Win32Process -Path $Path -Arguments $Arguments
                    Write-Verbose -Message [string]$Process
                    Wait-Win32ProcessEnd -Path $Path -Arguments $Arguments        
                }

                $WsusConfiguration.OobeInitialized = $true
                SaveWsusConfiguration
            }
        }

        # Configure WSUS subscription
        if($WsusConfiguration.OobeInitialized)
        {
            $WsusSubscription = $WsusServer.GetSubscription()

            # Products
            Write-Verbose -Message "Setting WSUS products"
            $ProductCollection = New-Object Microsoft.UpdateServices.Administration.UpdateCategoryCollection
            $AllWsusProducts = $WsusServer.GetUpdateCategories()
            if($Products -eq "*")
            {
                foreach($Product in $AllWsusProducts)
                {
                    $null = $ProductCollection.Add($WsusServer.GetUpdateCategory($Product.Id))
                }
            }
            else
            {
                foreach($Product in $Products)
                {
                    if($WsusProduct = $AllWsusProducts | Where-Object {$_.Title -eq $Product})
                    {
                        $null = $ProductCollection.Add($WsusServer.GetUpdateCategory($WsusProduct.Id))
                    }
                }
            }
            $WsusSubscription.SetUpdateCategories($ProductCollection)

            # Classifications
            Write-Verbose -Message "Setting WSUS classifications"
            $ClassificationCollection = New-Object Microsoft.UpdateServices.Administration.UpdateClassificationCollection
            $AllWsusClassifications = $WsusServer.GetUpdateClassifications()
            if($Classifications -eq "*")
            {
                foreach($Classification in $AllWsusClassifications)
                {
                    $null = $ClassificationCollection.Add($WsusServer.GetUpdateClassification($Classification.Id))
                }
            }
            else
            {
                foreach($Classification in $Classifications)
                {
                    if($WsusClassification = $AllWsusClassifications | Where-Object {$_.ID.Guid -eq $Classification})
                    {
                        $null = $ClassificationCollection.Add($WsusServer.GetUpdateClassification(`
                            $WsusClassification.Id))
                    }
                    else
                    {
                        Write-Verbose -Message "Classification $Classification not found"
                    }
                }
            }
            $WsusSubscription.SetUpdateClassifications($ClassificationCollection)

            #Synchronization Schedule
            Write-Verbose -Message "Setting WSUS synchronization schedule"
            $WsusSubscription.SynchronizeAutomatically = $SynchronizeAutomatically
            if($PSBoundParameters.ContainsKey('SynchronizeAutomaticallyTimeOfDay'))
            {
                $WsusSubscription.SynchronizeAutomaticallyTimeOfDay = $SynchronizeAutomaticallyTimeOfDay
            }
            $WsusSubscription.NumberOfSynchronizationsPerDay = $SynchronizationsPerDay

            $WsusSubscription.Save()

            if($Synchronize)
            {
                Write-Verbose -Message "Synchronizing WSUS"
                    
                $WsusServer.GetSubscription().StartSynchronization()
                while($WsusServer.GetSubscription().GetSynchronizationStatus() -eq 'Running')
                {
                    Start-Sleep -Seconds 1
                }
                if($WsusServer.GetSubscription().GetSynchronizationHistory()[0].Result -eq 'Succeeded')
                {
                    Write-Verbose -Message "WSUS synchronization succeeded"
                }
                else
                {
                    Write-Verbose -Message "WSUS synchronization failed"
                }
            }
        }
    }

    if(!(Test-TargetResource @PSBoundParameters))
    {
        throw New-TerminatingError -ErrorType TestFailedAfterSet -ErrorCategory InvalidResult
    }
}

<#
    .SYNOPSIS
    Configures a WSUS server instance
    .PARAMETER Ensure
    Determines if the task should be created or removed.
    Accepts 'Present'(default) or 'Absent'.
    .PARAMETER SetupCredential
    Credentisal to use when running setup.
    Applicable when using SQL as data store.
    .PARAMETER SQLServer
    Optionally specify a SQL instance to store WSUS data
    .PARAMETER ContentDir
    Location to store WSUS content files
    .PARAMETER UpdateImprovementProgram
    Provide feedback to Microsoft to help imrpove WSUS
    .PARAMETER UpstreamServerName
    Name of another WSUS server to retrieve content from
    .PARAMETER UpstreamServerPort
    If getting content from another server, port for traffic
    .PARAMETER UpstreamServerSSL
    If getting content from another server, whether to encrypt the traffic
    .PARAMETER UpstreamServerReplica
    Boolean to specify whether to retrieve content from anotehr server
    .PARAMETER ProxyServerName
    Host name of proxy server
    .PARAMETER ProxyServerPort
    Port of proxy server
    .PARAMETER ProxyServerCredential
    Credential to use when authenticating to proxy server
    .PARAMETER ProxyServerBasicAuthentication
    Use basic auth for proxy
    .PARAMETER Languages
    Specify list of languages for content, or "*" for all
    .PARAMETER Products
    List of products to include when synchronizing, by default Windows and Office
    .PARAMETER Classifications
    List of content classifications to synchronize to the WSUS server
    .PARAMETER SynchronizeAutomatically
    Automatically synchronize the WSUS instance
    .PARAMETER SynchronizeAutomaticallyTimeOfDay
    Time of day to schedule an automatic synchronization
    .PARAMETER SynchronizationsPerDay
    Number of automatic synchronizations per day
    .PARAMETER Synchronize
    Run a synchronization immediately when running Set
#>

function Test-TargetResource
{
    [CmdletBinding()]
    [OutputType([System.Boolean])]
    param
    (
        [Parameter(Mandatory = $true)]
        [ValidateSet("Present","Absent")]
        [System.String]
        $Ensure,

        [Parameter()]
        [System.Management.Automation.PSCredential]
        $SetupCredential,

        [Parameter()]
        [System.String]
        $SQLServer,

        [Parameter()]
        [System.String]
        $ContentDir,

        [Parameter()]
        [System.Boolean]
        $UpdateImprovementProgram,

        [Parameter()]
        [System.String]
        $UpstreamServerName,

        [Parameter()]
        [System.UInt16]
        $UpstreamServerPort = 8530,

        [Parameter()]
        [System.Boolean]
        $UpstreamServerSSL,

        [Parameter()]
        [System.Boolean]
        $UpstreamServerReplica,

        [Parameter()]
        [System.String]
        $ProxyServerName,

        [Parameter()]
        [System.UInt16]
        $ProxyServerPort = 80,

        [Parameter()]
        [System.Management.Automation.PSCredential]
        $ProxyServerCredential,

        [Parameter()]
        [System.Boolean]
        $ProxyServerBasicAuthentication,

        [Parameter()]
        [System.String[]]
        $Languages = "*",

        [Parameter()]
        [System.String[]]
        $Products = @("Windows","Office"),

        [Parameter()]
        [System.String[]]
        $Classifications = @('E6CF1350-C01B-414D-A61F-263D14D133B4','E0789628-CE08-4437-BE74-2495B842F43B','0FA1201D-4330-4FA8-8AE9-B877473B6441'),

        [Parameter()]
        [System.Boolean]
        $SynchronizeAutomatically,

        [Parameter()]
        [System.String]
        $SynchronizeAutomaticallyTimeOfDay,

        [Parameter()]
        [System.UInt16]
        $SynchronizationsPerDay = 1,

        [Parameter()]
        [System.Boolean]
        $Synchronize
    )

    $result = $true

    $Wsus = Get-TargetResource -Ensure $Ensure

    # Test Ensure
    if($Wsus.Ensure -ne $Ensure)
    {
        Write-Verbose -Message "Ensure test failed"
        $result = $false
    }
    if($result -and ($Wsus.Ensure -eq "Present"))
    {
        # Test Update Improvement Program
        if($Wsus.UpdateImprovementProgram -ne $UpdateImprovementProgram)
        {
            Write-Verbose -Message "UpdateImprovementProgram test failed"
            $result = $false
        }
        # Test Upstream Server
        if($Wsus.UpstreamServerName -ne $UpstreamServerName)
        {
            Write-Verbose -Message "UpstreamServerName test failed"
            $result = $false
        }
        if($PSBoundParameters.ContainsKey('UpstreamServerName'))
        {
            if($Wsus.UpstreamServerPort -ne $UpstreamServerPort)
            {
                Write-Verbose -Message "UpstreamServerPort test failed"
                $result = $false
            }
            if($Wsus.UpstreamServerSSL -ne $UpstreamServerSSL)
            {
                Write-Verbose -Message "UpstreamServerSSL test failed"
                $result = $false
            }
            if($Wsus.UpstreamServerReplica -ne $UpstreamServerReplica)
            {
                Write-Verbose -Message "UpstreamServerReplica test failed"
                $result = $false
            }
        }
        # Test Proxy Server
        if($Wsus.ProxyServerName -ne $ProxyServerName)
        {
            Write-Verbose -Message "ProxyServerName test failed"
            $result = $false
        }
        if($PSBoundParameters.ContainsKey('ProxyServerName'))
        {
            if($Wsus.ProxyServerPort -ne $ProxyServerPort)
            {
                Write-Verbose -Message "ProxyServerPort test failed"
                $result = $false
            }
            if($PSBoundParameters.ContainsKey('ProxyServerCredential'))
            {
                if(
                    ($null -eq $Wsus.ProxyServerCredentialUserName) -or
                    ($Wsus.ProxyServerCredentialUserName -ne $ProxyServerCredential.UserName)
                )
                {
                    Write-Verbose -Message "ProxyServerCredential test failed - incorrect credential"
                    $result = $false
                }
                if($Wsus.ProxyServerBasicAuthentication -ne $ProxyServerBasicAuthentication)
                {
                    Write-Verbose -Message "ProxyServerBasicAuthentication test failed"
                    $result = $false
                }
            }
            else
            {
                if($null -ne $Wsus.ProxyServerCredentialUserName)
                {
                    Write-Verbose -Message "ProxyServerCredential test failed - credential set"
                    $result = $false
                }
            }
        }
        # Test Languages
        if($Wsus.Languages.count -le 1 -and $Languages.count -le 1 -and $Languages -ne '*')
        {
            if($Wsus.Languages -notmatch $Languages)
            {
                Write-Verbose -Message "Languages test failed (evaluated as single string)"
                $result = $false
            }
        }
        else {
            if($null -ne (Compare-Object -ReferenceObject ($Wsus.Languages | Sort-Object -Unique) -DifferenceObject ($Languages | Sort-Object -Unique) -SyncWindow 0))
            {
                Write-Verbose -Message "Languages test failed"
                $result = $false
            }   
        }
        # Test Products
        if($null -ne (Compare-Object -ReferenceObject ($Wsus.Products | Sort-Object -Unique) -DifferenceObject ($Products | Sort-Object -Unique) -SyncWindow 0))
        {
            Write-Verbose -Message "Products test failed"
            $result = $false
        }
        # Test Classifications
        if($null -ne (Compare-Object -ReferenceObject ($Wsus.Classifications | Sort-Object -Unique) -DifferenceObject ($Classifications | Sort-Object -Unique) -SyncWindow 0))
        {
            Write-Verbose -Message "Classifications test failed"
            $result = $false
        }
        # Test Synchronization Schedule
        if($SynchronizeAutomatically)
        {
            if($PSBoundParameters.ContainsKey('SynchronizeAutomaticallyTimeOfDay'))
            {
                if($Wsus.SynchronizeAutomaticallyTimeOfDay -ne $SynchronizeAutomaticallyTimeOfDay)
                {
                    Write-Verbose -Message "SynchronizeAutomaticallyTimeOfDay test failed"
                    $result = $false
                }
            }
            if($Wsus.SynchronizationsPerDay -ne $SynchronizationsPerDay)
            {
                Write-Verbose -Message "SynchronizationsPerDay test failed"
                $result = $false
            }
        }
    }

    $result
}

<#
    .SYNOPSIS
    Saves the WSUS configuration
#>

function SaveWsusConfiguration
{
    do
    {
        try
        {
            $WsusConfiguration.Save()
            $WsusConfigurationReady = $true
        }
        catch
        {
            $WsusConfigurationReady = $false
            Start-Sleep -Seconds 1
        }
    }
    until($WsusConfigurationReady)    
}


Export-ModuleMember -Function *-TargetResource