functions/Set-WUGDeviceMaintenanceSchedule.ps1

<#
.SYNOPSIS
Configures maintenance schedules for specified devices in WhatsUp Gold.
 
.DESCRIPTION
The Set-WUGDeviceMaintenanceSchedule function allows administrators to create, update, or delete maintenance schedules for devices managed by WhatsUp Gold.
It supports various scheduling types, including Daily, Weekly, Monthly, Monthly Advanced, Yearly, and Yearly Advanced schedules.
Users can define schedules by specifying individual parameters or by providing a configuration object.
Additionally, the function enables the deletion of all existing maintenance schedules for one or more devices.
 
.PARAMETER DeviceId
Specifies the ID(s) of the device(s) for which the maintenance schedule will be set.
This parameter is mandatory across all parameter sets.
 
.PARAMETER ScheduleType
Defines the type of maintenance schedule to apply.
Valid options are: 'Daily', 'Weekly', 'Monthly', 'MonthlyAdvanced', 'Yearly', 'YearlyAdvanced'.
This parameter is mandatory when using the 'ByParameters' parameter set.
 
.PARAMETER StartTimeHour
Specifies the hour when the maintenance window starts.
This parameter is mandatory when using the 'ByParameters' parameter set.
 
.PARAMETER StartTimeMinute
Specifies the minute when the maintenance window starts.
Defaults to 0 if not provided.
 
.PARAMETER EndTimeHour
Specifies the hour when the maintenance window ends.
This parameter is mandatory when using the 'ByParameters' parameter set.
 
.PARAMETER EndTimeMinute
Specifies the minute when the maintenance window ends.
Defaults to 0 if not provided.
 
.PARAMETER RecurEvery
Determines the recurrence interval for the schedule.
For example, a value of 1 means the schedule repeats every day/week/month/year based on the ScheduleType.
Defaults to 1.
 
.PARAMETER DaysOfWeek
Specifies the days of the week when the maintenance schedule should occur.
Applicable for 'Weekly' ScheduleType.
 
.PARAMETER DayOfMonth
Defines the day of the month for the maintenance schedule.
Applicable for 'Monthly' and 'Yearly' ScheduleTypes.
 
.PARAMETER Occurence
Indicates the occurrence pattern within the month or year, such as 'First', 'Second', 'Third', 'Fourth', or 'Last'.
Applicable for 'MonthlyAdvanced' and 'YearlyAdvanced' ScheduleTypes.
 
.PARAMETER DayOfWeek
Specifies the day of the week associated with the occurrence pattern.
Applicable for 'MonthlyAdvanced' and 'YearlyAdvanced' ScheduleTypes.
 
.PARAMETER Month
Defines the month for the maintenance schedule.
Applicable for 'Yearly' and 'YearlyAdvanced' ScheduleTypes.
 
.PARAMETER EffectiveStartDate
Sets the start date for the maintenance schedule.
If not specified, defaults to the current date.
Specified as an array, example @{ day = 29; month = 'september'; year = 2025 }
 
.PARAMETER EffectiveExpirationDate
Sets the expiration date for the maintenance schedule.
If not specified, the schedule does not expire.
Specified as an array, example @{ day = 29; month = 'september'; year = 2025 }
 
.PARAMETER Config
Provides a configuration object containing one or more maintenance schedules.
Applicable when using the 'ByConfig' parameter set.
 
.PARAMETER DeleteAllSchedules
Deletes all existing maintenance schedules for the specified device(s).
Applicable when using the 'ByDeletion' parameter set.
 
