Added Horizon Tools
This commit is contained in:
1 parent
fc7cfeb008
commit
f13f6e1105
3 files changed
+1213
No files matched your search
@@ -0,0 +1,315 @@
|
|||||||
|
param(
|
||||||
|
[string]$HorizonServer = "",
|
||||||
|
[string]$OutFile = "",
|
||||||
|
[string]$InputXML = "",
|
||||||
|
[switch]$SaveCred = $false,
|
||||||
|
[switch]$debug = $false,
|
||||||
|
[switch]$help = $false
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
Write-Host $global:kishorizondebug
|
||||||
|
|
||||||
|
# Variables
|
||||||
|
$fileVersion = "1.1.0"
|
||||||
|
$fileDate = "2026-06-24"
|
||||||
|
$scriptName = Split-Path -Path $PSCommandPath -Leaf
|
||||||
|
$mypath = split-path -parent $MyInvocation.MyCommand.Definition
|
||||||
|
$now = (Get-Date).ToString("yyyy-MM-dd_HH:mm")
|
||||||
|
$userfolder = $env:USERPROFILE
|
||||||
|
|
||||||
|
# Display Script Banner
|
||||||
|
Write-Host "--------------------------------------------------" -ForegroundColor White
|
||||||
|
Write-Host "Export Pool Settings to Excel File" -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 "$moduleName module not instaled. Install now? (yes/no)").ToLower()
|
||||||
|
}
|
||||||
|
if ($response -eq "no") {
|
||||||
|
Write-Host "Operation aborted." -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"
|
||||||
|
CheckModuleInstalled -moduleName "ImportExcel" -command "Open-ExcelPackage"
|
||||||
|
Import-Module $mypath\KISHorizon.psm1
|
||||||
|
$abyss = Set-PowerCLIConfiguration -InvalidCertificateAction Ignore -Scope Session -Confirm:$false
|
||||||
|
|
||||||
|
if ($Help) {
|
||||||
|
Write-Host
|
||||||
|
Write-Host "Purpose: Resolve Horizon VMs with errors."
|
||||||
|
Write-Host "Command Syntax:"
|
||||||
|
Write-Host " $scriptName (-Horizon ""[cluster]"") (-SaveCred)" -ForegroundColor Green
|
||||||
|
Write-Host "Command Line Options:"
|
||||||
|
Write-Host
|
||||||
|
Write-Host -NoNewLine " -Horizon " -ForegroundColor Green
|
||||||
|
Write-Host "- The FQDN or IP address of a Horizon connection broker."
|
||||||
|
Write-Host -NoNewLine " -SaveCred " -ForegroundColor Green
|
||||||
|
Write-Host "- Ask for credentials and save them to current user's profile directory."
|
||||||
|
Write-Host
|
||||||
|
Write-Host "The Horizon server will be queried for all vCenter servers it is configured for and credentials will be required for each."
|
||||||
|
Write-Host "Any information not provided will be prompted for during script execution."
|
||||||
|
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($OutFile -eq "") {
|
||||||
|
$OutFile = "KISHorizonExport-$($HorizonServer).xlsx"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Test-Path -path $Outfile -PathType Leaf) {
|
||||||
|
Remove-Item -Path $Outfile
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($inputXML -eq "") {
|
||||||
|
if ($HorizonServer -eq "") {
|
||||||
|
$HorizonServer = Read-Host "Enter FQDN or IP address of Horizon Connection Broker"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($SaveCred) {
|
||||||
|
$hzcred = Get-Credential -Message "Enter credentials for Horizon server $HorizonServer in the format DOMAIN\USERNAME"
|
||||||
|
$hzcred | Export-Clixml -path $userfolder\kistools-$HorizonServer.cred
|
||||||
|
Write-Host "Saving credentials."
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Test-Path -path $userfolder\kistools-$HorizonServer.cred -PathType Leaf) {
|
||||||
|
$hzcred = Import-Clixml -path $userfolder\kistools-$HorizonServer.cred
|
||||||
|
Write-Host "- Using stored Horizon credentials"
|
||||||
|
} else {
|
||||||
|
$hzcred = Get-Credential -Message "Enter credentials for Horizon server $HorizonServer in the format DOMAIN\USERNAME"
|
||||||
|
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 $HorizonServer... "
|
||||||
|
$hzconn = Connect-HorizonServer -server $HorizonServer -credential $hzcred
|
||||||
|
Write-Host "Success" -ForegroundColor green
|
||||||
|
} catch {
|
||||||
|
Write-Host "Failure" -ForegroundColor Yellow -BackgroundColor Red -NoNewLine
|
||||||
|
Write-Host
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
|
||||||
|
# Establish connection to all vCenter servers configured on this Horizon system
|
||||||
|
$vcenterlist = $hzconn | Get-HorizonvCenter
|
||||||
|
foreach ($thisvc in $vcenterlist) {
|
||||||
|
$vcname = $thisvc.server_name
|
||||||
|
|
||||||
|
if ($SaveCred) {
|
||||||
|
$vccred = Get-Credential -Message "Enter credentials for vCenter server $vcname"
|
||||||
|
$vccred| Export-Clixml -path $userfolder\kistools-$vcname.cred
|
||||||
|
Write-Host "Saving credentials."
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Test-Path -path $userfolder\kistools-$vcname.cred -PathType Leaf) {
|
||||||
|
$vccred = Import-Clixml -path $userfolder\kistools-$vcname.cred
|
||||||
|
Write-Host "- Using stored vCenter credentials"
|
||||||
|
} else {
|
||||||
|
$vccred = Get-Credential -Message "Enter credentials for vCenter server $vcname"
|
||||||
|
if (!$vccred) {
|
||||||
|
Write-Host "ERROR: No credentials were entered" -foregroundcolor yellow
|
||||||
|
exit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#Establish connection to vCenter
|
||||||
|
try {
|
||||||
|
Write-Host -NoNewLine "Connecting to vCenter $vcname... "
|
||||||
|
$vcconn = Connect-VIServer -server $vcname -Credential $vccred -Force -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$pools = $hzconn | Get-HorizonDesktopPool
|
||||||
|
} else {
|
||||||
|
$pools = Import-CliXml -path $inputXML
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($thisvc in $vcenterlist) {
|
||||||
|
Write-Host -NoNewLine "Disconnecting from vCenter $vcname... "
|
||||||
|
try {
|
||||||
|
$abyss = Disconnect-ViServer $thisvc.server_name -confirm:$false
|
||||||
|
Write-Host "Success" -ForegroundColor green
|
||||||
|
} catch {
|
||||||
|
Write-Host "Failure" -ForegroundColor Yellow -BackgroundColor Red -NoNewLine
|
||||||
|
Write-Host
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host -NoNewLine "Generating Excel File $OutFile... "
|
||||||
|
$workbook = Open-ExcelPackage -Path $OutFile -Create
|
||||||
|
|
||||||
|
#Create Initial Data Worksheet
|
||||||
|
$thissheet = Add-Worksheet -ExcelPackage $workbook -WorksheetName "General Horizon Data"
|
||||||
|
Set-ExcelRange -worksheet $thissheet -Range "A:A" -VerticalAlignment Top
|
||||||
|
Set-Format -Address $thissheet.Cells["B:B"] -WrapText
|
||||||
|
$thissheet.column(1).Width = 40
|
||||||
|
$thissheet.column(2).Width = 40
|
||||||
|
$thissheet.column(3).Width = 40
|
||||||
|
Set-ExcelRange -Worksheet $thissheet -Range "A1:E1" -Merge:$true
|
||||||
|
$domains = Get-HorizonADDomains -Horizon $hzconn
|
||||||
|
$vcenterservers = Get-HorizonvCenter -Horizon $hzconn
|
||||||
|
$goldimages = $pools.provisioning_settings.base_vm_name | Select-Object -Unique
|
||||||
|
$row = 1
|
||||||
|
$thissheet.cells["A1"].value = "General data for Horizon server $HorizonServer"
|
||||||
|
Set-Format -Address $thissheet.Cells["A1"] -Bold
|
||||||
|
Set-ExcelRange -Worksheet $thissheet -Range "A1" -FontSize 20
|
||||||
|
$row += 2
|
||||||
|
$thissheet.cells["A" + $row].value = "vCenter Servers"
|
||||||
|
Set-Format -Address $thissheet.Cells["A" + $row] -Bold
|
||||||
|
Set-ExcelRange -Worksheet $thissheet -Range "A$row" -FontSize 16
|
||||||
|
|
||||||
|
$row += 1
|
||||||
|
$vctabletop = $row
|
||||||
|
$thissheet.cells["A" + $row].value = "vCenter Server"
|
||||||
|
$thissheet.cells["B" + $row].value = "Version"
|
||||||
|
$thissheet.cells["C" + $row].value = "User Account"
|
||||||
|
$row += 1
|
||||||
|
foreach ($vc in $vcenterlist) {
|
||||||
|
$thissheet.cells["A" + $row].value = $vc.server_name
|
||||||
|
$thissheet.cells["B" + $row].value = $vc.version
|
||||||
|
$thissheet.cells["C" + $row].value = $vc.user_name
|
||||||
|
$row += 1
|
||||||
|
}
|
||||||
|
$vcTableRange = $thissheet.Cells[$vctabletop, 1, ($row-1), 3]
|
||||||
|
$vcTable = $thissheet.Tables.Add($vcTableRange, "vCenterList")
|
||||||
|
$vcTable.StyleName = "TableStyleMedium15"
|
||||||
|
|
||||||
|
$row += 1
|
||||||
|
$thissheet.cells["A" + $row].value = "Active Directory Domains"
|
||||||
|
Set-Format -Address $thissheet.Cells["A" + $row] -Bold
|
||||||
|
Set-ExcelRange -Worksheet $thissheet -Range "A$row" -FontSize 16
|
||||||
|
$row += 1
|
||||||
|
$adtabletop = $row
|
||||||
|
$thissheet.cells["A" + $row].value = "DNS Name"
|
||||||
|
$thissheet.cells["B" + $row].value = "NetBIOS Name"
|
||||||
|
$row += 1
|
||||||
|
foreach ($add in $domains) {
|
||||||
|
$thissheet.cells["A" + $row].value = $add.dns_name
|
||||||
|
$thissheet.cells["B" + $row].value = $add.netbios_name
|
||||||
|
$row += 1
|
||||||
|
}
|
||||||
|
$adTableRange = $thissheet.Cells[$adtabletop, 1, ($row-1), 2]
|
||||||
|
$adTable = $thissheet.Tables.Add($adTableRange, "ADDomains")
|
||||||
|
$adTable.StyleName = "TableStyleMedium15"
|
||||||
|
|
||||||
|
$row += 1
|
||||||
|
$thissheet.cells["A" + $row].value = "Published Gold Images"
|
||||||
|
Set-Format -Address $thissheet.Cells["A" + $row] -Bold
|
||||||
|
Set-ExcelRange -Worksheet $thissheet -Range "A$row" -FontSize 16
|
||||||
|
$row += 1
|
||||||
|
$adtabletop = $row
|
||||||
|
$thissheet.cells["A" + $row].value = "Gold Image VMs"
|
||||||
|
$thissheet.cells["B" + $row].value = "Pools"
|
||||||
|
$row += 1
|
||||||
|
foreach ($image in $goldimages) {
|
||||||
|
$thissheet.cells["A" + $row].value = $image
|
||||||
|
$thissheet.cells["B" + $row].value = (($pools | Where-Object {$_.provisioning_settings.base_vm_name -eq $image}).Name -Join "`r`n")
|
||||||
|
$row += 1
|
||||||
|
}
|
||||||
|
$giTableRange = $thissheet.Cells[$adtabletop, 1, ($row-1), 2]
|
||||||
|
$giTable = $thissheet.Tables.Add($giTableRange, "GoldImages")
|
||||||
|
$giTable.StyleName = "TableStyleMedium15"
|
||||||
|
|
||||||
|
foreach ($pool in $pools) {
|
||||||
|
$thissheet = Add-Worksheet -ExcelPackage $workbook -WorksheetName $pool.name
|
||||||
|
$entitlements = $pool.entitlements | ForEach-Object {
|
||||||
|
$_.Domain + "\" + $_.SamAccountName
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = @(
|
||||||
|
[pscustomobject]@{Name = "Pool ID";Value=$pool.name}
|
||||||
|
[pscustomobject]@{Name = "Display Name";Value=$pool.display_name}
|
||||||
|
[pscustomobject]@{Name = "Description";Value=$pool.description}
|
||||||
|
[pscustomobject]@{Name = "Type";Value=$pool.session_type}
|
||||||
|
[pscustomobject]@{Name = "User Assignment";Value=$pool.user_assignment}
|
||||||
|
[pscustomobject]@{Name = "vCenter Server";Value=$pool.vcenter}
|
||||||
|
[pscustomobject]@{Name = "Naming Pattern";Value=$pool.pattern_naming_settings.naming_pattern}
|
||||||
|
[pscustomobject]@{Name = "Max Machines";Value=$pool.pattern_naming_settings.max_number_of_machines}
|
||||||
|
[pscustomobject]@{Name = "Spare Machines";Value=$pool.pattern_naming_settings.number_of_spare_machines}
|
||||||
|
[pscustomobject]@{Name = "Min Machines";Value=$pool.pattern_naming_settings.min_number_of_machines}
|
||||||
|
[pscustomobject]@{Name = "Datacenter";Value=$pool.provisioning_settings.datacenter_name}
|
||||||
|
[pscustomobject]@{Name = "Resource Pool";Value=$pool.provisioning_settings.resource_pool_name}
|
||||||
|
[pscustomobject]@{Name = "Parent VM";Value=$pool.provisioning_settings.base_vm_name}
|
||||||
|
[pscustomobject]@{Name = "Snapshot Path";Value=$pool.provisioning_settings.snapshot_path}
|
||||||
|
[pscustomobject]@{Name = "VM Folder Path";Value=$pool.provisioning_settings.base_folder_path}
|
||||||
|
[pscustomobject]@{Name = "Cluster";Value=$pool.cluster}
|
||||||
|
[pscustomobject]@{Name = "Resource Pool";Value=$pool.provisioning_settings.resource_pool_name}
|
||||||
|
[pscustomobject]@{Name = "Datacenter";Value=$pool.provisioning_settings.datacenter_name}
|
||||||
|
[pscustomobject]@{Name = "Datastores";Value=$pool.storage_settings.datastores.name -join "`r`n"}
|
||||||
|
[pscustomobject]@{Name = "Network";Value=($pool.machines.managed_machine_data.network_labels.network_label_name | Select-Object -Unique | Sort-Object) -join "`r`n"}
|
||||||
|
[pscustomobject]@{Name = "Use vSAN";Value=$pool.storage_settings.use_vsan}
|
||||||
|
[pscustomobject]@{Name = "Add Virtual TPM";Value=$pool.provisioning_settings.add_virtual_tpm}
|
||||||
|
[pscustomobject]@{Name = "vGPU Enabled";Value=$pool.GpuEnabled}
|
||||||
|
[pscustomobject]@{Name = "Customization Type";Value=$pool.customization_settings.customization_type}
|
||||||
|
[pscustomobject]@{Name = "Ad Container";Value=$pool.customization_settings.ad_container_rdn}
|
||||||
|
[pscustomobject]@{Name = "Reuse Existing AD Object";Value=$pool.customization_settings.reuse_pre_existing_accounts}
|
||||||
|
[pscustomobject]@{Name = "Cloneprep Script Settings";Value=$pool.customization_settings.cloneprep_script_settings}
|
||||||
|
[pscustomobject]@{Name = "Power Off Script Name";Value=$pool.customization_settings.cloneprep_customization_settings.power_off_script_name}
|
||||||
|
[pscustomobject]@{Name = "Power Off Script Parameters";Value=$pool.customization_settings.cloneprep_customization_settings.power_off_script_parameters}
|
||||||
|
[pscustomobject]@{Name = "Post Sync Script Name";Value=$pool.customization_settings.cloneprep_customization_settings.post_synchronization_script_name}
|
||||||
|
[pscustomobject]@{Name = "Post Sync Script Parameters";Value=$pool.customization_settings.cloneprep_customization_settings.post_synchronization_script_parameters}
|
||||||
|
[pscustomobject]@{Name = "Automatic Logoff Policy";Value=$pool.session_settings.disconnected_session_timeout_policy}
|
||||||
|
[pscustomobject]@{Name = "Automatic Logoff Minutes";Value=$pool.session_settings.disconnected_session_timeout_minutes}
|
||||||
|
[pscustomobject]@{Name = "Allow Users to Reset Machines";Value=$pool.session_settings.allow_users_to_reset_machines}
|
||||||
|
[pscustomobject]@{Name = "Allow Multiple Sessions Per User";Value=$pool.session_settings.allow_multiple_sessions_per_user}
|
||||||
|
[pscustomobject]@{Name = "Used VM Policy";Value=$pool.session_settings.delete_or_refresh_machine_after_logoff}
|
||||||
|
[pscustomobject]@{Name = "Logoff After Timeout";Value=$pool.session_settings.disconnected_session_timeout_policy}
|
||||||
|
[pscustomobject]@{Name = "Supported Display Protocols";Value=$pool.display_protocol_settings.display_protocols -join "`r`n"}
|
||||||
|
[pscustomobject]@{Name = "Default Display Protocol";Value=$pool.display_protocol_settings.default_display_protocol}
|
||||||
|
[pscustomobject]@{Name = "Allow Users to Choose Protocol";Value=$pool.display_protocol_settings.allow_users_to_choose_protocol}
|
||||||
|
[pscustomobject]@{Name = "External HTML Access";Value=$pool.display_protocol_settings.html_access_enabled}
|
||||||
|
[pscustomobject]@{Name = "Enable Collaboration";Value=$pool.display_protocol_settings.session_collaboration_enabled}
|
||||||
|
[pscustomobject]@{Name = "Entitlements";Value=$entitlements}
|
||||||
|
)
|
||||||
|
|
||||||
|
$rowcount = 1
|
||||||
|
foreach ($datum in $data) {
|
||||||
|
$thissheet.cells["A" + $rowcount].Value = $datum.name
|
||||||
|
$thissheet.cells["B" + $rowcount].Value = $datum.value
|
||||||
|
$rowcount += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
$thissheet.Column(1).Style.HorizontalAlignment = "Right"
|
||||||
|
$thissheet.Column(1).Width = 40
|
||||||
|
$thissheet.Column(2).Width = 200
|
||||||
|
Set-Column -ExcelPackage $workbook -WorksheetName $pool.Name -Column 2 -WrapText
|
||||||
|
Set-Column -ExcelPackage $workbook -WorksheetName $pool.Name -Column 2 -HorizontalAlignment Left
|
||||||
|
Set-Column -ExcelPackage $workbook -WorksheetName $pool.Name -Column 1 -VerticalAlignment Top
|
||||||
|
Set-ExcelRange -Worksheet $thissheet -Range "A:B" -FontSize 14
|
||||||
|
$thissheet.cells["A:A"].Style.Indent = 2
|
||||||
|
Set-ExcelRange -Worksheet $thissheet -Range "B:B" -Bold
|
||||||
|
}
|
||||||
|
Close-ExcelPackage $workbook
|
||||||
|
Write-Host "Success" -ForegroundColor green
|
||||||
@@ -0,0 +1,688 @@
|
|||||||
|
$global:request_header = @{
|
||||||
|
"accept"="*/*"
|
||||||
|
"content-type"="application/json"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Test-Server {
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory)] [string]$server
|
||||||
|
)
|
||||||
|
|
||||||
|
try {
|
||||||
|
$result = (new-object System.Net.Sockets.TcpClient).ConnectAsync($server,443).Wait(500)
|
||||||
|
} catch {
|
||||||
|
$result = $false
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-HorizonKeyAgreement {
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory)] [string]$server
|
||||||
|
)
|
||||||
|
|
||||||
|
$login_headers = $global:request_header
|
||||||
|
|
||||||
|
$kaUri = "https://$server/rest/key-agreement"
|
||||||
|
|
||||||
|
$body = '{"identifier":"string","nonce":"string","public_key":"string","supported_schemes": ["SCHEME-AES2"]}'
|
||||||
|
|
||||||
|
if ($global:kishorizondebug) {
|
||||||
|
Write-Host $MyInvocation.MyCommand.Name Starting
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Test-Server -server $server) {
|
||||||
|
try {
|
||||||
|
$response = Invoke-RestMethod -Method Post -Uri $kaUri -Body $body -Headers $login_headers -SessionVariable websession -SkipCertificateCheck
|
||||||
|
} catch {
|
||||||
|
Write-Host "ERROR accessing $loginUri"
|
||||||
|
Write-Host "StatusCode:" $_.Exception.Response.StatusCode.value__
|
||||||
|
Write-Host "StatusDescription:" $_.Exception.Response.StatusDescription
|
||||||
|
}
|
||||||
|
if ($response -ne $null) {
|
||||||
|
$response | Add-Member -MemberType NoteProperty -Name server_name -Value $server
|
||||||
|
$response | Add-Member -MemberType NoteProperty -Name session -Value $websession
|
||||||
|
$response | Add-Member -MemberType NoteProperty -Name user_name -Value "$domain\$username"
|
||||||
|
}
|
||||||
|
|
||||||
|
return $response
|
||||||
|
} else {
|
||||||
|
throw "Error: Unable to verify server $server on port 443."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function Connect-HorizonServer {
|
||||||
|
param (
|
||||||
|
[System.Management.Automation.PSCredential]$credential,
|
||||||
|
[Parameter(Mandatory)] [string]$Server
|
||||||
|
)
|
||||||
|
|
||||||
|
$loginUri = "https://$server/rest/login"
|
||||||
|
|
||||||
|
if ($credential) {
|
||||||
|
$username = $credential.UserName.split("\")[1]
|
||||||
|
$domain = $credential.UserName.split("\")[0]
|
||||||
|
$password = $credential.GetNetworkCredential().Password
|
||||||
|
}
|
||||||
|
|
||||||
|
$login_headers = $global:request_header
|
||||||
|
|
||||||
|
$body = '{"domain":"' + $domain + '","password":"' + $password + '","username":"' + $username + '"}'
|
||||||
|
|
||||||
|
if ($global:kishorizondebug) {
|
||||||
|
Write-Host $MyInvocation.MyCommand.Name Starting
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Test-Server -server $server) {
|
||||||
|
try {
|
||||||
|
$response = Invoke-RestMethod -Method Post -Uri $loginUri -Body $body -Headers $login_headers -SessionVariable websession -SkipCertificateCheck
|
||||||
|
} catch {
|
||||||
|
Write-Host "ERROR accessing $loginUri"
|
||||||
|
Write-Host "StatusCode:" $_.Exception.Response.StatusCode.value__
|
||||||
|
Write-Host "StatusDescription:" $_.Exception.Response.StatusDescription
|
||||||
|
}
|
||||||
|
$response | Add-Member -MemberType NoteProperty -Name server_name -Value $server
|
||||||
|
$response | Add-Member -MemberType NoteProperty -Name session -Value $websession
|
||||||
|
$response | Add-Member -MemberType NoteProperty -Name user_name -Value "$domain\$username"
|
||||||
|
|
||||||
|
return $response
|
||||||
|
} else {
|
||||||
|
throw "Error: Unable to verify server $server on port 443."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Refresh-HorizonConnection {
|
||||||
|
param (
|
||||||
|
[parameter(Mandatory = $true, ValueFromPipeline = $true)] $Horizon
|
||||||
|
)
|
||||||
|
|
||||||
|
$token = $Horizon.access_token
|
||||||
|
$server = $Horizon.server_name
|
||||||
|
|
||||||
|
$request_header = $global:request_header
|
||||||
|
$request_header += @{"authorization"="Bearer $token"}
|
||||||
|
$body = @{"refresh_token"="$token"}
|
||||||
|
|
||||||
|
$refreshUri = "https://$server/rest/refresh"
|
||||||
|
|
||||||
|
Write-Host $body
|
||||||
|
try {
|
||||||
|
$result = Invoke-RestMethod -Method Post -Uri $refreshUri -Headers $request_header -Body $body -SkipCertificateCheck
|
||||||
|
} catch {
|
||||||
|
Write-Error -Message "ERROR accessing $refreshUri`nStatusCode: $($_.Exception.Response.StatusCode.value__)`nStatusDescription: $($_.Exception.Response.StatusDescription)"
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-HorizonMachine {
|
||||||
|
param (
|
||||||
|
[parameter(Mandatory = $true, ValueFromPipeline = $true)] $Horizon
|
||||||
|
)
|
||||||
|
$token = $Horizon.access_token
|
||||||
|
$server = $Horizon.server_name
|
||||||
|
|
||||||
|
$request_header = $global:request_header
|
||||||
|
$request_header += @{"authorization"="Bearer $token"}
|
||||||
|
|
||||||
|
$machineUri = "https://$server/rest/inventory/v5/machines?sort_by=name&order_by=ASC"
|
||||||
|
|
||||||
|
try {
|
||||||
|
$machines = Invoke-RestMethod -Method Get -Uri $machineUri -Headers $request_header -SkipCertificateCheck
|
||||||
|
} catch {
|
||||||
|
throw "ERROR accessing $machineUri`nStatusCode: $($_.Exception.Response.StatusCode.value__)`nStatusDescription: $($_.Exception.Response.StatusDescription)"
|
||||||
|
}
|
||||||
|
|
||||||
|
return $machines
|
||||||
|
}
|
||||||
|
|
||||||
|
function Remove-HorizonMachine {
|
||||||
|
param (
|
||||||
|
[string]$id = "",
|
||||||
|
[parameter(Mandatory = $true, ValueFromPipeline = $true)] $Horizon
|
||||||
|
)
|
||||||
|
|
||||||
|
$token = $Horizon.access_token
|
||||||
|
$server = $Horizon.server_name
|
||||||
|
$ids = $id.split(",")
|
||||||
|
if ($ids.length -gt 1) {
|
||||||
|
$body = "{""machine_ids"":["
|
||||||
|
foreach ($thisid in $ids) {
|
||||||
|
$thisid = $thisid.Trim('"')
|
||||||
|
if ($body -ne "{""machine_ids"":[") { $body += "," }
|
||||||
|
$body += '"' + $thisid + '"'
|
||||||
|
}
|
||||||
|
$body += ']}'
|
||||||
|
} else {
|
||||||
|
$body = ']"{""machine_ids"":[""' + $id + '""]}'
|
||||||
|
}
|
||||||
|
|
||||||
|
$request_header = $global:request_header
|
||||||
|
$request_header += @{"authorization"="Bearer $token"}
|
||||||
|
|
||||||
|
$machineUri = "https://$server/rest/inventory/v1/machines"
|
||||||
|
|
||||||
|
if ($global:kishorizondebug) {
|
||||||
|
Write-Host $MyInvocation.MyCommand.Name Starting
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$result = Invoke-RestMethod -Method DELETE -Uri $machineUri -Body $body -Headers $request_header -SkipCertificateCheck -ErrorAction:SilentlyContinue
|
||||||
|
} catch {
|
||||||
|
$_.Exception.Response.StatusCode.value
|
||||||
|
$_.Exception.Response.StatusDescription
|
||||||
|
throw "ERROR accessing $machineUri`nStatusCode: $($_.Exception.Response.StatusCode.value__)`nStatusDescription: $($_.Exception.Response.StatusDescription)"
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-HorizonSession {
|
||||||
|
param (
|
||||||
|
[parameter(Mandatory = $true, ValueFromPipeline = $true)] $Horizon
|
||||||
|
)
|
||||||
|
$token = $Horizon.access_token
|
||||||
|
$server = $Horizon.server_name
|
||||||
|
|
||||||
|
$request_header = $global:request_header
|
||||||
|
$request_header += @{"authorization"="Bearer $token"}
|
||||||
|
|
||||||
|
$sessionUri = "https://$server/rest/inventory/v2/sessions?sort_by=session_type&order_by=ASC"
|
||||||
|
$userURI = "https://$server/rest/external/v2/ad-users-or-groups/**ID**"
|
||||||
|
|
||||||
|
if ($global:kishorizondebug) {
|
||||||
|
Write-Host $MyInvocation.MyCommand.Name Starting
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$sessions = Invoke-RestMethod -Method Get -Uri $sessionUri -Headers $request_header -SkipCertificateCheck
|
||||||
|
$sessions | Add-Member -MemberType NoteProperty -Name user_name -Value ""
|
||||||
|
$sessions | Add-Member -MemberType NoteProperty -Name user_principal_name -Value ""
|
||||||
|
$sessions | Add-Member -MemberType NoteProperty -Name session_length -Value 0
|
||||||
|
$sessions | Add-Member -MemberType NoteProperty -Name connected_length -Value 0
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
throw "ERROR accessing $sessionUri`nStatusCode: $($_.Exception.Response.StatusCode.value__)`nStatusDescription: $($_.Exception.Response.StatusDescription)"
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach($session in $sessions) {
|
||||||
|
$user = $Horizon | Get-HorizonADUser -id $session.user_id
|
||||||
|
|
||||||
|
$session.user_name = $user.login_name
|
||||||
|
$session.user_principal_name = $user.user_principal_name
|
||||||
|
|
||||||
|
$session.session_length = Get-HorizonSessionLength -session $session
|
||||||
|
$session.connected_length = Get-HorizonSessionConnectedLength -session $session
|
||||||
|
}
|
||||||
|
|
||||||
|
return $sessions
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-HorizonVcenter {
|
||||||
|
param (
|
||||||
|
[parameter(Mandatory = $true, ValueFromPipeline = $true)] $Horizon
|
||||||
|
)
|
||||||
|
$token = $Horizon.access_token
|
||||||
|
$server = $Horizon.server_name
|
||||||
|
|
||||||
|
$request_header = $global:request_header
|
||||||
|
$request_header += @{"authorization"="Bearer $token"}
|
||||||
|
|
||||||
|
$vcuri = "https://$server/rest/config/v1/virtual-centers"
|
||||||
|
|
||||||
|
if ($global:kishorizondebug) {
|
||||||
|
Write-Host $MyInvocation.MyCommand.Name Starting
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$vcs = Invoke-RestMethod -Method Get -Uri $vcUri -Headers $request_header -SkipCertificateCheck
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
throw "ERROR accessing $vcUri`nStatusCode: $($_.Exception.Response.StatusCode.value__)`nStatusDescription: $($_.Exception.Response.StatusDescription)"
|
||||||
|
}
|
||||||
|
|
||||||
|
return $vcs
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-HorizonBaseVMs {
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory)] [string]$vcid,
|
||||||
|
[parameter(Mandatory = $true, ValueFromPipeline = $true)] $Horizon
|
||||||
|
)
|
||||||
|
$token = $Horizon.access_token
|
||||||
|
$server = $Horizon.server_name
|
||||||
|
|
||||||
|
$request_header = $global:request_header
|
||||||
|
$request_header += @{"authorization"="Bearer $token"}
|
||||||
|
|
||||||
|
$bvuri = "https://$server/rest/external/v1/base-vms?vcenter_id=$vcid&filter_incompatible_vms=true"
|
||||||
|
|
||||||
|
if ($global:kishorizondebug) {
|
||||||
|
Write-Host $MyInvocation.MyCommand.Name Starting
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$bvs = Invoke-RestMethod -Method Get -Uri $bvUri -Headers $request_header -SkipCertificateCheck
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
throw "ERROR accessing $bvUri`nStatusCode: $($_.Exception.Response.StatusCode.value__)`nStatusDescription: $($_.Exception.Response.StatusDescription)"
|
||||||
|
}
|
||||||
|
|
||||||
|
return $bvs
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-HorizonBaseSnapshots {
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory)] [string]$vcid,
|
||||||
|
[Parameter(Mandatory)] [string]$vmid,
|
||||||
|
[parameter(Mandatory = $true, ValueFromPipeline = $true)] $Horizon
|
||||||
|
)
|
||||||
|
$token = $Horizon.access_token
|
||||||
|
$server = $Horizon.server_name
|
||||||
|
|
||||||
|
$request_header = $global:request_header
|
||||||
|
$request_header += @{"authorization"="Bearer $token"}
|
||||||
|
|
||||||
|
$bsuri = "https://$server/rest/external/v1/base-vms?vcenter_id=$vcid&base_vm_id=$vmid"
|
||||||
|
|
||||||
|
if ($global:kishorizondebug) {
|
||||||
|
Write-Host $MyInvocation.MyCommand.Name Starting
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$bss = Invoke-RestMethod -Method Get -Uri $bsUri -Headers $request_header -SkipCertificateCheck
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
throw "ERROR accessing $bsUri`nStatusCode: $($_.Exception.Response.StatusCode.value__)`nStatusDescription: $($_.Exception.Response.StatusDescription)"
|
||||||
|
}
|
||||||
|
|
||||||
|
return $bss
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-HorizonEntitlements {
|
||||||
|
param (
|
||||||
|
[parameter(Mandatory = $true, ValueFromPipeline = $true)] $Horizon
|
||||||
|
)
|
||||||
|
$token = $Horizon.access_token
|
||||||
|
$server = $Horizon.server_name
|
||||||
|
|
||||||
|
$request_header = $global:request_header
|
||||||
|
$request_header += @{"authorization"="Bearer $token"}
|
||||||
|
|
||||||
|
$enturi = "https://$server/rest/entitlements/v1/desktop-pools"
|
||||||
|
|
||||||
|
if ($global:kishorizondebug) {
|
||||||
|
Write-Host $MyInvocation.MyCommand.Name Starting
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$entitlements = Invoke-RestMethod -Method Get -Uri $entUri -Headers $request_header -SkipCertificateCheck
|
||||||
|
|
||||||
|
} catch {
|
||||||
|
throw "ERROR accessing $entUri`nStatusCode: $($_.Exception.Response.StatusCode.value__)`nStatusDescription: $($_.Exception.Response.StatusDescription)"
|
||||||
|
}
|
||||||
|
|
||||||
|
return $entitlements
|
||||||
|
}
|
||||||
|
|
||||||
|
Function Get-HorizonEntitlementDetail {
|
||||||
|
param (
|
||||||
|
[pscustomobject] $entitlements,
|
||||||
|
[parameter(Mandatory = $true, ValueFromPipeline = $true)] $Horizon
|
||||||
|
)
|
||||||
|
|
||||||
|
$domains = $Horizon | Get-HorizonAdDomains
|
||||||
|
|
||||||
|
if ($global:kishorizondebug) {
|
||||||
|
Write-Host $MyInvocation.MyCommand.Name Starting
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = @()
|
||||||
|
foreach ($entitlement in $entitlements) {
|
||||||
|
$entsid = $entitlement
|
||||||
|
$user = $Horizon | Get-HorizonADUser -id $entitlement
|
||||||
|
|
||||||
|
$object = $null
|
||||||
|
if ($user) {
|
||||||
|
if ($user.group) {
|
||||||
|
$type = "Group"
|
||||||
|
} else {
|
||||||
|
$type = "User"
|
||||||
|
}
|
||||||
|
$object = [PSCustomObject]@{
|
||||||
|
Type = $type
|
||||||
|
Domain = $user.domain
|
||||||
|
SID = $entitlement
|
||||||
|
SamAccountName = $user.login_name
|
||||||
|
GivenName = $user.first_name
|
||||||
|
Surname = $user.last_name
|
||||||
|
Name = $user.name
|
||||||
|
UserPrincipalName = $user.user_principal_name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$result += $object
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-HorizonDesktopPool {
|
||||||
|
param (
|
||||||
|
[string]$poolname = "",
|
||||||
|
[parameter(Mandatory = $true, ValueFromPipeline = $true)] $Horizon
|
||||||
|
)
|
||||||
|
$token = $Horizon.access_token
|
||||||
|
$server = $Horizon.server_name
|
||||||
|
|
||||||
|
$request_header = $global:request_header
|
||||||
|
$request_header += @{"authorization"="Bearer $token"}
|
||||||
|
|
||||||
|
$pooluri = "https://$server/rest/inventory/v8/desktop-pools?sort_by=name&order_by=ASC"
|
||||||
|
|
||||||
|
if ($global:kishorizondebug) {
|
||||||
|
Write-Host $MyInvocation.MyCommand.Name Starting
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Test-Server -server $server) {
|
||||||
|
try {
|
||||||
|
$pools = Invoke-RestMethod -Method Get -Uri $pooluri -Headers $request_header -SkipCertificateCheck
|
||||||
|
if ($poolname) {
|
||||||
|
$pools = $pools | Where-Object {$_.Name -eq $poolname}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
Write-Host "ERROR accessing $poolUri"
|
||||||
|
Write-Host "StatusCode:" $_.Exception.Response.StatusCode.value__
|
||||||
|
Write-Host "StatusDescription:" $_.Exception.Response.StatusDescription
|
||||||
|
return $null
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($pools) {
|
||||||
|
$pools | Add-Member -MemberType NoteProperty -Name machines -Value @()
|
||||||
|
$pools | Add-Member -MemberType NoteProperty -Name sessions -Value @()
|
||||||
|
$pools | Add-Member -MemberType NoteProperty -Name vCenter -Value ""
|
||||||
|
$pools | Add-Member -MemberType NoteProperty -Name cluster -Value ""
|
||||||
|
$pools | Add-Member -MemberType NoteProperty -Name GpuEnabled -Value 0
|
||||||
|
$pools | Add-Member -MemberType NoteProperty -Name Entitlements -Value @()
|
||||||
|
$pools.provisioning_settings | Add-Member -MemberType NoteProperty -Name base_vm_name -Value ""
|
||||||
|
$pools.provisioning_settings | Add-Member -MemberType NoteProperty -Name snapshot_path -Value ""
|
||||||
|
$pools.provisioning_settings | Add-Member -MemberType NoteProperty -Name datacenter_name -Value ""
|
||||||
|
$pools.provisioning_settings | Add-Member -MemberType NoteProperty -Name resource_pool_name -Value ""
|
||||||
|
$pools.provisioning_settings | Add-Member -MemberType NoteProperty -Name base_folder_path -Value ""
|
||||||
|
$pools.storage_settings.datastores | Add-Member -MemberType NoteProperty -Name name -Value ""
|
||||||
|
|
||||||
|
$machines = $Horizon | Get-HorizonMachine
|
||||||
|
$sessions = $Horizon | Get-HorizonSession
|
||||||
|
|
||||||
|
$counter = 1
|
||||||
|
foreach ($pool in $pools) {
|
||||||
|
Write-Progress -Activity "Processing Pools" -Status "Processing $counter of $($pools.length) - $($pool.name)" -PercentComplete (($counter / $pools.length) *100) -Id 1
|
||||||
|
$mymachines = $machines | where {$_.desktop_pool_id -eq $pool.id}
|
||||||
|
$pool.machines += $mymachines
|
||||||
|
$mysessions = $sessions | where {$_.desktop_pool_id -eq $pool.id}
|
||||||
|
$pool.sessions += $mysessions
|
||||||
|
foreach($session in $pool.sessions) {
|
||||||
|
$machineDNSName = ($mymachines | where {$_.id -eq $session.machine_id}).dns_name
|
||||||
|
$machineName = ($mymachines | where {$_.id -eq $session.machine_id}).name
|
||||||
|
$session | Add-Member -MemberType NoteProperty -Name machine_name -Value $machineName
|
||||||
|
$session | Add-Member -MemberType NoteProperty -Name machine_dns_name -Value $machineDNSName
|
||||||
|
}
|
||||||
|
$pool.vCenter = ($hzconn | Get-HorizonVcenter | where-object {$_.id -eq $pool.vcenter_id}).server_name
|
||||||
|
if (-not ($global:DefaultVIServer.Name -contains $pool.vCenter)) {
|
||||||
|
throw "Not Connected to vCenter $($pool.vCenter)"
|
||||||
|
}
|
||||||
|
$pool.cluster = (Get-Cluster | where {$_.id -eq "ClusterComputeResource-$($pool.provisioning_settings.host_or_cluster_id)"}).name
|
||||||
|
$pool.provisioning_settings.base_vm_name = (Get-View -Id "VirtualMachine-$($pool.provisioning_settings.parent_vm_id)").Name
|
||||||
|
$snappath = ""
|
||||||
|
$goldVM = Get-VM $pool.provisioning_settings.base_vm_name
|
||||||
|
$thissnap = $goldVM | Get-Snapshot | Where-Object {$_.Id -eq "VirtualMachineSnapshot-$($pool.provisioning_settings.base_snapshot_id)"}
|
||||||
|
while ($thissnap.parentsnapshotid.length -gt 0) {
|
||||||
|
if ($snappath -eq "") {
|
||||||
|
$snappath = $thissnap.Name
|
||||||
|
} else {
|
||||||
|
$snappath = $thissnap.Name + "\" + $snappath
|
||||||
|
}
|
||||||
|
$thissnap = Get-VM $pool.provisioning_settings.base_vm_name | Get-Snapshot | Where-Object {$_.Id -eq $thissnap.ParentSnapshotId}
|
||||||
|
}
|
||||||
|
if ($snappath -eq "") {
|
||||||
|
$snappath = $thissnap.Name
|
||||||
|
} else {
|
||||||
|
$snappath = $thissnap.Name + "\" + $snappath
|
||||||
|
}
|
||||||
|
$pool.provisioning_settings.snapshot_path = $snappath
|
||||||
|
$pool.provisioning_settings.resource_pool_name = $goldVM.ResourcePool
|
||||||
|
$pool.provisioning_settings.datacenter_name = (Get-View "Datacenter-$($pool.provisioning_settings.datacenter_id)").Name
|
||||||
|
$basefolderpath = ""
|
||||||
|
$thisfolder = Get-View "Folder-$($pool.provisioning_settings.vm_folder_id)"
|
||||||
|
while ($thisfolder.parentsnapshotid.length -gt 0) {
|
||||||
|
if ($basefolderpath -eq "") {
|
||||||
|
$basefolderpath = $thisfolder.Name
|
||||||
|
} else {
|
||||||
|
$basefolderpath = $thisfolder.Name + "\" + $basefolderpath
|
||||||
|
}
|
||||||
|
$thisfolder = Get-View "Folder-$($thisfolder.Parent)"
|
||||||
|
}
|
||||||
|
if ($basefolderpath -eq "") {
|
||||||
|
$basefolderpath = $thisfolder.Name
|
||||||
|
} else {
|
||||||
|
$basefolderpath = $thisfolder.Name + "\" + $basefolderpath
|
||||||
|
}
|
||||||
|
$pool.provisioning_settings.base_folder_path = $basefolderpath
|
||||||
|
$datastores = @()
|
||||||
|
foreach ($datastore in $pool.storage_settings.datastores) {
|
||||||
|
$thisds = Get-View -Id "Datastore-$($datastore.datastore_id)"
|
||||||
|
$datastore.name = $thisds.Name
|
||||||
|
$datastores += $thisds.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
$pool.GpuEnabled = ($pool.display_protocol_settings.vgpu_grid_profile.Length -gt 0);
|
||||||
|
$entsids = ($Horizon | Get-HorizonEntitlements | Where-Object {$_.id -eq $pool.id}).ad_user_or_group_ids
|
||||||
|
if ($entsids.length -gt 0) {
|
||||||
|
$entitlements = $Horizon | Get-HorizonEntitlementDetail -entitlements $entsids
|
||||||
|
$pool.entitlements = $entitlements
|
||||||
|
}
|
||||||
|
++$counter
|
||||||
|
}
|
||||||
|
Write-Progress -Activity "Processing Pools" -Completed -Id 1
|
||||||
|
return $pools
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return $false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-HorizonAdDomains {
|
||||||
|
param (
|
||||||
|
[parameter(Mandatory = $true, ValueFromPipeline = $true)] $Horizon
|
||||||
|
)
|
||||||
|
$token = $Horizon.access_token
|
||||||
|
$server = $Horizon.server_name
|
||||||
|
|
||||||
|
$request_header = $global:request_header
|
||||||
|
$request_header += @{"authorization"="Bearer $token"}
|
||||||
|
|
||||||
|
$domainURI = "https://$server/rest/external/v2/ad-domains"
|
||||||
|
|
||||||
|
if ($global:kishorizondebug) {
|
||||||
|
Write-Host $MyInvocation.MyCommand.Name Starting
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$domains = Invoke-RestMethod -Method Get -Uri $domainUri -Headers $request_header -SkipCertificateCheck
|
||||||
|
} catch {
|
||||||
|
throw "ERROR accessing $domainUri`nStatusCode: $($_.Exception.Response.StatusCode.value__)`nStatusDescription: $($_.Exception.Response.StatusDescription)"
|
||||||
|
}
|
||||||
|
|
||||||
|
return $domains
|
||||||
|
}
|
||||||
|
|
||||||
|
Function Get-HorizonADUser {
|
||||||
|
param (
|
||||||
|
[parameter(Mandatory = $true, ValueFromPipeline = $true)] $Horizon,
|
||||||
|
[string]$Domain = "",
|
||||||
|
[string]$Id = "",
|
||||||
|
[string]$Name = "",
|
||||||
|
[int]$Page = 0,
|
||||||
|
[int]$PageSize = 0
|
||||||
|
)
|
||||||
|
$token = $Horizon.access_token
|
||||||
|
$server = $Horizon.server_name
|
||||||
|
|
||||||
|
$request_header = $global:request_header
|
||||||
|
$request_header += @{"authorization"="Bearer $token"}
|
||||||
|
|
||||||
|
$userlookupURI = "https://$server/rest/external/v1/ad-users-or-groups"
|
||||||
|
|
||||||
|
$extension = "";
|
||||||
|
|
||||||
|
if ($domain -ne "") {
|
||||||
|
$extension = "?group_only=false&filter={`"type`":`"Equals`",`"name`":`"domain`",`"value`":`"$domain`"}"
|
||||||
|
if ($Page -ne 0) {
|
||||||
|
$extension += "&page=$Page"
|
||||||
|
if ($PageSize -ne 0) {
|
||||||
|
$extension += "&size=$PageSize"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($Id -ne "") {
|
||||||
|
$extension = "/$($Id)?group_only=false"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($Name -ne "") {
|
||||||
|
$extension = "?group_only=false&filter={`"type`":`"Equals`",`"name`":`"user_principal_name`",`"value`":`"$Name`"}"
|
||||||
|
}
|
||||||
|
|
||||||
|
$userlookupURI += $extension
|
||||||
|
|
||||||
|
if ($global:kishorizondebug) {
|
||||||
|
Write-Host $MyInvocation.MyCommand.Name Starting
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$users = Invoke-RestMethod -Method Get -Uri $userlookupURI -Headers $request_header -SkipCertificateCheck
|
||||||
|
} catch {
|
||||||
|
throw "ERROR accessing $userlookupURI`nStatusCode: $($_.Exception.Response.StatusCode.value__)`nStatusDescription: $($_.Exception.Response.StatusDescription)"
|
||||||
|
}
|
||||||
|
|
||||||
|
return $users
|
||||||
|
}
|
||||||
|
|
||||||
|
<# Function Get-HorizonADGroup {
|
||||||
|
param (
|
||||||
|
[parameter(Mandatory = $true, ValueFromPipeline = $true)] $Horizon,
|
||||||
|
[string]$Domain = "",
|
||||||
|
[string]$Id = "",
|
||||||
|
[string]$Name = "",
|
||||||
|
[int]$Page = 0,
|
||||||
|
[int]$PageSize = 0
|
||||||
|
)
|
||||||
|
$token = $Horizon.access_token
|
||||||
|
$server = $Horizon.server_name
|
||||||
|
|
||||||
|
$request_header = $global:request_header
|
||||||
|
$request_header += @{"authorization"="Bearer $token"}
|
||||||
|
|
||||||
|
$grouplookupURI = "https://$server/rest/external/v1/ad-users-or-groups"
|
||||||
|
|
||||||
|
$extension = "";
|
||||||
|
|
||||||
|
if ($domain -ne "") {
|
||||||
|
$extension = "?group_only=true&filter={`"type`":`"Equals`",`"name`":`"domain`",`"value`":`"$domain`"}"
|
||||||
|
if ($Page -ne 0) {
|
||||||
|
$extension += "&page=$Page"
|
||||||
|
if ($PageSize -ne 0) {
|
||||||
|
$extension += "&size=$PageSize"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($Id -ne "") {
|
||||||
|
$extension = "/$($Id)?group_only=true"
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($Name -ne "") {
|
||||||
|
$extension = "?group_only=true&filter={`"type`":`"Equals`",`"name`":`"name`",`"value`":`"$Name`"}"
|
||||||
|
}
|
||||||
|
|
||||||
|
$grouplookupURI += $extension
|
||||||
|
|
||||||
|
if ($global:kishorizondebug) {
|
||||||
|
Write-Host $MyInvocation.MyCommand.Name Starting
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$users = Invoke-RestMethod -Method Get -Uri $grouplookupURI -Headers $request_header -SkipCertificateCheck
|
||||||
|
} catch {
|
||||||
|
throw "ERROR accessing $grouplookupURI`nStatusCode: $($_.Exception.Response.StatusCode.value__)`nStatusDescription: $($_.Exception.Response.StatusDescription)"
|
||||||
|
}
|
||||||
|
|
||||||
|
return $users
|
||||||
|
} #>
|
||||||
|
|
||||||
|
function Invoke-SSHCommand {
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory)]
|
||||||
|
$ComputerName,
|
||||||
|
[Parameter(Mandatory)]
|
||||||
|
$Command,
|
||||||
|
[Parameter(Mandatory)]
|
||||||
|
$Credential
|
||||||
|
)
|
||||||
|
|
||||||
|
$username = $($Credential.UserName)
|
||||||
|
$password = $($Credential.GetNetworkCredential().password)
|
||||||
|
$done = $false
|
||||||
|
$count = 0;
|
||||||
|
while (!$done) {
|
||||||
|
$result = invoke-command {sshpass -p $password ssh $username@$ComputerName "$command"}
|
||||||
|
if ($result) {
|
||||||
|
$done = $true
|
||||||
|
} else {
|
||||||
|
++$count
|
||||||
|
if ($count >=5) { $done = $true }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-HorizonSessionLength {
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory)] $session
|
||||||
|
)
|
||||||
|
$now = [int](get-date -UFormat %s -Millisecond 0)
|
||||||
|
|
||||||
|
$start = $session.start_time
|
||||||
|
|
||||||
|
$retval = $now - ($start/ 1000)
|
||||||
|
|
||||||
|
return $retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Get-HorizonSessionConnectedLength {
|
||||||
|
param (
|
||||||
|
[Parameter(Mandatory)] $session
|
||||||
|
)
|
||||||
|
$retval = ($session.session_state -eq "CONNECTED") ? [math]::round($session.last_session_duration_ms/1000) : 0
|
||||||
|
|
||||||
|
return $retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Export-ModuleMember -Function Connect-HorizonServer
|
||||||
|
Export-ModuleMember -Function Get-HorizonVcenter
|
||||||
|
Export-ModuleMember -Function Get-HorizonDesktopPool
|
||||||
|
Export-ModuleMember -Function Get-HorizonMachine
|
||||||
|
Export-ModuleMember -Function Remove-HorizonMachine
|
||||||
|
Export-ModuleMember -Function Get-HorizonSession
|
||||||
|
Export-ModuleMember -Function Get-HorizonAdDomains
|
||||||
|
Export-ModuleMember -Function Get-HorizonAdUser
|
||||||
|
Export-ModuleMember -Function Invoke-SSHCommand
|
||||||
|
Export-ModuleMember -Function Get-HorizonSessionLength
|
||||||
|
Export-ModuleMember -Function Get-HorizonKeyAgreement
|
||||||
|
Export-ModuleMember -Function Get-HorizonEntitlements
|
||||||
|
Export-ModuleMember -Function Get-HorizonEntitlementDetail
|
||||||
|
|
||||||
@@ -0,0 +1,210 @@
|
|||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user