helpers/discovery/Start-WUGDiscoverySetup.ps1

<#
.SYNOPSIS
    Guided first-time setup wizard for WhatsUpGoldPS Discovery.
 
.DESCRIPTION
    Interactive wizard that walks a new user through the complete setup:
 
      Step 1 - WhatsUp Gold server connection (optional)
      Step 2 - Provider selection (which cloud/infra platforms to discover)
      Step 3 - Per-provider configuration (targets, credentials, auth methods)
      Step 4 - Test discovery run per provider
      Step 5 - Optionally schedule recurring discovery tasks
      Step 6 - Optionally schedule dashboard copy to WUG web console
 
    All credentials are stored in the local DPAPI-encrypted vault so
    future runs (interactive or scheduled) are seamless.
 
    Run this script once interactively. After that, providers can be
    run individually via Setup-*-Discovery.ps1 -NonInteractive, or
    on a schedule via Register-DiscoveryScheduledTask.ps1.
 
.PARAMETER SkipWUG
    Skip WhatsUp Gold server configuration (discovery-only mode).
 
.PARAMETER SkipTest
    Skip the test discovery run for each configured provider.
 
.PARAMETER SkipSchedule
    Skip the scheduled task registration prompts.
 
.PARAMETER OutputPath
    Output directory for discovery results and dashboards.
    Default: $env:LOCALAPPDATA\WhatsUpGoldPS\DiscoveryHelpers\Output
 
.EXAMPLE
    .\Start-WUGDiscoverySetup.ps1
 
    Full guided setup with all steps.
 
.EXAMPLE
    .\Start-WUGDiscoverySetup.ps1 -SkipWUG
 
    Set up providers for dashboard/export only (no WUG integration).
 
.EXAMPLE
    .\Start-WUGDiscoverySetup.ps1 -SkipTest -SkipSchedule
 
    Configure providers and credentials only; skip test runs and scheduling.
 
.NOTES
    Author : jason@wug.ninja
    Created : 2025-07-14
    Requires: PowerShell 5.1+
#>

[CmdletBinding()]
param(
    [switch]$SkipWUG,
    [switch]$SkipTest,
    [switch]$SkipSchedule,
    [string]$OutputPath,

    # Vault scope for DPAPI credential encryption.
    # LocalMachine (default): any administrator or SYSTEM process on this machine
    # can decrypt. Required for scheduled tasks running as SYSTEM.
    # CurrentUser: only your user account on this machine can decrypt.
    [ValidateSet('LocalMachine', 'CurrentUser')]
    [string]$VaultScope = 'LocalMachine'
)

# ============================================================================
# region Init
# ============================================================================
$ErrorActionPreference = 'Stop'
$scriptDir = Split-Path $MyInvocation.MyCommand.Path -Parent

# Dot-source shared helpers
. (Join-Path $scriptDir 'DiscoveryHelpers.ps1')

# Set vault scope before any credential operations
if ($VaultScope -eq 'LocalMachine') {
    $isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(
        [Security.Principal.WindowsBuiltInRole]::Administrator)
    if (-not $isAdmin) {
        Write-Host ''
        Write-Host ' [WARNING] LocalMachine vault requires administrator rights.' -ForegroundColor Yellow
        Write-Host ' Credentials will be readable by SYSTEM scheduled tasks ONLY if this' -ForegroundColor Yellow
        Write-Host ' wizard is run as Administrator. Re-run elevated for scheduled task support.' -ForegroundColor Yellow
        Write-Host ' Falling back to CurrentUser vault for this session.' -ForegroundColor DarkGray
        Write-Host ''
        $VaultScope = 'CurrentUser'
    }
}
Set-DiscoveryVaultScope -Scope $VaultScope
Write-Verbose "Vault scope: $VaultScope Path: $script:DiscoveryVaultPath"

if (-not $OutputPath) {
    if ($VaultScope -eq 'LocalMachine') {
        $OutputPath = Join-Path $env:ProgramData 'WhatsUpGoldPS\Output'
    }
    else {
        $OutputPath = Join-Path $env:LOCALAPPDATA 'WhatsUpGoldPS\DiscoveryHelpers\Output'
    }
}
if (-not (Test-Path $OutputPath)) {
    New-Item -ItemType Directory -Path $OutputPath -Force | Out-Null
}