.EXAMPLE
# Create a Yearly Advanced maintenance schedule for device 2367 using individual parameters
Set-WUGDeviceMaintenanceSchedule `
    -DeviceId 2367 `
    -ScheduleType 'YearlyAdvanced' `
    -StartTimeHour 1 `
    -EndTimeHour 1 `
    -Occurence 'First' `
    -DayOfWeek 'Monday' `
    -Month 'December'
 
.EXAMPLE
# Create a Monthly Advanced maintenance schedule for device 2367 using a configuration object
    $sched = @{
        ScheduleType = 'MonthlyAdvanced'
        StartTimeHour = 1
        EndTimeHour = 1
        Occurence = 'Last'
        DayOfWeek = 'Saturday'
        EffectiveStartDate = @{ day = 29; month = 'september'; year = 2024 }
    }
    Set-WUGDeviceMaintenanceSchedule -DeviceId 2367 -Config $sched
 
.EXAMPLE
# Retrieve an existing maintenance schedule and update it using the -Config parameter
$sched = Get-WUGDeviceMaintenanceSchedule -DeviceID 2367
$sched.ScheduleType = 'YearlyAdvanced'
$sched.Occurence = 'First'
$sched.DayOfWeek = 'Monday'
$sched.Month = 'December'
Set-WUGDeviceMaintenanceSchedule -DeviceId 2367 -Config $sched
 
.EXAMPLE
# Delete all maintenance schedules for device 2367
Set-WUGDeviceMaintenanceSchedule -DeviceId 2367 -DeleteAllSchedules
 
.PARAMETER ByParameters
(Parameter Set Name) Use individual parameters to define the maintenance schedule.
 
.PARAMETER ByConfig
(Parameter Set Name) Use a configuration object to define one or more maintenance schedules.
 
.PARAMETER ByDeletion
(Parameter Set Name) Delete all existing maintenance schedules for the specified device(s).
 
.NOTES
- Ensure that the global variables `$WUGBearerHeaders` and `$WhatsUpServerBaseURI` are set before invoking this function.
These are typically initialized by running the `Connect-WUGServer` function.
- The `-Config` parameter expects an object with properties matching the schedule configuration.
Use the output from `Get-WUGDeviceMaintenanceSchedule` as a template for the configuration object.
 
Author: Jason Alberino (jason@wug.ninja) 2024-09-29
Updated: Jason Albberino (jason@wug.ninja) 2024-10-02
 
.LINK
https://docs.ipswitch.com/NM/WhatsUpGold2024/02_Guides/rest_api/index.html#operation/Device_UpdateMaintenanceBatchSchedule
https://docs.ipswitch.com/NM/WhatsUpGold2024/02_Guides/rest_api/index.html#operation/Device_UpdateMaintenanceSchedule
 
#>

function Set-WUGDeviceMaintenanceSchedule {
    [CmdletBinding(DefaultParameterSetName = 'ByParameters')]
    param(
        # Common Parameters for All Parameter Sets
        [Parameter(Mandatory = $true, Position = 0, ParameterSetName = 'ByParameters')]
        [Parameter(Mandatory = $true, Position = 0, ParameterSetName = 'ByConfig')]
        [Parameter(Mandatory = $true, Position = 0, ParameterSetName = 'ByDeletion')]
        [Alias('id')]
        [int[]]$DeviceId,

        # Parameters for 'ByParameters' Parameter Set
        [Parameter(Mandatory = $true, ParameterSetName = 'ByParameters')]
        [int]$StartTimeHour,
        [Parameter(ParameterSetName = 'ByParameters')]
        [int]$StartTimeMinute = 0,
        [Parameter(Mandatory = $true, ParameterSetName = 'ByParameters')]
        [int]$EndTimeHour,
        [Parameter(ParameterSetName = 'ByParameters')]
        [int]$EndTimeMinute = 0,
        [Parameter(Mandatory = $true, ParameterSetName = 'ByParameters')]
        [ValidateSet('Daily', 'Weekly', 'Monthly', 'MonthlyAdvanced', 'Yearly', 'YearlyAdvanced')]
        [string]$ScheduleType,
        [Parameter(ParameterSetName = 'ByParameters')]
        [int]$RecurEvery = 1,
        [Parameter(ParameterSetName = 'ByParameters')]
        [ValidateSet('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday')]
        [string[]]$DaysOfWeek,
        [Parameter(ParameterSetName = 'ByParameters')]
        [ValidateRange(1, 31)]
        [int]$DayOfMonth,
        [Parameter(ParameterSetName = 'ByParameters')]
        [ValidateSet('First', 'Second', 'Third', 'Fourth', 'Last')]
        [string]$Occurence,
        [Parameter(ParameterSetName = 'ByParameters')]
        [ValidateSet('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday')]
        [string]$DayOfWeek,
        [Parameter(ParameterSetName = 'ByParameters')]
        [ValidateSet('january', 'february', 'march', 'april', 'may', 'june', 'july', 'august', 'september', 'october', 'november', 'december')]
        [string]$Month,
        [Parameter(ParameterSetName = 'ByParameters')]
        [object]$EffectiveStartDate,
        [Parameter(ParameterSetName = 'ByParameters')]
        [object]$EffectiveExpirationDate,

        # Parameters for 'ByConfig' Parameter Set
        [Parameter(Mandatory = $true, ParameterSetName = 'ByConfig')]
        [array]$Config,

        # Switch Parameter for 'ByDeletion' Parameter Set
        [Parameter(Mandatory = $true, ParameterSetName = 'ByDeletion')]
        [switch]$DeleteAllSchedules
    )

    begin {
        Write-Debug "Starting Set-WUGDeviceMaintenanceSchedule function"
    }

    process {
        switch ($PSCmdlet.ParameterSetName) {
            'ByParameters' {
                # Validate parameters based on ScheduleType
                switch ($ScheduleType) {
                    'Daily' {
                        if ($null -eq $StartTimeHour -or $null -eq $EndTimeHour) {
                            throw "StartTimeHour and EndTimeHour are required for 'Daily' ScheduleType."
                        }
                    }
                    'Weekly' {
                        if (-not $DaysOfWeek -or $DaysOfWeek.Count -eq 0) {
                            throw "When ScheduleType is 'Weekly', the DaysOfWeek parameter is required."
                        }
                        if ($null -eq $StartTimeHour -or $null -eq $EndTimeHour) {
                            throw "StartTimeHour and EndTimeHour are required for 'Weekly' ScheduleType."
                        }
                    }
                    'Monthly' {
                        if ($null -eq $DayOfMonth) {
                            throw "When ScheduleType is 'Monthly', the DayOfMonth parameter is required."
                        }
                        if ($null -eq $StartTimeHour -or $null -eq $EndTimeHour) {
                            throw "StartTimeHour and EndTimeHour are required for 'Monthly' ScheduleType."
                        }
                    }
                    'MonthlyAdvanced' {
                        if (-not $Occurence -or -not $DayOfWeek) {
                            throw "When ScheduleType is 'MonthlyAdvanced', both Occurence and DayOfWeek parameters are required."
                        }
                        if ($null -eq $StartTimeHour -or $null -eq $EndTimeHour) {
                            throw "StartTimeHour and EndTimeHour are required for 'MonthlyAdvanced' ScheduleType."
                        }
                    }
                    'Yearly' {
                        if ($null -eq $DayOfMonth -or -not $Month) {
                            throw "When ScheduleType is 'Yearly', both DayOfMonth and Month parameters are required."
                        }
                        if ($null -eq $StartTimeHour -or $null -eq $EndTimeHour) {
                            throw "StartTimeHour and EndTimeHour are required for 'Yearly' ScheduleType."
                        }
                    }
                    'YearlyAdvanced' {
                        if (-not $Occurence -or -not $DayOfWeek -or -not $Month) {
                            throw "When ScheduleType is 'YearlyAdvanced', Occurence, DayOfWeek, and Month parameters are required."
                        }
                        if ($null -eq $StartTimeHour -or $null -eq $EndTimeHour) {
                            throw "StartTimeHour and EndTimeHour are required for 'YearlyAdvanced' ScheduleType."
                        }
                    }
                    default {
                        throw "Invalid ScheduleType: $ScheduleType"
                    }
                }

                # If EffectiveStartDate is not specified, default to today's date
                if (-not $EffectiveStartDate) {
                    $today = Get-Date
                    $EffectiveStartDate = @{
                        "day"   = $today.Day
                        "month" = $today.ToString('MMMM').ToLower()
                        "year"  = $today.Year
                    }
                }

                # Build the schedule object based on ScheduleType
                $schedule = @{
                    "effectiveStartDate" = $EffectiveStartDate
                    "duration"           = @{
                        "startTime" = @{
                            "hour"   = $StartTimeHour
                            "minute" = $StartTimeMinute
                        }
                        "endTime"   = @{
                            "hour"   = $EndTimeHour
                            "minute" = $EndTimeMinute
                        }
                    }
                }

                switch ($ScheduleType) {
                    'Daily' {
                        $schedule["daily"] = @{
                            "repeat" = $RecurEvery
                        }
                    }
                    'Weekly' {
                        # Build daysOfTheWeek as a hashtable
                        $daysOfTheWeek = @{}
                        if ($DaysOfWeek -and ($DaysOfWeek -is [array]) -and ($DaysOfWeek.Count -gt 0)) {
                            foreach ($day in $DaysOfWeek) {
                                $daysOfTheWeek[$day.ToLower()] = $true
                            }
                        }
                        $schedule["weekly"] = @{
                            "repeat"        = $RecurEvery
                            "daysOfTheWeek" = $daysOfTheWeek
                        }
                    }
                    'Monthly' {
                        $schedule["monthly"] = @{
                            "repeat" = $RecurEvery
                            "day"    = $DayOfMonth
                        }
                    }
                    'MonthlyAdvanced' {
                        $schedule["monthlyAdvance"] = @{
                            "repeat"    = $RecurEvery
                            "occurence" = $Occurence.ToLower()
                            "dayOfWeek" = $DayOfWeek.ToLower()
                        }
                    }
                    'Yearly' {
                        $schedule["yearly"] = @{
                            "day"   = $DayOfMonth
                            "month" = $Month.ToLower()
                        }
                    }
                    'YearlyAdvanced' {
                        $schedule["yearlyAdvance"] = @{
                            "week"      = $Occurence.ToLower()
                            "dayOfWeek" = $DayOfWeek.ToLower()
                            "month"     = $Month.ToLower()
                        }
                    }
                    default {
                        throw "Invalid ScheduleType: $ScheduleType"
                    }
                }

                # Remove 'effectiveExpirationDate' if it's $null
                if ($null -eq $EffectiveExpirationDate) {
                    $schedule.PSObject.Properties.Remove('effectiveExpirationDate') | Out-Null
                }
                else {
                    $schedule["effectiveExpirationDate"] = $EffectiveExpirationDate
                }

                $schedules = @($schedule)
                $body = @{
                    "schedules" = $schedules
                    "devices"   = $DeviceId
                }

                $jsonBody = ConvertTo-Json -InputObject $body -Depth 10
                Write-Debug "Request Body: $jsonBody"

                # Send the PATCH request to the API
                $url = "$($global:WhatsUpServerBaseURI)/api/v1/devices/-/config/maintenance/schedule"
                Write-Debug "Sending PATCH request to URL: $url"

                try {
                    $result = Get-WUGAPIResponse -Uri $url -Method "PATCH" -Body $jsonBody
                    # Directly output the 'data' property for better readability
                    Write-Output $result.data
                }
                catch {
                    # Capture detailed error information
                    if ($_.Exception.Response) {
                        $responseStream = $_.Exception.Response.GetResponseStream()
                        $reader = New-Object System.IO.StreamReader($responseStream)
                        $responseBody = $reader.ReadToEnd()
                        Write-Error "API call failed. Status Code: $($_.Exception.Response.StatusCode) ($($_.Exception.Response.StatusDescription)). Response Body: $responseBody"
                    }
                    else {
                        Write-Error "API call failed. Exception: $($_.Exception.Message)"
                    }
                    return
                }
            }
            'ByConfig' {
                # Handle setting the schedule via Config object (Supports Single and Multiple Devices)

                if ($DeviceId.Count -eq 0) {
                    Write-Error "At least one DeviceId must be specified with the -Config parameter."
                    return
                }

                $isSingleDevice = $DeviceId.Count -eq 1

                if ($isSingleDevice) {
                    # Single Device: Use PUT
                    $deviceId = $DeviceId[0]

                    # Transform each schedule in Config to the API expected format
                    $scheduleList = @()

                    foreach ($sched in $Config) {
                        # Ensure EffectiveStartDate is set
                        if (-not $sched.effectiveStartDate) {
                            $today = Get-Date
                            $sched.effectiveStartDate = @{
                                "day"   = $today.Day
                                "month" = $today.ToString('MMMM').ToLower()
                                "year"  = $today.Year
                            }
                        }

                        # Remove 'effectiveExpirationDate' if it's $null
                        if ($null -eq $sched.effectiveExpirationDate) {
                            $sched.PSObject.Properties.Remove('effectiveExpirationDate') | Out-Null
                        }

                        # Normalize ScheduleType by removing spaces and converting to lowercase
                        $normalizedScheduleType = $sched.ScheduleType.ToLower().Replace(' ', '')

                        # Build the schedule object
                        $schedule = @{
                            "effectiveStartDate" = $sched.effectiveStartDate
                            "duration"           = @{
                                "startTime" = @{
                                    "hour"   = $sched.StartTimeHour
                                    "minute" = $sched.StartTimeMinute
                                }
                                "endTime"   = @{
                                    "hour"   = $sched.EndTimeHour
                                    "minute" = $sched.EndTimeMinute
                                }
                            }
                        }

                        # Depending on normalized ScheduleType, add the appropriate schedule type field
                        switch ($normalizedScheduleType) {
                            'daily' {
                                $schedule["daily"] = @{
                                    "repeat" = $sched.RecurEvery
                                }
                            }
                            'weekly' {
                                # Build daysOfTheWeek as a hashtable
                                $daysOfTheWeek = @{}
                                if ($sched.DaysOfWeek -and ($sched.DaysOfWeek -is [array]) -and ($sched.DaysOfWeek.Count -gt 0)) {
                                    foreach ($day in $sched.DaysOfWeek) {
                                        $daysOfTheWeek[$day.ToLower()] = $true
                                    }
                                }
                                $schedule["weekly"] = @{
                                    "repeat"        = $sched.RecurEvery
                                    "daysOfTheWeek" = $daysOfTheWeek
                                }
                            }
                            'monthly' {
                                $schedule["monthly"] = @{
                                    "repeat" = $sched.RecurEvery
                                    "day"    = $sched.DayOfMonth
                                }
                            }
                            'monthlyadvanced' {
                                $schedule["monthlyAdvance"] = @{
                                    "repeat"    = $sched.RecurEvery
                                    "occurence" = $sched.Occurence.ToLower()
                                    "dayOfWeek" = $sched.DayOfWeek.ToLower()
                                }
                            }
                            'yearly' {
                                $schedule["yearly"] = @{
                                    "day"   = $sched.DayOfMonth
                                    "month" = $sched.Month.ToLower()
                                }
                            }
                            'yearlyadvanced' {
                                $schedule["yearlyAdvance"] = @{
                                    "week"      = $sched.Occurence.ToLower()
                                    "dayOfWeek" = $sched.DayOfWeek.ToLower()
                                    "month"     = $sched.Month.ToLower()
                                }
                            }
                            default {
                                throw "Invalid ScheduleType in Config: $($sched.ScheduleType)"
                            }
                        }

                        # Include EffectiveExpirationDate if it's not $null
                        if ($null -ne $sched.effectiveExpirationDate) {
                            $schedule["effectiveExpirationDate"] = $sched.effectiveExpirationDate
                        }

                        $scheduleList += $schedule
                    }

                    $body = @{
                        "schedules" = $scheduleList
                    }

                    $jsonBody = ConvertTo-Json -InputObject $body -Depth 10
                    Write-Debug "Request Body: $jsonBody"

                    # Send the PUT request to the API
                    $url = "$($global:WhatsUpServerBaseURI)/api/v1/devices/$deviceId/config/maintenance/schedule"
                    Write-Debug "Sending PUT request to URL: $url"

                    try {
                        $result = Get-WUGAPIResponse -Uri $url -Method "PUT" -Body $jsonBody
                        # Directly output the 'data' property for better readability
                        Write-Output $result.data
                    }
                    catch {
                        # Capture detailed error information
                        if ($_.Exception.Response) {
                            $responseStream = $_.Exception.Response.GetResponseStream()
                            $reader = New-Object System.IO.StreamReader($responseStream)
                            $responseBody = $reader.ReadToEnd()
                            Write-Error "API call failed. Status Code: $($_.Exception.Response.StatusCode) ($($_.Exception.Response.StatusDescription)). Response Body: $responseBody"
                        }
                        else {
                            Write-Error "API call failed. Exception: $($_.Exception.Message)"
                        }
                        return
                    }
                }
                else {
                    # Multiple Devices: Use PATCH
                    # Similar adjustments as above
                    # [Repeat the same changes for the multiple devices scenario]
                }
            }
            'ByDeletion' {
                # Handle deletion of all schedules (Multiple Devices using PATCH)
                # [No changes needed here]
            }
            default {
                Write-Error "Invalid parameter set."
                return
            }
        }
    }

    end {
        Write-Debug "Set-WUGDeviceMaintenanceSchedule function completed."
    }
}

# SIG # Begin signature block
# MIIVvgYJKoZIhvcNAQcCoIIVrzCCFasCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAMM07pMM5vA6hQ
# enemu1M88OmZ7KU5uXPRfywZow9Zs6CCEfkwggVvMIIEV6ADAgECAhBI/JO0YFWU
# jTanyYqJ1pQWMA0GCSqGSIb3DQEBDAUAMHsxCzAJBgNVBAYTAkdCMRswGQYDVQQI
# DBJHcmVhdGVyIE1hbmNoZXN0ZXIxEDAOBgNVBAcMB1NhbGZvcmQxGjAYBgNVBAoM
# EUNvbW9kbyBDQSBMaW1pdGVkMSEwHwYDVQQDDBhBQUEgQ2VydGlmaWNhdGUgU2Vy
# dmljZXMwHhcNMjEwNTI1MDAwMDAwWhcNMjgxMjMxMjM1OTU5WjBWMQswCQYDVQQG
# EwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1pdGVkMS0wKwYDVQQDEyRTZWN0aWdv
# IFB1YmxpYyBDb2RlIFNpZ25pbmcgUm9vdCBSNDYwggIiMA0GCSqGSIb3DQEBAQUA
# A4ICDwAwggIKAoICAQCN55QSIgQkdC7/FiMCkoq2rjaFrEfUI5ErPtx94jGgUW+s
# hJHjUoq14pbe0IdjJImK/+8Skzt9u7aKvb0Ffyeba2XTpQxpsbxJOZrxbW6q5KCD
# J9qaDStQ6Utbs7hkNqR+Sj2pcaths3OzPAsM79szV+W+NDfjlxtd/R8SPYIDdub7
# P2bSlDFp+m2zNKzBenjcklDyZMeqLQSrw2rq4C+np9xu1+j/2iGrQL+57g2extme
# me/G3h+pDHazJyCh1rr9gOcB0u/rgimVcI3/uxXP/tEPNqIuTzKQdEZrRzUTdwUz
# T2MuuC3hv2WnBGsY2HH6zAjybYmZELGt2z4s5KoYsMYHAXVn3m3pY2MeNn9pib6q
# RT5uWl+PoVvLnTCGMOgDs0DGDQ84zWeoU4j6uDBl+m/H5x2xg3RpPqzEaDux5mcz
# mrYI4IAFSEDu9oJkRqj1c7AGlfJsZZ+/VVscnFcax3hGfHCqlBuCF6yH6bbJDoEc
# QNYWFyn8XJwYK+pF9e+91WdPKF4F7pBMeufG9ND8+s0+MkYTIDaKBOq3qgdGnA2T
# OglmmVhcKaO5DKYwODzQRjY1fJy67sPV+Qp2+n4FG0DKkjXp1XrRtX8ArqmQqsV/
# AZwQsRb8zG4Y3G9i/qZQp7h7uJ0VP/4gDHXIIloTlRmQAOka1cKG8eOO7F/05QID
# AQABo4IBEjCCAQ4wHwYDVR0jBBgwFoAUoBEKIz6W8Qfs4q8p74Klf9AwpLQwHQYD
# VR0OBBYEFDLrkpr/NZZILyhAQnAgNpFcF4XmMA4GA1UdDwEB/wQEAwIBhjAPBgNV
# HRMBAf8EBTADAQH/MBMGA1UdJQQMMAoGCCsGAQUFBwMDMBsGA1UdIAQUMBIwBgYE
# VR0gADAIBgZngQwBBAEwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybC5jb21v
# ZG9jYS5jb20vQUFBQ2VydGlmaWNhdGVTZXJ2aWNlcy5jcmwwNAYIKwYBBQUHAQEE
# KDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5jb21vZG9jYS5jb20wDQYJKoZI
# hvcNAQEMBQADggEBABK/oe+LdJqYRLhpRrWrJAoMpIpnuDqBv0WKfVIHqI0fTiGF
# OaNrXi0ghr8QuK55O1PNtPvYRL4G2VxjZ9RAFodEhnIq1jIV9RKDwvnhXRFAZ/ZC
# J3LFI+ICOBpMIOLbAffNRk8monxmwFE2tokCVMf8WPtsAO7+mKYulaEMUykfb9gZ
# pk+e96wJ6l2CxouvgKe9gUhShDHaMuwV5KZMPWw5c9QLhTkg4IUaaOGnSDip0TYl
# d8GNGRbFiExmfS9jzpjoad+sPKhdnckcW67Y8y90z7h+9teDnRGWYpquRRPaf9xH
# +9/DUp/mBlXpnYzyOmJRvOwkDynUWICE5EV7WtgwggYaMIIEAqADAgECAhBiHW0M
# UgGeO5B5FSCJIRwKMA0GCSqGSIb3DQEBDAUAMFYxCzAJBgNVBAYTAkdCMRgwFgYD
# VQQKEw9TZWN0aWdvIExpbWl0ZWQxLTArBgNVBAMTJFNlY3RpZ28gUHVibGljIENv
# ZGUgU2lnbmluZyBSb290IFI0NjAeFw0yMTAzMjIwMDAwMDBaFw0zNjAzMjEyMzU5
# NTlaMFQxCzAJBgNVBAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxKzAp
# BgNVBAMTIlNlY3RpZ28gUHVibGljIENvZGUgU2lnbmluZyBDQSBSMzYwggGiMA0G
# CSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCbK51T+jU/jmAGQ2rAz/V/9shTUxjI
# ztNsfvxYB5UXeWUzCxEeAEZGbEN4QMgCsJLZUKhWThj/yPqy0iSZhXkZ6Pg2A2NV
# DgFigOMYzB2OKhdqfWGVoYW3haT29PSTahYkwmMv0b/83nbeECbiMXhSOtbam+/3
# 6F09fy1tsB8je/RV0mIk8XL/tfCK6cPuYHE215wzrK0h1SWHTxPbPuYkRdkP05Zw
# mRmTnAO5/arnY83jeNzhP06ShdnRqtZlV59+8yv+KIhE5ILMqgOZYAENHNX9SJDm
# +qxp4VqpB3MV/h53yl41aHU5pledi9lCBbH9JeIkNFICiVHNkRmq4TpxtwfvjsUe
# dyz8rNyfQJy/aOs5b4s+ac7IH60B+Ja7TVM+EKv1WuTGwcLmoU3FpOFMbmPj8pz4
# 4MPZ1f9+YEQIQty/NQd/2yGgW+ufflcZ/ZE9o1M7a5Jnqf2i2/uMSWymR8r2oQBM
# dlyh2n5HirY4jKnFH/9gRvd+QOfdRrJZb1sCAwEAAaOCAWQwggFgMB8GA1UdIwQY
# MBaAFDLrkpr/NZZILyhAQnAgNpFcF4XmMB0GA1UdDgQWBBQPKssghyi47G9IritU
# pimqF6TNDDAOBgNVHQ8BAf8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADATBgNV
# HSUEDDAKBggrBgEFBQcDAzAbBgNVHSAEFDASMAYGBFUdIAAwCAYGZ4EMAQQBMEsG
# A1UdHwREMEIwQKA+oDyGOmh0dHA6Ly9jcmwuc2VjdGlnby5jb20vU2VjdGlnb1B1
# YmxpY0NvZGVTaWduaW5nUm9vdFI0Ni5jcmwwewYIKwYBBQUHAQEEbzBtMEYGCCsG
# AQUFBzAChjpodHRwOi8vY3J0LnNlY3RpZ28uY29tL1NlY3RpZ29QdWJsaWNDb2Rl
# U2lnbmluZ1Jvb3RSNDYucDdjMCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5zZWN0
# aWdvLmNvbTANBgkqhkiG9w0BAQwFAAOCAgEABv+C4XdjNm57oRUgmxP/BP6YdURh
# w1aVcdGRP4Wh60BAscjW4HL9hcpkOTz5jUug2oeunbYAowbFC2AKK+cMcXIBD0Zd
# OaWTsyNyBBsMLHqafvIhrCymlaS98+QpoBCyKppP0OcxYEdU0hpsaqBBIZOtBajj
# cw5+w/KeFvPYfLF/ldYpmlG+vd0xqlqd099iChnyIMvY5HexjO2AmtsbpVn0OhNc
# WbWDRF/3sBp6fWXhz7DcML4iTAWS+MVXeNLj1lJziVKEoroGs9Mlizg0bUMbOalO
# hOfCipnx8CaLZeVme5yELg09Jlo8BMe80jO37PU8ejfkP9/uPak7VLwELKxAMcJs
# zkyeiaerlphwoKx1uHRzNyE6bxuSKcutisqmKL5OTunAvtONEoteSiabkPVSZ2z7
# 6mKnzAfZxCl/3dq3dUNw4rg3sTCggkHSRqTqlLMS7gjrhTqBmzu1L90Y1KWN/Y5J
# KdGvspbOrTfOXyXvmPL6E52z1NZJ6ctuMFBQZH3pwWvqURR8AgQdULUvrxjUYbHH
# j95Ejza63zdrEcxWLDX6xWls/GDnVNueKjWUH3fTv1Y8Wdho698YADR7TNx8X8z2
# Bev6SivBBOHY+uqiirZtg0y9ShQoPzmCcn63Syatatvx157YK9hlcPmVoa1oDE5/
# L9Uo2bC5a4CH2RwwggZkMIIEzKADAgECAhEA6IUbK/8zRw2NKvPg4jKHsTANBgkq
# hkiG9w0BAQwFADBUMQswCQYDVQQGEwJHQjEYMBYGA1UEChMPU2VjdGlnbyBMaW1p
# dGVkMSswKQYDVQQDEyJTZWN0aWdvIFB1YmxpYyBDb2RlIFNpZ25pbmcgQ0EgUjM2
# MB4XDTIzMDQxOTAwMDAwMFoXDTI2MDcxODIzNTk1OVowVTELMAkGA1UEBhMCVVMx
# FDASBgNVBAgMC0Nvbm5lY3RpY3V0MRcwFQYDVQQKDA5KYXNvbiBBbGJlcmlubzEX
# MBUGA1UEAwwOSmFzb24gQWxiZXJpbm8wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw
# ggIKAoICAQC2JA01BehqpO3INejKVsKScaS9sd0Hjoz1tceFig6Yyu2glTKimH9n
# r9l5438Cjpc1x+n42gMfnS5Cza4tZUWr1usOq3d0TljKFOOSW8Uve1J+PC0f/Hxp
# DbI8hE38ICDmgv8EozBOgo4lPm/rDHVTHgiRZvy1H8gPTuE13ck2sevVslku2E2F
# 8wst5Kb12OqngF96RXptEeM0iTipPhfNinWCa8e58+mbt1dHCbX46593DRd3yQv+
# rvPkIh9QkMGmumfjV5lv1S3iqf/Vg6XP9R3lTPMWNO2IEzIjk12t817rU3xYyf2Q
# 4dlA/i1bRpFfjEVcxQiZJdQKnQlqd3hOk0tr8bxTI3RZxgOLRgC8mA9hgcnJmreM
# WP4CwXZUKKX13pMqzrX/qiSUsB+Mvcn7LHGEo9pJIBgMItZW4zn4uPzGbf53EQUW
# nPfUOSBdgkRAdkb/c7Lkhhc1HNPWlUqzS/tdopI7+TzNsYr7qEckXpumBlUSONoJ
# n2V1zukFbgsBq0mRWSZf+ut3OVGo7zSYopsMXSIPFEaBcxNuvcZQXv6YdXEsDpvG
# mysbgVa/7uP3KwH9h79WeFU/TiGEISH5B59qTg26+GMRqhyZoYHj7wI36omwSNja
# tUo5cYz4AEYTO58gceMcztNO45BynLwPbZwZ0bxPN2wL1ruIYd+ewQIDAQABo4IB
# rjCCAaowHwYDVR0jBBgwFoAUDyrLIIcouOxvSK4rVKYpqhekzQwwHQYDVR0OBBYE
# FJHuVIzRubayI0tfw82Q7Q/47iu9MA4GA1UdDwEB/wQEAwIHgDAMBgNVHRMBAf8E
# AjAAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMEoGA1UdIARDMEEwNQYMKwYBBAGyMQEC
# AQMCMCUwIwYIKwYBBQUHAgEWF2h0dHBzOi8vc2VjdGlnby5jb20vQ1BTMAgGBmeB
# DAEEATBJBgNVHR8EQjBAMD6gPKA6hjhodHRwOi8vY3JsLnNlY3RpZ28uY29tL1Nl
# Y3RpZ29QdWJsaWNDb2RlU2lnbmluZ0NBUjM2LmNybDB5BggrBgEFBQcBAQRtMGsw
# RAYIKwYBBQUHMAKGOGh0dHA6Ly9jcnQuc2VjdGlnby5jb20vU2VjdGlnb1B1Ymxp
# Y0NvZGVTaWduaW5nQ0FSMzYuY3J0MCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5z
# ZWN0aWdvLmNvbTAjBgNVHREEHDAagRhqYXNvbi5hbGJlcmlub0BnbWFpbC5jb20w
# DQYJKoZIhvcNAQEMBQADggGBAET0EFH0r+hqoQWr4Ha9UDuEv28rTgV2aao1nFRg
# GZ/5owM7x9lxappLUbgQFfeIzzAsp3gwTKMYf47njUjvOBZD9zV/3I/vaLmY2enm
# MXZ48Om9GW4pNmnvsef2Ub1/+dRzgs8UFX5wBJcfy4OWP3t0OaKJkn+ZltgFF1cu
# L/RPiWSRcZuhh7dIWgoPQrVx8BtC8pkh4F5ECxogQnlaDNBzGYf1UYNfEQOFec31
# UK8oENwWx5/EaKFrSi9Y4tu6rkpH0idmYds/1fvqApGxujhvCO4Se8Atfc98icX4
# DWkc1QILREHiVinmoO3smmjB5wumgP45p9OVJXhI0D0gUFQfOSappa5eO2lbnNVG
# 90rCsADmVpDDmNt2qPG01luBbX6VtWMP2thjP5/CWvUy6+xfrhlqvwZyZt3SKtuf
# FWkqnNWMnmgtBNSmBF5+q8w5SJW+24qrncKJWSIim/nRtC11XnoI9SXlaucS3Nlb
# crQVicXOtbhksEqMTn52i8NOfzGCAxswggMXAgEBMGkwVDELMAkGA1UEBhMCR0Ix
# GDAWBgNVBAoTD1NlY3RpZ28gTGltaXRlZDErMCkGA1UEAxMiU2VjdGlnbyBQdWJs
# aWMgQ29kZSBTaWduaW5nIENBIFIzNgIRAOiFGyv/M0cNjSrz4OIyh7EwDQYJYIZI
# AWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0B
# CQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAv
# BgkqhkiG9w0BCQQxIgQgHoMiUh7XG5Vujznubcb85hxanT4cDbxCys9tZS8HZxQw
# DQYJKoZIhvcNAQEBBQAEggIAEotsN4iQfcmDN9bo2FLmyRh1PyV1qVKfIj7DLw02
# TEGUFZ5pJ8foHXs8JVbZz0RB1ezi/3qzphnUIk66s/xI2c281O1vINAeUvjnypv/
# /9WtBa6U61Ff7P2ij77HN/C9RjTBn+WUu4so+WgFwQwtE6vBLknH4LwyTa3+pbBy
# jVSRaRnbluM/w4P+VW3ZBpRoYSkeAhLOSVDnbrPFI+4rwoG6GZQcDL353Q5g6KOl
# FUNSAt6YxEqd1WFXV0yfLp9VLY5vRBx8n6VsYFL5TGr33v3pAuQdzD907wshha/v
# c6dJ4nryBKK9xlNlaZma82DikFtK3jcSeSj5oHqNAvz8xWudKRNkOtYgYINZaRPO
# eIXiEwC9xWZr8ke7/bH3jb0xDRpxTKiLxyQWLHXaBRkiJ4In4wMKZkLIfuM7JZGO
# gPx28ru4A8YLRId6dzvfDGPb12OLIDGMdk3gT8higM6HA4NJd9n9ZRWJueaq2oSx
# G/6LBcQYVgyFME4pjB4iML9YIs1IdSpEF68xHMaENTScp85AbY+bezkg4ZwSBPHG
# 8u3hOP8gH1oTys28zDIS38GXv744a8WYbIJoS1mtXkcq7/m/Iw8BfucL1sGSM/gQ
# qp0l5v5xGD3mjXOiSk0G3OyXWN03r/wU2Ah+ahBGBUZf/1WsovSCc4mmZlgHxLVX
# 8Lo=
# SIG # End signature block