Jump to content
Welcome to our new Citrix community!
  • 1

Rolling Reboots in XenApp


Garrett Taylor1709157621

Question

I've been after a method to perform gently rolling reboots in XenApp for a long time and finally just took it upon myself to write something in PowerShell. This script can be run by a scheduled task with a semi-regular frequency (30-60 minutes?). I wanted something that would be refreshing provisioned VMs regularly without logging users out. I had always envisioned this working in a health care environment where you have users on 24x7. You would definitely want to set this in concert with a disconnected session timer.

 

Here's how it works:

  • Get Delivery Groups tagged with "AutoReboot"
  • Only permit a certain number of hosts to be rebooting or in maintenance mode or otherwise offline
  • Don't bother rebooting a host of it's only been for a short time
  • Reboot the oldest servers first
  • Don't reboot servers with users, drain them first

 

#Server reboot script
asnp Citrix*
#Minimum uptime. Servers with uptime less than the below value will be ignored
$minUptime = 16 #hours

#Server Down Threshold
$serverDownThreshold = 20 #percent

#Set the Broker Server
$broker = "yourDDC.Yourdomain.com"

#Get Delivery Groups tagged for Reboots

foreach ($group in Get-BrokerDesktopGroup -AdminAddress $broker -Tag AutoReboot)
{
    write-host $group.Name
	#collect percentage of VMs that are available
	$total = (Get-BrokerMachine -DesktopGroupName $group.Name -AdminAddress $broker| measure-object).Count
    write-host "`t" $total hosts found
	$allowedDown = [Math]::Ceiling($total * $serverDownThreshold / 100)
    write-host "`t" $allowedDown hosts allowed down
	$down = 0;
	$down += (Get-BrokerMachine -DesktopGroupName $group.Name -RegistrationState Unregistered -AdminAddress $broker| measure-object).Count
	$down += (Get-BrokerMachine -DesktopGroupName $group.Name -RegistrationState AgentError -AdminAddress $broker| measure-object).Count
	$down += (Get-BrokerMachine -DesktopGroupName $group.Name -InMaintenanceMode $true -AdminAddress $broker| measure-object).Count
	write-host "`t" $down hosts are down
	#Check machines in maintenance mode to see if we can reboot any of them
	foreach ($vm in Get-BrokerMachine -DesktopGroupName $group.Name -InMaintenanceMode $true -AdminAddress $broker)
	{
        write-host "`t" $vm.MachineName "In Maintenance Mode:" $vm.AssociatedUserNames.Count Users - Pending Reboot
		if ($vm.AssociatedUserNames.Count -eq 0)
		{
			New-BrokerHostingPowerAction -MachineName $vm.MachineName -Action Restart -AdminAddress $broker
			write-host Reboot $vm.MachineName
			Set-BrokerMachineMaintenanceMode -InputObject $vm.MachineName -MaintenanceMode $false -AdminAddress $broker
			write-host Disable MaintenanceMode $vm.MachineName
		}
	}
	if ($down -lt $allowedDown)
	{
		$vmsToGet = $allowedDown - $down
        $vmsWithUptime = @{};
        write-host "`t" need $vmsToGet more hosts down
		foreach ($vm in Get-BrokerMachine -DesktopGroupName $group.Name -RegistrationState Registered -InMaintenanceMode $false)
	    {
            try { 
                $wmi=Get-WmiObject -class Win32_OperatingSystem -computer $vm.IPAddress #use IP in case name resolution fails
                $LBTime=$wmi.ConvertToDateTime($wmi.Lastbootuptime)
                [TimeSpan]$uptime=New-TimeSpan $LBTime $(get-date)
                $vmsWithUptime.Add($vm.MachineName,$uptime)
                write-host "`t`t" $vm.MachineName $uptime.TotalHours Hours
            }
            catch {  
               
            }
        }
        foreach ($k in $vmsWithUptime.GetEnumerator() | Where-Object {$_.value.TotalHours -ge $minUptime} | sort -Property value.TotalHours -descending | select -first $vmsToGet)
        {
        	$vm = Get-BrokerMachine -MachineName $k.Name		
			if ($vm.AssociatedUserNames.Count -eq 0)
			{
				New-BrokerHostingPowerAction -MachineName $vm.MachineName -Action Restart -AdminAddress $broker
				write-host Reboot $vm.MachineName
			}
			else
			{
				Set-BrokerMachineMaintenanceMode -InputObject $vm.MachineName -MaintenanceMode $true -AdminAddress $broker
				write-host Enable MaintenanceMode $vm.MachineName
			}
		}
	}
}