# Provider metadata - defines what each provider needs
$providerDefs = [ordered]@{
    AWS      = @{
        Label       = 'Amazon Web Services (AWS)'
        Script      = 'Setup-AWS-Discovery.ps1'
        TargetLabel = 'AWS region(s) (comma-separated, or "all")'
        TargetDefault = 'all'
        TargetParam = 'Region'
        CredType    = 'AWSKeys'
        CredVault   = 'AWS.Credential'
        AuthChoices = $null
        ApiPort     = $null
        Notes       = 'Requires IAM Access Key + Secret Key with EC2/RDS/ELB read permissions.'
    }
    Azure    = @{
        Label       = 'Microsoft Azure'
        Script      = 'Setup-Azure-Discovery.ps1'
        TargetLabel = 'Subscription ID or name filter (blank = all subscriptions)'
        TargetDefault = ''
        TargetParam = 'SubscriptionFilter'
        CredType    = 'AzureSP'
        CredVault   = 'Azure'
        AuthChoices = $null
        ApiPort     = $null
        Notes       = 'Requires a Service Principal (App Registration) with Reader role.'
    }
    Bigleaf  = @{
        Label       = 'Bigleaf Networks'
        Script      = 'Setup-Bigleaf-Discovery.ps1'
        TargetLabel = 'Bigleaf API target'
        TargetDefault = 'bigleaf'
        TargetParam = 'Target'
        CredType    = 'PSCredential'
        CredVault   = 'Bigleaf.Credential'
        AuthChoices = $null
        ApiPort     = $null
        Notes       = 'Requires Bigleaf portal username + password.'
    }
    CUCM     = @{
        Label       = 'Cisco Unified Communications Manager (CUCM)'
        Script      = 'Setup-CUCM-Discovery.ps1'
        TargetLabel = 'CUCM host(s) - IP or FQDN (comma-separated)'
        TargetDefault = ''
        TargetParam = 'Target'
        CredType    = $null
        CredVault   = 'CUCM.Snmp'
        AuthChoices = @(
            @{ Key = '1'; Label = 'SNMP v2c (community string)'; Value = 'SnmpV2' }
            @{ Key = '2'; Label = 'SNMP v3 (user/auth/privacy)'; Value = 'SnmpV3' }
        )
        ApiPort     = $null
        Notes       = 'Runs on the WUG server using SharpSnmpLib and generates a phone inventory dashboard plus optional SNMPTable phone status monitors.'
    }
    CiscoWLC = @{
        Label       = 'Cisco Wireless LAN Controller (WLC)'
        Script      = 'Setup-CiscoWLC-Discovery.ps1'
        TargetLabel = 'WLC host(s) - IP or FQDN (comma-separated)'
        TargetDefault = ''
        TargetParam = 'Target'
        CredType    = $null
        CredVault   = 'CiscoWLC.Snmp'
        AuthChoices = @(
            @{ Key = '1'; Label = 'SNMP v2c (community string)'; Value = 'SnmpV2' }
            @{ Key = '2'; Label = 'SNMP v3 (user/auth/privacy)'; Value = 'SnmpV3' }
        )
        ApiPort     = $null
        Notes       = 'Discovers APs, clients, WLANs via SNMP LWAPP MIBs. Generates wireless dashboard and optional WUG monitors.'
    }
    Docker   = @{
        Label       = 'Docker Hosts'
        Script      = 'Setup-Docker-Discovery.ps1'
        TargetLabel = 'Docker host(s) - IP or FQDN (comma-separated)'
        TargetDefault = ''
        TargetParam = 'Target'
        CredType    = $null
        CredVault   = $null
        AuthChoices = $null
        ApiPort     = 2375
        Notes       = 'Docker API must be exposed (default port 2375). No credentials needed.'
    }
    F5       = @{
        Label       = 'F5 BIG-IP'
        Script      = 'Setup-F5-Discovery.ps1'
        TargetLabel = 'F5 BIG-IP host(s) - IP or FQDN (comma-separated)'
        TargetDefault = ''
        TargetParam = 'Target'
        CredType    = 'PSCredential'
        CredVault   = $null
        AuthChoices = $null
        ApiPort     = 443
        Notes       = 'Requires iControl REST API access (admin or resource-admin role).'
    }
    Fortinet = @{
        Label       = 'Fortinet FortiGate'
        Script      = 'Setup-Fortinet-Discovery.ps1'
        TargetLabel = 'FortiGate host(s) - IP or FQDN (comma-separated)'
        TargetDefault = ''
        TargetParam = 'Target'
        CredType    = 'BearerToken'
        CredVault   = $null
        AuthChoices = $null
        ApiPort     = 443
        Notes       = 'Requires a REST API token from FortiGate (System > Administrators > REST API).'
    }
    GCP      = @{
        Label       = 'Google Cloud Platform (GCP)'
        Script      = 'Setup-GCP-Discovery.ps1'
        TargetLabel = 'GCP project ID(s) (comma-separated)'
        TargetDefault = ''
        TargetParam = 'Target'
        CredType    = $null
        CredVault   = 'GCP.ServiceAccount'
        AuthChoices = $null
        ApiPort     = $null
        Notes       = 'Requires a service account JSON key file with Compute Viewer role.'
    }
    HyperV   = @{
        Label       = 'Microsoft Hyper-V'
        Script      = 'Setup-HyperV-Discovery.ps1'
        TargetLabel = 'Hyper-V host(s) - IP or FQDN (comma-separated)'
        TargetDefault = ''
        TargetParam = 'Target'
        CredType    = 'PSCredential'
        CredVault   = $null
        AuthChoices = $null
        ApiPort     = $null
        Notes       = 'Requires WinRM/CIM access with Hyper-V admin permissions.'
    }
    LoadMaster = @{
        Label       = 'Kemp LoadMaster'
        Script      = 'Setup-LoadMaster-Discovery.ps1'
        TargetLabel = 'LoadMaster host(s) - IP or FQDN (comma-separated)'
        TargetDefault = ''
        TargetParam = 'Target'
        CredType    = $null
        CredVault   = $null
        AuthChoices = @(
            @{ Key = '1'; Label = 'API Key (recommended for WUG monitoring)'; Value = 'ApiKey'; CredType = 'BearerToken' }
            @{ Key = '2'; Label = 'bal:password (standalone discovery or Basic Auth)'; Value = 'Password'; CredType = 'PSCredential' }
        )
        ApiPort     = 443
        Notes       = 'Requires LoadMaster firmware 7.2.50+ for APIv2 JSON support.'
    }
    MSCluster = @{
        Label       = 'Microsoft Failover Cluster (WSFC/MSCS)'
        Script      = 'Setup-MSCluster-Discovery.ps1'
        TargetLabel = 'Any cluster node - IP or FQDN (comma-separated)'
        TargetDefault = ''
        TargetParam = 'Target'
        CredType    = 'PSCredential'
        CredVault   = 'Windows.WMI.Credential.1'
        AuthChoices = $null
        ApiPort     = $null
        Notes       = 'One node discovers the whole cluster. Requires WMI/DCOM access with local admin rights. Node-local counters go on the nodes; clustered role availability goes on the role virtual IPs. Shares credentials with Windows Attributes.'
    }
    Nutanix  = @{
        Label       = 'Nutanix AHV / Prism'
        Script      = 'Setup-Nutanix-Discovery.ps1'
        TargetLabel = 'Nutanix Prism Central host(s) - IP or FQDN (comma-separated)'
        TargetDefault = ''
        TargetParam = 'Target'
        CredType    = 'PSCredential'
        CredVault   = $null
        AuthChoices = $null
        ApiPort     = 9440
        Notes       = 'Requires Prism Central admin or viewer credentials.'
    }
    NvidiaSmi = @{
        Label       = 'NVIDIA GPU (nvidia-smi via SSH)'
        Script      = 'Setup-NvidiaSmi-Discovery.ps1'
        TargetLabel = 'Linux GPU host(s) - IP or FQDN (comma-separated)'
        TargetDefault = ''
        TargetParam = 'Target'
        CredType    = 'PSCredential'
        CredVault   = 'NvidiaSmi.Ssh'
        AuthChoices = $null
        ApiPort     = $null
        Notes       = 'Requires SSH access to Linux hosts with nvidia-smi installed. Creates WUG SSH performance monitors.'
    }
    OCI      = @{
        Label       = 'Oracle Cloud Infrastructure (OCI)'
        Script      = 'Setup-OCI-Discovery.ps1'
        TargetLabel = 'OCI tenancy OCID (or blank to use config file)'
        TargetDefault = ''
        TargetParam = 'TenancyId'
        CredType    = 'OCIConfig'
        CredVault   = 'OCI.Config'
        AuthChoices = $null
        ApiPort     = $null
        Notes       = 'Requires an OCI config file (~/.oci/config) with API signing key.'
    }
    Proxmox  = @{
        Label       = 'Proxmox VE'
        Script      = 'Setup-Proxmox-Discovery.ps1'
        TargetLabel = 'Proxmox host(s) - IP or FQDN (comma-separated)'
        TargetDefault = ''
        TargetParam = 'Target'
        CredType    = $null
        CredVault   = $null
        AuthChoices = @(
            @{ Key = '1'; Label = 'API Token (recommended for WUG monitoring)'; Value = 'Token'; CredType = 'BearerToken' }
            @{ Key = '2'; Label = 'Username + Password (standalone discovery only)'; Value = 'Password'; CredType = 'PSCredential' }
        )
        ApiPort     = 8006
        Notes       = 'API token: Datacenter > Permissions > API Tokens. Needs PVEAuditor role.'
    }
    VMware   = @{
        Label       = 'VMware vCenter / ESXi'
        Script      = 'Setup-VMware-Discovery.ps1'
        TargetLabel = 'vCenter or ESXi host - IP or FQDN'
        TargetDefault = ''
        TargetParam = 'Target'
        CredType    = 'PSCredential'
        CredVault   = $null
        AuthChoices = $null
        ApiPort     = 443
        Notes       = 'Requires vSphere API access (read-only role sufficient for discovery).'
    }
    WindowsAttributes = @{
        Label       = 'Windows Attributes (WMI inventory)'
        Script      = 'Setup-WindowsAttributes-Discovery.ps1'
        TargetLabel = 'Windows host(s) - IP or FQDN (comma-separated)'
        TargetDefault = ''
        TargetParam = 'Target'
        CredType    = 'PSCredential'
        CredVault   = 'Windows.WMI.Credential.1'
        AuthChoices = $null
        ApiPort     = $null
        Notes       = 'Requires WMI/DCOM or WinRM access with local admin permissions.'
    }
    WindowsDiskIO = @{
        Label       = 'Windows Disk IO (WMI performance monitors)'
        Script      = 'Setup-WindowsDiskIO-Discovery.ps1'
        TargetLabel = 'Windows host(s) - IP or FQDN (comma-separated)'
        TargetDefault = ''
        TargetParam = 'Target'
        CredType    = 'PSCredential'
        CredVault   = 'Windows.WMI.Credential.1'
        AuthChoices = $null
        ApiPort     = $null
        Notes       = 'Requires WMI/DCOM or WinRM access with local admin permissions. Shares credentials with Windows Attributes.'
    }
}
# endregion

# ============================================================================
# region Helper: section header
# ============================================================================
function Write-WizardHeader {
    param([string]$Title, [string]$Step)
    Write-Host ''
    Write-Host ' =================================================================' -ForegroundColor DarkCyan
    if ($Step) {
        Write-Host " STEP $Step - $Title" -ForegroundColor Cyan
    }
    else {
        Write-Host " $Title" -ForegroundColor Cyan
    }
    Write-Host ' =================================================================' -ForegroundColor DarkCyan
    Write-Host ''
}

function Write-WizardNote {
    param([string]$Text)
    Write-Host " $Text" -ForegroundColor Gray
}
# endregion

# ============================================================================
# region Banner
# ============================================================================
Clear-Host
Write-Host ''
Write-Host ' =================================================================' -ForegroundColor Cyan
Write-Host ' WhatsUpGoldPS Discovery - First-Time Setup Wizard' -ForegroundColor White
Write-Host ' =================================================================' -ForegroundColor Cyan
Write-Host ''
Write-Host ' This wizard will walk you through:' -ForegroundColor White
Write-Host ''
Write-Host ' 1. WhatsUp Gold server connection' -ForegroundColor White
Write-Host ' 2. Select discovery providers (cloud + infrastructure)' -ForegroundColor White
Write-Host ' 3. Configure targets and credentials for each provider' -ForegroundColor White
Write-Host ' 4. Test discovery for each provider' -ForegroundColor White
Write-Host ' 5. Schedule recurring discovery tasks' -ForegroundColor White
Write-Host ' 6. Schedule dashboard copy to WUG web console' -ForegroundColor White
Write-Host ''
Write-Host ' All credentials are encrypted in a local DPAPI vault.' -ForegroundColor Gray
Write-Host ' You can re-run this wizard at any time to add/change providers.' -ForegroundColor Gray
Write-Host ''
Write-Host ' Press Ctrl+C at any time to exit.' -ForegroundColor DarkGray
Write-Host ''
$null = Read-Host -Prompt ' Press Enter to begin'
# endregion

# ============================================================================
# region STEP 1 - WUG Server
# ============================================================================
$wugConfigured = $false
$wugServer = $null

