param ( [string]$vCenter = "", [string]$Horizon = "", [string]$Pool = "", [switch]$Help = $false, [switch]$SaveCred = $false ) # Variables $fileVersion = "1.1.0" $fileDate = "2026-06-15" $scriptName = Split-Path -Path $PSCommandPath -Leaf $mypath = split-path -parent $MyInvocation.MyCommand.Definition $now = (Get-Date).ToString("yyyy-MM-dd_HH:mm") $LogFile = "$mypath\KISVDIDesktopResolver.log" # Display Script Banner Write-Host "--------------------------------------------------" -ForegroundColor White Write-Host "Resolve Failed Horizon Desktops" -ForegroundColor Green Write-Host "Version $($FileVersion) ($($FileDate))" -ForegroundColor Green Write-Host "Written by Charlie Silverman" -ForegroundColor Green Write-Host "--------------------------------------------------" -ForegroundColor White function CheckModuleInstalled($moduleName, $command) { Write-Host "Checking that package $moduleName is installed..." -NoNewLine if (-not(get-command $command -erroraction silentlycontinue)) { Write-Host " Missing" -ForegroundColor Red if (-not (Get-Module -ListAvailable -Name $moduleName)) { $response = "" while (($response -ne "yes") -and ($response -ne "no")) { $response = (Read-Host "ImportExcel module not instaled. Install now? (yes/no)").ToLower() } if ($response -eq "no") { Write-Host "Operation Cancelled." -ForegroundColor Yellow exit } Write-Host "Installing PowerShell module $moduleName..." -NoNewLine Install-Module -Name $moduleName -Scope CurrentUser -Force Write-Host " Success" -ForegroundColor Green } Write-Host "Loading PowerShell module $moduleName..." -NoNewLine Import-Module $moduleName Write-Host " Success" -ForegroundColor Green } else { Write-Host " Found" -ForegroundColor Green } } # Check for ImportExcel and install if missing CheckModuleInstalled -moduleName "VCF.PowerCLI" -command "connect-viserver" $void = Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Confirm:$false import-module $mypath/KISHorizon.psm1 -DisableNameChecking if ($Help) { Write-Host Write-Host "Purpose: Resolve Horizon VMs with errors." Write-Host "Command Syntax:" Write-Host " $scriptName (-vCenter [vcenter host name]) (-Horizon ""[cluster]"") (-Pool ""[filename]"")" -foregroundcolor green Write-Host "Command Line Options:" Write-Host Write-Host -NoNewLine " -vCenter " -foregroundcolor Green Write-Host "- The FQDN or IP address of a vCenter server." Write-Host -NoNewLine " -Horizon " -foregroundcolor Green Write-Host "- The FQDN or IP address of a Horizon connection broker." Write-Host -NoNewLine " -Pool " -foregroundcolor Green Write-Host "- A comma-delimited list of pools to check. If not specified, all pools will be checked." Write-Host Write-Host "If any of the information is not provided, you will be prompted with the exception of the pool list. If a credential has been" Write-Host "previously stored, it will be used again unless the -savecred option is specified." Write-Host exit } if ($vCenter -eq "") { $vCenter = Read-Host "Enter FQDN or IP address of vCenter Server" } if ($SaveCred) { $vccred = Get-Credential -Message "Enter credentials for vCenter server $vCenter" $vccred| Export-Clixml -path .\kistools-$vcenter.cred Write-Host "Saving credentials." } if (Test-Path -path .\kistools-$vcenter.cred -PathType Leaf) { $vccred = Import-Clixml -path .\kistools-$vcenter.cred Write-Host "- Using stored vCenter credentials" } else { $vccred = Get-Credential -Message "Enter credentials for vCenter server $vCenter" if (!$vccred) { Write-Host "ERROR: No credentials were entered" -foregroundcolor yellow exit } } #Establish connection to vCenter try { Write-Host -NoNewLine "Connecting to vCenter $vcenter... " $vcconn = Connect-VIServer -server $vcenter -Credential $vccred -ErrorAction Stop Write-Host "Success" -ForegroundColor green } catch { Write-Host "Failure" -ForegroundColor Yellow -BackgroundColor Red -NoNewLine Write-Host Write-Host "StatusCode:" $_.Exception.Response.StatusCode.value__ Write-Host "StatusDescription:" $_.Exception.Response.StatusDescription exit } if ($Horizon -eq "") { $Horizon = Read-Host "Enter FQDN or IP address of Horizon Connection Broker" } if ($SaveCred) { $hzcred = Get-Credential -Message "Enter credentials for Horizon Broker $Horizon" $hzcred | Export-Clixml -path .\kistools-$Horizon.cred Write-Host "Saving credentials." } if (Test-Path -path .\kistools-$Horizon.cred -PathType Leaf) { $hzcred = Import-Clixml -path .\kistools-$Horizon.cred Write-Host "- Using stored Horizon credentials" } else { $hzcred = Get-Credential -Message "Enter credentials for Horizon server $Horizon" if (!$hzcreds) { Write-Host "ERROR: No credentials were entered" -foregroundcolor yellow exit } } #Establish connection to Horizon try { Write-Host -NoNewLine "Connecting to Horizon Connection Broker $Horizon... " $hzconn = Connect-HorizonServer -server $Horizon -credential $hzcred Write-Host "Success" -ForegroundColor green } catch { Write-Host "Failure" -ForegroundColor Yellow -BackgroundColor Red -NoNewLine Write-Host exit } if ($Pool -ne "") { $Pools = $Pool.split(",") $poolsvalid = @() foreach($thisPool in $Pools) { Write-Host -NoNewLine "Verifying existence of pool $thisPool... " $thispool = $hzconn | Get-HorizonDesktopPool -poolname $thisPool if ($thispool) { Write-Host "Success" -ForegroundColor green $poolsvalid += $thisPool } else { Write-Host "Failure - Skipping" -ForegroundColor Yellow -BackgroundColor Red -NoNewLine Write-Host } } } else { $poolsvalid = $hzconn | Get-HorizonDesktopPool } $totalfixed = 0 $totalerror = 0 $poolreport = "" foreach ($thisPool in $poolsvalid) { $unreachable = $thispool.Machines | Where-Object {$_.state -eq "AGENT_UNREACHABLE"} if ($unreachable.length -gt 0) { $label = "desktops" if ($unreachable.length -eq 1) { $label = "desktop" } Write-Host "Pool $($thispool.Name) has $($unreachable.length) $label in an Unreachable state." } $poolfixed = 0 foreach ($machine in $unreachable) { $thisvm = Get-VM $machine.Name Write-Host "- Resetting NIC for VM $($thisvm.Name) in pool $($thisPool.Name)... " -NoNewLine $void = $thisvm | Get-NetworkAdapter | Set-NetworkAdapter -connected:$false -confirm:$false Start-Sleep -Seconds 2 $void = $thisvm | Get-NetworkAdapter | Set-NetworkAdapter -connected:$true -confirm:$false Write-Host "Success" -ForegroundColor Green $poolfixed += 1 } if ($poolfixed -gt 0) { $totalfixed += $poolfixed $label = "VMs" if ($poolfixed -eq 1) { $label = "VM" } $poolreport += "`r`n - Pool $($thispool.Name): $poolfixed $label resolved" } } try { Write-Host -NoNewLine "Disconnecting from vCenter $vcenter... " $void = Disconnect-VIServer -server $vcenter -confirm:$false Write-Host "Success" -ForegroundColor green } catch { Write-Host "Failure" -ForegroundColor Yellow -BackgroundColor Red -NoNewLine Write-Host Write-Host "StatusCode:" $_.Exception.Response.StatusCode.value__ Write-Host "StatusDescription:" $_.Exception.Response.StatusDescription exit } Write-Host "`r`nTotal Desktops fixed in this run: $totalfixed" Write-Host $poolreport $label = "desktops" if ($totalfixed -eq 1) { $label = "desktop" } Add-Content $LogFile "$Now - Total $label fixed in this run: $totalfixed" $reportlines = $poolreport.split("`r`n") foreach ($line in $reportlines) { if ($line -ne "") { Add-Content $LogFile "$Now - $line" } }