Skip to content

Commit 12e87dc

Browse files
author
Haan Mo Johng
committed
Update Get-VmInUnknownPowerState
1 parent ee9c4f9 commit 12e87dc

File tree

2 files changed

+348
-0
lines changed

2 files changed

+348
-0
lines changed
Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
Param([string]$HypervisorConnectionName, [string]$BrokerCatalogName, [switch]$Fix)
2+
3+
# Add XDHyp
4+
Add-PSSnapin -Name "Citrix.Broker.Admin.V2","Citrix.Host.Admin.V2"
5+
6+
# Get unavailable hypervisor connections.
7+
function Get-ConnectionUnavailable {
8+
$connectionsUnavailable = [System.Collections.ArrayList]::new()
9+
10+
# If a hypervisor is specified an an input, check the hypervisor connection associated the input.
11+
if ($BrokerCatalogName) {
12+
try {
13+
$connName = (Get-BrokerMachine -CatalogName $BrokerCatalogName -Property @("HypervisorConnectionName") -MaxRecordCount 1).HypervisorConnectionName
14+
$connection = Get-BrokerHypervisorConnection -Name $connName -Property @("Name", "HypHypervisorConnectionUid", "State", "FaultState")
15+
if ($connection.State -eq "Unavailable") {
16+
return $connection
17+
}
18+
return $null
19+
}
20+
catch {
21+
Write-Error -Message "Failed while getting connections unavailables. Check the input broker catalog $($BrokerCatalogName) and the associated connection." -ErrorAction Stop
22+
}
23+
}
24+
# If a catalog is specified an an input, check the hypervisor connection associated the input.
25+
elseif ($HypervisorConnectionName) {
26+
try {
27+
$connection = Get-BrokerHypervisorConnection -Name $HypervisorConnectionName -Property @("Name", "HypHypervisorConnectionUid", "State", "FaultState")
28+
if ($connection.State -eq "Unavailable") {
29+
return $connection
30+
}
31+
return $null
32+
}
33+
catch {
34+
Write-Error -Message "Failed while getting connections unavailables. Check the input hypervisor connection $($HypervisorConnectionName)." -ErrorAction Stop
35+
}
36+
}
37+
# else check all hypervisor connections
38+
else {
39+
try {
40+
# Get the total connection count.
41+
Get-BrokerHypervisorConnection -State "Unavailable" -ReturnTotalRecordCount -MaxRecordCount 0 -ErrorVariable resultsCount -ErrorAction SilentlyContinue
42+
$totalCount = $resultsCount[0].TotalAvailableResultCount
43+
$currentCount = 0
44+
$skipCount = 0
45+
46+
# Get the hypervisor connections unavailable. Each iteration can receive maximum 250 records only.
47+
while ($skipCount -lt $totalCount) {
48+
# Get 250 connections unavailable.
49+
$partialConnections = Get-BrokerHypervisorConnection -MaxRecordCount 250 -Skip $skipCount -State "Unavailable" -Property @("Name", "HypHypervisorConnectionUid", "FaultState")
50+
51+
# Add the hypervisor connections
52+
[void]$connectionsUnavailable.Add($partialConnections)
53+
54+
# Increase the skip count to check the remaining connections.
55+
$skipCount += 250
56+
57+
# Count the connections checked.
58+
$currentCount = $partialConnections.Count
59+
Write-Host "Checked $($currentCount) unavailable connections out of $($totalCount) unavailable connections ($($currentCount / $totalCount * 100))%."
60+
}
61+
62+
# return the total connections
63+
return $connectionsUnavailable
64+
}
65+
catch {
66+
Write-Error -Message "Failed while getting connections unavailables. Check Get-BrokerHypervisorConnection to get more than 250 records." -ErrorAction Stop
67+
}
68+
}
69+
}
70+
71+
72+
# Get the actual VMs on hypervisors.
73+
function Get-VmOnHypervisor {
74+
Param ($UnavailableConnection)
75+
76+
# Get VMs on the Hypervisor. If the connection is unavailable, ignore the connection.
77+
$vmList = [System.Collections.ArrayList]::new()
78+
$previousCount = 0
79+
80+
try {
81+
Get-ChildItem -Path @('XDHyp:\Connections') | Where-Object {$UnavailableConnection.Name -NotContains $_.HypervisorConnectionName} | ForEach-Object {
82+
# If a catalog is specified an an input, get the VMs associated to the catalog only.
83+
if ($BrokerCatalogName) {
84+
try {
85+
$connName = (Get-BrokerMachine -CatalogName $BrokerCatalogName -Property @("HypervisorConnectionName") -MaxRecordCount 1).HypervisorConnectionName
86+
if ($_.HypervisorConnectionName -eq $connName) {
87+
Write-Host "Checking the VMs on $($_.ConnectionTypeName), where the connection $($connName) connects to"
88+
Find-VMs -Path $_.PSPath -Connection $_.HypervisorConnectionName -VmList $vmList
89+
Write-Host "Checked $($vmList.Count - $previousCount) VMs using the connection $($_.HypervisorConnectionName)."
90+
}
91+
}
92+
catch {
93+
Write-Error -Message "Failed while getting VMs on XDHyp. Check the broker catalog $($BrokerCatalogName) and $($_.PSPath)." -ErrorAction Stop
94+
}
95+
}
96+
# If a hypervisor is specified an an input, get the VMs associated to the connection only.
97+
elseif ($HypervisorConnectionName) {
98+
try {
99+
# If the current connection equals to the input connection, then find the VMs on the connection.
100+
if ($_.HypervisorConnectionName -eq $HypervisorConnectionName) {
101+
Write-Host "Checking the VMs on $($_.ConnectionTypeName), where the connection $($_.HypervisorConnectionName) connects to"
102+
Find-VMs -Path $_.PSPath -Connection $_.HypervisorConnectionName -VmList $vmList
103+
Write-Host "Checked $($vmList.Count - $previousCount) VMs using the connection $($_.HypervisorConnectionName)."
104+
}
105+
}
106+
catch {
107+
Write-Error -Message "Failed while getting VMs on XDHyp. Check the hypervisor conection $($HypervisorConnectionName) and $($_.PSPath)." -ErrorAction Stop
108+
}
109+
}
110+
# Else, find VMs on all hypervisor connections.
111+
else {
112+
try {
113+
Write-Host "Checking the VMs using the connection $($_.HypervisorConnectionName)"
114+
$previousCount = $vmList.Count
115+
Find-VMs -Path $_.PSPath -Connection $_.HypervisorConnectionName -VmList $vmList
116+
Write-Host "Checked $($vmList.Count - $previousCount) VMs using the connection $($_.HypervisorConnectionName)."
117+
}
118+
catch {
119+
Write-Error -Message "Failed while getting VMs on XDHyp. Check $($_.PSPath)." -ErrorAction Stop
120+
}
121+
}
122+
}
123+
return $vmList
124+
}
125+
catch {
126+
Write-Error -Message "Failed while getting VMs on XDHyp." -ErrorAction Stop
127+
}
128+
}
129+
130+
# Get the actual VMs on hypervisors.
131+
function Find-VMs {
132+
param ($Path, $ConnectionName, $VmList)
133+
134+
try {
135+
Get-ChildItem -LiteralPath @($Path) -Force | ForEach-Object {
136+
# If the item is VM, then add it to the VM list
137+
if ($_.ObjectType -eq "Vm") {
138+
$vm = $_ | Select-Object FullName, Id, HypervisorConnectionName
139+
$vm.HypervisorConnectionName = $ConnectionName
140+
[void]$VmList.Add($vm)
141+
}
142+
# If the item contains sub-items, the travel the item as well
143+
if ( ($_.PSIsContainer -eq $true) -or ($_.IsContainer -eq $true)) {
144+
Find-VMs -Path $_.PSPath -Connection $ConnectionName -VmList $vmList
145+
}
146+
}
147+
}
148+
catch {
149+
Write-Error -Message "Failed while finding VMs on $($Path)." -ErrorAction Stop
150+
}
151+
}
152+
153+
# Get the VMs on Broker in an Unknown Power State.
154+
function Get-VmOnBroker {
155+
$vmsUnknown = [System.Collections.ArrayList]::new()
156+
157+
# Get the total number of VMs in an unknown state. If a catalog or a hypervisor connection is specified, then count the VMs associated to the input only.
158+
try {
159+
if ($BrokerCatalogName) {
160+
Get-BrokerMachine -PowerState "Unknown" -CatalogName $BrokerCatalogName -ReturnTotalRecordCount -MaxRecordCount 0 -ErrorVariable resultsCount -ErrorAction SilentlyContinue
161+
}
162+
elseif ($HypervisorConnectionName) {
163+
Get-BrokerMachine -PowerState "Unknown" -HypervisorConnectionName $HypervisorConnectionName -ReturnTotalRecordCount -MaxRecordCount 0 -ErrorVariable resultsCount -ErrorAction SilentlyContinue
164+
}
165+
else {
166+
Get-BrokerMachine -PowerState "Unknown" -ReturnTotalRecordCount -MaxRecordCount 0 -ErrorVariable resultsCount -ErrorAction SilentlyContinue
167+
}
168+
$totalCount = $resultsCount[0].TotalAvailableResultCount
169+
if ($totalCount -eq 0) {
170+
Write-Host "No VMs are in an unknown power state."
171+
exit
172+
}
173+
}
174+
catch {
175+
Write-Error -Message "Failed while getting the total number of VMs on CVAD in an unknown power state." -ErrorAction Stop
176+
}
177+
178+
# Get the VMs with the unknown power state. Each iteration can receive maximum 250 records only.
179+
$currentCount = 0
180+
$skipCount = 0
181+
182+
try {
183+
while ($skipCount -lt $totalCount) {
184+
# If a catalog name is specified in the input
185+
if ($BrokerCatalogName) {
186+
# Get 250 VMs in an unknown power state associated to the specified broker catalog.
187+
$partialVMs = Get-BrokerMachine -PowerState "Unknown" -MaxRecordCount 250 -Skip $skipCount -CatalogName $BrokerCatalogName -Property @("HostedMachineName", "HostedMachineId", "CatalogName", "CatalogUUID", "HypervisorConnectionName", "HypHypervisorConnectionUid")
188+
}
189+
# If a hypervisor name is specified in the input
190+
elseif ($HypervisorConnectionName) {
191+
# Get 250 VMs in an unknown power state associated to the specified hypervisor connection.
192+
$partialVMs = Get-BrokerMachine -PowerState "Unknown" -MaxRecordCount 250 -Skip $skipCount -HypervisorConnectionName $HypervisorConnectionName -Property @("HostedMachineName", "HostedMachineId", "CatalogName", "CatalogUUID", "HypervisorConnectionName", "HypHypervisorConnectionUid")
193+
}
194+
# If there is no input
195+
else {
196+
# Get 250 VMs in an unknown power state.
197+
$partialVMs = Get-BrokerMachine -PowerState "Unknown" -MaxRecordCount 250 -Skip $skipCount -Property @("HostedMachineName", "HostedMachineId", "CatalogName", "CatalogUUID", "HypervisorConnectionName", "HypHypervisorConnectionUid")
198+
}
199+
200+
# Count the VMs loaded.
201+
$currentCount = $partialVMs.Count
202+
Write-Host "Checked $($currentCount) VMs out of $($totalCount) VMs ($($currentCount / $totalCount * 100)%)."
203+
204+
# Add the VMs in an unknown power state
205+
$partialVMs = $partialVMs | Select-Object HostedMachineName, HostedMachineId, CatalogName, CatalogUUID, HypervisorConnectionName, HypHypervisorConnectionUid, Cause, Suggestion
206+
[void]$vmsUnknown.Add($partialVMs)
207+
208+
# Increase the skip count to check the remaining VMs.
209+
$skipCount += 250
210+
}
211+
212+
# return the total VMs
213+
return $vmsUnknown
214+
}
215+
catch {
216+
Write-Error -Message "Failed while getting partial VMs on CVAD in an unknown power state." -ErrorAction Stop
217+
}
218+
}
219+
220+
# Main logic.
221+
function Get-VMsInUnknownPowerState {
222+
$result = [System.Collections.ArrayList]::new()
223+
224+
# Get the hypervisor connections that have unavailable state
225+
Write-Host "1. Checking the states of hypervisor connections."
226+
$connectionsUnavailable = Get-ConnectionUnavailable
227+
228+
# Get the VMs in an unknown power state.
229+
Write-Host "2. Checking the VMs on CVAD in an unknown power state."
230+
$vmsUnknown = Get-VmOnBroker
231+
232+
# Get actual VMs on hypervisors
233+
Write-Host "3. Checking VMs on hypervisors corresponding to the VMs on CVAD."
234+
$vmsOnHyp = Get-VmOnHypervisor -UnavailableConnection $connectionsUnavailable
235+
236+
# Iterate each VM in an Unknown Power State.
237+
Write-Host "4. Reporting (and Fixing) the VMs in an unknown power state."
238+
239+
try {
240+
$vmsUnknown | ForEach-Object -Process {
241+
# Scenario 1: Broken Host Connections.
242+
# Get the host connection state that the VM is associated.
243+
$connection = $connectionsUnavailable | Where-Object HypHypervisorConnectionUid -eq $_.HypHypervisorConnectionUid
244+
if ($connection -ne $null) {
245+
$_.Cause = "The hypervisor connection is unavailable due to $($connection.FaultState)."
246+
$_.Suggestion = "Check the hypervisor connection named $($connection.Name)."
247+
}
248+
# Scenario 2: VMs are deleted.
249+
elseif ($vmsOnHyp.FullName -NotContains $($_.HostedMachineName + ".vm")) {
250+
$_.Cause = "The VM on the hypervisor is deleted."
251+
$_.Suggestion = "Check whether the VM still exists on the hypervisor and then update CVAD."
252+
}
253+
# Scenario 3: the id of the VM on CVAD is mismatched with the id of the corresponding VM on the hypervisor.
254+
elseif ($vmsOnHyp.FullName -Contains $($_.HostedMachineName + ".vm")) {
255+
$curVmOnHyp = $vmsOnHyp | Where-Object FullName -eq $($_.HostedMachineName + ".vm")
256+
if ($curVmOnHyp.Id -ne $_.HostedMachineId) {
257+
$_.Cause = "The ID of the VM on CVAD is mismatched with the ID of the corresponding VM on the hypervisor."
258+
# if -Fix parameter is given, then update the VM ID.
259+
if ($Fix) {
260+
try {
261+
Get-BrokerMachine -HostedMachineName $_.HostedMachineName | Set-BrokerMachine -HostedMachineId $curVmOnHyp.Id
262+
}
263+
catch {
264+
$_.Cause = "Failed to fix the mismatched IDs of VMs."
265+
$_.Suggestion = "Update the VM ID with $($curVmOnHyp.Id), then restart the broker service. `n option 1) To fix it automatiicaly, Get-MachineUnknown -Fix `n option 2) To fix it manually, Get-BrokerMachine -HostedMachineName ""$($_.HostedMachineName)"" | Set-BrokerMachine -HostedMachineId ""$($curVmOnHyp.Id)"""
266+
}
267+
$_.Suggestion = "The VM ID is updated with $($curVmOnHyp.Id), then restart the broker service."
268+
} else {
269+
# If -Fix parameter is not given, then provide a way to fix the ID manually
270+
$_.Suggestion = "Update VM ID with $($curVmOnHyp.Id), then restart the broker service. `n option 1) To fix it automatiicaly, Get-ProvVmInUnknownPowerState -Fix `n option 2) To fix it manually, Get-BrokerMachine -HostedMachineName ""$($_.HostedMachineName)"" | `nSet-BrokerMachine -HostedMachineId ""$($curVmOnHyp.Id)"""
271+
}
272+
}
273+
else {
274+
# After fixing the mismatche ID issue, but the broker service is not restarted properly yet.
275+
$_.Cause = "Please make sure the broker service is restarted properly."
276+
$_.Suggestion = "Restart the broker service or wait for the broker service to be restarted."
277+
}
278+
}
279+
# Exeptional cases. The broker service is not restarted properly yet.
280+
else {
281+
$_.Cause = "Please make sure the broker service is restarted properly."
282+
$_.Suggestion = "Restart the broker service or wait for the broker service to be restarted."
283+
}
284+
[void]$result.Add($_)
285+
}
286+
}
287+
catch {
288+
Write-Error -Message "Failed while updating the cause and suggestion for $($_.HostedMachineName)." -ErrorAction Stop
289+
}
290+
291+
return $result
292+
}
293+
294+
# Begining of Get-ProvVmInUnknownPowerState.
295+
$result = Get-VMsInUnknownPowerState
296+
return $result
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Get-ProvVmInUnknownPowerState: Diagnose and Recover VMs in an Unknow Power State.
2+
3+
This script is to diagnose and (semi-) automatically resolve VMs on Citrix Virtual Apps and Desktops (CVAD) in an unknown power state.
4+
5+
6+
## Getting Started
7+
8+
Official documentation of this script is available here: https://info.citrite.net/x/84xlXg
9+
(This link is an internal blog link. This will be updated after public blog is published.)
10+
11+
## Context and Problems
12+
The power state of the VMs on Citrix Virtual Apps and Desktops (CVAD) represents the power state of the corresponding VMs on hypervisors. However, in some cases, customers are experiencing VMs on CVAD continuously showing the power state unknown. Below are the reasons for the unknown power state.
13+
14+
1. The ID of the VM on CVAD mismatches with the corresponding ID of the VM on the hypervisor.
15+
2. The VM on the hypervisor is deleted.
16+
3. The hypervisor connection on CVAD is broken.
17+
18+
The current practice to resolve the issue is helping the customers manually change the IDs of VM on CVAD stored on their database, change the IDs on the hypervisor, e.g., VMware, or delete and recreate the VMs. Those practices would be inconvenient and error-prone for customers, especially when a number of machines are in an unknown power state.
19+
20+
21+
## A Solution: Get-ProvVmInUnknownPowerState
22+
23+
Get-ProvVmInUnknownPowerState diagnoses the VMs on CVAD in an unknown power state, reports the causes, and (semi-) automatically fixes the causes. Specifically, Get-ProvVmInUnknownPowerState utilizes the names of VMs on CVAD to find the corresponding VMs on hypervisors and check three scenarios below that can make the power state of the VMs unknown.
24+
25+
Scenario 1: the state of the hypervisor connection is unavailable. such as connection credentials being invalid.
26+
Scenario 2: the VMs on the hypervisor are deleted.
27+
Scenario 3: the IDs of VMs on CVAD mismatches the corresponding IDs of the VM on hypervisors.
28+
29+
30+
## Prerequisites
31+
32+
Minimal permission to execute Set-BrokerMachine is required.
33+
34+
35+
## Built With
36+
37+
* [Microsoft Powershell](https://msdn.microsoft.com/powershell)
38+
39+
40+
## EXAMPLES
41+
42+
The detailed examples are described in the blog.
43+
44+
45+
## Versioning & Authors
46+
47+
VERSION
48+
1.0.0
49+
50+
CONTRIBUTORS
51+
Haan Mo Johng, Engineering
52+
Charlie Wang, Engineering

0 commit comments

Comments
 (0)