if (-not $SkipWUG) {
    Write-WizardHeader -Title 'WhatsUp Gold Server Connection' -Step '1'

    Write-Host ' Do you want to connect to a WhatsUp Gold server?' -ForegroundColor Cyan
    Write-Host ' This enables pushing discovered devices and monitors into WUG.' -ForegroundColor Gray
    Write-Host ' (You can skip this and use dashboards/exports only)' -ForegroundColor Gray
    Write-Host ''
    Write-Host ' [Y] Yes - configure WUG server connection' -ForegroundColor White
    Write-Host ' [N] No - discovery-only mode (dashboards, JSON, CSV)' -ForegroundColor White
    Write-Host ''
    $wugChoice = Read-Host -Prompt ' Choice [Y/N, default: Y]'

    if ($wugChoice -notmatch '^[Nn]') {
        # Use the existing vault-backed credential resolver
        $wugResolved = Resolve-DiscoveryCredential -Name 'WUG.Server' -CredType WUGServer -ProviderLabel 'WhatsUp Gold'
        if ($wugResolved) {
            $wugConfigured = $true
            # Parse the stored connection to get the server address
            if ($wugResolved -is [hashtable] -and $wugResolved.Server) {
                $wugServer = $wugResolved.Server
            }
            elseif ($wugResolved -is [string] -and $wugResolved -match '\|') {
                $parts = $wugResolved -split '\|'
                $wugServer = $parts[0]
            }
            Write-Host ''
            Write-Host ' WUG server connection configured and saved to vault.' -ForegroundColor Green
        }
        else {
            Write-Host ''
            Write-Host ' WUG server not configured. You can still use dashboards and exports.' -ForegroundColor Yellow
        }
    }
    else {
        Write-Host ''
        Write-Host ' Skipped WUG server setup. You can configure it later by running:' -ForegroundColor Yellow
        Write-Host ' Connect-WUGServer' -ForegroundColor Gray
        Write-Host ' or re-running this wizard.' -ForegroundColor Gray
    }
}
else {
    Write-Host ''
    Write-WizardNote 'WUG server setup skipped (-SkipWUG).'
}
# endregion

# ============================================================================
# region STEP 2 - Provider Selection
# ============================================================================
Write-WizardHeader -Title 'Select Discovery Providers' -Step '2'

Write-Host ' Which platforms do you want to discover?' -ForegroundColor Cyan
Write-Host ' Enter the numbers separated by commas (e.g. 1,4,11)' -ForegroundColor Gray
Write-Host ''

$providerKeys = @($providerDefs.Keys)
for ($i = 0; $i -lt $providerKeys.Count; $i++) {
    $key = $providerKeys[$i]
    $def = $providerDefs[$key]
    $num = '{0,2}' -f ($i + 1)
    Write-Host " [$num] $($def.Label)" -ForegroundColor White
}
Write-Host ''
Write-Host ' [ A] All providers' -ForegroundColor DarkGray
Write-Host ''

$selInput = Read-Host -Prompt ' Selection'
$selectedProviders = @()

if ($selInput -match '^[Aa]') {
    $selectedProviders = $providerKeys
}
else {
    $nums = $selInput -split '[,\s]+' | Where-Object { $_ -match '^\d+$' } | ForEach-Object { [int]$_ }
    foreach ($n in $nums) {
        if ($n -ge 1 -and $n -le $providerKeys.Count) {
            $selectedProviders += $providerKeys[$n - 1]
        }
    }
}

if ($selectedProviders.Count -eq 0) {
    Write-Host ''
    Write-Host ' No providers selected. Exiting.' -ForegroundColor Yellow
    return
}

Write-Host ''
Write-Host " Selected $($selectedProviders.Count) provider(s):" -ForegroundColor Green
foreach ($sp in $selectedProviders) {
    Write-Host " - $($providerDefs[$sp].Label)" -ForegroundColor White
}
# endregion

# ============================================================================
# region STEP 3 - Per-Provider Configuration
# ============================================================================
Write-WizardHeader -Title 'Configure Providers' -Step '3'

$providerConfigs = @{}

foreach ($provKey in $selectedProviders) {
    $def = $providerDefs[$provKey]

    Write-Host ''
    Write-Host " --- $($def.Label) ---" -ForegroundColor Cyan
    if ($def.Notes) {
        Write-Host " $($def.Notes)" -ForegroundColor Gray
    }
    Write-Host ''

    # --- Target ---
    $targetValue = $null
    if ($def.TargetLabel) {
        $prompt = " $($def.TargetLabel)"
        if ($def.TargetDefault) {
            $prompt += " [default: $($def.TargetDefault)]"
        }
        $targetInput = Read-Host -Prompt $prompt
        if ([string]::IsNullOrWhiteSpace($targetInput)) {
            $targetValue = $def.TargetDefault
        }
        else {
            $targetValue = $targetInput
        }

        if ([string]::IsNullOrWhiteSpace($targetValue) -and $provKey -notin @('Azure', 'OCI')) {
            Write-Host ' No target specified - skipping this provider.' -ForegroundColor Yellow
            continue
        }
    }

    # --- Auth method (Proxmox-style multi-choice) ---
    $authMethod = $null
    $credType = $def.CredType
    if ($def.AuthChoices) {
        Write-Host ''
        Write-Host ' Authentication method:' -ForegroundColor Cyan
        foreach ($ac in $def.AuthChoices) {
            Write-Host " [$($ac.Key)] $($ac.Label)" -ForegroundColor White
        }
        Write-Host ''
        $authInput = Read-Host -Prompt " Choice [default: $($def.AuthChoices[0].Key)]"
        if ([string]::IsNullOrWhiteSpace($authInput)) { $authInput = $def.AuthChoices[0].Key }
        $selected = $def.AuthChoices | Where-Object { $_.Key -eq $authInput }
        if ($selected) {
            $authMethod = $selected.Value
            $credType = $selected.CredType
        }
        else {
            $authMethod = $def.AuthChoices[0].Value
            $credType = $def.AuthChoices[0].CredType
        }
    }

    # --- API port ---
    $apiPort = $def.ApiPort
    if ($apiPort) {
        $portInput = Read-Host -Prompt " API port [default: $apiPort]"
        if ($portInput -match '^\d+$') { $apiPort = [int]$portInput }
    }

    # --- Credentials ---
    $credResolved = $null
    if ($credType) {
        # Build vault name
        $vaultName = $def.CredVault
        if (-not $vaultName) {
            # Dynamic vault name based on first target
            $firstTarget = if ($targetValue -match ',') { ($targetValue -split ',')[0].Trim() } else { $targetValue }
            if ($authMethod -eq 'Token') {
                $vaultName = "$provKey.$firstTarget.Token"
            }
            elseif ($credType -eq 'BearerToken') {
                $vaultName = "$provKey.$firstTarget.Token"
            }
            else {
                $vaultName = "$provKey.$firstTarget.Credential"
            }
        }

        Write-Host ''
        $credSplat = @{
            Name          = $vaultName
            CredType      = $credType
            ProviderLabel = $provKey
        }
        try {
            $credResolved = Resolve-DiscoveryCredential @credSplat
        }
        catch {
            Write-Host " Error resolving credential for ${provKey}: $_" -ForegroundColor Red
            Write-Host " Skipping $provKey." -ForegroundColor Yellow
            continue
        }
        if (-not $credResolved) {
            Write-Host " Credential not provided - skipping $provKey." -ForegroundColor Yellow
            continue
        }
        Write-Host ''
    }

    # Store config
    $providerConfigs[$provKey] = @{
        Target     = $targetValue
        AuthMethod = $authMethod
        ApiPort    = $apiPort
        CredType   = $credType
        Credential = $credResolved
        Script     = Join-Path $scriptDir $def.Script
    }

    Write-Host " $provKey configured." -ForegroundColor Green
}

if ($providerConfigs.Count -eq 0) {
    Write-Host ''
    Write-Host ' No providers were fully configured. Exiting.' -ForegroundColor Yellow
    return
}

Write-Host ''
Write-Host " $($providerConfigs.Count) provider(s) configured successfully." -ForegroundColor Green
# endregion