To Do

  • Build better logging (Event Log?)
  • Per delivery group threshold settings (Tags?)

 

So to all of you who have PowerShell experience, please tell me everything I did wrong :13_upside_down:

 

Edit: There's no PowerShell Syntax highlighting in the code editor?

 

 

Link to comment

5 answers to this question

Recommended Posts

  • 0

Hi,

 

I have tried using your script but it seems to be failing at the below block:-

 

 write-host "`t" need $vmsToGet more hosts down
        foreach ($vm in Get-BrokerMachine -DesktopGroupName $group.Name -RegistrationState Registered -InMaintenanceMode $false)
        {
            try { 
                $wmi=Get-WmiObject -class Win32_OperatingSystem -computer $vm.IPAddress #use IP in case name resolution fails
                $LBTime=$wmi.ConvertToDateTime($wmi.LastBootUpTime)
                [TimeSpan]$uptime=New-TimeSpan $LBTime $(get-date)
                $vmsWithUptime.Add($vm.MachineName,$uptime.TotalHours) - I changed this as was noticing the time was not appearing correctly when I check the variable $vmsWithUptime}
                write-host "`t`t" $vm.MachineName $uptime.TotalHours Hours
            }
            catch {  
               
            }
        }
        foreach ($k in $vmsWithUptime.GetEnumerator() | Where-Object {$_.Value -ge $minUptime} | Sort-Object -Property Value -descending | select -first $vmsToGet)
        {

 

See below error :-

 

Where-Object : A positional parameter cannot be found that accepts argument ''.
At line:140 char:41
+ ...  in $vmsWithUptime | Where-Object {$_.Value -ge $minUptime} | Sort- ...
+                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Where-Object], ParameterBindingException
    + FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.WhereObjectCommand

 

Any help would be great..

 

Thanks 

 

HS

Link to comment
  • 0
2 hours ago, Harshit Shah said:

Hi,

 

I have tried using your script but it seems to be failing at the below block:-

 

 write-host "`t" need $vmsToGet more hosts down
        foreach ($vm in Get-BrokerMachine -DesktopGroupName $group.Name -RegistrationState Registered -InMaintenanceMode $false)
        {
            try { 
                $wmi=Get-WmiObject -class Win32_OperatingSystem -computer $vm.IPAddress #use IP in case name resolution fails
                $LBTime=$wmi.ConvertToDateTime($wmi.LastBootUpTime)
                [TimeSpan]$uptime=New-TimeSpan $LBTime $(get-date)
                $vmsWithUptime.Add($vm.MachineName,$uptime.TotalHours) - I changed this as was noticing the time was not appearing correctly when I check the variable $vmsWithUptime}
                write-host "`t`t" $vm.MachineName $uptime.TotalHours Hours
            }
            catch {  
               
            }
        }
        foreach ($k in $vmsWithUptime.GetEnumerator() | Where-Object {$_.Value -ge $minUptime} | Sort-Object -Property Value -descending | select -first $vmsToGet)
        {

 

See below error :-

 

Where-Object : A positional parameter cannot be found that accepts argument ''.
At line:140 char:41
+ ...  in $vmsWithUptime | Where-Object {$_.Value -ge $minUptime} | Sort- ...
+                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [Where-Object], ParameterBindingException
    + FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.WhereObjectCommand

 

Any help would be great..

 

Thanks 

 

HS

 

You are using 'Where-Object {$_.Value' rather than 'Where-Object {$_.Value.TotalHours'

.Value is just the Hashtable key which won't help you.

 

Link to comment
  • 0

@gtaylor955 great "zero-interruption" script !!

 

just one thing, i noticed it wasn't always selecting my longest running servers, dumping the sorted hash before the foreach loop revealed servers were indeed not in descending order of TotalHours

 

$vmsWithUptime.GetEnumerator() | Where-Object {$_.value.TotalHours -ge $minUptime} | sort -Property value.TotalHours -descending

 

this was resolved by dropping the .TotalHours on the sort, as the GetEnumerator() method creates a System.Collections.DictionaryEntry object that has a property named value

 

modifed

foreach ($k in $vmsWithUptime.GetEnumerator() | Where-Object {$_.value.TotalHours -ge $minUptime} | sort -Property value.TotalHours -descending | select -first $vmsToGet)

 

to 

 

foreach ($k in $vmsWithUptime.GetEnumerator() | Where-Object {$_.value.TotalHours -ge $minUptime} | sort -Property value -descending | select -first $vmsToGet)

 

 

Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...