helpers/discovery/Setup-VMware-Discovery.ps1
|
<#
.SYNOPSIS VMware vSphere Discovery -- Discover ESXi hosts/VMs and optionally push to WhatsUp Gold. .DESCRIPTION Interactive script that discovers VMware vSphere infrastructure, then lets you choose what to do with the results: [1] Push monitors to WhatsUp Gold (creates devices + monitors) [2] Export discovery plan to JSON [3] Export discovery plan to CSV [4] Show full plan table in console [5] Generate VMware HTML dashboard (live metrics) [6] Exit Two collection methods: [1] VMware PowerCLI -- uses Connect-VIServer, Get-VMHost, Get-VM, etc. [2] REST API (direct) -- zero external dependencies, uses vCenter REST API First Run: 1. Prompts for collection method, vCenter server, port, credentials 2. Stores credentials in DPAPI vault (encrypted to user + machine) 3. Discovers ESXi hosts + VMs from vCenter 4. Shows summary, then asks what to do with the results Subsequent Runs: Loads credentials from vault automatically -- skips credential prompt. .PARAMETER Target vCenter server or ESXi host — IP address or FQDN. When omitted in interactive mode, prompts for input. .PARAMETER ApiPort vCenter port. Default: 443. .PARAMETER UseRestApi Use vCenter REST API (no PowerCLI needed, vSphere 6.5+ required). Default: true. Pass -UseRestApi:$false for VMware PowerCLI mode. .PARAMETER Action What to do with discovery results. When specified, skips the interactive menu. Valid values: PushToWUG, ExportJSON, ExportCSV, ShowTable, Dashboard, None. .PARAMETER WUGServer WhatsUp Gold server address. Default: 192.168.74.74. .PARAMETER WUGCredential PSCredential for WhatsUp Gold admin login (non-interactive WUG push). .PARAMETER NonInteractive Suppress all prompts. Uses cached vault credentials and parameter defaults. Ideal for scheduled task execution. .EXAMPLE .\Setup-VMware-Discovery.ps1 # Interactive mode — prompts for everything. .EXAMPLE .\Setup-VMware-Discovery.ps1 -Target 'vcenter01.lab.local' -Action ExportJSON -NonInteractive # Scheduled mode — uses vault credentials, exports JSON, no prompts. .NOTES WhatsUpGoldPS module is only needed if you choose option [1]. REST API mode has zero external module dependencies (vSphere 6.5+ required). Module mode requires: VMware.PowerCLI. #> [CmdletBinding()] param( [string]$Target, [int]$ApiPort = 443, [switch]$UseRestApi, [ValidateSet('PushToWUG', 'ExportJSON', 'ExportCSV', 'ShowTable', 'Dashboard', 'DashboardAndPush', 'None')] [string]$Action, [string]$WUGServer = '192.168.74.74', [PSCredential]$WUGCredential, [string]$OutputPath, [switch]$NonInteractive ) # --- Output directory (persistent default for scheduled runs) ----------------- if (-not $OutputPath) { if ($NonInteractive) { $OutputPath = Join-Path $env:LOCALAPPDATA 'WhatsUpGoldPS\DiscoveryHelpers\Output' } else { $OutputPath = $env:TEMP } } if (-not (Test-Path $OutputPath)) { New-Item -ItemType Directory -Path $OutputPath -Force | Out-Null } $OutputDir = $OutputPath # --- Configuration ----------------------------------------------------------- $DefaultServer = 'vcenter01.lab.local' # Default vCenter server (interactive fallback) $DefaultPort = $ApiPort # Default to REST API if not explicitly set if (-not $PSBoundParameters.ContainsKey('UseRestApi')) { $UseRestApi = $true } # --- Load helpers (works from any directory) ---------------------------------- $scriptDir = Split-Path $MyInvocation.MyCommand.Path -Parent . (Join-Path $scriptDir 'DiscoveryHelpers.ps1') . (Join-Path $scriptDir 'DiscoveryProvider-VMware.ps1') # ============================================================================== # STEP 1: Gather target info # ============================================================================== Write-Host "=== VMware vSphere Discovery ===" -ForegroundColor Cyan Write-Host "" # --- Collection method --------------------------------------------------------- if (-not $PSBoundParameters.ContainsKey('UseRestApi') -and -not $NonInteractive) { Write-Host "VMware data collection method:" -ForegroundColor Cyan Write-Host " [1] VMware PowerCLI modules (requires VMware.PowerCLI)" -ForegroundColor White Write-Host " [2] REST API direct (zero external dependencies, vSphere 6.5+)" -ForegroundColor White Write-Host "" $methodChoice = Read-Host -Prompt "Choice [1/2, default: 2]" $UseRestApi = ($methodChoice -ne '1') } if ($UseRestApi) { Write-Host "Using REST API mode (no PowerCLI needed)." -ForegroundColor Green } else { Write-Host "Using VMware PowerCLI module mode." -ForegroundColor Green # --- Check for PowerCLI ------------------------------------------------------- if (-not (Get-Module -ListAvailable -Name VMware.PowerCLI -ErrorAction SilentlyContinue)) { Write-Warning "VMware.PowerCLI module not found. Install it with:" Write-Host " Install-Module -Name VMware.PowerCLI -Scope CurrentUser -Force" -ForegroundColor Yellow if ($NonInteractive) { Write-Error "VMware.PowerCLI module not found and cannot install in non-interactive mode." return } Write-Host "" $installChoice = Read-Host -Prompt "Attempt to install now? [y/N]" if ($installChoice -eq 'y' -or $installChoice -eq 'Y') { try { Install-Module -Name VMware.PowerCLI -Scope CurrentUser -Force -ErrorAction Stop Write-Host "VMware.PowerCLI installed successfully." -ForegroundColor Green } catch { Write-Error "Failed to install VMware.PowerCLI: $_" return } } else { Write-Host "Cannot proceed without PowerCLI. Exiting." -ForegroundColor Red return } } } # --- Resolve vCenter server ----------------------------------------------------- if ($Target) { $VMwareServer = $Target } elseif ($NonInteractive) { $VMwareServer = $DefaultServer } else { Write-Host "Enter vCenter Server or ESXi host -- IP address or FQDN." -ForegroundColor Cyan $serverInput = Read-Host -Prompt "vCenter server [default: $DefaultServer]" if ([string]::IsNullOrWhiteSpace($serverInput)) { $serverInput = $DefaultServer } $VMwareServer = $serverInput.Trim() } Write-Host "Target: $VMwareServer" -ForegroundColor Cyan Write-Host "" # --- Resolve port -------------------------------------------------------------- if (-not $PSBoundParameters.ContainsKey('ApiPort') -and -not $NonInteractive) { $portInput = Read-Host -Prompt "vCenter port [default: $DefaultPort]" if ($portInput -and $portInput -match '^\d+$') { $DefaultPort = [int]$portInput } } # ============================================================================== # STEP 2: Credentials (DPAPI vault -- encrypted, cached) # ============================================================================== $vaultName = "VMware.$VMwareServer.Credential" $credSplat = @{ Name = $vaultName; CredType = 'PSCredential'; ProviderLabel = 'VMware' } if ($NonInteractive) { $credSplat.NonInteractive = $true } elseif ($Action) { $credSplat.AutoUse = $true } $VMwareCred = Resolve-DiscoveryCredential @credSplat if (-not $VMwareCred) { Write-Error 'No credentials provided. Exiting.' return } # ============================================================================== # STEP 3: Discover -- query vCenter for hosts + VMs # ============================================================================== Write-Host "" Write-Host "Connecting to vCenter at $VMwareServer..." -ForegroundColor Cyan $plan = Invoke-Discovery -ProviderName 'VMware' ` -Target @($VMwareServer) ` -ApiPort $DefaultPort ` -Credential @{ Username = $VMwareCred.UserName; Password = $VMwareCred.GetNetworkCredential().Password; PSCredential = $VMwareCred; UseRestApi = $UseRestApi } if (-not $plan -or $plan.Count -eq 0) { Write-Warning "No items discovered. Check vCenter connectivity and credentials." return } # ============================================================================== # STEP 4: Show the plan # ============================================================================== # Group items by target device $devicePlan = [ordered]@{} foreach ($item in $plan) { if (-not $item -or -not $item.Attributes) { continue } $type = $item.Attributes['VMware.DeviceType'] switch ($type) { 'vCenter' { $key = "vcenter:$($item.Attributes['VMware.vCenter'])" $name = $item.Attributes['VMware.vCenter'] $ip = $item.Attributes['VMware.vCenter'] $parentHost = $null } 'ESXiHost' { $hostName = $item.Attributes['VMware.HostName'] $key = "host:${hostName}" $name = $hostName $ip = $item.Attributes['VMware.HostIP'] $parentHost = $null } 'VM' { $vmName = $item.Attributes['VMware.VMName'] $key = "vm:${vmName}" $name = $vmName $ip = $item.Attributes['VMware.VMIP'] $parentHost = $item.Attributes['VMware.ESXiHost'] } default { continue } } if (-not $devicePlan.Contains($key)) { $devicePlan[$key] = @{ Name = $name IP = $ip Type = $type ParentHost = $parentHost Attrs = $item.Attributes Items = [System.Collections.ArrayList]@() } } [void]$devicePlan[$key].Items.Add($item) } $hostDevices = @($devicePlan.Values | Where-Object { $_.Type -eq 'ESXiHost' }) $vmDevices = @($devicePlan.Values | Where-Object { $_.Type -eq 'VM' }) $vmWithIP = @($vmDevices | Where-Object { $_.IP }) $vmNoIP = @($vmDevices | Where-Object { -not $_.IP }) $activeTemplates = @($plan | Where-Object { $_.ItemType -eq 'ActiveMonitor' } | Select-Object -ExpandProperty Name -Unique) $perfTemplates = @($plan | Where-Object { $_.ItemType -eq 'PerformanceMonitor' } | Select-Object -ExpandProperty Name -Unique) Write-Host "" Write-Host "Discovery complete!" -ForegroundColor Green Write-Host " ESXi Hosts: $($hostDevices.Count)" -ForegroundColor White Write-Host " VMs (with IP): $($vmWithIP.Count)" -ForegroundColor White Write-Host " VMs (no IP/no agent): $($vmNoIP.Count)" -ForegroundColor White Write-Host " Total WUG devices: $($hostDevices.Count + $vmWithIP.Count + 1) (hosts + VMs w/ IP + vCenter)" -ForegroundColor White Write-Host "" Write-Host " Active monitor templates: $($activeTemplates.Count) ($(@($plan | Where-Object { $_.ItemType -eq 'ActiveMonitor' }).Count) assignments)" -ForegroundColor White Write-Host " Perf monitor templates: $($perfTemplates.Count) ($(@($plan | Where-Object { $_.ItemType -eq 'PerformanceMonitor' }).Count) assignments)" -ForegroundColor White Write-Host " Total plan items: $($plan.Count)" -ForegroundColor White Write-Host "" Write-Host "Monitor templates:" -ForegroundColor Cyan foreach ($t in $activeTemplates) { Write-Host " [Active] $t" -ForegroundColor White } foreach ($t in $perfTemplates) { Write-Host " [Perf] $t" -ForegroundColor White } Write-Host "" if ($vmNoIP.Count -gt 0) { Write-Host "VMs without IP (VMware Tools needed -- monitors attach to parent host):" -ForegroundColor Yellow foreach ($vm in $vmNoIP) { Write-Host " $($vm.Name) (on $($vm.ParentHost))" -ForegroundColor DarkYellow } Write-Host "" } $devicePlan.Values | Sort-Object @{E={$_.Type}}, @{E={$_.Name}} | ForEach-Object { [PSCustomObject]@{ Device = $_.Name Type = $_.Type IP = if ($_.IP) { $_.IP } else { '(via host)' } Monitors = $_.Items.Count Attrs = $_.Attrs.Count }} | Format-Table -AutoSize # ============================================================================== # STEP 5: Export or push to WUG # ============================================================================== # --- Map Action parameter to menu choice number --- $choice = $null if ($Action) { switch ($Action) { 'PushToWUG' { $choice = '1' } 'ExportJSON' { $choice = '2' } 'ExportCSV' { $choice = '3' } 'ShowTable' { $choice = '4' } 'Dashboard' { $choice = '5' } 'None' { $choice = '6' } 'DashboardAndPush' { $choice = '7' } } } if (-not $choice -and $NonInteractive) { $choice = '5' } if (-not $choice) { Write-Host "What would you like to do?" -ForegroundColor Cyan Write-Host " [1] Push monitors to WhatsUp Gold (creates devices + credential + monitors)" Write-Host " [2] Export plan to JSON file" Write-Host " [3] Export plan to CSV file" Write-Host " [4] Show full plan table" Write-Host " [5] Generate VMware HTML dashboard (from discovery data)" Write-Host " [6] Exit (do nothing)" Write-Host " [7] Dashboard + Push to WUG" Write-Host "" $choice = Read-Host -Prompt "Choice [1-7]" } # Handle DashboardAndPush: run Dashboard then PushToWUG sequentially if ($choice -eq '7') { $actionsToRun = @('5', '1') } else { $actionsToRun = @($choice) } foreach ($currentChoice in $actionsToRun) { switch ($currentChoice) { '1' { # --- Multi-device push to WUG ----------------------------------------- Write-Host "Loading WhatsUpGoldPS module..." -ForegroundColor Cyan try { $repoRoot = Split-Path (Split-Path $PSScriptRoot -Parent) -Parent $repoPsd1 = Join-Path $repoRoot 'WhatsUpGoldPS.psd1' if (Test-Path $repoPsd1) { Import-Module $repoPsd1 -Force -ErrorAction Stop } else { Import-Module WhatsUpGoldPS -ErrorAction Stop } } catch { Write-Error "Could not load WhatsUpGoldPS module. Is it installed? $_" return } # Dot-source internal helper so scripts can call Get-WUGAPIResponse directly $apiResponsePath = Join-Path $PSScriptRoot '..\..\functions\Get-WUGAPIResponse.ps1' if (Test-Path $apiResponsePath) { . $apiResponsePath } if ($WUGCredential) { Connect-WUGServer -serverUri $WUGServer -Credential $WUGCredential -IgnoreSSLErrors } else { Connect-WUGServer -AutoConnect -IgnoreSSLErrors } # Create/find devices in WUG Write-Host "" Write-Host "Creating devices in WUG..." -ForegroundColor Cyan $wugDeviceMap = @{} $devicesCreated = 0 $devicesFound = 0 foreach ($key in $devicePlan.Keys) { $dev = $devicePlan[$key] $addIP = $null if ($dev.Type -eq 'vCenter') { $addIP = $dev.IP } elseif ($dev.Type -eq 'ESXiHost') { $addIP = $dev.IP } elseif ($dev.Type -eq 'VM' -and $dev.IP) { $addIP = $dev.IP } else { continue } if (-not $addIP) { continue } $existingDevice = $null try { $searchResults = @(Get-WUGDevice -SearchValue $addIP) if ($searchResults.Count -gt 0) { $existingDevice = $searchResults | Where-Object { $_.networkAddress -eq $addIP -or $_.hostName -eq $addIP -or $_.displayName -eq $addIP -or $_.displayName -eq $dev.Name } | Select-Object -First 1 if (-not $existingDevice -and $searchResults.Count -eq 1) { $existingDevice = $searchResults[0] } } } catch { Write-Verbose "Search for '$addIP' returned error: $_" } if ($existingDevice) { $wugDeviceMap[$key] = $existingDevice.id $devicesFound++ Write-Host " Found: $($existingDevice.displayName) (ID: $($existingDevice.id)) [$($dev.Type)]" -ForegroundColor Green } else { Write-Host " Adding $addIP ($($dev.Name)) [$($dev.Type)]..." -ForegroundColor Yellow try { Add-WUGDevice -IpOrName $addIP -GroupId 0 | Out-Null Start-Sleep -Seconds 2 $newDevice = @(Get-WUGDevice -SearchValue $addIP) | Select-Object -First 1 if ($newDevice) { $wugDeviceMap[$key] = $newDevice.id $devicesCreated++ Write-Host " Added: $($newDevice.displayName) (ID: $($newDevice.id))" -ForegroundColor Green } else { Write-Warning "Added '$addIP' but could not find it in WUG." } } catch { Write-Warning "Failed to add device '$addIP': $_" } } } # Map no-IP VMs to parent host devices foreach ($key in $devicePlan.Keys) { $dev = $devicePlan[$key] if ($dev.Type -eq 'VM' -and -not $dev.IP -and -not $wugDeviceMap.ContainsKey($key)) { $parentKey = "host:$($dev.ParentHost)" if ($wugDeviceMap.ContainsKey($parentKey)) { $wugDeviceMap[$key] = $wugDeviceMap[$parentKey] Write-Host " $($dev.Name) (VM, no IP) -> host $($dev.ParentHost)" -ForegroundColor DarkGray } else { Write-Warning "No WUG device for VM '$($dev.Name)' -- parent host '$($dev.ParentHost)' not found." } } } Write-Host "" Write-Host "Devices: $devicesCreated created, $devicesFound existing" -ForegroundColor Cyan # Set device attributes Write-Host "Setting device attributes..." -ForegroundColor Cyan foreach ($key in @($wugDeviceMap.Keys)) { $devId = $wugDeviceMap[$key] $dev = $devicePlan[$key] foreach ($attrName in $dev.Attrs.Keys) { try { Set-WUGDeviceAttribute -DeviceId $devId -Name $attrName -Value $dev.Attrs[$attrName] | Out-Null } catch { Write-Verbose "Attribute set error for $attrName on device $devId`: $_" } } } # Update plan items with WUG device IDs and sync foreach ($key in $devicePlan.Keys) { if (-not $wugDeviceMap.ContainsKey($key)) { continue } $wugId = $wugDeviceMap[$key] foreach ($item in $devicePlan[$key].Items) { $item.DeviceId = $wugId } } Write-Host "" Write-Host "Syncing monitors..." -ForegroundColor Cyan $result = Invoke-WUGDiscoverySync -Plan $plan ` -PollingIntervalSeconds 300 ` -PerfPollingIntervalMinutes 5 Write-Host "" Write-Host "Sync complete!" -ForegroundColor Green Write-Host " Devices in WUG: $($wugDeviceMap.Count)" -ForegroundColor White Write-Host " Active monitors created: $($result.ActiveCreated)" -ForegroundColor White Write-Host " Performance monitors created: $($result.PerfCreated)" -ForegroundColor White Write-Host " Assigned to devices: $($result.Assigned)" -ForegroundColor White Write-Host " Skipped (already exist): $($result.Skipped)" -ForegroundColor White Write-Host " Attributes set: $($result.AttrsUpdated)" -ForegroundColor White if ($result.Failed -gt 0) { Write-Host " Failed: $($result.Failed)" -ForegroundColor Red } Write-Host "" Write-Host "Done! Monitors pushed to WhatsUp Gold." -ForegroundColor Green } '2' { $jsonPath = Join-Path $OutputDir 'vmware-discovery-plan.json' $plan | Export-DiscoveryPlan -Format JSON -Path $jsonPath -IncludeParams Write-Host "Exported to: $jsonPath" -ForegroundColor Green } '3' { $csvPath = Join-Path $OutputDir 'vmware-discovery-plan.csv' $plan | Export-DiscoveryPlan -Format CSV -Path $csvPath Write-Host "Exported to: $csvPath" -ForegroundColor Green } '4' { $plan | Export-DiscoveryPlan -Format Table } '5' { # ---------------------------------------------------------------- # Generate VMware HTML Dashboard from already-collected plan data # ---------------------------------------------------------------- Write-Host "" Write-Host "Building VMware dashboard from discovery data..." -ForegroundColor Cyan $dashboardRows = @() foreach ($key in $devicePlan.Keys) { $dev = $devicePlan[$key] $attrs = $dev.Attrs switch ($dev.Type) { 'ESXiHost' { $dashboardRows += [PSCustomObject]@{ Type = 'Host' Name = $dev.Name PowerState = if ($attrs['VMware.PowerState']) { $attrs['VMware.PowerState'] } elseif ($attrs['VMware.Version']) { 'PoweredOn' } else { 'N/A' } IPAddress = if ($dev.IP) { $dev.IP } else { 'N/A' } Cluster = if ($attrs['VMware.Cluster']) { $attrs['VMware.Cluster'] } else { 'N/A' } ESXiHost = $dev.Name GuestOS = if ($attrs['VMware.Version']) { "VMware ESXi $($attrs['VMware.Version'])" } else { 'ESXi' } ToolsStatus = 'N/A' CPU = if ($attrs['VMware.CpuCores'] -and $attrs['VMware.CpuCores'] -ne 'N/A') { "$($attrs['VMware.CpuSockets'])s/$($attrs['VMware.CpuCores'])c" } else { 'N/A' } Memory = if ($attrs['VMware.MemTotalGB'] -and $attrs['VMware.MemTotalGB'] -ne 'N/A') { "$($attrs['VMware.MemTotalGB']) GB" } else { 'N/A' } CpuUsagePct = 'N/A' MemUsagePct = 'N/A' NetUsageKBps = 'N/A' DiskUsageKBps = 'N/A' Hardware = if ($attrs['VMware.Manufacturer']) { "$($attrs['VMware.Manufacturer']) $($attrs['VMware.Model'])".Trim() } else { 'N/A' } VersionBuild = if ($attrs['VMware.Version']) { "ESXi $($attrs['VMware.Version']) Build $($attrs['VMware.Build'])" } else { 'N/A' } Datastores = 'N/A' ProvisionedSpaceGB = 'N/A' UsedSpaceGB = 'N/A' NicCount = 'N/A' DiskCount = 'N/A' DiskLatencyMs = 'N/A' } } 'VM' { $dashboardRows += [PSCustomObject]@{ Type = 'VM' Name = $dev.Name PowerState = if ($attrs['VMware.PowerState']) { $attrs['VMware.PowerState'] } else { 'N/A' } IPAddress = if ($dev.IP) { $dev.IP } else { 'N/A' } Cluster = if ($attrs['VMware.Cluster']) { $attrs['VMware.Cluster'] } else { 'N/A' } ESXiHost = if ($attrs['VMware.ESXiHost']) { $attrs['VMware.ESXiHost'] } else { 'N/A' } GuestOS = if ($attrs['VMware.GuestOS']) { $attrs['VMware.GuestOS'] } else { 'N/A' } ToolsStatus = if ($attrs['VMware.ToolsStatus']) { $attrs['VMware.ToolsStatus'] } else { 'N/A' } CPU = if ($attrs['VMware.NumCPU']) { "$($attrs['VMware.NumCPU']) vCPU" } else { 'N/A' } Memory = if ($attrs['VMware.MemoryGB']) { "$($attrs['VMware.MemoryGB']) GB" } else { 'N/A' } CpuUsagePct = 'N/A' MemUsagePct = 'N/A' NetUsageKBps = 'N/A' DiskUsageKBps = 'N/A' Hardware = 'N/A' VersionBuild = 'N/A' Datastores = 'N/A' ProvisionedSpaceGB = 'N/A' UsedSpaceGB = 'N/A' NicCount = if ($attrs['VMware.NicCount']) { $attrs['VMware.NicCount'] } else { 'N/A' } DiskCount = if ($attrs['VMware.DiskCount']) { $attrs['VMware.DiskCount'] } else { 'N/A' } DiskLatencyMs = 'N/A' } } } } if ($dashboardRows.Count -eq 0) { Write-Warning "No data to generate dashboard." } else { $dashReportTitle = "VMware vSphere Dashboard" $dashTempPath = Join-Path $OutputDir 'VMware-Dashboard.html' $null = Export-VMwareDiscoveryDashboardHtml ` -DashboardData $dashboardRows ` -OutputPath $dashTempPath ` -ReportTitle $dashReportTitle Write-Host "" Write-Host "Dashboard generated: $dashTempPath" -ForegroundColor Green $dHosts = @($dashboardRows | Where-Object { $_.Type -eq 'Host' }).Count $dVMs = @($dashboardRows | Where-Object { $_.Type -eq 'VM' }).Count $dOn = @($dashboardRows | Where-Object { $_.PowerState -eq 'PoweredOn' }).Count $dOff = @($dashboardRows | Where-Object { $_.PowerState -eq 'PoweredOff' }).Count Write-Host " Hosts: $dHosts | VMs: $dVMs | Powered On: $dOn | Powered Off: $dOff" -ForegroundColor White # Copy to NmConsole $nmConsolePaths = @( "${env:ProgramFiles(x86)}\Ipswitch\WhatsUp\Html\NmConsole" "${env:ProgramFiles}\Ipswitch\WhatsUp\Html\NmConsole" ) $nmConsolePath = $nmConsolePaths | Where-Object { Test-Path $_ } | Select-Object -First 1 if ($nmConsolePath) { $wugDashDir = Join-Path $nmConsolePath 'dashboards' if (-not (Test-Path $wugDashDir)) { New-Item -ItemType Directory -Path $wugDashDir -Force | Out-Null } $wugDashPath = Join-Path $wugDashDir 'VMware-Dashboard.html' try { Copy-Item -Path $dashTempPath -Destination $wugDashPath -Force Write-Host "Copied to WUG: $wugDashPath" -ForegroundColor Green Write-Host " Access via WUG web UI: /NmConsole/dashboards/VMware-Dashboard.html" -ForegroundColor Cyan } catch { Write-Warning "Could not copy to NmConsole (run as admin?): $_" Write-Host " Manual copy: Copy-Item '$dashTempPath' '$wugDashPath'" -ForegroundColor Yellow } Deploy-DashboardWebConfig -Path $wugDashDir } else { Write-Host "" Write-Host "WUG NmConsole directory not found locally." -ForegroundColor Yellow Write-Host "Copy the file to your WUG server:" -ForegroundColor Yellow Write-Host " Copy-Item '$dashTempPath' '<WUG_Install>\Html\NmConsole\dashboards\VMware-Dashboard.html'" -ForegroundColor Cyan } } } default { Write-Host "No action taken." -ForegroundColor Gray } } } # end foreach actionsToRun Write-Host "" Write-Host "Re-run anytime to discover new VMware hosts/VMs." -ForegroundColor Cyan # SIG # Begin signature block # MIIr+wYJKoZIhvcNAQcCoIIr7DCCK+gCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDc1KE6xfUEZRnC # 7Cy8krLldbVIuxXMokGGfOdJa6mtN6CCJQ0wggVvMIIEV6ADAgECAhBI/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 # BCB46TebazLkMsmzTisg0UQcbnx8FgqZDf8Cb9afy1tBrzANBgkqhkiG9w0BAQEF # AASCAgCq4w5TMPf5BXZs4fmpeJ/KvHoCP40fcQkGlF/PffXi2da9NO1fxLzLc1Pj # +APVPdv5ws4U8SiKJ6ue/qfgJWZP6tgptjanMKsbtTsm1kKac9T+gfmHo2xz7UJ0 # wrSt3bWLwPOKlymsGwZf6nTL7yQLtstvFlOwRlWiHF29QuUBwoI8MRH/01uQttab # 2WtH1I50349G9cNL6gv3KL04TBxonlcu4vFtq/00W4YNnEDSYHW9B/zmREDvlzJ2 # PQu9OgJcjRyz6wdHE14xDpLAElsGBUPejVeYRIchdBDxnSdxbJ3gdexVLK/kGO+Z # XBXV8i551LERE5Oz4BNVnZWdNJ2M/g214ybzLtwKBlcx39AKaFPoam1xKpPGiqjH # VJawL3lCTOHDinP1iaj5D+pXB2MCBPI9CJlULfb7SbQTDy/SwOeQYvUFn3mmVTqw # zynDx6e8q9zGs6oZ1cTtTtGipFH+Iv77NF4+idRCEvnBUjmeuodE+ZvLEHSphJv/ # uursWQjcIEHriLqy61uAmdrCUstoTKjxE3iCNtC4lCJgPUMIy1kgJty/XDMSFO5u # ND14ORiDTdiIMS2kogEKnWhZOdlhPcwk/OR29grNxlt/gBHAe7O0hWLX2Uu9tONv # tTVdT8N+hF7Y3Z6CL9nxdT3YxLsJFd7nvbybZTsXha7ZKssPTaGCAyYwggMiBgkq # hkiG9w0BCQYxggMTMIIDDwIBATB9MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5E # aWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBUaW1l # U3RhbXBpbmcgUlNBNDA5NiBTSEEyNTYgMjAyNSBDQTECEAqA7xhLjfEFgtHEdqeV # dGgwDQYJYIZIAWUDBAIBBQCgaTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwG # CSqGSIb3DQEJBTEPFw0yNjA1MjgyMDA4MjlaMC8GCSqGSIb3DQEJBDEiBCDhVlES # kVvosiq2TBYTjivILZgxYpZX9dAc0uXVp4whKTANBgkqhkiG9w0BAQEFAASCAgAK # oiUxCM37NiYKVReKNnfsaf91aFOd9no/vwTWeF8zyLX+8Mzo6aRLW5V0uJhSXL7d # 7FGUrcy7hHWBH3WedCHyYN5PdDyoYVrZjMTTZ84leHYe47Pqodb4qQUKSDiefhNO # MidiPATpC4YqX43p0IOMr0NzWGEVHX325b4JIGvyqAxKWCD8ZSPTC9Iya/470+le # vzechNyI5+tmkHlnO5NBFWeSYnAzi0Zp1CIztW38nh9+RyK9EoLB4PI1nE8e4rZ7 # 6VGivzRvggh/f2gM1Oars4KOkPkK0lOsL8mOuNj6wTmX52pLGtLsGoR6vSejZVsu # jqHVcoM8w5n7Oizi2kWUHJCCU8WzA7ZoilEX/jb3QtLOcVlRfq4IW0Z2qcgr3Gdw # Se/TiYUrZzPZ6xmmh2DK+Or5JwnQqISbXcDJdkKVFA8otxk0G8tjRDG4ogE17CZH # 5RgUn5d2zHOjoqLF8lnFaDDL++OhzV9R0ANOJzJ4VX13w5v9IAZjL7JmNJGkHrWZ # nusSci5uFRU4CWCaZf1kqrL0XeBBpfj6UPvsqlWsYgz/KuysIdHaYm9nZ5wHkYjW # EpMXc0E3n0GomH7Bg8U0oIMsR48vfCJ2C7M3wRh1iKAzwLNY0MvHkHkrVU8yyxxw # sDfpn7xjkKuX/AkcLEO33BfBrQn7S4zFGc6hDoyDfA== # SIG # End signature block |