# ============================================================================
# region STEP 4 - Test Discovery
# ============================================================================
if (-not $SkipTest) {
    Write-WizardHeader -Title 'Test Discovery Run' -Step '4'

    Write-Host ' Run a test discovery for each configured provider?' -ForegroundColor Cyan
    Write-Host ' This verifies connectivity and credentials are working.' -ForegroundColor Gray
    Write-Host ''
    Write-Host ' [Y] Yes - run test discovery (recommended)' -ForegroundColor White
    Write-Host ' [N] No - skip testing' -ForegroundColor White
    Write-Host ''
    $testChoice = Read-Host -Prompt ' Choice [Y/N, default: Y]'

    if ($testChoice -notmatch '^[Nn]') {
        foreach ($provKey in $providerConfigs.Keys) {
            $cfg = $providerConfigs[$provKey]
            Write-Host ''
            Write-Host " Testing $provKey..." -ForegroundColor Cyan

            try {
                $runArgs = @{
                    Action     = 'Dashboard'
                    OutputPath = $OutputPath
                }
                # SNMP-based providers handle their own credential prompts during first run
                # and save to vault. Other providers already have creds resolved.
                $snmpProviders = @('CUCM', 'CiscoWLC')
                if ($provKey -notin $snmpProviders) {
                    $runArgs['NonInteractive'] = $true
                }

                # Add target
                $targetParam = $providerDefs[$provKey].TargetParam
                if ($targetParam -and $cfg.Target) {
                    if ($cfg.Target -match ',') {
                        $runArgs[$targetParam] = ($cfg.Target -split ',' | ForEach-Object { $_.Trim() })
                    }
                    else {
                        $runArgs[$targetParam] = $cfg.Target
                    }
                }

                # Add auth method (SNMP providers use -SnmpVersion instead of -AuthMethod)
                if ($cfg.AuthMethod) {
                    if ($provKey -in $snmpProviders) {
                        switch ($cfg.AuthMethod) {
                            'SnmpV2' { $runArgs['SnmpVersion'] = 2 }
                            'SnmpV3' { $runArgs['SnmpVersion'] = 3 }
                        }
                    }
                    else {
                        $runArgs['AuthMethod'] = $cfg.AuthMethod
                    }
                }

                # Add API port (CUCM doesn't use this - SnmpPort is defaulted internally)
                if ($cfg.ApiPort) {
                    $runArgs['ApiPort'] = $cfg.ApiPort
                }

                # Extract TenantId for Azure (stored in PSCredential UserName as "TenantId|AppId")
                if ($provKey -eq 'Azure' -and $cfg.Credential -is [PSCredential]) {
                    $azParts = $cfg.Credential.UserName -split '\|', 2
                    if ($azParts.Count -ge 1 -and $azParts[0]) {
                        $runArgs['TenantId'] = $azParts[0]
                    }
                }

                & $cfg.Script @runArgs

                Write-Host " $provKey - test passed." -ForegroundColor Green
                $providerConfigs[$provKey]['TestPassed'] = $true
            }
            catch {
                Write-Host " $provKey - test FAILED: $_" -ForegroundColor Red
                $providerConfigs[$provKey]['TestPassed'] = $false
            }
        }

        # Summary
        Write-Host ''
        Write-Host ' Test Results:' -ForegroundColor Cyan
        foreach ($provKey in $providerConfigs.Keys) {
            $status = if ($providerConfigs[$provKey]['TestPassed']) { 'PASS' } else { 'FAIL' }
            $color  = if ($providerConfigs[$provKey]['TestPassed']) { 'Green' } else { 'Red' }
            Write-Host " $provKey : $status" -ForegroundColor $color
        }
    }
    else {
        Write-Host ' Skipped test discovery.' -ForegroundColor DarkGray
    }
}
else {
    Write-WizardNote 'Test discovery skipped (-SkipTest).'
}
# endregion

# ============================================================================
# region STEP 5 - Schedule Discovery Tasks
# ============================================================================
if (-not $SkipSchedule) {
    Write-WizardHeader -Title 'Schedule Recurring Discovery' -Step '5'

    Write-Host ' Do you want to schedule automatic recurring discovery?' -ForegroundColor Cyan
    Write-Host ' This creates Windows Scheduled Tasks that run discovery' -ForegroundColor Gray
    Write-Host ' on a schedule using your saved vault credentials.' -ForegroundColor Gray
    Write-Host ''
    Write-Host ' [Y] Yes - schedule tasks' -ForegroundColor White
    Write-Host ' [N] No - skip scheduling (run manually)' -ForegroundColor White
    Write-Host ''
    $schedChoice = Read-Host -Prompt ' Choice [Y/N, default: N]'

    if ($schedChoice -match '^[Yy]') {
        # Check if running as Administrator (required for Register-ScheduledTask)
        $isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(
            [Security.Principal.WindowsBuiltInRole]::Administrator
        )
        if (-not $isAdmin) {
            Write-Host ''
            Write-Host ' NOTE: A UAC elevation prompt will appear to register the tasks.' -ForegroundColor Yellow
        }

        # Choose action for scheduled runs
        Write-Host ''
        Write-Host ' What should scheduled discovery do?' -ForegroundColor Cyan
        Write-Host ' [1] Push to WUG (create/update devices + monitors)' -ForegroundColor White
        Write-Host ' [2] Generate dashboards only' -ForegroundColor White
        Write-Host ' [3] Dashboard + Push to WUG' -ForegroundColor White
        Write-Host ' [4] Export JSON' -ForegroundColor White
        Write-Host ''
        $actionChoice = Read-Host -Prompt ' Choice [1-4, default: 3]'
        $schedAction = switch ($actionChoice) {
            '1' { 'PushToWUG' }
            '2' { 'Dashboard' }
            '4' { 'ExportJSON' }
            default { 'DashboardAndPush' }
        }

        # Choose frequency
        Write-Host ''
        Write-Host ' How often should discovery run?' -ForegroundColor Cyan
        Write-Host ' [1] Every hour' -ForegroundColor White
        Write-Host ' [2] Every 2 hours' -ForegroundColor White
        Write-Host ' [3] Every 4 hours' -ForegroundColor White
        Write-Host ' [4] Daily at a set time' -ForegroundColor White
        Write-Host ''
        $freqChoice = Read-Host -Prompt ' Choice [1-4, default: 2]'

        $schedTrigger = 'Hourly'
        $schedInterval = 120
        $schedTime = '02:00'

        switch ($freqChoice) {
            '1' { $schedInterval = 60 }
            '3' { $schedInterval = 240 }
            '4' {
                $schedTrigger = 'Daily'
                $timeInput = Read-Host -Prompt ' Time of day (HH:mm) [default: 02:00]'
                if ($timeInput -match '^\d{1,2}:\d{2}$') { $schedTime = $timeInput }
            }
            default { $schedInterval = 120 }
        }

        # Register task for each provider
        $regScript = Join-Path $scriptDir 'Register-DiscoveryScheduledTask.ps1'

        if (-not (Test-Path $regScript)) {
            Write-Host " Register-DiscoveryScheduledTask.ps1 not found. Skipping." -ForegroundColor Yellow
        }
        else {
            if ($isAdmin) {
                # Already elevated — register directly
                foreach ($provKey in $providerConfigs.Keys) {
                    $cfg = $providerConfigs[$provKey]
                    Write-Host ''
                    Write-Host " Registering scheduled task for $provKey..." -ForegroundColor Cyan

                    try {
                        $regArgs = @{
                            Mode        = 'Provider'
                            Provider    = $provKey
                            Action      = $schedAction
                            TriggerType = $schedTrigger
                            OutputPath  = $OutputPath
                        }
                        if ($VaultScope -eq 'LocalMachine') {
                            $regArgs['UseSystemVault']    = $true
                            $regArgs['SkipVaultPopulate'] = $true
                        }
                        if ($cfg.Target) {
                            if ($cfg.Target -match ',') {
                                $regArgs['Target'] = ($cfg.Target -split ',' | ForEach-Object { $_.Trim() })
                            }
                            else {
                                $regArgs['Target'] = @($cfg.Target)
                            }
                        }
                        if ($cfg.AuthMethod) {
                            $regArgs['AuthMethod'] = $cfg.AuthMethod
                        }
                        if ($wugServer) {
                            $regArgs['WUGServer'] = $wugServer
                        }
                        if ($schedTrigger -eq 'Hourly') {
                            $regArgs['RepeatIntervalMinutes'] = $schedInterval
                        }
                        else {
                            $regArgs['TimeOfDay'] = $schedTime
                        }

                        & $regScript @regArgs
                        Write-Host " $provKey - task registered." -ForegroundColor Green
                    }
                    catch {
                        Write-Host " $provKey - failed to register task: $_" -ForegroundColor Red
                    }
                }
            }
            else {
                # Not elevated — build temporary script and self-elevate via UAC
                Write-Host ''
                Write-Host ' Launching elevated prompt to register tasks...' -ForegroundColor Cyan

                $tempScript = Join-Path $env:TEMP "DiscoverySyncRegister_$(Get-Date -Format yyyyMMdd_HHmmss).ps1"
                $sl = [System.Collections.Generic.List[string]]::new()
                [void]$sl.Add('# Elevated scheduled task registration')
                [void]$sl.Add("`$ErrorActionPreference = 'Stop'")
                [void]$sl.Add("`$regScript = '$($regScript -replace "'","''")'")
                [void]$sl.Add('')

                foreach ($provKey in $providerConfigs.Keys) {
                    $cfg = $providerConfigs[$provKey]
                    [void]$sl.Add("Write-Host ' Registering $provKey...' -ForegroundColor Cyan")
                    [void]$sl.Add('try {')
                    [void]$sl.Add(" `$a = @{")
                    [void]$sl.Add(" Mode = 'Provider'")
                    [void]$sl.Add(" Provider = '$provKey'")
                    [void]$sl.Add(" Action = '$schedAction'")
                    [void]$sl.Add(" TriggerType = '$schedTrigger'")
                    [void]$sl.Add(" OutputPath = '$($OutputPath -replace "'","''")'")
                    [void]$sl.Add(' }')
                    if ($cfg.Target) {
                        if ($cfg.Target -match ',') {
                            $tArr = ($cfg.Target -split ',' | ForEach-Object { "'$($_.Trim())'" }) -join ', '
                            [void]$sl.Add(" `$a['Target'] = @($tArr)")
                        }
                        else {
                            [void]$sl.Add(" `$a['Target'] = @('$($cfg.Target)')")
                        }
                    }
                    if ($cfg.AuthMethod) {
                        [void]$sl.Add(" `$a['AuthMethod'] = '$($cfg.AuthMethod)'")
                    }
                    if ($wugServer) {
                        [void]$sl.Add(" `$a['WUGServer'] = '$wugServer'")
                    }
                    if ($schedTrigger -eq 'Hourly') {
                        [void]$sl.Add(" `$a['RepeatIntervalMinutes'] = $schedInterval")
                    }
                    else {
                        [void]$sl.Add(" `$a['TimeOfDay'] = '$schedTime'")
                    }
                    [void]$sl.Add(' & $regScript @a')
                    [void]$sl.Add(" Write-Host ' $provKey - task registered.' -ForegroundColor Green")
                    [void]$sl.Add('} catch {')
                    [void]$sl.Add(" Write-Host `" $provKey - FAILED: `$_`" -ForegroundColor Red")
                    [void]$sl.Add('}')
                    [void]$sl.Add('')
                }

                [void]$sl.Add("Write-Host ''")
                [void]$sl.Add("Write-Host ' Press Enter to close...' -ForegroundColor Gray")
                [void]$sl.Add('Read-Host')

                ($sl -join "`r`n") | Set-Content -Path $tempScript -Encoding UTF8

                try {
                    Start-Process powershell -Verb RunAs -ArgumentList @(
                        '-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', $tempScript
                    ) -Wait
                    Write-Host ' Elevated registration complete.' -ForegroundColor Green
                }
                catch {
                    Write-Host " Failed to launch elevated prompt: $_" -ForegroundColor Red
                    Write-Host ' Run this wizard as Administrator to register tasks.' -ForegroundColor Yellow
                }
                finally {
                    Remove-Item $tempScript -ErrorAction SilentlyContinue
                }
            }
        }
    }
    else {
        Write-Host ' Skipped scheduling. You can schedule later with:' -ForegroundColor DarkGray
        Write-Host ' .\Register-DiscoveryScheduledTask.ps1 -Mode Provider -Provider <name> ...' -ForegroundColor Gray
    }
}
else {
    Write-WizardNote 'Scheduling skipped (-SkipSchedule).'
}
# endregion

# ============================================================================
# region STEP 6 - Dashboard Copy Task
# ============================================================================
if (-not $SkipSchedule) {
    Write-WizardHeader -Title 'Dashboard Copy to WUG Web Console' -Step '6'

    Write-Host ' Do you want to automatically copy dashboard HTML files' -ForegroundColor Cyan
    Write-Host ' to the WUG web console so they are accessible via browser?' -ForegroundColor Gray
    Write-Host ' (e.g. https://wugserver/NmConsole/dashboards/Proxmox-Dashboard.html)' -ForegroundColor Gray
    Write-Host ''
    Write-Host ' [Y] Yes - schedule dashboard copy' -ForegroundColor White
    Write-Host ' [N] No - skip' -ForegroundColor White
    Write-Host ''
    $dashChoice = Read-Host -Prompt ' Choice [Y/N, default: Y]'

    if ($dashChoice -notmatch '^[Nn]') {
        $copyScript = Join-Path $scriptDir 'Copy-WUGDashboardReports.ps1'

        if (-not (Test-Path $copyScript)) {
            Write-Host ' Copy-WUGDashboardReports.ps1 not found. Skipping.' -ForegroundColor Yellow
        }
        else {
            # Detect or ask for NmConsole path
            $nmCandidates = @(
                "${env:ProgramFiles(x86)}\Ipswitch\WhatsUp\Html\NmConsole"
                "${env:ProgramFiles}\Ipswitch\WhatsUp\Html\NmConsole"
            )
            $nmPath = $nmCandidates | Where-Object { Test-Path $_ } | Select-Object -First 1

            if (-not $nmPath) {
                Write-Host ' WUG NmConsole directory not found at default locations.' -ForegroundColor Yellow
                $nmInput = Read-Host -Prompt ' Enter the NmConsole path (or press Enter to use default)'
                if ($nmInput) {
                    $nmPath = $nmInput
                }
                else {
                    $nmPath = "${env:ProgramFiles(x86)}\Ipswitch\WhatsUp\Html\NmConsole"
                }
            }

            Write-Host " Destination: $nmPath" -ForegroundColor White
            Write-Host ''

            # Check if running as Administrator
            $isAdmin = ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole(
                [Security.Principal.WindowsBuiltInRole]::Administrator
            )

            if ($isAdmin) {
                try {
                    $copyArgs = @{
                        Register    = $true
                        SourcePath  = $OutputPath
                        Destination = $nmPath
                    }
                    & $copyScript @copyArgs
                    Write-Host ' Dashboard copy task registered.' -ForegroundColor Green
                }
                catch {
                    Write-Host " Failed to register dashboard copy task: $_" -ForegroundColor Red
                }
            }
            else {
                Write-Host ' Launching elevated prompt to register copy task...' -ForegroundColor Yellow

                $tempScript = Join-Path $env:TEMP "DashboardCopyRegister_$(Get-Date -Format yyyyMMdd_HHmmss).ps1"
                $cmd = @(
                    "`$copyScript = '$($copyScript -replace "'","''")'",
                    'try {',
                    " & `$copyScript -Register -SourcePath '$($OutputPath -replace "'","''")' -Destination '$($nmPath -replace "'","''")'",
                    " Write-Host ' Dashboard copy task registered.' -ForegroundColor Green",
                    '} catch {',
                    " Write-Host `" FAILED: `$_`" -ForegroundColor Red",
                    '}',
                    "Write-Host ''",
                    "Write-Host ' Press Enter to close...' -ForegroundColor Gray",
                    'Read-Host'
                ) -join "`r`n"
                $cmd | Set-Content -Path $tempScript -Encoding UTF8

                try {
                    Start-Process powershell -Verb RunAs -ArgumentList @(
                        '-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', $tempScript
                    ) -Wait
                    Write-Host ' Elevated registration complete.' -ForegroundColor Green
                }
                catch {
                    Write-Host " Failed to launch elevated prompt: $_" -ForegroundColor Red
                    Write-Host " Manual: .\Copy-WUGDashboardReports.ps1 -SourcePath '$OutputPath' -Destination '$nmPath'" -ForegroundColor Gray
                }
                finally {
                    Remove-Item $tempScript -ErrorAction SilentlyContinue
                }
            }
        }
    }
    else {
        Write-Host ' Skipped dashboard copy scheduling.' -ForegroundColor DarkGray
        Write-Host " Manual: .\Copy-WUGDashboardReports.ps1" -ForegroundColor Gray
    }
}
# endregion

# ============================================================================
# region Summary
# ============================================================================
Write-Host ''
Write-Host ' =================================================================' -ForegroundColor Cyan
Write-Host ' Setup Complete!' -ForegroundColor Green
Write-Host ' =================================================================' -ForegroundColor Cyan
Write-Host ''

if ($wugConfigured) {
    Write-Host ' WUG Server : Configured (saved in vault)' -ForegroundColor Green
}
else {
    Write-Host ' WUG Server : Not configured (discovery-only mode)' -ForegroundColor Yellow
}

Write-Host " Providers : $($providerConfigs.Count) configured" -ForegroundColor White
foreach ($provKey in $providerConfigs.Keys) {
    $testLabel = ''
    if ($providerConfigs[$provKey].ContainsKey('TestPassed')) {
        $testLabel = if ($providerConfigs[$provKey]['TestPassed']) { ' (tested OK)' } else { ' (test failed)' }
    }
    Write-Host " - $provKey$testLabel" -ForegroundColor White
}

Write-Host " Output dir : $OutputPath" -ForegroundColor White
Write-Host " Credential vault: $env:LOCALAPPDATA\WhatsUpGoldPS\DiscoveryHelpers\Vault" -ForegroundColor White
Write-Host ''
Write-Host ' What to do next:' -ForegroundColor Cyan
Write-Host ''
Write-Host ' Run a single provider:' -ForegroundColor White
Write-Host ' .\Setup-Proxmox-Discovery.ps1 -Target 192.168.1.30 -Action Dashboard' -ForegroundColor Gray
Write-Host ''
Write-Host ' Run non-interactively (uses vault creds):' -ForegroundColor White
Write-Host ' .\Setup-Proxmox-Discovery.ps1 -Target 192.168.1.30 -Action PushToWUG -NonInteractive' -ForegroundColor Gray
Write-Host ''
Write-Host ' View scheduled tasks:' -ForegroundColor White
Write-Host ' .\Register-DiscoveryScheduledTask.ps1 -Show' -ForegroundColor Gray
Write-Host ''
Write-Host ' Copy dashboards manually:' -ForegroundColor White
Write-Host ' .\Copy-WUGDashboardReports.ps1' -ForegroundColor Gray
Write-Host ''
Write-Host ' Re-run this wizard:' -ForegroundColor White
Write-Host ' .\Start-WUGDiscoverySetup.ps1' -ForegroundColor Gray
Write-Host ''
Write-Host ' =================================================================' -ForegroundColor Cyan
Write-Host ''
# endregion

# SIG # Begin signature block
# MIIr+wYJKoZIhvcNAQcCoIIr7DCCK+gCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCBuSE7XKTUVkeHN
# TKZL+rcNv7tVkdjKPigUh/iF/e8DfaCCJQ0wggVvMIIEV6ADAgECAhBI/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/mBlXpnYzyOmJRvOwkDynUWICE5EV7WtgwggWNMIIEdaADAgECAhAOmxiO
# +dAt5+/bUOIIQBhaMA0GCSqGSIb3DQEBDAUAMGUxCzAJBgNVBAYTAlVTMRUwEwYD
# VQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAi
# BgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0yMjA4MDEwMDAw
# MDBaFw0zMTExMDkyMzU5NTlaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdp
# Q2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERp
# Z2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCC
# AgoCggIBAL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsb
# hA3EMB/zG6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iT
# cMKyunWZanMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGb
# NOsFxl7sWxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclP
# XuU15zHL2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCr
# VYJBMtfbBHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFP
# ObURWBf3JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTv
# kpI6nj3cAORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWM
# cCxBYKqxYxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls
# 5Q5SUUd0viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBR
# a2+xq4aLT8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjggE6
# MIIBNjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTs1+OC0nFdZEzfLmc/57qY
# rhwPTzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYunpyGd823IDzAOBgNVHQ8BAf8E
# BAMCAYYweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5k
# aWdpY2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0
# LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcnQwRQYDVR0fBD4wPDA6oDig
# NoY0aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9v
# dENBLmNybDARBgNVHSAECjAIMAYGBFUdIAAwDQYJKoZIhvcNAQEMBQADggEBAHCg
# v0NcVec4X6CjdBs9thbX979XB72arKGHLOyFXqkauyL4hxppVCLtpIh3bb0aFPQT
# SnovLbc47/T/gLn4offyct4kvFIDyE7QKt76LVbP+fT3rDB6mouyXtTP0UNEm0Mh
# 65ZyoUi0mcudT6cGAxN3J0TU53/oWajwvy8LpunyNDzs9wPHh6jSTEAZNUZqaVSw
# uKFWjuyk1T3osdz9HNj0d1pcVIxv76FQPfx2CWiEn2/K2yCNNWAcAgPLILCsWKAO
# QGPFmCLBsln1VWvPJ6tsds5vIy30fnFqI2si/xK4VC0nftg62fC2h5b9W9FcrBjD
# TZ9ztwGpn1eqXijiuZQwggYaMIIEAqADAgECAhBiHW0MUgGeO5B5FSCJIRwKMA0G
# CSqGSIb3DQEBDAUAMFYxCzAJBgNVBAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExp
# bWl0ZWQxLTArBgNVBAMTJFNlY3RpZ28gUHVibGljIENvZGUgU2lnbmluZyBSb290
# IFI0NjAeFw0yMTAzMjIwMDAwMDBaFw0zNjAzMjEyMzU5NTlaMFQxCzAJBgNVBAYT
# AkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxKzApBgNVBAMTIlNlY3RpZ28g
# UHVibGljIENvZGUgU2lnbmluZyBDQSBSMzYwggGiMA0GCSqGSIb3DQEBAQUAA4IB
# jwAwggGKAoIBgQCbK51T+jU/jmAGQ2rAz/V/9shTUxjIztNsfvxYB5UXeWUzCxEe
# AEZGbEN4QMgCsJLZUKhWThj/yPqy0iSZhXkZ6Pg2A2NVDgFigOMYzB2OKhdqfWGV
# oYW3haT29PSTahYkwmMv0b/83nbeECbiMXhSOtbam+/36F09fy1tsB8je/RV0mIk
# 8XL/tfCK6cPuYHE215wzrK0h1SWHTxPbPuYkRdkP05ZwmRmTnAO5/arnY83jeNzh
# P06ShdnRqtZlV59+8yv+KIhE5ILMqgOZYAENHNX9SJDm+qxp4VqpB3MV/h53yl41
# aHU5pledi9lCBbH9JeIkNFICiVHNkRmq4TpxtwfvjsUedyz8rNyfQJy/aOs5b4s+
# ac7IH60B+Ja7TVM+EKv1WuTGwcLmoU3FpOFMbmPj8pz44MPZ1f9+YEQIQty/NQd/
# 2yGgW+ufflcZ/ZE9o1M7a5Jnqf2i2/uMSWymR8r2oQBMdlyh2n5HirY4jKnFH/9g
# Rvd+QOfdRrJZb1sCAwEAAaOCAWQwggFgMB8GA1UdIwQYMBaAFDLrkpr/NZZILyhA
# QnAgNpFcF4XmMB0GA1UdDgQWBBQPKssghyi47G9IritUpimqF6TNDDAOBgNVHQ8B
# Af8EBAMCAYYwEgYDVR0TAQH/BAgwBgEB/wIBADATBgNVHSUEDDAKBggrBgEFBQcD
# AzAbBgNVHSAEFDASMAYGBFUdIAAwCAYGZ4EMAQQBMEsGA1UdHwREMEIwQKA+oDyG
# Omh0dHA6Ly9jcmwuc2VjdGlnby5jb20vU2VjdGlnb1B1YmxpY0NvZGVTaWduaW5n
# Um9vdFI0Ni5jcmwwewYIKwYBBQUHAQEEbzBtMEYGCCsGAQUFBzAChjpodHRwOi8v
# Y3J0LnNlY3RpZ28uY29tL1NlY3RpZ29QdWJsaWNDb2RlU2lnbmluZ1Jvb3RSNDYu
# cDdjMCMGCCsGAQUFBzABhhdodHRwOi8vb2NzcC5zZWN0aWdvLmNvbTANBgkqhkiG
# 9w0BAQwFAAOCAgEABv+C4XdjNm57oRUgmxP/BP6YdURhw1aVcdGRP4Wh60BAscjW
# 4HL9hcpkOTz5jUug2oeunbYAowbFC2AKK+cMcXIBD0ZdOaWTsyNyBBsMLHqafvIh
# rCymlaS98+QpoBCyKppP0OcxYEdU0hpsaqBBIZOtBajjcw5+w/KeFvPYfLF/ldYp
# mlG+vd0xqlqd099iChnyIMvY5HexjO2AmtsbpVn0OhNcWbWDRF/3sBp6fWXhz7Dc
# ML4iTAWS+MVXeNLj1lJziVKEoroGs9Mlizg0bUMbOalOhOfCipnx8CaLZeVme5yE
# Lg09Jlo8BMe80jO37PU8ejfkP9/uPak7VLwELKxAMcJszkyeiaerlphwoKx1uHRz
# NyE6bxuSKcutisqmKL5OTunAvtONEoteSiabkPVSZ2z76mKnzAfZxCl/3dq3dUNw
# 4rg3sTCggkHSRqTqlLMS7gjrhTqBmzu1L90Y1KWN/Y5JKdGvspbOrTfOXyXvmPL6
# E52z1NZJ6ctuMFBQZH3pwWvqURR8AgQdULUvrxjUYbHHj95Ejza63zdrEcxWLDX6
# xWls/GDnVNueKjWUH3fTv1Y8Wdho698YADR7TNx8X8z2Bev6SivBBOHY+uqiirZt
# g0y9ShQoPzmCcn63Syatatvx157YK9hlcPmVoa1oDE5/L9Uo2bC5a4CH2RwwggY+
# MIIEpqADAgECAhAHnODk0RR/hc05c892LTfrMA0GCSqGSIb3DQEBDAUAMFQxCzAJ
# BgNVBAYTAkdCMRgwFgYDVQQKEw9TZWN0aWdvIExpbWl0ZWQxKzApBgNVBAMTIlNl
# Y3RpZ28gUHVibGljIENvZGUgU2lnbmluZyBDQSBSMzYwHhcNMjYwMjA5MDAwMDAw
# WhcNMjkwNDIxMjM1OTU5WjBVMQswCQYDVQQGEwJVUzEUMBIGA1UECAwLQ29ubmVj
# dGljdXQxFzAVBgNVBAoMDkphc29uIEFsYmVyaW5vMRcwFQYDVQQDDA5KYXNvbiBB
# bGJlcmlubzCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAPN6aN4B1yYW
# kI5b5TBj3I0VV/peETrHb6EY4BHGxt8Ap+eT+WpEpJyEtRYPxEmNJL3A38Bkg7mw
# zPE3/1NK570ZBCuBjSAn4mSDIgIuXZnvyBO9W1OQs5d67MlJLUAEufl18tOr3ST1
# DeO9gSjQSAE5Nql0QDxPnm93OZBon+Fz3CmE+z3MwAe2h4KdtRAnCqwM+/V7iBdb
# w+JOxolpx+7RVjGyProTENIG3pe/hKvPb501lf8uBAADLdjZr5ip8vIWbf857Yw1
# Bu10nVI7HW3eE8Cl5//d1ribHlzTzQLfttW+k+DaFsKZBBL56l4YAlIVRsrOiE1k
# dHYYx6IGrEA809R7+TZA9DzGqyFiv9qmJAbL4fDwetDeyIq+Oztz1LvEdy8Rcd0J
# BY+J4S0eDEFIA3X0N8VcLeAwabKb9AjulKXwUeqCJLvN79CJ90UTZb2+I+tamj0d
# n+IKMEsJ4v4Ggx72sxFr9+6XziodtTg5Luf2xd6+PhhamOxF2px9LObhBLLEMyRs
# CHZIzVZOFKu9BpHQH7ufGB+Sa80Tli0/6LEyn9+bMYWi2ttn6lLOPThXMiQaooRU
# q6q2u3+F4SaPlxVFLI7OJVMhar6nW6joBvELTJPmANSMjDSRFDfHRCdGbZsL/keE
# LJNy+jZctF6VvxQEjFM8/bazu6qYhrA7AgMBAAGjggGJMIIBhTAfBgNVHSMEGDAW
# gBQPKssghyi47G9IritUpimqF6TNDDAdBgNVHQ4EFgQU6YF0o0D5AVhKHbVocr8G
# aSIBibAwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwEwYDVR0lBAwwCgYI
# KwYBBQUHAwMwSgYDVR0gBEMwQTA1BgwrBgEEAbIxAQIBAwIwJTAjBggrBgEFBQcC
# ARYXaHR0cHM6Ly9zZWN0aWdvLmNvbS9DUFMwCAYGZ4EMAQQBMEkGA1UdHwRCMEAw
# PqA8oDqGOGh0dHA6Ly9jcmwuc2VjdGlnby5jb20vU2VjdGlnb1B1YmxpY0NvZGVT
# aWduaW5nQ0FSMzYuY3JsMHkGCCsGAQUFBwEBBG0wazBEBggrBgEFBQcwAoY4aHR0
# cDovL2NydC5zZWN0aWdvLmNvbS9TZWN0aWdvUHVibGljQ29kZVNpZ25pbmdDQVIz
# Ni5jcnQwIwYIKwYBBQUHMAGGF2h0dHA6Ly9vY3NwLnNlY3RpZ28uY29tMA0GCSqG
# SIb3DQEBDAUAA4IBgQAEIsm4xnOd/tZMVrKwi3doAXvCwOA/RYQnFJD7R/bSQRu3
# wXEK4o9SIefye18B/q4fhBkhNAJuEvTQAGfqbbpxow03J5PrDTp1WPCWbXKX8Oz9
# vGWJFyJxRGftkdzZ57JE00synEMS8XCwLO9P32MyR9Z9URrpiLPJ9rQjfHMb1BUd
# vaNayomm7aWLAnD+X7jm6o8sNT5An1cwEAob7obWDM6sX93wphwJNBJAstH9Ozs6
# LwISOX6sKS7CKm9N3Kp8hOUue0ZHAtZdFl6o5u12wy+zzieGEI50fKnN77FfNKFO
# WKlS6OJwlArcbFegB5K89LcE5iNSmaM3VMB2ADV1FEcjGSHw4lTg1Wx+WMAMdl/7
# nbvfFxJ9uu5tNiT54B0s+lZO/HztwXYQUczdsFon3pjsNrsk9ZlalBi5SHkIu+F6
# g7tWiEv3rtVApmJRnLkUr2Xq2a4nbslUCt4jKs5UX4V1nSX8OM++AXoyVGO+iTj7
# z+pl6XE9Gw/Td6WKKKswgga0MIIEnKADAgECAhANx6xXBf8hmS5AQyIMOkmGMA0G
# CSqGSIb3DQEBCwUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
# IFRydXN0ZWQgUm9vdCBHNDAeFw0yNTA1MDcwMDAwMDBaFw0zODAxMTQyMzU5NTla
# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBUaW1lU3RhbXBpbmcgUlNBNDA5NiBTSEEy
# NTYgMjAyNSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC0eDHT
# CphBcr48RsAcrHXbo0ZodLRRF51NrY0NlLWZloMsVO1DahGPNRcybEKq+RuwOnPh
# of6pvF4uGjwjqNjfEvUi6wuim5bap+0lgloM2zX4kftn5B1IpYzTqpyFQ/4Bt0mA
# xAHeHYNnQxqXmRinvuNgxVBdJkf77S2uPoCj7GH8BLuxBG5AvftBdsOECS1UkxBv
# MgEdgkFiDNYiOTx4OtiFcMSkqTtF2hfQz3zQSku2Ws3IfDReb6e3mmdglTcaarps
# 0wjUjsZvkgFkriK9tUKJm/s80FiocSk1VYLZlDwFt+cVFBURJg6zMUjZa/zbCclF
# 83bRVFLeGkuAhHiGPMvSGmhgaTzVyhYn4p0+8y9oHRaQT/aofEnS5xLrfxnGpTXi
# UOeSLsJygoLPp66bkDX1ZlAeSpQl92QOMeRxykvq6gbylsXQskBBBnGy3tW/AMOM
# CZIVNSaz7BX8VtYGqLt9MmeOreGPRdtBx3yGOP+rx3rKWDEJlIqLXvJWnY0v5ydP
# pOjL6s36czwzsucuoKs7Yk/ehb//Wx+5kMqIMRvUBDx6z1ev+7psNOdgJMoiwOrU
# G2ZdSoQbU2rMkpLiQ6bGRinZbI4OLu9BMIFm1UUl9VnePs6BaaeEWvjJSjNm2qA+
# sdFUeEY0qVjPKOWug/G6X5uAiynM7Bu2ayBjUwIDAQABo4IBXTCCAVkwEgYDVR0T
# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU729TSunkBnx6yuKQVvYv1Ensy04wHwYD
# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
# A1UdJQQMMAoGCCsGAQUFBwMIMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
# cnVzdGVkUm9vdEc0LmNybDAgBgNVHSAEGTAXMAgGBmeBDAEEAjALBglghkgBhv1s
# BwEwDQYJKoZIhvcNAQELBQADggIBABfO+xaAHP4HPRF2cTC9vgvItTSmf83Qh8WI
# GjB/T8ObXAZz8OjuhUxjaaFdleMM0lBryPTQM2qEJPe36zwbSI/mS83afsl3YTj+
# IQhQE7jU/kXjjytJgnn0hvrV6hqWGd3rLAUt6vJy9lMDPjTLxLgXf9r5nWMQwr8M
# yb9rEVKChHyfpzee5kH0F8HABBgr0UdqirZ7bowe9Vj2AIMD8liyrukZ2iA/wdG2
# th9y1IsA0QF8dTXqvcnTmpfeQh35k5zOCPmSNq1UH410ANVko43+Cdmu4y81hjaj
# V/gxdEkMx1NKU4uHQcKfZxAvBAKqMVuqte69M9J6A47OvgRaPs+2ykgcGV00TYr2
# Lr3ty9qIijanrUR3anzEwlvzZiiyfTPjLbnFRsjsYg39OlV8cipDoq7+qNNjqFze
# GxcytL5TTLL4ZaoBdqbhOhZ3ZRDUphPvSRmMThi0vw9vODRzW6AxnJll38F0cuJG
# 7uEBYTptMSbhdhGQDpOXgpIUsWTjd6xpR6oaQf/DJbg3s6KCLPAlZ66RzIg9sC+N
# Jpud/v4+7RWsWCiKi9EOLLHfMR2ZyJ/+xhCx9yHbxtl5TPau1j/1MIDpMPx0LckT
# etiSuEtQvLsNz3Qbp7wGWqbIiOWCnb5WqxL3/BAPvIXKUjPSxyZsq8WhbaM2tszW
# kPZPubdcMIIG7TCCBNWgAwIBAgIQCoDvGEuN8QWC0cR2p5V0aDANBgkqhkiG9w0B
# AQsFADBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/
# BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgVGltZVN0YW1waW5nIFJTQTQwOTYg
# U0hBMjU2IDIwMjUgQ0ExMB4XDTI1MDYwNDAwMDAwMFoXDTM2MDkwMzIzNTk1OVow
# YzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQD
# EzJEaWdpQ2VydCBTSEEyNTYgUlNBNDA5NiBUaW1lc3RhbXAgUmVzcG9uZGVyIDIw
# MjUgMTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBANBGrC0Sxp7Q6q5g
# VrMrV7pvUf+GcAoB38o3zBlCMGMyqJnfFNZx+wvA69HFTBdwbHwBSOeLpvPnZ8ZN
# +vo8dE2/pPvOx/Vj8TchTySA2R4QKpVD7dvNZh6wW2R6kSu9RJt/4QhguSssp3qo
# me7MrxVyfQO9sMx6ZAWjFDYOzDi8SOhPUWlLnh00Cll8pjrUcCV3K3E0zz09ldQ/
# /nBZZREr4h/GI6Dxb2UoyrN0ijtUDVHRXdmncOOMA3CoB/iUSROUINDT98oksouT
# MYFOnHoRh6+86Ltc5zjPKHW5KqCvpSduSwhwUmotuQhcg9tw2YD3w6ySSSu+3qU8
# DD+nigNJFmt6LAHvH3KSuNLoZLc1Hf2JNMVL4Q1OpbybpMe46YceNA0LfNsnqcnp
# JeItK/DhKbPxTTuGoX7wJNdoRORVbPR1VVnDuSeHVZlc4seAO+6d2sC26/PQPdP5
# 1ho1zBp+xUIZkpSFA8vWdoUoHLWnqWU3dCCyFG1roSrgHjSHlq8xymLnjCbSLZ49
# kPmk8iyyizNDIXj//cOgrY7rlRyTlaCCfw7aSUROwnu7zER6EaJ+AliL7ojTdS5P
# WPsWeupWs7NpChUk555K096V1hE0yZIXe+giAwW00aHzrDchIc2bQhpp0IoKRR7Y
# ufAkprxMiXAJQ1XCmnCfgPf8+3mnAgMBAAGjggGVMIIBkTAMBgNVHRMBAf8EAjAA
# MB0GA1UdDgQWBBTkO/zyMe39/dfzkXFjGVBDz2GM6DAfBgNVHSMEGDAWgBTvb1NK
# 6eQGfHrK4pBW9i/USezLTjAOBgNVHQ8BAf8EBAMCB4AwFgYDVR0lAQH/BAwwCgYI
# KwYBBQUHAwgwgZUGCCsGAQUFBwEBBIGIMIGFMCQGCCsGAQUFBzABhhhodHRwOi8v
# b2NzcC5kaWdpY2VydC5jb20wXQYIKwYBBQUHMAKGUWh0dHA6Ly9jYWNlcnRzLmRp
# Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFRpbWVTdGFtcGluZ1JTQTQwOTZT
# SEEyNTYyMDI1Q0ExLmNydDBfBgNVHR8EWDBWMFSgUqBQhk5odHRwOi8vY3JsMy5k
# aWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRUaW1lU3RhbXBpbmdSU0E0MDk2
# U0hBMjU2MjAyNUNBMS5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9
# bAcBMA0GCSqGSIb3DQEBCwUAA4ICAQBlKq3xHCcEua5gQezRCESeY0ByIfjk9iJP
# 2zWLpQq1b4URGnwWBdEZD9gBq9fNaNmFj6Eh8/YmRDfxT7C0k8FUFqNh+tshgb4O
# 6Lgjg8K8elC4+oWCqnU/ML9lFfim8/9yJmZSe2F8AQ/UdKFOtj7YMTmqPO9mzskg
# iC3QYIUP2S3HQvHG1FDu+WUqW4daIqToXFE/JQ/EABgfZXLWU0ziTN6R3ygQBHMU
# BaB5bdrPbF6MRYs03h4obEMnxYOX8VBRKe1uNnzQVTeLni2nHkX/QqvXnNb+YkDF
# kxUGtMTaiLR9wjxUxu2hECZpqyU1d0IbX6Wq8/gVutDojBIFeRlqAcuEVT0cKsb+
# zJNEsuEB7O7/cuvTQasnM9AWcIQfVjnzrvwiCZ85EE8LUkqRhoS3Y50OHgaY7T/l
# wd6UArb+BOVAkg2oOvol/DJgddJ35XTxfUlQ+8Hggt8l2Yv7roancJIFcbojBcxl
# RcGG0LIhp6GvReQGgMgYxQbV1S3CrWqZzBt1R9xJgKf47CdxVRd/ndUlQ05oxYy2
# zRWVFjF7mcr4C34Mj3ocCVccAvlKV9jEnstrniLvUxxVZE/rptb7IRE2lskKPIJg
# baP5t2nGj/ULLi49xTcBZU8atufk+EMF/cWuiC7POGT75qaL6vdCvHlshtjdNXOC
# IUjsarfNZzGCBkQwggZAAgEBMGgwVDELMAkGA1UEBhMCR0IxGDAWBgNVBAoTD1Nl
# Y3RpZ28gTGltaXRlZDErMCkGA1UEAxMiU2VjdGlnbyBQdWJsaWMgQ29kZSBTaWdu
# aW5nIENBIFIzNgIQB5zg5NEUf4XNOXPPdi036zANBglghkgBZQMEAgEFAKCBhDAY
# BgorBgEEAYI3AgEMMQowCKACgAChAoAAMBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3
# AgEEMBwGCisGAQQBgjcCAQsxDjAMBgorBgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEi
# BCAeuGTA9lxpKLa0Wf4LS3bTvlIdQI9AJxe3IUFQZsQq2TANBgkqhkiG9w0BAQEF
# AASCAgAgEkDLJX45807kN+Kw33aziTxjIzZkC9P7g2D1ZzmV8uB7i0mPu0S7+zpm
# l3GDZTKod2qHADY++Ax0e5GZGQrgZafyoWxL2nI6YWrhgqSzhIncDty9nI1NGFdk
# hf9Bng9f86HYNz9wTpOwXfO84pRkzX5/I9HuAMvtg5vrXrNkNPJbQiry5KpX/ztE
# R0IMCn7Wa/AcWhT/dDZd3kiby6flnKwwd7iGF95hKs5Er72i4Thti1bJdeyCBmA+
# OVzs+CJkoL0GohPhkfoLiXn9OpzGCnYumeV15E++FVS74yjoFty+2NDOV5G4p0G5
# fQBRwmyydxJnxZqqnXyNH8BnR6BeKgWi7afP9dY+SlrEmNiMXLjGSf7oYLp7Tghl
# DowcNqMLyHM54YXl0TgthuV7BZK6vJGulN4eR1qU8kIsTljVPwSrHl2sT69TigYb
# iNZHZIykhhS6fp+ypwExILpM0O/IyQ0OOM1JeIi8e4Knd34yfmr0uPJ9pRMIcYj0
# UNG0B1BaMym9QNRrgyrwTx/s8YAshdw383WBpDJI6hnIOZPGRkTeiAm8VTXxkTL5
# OUlr1Z1HcpM3o5Q7N465cjmwTTbeThZm+HT6YY6wCu9LpjyQ8E4ziR6FHtdRev99
# THrcxcn9F5GndIsFt5/jBj2iUZoy2jHtnlfEkpTJFRpp6QkNkqGCAyYwggMiBgkq
# hkiG9w0BCQYxggMTMIIDDwIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5E
# aWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBUaW1l
# U3RhbXBpbmcgUlNBNDA5NiBTSEEyNTYgMjAyNSBDQTECEAqA7xhLjfEFgtHEdqeV
# dGgwDQYJYIZIAWUDBAIBBQCgaTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwG
# CSqGSIb3DQEJBTEPFw0yNjA4MjgyMTI3NTBaMC8GCSqGSIb3DQEJBDEiBCD1iwo0
# CdoUX/Wd+m8RfyoTGjSd0epPmMpdClHrVjUk/TANBgkqhkiG9w0BAQEFAASCAgAp
# 4ADdOj0AflmsNthHwmMLCQL99kBsGRG5c35K5PUJjjCnA4upRcniWcq1SJLmINRl
# m/Gk7oInIMQn5jtXZcnEXWazFKb/RJFx5xPiFxUcVBIX8A/l1akB3azDFNqLK3rZ
# LzrmQ4QfvsZzeyovd9V2jNCdNH40nBcVXSv8n6ZcyjrRdyQuWx7ZnnDE/tLo+HZL
# fwliWRNowoqlBXFwM9juVlJKQePUbhpnGNE4e//welx7WrVfVr4y6Fzi5dfVg3Z8
# Hyp2JBpk+pfQaXpMkzyTpL5taLx63RJSTYgur4JyfmbUCfp0cZkzVmI0bQ5mqgZy
# Yykc7W/at7T6cMXRAKoEGJ57gqo3sq4nc/iQwJCwQKz/n+0a1IBEuhLUaKF4csdb
# Jz260uV3m+pODGKmFnYnmaNovdH7KOQgZFwNueOYn5WtBrWpOe5G3Ed7Lc+6KVef
# xe/kVWDgMdA3i3HXuifawq4+zJ/zqjAODsUWOuOHMcGnRMzwpQcKcW6JW2uP+5Xt
# 5eLVjWJ+DxLNPnmS7Urli8vKfgsV+jCJnhCcxqDJzTFX7ECItme8/qDQQoD+I6bs
# o1mJXXFD9SQ0bTTGZeNPOa3bkZhxQl1r1stn1EgPIa2HcC6CdvZJsucdDDC+yuk2
# 0NQ4KeWHhvZOS00IGGoJEbFUyLch341LgDjoraW4vQ==
# SIG # End signature block